Skip to content

Instantly share code, notes, and snippets.

@mjsabby
Created September 22, 2023 23:47
Show Gist options
  • Save mjsabby/44eb33ab140cf7404a436c76816bf7d3 to your computer and use it in GitHub Desktop.
Save mjsabby/44eb33ab140cf7404a436c76816bf7d3 to your computer and use it in GitHub Desktop.
#include <Windows.h>
#include <DbgEng.h>
#include <DbgHelp.h>
#include <cstdio>
#include <string>
#include <vector>
template <typename T, ULONG N> constexpr ULONG countOf(T const (&)[N]) noexcept { return N; }
#define addr(x) &x[0]
#define IfFailCleanup(EXPR) do { hr = (EXPR); if(FAILED(hr)) { goto cleanup; } } while (0)
struct ModuleInfo
{
GUID Signature;
DWORD Age;
char* FileName;
char* PdbFileName;
};
typedef void(*ReportInstructionPointer)(ULONG64 instructionPointer);
typedef void(*ReportNativeModuleInfo)(const char* modulefilePath, const GUID* signature, const char* pdbFileName, DWORD age, ULONG64 eip, ULONG64 imageBase, ULONG size);
typedef struct _CV_INFO_PDB70
{
DWORD CvSignature;
GUID Signature;
DWORD Age;
char PdbFileName[1];
} CV_INFO_PDB70, * PCV_INFO_PDB70;
extern "C" __declspec(dllexport) int ExtractExceptionStack(const char* dumpFileName, const ReportNativeModuleInfo reportNativeModuleInfo, const ReportInstructionPointer reportInstructionPointer)
{
HRESULT hr;
IDebugClient7* client = nullptr;
IDebugControl7* control = nullptr;
IDebugSymbols3* symbols = nullptr;
IDebugSystemObjects4* systemObjects = nullptr;
IDebugDataSpaces4* dataspaces = nullptr;
IDebugRegisters* registers = nullptr;
std::vector<DEBUG_STACK_FRAME> frames(1024); // 1,024 frames is a lot.
std::string imageName(4096, 0); // 4096 is pretty large
IfFailCleanup(DebugCreate(__uuidof(IDebugClient7), reinterpret_cast<LPVOID*>(&client)));
IfFailCleanup(client->QueryInterface(__uuidof(IDebugDataSpaces4), reinterpret_cast<LPVOID*>(&dataspaces)));
IfFailCleanup(client->QueryInterface(__uuidof(IDebugControl7), reinterpret_cast<LPVOID*>(&control)));
IfFailCleanup(client->QueryInterface(__uuidof(IDebugSymbols5), reinterpret_cast<LPVOID*>(&symbols)));
IfFailCleanup(client->QueryInterface(__uuidof(IDebugSystemObjects4), reinterpret_cast<LPVOID*>(&systemObjects)));
IfFailCleanup(client->QueryInterface(__uuidof(IDebugRegisters2), reinterpret_cast<LPVOID*>(&registers)));
IfFailCleanup(symbols->SetSymbolOptions(SYMOPT_DEFERRED_LOADS));
IfFailCleanup(client->OpenDumpFile(dumpFileName));
IfFailCleanup(control->WaitForEvent(DEBUG_WAIT_DEFAULT, INFINITE));
if (control->IsPointer64Bit() == S_FALSE)
{
goto cleanup;
}
ULONG eventThreadId;
IfFailCleanup(systemObjects->GetEventThread(&eventThreadId));
ULONG64 instructionOffset;
IfFailCleanup(registers->GetInstructionOffset(&instructionOffset));
ULONG64 stackOffset;
IfFailCleanup(registers->GetStackOffset(&stackOffset));
ULONG64 frameOffset;
IfFailCleanup(registers->GetFrameOffset(&frameOffset));
ULONG framesFilled;
IfFailCleanup(control->GetStackTrace(frameOffset, stackOffset, instructionOffset, addr(frames), static_cast<ULONG>(frames.size()), &framesFilled));
for (auto i = 0U; i < framesFilled; ++i)
{
ULONG moduleIndex;
ULONG64 imageBase;
const auto eip = frames[i].InstructionOffset;
hr = symbols->GetModuleByOffset(eip, 0, &moduleIndex, &imageBase);
if (FAILED(hr))
{
reportInstructionPointer(eip);
}
else
{
hr = symbols->GetModuleNameString(DEBUG_MODNAME_IMAGE, moduleIndex, DEBUG_ANY_ID, addr(imageName), static_cast<ULONG>(imageName.size()), nullptr);
if (FAILED(hr))
{
continue;
}
DEBUG_MODULE_PARAMETERS moduleParameters;
hr = symbols->GetModuleParameters(1, nullptr, moduleIndex, &moduleParameters);
if (FAILED(hr))
{
continue;
}
IMAGE_DOS_HEADER dosHeader;
hr = dataspaces->ReadVirtual(imageBase, &dosHeader, sizeof(IMAGE_DOS_HEADER), nullptr);
if (FAILED(hr))
{
continue;
}
if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
continue;
}
IMAGE_NT_HEADERS64 ntHeaders;
hr = dataspaces->ReadVirtual(imageBase + dosHeader.e_lfanew, &ntHeaders, sizeof(IMAGE_NT_HEADERS64), nullptr);
if (FAILED(hr))
{
continue;
}
if (ntHeaders.Signature != IMAGE_NT_SIGNATURE)
{
continue;
}
if (ntHeaders.FileHeader.SizeOfOptionalHeader < offsetof(decltype(ntHeaders.OptionalHeader), DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) + sizeof(ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]) || ntHeaders.OptionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_DEBUG)
{
continue;
}
const auto& dataDirectory = ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
IMAGE_DEBUG_DIRECTORY debugDirectory;
if (dataDirectory.Size % sizeof(debugDirectory) != 0)
{
continue;
}
for (size_t offset = 0; offset < dataDirectory.Size; offset += sizeof(debugDirectory))
{
dataspaces->ReadVirtual(imageBase + dataDirectory.VirtualAddress + offset, &debugDirectory, sizeof(debugDirectory), nullptr);
if (debugDirectory.Type != IMAGE_DEBUG_TYPE_CODEVIEW)
{
continue;
}
if (debugDirectory.AddressOfRawData > 0)
{
if (debugDirectory.SizeOfData < sizeof(CV_INFO_PDB70))
{
continue;
}
std::vector<BYTE> data(debugDirectory.SizeOfData);
dataspaces->ReadVirtual(imageBase + debugDirectory.AddressOfRawData, data.data(), debugDirectory.SizeOfData, nullptr);
if (*reinterpret_cast<DWORD*>(data.data()) != 'SDSR')
{
continue;
}
auto* pdb = reinterpret_cast<CV_INFO_PDB70*>(data.data());
reportNativeModuleInfo(addr(imageName), &pdb->Signature, pdb->PdbFileName, pdb->Age, eip, imageBase, moduleParameters.Size);
break;
}
}
}
}
hr = 0;
cleanup:
if (systemObjects != nullptr)
{
systemObjects->Release();
systemObjects = nullptr;
}
if (dataspaces != nullptr)
{
dataspaces->Release();
dataspaces = nullptr;
}
if (registers != nullptr)
{
registers->Release();
registers = nullptr;
}
if (symbols != nullptr)
{
symbols->Release();
symbols = nullptr;
}
if (control != nullptr)
{
control->Release();
control = nullptr;
}
if (client != nullptr)
{
client->Release();
client = nullptr;
}
return hr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment