未处理异常的处理

本文介绍了如何在Windows中通过SetUnhandledExceptionFilter处理未处理异常,并使用DbgHelp进行异常分析。异常处理通常会导致程序终止,但也可以选择只退出受影响的线程。此外,文章还探讨了无法捕获所有异常的原因,尤其是由于安全考虑,某些异常会被发送到默认异常处理器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    可以在Windows中通过SetUnhandledExceptionFilter设置未处理异常的默认处理。很多程序都用它来执行记录程序中的BUG,如QQ崩溃时弹出的框框。
    捕获到异常后,需要分析异常新,这个可以通过Windows提供的DbgHelp完成。对于异常的处理,一般都是终止程序。但是也可以在处理函数中调用ExitThread退出当前线程,这样就仅有当前线程被破坏,不影响进程的其他部分。关于异常的代码意义,可以参看GCLsoft的博文,地址是:http://hi.baidu.com/gclsoft/blog/item/96d7eb456934d75e500ffea1.html。下面是一个异常分析代码,参考了http://blog.vckbase.com/zaboli/archive/2010/11/18/52088.html

void GetCurModulePath(TCHAR* ptcPath,int iLen,LPVOID addr)
{
 MEMORY_BASIC_INFORMATION mbi = {0};
 if(FALSE == ::VirtualQuery( addr, &mbi, sizeof(mbi)))
  return;
 UINT_PTR h_module = (UINT_PTR)mbi.AllocationBase;
 ::GetModuleFileName((HMODULE)h_module, ptcPath, iLen);
 return;
}

void EI2Str(TCHAR* ptcInfo,int iInfoLen,ULONG_PTR* Info,int iCount)
{
 int        i    = 0;
 TCHAR*    p1    = ptcInfo;
 for(i = 0; i < iCount; i++)
 {
  _stprintf(p1,_T("%08X "),Info[i]);
  p1 += 9;
 }
}


LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS* pei)
{
 STACKFRAME    sf;
 CONTEXT        context;
 TCHAR        tcPath[2048]    = {0};
 TCHAR        tcInfo[10240]    = {0};
 TCHAR        tcExcep[20480]    = {0};
 GetCurModulePath(tcPath,2048,pei->ExceptionRecord->ExceptionAddress);
 EI2Str(tcInfo,10240,pei->ExceptionRecord->ExceptionInformation,pei->ExceptionRecord->NumberParameters);
 _stprintf(tcExcep,_T("程序遇到未知异常,具体信息如下: 代码:0x%08X  标志:0x%08X  地址:0x%08X ")
  _T("参数:%d  %s 模块:%s 堆栈信息如下: /r/n"),
  pei->ExceptionRecord->ExceptionCode,pei->ExceptionRecord->ExceptionFlags,pei->ExceptionRecord->ExceptionAddress,
  pei->ExceptionRecord->NumberParameters,tcInfo,tcPath);

 memset(tcInfo,0,sizeof(tcInfo));
 memset( &sf, 0, sizeof(STACKFRAME));
 memcpy(&context,pei->ContextRecord,sizeof(CONTEXT));

 sf.AddrPC.Offset    = context.Eip;
 sf.AddrPC.Mode        = AddrModeFlat;
 sf.AddrStack.Offset    = context.Esp;
 sf.AddrStack.Mode    = AddrModeFlat;
 sf.AddrFrame.Offset    = context.Ebp;
 sf.AddrFrame.Mode    = AddrModeFlat;

 DWORD machineType    = IMAGE_FILE_MACHINE_I386;
 HANDLE hProcess        = GetCurrentProcess();
 HANDLE hThread        = GetCurrentThread();
 SymInitialize(hProcess,NULL,TRUE);

 for( ; ; )
 {
  if( !StackWalk(machineType, hProcess, hThread, &sf, &context, 0, SymFunctionTableAccess, SymGetModuleBase, 0 ) )
  {
   break;
  }

  if( sf.AddrFrame.Offset == 0 )
  {
   break;
  }
  BYTE symbolBuffer[ sizeof( SYMBOL_INFO ) + 1024 ];
  PSYMBOL_INFO pSymbol = ( PSYMBOL_INFO ) symbolBuffer;

  pSymbol->SizeOfStruct = sizeof( symbolBuffer );
  pSymbol->MaxNameLen = 1024;

  _stprintf(tcPath,_T("sf.AddrPC.Offset:0x%08X "),sf.AddrPC.Offset);
  _tcscat(tcExcep,tcPath);

  memset(tcPath,0,sizeof(tcPath));
  DWORD64 symDisplacement = 0;
  if( SymFromAddr( hProcess, sf.AddrPC.Offset, 0, pSymbol ) )
  {
   _stprintf(tcPath,_T("Function : %S "), pSymbol->Name );
  }
  else
  {

  }

  _tcscat(tcExcep,tcPath);

  IMAGEHLP_LINE lineInfo = { sizeof(IMAGEHLP_LINE) };
  DWORD dwLineDisplacement;

  memset(tcPath,0,sizeof(tcPath));
  if( SymGetLineFromAddr( hProcess, sf.AddrPC.Offset, &dwLineDisplacement, &lineInfo ) )
  {
   _stprintf(tcPath,_T( "[Source File : %S] [Source Line : %u] "), lineInfo.FileName,lineInfo.LineNumber );
  }
  else
  {
  }
  _tcscat(tcExcep,tcPath);
  _tcscat(tcExcep, L"/r/n");
 }

 SymCleanup(hProcess);

 //AfxMessageBox(tcExcep);
 MessageBox(NULL, tcExcep, L"出现错误", MB_OK);
 ExitThread(-1);
 return EXCEPTION_EXECUTE_HANDLER;//EXCEPTION_CONTINUE_SEARCH;
}

    最后,要说的是异常处理并不能捕获到所有异常,主要是安全原因导致CRT将某些异常的处理强行发送到了默认异常处理器(一般为dr watson,程序自动保存系统,http://baike.baidu.com/view/1300613.htm)。可以通过Hook SetUnhandledExceptionFilter调用完成。可用http://www.cppblog.com/woaidongmao/archive/2009/10/21/99129.html中的方法:

#ifndef _M_IX86
       #error "The following code only works for x86!"
#endif

void DisableSetUnhandledExceptionFilter()
{
    void *addr = (void*)GetProcAddress(LoadLibrary(_T("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);
     }
}

    也可以用http://blog.kalmbachnet.de/?postid=75中的方法:
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
 return NULL;
}

BOOL PreventSetUnhandledExceptionFilter()
{
 HMODULE hKernel32 = LoadLibrary(_T("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 far
 void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
 DWORD dwNewEntryAddr = (DWORD) pNewFunc;
 DWORD dwRelativeAddr = dwNewEntryAddr - dwOrgEntryAddr;

 newJump[ 0 ] = 0xE9;  // JMP absolute
 memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
 SIZE_T bytesWritten;
 BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
  pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
 return bRet;
}

    上述两种方法是类似的,但是都不能彻底解决问题。对于如堆栈溢出这类异常,这两类方法都没办法解决。原因当然还是来自微软,还是安全因素导致的。因此,理论上也就不可能处理所有异常了,否则这点必然会被Cracker利用,安全也就无从谈起了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值