2

我有我制作的这个DLL。它被注入另一个进程。在另一个进程中,我使用以下函数从它的内存空间中进行搜索:


void MyDump(const void *m, unsigned int n)
{
        const char *p = reinterpret_cast(m);

        for (unsigned int i = 0; i < n; ++i) {
                // Do something with p[i]...
        }
}

现在我的问题。如果目标进程使用数据结构,假设

struct S
{
        unsigned char a;
        unsigned char b;
        unsigned char c;
};

它在进程的内存中总是以相同的方式呈现吗?我的意思是,如果 Sa = 2(始终遵循 b = 3,c = 4),是在进程的内存空间中以连续行呈现的结构,例如

Offset
---------------------
0x0000 | 0x02 0x03 0x04

或者这些变量可以在不同的地方,比如

Offset
---------------------
0x0000 | 0x00 0x02 0x00
0x03fc | 0x00 0x03 0x04

如果是后者,如何从内存中的各个点重建数据结构?

非常感谢,
nhaa123

4

4 回答 4

1

如果你的受害者是用 C 或 C++ 编写的,并且使用的数据类型真的那么简单,那么你总是会发现它们是内存中的单个字节块。

但是一旦你有了像这样的 C++ 类型std::string,这种观察就不再成立了。对于初学者来说,C++ 编译器之间的确切布局会有所不同,甚至同一编译器的不同版本也会有所不同。std::string 的字节可能不在连续数组中,但有时它们是。如果它们一分为二,找到后半部分可能不会帮助您找到前半部分。

不要投入更复杂的环境中,例如运行 Java 应用程序的 JIT'ting JVM。你在内存中遇到的类型非常非常复杂;可以写一本关于解码它们的书。

于 2009-09-09T08:41:38.437 回答
0

成员的顺序将始终相同,结构将占用一个连续的内存块。

根据编译器填充可能会在成员之间添加,但如果使用相同的编译器和相同的设置重新编译程序,它仍然是相同的。如果添加了填充并且您没有意识到它,您将无法在运行时可靠地检测到它 - 编译器所拥有的所有信息都丢失了,您只能分析模式和猜测。

于 2009-09-09T08:26:22.633 回答
0

这取决于结构的对齐方式。

如果你有这样的事情:

struct A
{
  int16_t a;
  char    b;
  int32_t c;
  char    d;
}

然后默认情况下在 32 位平台上(我不知道这是否适用于 64 位),c 的偏移量为 4,因为在 b 之后填充了一个字节,在 d 之后最后填充了 3 个字节(如果我没记错的话)。

如果结构具有指定的对齐方式,情况会有所不同。

于 2009-09-09T11:01:05.353 回答
0

现在我的问题。如果目标进程使用数据结构 [...],它在进程的内存中是否总是以相同的方式呈现?我的意思是,如果 Sa = 2(始终遵循 b = 3,c = 4),该结构是否在进程的内存空间中以连续行的形式呈现?

是的,但是它通常会被填充以以您可能意想不到的方式对齐成员。因此,只需重新创建数据结构,以便通过代码注入与其交互。

我强烈推荐使用ReClassExReClass.NET,这两个开源程序专门用于从内存中重建数据结构并生成可用的 C++ 代码!查看屏幕截图:

ReClass.NET 截图

于 2017-06-25T21:51:05.557 回答