unit IsDebbuged; interface uses Windows; function PD_PEB_BeingDebuggedFlag(): Boolean; function FD_PEB_NtGlobalFlags(): Boolean; function FD_Heap_HeapFlags(): Boolean; function FD_Heap_ForceFlags(): Boolean; function FD_CheckRemoteDebuggerPresent(): Boolean; //能搞定OD function FD_NtQueryInfoProc_DbgPort(): Boolean; function FD_NtQueryInfoProc_DbgObjHandle(): Boolean; function FD_NtQueryInfoProc_DbgFlags(): Boolean; //能搞定OD function FD_SeDebugPrivilege(csrssPid: THandle): Boolean; function FD_Find_Debugger_Window(): Boolean; function FD_Exception_Closehandle(): Boolean; //能搞定OD function FD_Exception_Int3(): Boolean; function FD_OutputDebugString(): boolean; function FD_Check_StartupInfo(): Boolean; function FD_INT_2d(): Boolean; //能搞定OD function FS_OD_Int3_Pushfd(): Boolean; function FS_SI_Exception_Int1(): Boolean; function FB_HWBP_Exception(): Boolean; //综合查看是否被调试 function IsDebugged:boolean; { 测试 procedure TForm1.FormCreate(Sender: TObject); begin if IsDebugged then application.Terminate; end; } implementation function IsDebugged:boolean; begin result := false; if FB_HWBP_Exception then Result := true; if FS_SI_Exception_Int1 then Result := true; if FD_Find_Debugger_Window then Result := true; if PD_PEB_BeingDebuggedFlag then Result := true; if FD_PEB_NtGlobalFlags then Result := true; if FD_Heap_HeapFlags then Result := true; if FD_CheckRemoteDebuggerPresent then Result := true; if FD_NtQueryInfoProc_DbgPort then Result := true; if FD_NtQueryInfoProc_DbgObjHandle then Result := true; if FD_NtQueryInfoProc_DbgFlags then Result := true; if FD_SeDebugPrivilege(916) then Result := true; if FD_Exception_Closehandle then Result := true; if FD_Exception_Int3 then Result := true; if FD_OutputDebugString then Result := true; if FD_Check_StartupInfo then Result := true; if FD_INT_2d then Result := true; if FS_OD_Int3_Pushfd then Result := true; end; //使用查看PEB结构中标志位beingDegug来检测是否被调试 function PD_PEB_BeingDebuggedFlag(): Boolean; begin asm mov @result, 0 mov eax, fs:[30h] //EAX = TEB.ProcessEnvironmentBlock add eax, 2 mov eax, [eax] and eax, $000000ff //AL = PEB.BeingDebugged test eax, eax jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; //查看PEB结构中的NtGlobalFlags标志位来检测是否被调试 function FD_PEB_NtGlobalFlags(): Boolean; begin asm mov @result, 0 mov eax, fs:[30h] mov eax, [eax+68h] and eax, $70 //NtGlobalFlags test eax, eax jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; //在PEB结构中,使用HeapFlags来 //检测调试器也不是非常可靠,但却很常用。 //这个域由一组标志组成,正常情况下,该值应为2 function FD_Heap_HeapFlags(): Boolean; begin asm mov @result, 0 mov eax, fs:[30h] mov eax, [eax+18h] //PEB.ProcessHeap mov eax, [eax+0ch] //PEB.ProcessHeap.Flags cmp eax, 2 jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; //检测PEB结构中的标志位ForceFlags,它也由一 //组标志组成,正常情况下,该值应为0 function FD_Heap_ForceFlags(): Boolean; begin asm mov @result, 0 mov eax, fs:[30h] mov eax, [eax+18h] mov eax, [eax+10h] test eax, eax jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; //使用API:CheckRemoteDebuggerPresent function FD_CheckRemoteDebuggerPresent(): Boolean; var Func_Addr: Pointer; hModule: Cardinal; pDebugBool: PBool; begin result := false; hModule := GetModuleHandle('kernel32.dll'); if hModule = INVALID_HANDLE_VALUE then exit; Func_addr := GetProcAddress(hModule, 'CheckRemoteDebuggerPresent'); if (Func_addr <> nil) then begin asm lea eax, pDebugBool push eax push $ffffffff call Func_addr cmp dword ptr[pDebugBool], 0 jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; end; //使用ntdll_NtQueryInformationProcess()来查询 //ProcessDebugPort可以用来检测反调试 function FD_NtQueryInfoProc_DbgPort(): Boolean; var Func_Addr: Pointer; hModule: Cardinal; ReturnLength: PULONG; dwDebugPort: PDWORD; begin result := false; hModule := GetModuleHandle('ntdll.dll'); if hModule = INVALID_HANDLE_VALUE then exit; Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess'); if (Func_addr <> nil) then begin asm lea eax, ReturnLength push eax //ReturnLength push 4 //ProcessInformationLength lea eax, dwDebugPort push eax //ProcessInformation push 7 //ProcessInformationClass push $FFFFFFFF //ProcessHandle call Func_addr //NtQueryInformationProcess cmp [dwDebugPort], 0 jne @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; end; //查询winXp自动创建的"debug object"的句柄 function FD_NtQueryInfoProc_DbgObjHandle(): Boolean; var Func_Addr: Pointer; hModule: Cardinal; ReturnLength: PULONG; dwDebugPort: PDWORD; begin result := false; hModule := GetModuleHandle('ntdll.dll'); if hModule = INVALID_HANDLE_VALUE then exit; Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess'); if (Func_addr <> nil) then begin asm lea eax, ReturnLength push eax push 4 lea eax, dwDebugPort push eax push $1E push $FFFFFFFF call Func_addr mov eax, [dwDebugPort] test eax, eax jnz @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; end; //查询winXp自动创建的"debug object", //未公开的ProcessDebugFlags类,当调试器存在时,它会返回false function FD_NtQueryInfoProc_DbgFlags(): Boolean; var Func_Addr: Pointer; hModule: Cardinal; ReturnLength: PULONG; dwDebugPort: PDWORD; begin result := false; hModule := GetModuleHandle('ntdll.dll'); if hModule = INVALID_HANDLE_VALUE then exit; Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess'); if (Func_addr <> nil) then begin asm lea eax, ReturnLength push eax push 4 lea eax, dwDebugPort push eax push $1F push $FFFFFFFF call Func_addr mov eax, [dwDebugPort] test eax, eax jz @IsDebug jmp @exit @IsDebug: mov @result, 1 @exit: end; end; end; //是否获得SeDebugPrivilege //是否可以使用openprocess操作CSRSS.EXE function FD_SeDebugPrivilege(csrssPid: THandle): Boolean; var hTmp: Cardinal; begin result := False; hTmp := OpenProcess(PROCESS_ALL_ACCESS,false,csrssPid); if hTmp <> 0 then begin CloseHandle (hTmp); result := true; end; end; //查找已知的调试器的窗口来检测是否被调试 function FD_Find_Debugger_Window(): Boolean; var whWnd: DWORD; begin result := True; //ollydbg v1.1 whWnd := FindWindow('icu_dbg', nil); if whWnd <> 0 then Exit; //ollyice pe--diy whWnd := FindWindow('pe--diy', nil); if whWnd <> 0 then Exit; //ollydbg ?- whWnd := FindWindow('ollydbg', nil); if whWnd <> 0 then Exit; //windbg whWnd := FindWindow('WinDbgFrameClass', nil); if whWnd <> 0 then Exit; //dede3.50 whWnd := FindWindow('TDeDeMainForm', nil); if whWnd <> 0 then Exit; //IDA5.20 whWnd := FindWindow('TIdaWindow', nil); if whWnd <> 0 then Exit; result := False; end; //给CloseHandle()函数一个无效句柄作为输入参数 //是否触发一个EXCEPTION_INVALID_HANDLE (0xc0000008)的异常 function FD_Exception_Closehandle(): Boolean; begin try CloseHandle($00001234); result := False; except Result := True; end; end; //int3 检测 function FD_Exception_Int3(): Boolean; begin asm mov @result, 0 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 @IsDebug jmp @exit @exception_handler: mov eax,dword ptr [esp+$c]//EAX = ContextRecord mov dword ptr [eax+$b0],$ffffffff//set flag (ContextRecord.EAX) inc dword ptr [eax+$b8]//set ContextRecord.EIP xor eax,eax ret @IsDebug: xor eax,eax inc eax mov esp,ebp pop ebp ret @exit: xor eax,eax mov esp,ebp pop ebp ret end; end; //使用OutputDebugString函数来检测 function FD_OutputDebugString(): boolean; var tmpD: DWORD; begin OutputDebugString(''); tmpD := GetLastError; if(tmpD = 0) then result := true else Result := false; end; //检测STARTUPINFO结构中的值是否为0 function FD_Check_StartupInfo(): Boolean; var si: STARTUPINFO; begin ZeroMemory(@si, sizeof(si)); si.cb := sizeof(si); GetStartupInfo(si); if (si.dwX <> 0) and (si.dwY <> 0) and (si.dwXCountChars <> 0) and (si.dwYCountChars <> 0) and (si.dwFillAttribute <> 0) and (si.dwXSize <> 0) and (si.dwYSize <> 0) then begin result := true end else result := false; end; //使用int 2dh中断的异常检测 function FD_INT_2d(): Boolean; begin try asm int 2dh inc eax //any opcode of singlebyte. //;or u can put some junkcode, //"0xc8"..."0xc2"..."0xe8"..."0xe9" mov @result, 1 end; except Result := false; end; end; //最近比较牛的反调试 function FS_OD_Int3_Pushfd(): Boolean; begin asm push offset @e_handler //set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp xor eax,eax //reset EAX invoke int3 int 3h pushfd nop nop nop nop pop dword ptr fs:[0h] //restore exception handler add esp,4 test eax,eax //check the flag je @IsDebug jmp @Exit @e_handler: push offset @e_handler1 //set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp xor eax,eax //reset EAX invoke int3 int 3h nop pop dword ptr fs:[0h] //restore exception handler add esp,4 //EAX = ContextRecord mov ebx,eax //dr0=>ebx mov eax,dword ptr [esp+$c] //set ContextRecord.EIP inc dword ptr [eax+$b8] mov dword ptr [eax+$b0],ebx //dr0=>eax xor eax,eax ret @e_handler1: //EAX = ContextRecord mov eax,dword ptr [esp+$c] //set ContextRecord.EIP inc dword ptr [eax+$b8] mov ebx,dword ptr[eax+$04] mov dword ptr [eax+$b0],ebx //dr0=>eax xor eax,eax ret @IsDebug: mov @result, 1 mov esp,ebp pop ebp ret @Exit: mov esp,ebp pop ebp ret end; end; //使用int1的异常检测来反调试 function FS_SI_Exception_Int1(): Boolean; begin asm mov @result, 0 push offset @eh_int1 //set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp xor eax,eax //reset flag(EAX) invoke int3 int 1h pop dword ptr fs:[0h] //restore exception handler add esp,4 test eax, eax // check the flag je @IsDebug jmp @Exit @eh_int1: mov eax,[esp+$4] mov ebx,dword ptr [eax] mov eax,dword ptr [esp+$c] //EAX = ContextRecord mov dword ptr [eax+$b0],1 //set flag (ContextRecord.EAX) inc dword ptr [eax+$b8] //set ContextRecord.EIP inc dword ptr [eax+$b8] //set ContextRecord.EIP xor eax, eax ret @IsDebug: mov @result, 1 mov esp,ebp pop ebp ret @Exit: xor eax, eax mov esp,ebp pop ebp ret end; end; //在异常处理过程中检测硬件断点 function FB_HWBP_Exception(): Boolean; begin asm push offset @exeception_handler //set exception handler push dword ptr fs:[0h] mov dword ptr fs:[0h],esp xor eax,eax //reset EAX invoke int3 int 1h pop dword ptr fs:[0h] //restore exception handler add esp,4 //test if EAX was updated (breakpoint identified) test eax,eax jnz @IsDebug jmp @Exit @exeception_handler: //EAX = CONTEXT record mov eax,dword ptr [esp+$c] //check if Debug Registers Context.Dr0-Dr3 is not zero cmp dword ptr [eax+$04],0 jne @hardware_bp_found cmp dword ptr [eax+$08],0 jne @hardware_bp_found cmp dword ptr [eax+$0c],0 jne @hardware_bp_found cmp dword ptr [eax+$10],0 jne @hardware_bp_found jmp @exception_ret @hardware_bp_found: //set Context.EAX to signal breakpoint found mov dword ptr [eax+$b0],$FFFFFFFF @exception_ret: //set Context.EIP upon return inc dword ptr [eax+$b8] //set ContextRecord.EIP inc dword ptr [eax+$b8] //set ContextRecord.EIP xor eax,eax ret @IsDebug: mov @result, 1 mov esp,ebp pop ebp ret @Exit: xor eax, eax mov esp,ebp pop ebp ret end; end; end.