eMule代码分析(1)----错误机制

最近在研究eMule代码,eMule是一款开源的软件,采用VS2003,MFC开发,微软的各个东西,都用到极致了。

本节主要是节选一个小功能,Crash机制。

首先我先复习一下Windows异常机制,异常处理机制流程图如下:

详细内容可以参考《软件调试》张银奎编

由图可以看出,软件在发生异常的时候,Windows有两轮异常分发过程,在eMule中,是如何实现呢?

eMule的异常处理建立过程是调用了debughelp.dll(此接口windbg有提供,详细的接口用法可以参考windbg的sdk的help)的相关接口

主要流程图如下:

                          ||  检测冲突软件 ||

                        ||  注册异常处理函数||

                            ||发送错误报告||

检测冲突软件主要代码如下:

BOOL CCheckConflict::CheckModules(struct _EXCEPTION_POINTERS* pExceptionInfo)
{
 int    i, j, jCount;
 HMODULE   hMod;
 CONTEXT   context;
 STACKFRAME64 sf;

 for (i = 0; i < MODULE_LIST_COUNT; i++)
 {
  hMod = GetModuleHandle(s_conflictModuleList[i].szModuleName);
  if (NULL == hMod)
   continue;

  memcpy(&context, pExceptionInfo->ContextRecord, sizeof(context));

  ZeroMemory(&sf, sizeof(sf));
  sf.AddrPC.Offset = context.Eip;
  sf.AddrPC.Mode = AddrModeFlat;
  sf.AddrFrame.Offset = context.Ebp;
  sf.AddrFrame.Mode = AddrModeFlat;

  jCount = (s_conflictModuleList[i].iStackSearchLevel == 0) ? 100/*最多查找100层*/ : s_conflictModuleList[i].iStackSearchLevel;
  for (j = 0; j < jCount; j++)
  {
   if (! m_pfnStackWalk64(IMAGE_FILE_MACHINE_I386, GetCurrentProcess(), GetCurrentThread(),
    &sf, NULL, NULL, m_pfnSymFunctionTableAccess64, m_pfnSymGetModuleBase64, NULL))
   {
    break;
   }

   if (IsAddressInModule((PVOID)sf.AddrPC.Offset, hMod))
   {
    return s_conflictModuleList[i].conflictProc(i, hMod);
   }
  }
 }
 
 return FALSE;
}


其中s_conflictModuleList在初始化的时候已增加了几项被检测到的与eMule冲突的模块或者软件。

代码中我们看出eMule在检测的时候历遍了堆栈里边的模块,然后调用IsAddressInModule函数,判断模块的地址是否处于eMule内存地址范围内,若是,则执行conflictProc函数,此函数在s_conflictModuleList初始化的时候,给了一个指定的错误函数地址。

 

注册异常函数是通过调用

    SetUnhandledExceptionFilter

实现的,其中有一个参数为异常处理函数地址,代码如下:

 

LONG CMiniDumper::TopLevelFilter(struct _EXCEPTION_POINTERS* pExceptionInfo)
{
	try
	{
		//ADDED by fengwen on 2006/12/25	<begin> :	检测第三方软件冲突。
		CCheckConflict	cc;
		if (cc.CheckConflict(pExceptionInfo))
		{
			ExitProcess(1);
		}
		//ADDED by fengwen on 2006/12/25	<end> :	检测第三方软件冲突。
	}
	catch(...)
	{
	}
	


	LONG lRetValue = EXCEPTION_CONTINUE_SEARCH;
	TCHAR szResult[MAX_PATH + 1024] = {0};
	MINIDUMPWRITEDUMP pfnMiniDumpWriteDump = NULL;
	HMODULE hDll = GetDebugHelperDll((FARPROC*)&pfnMiniDumpWriteDump, true);
	HINSTANCE	hInstCrashReporter = NULL;	//ADDED by fengwen on 2006/11/15 : 使用新的发送错误报告机制。

	if (hDll)
	{
		if (pfnMiniDumpWriteDump)
		{
			// Ask user if they want to save a dump file
			// Do *NOT* localize that string (in fact, do not use MFC to load it)!
			//COMMENTED by fengwen on 2006/11/15	<begin> : 使用新的发送错误报告机制。
			//if (MessageBox(NULL, _T("eMule crashed :-(\r\n\r\nA diagnostic file can be created which will help the author to resolve this problem. This file will be saved on your Disk (and not sent).\r\n\r\nDo you want to create this file now?"), m_szAppName, MB_ICONSTOP | MB_YESNO) == IDYES)
			//COMMENTED by fengwen on 2006/11/15	<end> : 使用新的发送错误报告机制。
			{
				// Create full path for DUMP file
				TCHAR szDumpPath[MAX_PATH];
				_tcsncpy(szDumpPath, m_szDumpDir, _countof(szDumpPath) - 1);
				szDumpPath[_countof(szDumpPath) - 1] = _T('\0');
				size_t uDumpPathLen = _tcslen(szDumpPath);

				TCHAR szBaseName[MAX_PATH] = {0};
				_tcsncpy(szBaseName, m_szAppName, _countof(szBaseName) - 1);
				/*szBaseName[_countof(szBaseName) - 1] = _T('\0');
				size_t uBaseNameLen = _tcslen(szBaseName);

				time_t tNow = time(NULL);
				_tcsftime(szBaseName + uBaseNameLen, _countof(szBaseName) - uBaseNameLen, _T("_%Y%m%d-%H%M%S"), localtime(&tNow));
				szBaseName[_countof(szBaseName) - 1] = _T('\0');
				*/

				// Replace spaces and dots in file name.
				LPTSTR psz = szBaseName;
				while (*psz != _T('\0')) {
					if (*psz == _T('.'))
						*psz = _T('-');
					else if (*psz == _T(' '))
						*psz = _T('_');
					psz++;
				}
				if (uDumpPathLen < _countof(szDumpPath) - 1) {
					_tcsncat(szDumpPath, szBaseName, _countof(szDumpPath) - uDumpPathLen - 1);
					szDumpPath[_countof(szDumpPath) - 1] = _T('\0');
					uDumpPathLen = _tcslen(szDumpPath);
					if (uDumpPathLen < _countof(szDumpPath) - 1) {
						_tcsncat(szDumpPath, _T(".dmp"), _countof(szDumpPath) - uDumpPathLen - 1);
						szDumpPath[_countof(szDumpPath) - 1] = _T('\0');
					}
				}

				HANDLE hFile = CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
				if (hFile != INVALID_HANDLE_VALUE)
				{
					_MINIDUMP_EXCEPTION_INFORMATION ExInfo = {0};
					ExInfo.ThreadId = GetCurrentThreadId();
					ExInfo.ExceptionPointers = pExceptionInfo;
					ExInfo.ClientPointers = NULL;

					BOOL bOK = (*pfnMiniDumpWriteDump)(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);
					if (bOK)
					{
						// Do *NOT* localize that string (in fact, do not use MFC to load it)!
						_sntprintf(szResult, _countof(szResult) - 1, _T("Saved dump file to \"%s\".\r\n\r\nPlease send this file together with a detailed bug report to dumps@emule-project.net !\r\n\r\nThank you for helping to improve eMule."), szDumpPath);
						szResult[_countof(szResult) - 1] = _T('\0');
						lRetValue = EXCEPTION_EXECUTE_HANDLER;

						//ADDED by fengwen on 2006/11/15	<begin> : 使用新的发送错误报告机制。
						hInstCrashReporter = ShellExecute(NULL, _T("open"), _T("CrashReporter.exe"), szDumpPath, NULL, SW_SHOW);
						if (hInstCrashReporter <= (HINSTANCE)32)
							lRetValue = EXCEPTION_CONTINUE_SEARCH;
						//ADDED by fengwen on 2006/11/15	<end> : 使用新的发送错误报告机制。
					}
					else
					{
						// Do *NOT* localize that string (in fact, do not use MFC to load it)!
						_sntprintf(szResult, _countof(szResult) - 1, _T("Failed to save dump file to \"%s\".\r\n\r\nError: %u"), szDumpPath, GetLastError());
						szResult[_countof(szResult) - 1] = _T('\0');
					}
					CloseHandle(hFile);
				}
				else
				{
					// Do *NOT* localize that string (in fact, do not use MFC to load it)!
					_sntprintf(szResult, _countof(szResult) - 1, _T("Failed to create dump file \"%s\".\r\n\r\nError: %u"), szDumpPath, GetLastError());
					szResult[_countof(szResult) - 1] = _T('\0');
				}
			}
		}
		FreeLibrary(hDll);
		hDll = NULL;
		pfnMiniDumpWriteDump = NULL;
	}

	//COMMENTED by fengwen on 2006/11/15	<begin> : 使用新的发送错误报告机制。
	//if (szResult[0] != _T('\0'))
	//	MessageBox(NULL, szResult, m_szAppName, MB_ICONINFORMATION | MB_OK);
	//COMMENTED by fengwen on 2006/11/15	<end> : 使用新的发送错误报告机制。

#ifndef _DEBUG
	if (EXCEPTION_EXECUTE_HANDLER == lRetValue)		//ADDED by fengwen on 2006/11/15 : 由此filter处理了异常,才去中止进程。
	{
		// Exit the process only in release builds, so that in debug builds the exceptio is passed to a possible
		// installed debugger
		ExitProcess(0);
	}
	else
		return lRetValue;

#else

	return lRetValue;
#endif
}


先是检测,然后是写dmp文件,然后是采用CrashReport函数发送错误。

 

--------end------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值