获取动态链接库在进程中的加载地址

我们知道,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值做为偏移就可以了。

 

分享到: