指令ADD ESP 4的真实行为

时间:2019-01-02 23:30:29

标签: assembly x86 stack

据我了解,向堆栈指针添加4(或可能8)字节实际上会从堆栈中擦除先前的值,而无需将其放入pop之类的寄存器中。但是堆栈和堆栈指针是两件事。

  • 这是否意味着您实际上并没有擦除该值,而只是擦除了指针位置,以便它看起来像是空的一样?
  • 这是否意味着即使“擦除”数据后,您也可以SUB ESP, 4并检索数据?

2 个答案:

答案 0 :(得分:4)

中断,上下文切换或类似的操作可能会将数据保存在堆栈中,并覆盖ESP以下的任何数据,但是直到这种情况发生时,堆栈上的数据才不会在程序外部更改。如果您的程序将某些内容压入堆栈或进行调用,则将覆盖在压入或调用之前位于ESP之下的数据。

答案 1 :(得分:3)

  

[...]向您的堆栈指针添加四个[32位](或可能八个[64位])字节实际上会从堆栈中擦除先前的值,而无需将其放入像pop这样的寄存器中。

堆栈是LIFO数据结构,因此将首先删除堆栈上的最后一个值。将4(8)个字节的值添加到堆栈中只会跳过当前的最高值,而不会将其传输到寄存器中。 POP也会将该值传送到寄存器。

  

这是否意味着您实际上并没有擦除该值,而只是擦除了指针的位置,以便它看起来像是空的一样?

您只需将堆栈顶部指针 ESP(RSP)指向上一个条目。当前条目不会被擦除,而是会被跳过(不再指向)。

  

这是否意味着即使“擦除”数据后,您也可以使用SUB ESP 4并检索数据?

从本质上讲,是的。除非堆栈已被另一个过程,中断处理程序,异步调用或确实利用了堆栈的其他影响所修改,否则您可以检索堆栈的先前值。这对于安全性方面尤其重要。因此,要安全擦除堆栈上的数据,必须手动将其重写为零。

受@PeterCordes的评论启发,我在Linux 32位程序集中编写了一个简单的示例来说明这一点:

.intel_syntax noprefix
.global _start
.text

_start:
    call subroutine
    mov  ebx, [esp-12]  /* Get data from subroutine */
    mov  eax, 1 
    int  0x80
    /* echo $? outputs '66' */

subroutine:
    push ebp
    mov ebp, esp
    push 66  /* local variable to be leaked */
    mov esp, ebp    
    pop ebp
    ret 

命名为stack.s并用

进行组装
as -32 stack.s
ld -m elf_i386 -elf -o a a.out
./a
echo $?

输出将为66,即子例程中局部变量的值。因此,可能会从堆栈中泄漏数据。这有多严重?我不确定。但这是可能的。

如果将关键数据归零,则必须注意编译器不会优化这些指令。您可以在YouTube上找到一个长达一个小时的名为"35C3 - Memsad"的讲座(这是德国人CCC的近期演讲)。