views:

204

answers:

2

I would like to dump the fields and offsets of structures in the same way as windbg's dt command. Let's say for example I would like to dump the _PEB structure which is in the Microsoft Public symbols (since windbg's DT command works).

From MSDN documentation I understood that the SymFromName function should be able to do this, below the is the code I've tried that fails on SymFromName with LastError 126 (The specified module could not be found). From the registered Callback I get the following output:

CBA_SET_OPTIONS
CBA_SET_OPTIONS
CBA_SET_OPTIONS
CBA_EVENT: code 0 desc DBGHELP: Symbol Search Path: symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols

DBGHELP: Symbol Search Path: symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols

CBA_DEFERRED_SYMBOL_LOAD_START: C:\Windows\Sysnative\ntdll.dll
CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: C:\Windows\Sysnative\ntdll.dll
CBA_EVENT: code 0 desc DBGHELP: No header for C:\Windows\Sysnative\ntdll.dll.  Searching for image on disk

DBGHELP: No header for C:\Windows\Sysnative\ntdll.dll.  Searching for image on disk

CBA_EVENT: code 0 desc DBGHELP: C:\Windows\Sysnative\ntdll.dll - OK

DBGHELP: C:\Windows\Sysnative\ntdll.dll - OK

CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: C:\Windows\Sysnative\ntdll.dll
CBA_EVENT: code 0 desc DBGHELP: ntdll - public symbols  
         C:\Symbols\ntdll.pdb\823B51C37A764AF7BA1558B42B627FAC2\ntdll.pdb

DBGHELP: ntdll - public symbols  
         C:\Symbols\ntdll.pdb\823B51C37A764AF7BA1558B42B627FAC2\ntdll.pdb

The Code:

const
  Index: THandle =1;
  Size = (SizeOf(SYMBOL_INFO)-1 + MAX_SYM_NAME * SizeOf(TCHAR) + SizeOf(ULONG64) -1) div SizeOf(ULONG64);
var
  Symbol: String;
  Filename: String;
  Path: String;
  dwBaseAddress: DWORD;
  im: IMAGEHLP_MODULE64;
  Buffer: array[0..Size] of ULONG64;
  pSymbol: PSYMBOL_INFO;
  SymbolName: array[0..MAX_SYM_NAME-1] of Char;
begin
  ZeroMemory(@SymbolName, SizeOf(SymbolName));
  SymbolName := '_PEB';
  Filename := 'C:\Windows\Sysnative\ntdll.dll';
  Path := 'symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols';

  { Initialize }
  Win32Check(SymInitialize(Index, nil, False));
  { Register callback to get some debug info }
  Win32Check(SymRegisterCallback64(Index, DbgHelpCallback, 0));

  { Set Options }
  SymSetOptions(SymGetOptions or SYMOPT_UNDNAME);
  SymSetOptions(SymGetOptions or SYMOPT_DEBUG);
  SymSetOptions(SymGetOptions or SYMOPT_LOAD_ANYTHING);

  { Set Symbol Path }
  Win32Check(SymSetSearchPathW(Index, PChar(Path)));

  { Load Module }
  dwBaseAddress := SymLoadModuleExW(Index, 0, PChar(Filename), nil, 0, 0, nil, 0);
  Win32Check(dwBaseAddress > 0);

  ZeroMemory(@im, SizeOf(im));
  im.SizeOfStruct := SizeOf(im);
  Win32Check(SymGetModuleInfoW64(Index, dwBaseAddress, im));

  ZeroMemory(@Buffer, SizeOf(Buffer));
  pSymbol := PSYMBOL_INFO(@Buffer);
  pSymbol^.SizeOfStruct := SizeOf(SYMBOL_INFO);
  pSymbol^.MaxNameLen := MAX_SYM_NAME;

  Win32Check(SymFromNameW(Index, Symbolname, pSymbol));

  Win32Check(SymUnloadModule64(Index, dwBaseAddress));
  Win32Check(SymCleanup(Index));
A: 

I'm not exactly an expert in these things, but the leading underscore in C namemangling is sometimes supposed to be part of the binary format.

Does it work if you remove the leading underscore?

Marco van de Voort
I've tried tried already and doesn't make a difference.If I load and executable eg notepad.exe and specify WinMain as the symbol name it works though.
Remko
+1  A: 

I got it to work by using SymGetTypeFromName to get the Symbol Index and then use SymGetTypeInfo to get the details:

const
  Index: THandle =1;
  Size = (SizeOf(SYMBOL_INFO)-1 + MAX_SYM_NAME * SizeOf(TCHAR) + SizeOf(ULONG64) -1) div SizeOf(ULONG64);
var
  Filename: String;
  Path: String;
  dwBaseAddress: array[0..0] of DWORD;
  im: IMAGEHLP_MODULE64;
  Buffer: array[0..Size] of ULONG64;
  pSymbol: PSYMBOL_INFO;
  SymbolName: array[0..MAX_SYM_NAME-1] of Char;
  i: Integer;
  ChildParams: TI_FINDCHILDREN_PARAMS;
  dwOffset: DWORD;
  pSymName: PChar;
begin
  ZeroMemory(@SymbolName, SizeOf(SymbolName));
  SymbolName := '_PEB';
  Filename := 'C:\Windows\System32\ntdll.dll';
  Path := 'symsrv*symsrv.dll*C:\Symbols*http://msdl.microsoft.com/download/symbols';

  { Initialize }
  Win32Check(SymInitialize(Index, nil, False));
  { Register callback to get some debug info }
  Win32Check(SymRegisterCallback64(Index, DbgHelpCallback, 0));

  { Set Options }
  SymSetOptions(SymGetOptions or SYMOPT_UNDNAME);
  SymSetOptions(SymGetOptions or SYMOPT_DEBUG);
  SymSetOptions(SymGetOptions or SYMOPT_LOAD_ANYTHING);

  { Set Symbol Path }
  Win32Check(SymSetSearchPathW(Index, PChar(Path)));

  { Load Module }
  dwBaseAddress[0] := SymLoadModuleExW(Index, 0, PChar(Filename), nil, 0, 0, nil, 0);

  ZeroMemory(@im, SizeOf(im));

  im.SizeOfStruct := SizeOf(im);
  for i := 0 to Length(dwBaseAddress)-1 do
  begin
    SymGetModuleInfoW64(Index, dwBaseAddress[i], im);
  end;

  ZeroMemory(@Buffer, SizeOf(Buffer));
  pSymbol := PSYMBOL_INFO(@Buffer);
  pSymbol^.SizeOfStruct := SizeOf(SYMBOL_INFO);
  pSymbol^.MaxNameLen := MAX_SYM_NAME;
  { Get Type Info by Symbol Name (we need the index) }
  Win32Check(SymGetTypeFromNameW(Index, dwBaseAddress[0], SymbolName, pSymbol));

  { Get Child Count }
  ZeroMemory(@ChildParams, SizeOf(ChildParams));
  Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], pSymbol^.TypeIndex, TI_GET_CHILDRENCOUNT, @ChildParams.Count));

  { Get Child Info }
  // TODO: Caller must reserve memory for the ChildId array (Count * SizeOf(ULONG))
  Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], pSymbol^.TypeIndex, TI_FINDCHILDREN, @ChildParams));
  for i := ChildParams.Start to ChildParams.Count - 1 do
  begin
    { Get Child Name }
    Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], {pSymbol^.TypeIndex + }ChildParams.ChildId[i], TI_GET_SYMNAME, @pSymName));
    { Get Child Offset }
    Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], {pSymbol^.TypeIndex + }ChildParams.ChildId[i], TI_GET_OFFSET, @dwOffset));
    Memo1.Lines.Add(Format('+0x%.3x %s', [dwOffset, pSymName]));
    LocalFree(Cardinal(pSymName));
  end;

  for i := 0 to Length(dwBaseAddress)-1 do
  begin
    Win32Check(SymUnloadModule64(Index, dwBaseAddress[i]));
  end;
  Win32Check(SymCleanup(Index));
end;

and this is the output:

+0x000 InheritedAddressSpace
+0x001 ReadImageFileExecOptions
+0x002 BeingDebugged
+0x003 BitField
+0x003 ImageUsesLargePages
+0x003 IsProtectedProcess
+0x003 IsLegacyProcess
+0x003 IsImageDynamicallyRelocated
+0x003 SkipPatchingUser32Forwarders
+0x003 SpareBits
+0x004 Mutant
+0x008 ImageBaseAddress
+0x00C Ldr
+0x010 ProcessParameters
+0x014 SubSystemData
+0x018 ProcessHeap
+0x01C FastPebLock
+0x020 AtlThunkSListPtr
+0x024 IFEOKey
+0x028 CrossProcessFlags
+0x028 ProcessInJob
+0x028 ProcessInitializing
+0x028 ProcessUsingVEH
+0x028 ProcessUsingVCH
+0x028 ProcessUsingFTH
+0x028 ReservedBits0
+0x02C KernelCallbackTable
+0x02C UserSharedInfoPtr
+0x030 SystemReserved
+0x034 AtlThunkSListPtr32
+0x038 ApiSetMap
+0x03C TlsExpansionCounter
+0x040 TlsBitmap
+0x044 TlsBitmapBits
+0x04C ReadOnlySharedMemoryBase
+0x050 HotpatchInformation
+0x054 ReadOnlyStaticServerData
+0x058 AnsiCodePageData
+0x05C OemCodePageData
+0x060 UnicodeCaseTableData
+0x064 NumberOfProcessors
+0x068 NtGlobalFlag
+0x070 CriticalSectionTimeout
+0x078 HeapSegmentReserve
+0x07C HeapSegmentCommit
+0x080 HeapDeCommitTotalFreeThreshold
+0x084 HeapDeCommitFreeBlockThreshold
+0x088 NumberOfHeaps
+0x08C MaximumNumberOfHeaps
+0x090 ProcessHeaps
+0x094 GdiSharedHandleTable
+0x098 ProcessStarterHelper
+0x09C GdiDCAttributeList
+0x0A0 LoaderLock
+0x0A4 OSMajorVersion
+0x0A8 OSMinorVersion
+0x0AC OSBuildNumber
+0x0AE OSCSDVersion
+0x0B0 OSPlatformId
+0x0B4 ImageSubsystem
+0x0B8 ImageSubsystemMajorVersion
+0x0BC ImageSubsystemMinorVersion
+0x0C0 ActiveProcessAffinityMask
+0x0C4 GdiHandleBuffer
+0x14C PostProcessInitRoutine
+0x150 TlsExpansionBitmap
+0x154 TlsExpansionBitmapBits
+0x1D4 SessionId
+0x1D8 AppCompatFlags
+0x1E0 AppCompatFlagsUser
+0x1E8 pShimData
+0x1EC AppCompatInfo
+0x1F0 CSDVersion
+0x1F8 ActivationContextData
+0x1FC ProcessAssemblyStorageMap
+0x200 SystemDefaultActivationContextData
+0x204 SystemAssemblyStorageMap
+0x208 MinimumStackCommit
+0x20C FlsCallback
+0x210 FlsListHead
+0x218 FlsBitmap
+0x21C FlsBitmapBits
+0x22C FlsHighIndex
+0x230 WerRegistrationData
+0x234 WerShipAssertPtr
+0x238 pContextData
+0x23C pImageHeaderHash
+0x240 TracingFlags
+0x240 HeapTracingEnabled
+0x240 CritSecTracingEnabled
+0x240 SpareTracingBits

Now on to the next step: use Delphi 2010's RTTI and use this mechanism to compare offsets (this helps me converting headers for the Jedi ApiLib).

Remko
Oooops: code above had a bug in this line: Win32Check(SymGetTypeInfo(Index, dwBaseAddress[0], pSymbol^.TypeIndex, TI_FINDCHILDREN, @ChildParams));Caller is responsible for the memory so we need to reserve the memory for the ChildId array of the TI_FINDCHILDREN_PARAMS structure!
Remko