- UID
 - 56403
 
 注册时间2008-9-25
阅读权限30
最后登录1970-1-1
龙战于野 
  
 
 
 
TA的每日心情  | 衰 2017-6-17 16:59 | 
|---|
 
  签到天数: 2 天 [LV.1]初来乍到  
 | 
 
调试与反调试、反反调试是永远存在的问题,现在的大多数软件也加了反调试功能(尤其是网游),保护其不被调试破解。 
    调试大家都知道有很多这方面的工具,如OD、CE、ICE...,反调试大家也知道有很多种方法,如自己加代码实现、加壳等,反反调试... 
      今天做了一个小程序,采用了19种方式来检测自己是否被调试、下断等,这只是一个小测试,没有加入驱动和hook等乱七八糟的东西,纯以代码实现。有兴趣的朋友可以帮忙测试下。好了,废话到此为止,我们来看代码:(代码随便写的,如有BUG请勿取笑) 
 
 
unit Unit1; 
 
inte**ce 
 
uses 
  JwaNative,  Debug, 
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, StdCtrls, ExtCtrls; 
 
type 
  TForm1 = class(TForm) 
      Button1: TButton; 
      Timer1: TTimer; 
      Button2: TButton; 
      Label1: TLabel; 
      Label2: TLabel; 
      Label3: TLabel; 
      Label4: TLabel; 
      procedure Timer1Timer(Sender: TObject); 
      procedure Button2Click(Sender: TObject); 
  private 
      { Private declarations } 
  public 
      { Public declarations } 
  end; 
 
var 
  Form1: TForm1; 
 
function FD_IsDebuggerPresent(): Boolean; 
  function PD_PEB_BeingDebuggedFlag(): Boolean; 
  function FD_PEB_NtGlobalFlags(): Boolean; 
  function FD_Heap_HeapFlags(): Boolean; 
  function FD_Heap_ForceFlags(): Boolean; 
  function FD_CheckRemoteDebuggerPresent(): Boolean; 
  function FD_NtQueryInfoProc_DbgPort(): Boolean; 
  function FD_NtQueryInfoProc_DbgObjHandle(): Boolean; 
  function FD_NtQueryInfoProc_DbgFlags(): Boolean; 
  function FD_SeDebugPrivilege(csrssPid: THandle): Boolean; 
  function FD_Find_Debugger_Window(): Boolean; 
  function FD_Exception_Closehandle(): Boolean; 
  function FD_Exception_Int3(): Boolean; 
  function FD_OutputDebugString(): boolean; 
  function FD_Check_StartupInfo(): Boolean; 
  function FD_INT_2d(): Boolean; 
  function FS_OD_Int3_Pushfd(): Boolean; 
  function FS_SI_Exception_Int1(): Boolean; 
  function FB_HWBP_Exception(): Boolean; 
 
implementation 
 
{$R *.dfm} 
procedure TForm1.Button2Click(Sender: TObject); 
begin 
  ExitProcess(0); 
end; 
 
procedure TForm1.Timer1Timer(Sender: TObject); 
var 
  isdebugged: DWORD; 
  retLen: PULONG; 
  ProcessHandle: DWORD; 
  tmp: PChar; 
label 
  IsDebug; 
begin 
  try 
      //反调试检测 
 
      isdebugged := 0; 
      if FB_HWBP_Exception then isdebugged := isdebugged + 1; 
      label4.Caption := IntToStr(isdebugged); 
      if FS_SI_Exception_Int1 then isdebugged := isdebugged + 1; 
      label4.Caption := IntToStr(isdebugged); 
      if FD_Find_Debugger_Window then isdebugged := isdebugged + 1; 
      if FD_IsDebuggerPresent then isdebugged := isdebugged + 1; 
      if PD_PEB_BeingDebuggedFlag then isdebugged := isdebugged + 1; 
      if FD_PEB_NtGlobalFlags then isdebugged := isdebugged + 1; 
      if FD_Heap_HeapFlags then isdebugged := isdebugged + 1; 
      if FD_CheckRemoteDebuggerPresent then isdebugged := isdebugged + 1; 
      if FD_NtQueryInfoProc_DbgPort then isdebugged := isdebugged + 1; 
      if FD_NtQueryInfoProc_DbgObjHandle then isdebugged := isdebugged + 1; 
      if FD_NtQueryInfoProc_DbgFlags then isdebugged := isdebugged + 1; 
      if FD_SeDebugPrivilege(916) then isdebugged := isdebugged + 1; 
      if FD_Exception_Closehandle then isdebugged := isdebugged + 1; 
      if FD_Exception_Int3 then isdebugged := isdebugged + 1; 
      if FD_OutputDebugString then isdebugged := isdebugged + 1; 
      if FD_Check_StartupInfo then isdebugged := isdebugged + 1; 
      if FD_INT_2d then isdebugged := isdebugged + 1; 
      if FS_OD_Int3_Pushfd then isdebugged := isdebugged + 1; 
 
 
IsDebug: 
      if isdebugged > 0 then 
        tmp := pchar('存在调试器!(共有' + inttostr(isdebugged) + '种方法检测出调试器)') 
      else 
        tmp := '正常执行!'; 
      Label1.Caption := tmp; 
  except 
      on e: Exception do 
      debug.DebugPrint('发生错误!' + #10#13 + e.Message); 
  end; 
end; 
 
 
//使用IsDebuggerPresent这个API来检测是否被调试 
function FD_IsDebuggerPresent(): Boolean; 
begin 
  if IsDebuggerPresent then 
      Result := True 
  else 
      Result := False; 
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. 
 
 
现在你可以试试用CE、OD等调试工具,看这个小小的程序能检测出自己被调试吗? |   
 
 
 
 |