windows程序崩溃调试终极武器---dump文件

一、前言

前不久开发了一款windows程序,目前已经是测试跑了,对于windows程序熟悉的童鞋,应该都知道一个事,就是他运行时有一个黑框,如果崩溃的就是下面这种情形~

这种情况有时候会给我们一种不知所措的感觉,看日志吧~有时候崩溃了,不一定出现在什么地方;异常处理吧,又不像JAVA那么多的异常,所以很多时候,我们遇到这种情况就有些不知所措了~

今天,带来一款终极秘密武器---dump文件;


二、实战

1、dump文件简介

dump文件是进程的内存镜像,可以吧程序的执行状态通过调试器保存到dump文件中;


2、通过任务管理生成dump文件

首先,我们写一段测试程序:

#include <iostream>using namespace std;void fun(int* p)
{p[0] = 1;
}int main()
{fun(NULL);return 0;
}

然后我们编译一把,再运行

我们会得到这么一个错误:

此时,我们不要做关闭这个框,我们只需要吧任务管理器打开,找到该进程,然后导出文件就可以了

我们打开路径,拷贝该文件到我们exe所在的目录:

然后我们打开vs,这里使用的是vs2015

由于我吧dmp文件放在了exedpb目录下,不用设置符号路径

这里千万注意一点,很多博客上都没有说到这一点:32位程序和64位程序调试是不同的

如果我们程序是32位的,但是我们的开发机是64位,通过转存储文件生成的文件就不是我们32位程序对应的文件了,就会无法调试;


3、通过程序生成dump文件

上面我们说到了通过任务管理器生成的dump文件的方式会出现不兼容或者说是错误,那么怎么去解决这个问题呢?

还好微软也提供了API出来,我们可以再程序中使用微软的API进行调用,这样通过程序产生的dump文件就没有位数的问题了;

这里提供一个通用的代码,是直接可以拿过来用的~感觉我吧

minidmp.h#pragma once
#include <windows.h>
#include <DbgHelp.h>
#include <stdlib.h>
#pragma comment(lib, "dbghelp.lib")
#pragma warning(disable:4996) //全部关掉
#pragma warning(once:4996) //仅显示一个/*
#ifndef _M_IX86
#error "The following code only works for x86!"
#endif
*/inline BOOL IsDataSectionNeeded(const WCHAR* pModuleName)
{if (pModuleName == 0){return FALSE;}WCHAR szFileName[_MAX_FNAME] = L"";_wsplitpath(pModuleName, NULL, NULL, szFileName, NULL);if (wcsicmp(szFileName, L"ntdll") == 0)return TRUE;return FALSE;
}inline BOOL CALLBACK MiniDumpCallback(PVOID                            pParam,const PMINIDUMP_CALLBACK_INPUT   pInput,PMINIDUMP_CALLBACK_OUTPUT        pOutput)
{if (pInput == 0 || pOutput == 0)return FALSE;switch (pInput->CallbackType){case ModuleCallback:if (pOutput->ModuleWriteFlags & ModuleWriteDataSeg)if (!IsDataSectionNeeded(pInput->Module.FullPath))pOutput->ModuleWriteFlags &= (~ModuleWriteDataSeg);case IncludeModuleCallback:case IncludeThreadCallback:case ThreadCallback:case ThreadExCallback:return TRUE;default:;}return FALSE;
}inline void CreateMiniDump(PEXCEPTION_POINTERS pep, LPCTSTR strFileName)
{HANDLE hFile = CreateFile(strFileName, GENERIC_READ | GENERIC_WRITE,FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)){MINIDUMP_EXCEPTION_INFORMATION mdei;mdei.ThreadId = GetCurrentThreadId();mdei.ExceptionPointers = pep;mdei.ClientPointers = NULL;MINIDUMP_CALLBACK_INFORMATION mci;mci.CallbackRoutine = (MINIDUMP_CALLBACK_ROUTINE)MiniDumpCallback;mci.CallbackParam = 0;::MiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, MiniDumpNormal, (pep != 0) ? &mdei : 0, NULL, &mci);CloseHandle(hFile);}
}LONG __stdcall MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{CreateMiniDump(pExceptionInfo, "orderBookMatchEngine.dmp");// EXCEPTION_EXECUTE_HANDLER equ 1 表示我已经处理了异常, 可以优雅地结束了// EXCEPTION_CONTINUE_SEARCH equ 0 表示我不处理, 其他人来吧, 于是windows调用默认的处理程序显示一个错误框, 并结束// EXCEPTION_CONTINUE_EXECUTION equ - 1 表示错误已经被修复, 请从异常发生处继续执行return EXCEPTION_EXECUTE_HANDLER;
}// 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
void DisableSetUnhandledExceptionFilter()
{void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),"SetUnhandledExceptionFilter");if (addr){unsigned char code[16];int size = 0;code[size++] = 0x33;code[size++] = 0xC0;code[size++] = 0xC2;code[size++] = 0x04;code[size++] = 0x00;DWORD dwOldFlag, dwTempFlag;VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);}
}
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{return NULL;
}BOOL PreventSetUnhandledExceptionFilter()
{HMODULE hKernel32 = LoadLibrary("kernel32.dll");if (hKernel32 == NULL) return FALSE;void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");if (pOrgEntry == NULL) return FALSE;unsigned char newJump[100];DWORD dwOrgEntryAddr = (DWORD)pOrgEntry;dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp farvoid *pNewFunc = &MyDummySetUnhandledExceptionFilter;DWORD dwNewEntryAddr = (DWORD)pNewFunc;DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;newJump[0] = 0xE9;  // JMP absolutememcpy(&newJump[1], &dwRelativeAddr, sizeof(pNewFunc));SIZE_T bytesWritten;BOOL bRet = WriteProcessMemory(GetCurrentProcess(),pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);return bRet;
}void InitMinDump()
{//注册异常处理函数SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);// PreventSetUnhandledExceptionFilter();//使SetUnhandledExceptionFilterDisableSetUnhandledExceptionFilter();
}

在main函数中只要在最开始吧的时候使用初始化函数就可以了~

案例:

#include <iostream>
#include "minidmp.h"using namespace std;void fun(int* p)
{p[0] = 1;
}int main()
{InitMinDump();fun(NULL);return 0;
}

然后编译运行,会在当前文件夹中出现orderBookMatchEngine.dmp文件,然后按照上面的流程打开调试就可以了~


三、总结

调试程序和寻找BUG,是一个程序员必备的技能之一,很多处理问题的能力是从工作中遇到的问题总结得到的,也是其他地方学习不到的~

学习更多的调试技能和知识,才能在工作中游刃有余~

windows程序崩溃调试终极武器相关推荐

  1. 关于Windows程序崩溃(Crash)以及生成dump文件的探究

    文章目录 关于Windows程序崩溃(Crash)以及生成dump文件的探究 什么是崩溃(Crash),崩溃(Crash)的现象 崩溃(Crash)的原因是什么 如何消除崩溃时出现的异常信息对话框 如 ...

  2. 栈windows linux,Linux+Windows: 程序崩溃时,在 C++ 代码中,如何获取函数调用栈信息...

    一.前言 程序在执行过程中 crash 是非常严重的问题,一般都应该在测试阶段排除掉这些问题,但是总会有漏网之鱼被带到 release 阶段. 因此,程序的日志系统需要侦测这种情况,在代码崩溃的时候获 ...

  3. Android系统调试-程序崩溃调试

    Android系统对于Native(C/C++)应用程序的调试手段比单纯的linux系统coredump文件与gdb结合调试的手段.但是Android系统的天然不支持这种调试方式,其在内核中就没有启用 ...

  4. windows程序崩溃生成dump文件

    第一种: 通过任务管理器:这种适用在程序挂了(crash)的时候进程还未退出,比如我运行程序,出现了下面的错: 此时打开任务管理器,右击相应进程,点击"Create Dump File&qu ...

  5. 如何定位Release程序崩溃原因

    [转]如何定位Release程序崩溃原因 Posted on 2011-08-19 10:44 单鱼游弋 阅读(2162) 评论(1) 编辑 收藏 1       案例描述 作为Windows程序员, ...

  6. 编写的windows程序,崩溃时产生crash dump文件的办法

    一.引言 dump文件是C++程序发生异常时,保存当时程序运行状态的文件,是调试异常程序重要的方法,所以程序崩溃时,除了日志文件,dump文件便成了我们查找错误的最后一根救命的稻草.windows程序 ...

  7. windows程序员进阶系列:《软件调试》之O--- WinDbg使用介绍

    windows程序员进阶系列:<软件调试>之O--- WinDbg使用介绍 拥有一个顺手的武器是每一个武林高手梦寐以求的.对于windows程序员来说,WinDbg调试器就是我们的武器.熟 ...

  8. qt调试android程序崩溃,使用qt5开发的Android应用程序合并了AWS C++库崩溃

    我想开发一种 Android应用程序 它在行刑一开始就崩溃了. 我在用 第5.11节 我认为配置很好(我正在使用 上一个Android SDK和ndk10e )因为我可以运行一个简单的Android应 ...

  9. windows 程序异常崩溃等错误定位

      MAP/映射文件 1.      MAP 映射文件的作用:MAP文件可以查找崩溃或者程序异常地址,然后就可以精确地定位到源代码中出错的代码行. 2.VS中生成MAP文件的方法,项目属性中选择生成映 ...

最新文章

  1. Unet实现图像分割(二)
  2. es6中export和export default的区别
  3. .net语言_小白对入门语言的选择,C、C++、Java、Python、.NET该怎么选?
  4. 操作元素之表单属性设置
  5. spring+jdbc+template+transaction实现
  6. 数值运算pythonmopn_python – 计算每列的Pandas DataFrame的自相关性
  7. 树莓派Python开发教程:什么是树莓派
  8. 自制变压器,要注意几个内容
  9. 使用Sbert预训练的TTS模型《Expressive Text-to-Speech using Style Tag》
  10. 我喜欢生命本来的样子--周国平
  11. Photoshop设计网站原型图
  12. html chm 64,Win7 64位下的CHM
  13. JAVA_HOME环境变量
  14. 图神经网络(一)DGL框架搭建GCN图卷积神经网络模型
  15. Centos GNOME桌面
  16. Promise在各种场景的使用(Promise.all、Promise.allSettled、Promise.race、Promise.any)
  17. p2p 文件服务器集群,基于云计算P2P流媒体服务器集群部署算法.doc
  18. VMware下CentOS7最小化安装及配置
  19. 从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘
  20. 关于右键auto病毒清除

热门文章

  1. bp神经网络可以用什么做,BP神经网络用什么软件
  2. JUC并发编程共享模型之无锁(五)
  3. arcgis 合并 联合_合并不同图层中的要素(联合)
  4. chrome插件Vue Devtools失效
  5. AIR管理文件关联, 指定文件默认打开方式
  6. 【光纤传感硬件类论文】
  7. 2021区块链行业十大影响力事件盘点
  8. virsh console无法连接虚拟机
  9. 若依框架如何新建模块
  10. 区块链之常用数据算法介绍