Created
September 22, 2023 23:47
-
-
Save mjsabby/44eb33ab140cf7404a436c76816bf7d3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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*>(®isters))); | |
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