飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 5306|回复: 7

Delphi 制作防调试程序[19种反调试检测法]

[复制链接]
  • TA的每日心情

    2017-6-17 16:59
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2009-12-22 20:03:07 | 显示全部楼层 |阅读模式
    调试与反调试、反反调试是永远存在的问题,现在的大多数软件也加了反调试功能(尤其是网游),保护其不被调试破解
        调试大家都知道有很多这方面的工具,如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等调试工具,看这个小小的程序能检测出自己被调试吗?
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2015-8-23 23:49
  • 签到天数: 27 天

    [LV.4]偶尔看看III

    发表于 2009-12-22 21:25:45 | 显示全部楼层
    膜拜下强大的火花!
    PYG19周年生日快乐!
  • TA的每日心情

    2017-6-17 16:59
  • 签到天数: 2 天

    [LV.1]初来乍到

     楼主| 发表于 2009-12-22 21:37:04 | 显示全部楼层
    晕啊。。我是转帖/:L /:L ,觉得交流下有利于我们破解!:funk:
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2009-12-23 08:58:22 | 显示全部楼层
    学习一下
    PYG19周年生日快乐!
  • TA的每日心情
    擦汗
    2019-1-20 21:42
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2009-12-23 13:04:58 | 显示全部楼层
    csdn上一个副版的原创,写得不错
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2016-4-29 07:52
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2009-12-23 14:27:33 | 显示全部楼层
    提示: 作者被禁止或删除 内容自动屏蔽
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2009-12-28 00:37:00 | 显示全部楼层
    方法都是一样/:L
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-5 21:36:52 | 显示全部楼层
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表