Skip to content

Instantly share code, notes, and snippets.

@HoShiMin
Created September 24, 2018 21:03
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save HoShiMin/779d1c5e96e50a653ca43511b7bcb69a to your computer and use it in GitHub Desktop.
Save HoShiMin/779d1c5e96e50a653ca43511b7bcb69a to your computer and use it in GitHub Desktop.
PDB symbols downloader and parser
#include <windows.h>
#include <vector>
#include <string>
#include "SymParser.h"
// Using Wide-versions of DbgHelp functions:
#define DBGHELP_TRANSLATE_TCHAR
// Expose additional declarations from DbgHelp.h:
#define _NO_CVCONST_H
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
SymParser::SymParser(OPTIONAL LPCWSTR SymbolsPath)
: Initialized(FALSE), hProcess(GetCurrentProcess()), ModuleBase(NULL)
{
Initialized = SymInitialize(
hProcess,
SymbolsPath ? SymbolsPath : DefaultSymbolsPath,
FALSE
);
}
SymParser::~SymParser() {
if (Initialized) SymCleanup(hProcess);
}
BOOL SymParser::LoadModule(LPCWSTR ModulePath, OPTIONAL DWORD64 ImageBase, OPTIONAL DWORD ImageSize) {
ModuleBase = SymLoadModuleEx(hProcess, NULL, ModulePath, NULL, ImageBase, ImageSize, NULL, 0);
return ModuleBase != NULL;
}
std::wstring SymParser::GetSymName(ULONG Index, OPTIONAL OUT PBOOL Status) {
LPCWSTR Name = NULL;
if (SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_SYMNAME, &Name) && Name) {
std::wstring SymName = Name;
VirtualFree(const_cast<LPWSTR>(Name), 0, MEM_RELEASE);
if (Status) *Status = TRUE;
return SymName;
}
if (Status) *Status = FALSE;
return L"";
}
std::wstring SymParser::GetSymTypeName(ULONG Index, OPTIONAL OUT PUINT64 BaseTypeSize, OPTIONAL OUT PBOOL Status) {
if (!Index) return L"";
UINT64 SymSize = GetSymSize(Index, Status);
if (BaseTypeSize) *BaseTypeSize = SymSize;
std::wstring TypeName = GetSymName(Index, Status);
if (!TypeName.empty()) return TypeName;
enum SymTagEnum Tag = GetSymTag(Index, Status);
switch (Tag) {
case SymTagBaseType: {
enum SymParser::BasicType Type = GetSymBaseType(Index, Status);
switch (Type) {
case btNoType:
TypeName = L"NO_TYPE";
break;
case btVoid:
TypeName = L"VOID";
break;
case btChar:
TypeName = L"CHAR";
break;
case btWChar:
TypeName = L"WCHAR";
break;
case btInt:
TypeName = SymSize == sizeof(INT64) ? L"INT64" : L"INT";
break;
case btUInt:
TypeName = SymSize == sizeof(UINT64) ? L"UINT64" : L"UINT";
break;
case btFloat:
TypeName = L"float";
break;
case btBCD:
TypeName = L"BCD"; // Binary-coded decimal
break;
case btBool:
TypeName = L"BOOL";
break;
case btLong:
TypeName = SymSize == sizeof(LONGLONG) ? L"LONGLONG" : L"LONG";
break;
case btULong:
TypeName = SymSize == sizeof(ULONGLONG) ? L"ULONGLONG" : L"ULONG";
break;
case btCurrency:
TypeName = L"CurrencyType"; // ???
break;
case btDate:
TypeName = L"DateType"; // ???
break;
case btVariant:
TypeName = L"VariantType"; // ???
break;
case btComplex:
TypeName = L"ComplexType"; // ???
break;
case btBit:
TypeName = L"Bit";
break;
case btBSTR:
TypeName = L"BSTR"; // Binary string
break;
case btHresult:
TypeName = L"HRESULT";
break;
}
break;
}
case SymTagPointerType: {
ULONG Type = GetSymType(Index, Status);
TypeName = GetSymTypeName(Type, BaseTypeSize, Status) + L"*";
break;
}
case SymTagArrayType: {
ULONG Type = GetSymArrayTypeId(Index, Status);
TypeName = GetSymTypeName(Type, BaseTypeSize, Status);
break;
}
default: {
ULONG Type = GetSymType(Index, Status);
TypeName = GetSymTypeName(Type, BaseTypeSize, Status);
}
}
return TypeName;
}
UINT64 SymParser::GetSymSize(ULONG Index, OPTIONAL OUT PBOOL Status) {
UINT64 Size = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_LENGTH, &Size);
if (Status) *Status = SymStatus;
return Size;
}
ULONG SymParser::GetSymOffset(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG Offset = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_OFFSET, &Offset);
if (Status) *Status = SymStatus;
return Offset;
}
ULONG SymParser::GetSymAddressOffset(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG Offset = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_ADDRESSOFFSET, &Offset);
if (Status) *Status = SymStatus;
return Offset;
}
ULONG SymParser::GetSymBitPosition(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG BitPosition = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_BITPOSITION, &BitPosition);
if (Status) *Status = SymStatus;
return BitPosition;
}
ULONG SymParser::GetSymTypeId(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG TypeId = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_TYPEID, &TypeId);
if (Status) *Status = SymStatus;
return TypeId;
}
ULONG SymParser::GetSymArrayTypeId(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG TypeId = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_ARRAYINDEXTYPEID, &TypeId);
if (Status) *Status = SymStatus;
return TypeId;
}
enum SymTagEnum SymParser::GetSymTag(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG Tag = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_SYMTAG, &Tag);
if (Status) *Status = SymStatus;
return static_cast<enum SymTagEnum>(Tag);
}
enum SymParser::BasicType SymParser::GetSymType(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG Type = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_TYPE, &Type);
if (Status) *Status = SymStatus;
return static_cast<enum BasicType>(Type);
}
enum SymParser::BasicType SymParser::GetSymBaseType(ULONG Index, OPTIONAL OUT PBOOL Status) {
ULONG BasicType = 0;
BOOL SymStatus = SymGetTypeInfo(hProcess, ModuleBase, Index, TI_GET_BASETYPE, &BasicType);
if (Status) *Status = SymStatus;
return static_cast<enum BasicType>(BasicType);
}
BOOL SymParser::DumpSymbol(LPCWSTR SymbolName, OUT SYM_INFO& SymInfo) {
SymInfo = {};
// Obtaining root symbol:
const ULONG SymNameLength = 128;
const ULONG SymInfoSize = sizeof(SYMBOL_INFO) + SymNameLength * sizeof(WCHAR);
std::vector<BYTE> RootSymbolInfoBuffer(SymInfoSize);
auto RootSymbolInfo = reinterpret_cast<PSYMBOL_INFO>(&RootSymbolInfoBuffer[0]);
RootSymbolInfo->SizeOfStruct = SymInfoSize;
BOOL Status = SymGetTypeFromName(hProcess, ModuleBase, SymbolName, RootSymbolInfo);
if (!Status) return FALSE;
ULONG RootIndex = RootSymbolInfo->Index;
SymInfo.Name = GetSymName(RootIndex);
SymInfo.Size = GetSymSize(RootIndex);
SymInfo.Offset = GetSymOffset(RootIndex, &Status);
if (!Status) SymInfo.Offset = GetSymAddressOffset(RootIndex);
// Obtaining root symbol children count:
ULONG ChildrenCount = 0;
Status = SymGetTypeInfo(hProcess, ModuleBase, RootIndex, TI_GET_CHILDRENCOUNT, &ChildrenCount);
if (!Status) return FALSE;
SymInfo.Name = SymbolName;
SymGetTypeInfo(hProcess, ModuleBase, RootIndex, TI_GET_LENGTH, &SymInfo.Size);
if (ChildrenCount) {
// Obtaining children indices:
std::vector<BYTE> FindChildrenParamsBuffer(sizeof(TI_FINDCHILDREN_PARAMS) + ChildrenCount * sizeof(ULONG));
auto Children = reinterpret_cast<TI_FINDCHILDREN_PARAMS*>(&FindChildrenParamsBuffer[0]);
Children->Count = ChildrenCount;
Status = SymGetTypeInfo(hProcess, ModuleBase, RootIndex, TI_FINDCHILDREN, Children);
if (!Status) return FALSE;
for (unsigned int i = 0; i < ChildrenCount; i++) {
SYM_CHILD_ENTRY Entry = {};
ULONG ChildIndex = Children->ChildId[i];
ULONG TypeId = GetSymTypeId(ChildIndex);
Entry.Name = GetSymName(ChildIndex);
Entry.Size = GetSymSize(TypeId);
Entry.Offset = GetSymOffset(ChildIndex);
Entry.BitPosition = GetSymBitPosition(ChildIndex, &Entry.IsBitField);
UINT64 BaseTypeSize = 0;
Entry.TypeName = GetSymTypeName(TypeId, &BaseTypeSize);
Entry.ElementsCount = BaseTypeSize != 0 ? Entry.Size / BaseTypeSize : 1;
if (Entry.Name.empty()) Entry.Name = L"UNKNOWN_NAME";
if (Entry.TypeName.empty()) Entry.TypeName = L"UNKNOWN_TYPE";
SymInfo.Entries.emplace_back(Entry);
}
}
return TRUE;
}
#pragma once
class SymParser {
private:
// From cvconst.h:
enum BasicType {
btNoType = 0,
btVoid = 1,
btChar = 2,
btWChar = 3,
btInt = 6,
btUInt = 7,
btFloat = 8,
btBCD = 9,
btBool = 10,
btLong = 13,
btULong = 14,
btCurrency = 25,
btDate = 26,
btVariant = 27,
btComplex = 28,
btBit = 29,
btBSTR = 30,
btHresult = 31
};
BOOL Initialized;
HANDLE hProcess;
DWORD64 ModuleBase;
LPCWSTR DefaultSymbolsPath = L"srv*C:\\Symbols*https://msdl.microsoft.com/download/symbols";
std::wstring GetSymName(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
std::wstring GetSymTypeName(ULONG Index, OPTIONAL OUT PUINT64 BaseTypeSize = NULL, OPTIONAL OUT PBOOL Status = NULL);
UINT64 GetSymSize(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
ULONG GetSymOffset(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
ULONG GetSymAddressOffset(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
ULONG GetSymBitPosition(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
ULONG GetSymTypeId(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
ULONG GetSymArrayTypeId(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
enum SymTagEnum GetSymTag(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
enum BasicType GetSymType(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
enum BasicType GetSymBaseType(ULONG Index, OPTIONAL OUT PBOOL Status = NULL);
public:
using SYM_CHILD_ENTRY = struct {
std::wstring Name;
std::wstring TypeName;
UINT64 ElementsCount;
UINT64 Size;
ULONG Offset;
BOOL IsBitField;
ULONG BitPosition;
};
using SYM_INFO = struct {
std::wstring Name;
UINT64 Size;
ULONG Offset;
std::vector<SYM_CHILD_ENTRY> Entries;
};
SymParser(OPTIONAL LPCWSTR SymbolsPath = NULL);
~SymParser();
BOOL IsInitialized() const { return Initialized; }
// Load symbols for specified module (*.exe/*.dll/*.sys etc.):
BOOL LoadModule(LPCWSTR ModulePath, OPTIONAL DWORD64 ImageBase = NULL, OPTIONAL DWORD ImageSize = 0);
BOOL DumpSymbol(LPCWSTR SymbolName, OUT SYM_INFO& SymInfo);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment