问题描述
我正在尝试计算将数据压入堆栈的大小。尝试此操作时,我收到分段错误。我的目标是在ebp
将任何数据放入堆栈之前将esp
指向push
。将数据压入堆栈后,我将以字节ebp - esp
为单位测量所述数据的大小,并将其存储在ebx
中,并使用printf
打印到stdout。
例如:
; compile with:
; nasm -f elf test.asm && gcc -m32 -o test test.o
section .text
global main
extern printf
main:
; set the frame pointer to the beginning of the stack before-
; data is pushed.
push ebp
mov ebp,esp
push ebx
push 0x00 ; <- null terminating byte/string truncation
push 0x64636261 ; <- data
mov ebx,ebp
sub ebx,esp ; start of stack - end of stack = sizeof(data) stored in ebx
push ebx
push format_str
call printf
add esp,8
pop ebp
pop ebx
ret
section .data
format_str db "%s",2,0
编译此代码时,我收到输出:
Segmentation fault (core dumped)
预期输出:
5
解决方法
首先,如果希望将项目放回其原始寄存器中,则应按反向的顺序弹出它们。您在这里拥有的内容(删除了不相关的行):
push ebp
push ebx
pop ebp
pop ebx
不会将ebp
恢复到先前的值,并且非常可能会导致在堆栈帧中向上移动的问题。
另外,您最好遵循从esp
恢复ebp
的常规做法,而不是盲目地添加8。最后就是这样:
; add esp,8 ; not the normal way.
mov esp,ebp
pop ebp
通过这种方式进行操作,您无需手动计算需要从堆栈中取出多少字节,因为您没有考虑到所推送的所有内容,因此您实际上错了。
最后,在 之前,您必须确保esp
在正确的位置,以便pop ebp
可以正常工作。这意味着弹出您推送的所有内容(ebp
本身除外)。由于您在ebp
之后ebx
,0x00000000
,0c64636261
,ebx
和format_str
进行推送,因此您应该确保所有这些在尝试弹出ebp
之前先退出堆栈。
考虑到所有这些因素,您会得到类似的东西:
main:
push ebp
mov ebp,esp
push ebx ; (+1)
push 0x00 ; (+2)
push 0x64636261 ; (+3)
mov ebx,ebp
sub ebx,esp
push ebx ; (+4)
push format_str ; (+5)
call printf
add esp,16 ; (-5,-4,-3,-2)
pop ebx ; (-1)
mov esp,ebp
pop ebp
ret
这些(+N)
注释中的每一个表示已被压入堆栈的32位值,而(-N)
注释指示哪些指令使这些压入反向。 add esp,16
将其中的四个反转(每个四个字节),并且这样做是因为我们不在乎那些项目会发生什么。这样最后的pop
就可以恢复原始的ebx
(我们要做在意)。
在我看来,esp
的最终重载在这种情况下是不必要的,因为它已通过前面的步骤恢复为正确的值。是否保留序言/后记的一致性取决于您自己。