程序集8086-从堆栈中推送和弹出寄存器不起作用

时间:2017-08-08 09:40:26

标签: assembly x86-16 qemu

我是初学者,因为某些原因,推出和弹出堆栈并不适合我。我的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

我认为应该做的是将地址msgbx推送到堆栈,然后将其检索到bx。但是,情况似乎并非如此。 'H'无法打印。而是打印'-'。如果我使用msg作为有效地址,它会起作用。

编辑:正如Duncan指出的那样,call指令正在推送堆栈顶部的返回地址,这使得上面的程序使用了BIOS中断的返回地址!我现在pop将回复地址转换为dx,然后pop转换为bx,使用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

2 个答案:

答案 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>