1、MiniDump文件的创建:
创建miniDump的方法有很多。可以通过MiniDumpCreateDumpWin32Api创建。必要参数为EXCEPTION_POINTERS结构,获取这个结构可以通过在异常出通过GetExceptionInfomation函数API获取这个结构,也可以通过SetUnhandleExceptionFilter函数设置自定义异常处理函数(注意:这个函数是处理系统无法处理的异常时触发的),这个自定义异常处理函数的函数原型为
typedef LONG (WINAPI *PTOP_LEVEL_EXCEPTION_FILTER)(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo);
这个里面的参数为系统传入的,其中包括了异常信息,以及上下文句柄。
而写Dump的函数原型为:
BOOL MiniDumpWriteDump( HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam );
其中需要主要的是第三个参数和 第四个参数:第三个参数表示需要在dump文件中记录什么信息。详细解释见MSDN,第四个参数从众多网上例子来看很多都没有在意这个参数。这个参数的结构体原型如下:
typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
DWORD ThreadId;
PEXCEPTION_POINTERS ExceptionPointers;
BOOL ClientPointers;
} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
ClientPointers指针这个参数到底什么时候为TRUE什么时候为FALSE呢?
MSDN的解释为如果调试进程在内存中为true,否则为false,如果错误地址所在进程在内存中,应该为false,否则为true,总结一句话为,如果要记录内存中的地址相关信息(包括堆栈,偏移地址,断片地址,节表信息等等)应该为FALSE,其他为true。这个参数决定着dump文件的价值。
分析堆栈信息
堆栈信息可以帮助我们快速定位程序所谓位置以及错误发生的原因。有利于程序的错误修复,加快工作效率。如果获取异常程序的堆栈信息呢?
在设置自定义异常处理函数时,传入的异常信息结构中包括了一个CONTEXT结构体的指针。这个指针就是上下文句柄。
这个结构体的原型如下:
typedef struct _CONTEXT {
RD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
这个结构体的详细解释见MSDN。
通过这个结构我们里面的相关信息就可以获取异常程序的堆栈信息。获取堆栈信息的API 函数原型如下:
BOOL IMAGEAPI StackWalk64( DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress );
第一个参数:表示本机芯片类型,芯片不同也表明取值不一样。
第四个参数表示获取堆栈信息所需要的一些关键信息。
其他参数详情见MSDN。
举例如下:这个例子网上很多,详细信息请将CONTEXT结构和STACKFRAME64结构结合学习。
unsigned long dwImageType=IMAGE_FILE_MACHINE_I386;//设置默认芯片类型为X86
#ifdef _M_IX86
sf.AddrPC.Offset = c.Eip;//当芯片类型为X86是,程序计数器地址的偏移量(或者首地址)取值为CONTEXT结构的Eip成员
sf.AddrPC.Mode = AddrModeFlat;//平面寻址//详细解释见MSDN
sf.AddrStack.Offset = c.Esp;//堆栈偏移
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = c.Ebp;//帧偏移
sf.AddrFrame.Mode = AddrModeFlat;
#elif _M_X64
dwImageType = IMAGE_FILE_MACHINE_AMD64;
sf.AddrPC.Offset = c.Rip;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrFrame.Offset = c.Rbp;
sf.AddrFrame.Mode = AddrModeFlat;
sf.AddrStack.Offset = c.Rsp;
sf.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
dwImageType = IMAGE_FILE_MACHINE_IA64;
sf.AddrPC.Offset = c.StIIP;
sf.AddrPC.Mode = AddrModeFlat;
sf.AddrFrame.Offset = c.IntSp;
sf.AddrFrame.Mode = AddrModeFlat;
sf.AddrBStore.Offset = c.RsBSP;
sf.AddrBStore.Mode = AddrModeFlat;
sf.AddrStack.Offset = c.IntSp;
sf.AddrStack.Mode = AddrModeFlat;
#endif
获取错误函数API
SymGetSymFromAddr64(
_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr,
_Out_opt_ PDWORD64 pdwDisplacement,
_Inout_ PIMAGEHLP_SYMBOL64 Symbol
);
获取错误错误行的API
BOOL
IMAGEAPI
SymGetLineFromAddr64(
_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr,
_Out_ PDWORD pdwDisplacement,
_Out_ PIMAGEHLP_LINE64 Line64
);
//获取错误模块
BOOL
IMAGEAPI
SymGetModuleInfo64(
_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr,
_Out_ PIMAGEHLP_MODULE64 ModuleInfo
);
这三个API的参数详细解释见MSDN,
第二个参数的取值为为当前程序的程序计时器偏移地址,表示从当前程序的主线程开始获取错误信息。
符号表:
symbolTable是分析dump文件的关键所在,symbol可以帮助更加准确的定位程序错误。因此,在获取堆栈信息获取生成dump文件之前绑定符号表是必须的。绑定的符号表的API如下:
BOOL IMAGEAPI SymInitialize( HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess );
详细解释见MSDN,这里详细讲解第二个参数,符号表路径,如何设置符号表路径呢?可以设置为NULL,具体解释见MSDN,
但如果不为空如何加载呢?
示例如下:
#define SYMPATH "%SYSTEMROOT%;.\\Symbols\\;..\\Symbols\\;..\\..\\Symbols\\;..\\..\\..\\Symbols\\"
ExpandEnvironmentStringsA(SYMPATH, symPath, sizeof(symPath));
这个API的功能是扩展环境变量的取值,通俗一点将就是获取变量值。
至于字符串是什么含义,请查询环境变量相关变量含义。
查询异常处理信息:
首先查询异常信息需要一定的windebug基础。
https://blog.csdn.net/sunboyhch/article/details/37914727
查询具体异常的一般流程为:
miniDump的具体操作流程是这样:
1、打开异常dump文件
2、导入pdb符号文件
3、输入!analyze -v 命令分析出错原因,堆栈信息、出错位置,以及包含错误位置的文件。
4、然后通过文件菜单打开文件。
5、输入.ecxr 关联
6、kp命令。输出详细的错误信息。
第二种方法是
1、直接输入.excr直接通过dump文件的记录的索引关联源文件。
2、输入kp命令查看详细的堆栈错误信息
在异常处理的过程中。还需要了解一些其他的知识,如dos文件头、PE文件格式等等。推荐博客链接如下:
https://blog.csdn.net/chenlycly/article/details/53378196
bool CrashHandleCls::GetLogicalAddress(PVOID pLinearAddress,PTSTR pszModule,DWORD dwModuleNameLen,DWORD_PTR& dwSection,DWORD_PTR& dwOffset){
MEMORY_BASIC_INFORMATION mbi;
if(!::VirtualQuery(pLinearAddress,&mbi,sizeof(mbi)))return false;
DWORD_PTR hModule=(DWORD_PTR)mbi.AllocationBase;//获取页面起始地址
//验证访问权限
if(IsBadReadPtr((const void *)hModule,sizeof(IMAGE_DOS_HEADER))) {
_tcsncpy(pszModule,_T("Unknown"),dwModuleNameLen);
dwSection=0;
dwOffset=0;
return false;
}
//获取完整的路径
if(!::GetModuleFileNameW((HMODULE)hModule,pszModule,dwModuleNameLen))return false;
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hModule;//转换为DOS文件结构
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hModule + pDosHdr->e_lfanew); //获取PE文件的文件头 PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);//定位区块表(节表)
DWORD_PTR dwRVA = (DWORD_PTR)pLinearAddress - hModule;//获取偏移地址
//pNtHdr->FileHeader.NumberOfSections 获取节表总数
for(UINT i = 0; i < pNtHdr->FileHeader.NumberOfSections; i++, pSection++)
{
DWORD_PTR dwSectionStart = pSection->VirtualAddress;//获取目标在内存中的开始地址(节区的 RVA 地址)
DWORD_PTR dwSectionEnd = dwSectionStart +
max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);//获取目标在内存中的结束地
if((dwRVA >= dwSectionStart) && (dwRVA <= dwSectionEnd)) {
dwSection = i + 1;
dwOffset = dwRVA - dwSectionStart;//获取偏移量
return TRUE;
}
}
dwSection = 0;
dwOffset = 0;
return FALSE;
}

本文详细介绍了如何创建MiniDump文件,分析堆栈信息以定位错误,以及查看异常处理信息。通过MiniDumpCreateDumpWin32Api创建MiniDump,使用异常处理函数获取堆栈信息,利用StackWalk64等API解析堆栈,通过SymGetSymFromAddr64等函数获取错误信息。此外,文章还提到了符号表绑定和MiniDump的分析流程,包括使用WinDbg工具进行调试。
8万+

被折叠的 条评论
为什么被折叠?



