Win32:基于Windows异常机制的反调试技术

一、Windows 异常处理机制

  • Windows的异常处理流程
  1. 交给调试器(进程必须被调试)
  2. 执行VEH 向量化异常处理(如果调试器不处理)
  3. 执行SEH 结构化异常处理(如果VEH不处理)
  4. TopLevelEH 顶层异常处理 (如果SEH不处理)(进程被调试时不会被执行)
  5. 交给调试器(上面的异常处理都说处理不了,就再次交给调试器)
  6. 调用异常端口通知csrss.exe

二、反调试:利用结构化异常处理(SEH)

  • Windows提供了关键字__try 、 __except 进行SEH 结构化异常处理(而非C++的try、catch)。
  • 如果程序正常运行未被调试,那么在__try中产生的异常将在__except模块中被处理。如果一个程序正在被调试,那么调试器会接管程序产生的异常,不会交给__except模块处理。根据这个思路,可以利用关键字__try 、 __except 判断程序是否处于调试器下。
  • 系统提供了RaiseException 这个API用于手动抛出异常,第一个参数是要抛出的异常代码,其有效值如下:
    STATUS_BREAKPOINT                  (0x80000003)
    STATUS_SINGLE_STEP                 (0x80000004)    
    DBG_PRINTEXCEPTION_C               (0x40010006)
    DBG_RIPEXCEPTION                   (0x40010007)
    DBG_CONTROL_C                      (0x40010005)
    DBG_CONTROL_BREAK                  (0x40010008)
    DBG_COMMAND_EXCEPTION              (0x40010009)
    ASSERTION_FAILURE                  (0xC0000420)
    STATUS_GUARD_PAGE_VIOLATION        (0x80000001)
    SEGMENT_NOTIFICATION               (0x40000005)
    EXCEPTION_WX86_SINGLE_STEP         (0x4000001E)
    EXCEPTION_WX86_BREAKPOINT          (0x4000001F)
    
  • 反调试代码:
    #include <iostream>
    #include <Windows.h>
    
    bool CheckDebugger() {
    
    	__try {
    
    		//触发空指针异常
    		//int* p = NULL;
    		//*p = 6;
    
    		//
    		//RaiseException手动抛出一种指定的异常
    		//
    		RaiseException(STATUS_BREAKPOINT, 0, 0, 0);
    	}
    
    	// 利用关键字__except去进行结构化异常处理
    	// 如果程序被调试,那么__except模块不会被执行,而是由调试器处理异常
    	__except (EXCEPTION_EXECUTE_HANDLER) {	//EXCEPTION_EXECUTE_HANDLER 表示异常被识别,控制流将进入到__except模块中运行异常处理代码
    		DWORD ExceptionCode = GetExceptionCode();
    		printf("not in debugging :利用__except模块进行结构化异常处理! \n");
    		printf("ExceptionCode = %08C \n", ExceptionCode);
    		return FALSE;
    	}
    
    	return TRUE;
    }
    
    
    int main()
    {
    	bool res = CheckDebugger();
    
    	system("PAUSE");
    }
    

三、反调试:利用顶层异常处理(UEF)

  • TopLevelEH 全称顶层异常处理器(UEF),这个函数只能有一个,被保存在全局变量中。由于只会被系统默认的最底层 SEH 调用,所以又会被称作是 SEH 的一种,是整个异常处理的最后一环。所以通常都不会再此执行异常处理操作,而是进行内存 dump ,将消息发送给服务器,进行异常分析。
  • 异常一旦被结构化异常处理(SEH )处理,就不会再传递给顶层异常处理器(UEF)。

LONG WINAPI MyTopLevelExceptionHandle(PEXCEPTION_POINTERS ExceptionInfo)
{
	printf("ExceptionCode: %X\n", ExceptionInfo->ExceptionRecord->ExceptionCode);

	// 如果当前的异常是除零异常,那么就通过修改寄存器处理异常
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)
	{
		ExceptionInfo->ContextRecord->Eax = 6;
		ExceptionInfo->ContextRecord->Ecx = 2;

		// 异常如果被处理了,那么就返回重新执行当前的代码
		return EXCEPTION_CONTINUE_EXECUTION;
	}

	// 如果不是自己能够处理的异常,就不处理只报告。【程序将崩溃】
	return EXCEPTION_EXECUTE_HANDLER;
}


int main()
{
	//安装顶层异常处理函数
	SetUnhandledExceptionFilter(MyTopLevelExceptionHandle);

	//手动触发一个除0异常
	int number = 6;
	number /= 0;
	printf("number = %d \n", number);

	system("PAUSE");
}

参考资料

发布了56 篇原创文章 · 获赞 5 · 访问量 7432

猜你喜欢

转载自blog.csdn.net/forchoosen/article/details/104026116