我们知道,Windows下DLL被加载到进程中时,可能发生地址冲突而被重定位,那么我们如何获取这些进程的重定位地址呢?实际上很简单,通过GetModuleHandle函数可以获取到已经被加载到内存中的DLL映像的句柄,这个句柄的值就是DLL被加载的映像基准地址。要列举出所有的模块映像加载地址,用EnumProcessModules就可以了。
下面引用QMapSymbols中的一段代码给大家参考:
var AHandles: array of THandle; ACount: Cardinal; I, J: Integer; AMapFile: TQSymbolMapFile; AFile: TFileStream; AImports: TStringList; begin // 枚举已加载动态链接库导入函数 ACount := 0; EnumProcessModules(GetCurrentProcess, nil, 0, ACount); SetLength(AHandles, ACount); if EnumProcessModules(GetCurrentProcess, @AHandles[0], ACount, ACount) then begin for I := 0 to ACount - 1 do begin if (AHandles[I] <> 0) and (AHandles[I] <> MainInstance) then begin //此时,AHandles列表就是每个DLL的实际加载地址,用GetModuleFileName函数就可以获取到句柄对应的文件名 end; end; ...
QMapSymbols为了实现DLL中导出函数名的映射,还加入了一些代码来解析PE格式,从中获取映像的代码段偏移和代码段大小,实际的代码段起始地址,是模块句柄地址+代码段偏移,这样子就可以得到特定的DLL代码在内存中的分布地址范围为:
模块句柄值+代码段偏移 ~ 模块句柄值+代码段偏移+代码段大小
然后我们再从PE文件头部中读取导入函数列表,然后将导入函数地址保存起来,以便与请求的地址进行比较就可以知道代码来自那个函数。具体代码参考LoadPEImports和ReadImports函数,主要其中牵涉到虚拟地址到文件偏移地址的计算,有点小闹心,好在解决掉了,使用的算法是直接用IMAGE_SECTION_HEADER的PointerToRawData – VirtualAddress值做为偏移就可以了。