我已经制作了这个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;
};
在进程的记忆中它总是以相同的方式呈现吗?我的意思是,如果S.a = 2(总是跟随b = 3,c = 4),那么结构呈现在进程'内存空间的连续行中,如
Offset
---------------------
0x0000 | 0x02 0x03 0x04
或者那些变量可以在那里的不同位置,例如
Offset
---------------------
0x0000 | 0x00 0x02 0x00
0x03fc | 0x00 0x03 0x04
如果是后者,如何从内存中的各个点重建数据结构?
非常感谢提前,
nhaa123
答案 0 :(得分:1)
如果您的受害者是用C或C ++编写的,并且所使用的数据类型真的很简单,那么您将始终将它们视为内存中的单个字节块。
但是只要你拥有像std::string
这样的C ++类型,观察就不再适用了。对于初学者来说,C ++编译器之间的确切布局会有所不同,甚至是同一编译器的不同版本。 std :: string的字节可能不在连续的数组中,但有时它们是。如果他们被分成两半,找到下半场可能无法帮助你找到上半场。
不要像JIT运行Java应用程序的JVM那样投入更复杂的环境。你在记忆中遇到的类型非常复杂;人们可以写一本关于解码它们的书。
答案 1 :(得分:0)
成员的顺序将始终相同,结构将占用连续的内存块。
根据编译器的填充,可能会在成员之间添加,但如果使用相同的编译器和相同的设置重新编译程序,它仍然是相同的。如果添加了填充并且您不知道它,则无法在运行时可靠地检测到它 - 编译器所具有的所有信息都会丢失到那一刻,您只需分析模式并进行猜测。
答案 2 :(得分:0)
这取决于结构的对齐方式。
如果你有这样的事情:
struct A
{
int16_t a;
char b;
int32_t c;
char d;
}
然后默认在32位平台上(我不知道64位是否为真),c的偏移量为4,因为在b之后填充了一个字节,在d之后还有3个bytess填充在末尾(如果我记得没错。
如果结构具有指定的对齐方式,则会有所不同。
答案 3 :(得分:0)
现在我的问题。如果目标进程使用数据结构,那么它在进程内存中总是以相同的方式呈现吗?我的意思是,如果S.a = 2(总是跟随b = 3,c = 4),那么结构是否在过程'存储空间中的连续行中呈现?
是的,但是它通常会被填充以便以您可能不期望的方式对齐成员。因此,只需重新创建数据结构,以便通过代码注入与它进行交互。
我强烈建议使用ReClassEx或ReClass.NET两个专门用于从内存重建数据结构并生成可用C ++代码的开源程序!看一下截图: