【问题标题】:Access JCL Debug information contained in executable?访问可执行文件中包含的 JCL 调试信息?
【发布时间】:2011-05-16 15:31:59
【问题描述】:

有没有办法访问包含在可执行文件中的 Jedi 调试信息 (JDBG)?

Microsoft 调试工具已将我指向二进制文件中的堆栈链,我想知道这些偏移对应于哪些方法/过程/函数:

user32.dll!SendMessageA+0x4c
StackOverflow.exe+0x179263
StackOverflow.exe+0x2315b5
StackOverflow.exe+0x1fc82
StackOverflow.exe+0x50388
StackOverflow.exe+0x541fe
user32.dll!gapfnScSendMessage+0x332

显然我正在打电话给SendMessage,但我不知道从哪里来。可执行文件是使用嵌入在可执行文件中的 Jcl 调试信息构建的;但我不知道怎么读。

查看JclDebug.pas 中的一些函数和类,一切似乎都是为了获取当前进程内部的调试信息,例如:

function GetLocationInfo(const Addr: Pointer; var Info: TJclLocationInfo): Boolean;

在我当前进程的地址空间中获取一个地址。它找出地址在哪个HMODULE,例如:

  • Stackoverflow.exe
  • GDI32.dll
  • USER32.dll
  • KERNELBASE.dll
  • dwmapi.dll
  • UxTheme.dll

我在想我可以使用LoadLibrary(它返回一个HMODULE)来手动加载一个模块,然后将它提供给一些通过模块图像挖掘以获取调试信息的类:

module := LoadLibrary('C:\Users\Ian\Desktop\StackOverflow.exe');

TJclDebugInfoList = class(TObjectList)
private
   function GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;
   ...
protected
   function CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;
   ...
end;

除非它受到保护。

我正在尝试(希望)我可以编写一个工具来选择二进制文件(*.exe),输入地址,然后返回

  • 功能
  • 方法
  • 文件
  • 行号

偏移量。

例如

[002315B5] FMain.TfrmMain.lvQuestions (Line 158, "FMain.pas" + 1) + $11

可能吗?


编辑:我的第一个粗略且现成的方法是提取压缩的map 文件,以便我查看它。但它没有保存为资源(?):

虽然通用工具会更有用:


更新

我尝试使用TJclDebugInfoList;我意识到数组属性ItemFromModule 会访问受保护的方法:

function GetModuleLocationInfo(filename: string; Addr: Pointer): TJclLocationInfo;
var
   module: HMODULE;
   infoList: TJclDebugInfoList;
   infoSource: TJclDebugInfoSource;

   Address: Pointer;
   locationInfo: TJclLocationInfo;
   AddressOffset: Integer;
begin
   module := LoadLibrary(filename);
   if module = 0 then
      RaiseLastWin32Error;
   try
      infoList := TJclDebugInfoList.Create;
      try
         infoSource := infoList.ItemFromModule[module];
         if source = nil then
            raise Exception.Create('Could not find debug info source for module '+IntToStr(module));
         if not source.GetLocationInfo(Addr, {var}locationInfo) then
            raise Exception.Create('Could not get location info for address $'+IntToHex(Integer(Address), 8));

         Result := locationInfo;
      finally
         infoList.Free;
      end;
   finally
      FreeLibrary(module);
   end;
end;

除非TJclDebugInfoSource 后代类之一中的代码在尝试将其假定为虚拟地址的内容转换为偏移地址时发生下溢。

【问题讨论】:

  • 在 .map 文件中查找可能最简单
  • 澄清:您确实保留了构建此可执行文件时生成的 .map 文件,不是吗?

标签: delphi delphi-5 jedi-code-library


【解决方案1】:

使用从LoadLibrary 获得的HModule 句柄创建一个TJclDebugInfoBinary 对象。然后拨打GetLocationInfo就可以了。这就是TJclDebugInfoList 无论如何都会做的,除了它有一些帮助方法将地址从当前地址空间映射到它们对应的模块,而当你手动执行时,你必须已经知道地址属于哪个模块到。 (但是故障转储已经告诉了你那部分,所以你不需要列表类的帮助。)

您可能需要修改地址,因为崩溃时模块的基地址与您使用 LoadLibrary 加载它时的基地址不同。

JCL 调试信息未存储在资源中。它存储在名为 JCLDEBUG 的 PE 部分中。请参阅 JclDebug.pasPeMapImgFindSection32PeMapImgFindSectionFromModule 的用法。

【讨论】:

  • 我试过了。 TJclDebugInfoBinary 获取提供的地址(例如 $2315b5)并尝试调用 VAFromAddr(addr),当它尝试执行地址修复时会下溢,因为 hModule 是 $3CF0000
  • 我尝试使用TJclDebugInfoList,它没有崩溃,但它也没有返回任何正确的值。
  • 我找到了这些值不起作用的原因。 Delphi 地图文件的代码偏移量不是相对于二进制图像的开头,而是相对于图像开头的 +0x1000 字节的点。 Unthunking Borland 的 thunk,JclDebug 在我 rethunk 之前取消thunk,使偏移量对齐,我得到正确的值。
【解决方案2】:

前段时间我做了一个这样的工具,不知道能不能找回来,但至少有可能:-)

另一方面,我使用 jclDebug.pas 制作了几个工具,现在我记得:我对其进行了一些更改以使“离线”堆栈跟踪成为可能。 你可以看看这些:

实时进程堆栈查看器: http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

Minidump 阅读器(使用离线读取 .map 或从 exe 中嵌入 jdbg 信息): http://code.google.com/p/asmprofiler/source/browse/#svn%2Ftrunk%2FMiniDumpReader

【讨论】:

    【解决方案3】:

    这里的代码可以提供有关模块中地址的调试信息

    function GetModuleLocationInfo(filename: string; AddressOffset: Pointer; AssumeOffsetIsRelativeToStartOfCodeSection: Boolean=False): TJclLocationInfo;
    var
        module: HMODULE;
        infoList: TJclDebugInfoList;
        infoSource: TJclDebugInfoSource;
    
        Address: Pointer;
        locationInfo: TJclLocationInfo;
    begin
        //Code is public domain. No attribution required.
        module := LoadLibrary(PChar(filename));
        if module = 0 then
            RaiseLastWin32Error;
        try
            infoList := TJclDebugInfoList.Create;
            try
                infoSource := infoList.ItemFromModule[module];
                if infoSource = nil then
                    raise Exception.Create('Could not find debug info source for module '+IntToStr(module));
    
                DWORD(Address) := DWORD(AddressOffset) + DWORD(module) + DWORD(ModuleCodeOffset);
                if not infoSource.GetLocationInfo(Address, {var}locationInfo) then
                    raise Exception.Create('Could not get location info for address $'+IntToHex(Integer(Address), 8));
    
                Result := locationInfo;
            finally
                infoList.Free;
            end;
        finally
            FreeLibrary(module);
        end;
    end;
    

    还有一个实际的例子,来自 Process Explorer 的偏移:

    GetModuleLocationInfo('C:\Program Files (x86)\Avatar\HelpDesk.exe', 0xdcb17);
    

    返回:

    TJclLocationInfo
       Address: $266CB17
       UnitName: 'BalloonHint'
       ProcedureName: 'TBalloonHint.SetVisible'
       OffsetFromProcName: 83
       LineNumber: 281
       OffsetFromLineNumber: 0
       SourceName: 'BalloonHint.pas'
       DebugInfo: $1F25C74
    

    或者JclDebug的风格:

    [0266CB17] BalloonHint.TBalloonHint.SetVisible(第 281 行,“BalloonHint.pas”)+ $0

    【讨论】:

      猜你喜欢
      • 2022-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-16
      • 1970-01-01
      相关资源
      最近更新 更多