Delphi制作19种反调试检测调试程序
曾几何时,我徘徊在了调试与反调试的地平线上。 调试与反调试、反反调试是永远存在的问题,现在的大多数软件也加了反调试功能(尤其是网游),保护其不被调试破解。调试大家都知道有很多这方面的工具,如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://EAX = TEB.ProcessEnvironmentBlock
add eax, 2
mov 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:
mov eax,
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:
mov eax, //PEB.ProcessHeap
mov eax, //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:
mov eax, mov eax,
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, 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 , 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,
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,
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:
mov dword ptr fs:,esp
xor eax,eax //reset EAX invoke int3
int 3h
pop dword ptr fs: //restore exception handler
add esp,4
test eax,eax // check the flag
je @IsDebug
jmp @exit
@exception_handler:
mov eax,dword ptr //EAX = ContextRecord
mov dword ptr ,$ffffffff//set flag (ContextRecord.EAX)
inc dword ptr //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:
mov dword ptr fs:,esp
xor eax,eax //reset EAX invoke int3
int 3h
pushfd
nop
nop
nop
nop
pop dword ptr fs://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:
mov dword ptr fs:,esp
xor eax,eax//reset EAX invoke int3
int 3h
nop
pop dword ptr fs://restore exception handler
add esp,4 //EAX = ContextRecord
mov ebx,eax//dr0=>ebx
mov eax,dword ptr //set ContextRecord.EIP
inc dword ptr
mov dword ptr ,ebx//dr0=>eax
xor eax,eax
ret
@e_handler1: //EAX = ContextRecord
mov eax,dword ptr //set ContextRecord.EIP
inc dword ptr
mov ebx,dword ptr
mov dword ptr ,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:
mov dword ptr fs:,esp
xor eax,eax//reset flag(EAX) invoke int3
int 1h
pop dword ptr fs: //restore exception handler
add esp,4
test eax, eax// check the flag
je @IsDebug
jmp @Exit
@eh_int1:
mov eax,
mov ebx,dword ptr
mov eax,dword ptr //EAX = ContextRecord
mov dword ptr ,1 //set flag (ContextRecord.EAX)
inc dword ptr //set ContextRecord.EIP
inc dword ptr //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:
mov dword ptr fs:,esp
xor eax,eax//reset EAX invoke int3
int 1h
pop dword ptr fs://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 //check if Debug Registers Context.Dr0-Dr3 is not zero
cmp dword ptr ,0
jne @hardware_bp_found
cmp dword ptr ,0
jne @hardware_bp_found
cmp dword ptr ,0
jne @hardware_bp_found
cmp dword ptr ,0
jne @hardware_bp_found
jmp @exception_ret
@hardware_bp_found: //set Context.EAX to signal breakpoint found
mov dword ptr ,$FFFFFFFF
@exception_ret: //set Context.EIP upon return
inc dword ptr //set ContextRecord.EIP
inc dword ptr //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等调试工具,看这个小小的程序能检测出自己被调试吗?
页:
[1]