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



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

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

我相信,这应该做的是将地址msgbx推送到堆栈上,然后将其检索到bx中。然而,情况似乎并非如此。'H'不会被打印出来。而是打印'-'。如果我使用msg作为有效地址,它可以工作。

编辑:正如邓肯指出的那样,call指令正在推动我的堆栈顶部的返回地址,这使得上述程序使用该返回地址进行 BIOS 中断!我现在将退货地址popdx然后popbx中,完成后使用bxjmp的值来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

最新更新