|
欢迎您注册加入!这里有您将更精采!
您需要 登录 才可以下载或查看,没有账号?注册
x
曾几何时,我徘徊在了调试与反调试的地平线上。 调试与反调试、反反调试是永远存在的问题,现在的大多数软件也加了反调试功能(尤其是网游),保护其不被调试破解。调试大家都知道有很多这方面的工具,如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等调试工具,看这个小小的程序能检测出自己被调试吗? |
|