作 者: 玩命
时 间: 2008-08-21,10:38
链 接: http://bbs.pediy.com/showthread.php?t=71113
反调试是软件保护壳的最基本的功能之一。
反调试方法也是多种多样。通过调用标准的API接口,计算指令时间差。查看当调试器加载后的
内存的一些标志,还有就是判断当前运行环境是否合乎逻辑等方法。这里收集了一些反调试的方法,其中的命名规则使用了壳狼的反调试程序的方式,希望不要和我收取版权的费用。^_^,其中借鉴了不少壳狼的函数。增加了一些,也删除了一些。大部分的参考资料来自<<脱壳的艺术>>,<<Anti-UnPacker Tricks>>与<<加密与解密第三版>>。
这里要说明的一点是。每个函数编写都是自己建立堆栈了,看的不习惯的多看下就习惯了 呵呵。
原因也很简单,MASM不允许在函数内定义函数了。
这些函数还有一个要讲的是。因为最后这些函数要在以后的章节中用到
为了能允许用户自定义反调试的功能。免去重定位的麻烦,所以
利用栈传递了API集合地址和外部函数集合的地址。
朋友们还是先看代码了。。。
利用IsDebuggerPresent确定是否存在,IsDebuggerPresent是WIN提供的一个标准调试API
用于确定是否存在调试器。这个方法很简单TRUE为存在,FASLE则为不存在。
FD_IsDebuggerPresent: FD_IsDebugger_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FD_IsDebugger_Arg_Win32Api] assume eax : ptr WIN32APIBASE call dword ptr [eax].xIsDebuggerPresent Exit_FD_IsDebuggerPresent: assume eax : nothing retn 04h End_FD_IsDebuggerPresent:
我们更进一步的剖析IsDebuggerPresent函数,逆向它后即可得知。
这个函数读取当前进程的PEB中的BeingDebugger标志。
如果确定这个标志呢?
当进程加载的时候FS寄存器总是被设置成当前线程的TEB。这下就都OK
而在TEB的30h偏移处就是ProcessEnvironmentBlock了。
而PEB的偏移02h出就是BeingDebugged了。当BeingDebugger为0则没有调试器
不为0时则存在调试器。
FD_PEB_BeingDebuggedFlag: assume fs : nothing mov eax, fs:[30h] ; eax = TEB.ProcessEnvironmentBlock inc eax inc eax mov eax, dword ptr [eax] and eax, 000000FFh ; al = PEB.BeingDebugged test eax, eax jnz FD_PEB_BeingDebuggedFlag_Found Exit_PEB_BeingDebuggedFlag: retn 0 FD_PEB_BeingDebuggedFlag_Found: mov eax, 1 jmp Exit_PEB_BeingDebuggedFlag End_FD_PEB_BeingDebuggedFlag:
在当BeingDebugged被设置为TRUE时,存在与PEB中的
NtGlobalFlag也会做出相应的改变。查看WIN2K代码的LdrpInitialize
会发现以下代码
if (Peb->BeingDebugged)
Peb->NtGlobalFlag |= ***_HEAP_ENABLE_FREE_CHECK |
***_HEAP_ENABLE_TAIL_CHECK |
***_HEAP_VALIDATE_PARAMETERS;
这个组合值为70h。所以我们又得到一个反调试的函数
FD_PEB_NtGlobalFlags: assume fs : nothing mov eax, fs:[30h] mov eax, dword ptr [eax+68h] and eax, 070h test eax, eax jnz FD_PEB_NtGlobalFlags_Found Exit_FD_PEB_NtGlobalFlags: retn 0 FD_PEB_NtGlobalFlags_Found: mov eax, 1 jmp Exit_FD_PEB_NtGlobalFlags End_FD_PEB_NtGlobalFlags:
BeingDebugger标志还会影响ProcessHeap.Flags
如果这个标志不为0则存在调试器。
FD_Heap_ForceFlags: assume fs : nothing mov eax, fs:[30h] mov eax, dword ptr [eax+18h] ; PEB.ProcessHeap mov eax, dword ptr [eax+10h] ; PEB.ProcessHeap.Flags test eax, eax jnz Found_FD_Heap_ForceFlags Exit_FD_Heap_ForceFlag: retn 0 Found_FD_Heap_ForceFlags: mov eax, 1 jmp Exit_FD_Heap_ForceFlag End_FD_Heap_ForceFlags:
在BeingDebugger下ProcessHeap.ForceFlags也受到影响
如果不为2则存在调试器。细节部分可以查看WIN2K的代码。
由于版本影响这里就不给出其中的代码了。
FD_Heap_HeapFlags: assume fs : nothing mov eax, fs:[30h] mov eax, dword ptr [eax+18h] ; PEB.ProcessHeap mov eax, dword ptr [eax+0ch] ; PEB.ProcessHeap.ForceFlags cmp eax, 2 jnz Found_FD_Heap_HeapFlags Exit_FD_Heap_HeapFlags: retn 0 Found_FD_Heap_HeapFlags: mov eax, 1 jmp Exit_FD_Heap_HeapFlags End_FD_Heap_HeapFlags:
通过另一个API。CheckRemoteDebuggerPresent,这个API可以检测任何进程是否被调试器
加载。这里通过GetCurrentProcess取得自身进程的句柄。进行鉴别
FD_CheckRemoteDebuggerPresent: FD_CheckRemoteDebuggerPresent_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FD_CheckRemoteDebuggerPresent_Arg_Win32Api] assume eax : ptr WIN32APIBASE push esp push esp call dword ptr [eax].xGetCurrentProcess push eax call dword ptr [eax].xCheckRemoteDebuggerPresent pop esp assume eax : nothing retn 04h End_FD_CheckRemoteDebuggerPresent:
如果逆向了CheckRemoteDebuggerPresent函数就可以明白,其实最终它是调用
NtQueryInformationProcess,查询进程的ProcessDebugPort。此值是用来维持系统
与调试器通讯的,在程序被调试器加载时系统会设置这个值为非0。
FD_NtQueryInfoProc_DbgPort: FD_NtQueryInfoProc_DbgPort_Arg_Win32Api equ 08h FD_NtQueryInfoProc_DbgPort_StackSize equ sizeof PROCESS_DEBUG_PORT_INFO FD_NtQueryInfoProc_DbgPort_ProcessInfo equ -(FD_NtQueryInfoProc_DbgPort_StackSize) push ebp mov ebp, esp sub esp, FD_NtQueryInfoProc_DbgPort_StackSize push ebx mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgPort_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push NULL push sizeof PROCESS_DEBUG_PORT_INFO lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo] push eax push ProcessDebugPort call dword ptr [ebx].xGetCurrentProcess push eax call dword ptr [ebx].xZwQueryInformationProcess test eax, eax jnz FD_NtQueryInfoProc_DbgPort_Tmp1 lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo] assume eax : ptr PROCESS_DEBUG_PORT_INFO mov eax, dword ptr [eax].DebugPort test eax, eax jnz Found_FD_NtQueryInfoProc_DbgPort FD_NtQueryInfoProc_DbgPort_Tmp1: xor eax, eax Exit_FD_NtQueryInfoProc_DbgPort: assume eax : nothing assume ebx : nothing pop ebx mov esp, ebp pop ebp retn 04h Found_FD_NtQueryInfoProc_DbgPort: mov eax, 1 jmp Exit_FD_NtQueryInfoProc_DbgPort End_FD_NtQueryInfoProc_DbgPort:
当Windows系统创建一个调试会话开始,一个调试对象也被创建并产生一个
调试句柄。我们可以查询这个句柄的值来确定是否存在调试器。
FD_NtQueryInfoProc_DbgObjHandle: FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api equ 08h FD_NtQueryInfoProc_DbgObjHandle_StackSize equ sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo equ -(FD_NtQueryInfoProc_DbgObjHandle_StackSize) push ebp mov ebp, esp sub esp, FD_NtQueryInfoProc_DbgObjHandle_StackSize push ebx mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push NULL push sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo] push eax push SystemNotImplemented8 call dword ptr [ebx].xGetCurrentProcess push eax call dword ptr [ebx].xZwQueryInformationProcess test eax, eax jnz FD_NtQueryInfoProc_DbgObjHandle_Tmp1 lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo] assume eax : ptr PROCESS_DEBUG_OBJECTHANDLE_INFO mov eax, dword ptr [eax].ObjectHandle test eax, eax jnz Found_FD_NtQueryInfoProc_DbgObjHandle FD_NtQueryInfoProc_DbgObjHandle_Tmp1: xor eax, eax Exit_FD_NtQueryInfoProc_DbgObjHandle: assume eax : nothing assume ebx : nothing pop ebx mov esp, ebp pop ebp retn 04h Found_FD_NtQueryInfoProc_DbgObjHandle: mov eax, 1 jmp Exit_FD_NtQueryInfoProc_DbgObjHandle End_FD_NtQueryInfoProc_DbgObjHandle:
也可以通过使用ZwQueryInformationProcess函数,利用SystemNotImplemented9(1fh)
返回的PROCESS_DEBUG_FLAGS_INFO结构,如果DebugFlags返回0则检测到调试器。返回非0
则没发现调试器。
FD_NtQueryInfoProc_DbgFlags: FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api equ 08h FD_NtQueryInfoProc_DbgFlags_StackSize equ sizeof PROCESS_DEBUG_FLAGS_INFO FD_NtQueryInfoProc_DbgFlags_ProcessInfo equ -(FD_NtQueryInfoProc_DbgFlags_StackSize) push ebp mov ebp, esp sub esp, FD_NtQueryInfoProc_DbgFlags_StackSize push ebx mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push NULL push sizeof PROCESS_DEBUG_FLAGS_INFO lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo] push eax push SystemNotImplemented9 call dword ptr [ebx].xGetCurrentProcess push eax call dword ptr [ebx].xZwQueryInformationProcess test eax, eax jnz FD_NtQueryInfoProc_DbgFlags_Tmp1 lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo] assume eax : ptr PROCESS_DEBUG_FLAGS_INFO mov eax, dword ptr [eax].DebugFlags test eax, eax jz Found_FD_NtQueryInfoProc_DbgFlags FD_NtQueryInfoProc_DbgFlags_Tmp1: xor eax, eax Exit_FD_NtQueryInfoProc_DbgFlags: assume eax : nothing assume ebx : nothing pop ebx mov esp, ebp pop ebp retn 04h Found_FD_NtQueryInfoProc_DbgFlags: mov eax, 1 jmp Exit_FD_NtQueryInfoProc_DbgFlags End_FD_NtQueryInfoProc_DbgFlags:
如果Windows以调试方式启动,并与系统调试器建立通讯。
通过ZwQuerySystemInformation对SystemKernelDebuggerInformation进行查询
系统中是否存在系统调试器。
FD_NtQueryInfoProc_SysKrlDbgInfo: FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api equ 08h FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize equ sizeof PROCESS_DEBUG_FLAGS_INFO FD_NtQueryInfoProc_SysKrlDbgInfo_Info equ -(sizeof PROCESS_DEBUG_FLAGS_INFO) push ebp mov ebp, esp sub esp, FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize push ebx mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push NULL push sizeof PROCESS_DEBUG_FLAGS_INFO lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info] push eax push SystemKernelDebuggerInformation call dword ptr [ebx].xGetCurrentProcess push eax call dword ptr [ebx].xZwQuerySystemInformation test eax, eax jnz FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1 lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info] assume eax : ptr PROCESS_DEBUG_FLAGS_INFO mov eax, dword ptr [eax].DebugFlags test eax, eax jz Found_FD_NtQueryInfoProc_SysKrlDbgInfo FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1: xor eax, eax Exit_FD_NtQueryInfoProc_SysKrlDbgInfo: assume eax : nothing assume ebx : nothing pop ebx mov esp, ebp pop ebp retn 04h Found_FD_NtQueryInfoProc_SysKrlDbgInfo: mov eax, 1 jmp Exit_FD_NtQueryInfoProc_SysKrlDbgInfo End_FD_NtQueryInfoProc_SysKrlDbgInfo:
当调试会话被创建,这个标志会影响堆的创建。初始化中的堆内存填充了
很多类似0ABABABABh,0BAADF00Dh,0FEEEFEEEh这三个值。可以通过检测内存看
是否存在过多的这样值。判断调试器的存在,而正常启动的程序则不会被填充。
FD_Heap_Magic: FD_Heap_Magic_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FD_Heap_Magic_Arg_Win32Api] push ebx push ecx push edx push esi push edi mov ebx, eax assume ebx : ptr WIN32APIBASE push 100h push NULL call dword ptr [ebx].xGetProcessHeap mov edi, eax ; HeapHandle push eax call dword ptr [ebx].xHeapAlloc mov esi, eax ; HeapMem xor ecx, ecx mov edx, 100h cld FD_Heap_Magic_Loop: lodsd cmp eax, 0ABABABABh jnz FD_Heap_Magic_Tmp1 inc ecx FD_Heap_Magic_Tmp1: cmp eax, 0BAADF00Dh jnz FD_Heap_Magic_Tmp2 inc ecx FD_Heap_Magic_Tmp2: cmp eax, 0FEEEFEEEh jnz FD_Heap_Magic_Tmp3 inc ecx FD_Heap_Magic_Tmp3: sub edx, 04h jnz FD_Heap_Magic_Loop push ecx ;; free heap push esi push HEAP_NO_SERIALIZE push edi call dword ptr [ebx].xHeapFree pop ecx ;; judge count cmp ecx, 10h jae Found_FD_Heap_Magic xor eax, eax Exit_FD_Heap_Magic: pop edi pop esi pop edx pop ecx pop ebx assume ebx : nothing retn 04h Found_FD_Heap_Magic: mov eax, 1 jmp Exit_FD_Heap_Magic End_FD_Heap_Magic:
一般程序是没有被设置SeDebugPrivilege,如果一个当前进程被设置SeDebugPrivilege后
它就拥有了完全控制CSRSS.EXE的权限。通过进程表快照取得CSRSS.EXE进程的PID,之后
通过OpenProcess以PROCESS_ALL_ACCESS打开。开是否能打开此进程。
FD_SeDebugPrivilege: FD_SeDebugPrivilege_Arg_Win32Api equ 08h FD_SeDebugPrivilege_StackSize equ 10h + sizeof PROCESSENTRY32 FD_SeDebugPrivilege_hProcessSnap equ -04h FD_SeDebugPrivilege_PID_csrss equ -08h FD_SeDebugPrivilege_FingFlag equ -0ch FD_SeDebugPrivilege_pe32 equ -(10h+sizeof PROCESSENTRY32) push ebp mov ebp, esp sub esp, FD_SeDebugPrivilege_StackSize push ebx push ecx push edi ;; clear stack lea edi, [ebp-FD_SeDebugPrivilege_StackSize] mov ecx, FD_SeDebugPrivilege_StackSize xor eax, eax cld rep stosb mov ebx, dword ptr [ebp+FD_SeDebugPrivilege_Arg_Win32Api] assume ebx : ptr WIN32APIBASE lea edi, [ebp+FD_SeDebugPrivilege_pe32] assume edi : ptr PROCESSENTRY32 push 0 push TH32CS_SNAPPROCESS call dword ptr [ebx].xCreateToolhelp32Snapshot cmp eax, INVALID_HANDLE_VALUE jz NotFound_FD_SeDebugPrivilege mov dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap], eax push sizeof PROCESSENTRY32 pop dword ptr [edi].dwSize push edi push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap] call dword ptr [ebx].xProcess32First test eax, eax jnz FD_SeDebugPrivilege_Loop push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap] call dword ptr [ebx].xCloseHandle jmp NotFound_FD_SeDebugPrivilege FD_SeDebugPrivilege_Loop: call FD_SeDebugPrivilege_Str db 'CSRSS.EXE',0 FD_SeDebugPrivilege_Str: lea eax, [edi].szExeFile push eax call dword ptr [ebx].xlstrcmpiA test eax, eax jnz FD_SeDebugPrivilege_Tmp2 push dword ptr [edi].th32ProcessID pop dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss] push TRUE pop dword ptr [ebp+FD_SeDebugPrivilege_FingFlag] FD_SeDebugPrivilege_Tmp2: mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag] test eax, eax jnz FD_SeDebugPrivilege_Tmp3 push edi push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap] call dword ptr [ebx].xProcess32Next test eax, eax jnz FD_SeDebugPrivilege_Loop FD_SeDebugPrivilege_Tmp3: mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag] test eax, eax jz FD_SeDebugPrivilege_Tmp4 push dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss] push FALSE push PROCESS_QUERY_INFORMATION call dword ptr [ebx].xOpenProcess test eax, eax jz FD_SeDebugPrivilege_Tmp4 push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap] call dword ptr [ebx].xCloseHandle jmp Found_FD_SeDebugPrivilege FD_SeDebugPrivilege_Tmp4: push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap] call dword ptr [ebx].xCloseHandle jmp NotFound_FD_SeDebugPrivilege Exit_FD_SeDebugPrivilege: pop edi pop ecx pop ebx assume ebx : nothing assume edi : nothing mov esp, ebp pop ebp retn 04h NotFound_FD_SeDebugPrivilege: xor eax, eax jmp Exit_FD_SeDebugPrivilege Found_FD_SeDebugPrivilege: mov eax, 1 jmp Exit_FD_SeDebugPrivilege End_FD_SeDebugPrivilege:
通过逻辑的判断也可以找到调试器所在,一般来讲程序都是有explorer.exe进程启动的(通过双击)
如果我们的进程的父进程不是explorer.exe则发现调试器。如果有调试的名称冒名是explorer.exe
那么我们判断父进程ID后进一步判断explorer.exe进程的路径是否存在于Windows目录下。如果不是
则发现调试器。此类方法也可以也被病毒用作穿透仿真机。
FD_Parent_Process: FD_Parent_Process_Arg_Win32Api equ 08h FD_Parent_Process_StackSize equ MAX_PATH + sizeof PROCESSENTRY32 + sizeof MODULEENTRY32 + 20h FD_Parent_Process_hParnet equ -04h FD_Parent_Process_PIDExplorer equ -08h FD_Parent_Process_PIDParent equ -0ch FD_Parent_Process_PIDChild equ -10h FD_Parent_Process_hSnapshot equ -14h FD_Parent_Process_pe32 equ -(20h + PROCESSENTRY32) FD_Parent_Process_me32 equ -(20h + PROCESSENTRY32 + MODULEENTRY32) FD_Parent_Process_lpszSystemInfo equ -(20h + PROCESSENTRY32 + MODULEENTRY32 + MAX_PATH) push ebp mov ebp, esp sub esp, FD_Parent_Process_StackSize push ebx push ecx push edi push esi ;; clear the stack lea edi, [ebp-FD_Parent_Process_StackSize] xor eax, eax mov ecx, FD_Parent_Process_StackSize cld rep stosb mov ebx, dword ptr [ebp+FD_Parent_Process_Arg_Win32Api] assume ebx : ptr WIN32APIBASE lea eax, [ebp+FD_Parent_Process_pe32] assume eax : ptr PROCESSENTRY32 push sizeof PROCESSENTRY32 pop dword ptr [eax].dwSize call dword ptr [ebx].xGetCurrentProcessId mov dword ptr [ebp+FD_Parent_Process_PIDChild], eax push 0 push TH32CS_SNAPPROCESS call dword ptr [ebx].xCreateToolhelp32Snapshot mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax lea eax, [ebp+FD_Parent_Process_pe32] push eax push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xProcess32First test eax, eax jz FD_Parent_Process_Tmp1 FD_Parent_Process_Loop1: lea eax, [ebp+FD_Parent_Process_pe32] push eax push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xProcess32Next test eax, eax jz FD_Parent_Process_Tmp2 call FD_Parent_Process_Str1 db "EXPLORER.EXE",0 FD_Parent_Process_Str1: lea eax, [ebp+FD_Parent_Process_pe32] lea eax, [eax].szExeFile push eax call dword ptr [ebx].xlstrcmpiA jnz FD_Parent_Process_Tmp3 mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer] test eax, eax jnz FD_Parent_Process_Tmp3 lea eax, [ebp+FD_Parent_Process_pe32] assume eax : ptr PROCESSENTRY32 push dword ptr [eax].th32ProcessID pop dword ptr [ebp+FD_Parent_Process_PIDExplorer] FD_Parent_Process_Tmp3: lea eax, [ebp+FD_Parent_Process_pe32] mov eax, dword ptr [eax].th32ProcessID sub eax, dword ptr [ebp+FD_Parent_Process_PIDChild] jnz FD_Parent_Process_Tmp4 lea eax, [ebp+FD_Parent_Process_pe32] push dword ptr [eax].th32ParentProcessID pop dword ptr [ebp+FD_Parent_Process_PIDParent] FD_Parent_Process_Tmp4: jmp FD_Parent_Process_Loop1 FD_Parent_Process_Tmp1: push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xCloseHandle jmp NotFound_FD_Parent_Process FD_Parent_Process_Tmp2: mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer] sub eax, dword ptr [ebp+FD_Parent_Process_PIDParent] jz FD_Parent_Process_Tmp5 push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xCloseHandle jmp Found_FD_Parent_Process FD_Parent_Process_Tmp5: lea eax, [ebp+FD_Parent_Process_me32] assume eax : ptr MODULEENTRY32 push sizeof MODULEENTRY32 pop dword ptr [eax].dwSize push dword ptr [ebp+FD_Parent_Process_PIDExplorer] push TH32CS_SNAPMODULE call dword ptr [ebx].xCreateToolhelp32Snapshot mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax lea eax, [ebp+FD_Parent_Process_me32] push eax push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xModule32First test eax, eax jz FD_Parent_Process_Tmp6 FD_Parent_Process_Loop2: lea eax, [ebp+FD_Parent_Process_me32] mov eax, dword ptr [eax].th32ProcessID sub eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer] jnz FD_Parent_Process_Tmp7 push MAX_PATH lea eax, [ebp+FD_Parent_Process_lpszSystemInfo] push eax call dword ptr [ebx].xGetWindowsDirectoryA call FD_Parent_Process_Str2 db '/',0 FD_Parent_Process_Str2: lea eax, [ebp+FD_Parent_Process_lpszSystemInfo] push eax call dword ptr [ebx].xlstrcatA call FD_Parent_Process_Str3 db "EXPLORER.EXE",0 FD_Parent_Process_Str3: lea eax, [ebp+FD_Parent_Process_lpszSystemInfo] push eax call dword ptr [ebx].xlstrcatA lea eax, [ebp+FD_Parent_Process_lpszSystemInfo] push eax lea eax, [ebp+FD_Parent_Process_me32] lea eax, [eax].szExePath push eax call dword ptr [ebx].xlstrcmpiA test eax, eax jz FD_Parent_Process_Tmp6 push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xCloseHandle jmp Found_FD_Parent_Process FD_Parent_Process_Tmp7: lea eax, [ebp+FD_Parent_Process_me32] push eax push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xModule32Next test eax, eax jnz FD_Parent_Process_Loop2 FD_Parent_Process_Tmp6: push dword ptr [ebp+FD_Parent_Process_hSnapshot] call dword ptr [ebx].xCloseHandle jmp NotFound_FD_Parent_Process Exit_FD_Parent_Process: pop esi pop edi pop ecx pop ebx assume eax : nothing assume ebx : nothing mov esp, ebp pop ebp retn 04h NotFound_FD_Parent_Process: xor eax, eax jmp Exit_FD_Parent_Process Found_FD_Parent_Process: mov eax, 1 jmp Exit_FD_Parent_Process End_FD_Parent_Process:
当调试会话创建,将产生一个调试对象,我们通过ntdll中的
NtQueryObject函数参看调试对象的个数是否不为零,来确定调试器的存在
以ObjectAllTypeInformation使用NtQueryObject查询后会返回一个
OBJECT_ALL_INFORMATION的结构,其中NumberOfObjectsTypes成员为所有的
对象类型在ObjectTypeInformation数组中的计数
此对象如下
typedef struct _OBJECT_ALL_INFORMATION
ULONG NumberOfObjectsTypes;
OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];
}
typedef strcut _OBJECT_TYPE_INFORMATION {
[00]UNICODE_STRING TypeName;
[08]ULONG TotalNumberofHandles;
[0c]ULONG TotalNumberofObjects;
...
}
循环遍历ObjectTypeInformation对比类型的名字,如有类型名为DebugObject则
检测TotalNumberofHandles与TotalNumberofObjects如果不为0则存在调试器。
FD_DebugObject_NtQueryObject: FD_DebugObject_NtQueryObject_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FD_DebugObject_NtQueryObject_Arg_Win32Api] push ebx push ecx push edx push edi push esi mov ebx, eax assume ebx : ptr WIN32APIBASE push edx ; alloc the stack push esp ; ReturnLength push 0 push 0 push ObjectAllTypeInformation push 0 call dword ptr [ebx].xNtQueryObject pop ecx ;; make a tmp stack push ebp mov ebp, esp sub esp, ecx mov esi, esp ;; ObjectInformationLength push 0 push ecx push esi push ObjectAllTypeInformation push 0 call dword ptr [ebx].xNtQueryObject cld ;; NumberOfObjectsTypes lodsd xchg ecx, eax ; ecx = NumberOfObjectsTypes FD_DebugObject_NtQueryObject_Loop: ;; load string lengths lodsd movzx edx, ax ;; pointer to TypeName lodsd xchg esi, eax ;; sizeof(L"DebugObject") ;; avoids superstrings ;; like "DebugObjective" cmp edx, 16h jnz FD_DebugObject_NtQueryObject_Tmp2 xchg ecx, edx FD_DebugObject_NtQueryObject_Tmp1: call FD_DebugObject_NtQueryObject_UnicodeStr1 dw 'D','e','b','u','g' dw 'O','b','j','e','c','t' FD_DebugObject_NtQueryObject_UnicodeStr1: pop edi repe cmpsb xchg ecx, edx jnz FD_DebugObject_NtQueryObject_Tmp2 ;; TotalNumberOfObjects cmp dword ptr [eax], edx jnz Found_FD_DebugObject_NtQueryObject ;; point to trailing null FD_DebugObject_NtQueryObject_Tmp2: add esi, edx ;; round down to dword and esi, -4 ;; skip trailing null ;; and any alignment bytes lodsd loop FD_DebugObject_NtQueryObject_Loop xor eax, eax Exit_FD_DebugObject_NtQueryObject: ;; clear the tmp stack mov esp, ebp pop ebp pop esi pop edi pop edx pop ecx pop ebx assume ebx : nothing retn 04h Found_FD_DebugObject_NtQueryObject: mov eax, 1 jmp Exit_FD_DebugObject_NtQueryObject End_FD_DebugObject_NtQueryObject:
此类方法,利用FindWindow函数通过寻找是否存在一些常见调试软件的Title。这里收集了一些
如果有其他的Title,请朋友们告知。。。
FD_Find_Debugger_Window: FD_Find_Debugger_Window_Arg_WinApi32 equ 08h push ebp mov ebp, esp push ebx mov ebx, dword ptr [ebp+FD_Find_Debugger_Window_Arg_WinApi32] assume ebx : ptr WIN32APIBASE push NULL call FD_Find_Debugger_Window_Str1 db "1212121",0 FD_Find_Debugger_Window_Str1: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str2 db "icu_dbg",0 FD_Find_Debugger_Window_Str2: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str3 db "pe--diy",0 FD_Find_Debugger_Window_Str3: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str5 db "ollydbg",0 FD_Find_Debugger_Window_Str5: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str6 db "odbydyk",0 FD_Find_Debugger_Window_Str6: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str7 db "WinDbgFrameClass",0 FD_Find_Debugger_Window_Str7: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str8 db "TDeDeMainForm",0 FD_Find_Debugger_Window_Str8: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str9 db "TIdaWindow",0 FD_Find_Debugger_Window_Str9: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrA db "TESTDBG",0 FD_Find_Debugger_Window_StrA: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrB db "kk1",0 FD_Find_Debugger_Window_StrB: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrC db "Eew75",0 FD_Find_Debugger_Window_StrC: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrD db "Shadow",0 FD_Find_Debugger_Window_StrD: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrE db "PEiD v0.94",0 FD_Find_Debugger_Window_StrE: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_StrF db "Registry Monitor - Sysinternals: www.sysinternals.com",0 FD_Find_Debugger_Window_StrF: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str10 db "File Monitor - Sysinternals: www.sysinternals.com",0 FD_Find_Debugger_Window_Str10: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window push NULL call FD_Find_Debugger_Window_Str11 db "Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF",0 FD_Find_Debugger_Window_Str11: call dword ptr [ebx].xFindWindowA test eax, eax jnz Found_FD_Find_Debugger_Window jmp NotFound_Found_FD_Find_Debugger_Window Exit_FD_Find_Debugger_Window: pop ebx assume ebx : nothing mov esp, ebp pop ebp retn 04h NotFound_Found_FD_Find_Debugger_Window: xor eax, eax jmp Exit_FD_Find_Debugger_Window Found_FD_Find_Debugger_Window: mov eax, 1 jmp Exit_FD_Find_Debugger_Window End_FD_Find_Debugger_Window:
这种方面的核心思想是比对在进程表中是否是出现了调试器进程名,如果出现则退出。
不过这种方法很容易躲过,而且也可能造成误判。
FD_Find_Debugger_Process: FD_Find_Debugger_Process_Arg_Win32Api equ 08h FD_Find_Debugger_Process_StackSize equ 10h + sizeof PROCESSENTRY32 FD_Find_Debugger_Process_hSnapshot equ -04h FD_Find_Debugger_Process_hParnet equ -08h FD_Find_Debugger_Process_pe32 equ -(10+sizeof PROCESSENTRY32) push ebp mov ebp, esp sub esp, FD_Find_Debugger_Process_StackSize push edi push esi push edx push ecx push ebx ;; clear the stack lea edi, [ebp-FD_Find_Debugger_Process_StackSize] mov ecx, FD_Find_Debugger_Process_StackSize xor eax, eax cld rep stosb mov ebx, dword ptr [ebp+FD_Find_Debugger_Process_Arg_Win32Api] assume ebx : ptr WIN32APIBASE lea esi, [ebp+FD_Find_Debugger_Process_pe32] assume esi : ptr PROCESSENTRY32 push sizeof PROCESSENTRY32 pop dword ptr [esi].dwSize push 0 push TH32CS_SNAPPROCESS call dword ptr [ebx].xCreateToolhelp32Snapshot mov dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot], eax lea eax, [ebp+FD_Find_Debugger_Process_pe32] push eax push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot] call dword ptr [ebx].xProcess32First test eax, eax jz NotFound_FD_Find_Debugger_Process FD_Find_Debugger_Process_Loop: lea eax, [esi].szExeFile mov edi, eax call FD_Find_Debugger_Process_Str1 db "OLLYICE.EXE",0 FD_Find_Debugger_Process_Str1: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str2 db "IDAG.EXE",0 FD_Find_Debugger_Process_Str2: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str3 db "OLLYDBG.EXE",0 FD_Find_Debugger_Process_Str3: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str4 db "PEID.EXE",0 FD_Find_Debugger_Process_Str4: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str5 db "SOFTICE.EXE",0 FD_Find_Debugger_Process_Str5: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str6 db "LORDPE.EXE",0 FD_Find_Debugger_Process_Str6: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str7 db "IMPORTREC.EXE",0 FD_Find_Debugger_Process_Str7: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str8 db "W32DSM89.EXE",0 FD_Find_Debugger_Process_Str8: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process call FD_Find_Debugger_Process_Str9 db "WINDBG.EXE",0 FD_Find_Debugger_Process_Str9: push edi call dword ptr [ebx].xlstrcmpiA test eax, eax jz Found_FD_Find_Debugger_Process lea eax, [ebp+FD_Find_Debugger_Process_pe32] push eax push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot] call dword ptr [ebx].xProcess32Next test eax, eax jnz FD_Find_Debugger_Process_Loop jmp NotFound_FD_Find_Debugger_Process Exit_FD_Find_Debugger_Process: ;; close the Shotsnap handle push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot] call dword ptr [ebx].xCloseHandle pop edi pop esi pop edx pop ecx pop ebx assume ebx : nothing assume esi : nothing mov esp, ebp pop ebp retn 04h NotFound_FD_Find_Debugger_Process: xor eax, eax jmp Exit_FD_Find_Debugger_Process Found_FD_Find_Debugger_Process: mov eax, 1 jmp Exit_FD_Find_Debugger_Process End_FD_Find_Debugger_Process:
这种方法通过打开一些调试软件加载到系统中驱动的句柄,从而判断是否有
调试软件的存在。
FD_Find_Device_Driver: FD_Find_Device_Driver_Arg_Win32Api equ 08h push ebp mov ebp, esp push ebx push ecx push edx push esi push edi mov ebx, dword ptr [ebp+FD_Find_Device_Driver_Arg_Win32Api] assume ebx : ptr WIN32APIBASE ;; check softice on unknow system push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str1 db "//./SIWVID",0 FD_Find_Device_Driver_Str1: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check softice 4.05 on win2k push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str2 db "//./NTICE",0 FD_Find_Device_Driver_Str2: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check softice on win9x push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str3 db "//./SICE",0 FD_Find_Device_Driver_Str3: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check softice on win9x push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str4 db "//./SIWDEBUG",0 FD_Find_Device_Driver_Str4: call dword ptr [ebx].xCreateFileA push eax call dword ptr [ebx].xGetLastError test al, 032h pop eax jz Found_FD_Find_Device_Driver ;; check regmon on win9x push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str5 db "//./REGVXD",0 FD_Find_Device_Driver_Str5: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check RegMON push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str6 db "//./FILEM",0 FD_Find_Device_Driver_Str6: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check TRW push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str7 db "//./TRW",0 FD_Find_Device_Driver_Str7: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver ;; check softice extender push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ + FILE_SHARE_WRITE push GENERIC_READ + GENERIC_WRITE call FD_Find_Device_Driver_Str8 db "//./ICEEXT",0 FD_Find_Device_Driver_Str8: call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jnz Found_FD_Find_Device_Driver jmp NotFound_FD_Find_Device_Driver Exit_FD_Find_Device_Driver: pop edi pop esi pop edx pop ecx pop ebx assume ebx : nothing mov esp, ebp pop ebp retn 04h NotFound_FD_Find_Device_Driver: xor eax, eax jmp Exit_FD_Find_Device_Driver Found_FD_Find_Device_Driver: push eax assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xCloseHandle assume ebx : nothing mov eax, 1 jmp Exit_FD_Find_Device_Driver End_FD_Find_Device_Driver:
利用int3中断引起异常。如果,正常运行则触发异常。
FD_Exception_Int3: call Get_FD_Exception_Int3_Eip Get_FD_Exception_Int3_Eip: pop eax add eax, offset FD_Exception_Int3_Exception - offset Get_FD_Exception_Int3_Eip ;; setup exception assume fs : nothing push eax push dword ptr fs : [0] mov dword ptr fs : [0], esp ;; reset eax xor eax, eax int 03h ;; unsetup exception pop dword ptr fs : [0] add esp, 04h ;; check the flag test eax, eax jz Found_FD_Exception_Int3 jmp NotFound_FD_Exception_Int3 FD_Exception_Int3_Exception: mov eax, dword ptr [esp+0ch] ;; eax = ContextRecord assume eax : ptr CONTEXT mov dword ptr [eax].regEax, 0FFFFFFFFh inc dword ptr [eax].regEip xor eax, eax assume eax : nothing retn Exit_FD_Exception_Int3: retn 0h NotFound_FD_Exception_Int3: xor eax, eax jmp Exit_FD_Exception_Int3 Found_FD_Exception_Int3: mov eax, 1 jmp Exit_FD_Exception_Int3 End_FD_Exception_Int3:
通过对EFLAGS中的TF的检测来确定是否存在调试器。当TF=1的时会触发一个
单步执行异常,可以通过安装异常处理针来捕获。
FD_Exception_Popf: push ebx call Get_FD_Exception_Popf_Eip Get_FD_Exception_Popf_Eip: pop eax add eax, offset FD_Exception_Popf_Exception - offset Get_FD_Exception_Popf_Eip assume fs : nothing push eax push dword ptr fs:[0] mov dword ptr fs:[0], esp ;; reset eax xor eax, eax pushf mov dword ptr [esp], 0100h popf nop FD_Exception_Popf_NextEip: pop dword ptr fs:[0] add esp, 04h ;; check the flag test eax, eax jz Found_FD_Exception_Popf jmp NotFound_FD_Exception_Popf FD_Exception_Popf_Exception: mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT mov dword ptr [eax].regEax, 0FFFFFFFFh call Get_FD_Exception_Popf_Exception_Eip2 Get_FD_Exception_Popf_Exception_Eip2: pop ebx sub ebx, offset Get_FD_Exception_Popf_Exception_Eip2 - offset FD_Exception_Popf_NextEip mov dword ptr [eax].regEip, ebx xor eax, eax retn assume eax : nothing Exit_FD_Exception_Popf: pop ebx retn 0h NotFound_FD_Exception_Popf: xor eax, eax jmp Exit_FD_Exception_Popf Found_FD_Exception_Popf: mov eax, 1 jmp Exit_FD_Exception_Popf End_FD_Exception_Popf:
触发OutputDebugString的错误。如果调试器存在则GetLastError为0。
FD_OutputDebugString: FD_OutputDebugString_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FD_OutputDebugString_Arg_Win32Api] push ebx mov ebx, eax assume ebx : ptr WIN32APIBASE call FD_OutputDebugString_Str db 0 FD_OutputDebugString_Str: call dword ptr [ebx].xOutputDebugStringA call dword ptr [ebx].xGetLastError test eax, eax jnz NotFound_FD_OutputDebugString inc eax pop ebx assume ebx : nothing retn 04h NotFound_FD_OutputDebugString: pop ebx xor eax, eax retn 04h End_FD_OutputDebugString:
Ollydbg允许设置一个内存访问/写入断点。它通过页面保护来实现。
页面保护提供了当应用程序的某块内存被访问时获得通知。
页面保护通过PAGE_GUARD页面保护修改符来设置。如果访问的内存
是受保护的将会产生一个STATUS_GUARD_PAGE_VIOLATION(0x80000001)异常
如果进程被Ollydbg调试并且受保护的页面被访问,将不会抛出异常。访问
将会被当作内存断点来处理。
FS_OD_Exception_GuardPages: FS_OD_Exception_GuardPages_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FS_OD_Exception_GuardPages_Arg_Win32Api] push edi ; pAllocatedMem push ebx mov ebx, eax assume ebx : ptr WIN32APIBASE push PAGE_READWRITE push MEM_COMMIT push 01000h push NULL call dword ptr [ebx].xVirtualAlloc test eax, eax jz NotFound_FS_OD_Exception_GuardPages mov edi, eax ;; store a RENT on the allocated memory mov byte ptr [edi], 0c3h push edi ; alloc stack push esp push PAGE_EXECUTE_READ + PAGE_GUARD push 01000h push edi call dword ptr [ebx].xVirtualProtect ;; set eax to 0 xor eax, eax ;; trigger a STATUS_GUARD_PAGE_VIOLATION exception ;; setup expcetion call Get_FS_OD_Exception_GuardPages_Eip Get_FS_OD_Exception_GuardPages_Eip: pop eax add eax, offset FS_OD_Exception_GuardPages_Exception - offset Get_FS_OD_Exception_GuardPages_Eip assume fs : nothing push eax push dword ptr fs:[0] mov dword ptr fs:[0], esp call edi FS_OD_Exception_GuardPages_Continue: pop edi ;; unsetup exception pop dword ptr fs:[0] add esp, 04h test eax, eax jz Found_FS_OD_Exception_GuardPages jmp NotFound_FS_OD_Exception_GuardPages FS_OD_Exception_GuardPages_Exception: mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT mov dword ptr [eax].regEax, 0FFFFFFFFh call Get_FS_OD_Exception_GuardPages_Exception_Eip Get_FS_OD_Exception_GuardPages_Exception_Eip: pop ebx sub ebx, offset Get_FS_OD_Exception_GuardPages_Exception_Eip - offset FS_OD_Exception_GuardPages_Continue mov dword ptr [eax].regEip, ebx assume eax : nothing xor eax, eax retn Exit_FS_OD_Exception_GuardPages: pop ebx pop edi assume ebx : nothing retn 04h NotFound_FS_OD_Exception_GuardPages: assume ebx : ptr WIN32APIBASE push MEM_DECOMMIT push 01000h push edi call dword ptr [ebx].xVirtualFree assume ebx : nothing xor eax, eax jmp Exit_FS_OD_Exception_GuardPages Found_FS_OD_Exception_GuardPages: assume ebx : ptr WIN32APIBASE push MEM_DECOMMIT push 01000h push edi call dword ptr [ebx].xVirtualFree assume ebx : nothing mov eax, 1 jmp Exit_FS_OD_Exception_GuardPages End_FS_OD_Exception_GuardPages:
Softice调试器会对UnhandleExceptionFilter进行修改,将第一个字节修改为CCh(int 3)
FS_SI_UnhandledExceptionFilter: FS_SI_UnhandledExceptionFilter_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FS_SI_UnhandledExceptionFilter_Arg_Win32Api] assume eax : ptr WIN32APIBASE mov eax, dword ptr [eax].xUnhandledExceptionFilter mov al, byte ptr [eax] sub al, 0cch jz Found_FS_SI_UnhandledExceptionFilter xor eax, eax Exit_FS_SI_UnhandledExceptionFilter: assume eax : nothing retn 04h Found_FS_SI_UnhandledExceptionFilter: mov eax, 1 jmp Exit_FS_SI_UnhandledExceptionFilter End_FS_SI_UnhandledExceptionFilter:
Ollydbg的插件对几个函数的首部进行修改。通过对齐的检测来判断系统中是否存在调试器
FS_ODP_Process32NextW: FS_ODP_Process32NextW_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FS_ODP_Process32NextW_Arg_Win32Api] assume eax : ptr WIN32APIBASE mov eax, dword ptr [eax].xProcess32NextW mov ax, word ptr [eax] sub ax, 0FF88h jnz Found_FS_ODP_Process32NextW xor eax, eax Exit_FS_ODP_Process32NextW: assume eax : nothing retn 04h Found_FS_ODP_Process32NextW: mov eax, 1 jmp Exit_FS_ODP_Process32NextW End_FS_ODP_Process32NextW: FS_ODP_OutputDebugStringA: FS_ODP_OutputDebugStringA_Arg_Win32Api equ 04h mov eax, dword ptr [ebp+FS_ODP_OutputDebugStringA_Arg_Win32Api] assume eax : ptr WIN32APIBASE mov eax, dword ptr [eax].xOutputDebugStringA mov ax, word ptr [eax] sub ax, 03468h jnz Found_FS_ODP_OutputDebugStringA xor eax, eax Exit_FS_ODP_OutputDebugStringA: assume eax : nothing retn 04h Found_FS_ODP_OutputDebugStringA: mov eax, 1 jmp Exit_FS_ODP_OutputDebugStringA End_FS_ODP_OutputDebugStringA: FS_ODP_OpenProcess: FS_ODP_OpenProcess_Arg_Win32Api equ 04h mov eax, dword ptr [ebp+FS_ODP_OpenProcess_Arg_Win32Api] assume eax : ptr WIN32APIBASE mov eax, dword ptr [eax].xOpenProcess mov al, byte ptr [eax+06h] ;; Hide Debugger Plugin of OD is present sub al, 0eah jnz Found_FS_ODP_OpenProcess xor eax, eax Exit_FS_ODP_OpenProcess: assume eax : nothing retn 04h Found_FS_ODP_OpenProcess: mov eax, 1 jmp Exit_FS_ODP_OpenProcess End_FS_ODP_OpenProcess: FB_HWBP_Exception: call Get_FB_HWBP_Exception_Eip Get_FB_HWBP_Exception_Eip: pop eax add eax, offset FB_HWBP_Exception_Exception - offset FB_HWBP_Exception assume fs : nothing push eax push fs:[0] mov dword ptr fs:[0], esp ;; reset eax xor eax, eax int 01h pop dword ptr fs:[0] add esp, 04h test eax, eax jnz Found_FB_HWBP_Exception jmp NotFound_FB_HWBP_Exception FB_HWBP_Exception_Exception: mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT ;; check if debug Registers Context.Dr0-Dr3 is not zero cmp dword ptr [eax].iDr0, 0 jnz FB_HWBP_Exception_HardwareBp_Found cmp dword ptr [eax].iDr1, 0 jnz FB_HWBP_Exception_HardwareBp_Found cmp dword ptr [eax].iDr2, 0 jnz FB_HWBP_Exception_HardwareBp_Found cmp dword ptr [eax].iDr3, 0 jnz FB_HWBP_Exception_HardwareBp_Found jmp FB_HWBP_Exception_Exception_Ret FB_HWBP_Exception_HardwareBp_Found: ;; set Context.Eax to signal breakpoint found mov dword ptr [eax].regEax, 0FFFFFFFFh FB_HWBP_Exception_Exception_Ret: ;; set Context.Eip upon return add dword ptr [eax].regEip, 02h xor eax, eax retn Exit_FB_HWBP_Exception: retn 0h NotFound_FB_HWBP_Exception: xor eax, eax jmp Exit_FB_HWBP_Exception Found_FB_HWBP_Exception: mov eax, 1 jmp Exit_FB_HWBP_Exception End_FB_HWBP_Exception:
通过对代码节做CRC值的验证来判断是否存在内存补丁
FB_SWBP_Memory_CRC:
FB_SWBP_Memory_CRC_Arg_Win32Api equ 08h
FB_SWBP_Memory_CRC_Arg_FuncAddr equ 0ch
FB_SWBP_Memory_CRC_StackSize equ 10h+MAX_PATH
FB_SWBP_Memory_CRC_fileSize equ -04h
FB_SWBP_Memory_CRC_NumberOfBytesRW equ -08h
FB_SWBP_Memory_CRC_pBuffer equ -0ch
FB_SWBP_Memory_CRC_szFileName equ -(10h+MAX_PATH)
push ebp
mov ebp, esp
sub esp, FB_SWBP_Memory_CRC_StackSize
push esi
push edi
push ecx
push edx
push ebx
;; clear the stack
lea edi, [ebp+FB_SWBP_Memory_CRC_szFileName]
mov ecx, FB_SWBP_Memory_CRC_StackSize
xor eax, eax
cld
rep stosb
mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
assume ebx : ptr WIN32APIBASE
push MAX_PATH
lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
push eax
push NULL
call dword ptr [ebx].xGetModuleFileNameA
;; open file
push NULL
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push NULL
push FILE_SHARE_READ
push GENERIC_READ
lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
push eax
call dword ptr [ebx].xCreateFileA
cmp eax, INVALID_HANDLE_VALUE
jz Error_FB_SWBP_Memory_CRC
mov edi, eax
p