如何找到Win32进程的哪些内存区域包含每个线程的全局数据和堆栈数据?
答案 0 :(得分:2)
没有API(我知道)可以做到这一点。但是如果您在进程中有DLL,则在创建每个线程时,您将在DllMain中获得DLL_PROCESS_ATTACH / DLL_THREAD_ATTACH通知。当您收到这些通知时,您可以记录该线程的线程ID和堆栈对象的地址,因为您将在新线程上调用。因此,将线程ID和堆栈地址存储在您当时创建的某个表中。不要尝试在DllMain中做很多工作,只记录堆栈位置并返回。
然后,您可以使用VirtualQuery将每个线程堆栈上的变量地址转换为虚拟分配范围,这应该为您提供堆栈的基址(请记住堆栈从高地址变为低地址)地址)。堆栈的默认分配大小为1Mb,但可以由链接器开关或线程创建者覆盖,但堆栈必须是连续的。那么从VirtualQuery
返回的内容将是该时间点的完整堆栈
至于堆位置 - 堆可以有多个位置,但一般情况下,如果要假设一个连续的堆位置,则使用HeapAlloc获取堆对象的地址,然后{{ 1}}确定堆的该部分的页面范围。
或者,您可以在hModule上为EXE和每个DLL使用VirtualQuery
。然后你可以假设读写的任何东西都不是堆栈或模块是堆的一部分。请注意,在大多数进程中都是如此,但在某些进程中可能不正确,因为应用程序可以直接调用VirtualQuery
或VirtualAlloc
,从而产生不是来自堆栈或堆的有效数据指针。
使用EnumProcessModules获取加载到进程中的模块列表。
VirtualQuery基本上采用随机地址,并返回该地址所属页面集合的基址,以及页面保护。因此,从一个“类型”分配的特定指针开始是有好处的。
答案 1 :(得分:1)
获取您感兴趣的内存区域中分配的变量的地址。如果你拥有地址,你对地址的处理完全是另一个问题。
您还可以objdump -h
(我认为它是-h,可能是-x)列出部分地址,包括数据部分。
答案 2 :(得分:1)
全球数据
通过“Global”我将假设您指的是所有未使用new,malloc,HeapAlloc,VirtualAlloc等动态分配的数据 - 您可以在源代码中声明的数据,这些数据不在函数之外类定义。
您可以通过在PE文件读取器中将每个DLL作为PE文件加载并确定.data和.bss部分的位置来定位这些部分(这些部分可能具有不同编译器的不同名称)。您需要为每个DLL执行此操作。这为您提供了每个DLL的此数据的一般位置。然后,如果您有调试信息或失败的MAP文件,您可以根据调试信息/ mapfile信息映射DLL地址,以获取每个变量的名称和确切位置。
您可能会发现PE Format DLL比编写代码自行查询PE文件更容易帮助您执行此任务。
线程堆栈
使用ToolHelp32(或Windows NT 4上的PSAPI库)枚举应用程序中的线程。对于每个线程,获取线程上下文并读取ESP寄存器(RSP for x64)。现在对每个上下文中读取的ESP / RSP寄存器中的地址执行VirtualQuery。该地址周围的1MB(默认值)区域(从mbi.AllocationBase开始,向上工作1MB)是堆栈位置。请注意,堆栈大小可能不是1MB,如果您愿意,可以从启动该线程的DLL / EXE的PE头中进行查询。
编辑,修复了我换掉一些注册名称的错误,谢谢@interjay