我是初学者,因为某些原因,推出和弹出堆栈并不适合我。我的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
作为有效地址,它会起作用。
编辑:正如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
答案 0 :(得分:2)
call
指令将程序计数器推入堆栈,ret
从堆栈中弹出最高值并跳转到它。
所以你不能通过在调用之前推送参数来将参数传递给函数,并将它们弹出到调用中,因为保存的pc会妨碍它。
选项可能包括设置一个帧指针,以便您可以访问仍在堆栈中的参数,然后将它们作为返回的一部分弹出。
或者对于这么简单的事情你可以将返回地址弹出到另一个寄存器而不是返回你只是跳转到它。
答案 1 :(得分:0)
从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
。{/ p>