Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly 组件8086-从堆栈推送和弹出寄存器不工作_Assembly_X86 16_Qemu - Fatal编程技术网

Assembly 组件8086-从堆栈推送和弹出寄存器不工作

Assembly 组件8086-从堆栈推送和弹出寄存器不工作,assembly,x86-16,qemu,Assembly,X86 16,Qemu,我是一个初学者,出于某种原因,推和弹出堆栈对我不起作用。我的引导加载程序: org 0x7c00 bits 16 jmp main print: pop bx mov al, [bx] mov ah, 0eh int 10h ret main: mov bx, msg push bx call print cli hlt msg: db 'Hello World!', 0 times 510 -

我是一个初学者,出于某种原因,推和弹出堆栈对我不起作用。我的引导加载程序:

org 0x7c00
bits 16
jmp main
print:
    pop bx
    mov al, [bx]
    mov ah, 0eh
    int 10h
    ret
main:
    mov bx, msg
    push bx
    call print

    cli
    hlt

    msg: db 'Hello World!', 0
    times 510 - ($-$$) db 0
    dw 0xAA55
我认为这应该做的是,将地址
msg
bx
推到堆栈上,然后将其检索到
bx
。然而,情况似乎并非如此<代码>'H'无法打印。而是打印一个
'-'
。如果我使用
msg
作为有效地址,它会起作用

编辑:正如邓肯指出的那样,
调用
指令将返回地址推到堆栈顶部,这使得上述程序将该返回地址用于BIOS中断!我现在
pop
将返回地址转换成
dx
然后
pop
转换成
bx
,完成后使用
bx
jmp
的值转换成
dx

org 0x7c00
bits 16
jmp main
print:
    pop dx
    pop bx
    mov al, [bx]
    mov ah, 0eh
    int 10h
    jmp dx
main:
    mov bx, msg
    push bx
    call print

    cli
    hlt

    msg: db 'Hello World!', 0
    times 510 - ($-$$) db 0
    dw 0xAA55

call
指令将程序计数器推到堆栈上,然后
ret
从堆栈中弹出顶部值并跳转到它

因此,您不能在调用前按参数并在调用中弹出参数来将参数传递给函数,因为保存的pc会挡住它的去路

选项可能包括设置帧指针,以便访问仍在堆栈上的参数,然后将其作为返回的一部分弹出


或者对于这样简单的事情,您可以将返回地址弹出到另一个寄存器中,而不是跳转到它。

jmp far 0:main
开始,以规范化
cs:ip
,因为有些BIOS会使用
cs=0
调用您,而有些则使用
cs=0x07c0
调用您

print:
    pop bx
    mov al, [bx]
    mov ah, 0eh
    int 10h
    ret
也许您在shell漏洞利用中见过类似的情况,但正确的用法如下所示:

    call print  ; this will put `msg` at top of stack!
msg: db 'H'
print:
    pop bx      ; load `msg` address
    ... no RET !! (there's nowhere to return to)
这是一个技巧,当您不知道代码将位于何处时,如何将正确的偏移量放入堆栈中(shell攻击通常会进入一些缓冲区溢出区域,带有一些随机地址)

这在引导加载程序中是不需要的,因为您位于固定位置
0000:7C00
,并且可以这样进行组装,所以对于像
print
这样的自定义过程,使用一些自定义的“通过寄存器传递参数值”调用约定是可以的,根本不涉及堆栈(除了通过
call+ret
对使用它存储返回地址之外)


在使用堆栈之前,也要初始化堆栈,即在main的开头,您可能需要执行以下操作,例如:

main:
    ; set ss:sp to 0000:7C00 (so you have about ~29kB of RAM for stack)
    xor ax,ax
    mov ss,ax       ; disables interrupts for 1 more instruction
    mov sp,0x7C00   ; so sp set must follow the `ss` setup
当然,不要将它用于任何深度递归,29kB是一个小堆栈(但对于合理编写的引导加载程序来说已经足够了,只需从磁盘加载一些内核,实际上100-200B的堆栈就足够了)


当然可以设置
ds
!就像您设置
mov al[bx]
ds
一样,已经设置好了。在您的情况下,最简单的事情是将所有内容保持在
0000:7C00
,因此在
mov ss,ax
之前,您也可以执行
mov ds,ax

如果您知道返回地址的长度和/或其他推送值,您可以直接寻址,例如:
mov bx,2[sp]
在汇编这样的语言中,这是一个很好的实践吗?我有OOP背景。@ArjavGarg在尝试编写像bootloader这样棘手的东西之前,首先学习汇编基础知识是一个很好的实践,在这种情况下,您从一开始就要与非常恶劣的环境作斗争(初始状态非常模糊且有缺陷,因为BIOS供应商不太关心,加上除了BIOS服务外,几乎没有可调用的服务)。我仍然不明白,编写自己的引导加载程序甚至裸机操作系统,而不是使用稳定的操作系统环境来编写一些有趣的东西,比如玩一些图形和使用GUI调试器,有什么吸引力。操作系统开发更复杂。我已经完成了OpenGL和2d Java图形。这似乎是下一个逻辑问题step@ArjavGarg对我来说,下一个逻辑步骤听起来像是在汇编中进行OGL+2D,因为您已经知道了原理,可以专注于学习汇编本身,熟悉基本指令、算术、内存管理、堆栈使用等……然后再次启动加载程序(至少是初始部分)必须在16b实模式下,这是另一个PITA,所以…是的,这没关系,如果你想做引导加载程序,它很好,只是不要期望它很容易,它会咬你,比任何Java都要多出很多次,需要更多的注意细节和微观管理一切。当然,还有一个问题是
dx
int10h
调用中保留。该“修复”在我看来是不合理的复杂和脆弱。如果有什么问题的话,您可以在
pop bx
之后立即执行
push dx
,然后使用
ret
,因此您不必担心
dx
,但是如果您坚持通过堆栈传递参数,您应该执行详细的
push bp
mov bp,sp
naive-C堆栈frame thing(然后
mov bx,[bp+4]
是第一个参数),因为这对任何有经验的ASM程序员来说都很容易阅读,所以以后帮助你会更容易。@ArjavGarg花了几周时间阅读并尝试其他源代码。你甚至可能想阅读
grub
,它必须在真实场景中工作。我自己从来没有做过引导加载程序,对我来说太无聊了,我更喜欢real应用程序编码,所以我不知道编写bootloader时可能出现的所有问题,但肯定不可能将它们放在简短的答案中,因为它至少有几十页的文本。您所拥有的可能已经在qemu或bochs下工作,我想这两个选项都可以通过某种方式进行调试?bochs是肯定的。