【发布时间】:2020-12-05 20:53:45
【问题描述】:
我对汇编很陌生,我目前正在阅读一本名为面向初学者的逆向工程的书,我进入了讨论堆栈上的内存分配的部分。 我理解(我认为)堆栈分配的概念,但在示例中有些东西我不理解,如果有人能提供帮助,我会很高兴。
书中以这个函数为例:
#ifdef __GNUC__
#include <alloca.h> // GCC
#else
#include <malloc.h> // MSVC
#endif
#include <stdio.h>
void f()
{
char *buf=(char*)alloca (600);
#ifdef __GNUC__
snprintf (buf, 600, "hi! %d, %d, %d\n", 1, 2, 3); // GCC
#else
_snprintf (buf, 600, "hi! %d, %d, %d\n", 1, 2, 3); // MSVC
#endif
puts (buf);
};
我了解 C 函数的作用。它在堆栈上分配 600 字节的内存,然后将字符串“hi!”写入该空间。使用_snprintf 函数。然后该函数将其打印出来。
现在一切都好。之后,本书给出了 MSVC 编译器生成的汇编实现,代码如下所示:
mov eax, 600 ; 00000258H
call __alloca_probe_16
mov esi, esp
push 3
push 2
push 1
push OFFSET $SG2672
push 600 ; 00000258H
push esi
call __snprintf
push esi
call _puts
add esp, 28
这里我知道EAX 寄存器将包含__alloca_probe_16 函数的参数。
但现在有些东西对我来说没有意义。据我了解,函数__alloca_probe_16 基本上只是从ESP 中减去EAX 的值中的字节数。
例如,如果ESP 指向 1000 现在它指向 400。然后我们将 400 存储到 ESI 并开始将 _snprintf 的参数推入堆栈,ESI 指向的位置该函数需要开始写入数据。
所以我的问题是,如果ESP 和ESI 寄存器都指向400,并且我分配了1000-400(600字节)的内存,并且我开始将东西压入堆栈,它们不会进入位置从 400 开始并减少?我的意思是,如果不使用它们,为什么要减去 600 字节?
在我看来,这就是堆栈在push esi 行之后的样子。
| 400 |
| 600 |
| adrr of string |
| 1 |
| 2 |
| 3 | 400 , ESP , ESI
| | ...
|_______________________| 1000
我知道我可能是错的并且没有理解正确的东西,因为我认为这本书没有错,如果有人能帮助我理解这段汇编代码中发生的事情,我会很高兴。
【问题讨论】:
-
是的,这正是堆栈的样子。你认为这张照片有什么问题?你觉得它有什么令人惊讶的地方?
-
我的问题是如果我不使用它为什么要分配 600 字节?
-
您将指向这些字节的指针作为
snprintf的第一个参数传递。那是图片顶部的400。snprintf会将输出写入这些字节。 -
所以 snprintf 将数据写入 [ESI + 600] ,[ESI+601] 等,直到它到达 \0 字节?还是从 400 和 401 402 等开始?
-
snprintf会将数据写入400、401等,其中400是它的第一个参数,即调用时位于堆栈顶部的值。但不超过400+600,其中600是它的第二个参数。
标签: assembly visual-c++ x86 alloca