据我了解,向堆栈指针添加4(或可能8)字节实际上会从堆栈中擦除先前的值,而无需将其放入pop
之类的寄存器中。但是堆栈和堆栈指针是两件事。
SUB ESP, 4
并检索数据?答案 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的近期演讲)。