2

如何定位 Win32 进程的哪些内存区域包含每个线程的全局数据和堆栈数据?

4

3 回答 3

2

没有 API(我知道)可以做到这一点。但是如果您在进程中有一个 DLL,那么当每个线程被创建时,您将在DllMain中收到 DLL_PROCESS_ATTACH/DLL_THREAD_ATTACH 通知。当您收到这些通知时,您可以记录该线程的线程 ID 和堆栈对象的地址,因为您将在新线程上被调用。因此,将线程 ID 和堆栈地址存储在您当时创建的某个表中。不要试图在 DllMain 中做很多工作,只记录堆栈位置并返回即可。

然后,您可以使用VirtualQuery将每个线程堆栈上的变量地址转换为虚拟分配范围,这应该为您提供堆栈的基地址(请记住,堆栈从高地址增长到低地址)。堆栈的默认分配大小为 1Mb,但可以由链接器开关或线程创建者覆盖,但堆栈必须是连续的。所以你得到的VirtualQuery将是那个时间点的完整堆栈

至于堆位置 - 堆可以有多个位置,但一般来说,如果你想假设一个连续的堆位置,那么使用HeapAlloc获取堆对象的地址,然后VirtualQuery确定该部分的页面范围堆的。

或者,您可以VirtualQuery在 hModule 上为 EXE 和每个 DLL 使用。然后您可以假设任何读写且不是堆栈或模块的东西都是堆的一部分。请注意,这在大多数进程中是正确的,但在某些进程中可能不是正确的,因为应用程序可以直接调用VirtualAlloc或调用CreateFileMapping,从而导致有效的数据指针既不是来自堆栈也不是来自堆。使用EnumProcessModules获取加载到进程中的模块列表。

VirtualQuery 基本上采用随机地址,并返回该地址所属的页面集合的基地址,以及页面保护。因此,从分配“类型”的特定指针开始是有好处的。

于 2010-01-12T21:11:23.710 回答
1

全球数据

通过“全局”,我将假设您的意思是所有未使用 new、malloc、HeapAlloc、VirtualAlloc 等动态分配的数据——您可以在源代码中声明的函数之外和类定义之外的数据.

您可以通过将每个 DLL 作为 PE 文件加载到 PE 文件阅读器中并确定 .data 和 .bss 部分的位置来定位这些部分(对于不同的编译器,它们可能具有不同的名称)。您需要为每个 DLL 执行此操作。这为您提供了每个 DLL 的此数据的一般位置。然后,如果您有调试信息,或者没有 MAP 文件,您可以将 DLL 地址映射到调试信息/映射文件信息,以获取每个变量的名称和确切位置。

您可能会发现PE 格式 DLL帮助您执行此任务比自己编写代码来查询 PE 文件要容易得多。

线程堆栈

使用 ToolHelp32(或 PSAPI 库,如果在 Windows NT 4 上)枚举应用程序中的线程。对于每个线程,获取线程上下文并读取 ESP 寄存器(RSP for x64)。现在对从每个上下文读取的 ESP/RSP 寄存器中的地址执行 VirtualQuery。该地址周围的 1MB(默认值)区域(从 mbi.AllocationBase 开始,向上 1MB)是堆栈位置。请注意,堆栈大小可能不是 1MB,如果您愿意,可以从启动线程的 DLL/EXE 的 PE 头中查询。

编辑,修复我交换了一些寄存器名称的错字,谢谢@interjay

于 2010-05-12T17:27:29.173 回答
1

获取您感兴趣的内存区域中分配的变量的地址。当你拥有这些地址时,你如何处理它们完全是另一个问题。

您也可以objdump -h(我认为是-h,可能是-x)列出节地址,包括数据节。

于 2010-01-12T17:15:21.297 回答