加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

反调试技术(以OD为例附核心原代码)【delphi】

(2011-09-11 00:11:52)
标签:

delphi

反调试

杂谈

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


unit Unit1;

interface

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等调试工具,看这个小小的程序能检测出自己被调试吗?

天天精品下载:http://www.ttdown.com/
泡泡网高速下载http://download.pcpop.com/

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有