通过CONTEXT.Ebx+8指向基地址的指针

3
我想要实现“Win32 EXE动态分支”的已知方法,也称为RunPE。 我的问题是,我无法像在http://www.security.org.sg/code/loadexe.html的第3点中提到的那样获得“基地址”的正确结果。
这是我的代码:
DWORD* peb;
DWORD* baseAddress;
...snip...

GetThreadContext(hTarget, &contx)

peb = (DWORD *) contx.Ebx;
baseAddress = (DWORD *) contx.Ebx+8;

_tprintf(_T("The EBX [PEB] is: 0x%08X\nThe base address is: 0x%08X\nThe Entry Point is: 0x%08X\n"), peb, baseAddress, contx.Eax);

输出如下:

EBX [PEB] 为: 0x7FFD4000

基地址为: 0x7FFD4020

入口点为: 0x00401000

我认为我的问题出在 baseAddress 指针的实现上,但我无法确定具体是什么问题。或者可能是我没有正确理解上述文章中的 baseAddress 并不是 ImageBase,如果是这样,那么 baseAddress 是什么?

我已尝试在 Win 7 64b 和 Win-XP 下运行它,但两者都得到了相同的错误结果。

3个回答

2

您只是在进行指针运算,而不是实际解引用内存,在这一行代码中:

baseAddress = (DWORD *) contx.Ebx+8;

你只是将8*sizeof(DWORD) = 32添加到contx.Ebx的值上。你真正想要做的是在新进程的地址空间中读取contx.Ebx+8地址处的数据。为了实现这一点,你需要使用ReadProcessMemory,不要进行转换,因为你想要使用原始偏移量,而不是指针算术中使用DWORD*值时乘以sizeof(DWORD)的偏移量。
但是,我强烈警告你不要深入到这样的实现细节中,因为在不同版本的Windows中,它们可能会发生变化。请记住,你链接的文章是2004年写的,仅仅是一个概念验证,所以在Vista、Windows 7、Windows 8和未来的版本中可能会出现许多隐藏的问题和意外的问题。
Windows API没有像Unix的fork(2)函数一样的函数,因此,尽可能避免需要fork -- 使用CreateProcess而不是fork+exec等。Cygwin的fork实现很丑陋,速度很慢,并且由于DLL内存映射问题而可能意外失败。

我不明白为什么(DWORD *)会变成乘法运算?我知道算术运算的顺序,但为什么强制转换会变成算术运算? - Hanan N.
@Hanan:你理解指针算术是如何工作的吗?((DWORD *)anything)是一个指针,当你将一个指针和一个整数相加时,编译器总是将整数乘以所指向元素的大小,而在这种情况下,所指向的元素是大小为4的DWORD*(contx.Ebx+8)不能使用指针来完成,因为你想要的数据位于另一个进程的虚拟地址空间中。 - Adam Rosenfield

2
请注意指令中的“在[EBX+8]”这句话。括号里的内容代表那个地址处的值。以下存在一些问题。
baseAddress = (DWORD *) contx.Ebx+8;

首先,编译器不关注空格,只关注括号的使用,因此这意味着

baseAddress = ((DWORD *)contx.Ebx) + 8;

这是错误的,因为8正在计算DWORDs,而不是字节。您需要
baseAddress = (DWORD *)(contx.Ebx + 8);

但是这只是获取存储baseAddress的地址,而不是baseAddress的值。为了获取值,您需要

baseAddress = *(DWORD *)(contx.Ebx + 8);

然而,这仅在contx.Ebx引用您的进程中的地址时才有效,但每个进程都有自己的地址空间,您需要访问挂起进程的地址空间;为此,您需要使用ReadProcessMemory(http://msdn.microsoft.com/en-us/library/windows/desktop/ms680553%28v=vs.85%29.aspx):
ok = ReadProcessMemory(hTarget, (LPCVOID)(contx.Ebx + 8), (LPVOID)&baseAddress, sizeof baseAddress, NULL);

我只需要挂起线程的基地址,即使没有ReadProcessMemory,我认为我也能够获得它,对吗? - Hanan N.
1
@HananN。不,你不能没有ReadProcessMemory,就像我刚刚解释过的,也像Adam Rosenfield解释的一样。如果你想得到帮助,请努力阅读和理解这些解释。你尝试使用我建议的ReadProcessMemory了吗?它对你有用吗? - Jim Balter
我只是想学习和理解,所以我再次询问,因为我没有完全理解你和亚当说的话。 - Hanan N.
为什么在调用ReadProcessMemory函数时,我应该在(contx.Ebx + 8)前面添加(LPCVOID)? - Hanan N.
1
@HananN。因为,从我链接的MS文档中可以看到,该参数被声明为_In_ LPCVOID lpBaseAddress。如果您不进行强制转换,将会出现类型不匹配(contx.Ebx + 8的类型为DWORD),编译器可能会产生警告。 - Jim Balter

2
除了其他人说的内容之外,有一种更简单的方法来获取进程的PEB结构的基地址。使用NtQueryInformationProcess(),将其ProcessInformationClass参数设置为ProcessBasicInformation。输出将是一个PROCESS_BASIC_INFORMATION结构:
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;

第二个成员就是你要找的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接