宝峰科技

 找回密码
 注册

QQ登录

只需一步,快速开始

智能终端设备维修查询系统注册会员邮箱认证须知!
查看: 3088|回复: 0

[分享] Delphi制作19种反调试检测调试程序

[复制链接]

该用户从未签到

破解狼人 发表于 2010-10-31 21:30:17 | 显示全部楼层 |阅读模式

欢迎您注册加入!这里有您将更精采!

您需要 登录 才可以下载或查看,没有账号?注册

x
曾几何时,我徘徊在了调试与反调试的地平线上。 调试与反调试、反反调试是永远存在的问题,现在的大多数软件也加了反调试功能(尤其是网游),保护其不被调试破解。调试大家都知道有很多这方面的工具,如OD、CE、ICE...,反调试大家也知道有很多种方法,如自己加代码实现、加壳等,反反调试...今天做了一个小程序,采用了19种方式来检测自己是否被调试、下断等,这只是一个小测试,没有加入驱动和hook等乱七八糟的东西,纯以代码实现。有兴趣的朋友可以帮忙测试下。好了,废话到此为止,我们来看代码:(代码随便写的,如有BUG请勿取笑)
  1. unit Unit1;

  2. interface

  3. uses
  4.   JwaNative,  Debug,
  5.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  6.   Dialogs, StdCtrls, ExtCtrls;

  7. type
  8.   TForm1 = class(TForm)
  9.       Button1: TButton;
  10.       Timer1: TTimer;
  11.       Button2: TButton;
  12.       Label1: TLabel;
  13.       Label2: TLabel;
  14.       Label3: TLabel;
  15.       Label4: TLabel;
  16.       procedure Timer1Timer(Sender: TObject);
  17.       procedure Button2Click(Sender: TObject);
  18.   private
  19.       { Private declarations }
  20.   public
  21.       { Public declarations }
  22.   end;

  23. var
  24.   Form1: TForm1;

  25. function FD_IsDebuggerPresent(): Boolean;
  26.   function PD_PEB_BeingDebuggedFlag(): Boolean;
  27.   function FD_PEB_NtGlobalFlags(): Boolean;
  28.   function FD_Heap_HeapFlags(): Boolean;
  29.   function FD_Heap_ForceFlags(): Boolean;
  30.   function FD_CheckRemoteDebuggerPresent(): Boolean;
  31.   function FD_NtQueryInfoProc_DbgPort(): Boolean;
  32.   function FD_NtQueryInfoProc_DbgObjHandle(): Boolean;
  33.   function FD_NtQueryInfoProc_DbgFlags(): Boolean;
  34.   function FD_SeDebugPrivilege(csrssPid: THandle): Boolean;
  35.   function FD_Find_Debugger_Window(): Boolean;
  36.   function FD_Exception_Closehandle(): Boolean;
  37.   function FD_Exception_Int3(): Boolean;
  38.   function FD_OutputDebugString(): boolean;
  39.   function FD_Check_StartupInfo(): Boolean;
  40.   function FD_INT_2d(): Boolean;
  41.   function FS_OD_Int3_Pushfd(): Boolean;
  42.   function FS_SI_Exception_Int1(): Boolean;
  43.   function FB_HWBP_Exception(): Boolean;

  44. implementation

  45. {$R *.dfm}
  46. procedure TForm1.Button2Click(Sender: TObject);
  47. begin
  48.   ExitProcess(0);
  49. end;

  50. procedure TForm1.Timer1Timer(Sender: TObject);
  51. var
  52.   isdebugged: DWORD;
  53.   retLen: PULONG;
  54.   ProcessHandle: DWORD;
  55.   tmp: PChar;
  56. label
  57.   IsDebug;
  58. begin
  59.   try
  60.       //反调试检测

  61.       isdebugged := 0;
  62.       if FB_HWBP_Exception then isdebugged := isdebugged + 1;
  63.       label4.Caption := IntToStr(isdebugged);
  64.       if FS_SI_Exception_Int1 then isdebugged := isdebugged + 1;
  65.       label4.Caption := IntToStr(isdebugged);
  66.       if FD_Find_Debugger_Window then isdebugged := isdebugged + 1;
  67.       if FD_IsDebuggerPresent then isdebugged := isdebugged + 1;
  68.       if PD_PEB_BeingDebuggedFlag then isdebugged := isdebugged + 1;
  69.       if FD_PEB_NtGlobalFlags then isdebugged := isdebugged + 1;
  70.       if FD_Heap_HeapFlags then isdebugged := isdebugged + 1;
  71.       if FD_CheckRemoteDebuggerPresent then isdebugged := isdebugged + 1;
  72.       if FD_NtQueryInfoProc_DbgPort then isdebugged := isdebugged + 1;
  73.       if FD_NtQueryInfoProc_DbgObjHandle then isdebugged := isdebugged + 1;
  74.       if FD_NtQueryInfoProc_DbgFlags then isdebugged := isdebugged + 1;
  75.       if FD_SeDebugPrivilege(916) then isdebugged := isdebugged + 1;
  76.       if FD_Exception_Closehandle then isdebugged := isdebugged + 1;
  77.       if FD_Exception_Int3 then isdebugged := isdebugged + 1;
  78.       if FD_OutputDebugString then isdebugged := isdebugged + 1;
  79.       if FD_Check_StartupInfo then isdebugged := isdebugged + 1;
  80.       if FD_INT_2d then isdebugged := isdebugged + 1;
  81.       if FS_OD_Int3_Pushfd then isdebugged := isdebugged + 1;


  82. IsDebug:
  83.       if isdebugged > 0 then
  84.         tmp := pchar('存在调试器!(共有' + inttostr(isdebugged) + '种方法检测出调试器)')
  85.       else
  86.         tmp := '正常执行!';
  87.       Label1.Caption := tmp;
  88.   except
  89.       on e: Exception do
  90.       debug.DebugPrint('发生错误!' + #10#13 + e.Message);
  91.   end;
  92. end;


  93. //使用IsDebuggerPresent这个API来检测是否被调试
  94. function FD_IsDebuggerPresent(): Boolean;
  95. begin
  96.   if IsDebuggerPresent then
  97.       Result := True
  98.   else
  99.       Result := False;
  100. end;

  101. //使用查看PEB结构中标志位beingDegug来检测是否被调试
  102. function PD_PEB_BeingDebuggedFlag(): Boolean;
  103. begin
  104.   asm
  105.       mov @result, 0
  106.       mov eax, fs:[30h]  //EAX = TEB.ProcessEnvironmentBlock
  107.       add eax, 2
  108.       mov eax, [eax]
  109.       and eax, $000000ff //AL = PEB.BeingDebugged
  110.       test eax, eax
  111.       jne @IsDebug
  112.       jmp @exit
  113.   @IsDebug:
  114.       mov @result, 1
  115.   @exit:
  116.   end;
  117. end;

  118. //查看PEB结构中的NtGlobalFlags标志位来检测是否被调试
  119. function FD_PEB_NtGlobalFlags(): Boolean;
  120. begin
  121.   asm
  122.       mov @result, 0
  123.       mov eax, fs:[30h]
  124.       mov eax, [eax+68h]
  125.       and eax, $70      //NtGlobalFlags
  126.       test eax, eax
  127.       jne @IsDebug
  128.       jmp @exit
  129.   @IsDebug:
  130.       mov @result, 1
  131.   @exit:
  132.   end;
  133. end;

  134. //在PEB结构中,使用HeapFlags来
  135. //检测调试器也不是非常可靠,但却很常用。
  136. //这个域由一组标志组成,正常情况下,该值应为2
  137. function FD_Heap_HeapFlags(): Boolean;
  138. begin
  139.   asm
  140.       mov @result, 0
  141.       mov eax, fs:[30h]
  142.       mov eax, [eax+18h] //PEB.ProcessHeap
  143.       mov eax, [eax+0ch] //PEB.ProcessHeap.Flags
  144.       cmp eax, 2
  145.       jne @IsDebug
  146.       jmp @exit
  147.   @IsDebug:
  148.       mov @result, 1
  149.   @exit:
  150.   end;
  151. end;

  152. //检测PEB结构中的标志位ForceFlags,它也由一
  153. //组标志组成,正常情况下,该值应为0
  154. function FD_Heap_ForceFlags(): Boolean;
  155. begin
  156.   asm
  157.       mov @result, 0
  158.       mov eax, fs:[30h]
  159.       mov eax, [eax+18h]       mov eax, [eax+10h]
  160.       test eax, eax
  161.       jne @IsDebug
  162.       jmp @exit
  163.   @IsDebug:
  164.       mov @result, 1
  165.   @exit:
  166.   end;
  167. end;

  168. //使用API:CheckRemoteDebuggerPresent
  169. function FD_CheckRemoteDebuggerPresent(): Boolean;
  170. var
  171.   Func_Addr: Pointer;
  172.   hModule: Cardinal;
  173.   pDebugBool: PBool;
  174. begin
  175.   result := false;
  176.   hModule := GetModuleHandle('kernel32.dll');
  177.   if hModule = INVALID_HANDLE_VALUE then exit;
  178.   Func_addr := GetProcAddress(hModule, 'CheckRemoteDebuggerPresent');
  179.   if (Func_addr <> nil) then begin
  180.       asm
  181.         lea eax, pDebugBool
  182.         push eax
  183.         push $ffffffff
  184.         call Func_addr
  185.         cmp dword ptr[pDebugBool], 0
  186.         jne @IsDebug
  187.         jmp @exit
  188.       @IsDebug:
  189.         mov @result, 1
  190.       @exit:
  191.       end;
  192.   end;
  193. end;

  194. //使用ntdll_NtQueryInformationProcess()来查询
  195. //ProcessDebugPort可以用来检测反调试
  196. function FD_NtQueryInfoProc_DbgPort(): Boolean;
  197. var
  198.   Func_Addr: Pointer;
  199.   hModule: Cardinal;
  200.   ReturnLength: PULONG;
  201.   dwDebugPort: PDWORD;
  202. begin
  203.   result := false;
  204.   hModule := GetModuleHandle('ntdll.dll');
  205.   if hModule = INVALID_HANDLE_VALUE then exit;
  206.   Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess');
  207.   if (Func_addr <> nil) then begin
  208.       asm
  209.         lea eax, ReturnLength
  210.         push eax                    //ReturnLength
  211.         push 4                      //ProcessInformationLength
  212.         lea eax, dwDebugPort
  213.         push eax                    //ProcessInformation
  214.         push 7                      //ProcessInformationClass
  215.         push $FFFFFFFF              //ProcessHandle
  216.         call Func_addr              //NtQueryInformationProcess
  217.         cmp [dwDebugPort], 0
  218.         jne @IsDebug
  219.         jmp @exit
  220.       @IsDebug:
  221.         mov @result, 1
  222.       @exit:
  223.       end;
  224.   end;
  225. end;

  226. //查询winXp自动创建的"debug object"的句柄
  227. function FD_NtQueryInfoProc_DbgObjHandle(): Boolean;
  228. var
  229.   Func_Addr: Pointer;
  230.   hModule: Cardinal;
  231.   ReturnLength: PULONG;
  232.   dwDebugPort: PDWORD;
  233. begin
  234.   result := false;
  235.   hModule := GetModuleHandle('ntdll.dll');
  236.   if hModule = INVALID_HANDLE_VALUE then exit;
  237.   Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess');
  238.   if (Func_addr <> nil) then begin
  239.       asm
  240.         lea eax, ReturnLength
  241.         push eax
  242.         push 4
  243.         lea eax, dwDebugPort
  244.         push eax
  245.         push $1E
  246.         push $FFFFFFFF
  247.         call Func_addr
  248.         mov eax, [dwDebugPort]
  249.         test eax, eax
  250.         jnz @IsDebug
  251.         jmp @exit
  252.       @IsDebug:
  253.         mov @result, 1
  254.       @exit:
  255.       end;
  256.   end;
  257. end;

  258. //查询winXp自动创建的"debug object",
  259. //未公开的ProcessDebugFlags类,当调试器存在时,它会返回false
  260. function FD_NtQueryInfoProc_DbgFlags(): Boolean;
  261. var
  262.   Func_Addr: Pointer;
  263.   hModule: Cardinal;
  264.   ReturnLength: PULONG;
  265.   dwDebugPort: PDWORD;
  266. begin
  267.   result := false;
  268.   hModule := GetModuleHandle('ntdll.dll');
  269.   if hModule = INVALID_HANDLE_VALUE then exit;
  270.   Func_addr := GetProcAddress(hModule, 'ZwQueryInformationProcess');
  271.   if (Func_addr <> nil) then begin
  272.       asm
  273.         lea eax, ReturnLength
  274.         push eax
  275.         push 4
  276.         lea eax, dwDebugPort
  277.         push eax
  278.         push $1F
  279.         push $FFFFFFFF
  280.         call Func_addr
  281.         mov eax, [dwDebugPort]
  282.         test eax, eax
  283.         jz @IsDebug
  284.         jmp @exit
  285.       @IsDebug:
  286.         mov @result, 1
  287.       @exit:
  288.       end;
  289.   end;
  290. end;

  291. //是否获得SeDebugPrivilege
  292. //是否可以使用openprocess操作CSRSS.EXE
  293. function FD_SeDebugPrivilege(csrssPid: THandle): Boolean;
  294. var
  295.   hTmp: Cardinal;
  296. begin
  297.   result := False;
  298.   hTmp := OpenProcess(PROCESS_ALL_ACCESS,false,csrssPid);
  299.   if hTmp <> 0 then begin
  300.       CloseHandle (hTmp);
  301.       result := true;
  302.   end;
  303. end;

  304. //查找已知的调试器的窗口来检测是否被调试
  305. function FD_Find_Debugger_Window(): Boolean;
  306. var
  307.   whWnd: DWORD;
  308. begin
  309.   result := True;
  310.   //ollydbg v1.1
  311.   whWnd := FindWindow('icu_dbg', nil);
  312.   if whWnd <> 0 then Exit;
  313.   //ollyice pe--diy
  314.   whWnd := FindWindow('pe--diy', nil);
  315.   if whWnd <> 0 then Exit;
  316.   //ollydbg ?-
  317.   whWnd := FindWindow('ollydbg', nil);
  318.   if whWnd <> 0 then Exit;
  319.   //windbg
  320.   whWnd := FindWindow('WinDbgFrameClass', nil);
  321.   if whWnd <> 0 then Exit;
  322.   //dede3.50
  323.   whWnd := FindWindow('TDeDeMainForm', nil);
  324.   if whWnd <> 0 then Exit;
  325.   //IDA5.20
  326.   whWnd := FindWindow('TIdaWindow', nil);
  327.   if whWnd <> 0 then Exit;
  328.   result := False;
  329. end;

  330. //给CloseHandle()函数一个无效句柄作为输入参数
  331. //是否触发一个EXCEPTION_INVALID_HANDLE (0xc0000008)的异常
  332. function FD_Exception_Closehandle(): Boolean;
  333. begin
  334.   try
  335.       CloseHandle($00001234);
  336.       result := False;
  337.   except
  338.       Result := True;
  339.   end;
  340. end;

  341. //int3 检测
  342. function FD_Exception_Int3(): Boolean;
  343. begin
  344.       asm
  345.         mov @result, 0
  346.         push offset @exception_handler //set exception handler
  347.         push dword ptr fs:[0h]
  348.         mov dword ptr fs:[0h],esp
  349.         xor eax,eax       //reset EAX invoke int3
  350.         int 3h
  351.         pop dword ptr fs:[0h] //restore exception handler
  352.         add esp,4
  353.         test eax,eax // check the flag
  354.         je @IsDebug
  355.         jmp @exit
  356.       @exception_handler:
  357.         mov eax,dword ptr [esp+$c]//EAX = ContextRecord
  358.         mov dword ptr [eax+$b0],$ffffffff//set flag (ContextRecord.EAX)
  359.         inc dword ptr [eax+$b8]//set ContextRecord.EIP
  360.         xor eax,eax
  361.         ret
  362.       @IsDebug:
  363.         xor eax,eax
  364.         inc eax
  365.         mov esp,ebp
  366.         pop ebp
  367.         ret
  368.       @exit:
  369.         xor eax,eax
  370.         mov esp,ebp
  371.         pop ebp
  372.         ret
  373.       end;
  374. end;

  375. //使用OutputDebugString函数来检测
  376. function FD_OutputDebugString(): boolean;
  377. var
  378.   tmpD: DWORD;
  379. begin
  380.   OutputDebugString('');
  381.   tmpD := GetLastError;
  382.   if(tmpD = 0) then
  383.       result := true
  384.   else
  385.       Result := false;
  386. end;

  387. //检测STARTUPINFO结构中的值是否为0
  388. function FD_Check_StartupInfo(): Boolean;
  389. var
  390.   si: STARTUPINFO;
  391. begin
  392.   ZeroMemory(@si, sizeof(si));
  393.   si.cb := sizeof(si);
  394.   GetStartupInfo(si);
  395.   if (si.dwX <> 0) and (si.dwY <> 0)
  396.       and (si.dwXCountChars <> 0)
  397.       and (si.dwYCountChars <> 0)
  398.       and (si.dwFillAttribute <> 0)
  399.       and (si.dwXSize <> 0)
  400.       and (si.dwYSize <> 0) then begin
  401.       result := true
  402.   end else
  403.       result := false;
  404. end;

  405. //使用int 2dh中断的异常检测
  406. function FD_INT_2d(): Boolean;
  407. begin
  408.   try
  409.       asm
  410.         int 2dh
  411.         inc eax //any opcode of singlebyte.
  412.                 //;or u can put some junkcode,
  413.                 //"0xc8"..."0xc2"..."0xe8"..."0xe9"
  414.         mov @result, 1
  415.       end;
  416.   except
  417.       Result := false;
  418.   end;
  419. end;

  420. //最近比较牛的反调试
  421. function FS_OD_Int3_Pushfd(): Boolean;
  422. begin
  423.   asm
  424.       push offset @e_handler //set exception handler
  425.       push dword ptr fs:[0h]
  426.       mov dword ptr fs:[0h],esp
  427.       xor eax,eax //reset EAX invoke int3
  428.       int 3h
  429.       pushfd
  430.       nop
  431.       nop
  432.       nop
  433.       nop
  434.       pop dword ptr fs:[0h]  //restore exception handler
  435.       add esp,4

  436.       test eax,eax  //check the flag
  437.       je @IsDebug
  438.       jmp @Exit

  439. @e_handler:
  440.       push offset @e_handler1  //set exception handler
  441.       push dword ptr fs:[0h]
  442.       mov dword ptr fs:[0h],esp
  443.       xor eax,eax  //reset EAX invoke int3
  444.       int 3h
  445.       nop
  446.       pop dword ptr fs:[0h]  //restore exception handler
  447.       add esp,4      //EAX = ContextRecord
  448.       mov ebx,eax  //dr0=>ebx
  449.       mov eax,dword ptr [esp+$c]     //set ContextRecord.EIP
  450.       inc dword ptr [eax+$b8]
  451.       mov dword ptr [eax+$b0],ebx  //dr0=>eax
  452.       xor eax,eax
  453.       ret

  454. @e_handler1:        //EAX = ContextRecord
  455.       mov eax,dword ptr [esp+$c]     //set ContextRecord.EIP
  456.       inc dword ptr [eax+$b8]
  457.       mov ebx,dword ptr[eax+$04]
  458.       mov dword ptr [eax+$b0],ebx  //dr0=>eax
  459.       xor eax,eax
  460.       ret

  461. @IsDebug:
  462.       mov @result, 1
  463.       mov esp,ebp
  464.       pop ebp
  465.       ret
  466.   @Exit:
  467.       mov esp,ebp
  468.       pop ebp
  469.       ret
  470.   end;
  471. end;

  472. //使用int1的异常检测来反调试
  473. function FS_SI_Exception_Int1(): Boolean;
  474. begin
  475.   asm
  476.       mov @result, 0
  477.       push offset @eh_int1 //set exception handler
  478.       push dword ptr fs:[0h]
  479.       mov dword ptr fs:[0h],esp
  480.       xor eax,eax  //reset flag(EAX) invoke int3
  481.       int 1h
  482.       pop dword ptr fs:[0h] //restore exception handler
  483.       add esp,4
  484.       test eax, eax  // check the flag
  485.       je @IsDebug
  486.       jmp @Exit

  487. @eh_int1:
  488.       mov eax,[esp+$4]
  489.       mov ebx,dword ptr [eax]
  490.       mov eax,dword ptr [esp+$c] //EAX = ContextRecord
  491.       mov dword ptr [eax+$b0],1 //set flag (ContextRecord.EAX)
  492.       inc dword ptr [eax+$b8] //set ContextRecord.EIP
  493.       inc dword ptr [eax+$b8] //set ContextRecord.EIP
  494.       xor eax, eax
  495.       ret
  496.   @IsDebug:
  497.       mov @result, 1
  498.       mov esp,ebp
  499.       pop ebp
  500.       ret
  501.   @Exit:
  502.       xor eax, eax
  503.       mov esp,ebp
  504.       pop ebp
  505.       ret
  506.   end;
  507. end;

  508. //在异常处理过程中检测硬件断点
  509. function FB_HWBP_Exception(): Boolean;
  510. begin
  511.   asm
  512.       push offset @exeception_handler //set exception handler
  513.       push dword ptr fs:[0h]
  514.       mov dword ptr fs:[0h],esp
  515.       xor eax,eax  //reset EAX invoke int3
  516.       int 1h
  517.       pop dword ptr fs:[0h]  //restore exception handler
  518.       add esp,4  //test if EAX was updated (breakpoint identified)
  519.       test eax,eax
  520.       jnz @IsDebug
  521.       jmp @Exit

  522. @exeception_handler:       //EAX = CONTEXT record
  523.       mov eax,dword ptr [esp+$c]  //check if Debug Registers Context.Dr0-Dr3 is not zero
  524.       cmp dword ptr [eax+$04],0
  525.       jne @hardware_bp_found
  526.       cmp dword ptr [eax+$08],0
  527.       jne @hardware_bp_found
  528.       cmp dword ptr [eax+$0c],0
  529.       jne @hardware_bp_found
  530.       cmp dword ptr [eax+$10],0
  531.       jne @hardware_bp_found
  532.       jmp @exception_ret
  533.   @hardware_bp_found: //set Context.EAX to signal breakpoint found
  534.       mov dword ptr [eax+$b0],$FFFFFFFF
  535.   @exception_ret:       //set Context.EIP upon return
  536.       inc dword ptr [eax+$b8] //set ContextRecord.EIP
  537.       inc dword ptr [eax+$b8] //set ContextRecord.EIP
  538.       xor eax,eax
  539.       ret
  540.   @IsDebug:
  541.       mov @result, 1
  542.       mov esp,ebp
  543.       pop ebp
  544.       ret
  545.   @Exit:
  546.       xor eax, eax
  547.       mov esp,ebp
  548.       pop ebp
  549.       ret
  550.   end;
  551. end;

  552. end.
复制代码
现在你可以试试用CE、OD等调试工具,看这个小小的程序能检测出自己被调试吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

免责声明

本站中所有被研究的素材与信息全部来源于互联网,版权争议与本站无关。本站所发布的任何软件编程开发或软件的逆向分析文章、逆向分析视频、补丁、注册机和注册信息,仅限用于学习和研究软件安全的目的。全体用户必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。学习编程开发技术或逆向分析技术是为了更好的完善软件可能存在的不安全因素,提升软件安全意识。所以您如果喜欢某程序,请购买注册正版软件,获得正版优质服务!不得将上述内容私自传播、销售或者用于商业用途!否则,一切后果请用户自负!

QQ|Archiver|手机版|小黑屋|联系我们|宝峰科技 ( 滇公网安备 53050202000040号 | 滇ICP备09007156号-2 )

Copyright © 2001-2023 Discuz! Team. GMT+8, 2024-12-22 20:24 , File On Powered by Discuz! X3.49

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