程序集中保留的堆栈空间似乎与c代码不匹配

时间:2021-07-09 19:24:29

I have the following code example:

我有以下代码示例:

int main(int argc, char **argv) {
    char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL;
    execve(name[0], name, NULL);
    exit(0);
}

Disassembling the program leads to something similar to:

拆解程序会导致类似于:

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>

I am trying to understand the assembly:

我想了解大会:

On line 4 the stackpointer is decreased to allocate space for the local variables, but I don't understand why it reserves 32 bytes (0x20=32 bytes)? As I understand it should only have to allocate:

在第4行,stackpointer被减少以为局部变量分配空间,但我不明白为什么它保留32个字节(0x20 = 32个字节)?据我所知,它应该只需要分配:

  • 4 bytes for the pointer "name"
  • 指针“name”的4个字节

  • 8 bytes for the string "/bin/sh";
  • 字符串“/ bin / sh”为8个字节;

  • Does NULL also take up space? If so it is equivalent to 0x0 of 4 bytes right?
  • NULL也占用空间吗?如果是这样,它相当于4个字节的0x0对吗?

  • The argc and argv are not part of this stackframe right? they are pushed by mains caller and as such don't need to be reserved in this stackframe.
  • argc和argv不属于这个堆栈框架吗?它们由主电话调用者推动,因此不需要在此堆栈帧中保留。

Also I see some data that is being stored at an offset from the stack pointer but it doesn't seem like all the space is being used.

此外,我看到一些数据存储在堆栈指针的偏移处,但似乎并没有使用所有空间。

Could someone maybe explain this piece of assembly? I am having troubles mapping the c code to the given assembly instructions. Especially since the lengths of the reserved space doesn' t seem to match with the c code.

有人可以解释这件装配吗?我遇到了将c代码映射到给定汇编指令的麻烦。特别是因为保留空间的长度似乎与c代码不匹配。

I am particularly interested in:

我特别感兴趣的是:

  • Line 3: What is this for?
  • 第3行:这是为了什么?

  • Line 9: what does this do?
  • 第9行:这是做什么的?

  • Line 11 and 13: what does the (%esp) syntax mean?
  • 第11行和第13行:(%esp)语法是什么意思?

Thanks!

2 个解决方案

#1


3  

Regarding "it should only have to allocate":

关于“它应该只需要分配”:

  • "4 bytes for the pointer name" - not quite. name is an array of two pointers, so 2*4 = 8 bytes (assuming that you're using 32-bit pointers, which is what it looks like).

    “指针名称的4个字节” - 不完全。 name是一个由两个指针组成的数组,所以2 * 4 = 8个字节(假设您使用的是32位指针,这就是它的样子)。

  • "8 bytes for the string "/bin/sh" - this won't be on the stack. It'll be elsewhere in the binary (probably the .rodata segment, i.e. read-only data), so doesn't take up any stack space.

    “8个字节用于字符串”/ bin / sh“ - 这不会在堆栈上。它将在二进制文件的其他地方(可能是.rodata段,即只读数据),所以不会占用任何堆栈空间。

  • "Does NULL also take up space?" - NULL is (probably, unless you have a perverse C compiler) the value 0. A "value" can't in itself take up stack space, but if there's a variable on the stack with value NULL then you'll find zero on the stack.

    “NULL也会占用空间吗?” - NULL(可能,除非你有一个反向的C编译器)值0.一个“值”本身不能占用堆栈空间,但如果堆栈上的变量值为NULL,那么你会发现零堆栈。

  • "The argc and argv [etc]" - these are arguably part of the stack frame for this function call, but are initialised by the caller, so aren't reserved by decreasing %esp.

    “argc和argv [etc]” - 这些可以说是这个函数调用的堆栈帧的一部分,但是由调用者初始化,因此不会通过减少%esp来保留。

"doesn't seem like all the space is being used" - correct in general, due to alignment. Consider:

“似乎并没有使用所有空间” - 由于对齐,一般来说是正确的。考虑:

struct {
    char ch;
    int *ptr;
};

For this struct case we'll have a one byte char, then three bytes of padding in order to align ptr correctly, then four bytes of ptr. (Or 7 bytes of padding if pointers are 64 bits.) If we allocate one of these structures on the stack then we'll have three (or 7) "unused" stack bytes.

对于这种结构情况,我们将有一个字节的char,然后是三个字节的填充,以便正确对齐ptr,然后是4个字节的ptr。 (如果指针是64位,则为7个字节的填充。)如果我们在堆栈上分配其中一个结构,那么我们将有三个(或7个)“未使用”的堆栈字节。

However, in this case the compiler is creating room on the stack for the arguments to the execve() call within the initial "sub ... %esp" instruction, which is a slight optimisation.

但是,在这种情况下,编译器在堆栈中为初始“sub ...%esp”指令中的execve()调用的参数创建空间,这是一个轻微的优化。

Line 3: alignment: align %esp on a 16-byte boundary. (Should be enough for anybody.) Line 4: create some stack space. Some of this is the local variables; some other bytes of it are used for scratch space by the compiler when putting together function calls.

第3行:对齐:在16字节边界上对齐%esp。 (应该对任何人都足够了。)第4行:创建一些堆栈空间。其中一些是局部变量;在汇总函数调用时,编译器的其他一些字节用于临时空间。

Line 9: this (effectively) puts the string "/bin/sh" into %edx. The address of that string is at location 0x80a6f68 in the binary (which will be where .rodata is loaded). Line 5 puts the value 0x80a6f68 into (%esp + 0x18). Line 9 puts *(%esp + 0x18), i.e. *(char **)0x80a6f68, into %edx.

第9行:这(有效地)将字符串“/ bin / sh”放入%edx。该字符串的地址位于二进制文件中的0x80a6f68位置(将加载.rodata的位置)。第5行将值0x80a6f68置入(%esp + 0x18)。第9行将*(%esp + 0x18),即*(char **)0x80a6f68放入%edx。

Line 11: (%esp) is effectively pointer deference: *esp = eax. It puts the value of the eax register into *esp, i.e. the four bytes immediately above the stack pointer. The value of eax was defined on line 7.

第11行:(%esp)实际上是指针deference:* esp = eax。它将eax寄存器的值放入* esp,即堆栈指针正上方的四个字节。 eax的值在第7行定义。

Line 13: this sets *esp to 0x00000000. Since we then call exit(), this sets exit's first argument (i.e. *esp) to 0.

第13行:这将* esp设置为0x00000000。因为我们然后调用exit(),这会将exit的第一个参数(即* esp)设置为0。

#2


2  

name is an array of 2 pointers. This will take 8 or 16 bytes depending on whether your platform is 32 bit or 64 bit.

name是2个指针的数组。这将需要8或16个字节,具体取决于您的平台是32位还是64位。

The string /bin/sh will not be on the heap at all but will be in the initialized data segment; for completeness yes the \0 does need space.

字符串/ bin / sh根本不在堆上,而是在初始化的数据段中;为了完整性,\ 0确实需要空间。

argc and argv will be passed on the stack as they are parameters.

argc和argv将作为参数传递给堆栈。

Line 3 is aligning the stack pointer to be a multiple of 16 bytes

第3行将堆栈指针对齐为16字节的倍数

Line 19 is effectively say 'let edx be 0x18 plus the value of esp'; lea is 'load effective address'; think of it like a mov but rather than do the memory load it returns the address it would have loaded the memory from.

第19行实际上是'让edx为0x18加上esp的值'; lea是'加载有效地址';把它想象成一个mov而不是做内存加载它会返回它本来会加载内存的地址。

On line 11 and 13, the (esp) means the contents of the location pointed to by esp.

在第11和13行,(esp)表示由esp指向的位置的内容。

I will admit my assembler is a little rusty.

我会承认我的装配工有点生疏了。

#1


3  

Regarding "it should only have to allocate":

关于“它应该只需要分配”:

  • "4 bytes for the pointer name" - not quite. name is an array of two pointers, so 2*4 = 8 bytes (assuming that you're using 32-bit pointers, which is what it looks like).

    “指针名称的4个字节” - 不完全。 name是一个由两个指针组成的数组,所以2 * 4 = 8个字节(假设您使用的是32位指针,这就是它的样子)。

  • "8 bytes for the string "/bin/sh" - this won't be on the stack. It'll be elsewhere in the binary (probably the .rodata segment, i.e. read-only data), so doesn't take up any stack space.

    “8个字节用于字符串”/ bin / sh“ - 这不会在堆栈上。它将在二进制文件的其他地方(可能是.rodata段,即只读数据),所以不会占用任何堆栈空间。

  • "Does NULL also take up space?" - NULL is (probably, unless you have a perverse C compiler) the value 0. A "value" can't in itself take up stack space, but if there's a variable on the stack with value NULL then you'll find zero on the stack.

    “NULL也会占用空间吗?” - NULL(可能,除非你有一个反向的C编译器)值0.一个“值”本身不能占用堆栈空间,但如果堆栈上的变量值为NULL,那么你会发现零堆栈。

  • "The argc and argv [etc]" - these are arguably part of the stack frame for this function call, but are initialised by the caller, so aren't reserved by decreasing %esp.

    “argc和argv [etc]” - 这些可以说是这个函数调用的堆栈帧的一部分,但是由调用者初始化,因此不会通过减少%esp来保留。

"doesn't seem like all the space is being used" - correct in general, due to alignment. Consider:

“似乎并没有使用所有空间” - 由于对齐,一般来说是正确的。考虑:

struct {
    char ch;
    int *ptr;
};

For this struct case we'll have a one byte char, then three bytes of padding in order to align ptr correctly, then four bytes of ptr. (Or 7 bytes of padding if pointers are 64 bits.) If we allocate one of these structures on the stack then we'll have three (or 7) "unused" stack bytes.

对于这种结构情况,我们将有一个字节的char,然后是三个字节的填充,以便正确对齐ptr,然后是4个字节的ptr。 (如果指针是64位,则为7个字节的填充。)如果我们在堆栈上分配其中一个结构,那么我们将有三个(或7个)“未使用”的堆栈字节。

However, in this case the compiler is creating room on the stack for the arguments to the execve() call within the initial "sub ... %esp" instruction, which is a slight optimisation.

但是,在这种情况下,编译器在堆栈中为初始“sub ...%esp”指令中的execve()调用的参数创建空间,这是一个轻微的优化。

Line 3: alignment: align %esp on a 16-byte boundary. (Should be enough for anybody.) Line 4: create some stack space. Some of this is the local variables; some other bytes of it are used for scratch space by the compiler when putting together function calls.

第3行:对齐:在16字节边界上对齐%esp。 (应该对任何人都足够了。)第4行:创建一些堆栈空间。其中一些是局部变量;在汇总函数调用时,编译器的其他一些字节用于临时空间。

Line 9: this (effectively) puts the string "/bin/sh" into %edx. The address of that string is at location 0x80a6f68 in the binary (which will be where .rodata is loaded). Line 5 puts the value 0x80a6f68 into (%esp + 0x18). Line 9 puts *(%esp + 0x18), i.e. *(char **)0x80a6f68, into %edx.

第9行:这(有效地)将字符串“/ bin / sh”放入%edx。该字符串的地址位于二进制文件中的0x80a6f68位置(将加载.rodata的位置)。第5行将值0x80a6f68置入(%esp + 0x18)。第9行将*(%esp + 0x18),即*(char **)0x80a6f68放入%edx。

Line 11: (%esp) is effectively pointer deference: *esp = eax. It puts the value of the eax register into *esp, i.e. the four bytes immediately above the stack pointer. The value of eax was defined on line 7.

第11行:(%esp)实际上是指针deference:* esp = eax。它将eax寄存器的值放入* esp,即堆栈指针正上方的四个字节。 eax的值在第7行定义。

Line 13: this sets *esp to 0x00000000. Since we then call exit(), this sets exit's first argument (i.e. *esp) to 0.

第13行:这将* esp设置为0x00000000。因为我们然后调用exit(),这会将exit的第一个参数(即* esp)设置为0。

#2


2  

name is an array of 2 pointers. This will take 8 or 16 bytes depending on whether your platform is 32 bit or 64 bit.

name是2个指针的数组。这将需要8或16个字节,具体取决于您的平台是32位还是64位。

The string /bin/sh will not be on the heap at all but will be in the initialized data segment; for completeness yes the \0 does need space.

字符串/ bin / sh根本不在堆上,而是在初始化的数据段中;为了完整性,\ 0确实需要空间。

argc and argv will be passed on the stack as they are parameters.

argc和argv将作为参数传递给堆栈。

Line 3 is aligning the stack pointer to be a multiple of 16 bytes

第3行将堆栈指针对齐为16字节的倍数

Line 19 is effectively say 'let edx be 0x18 plus the value of esp'; lea is 'load effective address'; think of it like a mov but rather than do the memory load it returns the address it would have loaded the memory from.

第19行实际上是'让edx为0x18加上esp的值'; lea是'加载有效地址';把它想象成一个mov而不是做内存加载它会返回它本来会加载内存的地址。

On line 11 and 13, the (esp) means the contents of the location pointed to by esp.

在第11和13行,(esp)表示由esp指向的位置的内容。

I will admit my assembler is a little rusty.

我会承认我的装配工有点生疏了。