我是一个初学者,由于某种原因,推出和弹出堆栈对我来说不起作用 . 我的bootloader:
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
作为有效地址,它可以工作 .
Edit :正如Duncan指出的那样, call
指令正在推送堆栈顶部的返回地址,这使得上面的程序使用了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
2 回答
call
指令将程序计数器推入堆栈,ret
从堆栈中弹出顶部值并跳转到它 .所以你不能通过在调用之前推送参数来将参数传递给函数,并在调用中弹出它们,因为保存的pc会妨碍它 .
选项可能包括设置帧指针,以便您可以访问仍在堆栈中的参数,然后将它们作为返回的一部分弹出 .
或者对于这个简单的东西你可以将返回地址弹出到另一个寄存器而不是返回你只是跳转到它 .
从
jmp far 0:main
开始,以规范化cs:ip
,因为某些BIOS将使用cs=0
调用您,而某些BIOS将使用cs=0x07c0
调用 .也许你在shell漏洞利用中看到过这样的东西,但正确的用法看起来像这样:
当你不知道你的代码将位于何处时,这是一个如何将正确的偏移量放入堆栈的技巧(shell利用通常进入某个缓冲区溢出区域,带有一些随机地址) .
在引导加载程序中不需要这样,因为你处于固定位置
0000:7C00
并且你可以像这样组装,所以使用一些自定义"pass argument value by register"自定义程序的自定义"pass argument value by register"调用约定很好,完全没有涉及堆栈(除了使用它来存储返回地址)call + ret
对) .在使用之前初始化堆栈,即在main的开头你可能想要做的例如:
当然不要将它用于任何深度递归,29kB是小堆栈(但是对于合理编写的引导加载程序来说很多,从磁盘加载一些内核,实际上100-200B的堆栈应该足够了) .
并设置
ds
当然!正如您所做的那样mov al,[bx]
已经设置好了 . 在你的情况下,最简单的方法是将所有内容保存在0000:7C00
,所以在mov ss,ax
之前你也可以做mov ds,ax
.