1

我对汇编很陌生,我目前正在阅读一本名为《逆向工程初学者》的书,我进入了讨论堆栈内存分配的部分。我理解(我认为)堆栈分配的概念,但在示例中有些东西我不理解,如果有人能提供帮助,我会很高兴。

书中以这个函数为例:

#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函数的参数。但现在有些东西对我来说没有意义。据我了解,该函数基本上只是减去from__alloca_probe_16值中的字节数。EAXESP

例如,如果ESP指向 1000 现在它指向 400。然后我们将 400 存储到ESI并开始将参数压_snprintf入堆栈,并ESI指向函数需要开始写入数据的位置。所以我的问题是,如果ESPESI寄存器都指向 400 并且我从 1000-400(600 字节)分配内存,并且我开始将东西压入堆栈,它们不会进入从 400 开始的位置并减少? 我的意思是,如果不使用它们,为什么要减去 600 字节?在我看来,这就是堆栈在行后的样子push esi


|          400          |
|          600          |
|    adrr of string     |
|           1           |
|           2           |
|           3           | 400 , ESP , ESI
|                       | ...
|_______________________| 1000

我知道我可能错了并且没有理解正确的东西,因为我认为这本书没有错,如果有人能帮助我理解这段汇编代码中发生的事情,我会很高兴。

4

1 回答 1

2

例如,如果 ESP 指向 1000 现在它指向 400。然后我们将 400 存储到 ESI 并开始将 _snprintf 的参数推送到堆栈,ESI 指向函数需要开始写入数据的位置。

正确的。

所以我的问题是,如果 ESP 和 ESI 寄存器都指向 400,并且我分配了 1000-400(600 字节)的内存,并且我开始将东西推入堆栈,它们会不会进入从 400 开始的位置并减少?

是的,他们当然会。

我的意思是,如果我不使用它们,我们是否减去了 600 字节?

你还没有使用那个空间,但是你已经让它可用了,所以它snprintf有一个地方来写它产生的字符串,你给它传递了地址 400(在 ESI 中)告诉它这样做。返回时snprintf,字符串"hi! 1, 2, 3 \n"将从地址 400 开始存储。

当然,对于这么短的字符串,你真的不需要 600 个字节;这只是一个示例。如果你愿意,你可以把它变小。

于 2020-08-16T17:08:48.400 回答