异常情况
以下方法有意的引起异常,以验证进程是否运行在调试器下。
1. UnhandledExceptionFilter()
如果发生异常并且未注册任何异常处理程序(或已注册但未处理此类异常),则将调用kernel32!UnhandledExceptionFilter()函数。可以使用kernel32!SetUnhandledExceptionFilter()注册自定义未处理的异常过滤器。但是,如果程序在调试器下运行,则不会调用自定义过滤器,并且会将异常传递给调试器。因此,如果未处理的异常筛选器已注册并将控件传递给它,则该进程将不会在调试器中运行。
x86汇编(FASM)
include 'win32ax.inc'.codestart:jmp beginnot_debugged:invoke MessageBox,HWND_DESKTOP,"Not Debugged","",MB_OKinvoke ExitProcess,0begin:invoke SetUnhandledExceptionFilter, not_debuggedint 3jmp being_debuggedbeing_debugged:invoke MessageBox,HWND_DESKTOP,"Debugged","",MB_OKinvoke ExitProcess,0.end start
C / C ++代码
LONG UnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo){PCONTEXT ctx = pExceptionInfo->ContextRecord;ctx->Eip += 3; // Skip \xCC\xEB\x??return EXCEPTION_CONTINUE_EXECUTION;}bool Check(){bool bDebugged = true;SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)UnhandledExceptionFilter);__asm{int 3 // CCjmp near being_debugged // EB ??}bDebugged = false;being_debugged:return bDebugged;}
2. RaiseException()
诸如DBC_CONTROL_C或DBG_RIPEVENT之类的异常不会传递给当前进程的异常处理程序,而是由调试器使用。这使我们可以注册一个异常处理程序,使用kernel32!RaiseException()函数引发这些异常,并检查控件是否传递给了我们的处理程序。如果未调用异常处理程序,则该进程可能正在调试中。
C / C ++代码
bool Check(){__try{RaiseException(DBG_CONTROL_C, 0, 0, NULL);return true;}__except(DBG_CONTROL_C == GetExceptionCode()? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH){return false;}}
3.使用异常处理程序隐藏控制流
这种方法不检查是否存在调试器,但有助于按异常处理程序的顺序隐藏程序的控制流。
我们可以注册一个引发另一个异常的异常处理程序(结构化异常处理SEH or 向量化异常处理VEH),该异常将传递给下一个引发下一个异常的处理程序,依此类推。最后,处理程序的顺序应导向我们要隐藏的进程。
使用结构化异常处理程序:
C / C ++代码
#include <Windows.h>void MaliciousEntry(){// ...}void Trampoline2(){__try{__asm int 3;}__except (EXCEPTION_EXECUTE_HANDLER){MaliciousEntry();}}void Trampoline1(){__try{__asm int 3;}__except (EXCEPTION_EXECUTE_HANDLER){Trampoline2();}}int main(void){__try{__asm int 3;}__except (EXCEPTION_EXECUTE_HANDLER) {}{Trampoline1();}return 0;}
使用向量异常处理程序:
C / C ++代码
#include <Windows.h>PVOID g_pLastVeh = nullptr;void MaliciousEntry(){// ...}LONG WINAPI ExeptionHandler2(PEXCEPTION_POINTERS pExceptionInfo){MaliciousEntry();ExitProcess(0);}LONG WINAPI ExeptionHandler1(PEXCEPTION_POINTERS pExceptionInfo){if (g_pLastVeh){RemoveVectoredExceptionHandler(g_pLastVeh);g_pLastVeh = AddVectoredExceptionHandler(TRUE, ExeptionHandler2);if (g_pLastVeh)__asm int 3;}ExitProcess(0);}int main(void){g_pLastVeh = AddVectoredExceptionHandler(TRUE, ExeptionHandler1);if (g_pLastVeh)__asm int 3;return 0;}
缓解措施
调试期间:
- 对于调试器检测检查:只需用
NOP填充相应的检查。 - 对于控制流隐藏:您必须手动跟踪程序直到有效负载。
- 对于调试器检测检查:只需用
- 对于反反调试工具开发:这类技术的问题在于,不同的调试器会使用不同的异常,并且不将其返回给调试器。这意味着您必须为特定的调试器实现一个插件,并更改在相应异常之后触发的事件处理程序的行为。
