我有以下代码示例:
int main(int argc, char **argv) {
char *name[2];
name[0] = "/bin/sh";
name[1] = NULL;
execve(name[0], name, NULL);
exit(0);
}
反汇编程序会导致类似于:
1. 0x08048250 <main+0>: push %ebp
2. 0x08048251 <main+1>: mov %esp,%ebp
3. 0x08048253 <main+3>: and $0xfffffff0,%esp
4. 0x08048256 <main+6>: sub $0x20,%esp
5. 0x08048259 <main+9>: movl $0x80a6f68,0x18(%esp)
6. 0x08048261 <main+17>: movl $0x0,0x1c(%esp)
7. 0x08048269 <main+25>: mov 0x18(%esp),%eax
8. 0x0804826d <main+29>: movl $0x0,0x8(%esp)
9. 0x08048275 <main+37>: lea 0x18(%esp),%edx
10. 0x08048279 <main+41>: mov %edx,0x4(%esp)
11. 0x0804827d <main+45>: mov %eax,(%esp)
12. 0x08048280 <main+48>: call 0x804f5c0 <execve>
13. 0x08048285 <main+53>: movl $0x0,(%esp)
14. 0x0804828c <main+60>: call 0x8048af0 <exit>
我想了解集会:
在第4行,stackpointer被减少以为局部变量分配空间,但我不明白为什么它保留32个字节(0x20 = 32个字节)?据我所知,它只需要分配:
此外,我看到一些数据存储在堆栈指针的偏移处,但似乎并没有使用所有空间。
有人可以解释这件装配吗?我遇到了将c代码映射到给定汇编指令的麻烦。特别是因为保留空间的长度似乎与c代码不匹配。
我对以下内容特别感兴趣:
谢谢!
答案 0 :(得分:3)
关于&#34;它应该只需要分配&#34;:
&#34; 4个字节用于指针名称&#34; - 不完全的。 name是一个由两个指针组成的数组,所以2 * 4 = 8个字节(假设您使用的是32位指针,这就是它的样子)。
&#34;字符串的8个字节&#34; / bin / sh&#34; - 这不会成为筹码。它将在二进制文件的其他地方(可能是.rodata段,即只读数据),因此不会占用任何堆栈空间。
&#34; NULL是否占用空间?&#34; - NULL(可能,除非你有一个反向的C编译器)值为0. A&#34; value&#34;本身不能占用堆栈空间,但如果堆栈上的变量值为NULL,那么您将在堆栈中找到零。
&#34; argc和argv [etc]&#34; - 这些可以说是此函数调用的堆栈帧的一部分,但是由调用者初始化,因此不会通过减少%esp来保留。
&#34;似乎没有使用所有空间&#34; - 由于对齐,一般是正确的。考虑:
struct {
char ch;
int *ptr;
};
对于这种结构情况,我们将有一个字节的字符,然后是三个字节的填充,以便正确对齐ptr,然后是4个字节的ptr。 (如果指针是64位,则为7个字节的填充。)如果我们在堆栈中分配这些结构中的一个,那么我们将有三个(或7个)&#34;未使用的&#34;堆栈字节。
但是,在这种情况下,编译器在堆栈中为初始&#34; sub ...%esp&#34;中的execve()调用的参数创建空间。指令,这是一个轻微的优化。
第3行:对齐:在16字节边界上对齐%esp。 (应该对任何人都足够了。) 第4行:创建一些堆栈空间。其中一些是局部变量;在汇总函数调用时,编译器的其他一些字节用于临时空间。
第9行:这(有效地)将字符串&#34; / bin / sh&#34;进入%edx。该字符串的地址位于二进制文件中的0x80a6f68位置(将加载.rodata的位置)。第5行将值0x80a6f68置入(%esp + 0x18)。第9行将*(%esp + 0x18),即*(char **)0x80a6f68放入%edx。
第11行:(%esp)实际上是指针deference:* esp = eax。它将eax寄存器的值放入* esp,即堆栈指针正上方的四个字节。 eax的值在第7行定义。
第13行:这将* esp设置为0x00000000。因为我们然后调用exit(),这会将exit的第一个参数(即* esp)设置为0.
答案 1 :(得分:2)
name
是一个包含2个指针的数组。这将需要8或16个字节,具体取决于您的平台是32位还是64位。
字符串/bin/sh
根本不在堆上,而是在初始化的数据段中;为了完整性,\0
确实需要空间。
argc
和argv
将作为参数传递到堆栈上。
第3行将堆栈指针对齐为16字节的倍数
有效地说,第19行是edx
为0x18加上esp
&#39;的值; lea
已加载有效地址&#39 ;;把它想象成mov
而不是内存加载它会返回它从内存中加载的地址。
在第11行和第13行,(esp)
表示esp
指向的位置内容。
我会承认我的装配工有点生锈。