return true;
if(strcmp(pe32.szExeFile,\return true;
if(strcmp(pe32.szExeFile,\return true;
if(strcmp(pe32.szExeFile,\return true;
2.17 FD_Find_Device_Driver() 调试工具通常会使用内核驱动,因此如果尝试是否可以打开一些调试器所用到的设备,就可判断是否存在调试器。常用的设备名称如下: \\\\.\\SICE (SoftICE) \\\\.\\SIWVID(SoftICE) \\\\.\\NTICE (SoftICE) \\\\.\\REGVXG(RegMON) \\\\.\\REGVXD(RegMON) \\\\.\\REGSYS(RegMON) \\\\.\\REGSYS(RegMON) \\\\.\\FILEVXG(FileMON) \\\\.\\FILEM(FileMON) \\\\.\\TRW(TRW2000)
2.18 FD_Exception_Closehandle()
如果给CloseHandle()函数一个无效句柄作为输入参数,在无调试器时,将会返回一个错误代码,而有调试器存在时,将会触发一个EXCEPTION_INVALID_HANDLE (0xc0000008)的异常。 __try {
CloseHandle(HANDLE(0x00001234)); return false; }
__except(1) {
return true; }
2.19 FD_Exception_Int3()
通过Int3产生异常中断的反调试比较经典。当INT3 被执行到时, 如果程序未被调试, 将会异常处理器程序继续执行。而INT3指令常被调试器用于设置软件断点,int 3会导致调试器误认为这是一个自己的断点,从而不会进入异常处理程序。 __asm {
push offset exception_handler; set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp
xor eax,eax;reset EAX invoke int3 int 3h
pop dword ptr fs:[0h];restore exception handler add esp,4
test eax,eax; check the flag je rt_label jmp rf_label
exception_handler:
mov eax,dword ptr [esp+0xc];EAX = ContextRecord
mov dword ptr [eax+0xb0],0xffffffff;set flag (ContextRecord.EAX) inc dword ptr [eax+0xb8];set ContextRecord.EIP xor eax,eax retn
rt_label: xor eax,eax inc eax mov esp,ebp pop ebp retn
rf_label: xor eax,eax mov esp,ebp pop ebp retn }
2.20 FD_Exception_Popf()
我们都知道标志寄存器中的陷阱标志,当该标志被设置时,将产生一个单步异常。在程序中动态设置这给标志,如果处于调试器中,该异常将会被调试器捕获。 可通过下面的代码设置标志寄存器。 pushf
mov dword ptr [esp], 0x100 popf
2.21 FD_OutputDebugString()
在有调试器存在和没有调试器存在时,OutputDebugString函数表现会有所不同。最明显的不同是, 如果有调试器存在,其后的GetLastError()的返回值为零。 OutputDebugString(\
tmpD=GetLastError(); if(tmpD==0) return true; return false;
2.22 FD_TEB_check_in_Vista();
这是从windows anti-debug reference里拷贝出来的,据说是适用于vista系统下检测调试器。我没有vista所以也没有测试。有条件的可以试下,有问题帮忙反馈给我。多谢。 //vista __asm {
push offset exception_handler; set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp
xor eax,eax;reset EAX invoke int3 int 3h
pop dword ptr fs:[0h];restore exception handler add esp,4
mov eax, fs:[18h] ; teb add eax, 0BFCh
mov ebx, [eax] ; pointer to a unicode string test ebx, ebx ; (ntdll.dll, gdi32.dll,...) je rf_label jmp rt_label
exception_handler:
mov eax,dword ptr [esp+0xc];EAX = ContextRecord inc dword ptr [eax+0xb8];set ContextRecord.EIP xor eax,eax retn }
2.23 FD_check_StartupInfo();
这是从pediy上拷贝来的。Window创建进程的时候会把STARTUPINFO结构中的值设为0,而通过调试器创建进程的时候会忽略这个结构中的值,也就是结构中的值不为0,所以可以利用这个来判断是否在调试程序。 STARTUPINFO si;
ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); GetStartupInfo(&si);
if ( (si.dwX != 0) || (si.dwY !=0)
|| (si.dwXCountChars != 0) || (si.dwYCountChars !=0 ) || (si.dwFillAttribute != 0) || (si.dwXSize != 0) || (si.dwYSize != 0) ) return true;
else
return false;
2.24 FD_Parent_Process1()
与前面的FD_Parent_Process原理一样,唯一不同的是使用ZwQueryInformationProcess检测父进程,而没有使用Process32Next,这有一个好处是可以绕过OD的HideOD插件。
2.25 FD_Exception_Instruction_count()
好像《软件加解密技术》中有提到这个反调试。
通过注册一个异常句柄,在特定地址设置一些硬件断点,当通过这些地址时都会触发EXCEPTION_SINGLE_STEP (0x80000004)的异常,在异常处理程序中,将会调整指令指针到一条新指令,然后恢复运行。可以通过进入进程context结构来设置这些断点,有些调试器不能处理那些不是自己设置的硬件断点,从而导致一些指令将会被漏掉计数,这就形成了一个反调试。 __asm {
xor eax,eax; cdq;
push e_handler;
push dword ptr fs:[eax]; mov fs:[eax],esp; int 3; hwbp1: nop hwbp2: nop hwbp3: nop hwbp4: nop div edx nop
pop dword ptr fs:[0] add esp,4 cmp al,4; jne rt_label; jmp rf_label;
e_handler: xor eax,eax; ;ExceptionRecord
mov ecx,dword ptr[esp+0x04] ;Contextrecord
mov edx,dword ptr[esp+0x0c] ;ContextEIP
inc byte ptr[edx+0xb8];
;ExceptionCode
mov ecx,dword ptr[ecx];
;1.EXCEPTION_INT_DIVIDE_BY_ZERO cmp ecx,0xc0000094; jne Ex_next2; ;Context_eip
inc byte ptr[edx+0xb8];
mov dword ptr[edx+0x04],eax;dr0 mov dword ptr[edx+0x08],eax;dr1 mov dword ptr[edx+0x0c],eax;dr2 mov dword ptr[edx+0x10],eax;dr3 mov dword ptr[edx+0x14],eax;dr6 mov dword ptr[edx+0x18],eax;dr7 ret
;2.EXCEPTION_BREAKPOINT Ex_next2:
cmp ecx,0x80000003; jne Ex_next3;
mov dword ptr[edx+0x04],offset hwbp1;dr0 mov dword ptr[edx+0x08],offset hwbp2;dr1 mov dword ptr[edx+0x0c],offset hwbp3;dr2 mov dword ptr[edx+0x10],offset hwbp4;dr3 mov dword ptr[edx+0x18],0x155;dr7 ret
;3.EXCEPTION_SINGLE_STEP Ex_next3:
cmp ecx,0x80000004 jne rt_label ;CONTEXT_Eax
inc byte ptr[edx+0xb0] ret }
2.26 FD_INT_2d()
在windows anti-debug reference中指出,如果程序未被调试这个中断将会生产一个断点异常. 被调试并且未使用跟踪标志执行这个指令, 将不会有异常产生程序正常执行. 如果被调试并且指令被跟踪, 尾随的字节将被跳过并且执行继续. 因此, 使用 INT 2Dh 能作为一个强有力的反调试和反跟踪机制。 __try { __asm