177 lines
6.3 KiB
C++
177 lines
6.3 KiB
C++
|
|
#include "crashdump.h"
|
||
|
|
|
||
|
|
#include <iomanip>
|
||
|
|
#include <sstream>
|
||
|
|
|
||
|
|
CrashDump::CrashDump(EXCEPTION_POINTERS* exp) : exp_(exp)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string CrashDump::GetModuleByReAddr(PBYTE retAddr, PBYTE& moduleAddr)
|
||
|
|
{
|
||
|
|
MODULEENTRY32 M = {sizeof(M)};
|
||
|
|
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
|
||
|
|
char moduleName[MAX_PATH]{};
|
||
|
|
if (hSnapshot != INVALID_HANDLE_VALUE && Module32First(hSnapshot, &M)) {
|
||
|
|
do {
|
||
|
|
if (DWORD(retAddr - M.modBaseAddr) < M.modBaseSize) {
|
||
|
|
std::snprintf(moduleName, MAX_PATH, "%s", M.szExePath);
|
||
|
|
moduleAddr = M.modBaseAddr;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} while (Module32Next(hSnapshot, &M));
|
||
|
|
}
|
||
|
|
std::string name(moduleName);
|
||
|
|
return name;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string CrashDump::GetVersion()
|
||
|
|
{
|
||
|
|
OSVERSIONINFO V = {sizeof(OSVERSIONINFO)};
|
||
|
|
if (!GetVersionEx((POSVERSIONINFO)&V)) {
|
||
|
|
ZeroMemory(&V, sizeof(V));
|
||
|
|
V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||
|
|
GetVersionEx((POSVERSIONINFO)&V);
|
||
|
|
}
|
||
|
|
if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) {
|
||
|
|
V.dwBuildNumber = LOWORD(V.dwBuildNumber);
|
||
|
|
}
|
||
|
|
char buffer[512]{};
|
||
|
|
std::snprintf(buffer, sizeof(buffer), "Windows: %ld.%ld.%ld, %s.", V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber,
|
||
|
|
V.szCSDVersion);
|
||
|
|
std::string info(buffer);
|
||
|
|
return info;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string CrashDump::GetExceptionInfo()
|
||
|
|
{
|
||
|
|
char moduleName[MAX_PATH]{};
|
||
|
|
PBYTE moduleAddr{};
|
||
|
|
GetModuleFileName(NULL, moduleName, MAX_PATH);
|
||
|
|
if (exp_ == nullptr) {
|
||
|
|
return "Not Set Exception Pointer.";
|
||
|
|
}
|
||
|
|
std::ostringstream oss;
|
||
|
|
auto& E = exp_->ExceptionRecord;
|
||
|
|
auto& C = exp_->ContextRecord;
|
||
|
|
oss << "ExceptionAddr: " << E->ExceptionAddress << "\n";
|
||
|
|
oss << "ExceptionCode: " << E->ExceptionCode << "\n";
|
||
|
|
if (E->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
|
||
|
|
oss << (E->ExceptionInformation[0] ? "Write " : "Read ");
|
||
|
|
oss << E->ExceptionInformation[1] << "\n";
|
||
|
|
}
|
||
|
|
oss << "Instruction:";
|
||
|
|
for (int i = 0; i < 16; i++) {
|
||
|
|
oss << " " << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(PBYTE(E->ExceptionAddress)[i]);
|
||
|
|
}
|
||
|
|
oss << std::hex << std::setfill('0');
|
||
|
|
|
||
|
|
// 1. 基本寄存器组
|
||
|
|
oss << "\nGeneral Purpose Registers:"
|
||
|
|
<< "\nRAX: " << std::setw(16) << C->Rax << " RBX: " << std::setw(16) << C->Rbx << " RCX: " << std::setw(16) << C->Rcx
|
||
|
|
<< " RDX: " << std::setw(16) << C->Rdx
|
||
|
|
|
||
|
|
<< "\nRSI: " << std::setw(16) << C->Rsi << " RDI: " << std::setw(16) << C->Rdi << " RBP: " << std::setw(16) << C->Rbp
|
||
|
|
<< " RSP: " << std::setw(16) << C->Rsp
|
||
|
|
|
||
|
|
<< "\nR8: " << std::setw(16) << C->R8 << " R9: " << std::setw(16) << C->R9 << " R10: " << std::setw(16) << C->R10
|
||
|
|
<< " R11: " << std::setw(16) << C->R11
|
||
|
|
|
||
|
|
<< "\nR12: " << std::setw(16) << C->R12 << " R13: " << std::setw(16) << C->R13 << " R14: " << std::setw(16) << C->R14
|
||
|
|
<< " R15: " << std::setw(16) << C->R15;
|
||
|
|
|
||
|
|
// 2. 关键控制寄存器
|
||
|
|
oss << "\n\nControl Registers:"
|
||
|
|
<< "\nRIP: " << std::setw(16) << C->Rip << " EFLAGS: " << std::setw(8) << C->EFlags << " CS: " << std::setw(4)
|
||
|
|
<< C->SegCs << " SS: " << std::setw(4) << C->SegSs;
|
||
|
|
|
||
|
|
// 3. 调试寄存器(如果有用)
|
||
|
|
if (C->ContextFlags & CONTEXT_DEBUG_REGISTERS) {
|
||
|
|
oss << "\n\nDebug Registers:"
|
||
|
|
<< "\nDR0: " << std::setw(16) << C->Dr0 << " DR1: " << std::setw(16) << C->Dr1 << " DR2: " << std::setw(16)
|
||
|
|
<< C->Dr2 << " DR3: " << std::setw(16) << C->Dr3 << "\nDR6: " << std::setw(16) << C->Dr6
|
||
|
|
<< " DR7: " << std::setw(16) << C->Dr7;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. MXCSR状态(浮点/SSE)
|
||
|
|
if (C->ContextFlags & CONTEXT_FLOATING_POINT) {
|
||
|
|
oss << "\n\nFPU/SSE State:"
|
||
|
|
<< "\nMXCSR: " << std::setw(8) << C->MxCsr;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 5. 最后分支记录(如果可用)
|
||
|
|
if (C->LastBranchToRip || C->LastBranchFromRip) {
|
||
|
|
oss << "\n\nLast Branch:"
|
||
|
|
<< "\nFrom: " << std::setw(16) << C->LastBranchFromRip << " To: " << std::setw(16) << C->LastBranchToRip;
|
||
|
|
}
|
||
|
|
|
||
|
|
oss << GetCallStack(exp_) << "\n";
|
||
|
|
|
||
|
|
return oss.str();
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string CrashDump::GetCallStack(EXCEPTION_POINTERS* exp)
|
||
|
|
{
|
||
|
|
struct StackFrame {
|
||
|
|
StackFrame* prev_frame;
|
||
|
|
void* return_address;
|
||
|
|
};
|
||
|
|
|
||
|
|
std::ostringstream oss;
|
||
|
|
StackFrame* current_frame = nullptr;
|
||
|
|
|
||
|
|
// 1. 初始化栈帧指针
|
||
|
|
if (exp) {
|
||
|
|
// 异常情况:从异常上下文中获取栈帧
|
||
|
|
#if defined(_M_X64) || defined(__x86_64__)
|
||
|
|
current_frame = reinterpret_cast<StackFrame*>(exp->ContextRecord->Rbp);
|
||
|
|
#else
|
||
|
|
current_frame = reinterpret_cast<StackFrame*>(exp->ContextRecord->Ebp);
|
||
|
|
#endif
|
||
|
|
} else {
|
||
|
|
// 非异常情况:手动获取当前栈帧(编译器相关)
|
||
|
|
#if defined(__GNUC__) || defined(__clang__)
|
||
|
|
// GCC/Clang 内联汇编获取 EBP/RBP
|
||
|
|
#if defined(__x86_64__)
|
||
|
|
register void* frame_ptr asm("rbp");
|
||
|
|
#else
|
||
|
|
register void* frame_ptr asm("ebp");
|
||
|
|
#endif
|
||
|
|
current_frame = reinterpret_cast<StackFrame*>(frame_ptr);
|
||
|
|
#elif defined(_MSC_VER)
|
||
|
|
// MSVC 内置函数(如果可用)
|
||
|
|
current_frame = reinterpret_cast<StackFrame*>(_AddressOfReturnAddress() - sizeof(void*));
|
||
|
|
#else
|
||
|
|
return "Stack walking not supported on this compiler";
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. 遍历调用栈
|
||
|
|
constexpr size_t max_frames = 64;
|
||
|
|
PBYTE module_addr = nullptr;
|
||
|
|
for (size_t i = 0; i < max_frames && current_frame; ++i) {
|
||
|
|
// 安全检查
|
||
|
|
if (IsBadReadPtr(current_frame, sizeof(StackFrame)) ||
|
||
|
|
IsBadCodePtr(reinterpret_cast<FARPROC>(current_frame->return_address))) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 格式化输出
|
||
|
|
oss << "\n0x" << std::hex << std::setw(8) << std::setfill('0')
|
||
|
|
<< reinterpret_cast<uintptr_t>(current_frame->return_address) << " ";
|
||
|
|
|
||
|
|
// 获取模块信息
|
||
|
|
std::string module_name = GetModuleByReAddr(reinterpret_cast<PBYTE>(current_frame->return_address), module_addr);
|
||
|
|
if (!module_name.empty()) {
|
||
|
|
DWORD rva = static_cast<DWORD>(reinterpret_cast<PBYTE>(current_frame->return_address) - module_addr);
|
||
|
|
// 输出模块信息和RVA
|
||
|
|
oss << " (RVA: 0x" << std::hex << std::setw(8) << std::setfill('0') << rva << ") " << module_name;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 移动到上一栈帧
|
||
|
|
current_frame = current_frame->prev_frame;
|
||
|
|
}
|
||
|
|
|
||
|
|
return oss.str();
|
||
|
|
}
|