作 者: 玩命
时 间: 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 push NULL push eax call dword ptr [ebx].xGetFileSize ; cmp eax, INVALID_FILE_SIZE ; jz Error_FB_SWBP_Memory_CRC mov dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize], eax push PAGE_READWRITE push MEM_COMMIT push eax push NULL call dword ptr [ebx].xVirtualAlloc mov dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer], eax push NULL lea eax, [ebp+FB_SWBP_Memory_CRC_NumberOfBytesRW] push eax push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize] push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] push edi call dword ptr [ebx].xReadFile push edi call dword ptr [ebx].xCloseHandle mov esi, dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] add esi, dword ptr [esi+3ch] assume esi : ptr IMAGE_NT_HEADERS mov edx, dword ptr [esi].OptionalHeader.ImageBase mov eax, dword ptr [esi].OptionalHeader.AddressOfEntryPoint add eax, edx mov cx, word ptr [esi].FileHeader.NumberOfSections movzx ecx, cx mov edi, esi add edi, sizeof IMAGE_NT_HEADERS assume edi : ptr IMAGE_SECTION_HEADER ;; find the entry section ;; eax = AddressOfEntryPoint ;; ebx = Each Section Virtual Address ;; ecx = NumberOfSections ;; edx = ImageBase ;; edi = Section Table Point FB_SWBP_Memory_CRC_Loop: mov ebx, dword ptr [edi].VirtualAddress add ebx, edx cmp ebx, eax jz FB_SWBP_Memory_CRC_Loop_End add edi, sizeof IMAGE_SECTION_HEADER dec ecx jnz FB_SWBP_Memory_CRC_Loop jmp Error_FB_SWBP_Memory_CRC FB_SWBP_Memory_CRC_Loop_End: mov edi, dword ptr [edi].Misc.VirtualSize mov esi, eax ;; alloc the memory push MEM_DECOMMIT push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize] push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api] assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xVirtualFree mov edx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_FuncAddr] assume edx : ptr FUNCADDRTBL push edi push esi call dword ptr [edx].xCRC32 call Get_FB_SWBP_Memory_CRC_Eip Get_FB_SWBP_Memory_CRC_Eip: pop ecx add ecx, offset FB_SWBP_Memory_CRC_Orig_CRC - offset Get_FB_SWBP_Memory_CRC_Eip cmp eax, ecx jz NotFound_FB_SWBP_Memory_CRC jmp Found_FB_SWBP_Memory_CRC Exit_FB_SWBP_Memory_CRC: pop ebx pop edx pop ecx pop edi pop esi assume ebx : nothing assume edx : nothing assume edi : nothing assume esi : nothing mov esp, ebp pop ebp retn 08h Error_FB_SWBP_Memory_CRC: mov eax, dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] test eax, eax jz Error_FB_SWBP_Memory_CRC_Continue push MEM_DECOMMIT push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize] push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api] assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xVirtualFree assume ebx : nothing Error_FB_SWBP_Memory_CRC_Continue: xor eax, eax jmp Exit_FB_SWBP_Memory_CRC Found_FB_SWBP_Memory_CRC: push MEM_DECOMMIT push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize] push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api] assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xVirtualFree assume ebx : nothing mov eax, 1 jmp Exit_FB_SWBP_Memory_CRC NotFound_FB_SWBP_Memory_CRC: push MEM_DECOMMIT push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize] push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer] mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api] assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xVirtualFree assume ebx : nothing xor eax, eax jmp Exit_FB_SWBP_Memory_CRC FB_SWBP_Memory_CRC_Orig_CRC: dd 0 End_FB_SWBP_Memory_CRC:
通过检测标志位来检测是否存在单步运行标志
FT_PushSS_PopSS: ;; release version push ebp mov ebp, esp push ss pop ss pushf pop eax and eax, 00000100h jnz Found_FT_PushSS_PopSS Exit_FT_PushSS_PopSS: xor eax, eax mov esp, ebp pop ebp retn 0h Found_FT_PushSS_PopSS: mov eax, 1 jmp Exit_FT_PushSS_PopSS End_FT_PushSS_PopSS:
以下三个函数都是通过调用取单步运行时间差的原理来检测是否进行单步运行的
分别使用了rdtsc,GetTickCount,timeGetTime三个函数来确定
FT_RDTSC: push ecx rdtsc xchg ecx, eax rdtsc sub eax, ecx cmp eax, 500h jnbe Found_FT_RDTSC xor eax, eax Exit_FT_RDTSC: pop ecx retn 0h Found_FT_RDTSC: mov eax, 1 jmp Exit_FT_RDTSC End_FT_RDTSC: FT_GetTickCount: FT_GetTickCount_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FT_GetTickCount_Arg_Win32Api] push ebx push ecx mov ebx, eax assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xGetTickCount xchg ecx, eax call dword ptr [ebx].xGetTickCount sub eax, ebx cmp eax, 1 jnb Found_FT_GetTickCount xor eax, eax Exit_FT_GetTickCount: pop ecx pop ebx assume ebx : nothing retn 04h Found_FT_GetTickCount: mov eax, 1 jmp Exit_FT_GetTickCount End_FT_GetTickCount: FT_timeGetTime: FT_timeGetTime_Arg_Win32Api equ 04h mov eax, dword ptr [esp+FT_timeGetTime_Arg_Win32Api] push ebx push ecx mov ebx, eax assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xtimeGetTime xchg ecx, eax call dword ptr [ebx].xtimeGetTime sub eax, ebx cmp eax, 10h jnb Found_FT_timeGetTime xor eax, eax Exit_FT_timeGetTime: pop ecx pop ebx assume ebx : nothing retn 04h Found_FT_timeGetTime: mov eax, 1 jmp Exit_FT_timeGetTime End_FT_timeGetTime:
手动检测位于7FFE0000h地址的SharedUserData数据结构的TickCoutLow与TickCountMultipiler字段
来获取单步运行时间差。
FT_SharedUserData_TickCount: push ecx push edx mov edx, 07FFE0000h mov eax, dword ptr [edx] imul dword ptr [edx+04h] shrd eax, edx, 018h xchg eax, ecx mov edx, 07FFE0000h mov eax, dword ptr [edx] imul dword ptr [edx+04h] shrd eax, edx, 018h sub eax, ecx cmp eax, 500h jnbe Found_FT_SharedUserData_TickCount xor eax, eax Exit_FT_SharedUserData_TickCount: pop edx pop ecx retn 0h Found_FT_SharedUserData_TickCount: mov eax, 1 jmp Exit_FT_SharedUserData_TickCount End_FT_SharedUserData_TickCount:
中断1不能从r3下调用。如果从r3下调用,将产生一个EXECEPTION_ACCESS_VIOLATION(0x0C0000005)的异常。
如果softice存在,它HOOK了中断1并且使它的DPL(descriptor privilege level)到环3下
使得softice能单步处理用户态的程序。如果程序使用中断1,softice不检查它是否在单步运行或者是一软中断。
softice总是调用原始的中断1处理函数。并且使用EXCEPTION_SINGLE_STEP(0x80000004)异常替换原本的EXECEPTION_ACCESS_VIOLATION
(0xC0000005)异常。
FT_INT1_IceBreakpoint: push ebp mov ebp, esp call Get_FT_INT1_IceBreakpoint_Eip Get_FT_INT1_IceBreakpoint_Eip: pop eax add eax, offset FT_INT1_IceBreakpoint_Exception - offset Get_FT_INT1_IceBreakpoint_Eip push eax assume fs : nothing push dword ptr fs:[0] mov dword ptr fs:[0], esp ;; reset eax xor eax, eax db 0CDh, 01h ; int 1 pop dword ptr fs:[0] add esp, 04h test eax, eax jz Found_FT_INT1_IceBreakpoint jmp NotFound_FT_INT1_IceBreakpoint FT_INT1_IceBreakpoint_Exception: ;mov eax, dword ptr [esp+04h] ;cmp dword ptr [eax], 80000004h mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT mov dword ptr [eax].regEax, 1 inc dword ptr [eax].regEip xor eax, eax retn Exit_FT_INT1_IceBreakpoint: mov esp, ebp pop ebp retn 0h NotFound_FT_INT1_IceBreakpoint: xor eax, eax jmp Exit_FT_INT1_IceBreakpoint Found_FT_INT1_IceBreakpoint: mov eax, 1 jmp Exit_FT_INT1_IceBreakpoint End_FT_INT1_IceBreakpoint:
以下为检测Virtual PC 与 VMWARE的两个例子,这类检测技术大多都是通过与真实环境中不同的
异常,指令实现的。严格的描述可以在其规定的文档中找到。
FV_VPC_Exception: push ebx push ecx call Get_FV_VPC_Exception_Eip Get_FV_VPC_Exception_Eip: pop ecx add ecx, offset FV_VPC_Exception_Exception - offset Get_FV_VPC_Exception_Eip assume fs : nothing push ecx push dword ptr fs:[0] mov dword ptr fs:[0], esp ;; Flag xor ebx, ebx ;; VPC function number mov eax, 1 ;; call VPC db 0Fh,3Fh,07h,0Bh pop dword ptr fs:[0] add esp, 04h test ebx, ebx setz al pop ecx pop ebx retn 0h FV_VPC_Exception_Exception: mov ecx, dword ptr [esp+0ch] assume ecx : ptr CONTEXT ;; ebx = -1 : not running ;; ebx = 0 : running mov dword ptr [ecx].regEbx, -1 ;; skip past the call to VPC add dword ptr [ecx].regEip, 04h xor eax, eax retn End_FV_VPC_Exception: FV_VMWare_VMX: call IsInsideVMWare retn 0h IsInsideVMWare: push ebx push ecx push edx assume fs : nothing call Get_IsInsideVMWare_Eip Get_IsInsideVMWare_Eip: pop eax add eax, offset IsInsideVMWare_Exception - offset Get_IsInsideVMWare_Eip push eax push dword ptr fs:[0] mov dword ptr fs:[0], esp mov eax, 'VMXh' mov ebx, 0 ; any value but MAGIC VALUE mov ecx, 10 ; get VMWare version mov edx, 'VX' ; port number in eax, dx ; read port ; on return EAX returns the VERSION cmp ebx, 'VMXh' ; is it a reply from VMWare? setz al ; set return value movzx eax, al pop dword ptr fs:[0] add esp, 04h pop edx pop ecx pop ebx retn 0h IsInsideVMWare_Exception: mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT inc dword ptr [eax].regEip retn End_IsInsideVMWare: End_FV_VMWare_VMX:
通过计算文件的长度与原先的对比,来确定是否进行过修改。来发现补丁。
FP_Check_FileSize: FP_Check_FileSize_Arg_Win32Api equ 08h FP_Check_FileSize_StackSize equ MAX_PATH FP_Check_FileSize_szPath equ -MAX_PATH push ebp mov ebp, esp sub esp, FP_Check_FileSize_StackSize push ebx push ecx push edx mov ebx, dword ptr [ebp+FP_Check_FileSize_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push MAX_PATH lea eax, [ebp+FP_Check_FileSize_szPath] push eax push NULL call dword ptr [ebx].xGetModuleFileNameA test eax, eax jz NotFound_FP_Check_FileSize ;; create file push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_ALWAYS push NULL push FILE_SHARE_READ push GENERIC_READ lea eax, [ebp+FP_Check_FileSize_szPath] push eax call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jz NotFound_FP_Check_FileSize mov ecx, eax push NULL push eax call dword ptr [ebx].xGetFileSize mov edx, eax push ecx call dword ptr [ebx].xCloseHandle call Get_FP_Check_FileSize_Eip Get_FP_Check_FileSize_Eip: pop eax add eax, offset FP_Check_FileSize_Orig_Size - offset Get_FP_Check_FileSize_Eip cmp edx, eax jnz Found_FP_Check_FileSize jmp NotFound_FP_Check_FileSize Exit_FP_Check_FileSize: pop edx pop ecx pop ebx assume ebx : nothing mov esp, ebp pop ebp retn 04h NotFound_FP_Check_FileSize: xor eax, eax jmp Exit_FP_Check_FileSize Found_FP_Check_FileSize: mov eax, 1 jmp Exit_FP_Check_FileSize FP_Check_FileSize_Orig_Size: dd 0 End_FP_Check_FileSize:
通过计算文件的CRC值来对比是否被Cracker进行过修改打过补丁。
FP_Check_FileHashValue_CRC: FP_Check_FileHashValue_CRC_Arg_Win32Api equ 08h FP_Check_FileHashValue_CRC_Arg_FuncAddr equ 0ch FP_Check_FileHashValue_CRC_StackSize equ 20h+MAX_PATH FP_Check_FileHashValue_CRC_fileSize equ -04h FP_Check_FileHashValue_CRC_NumberOfBytesRW equ -08h FP_Check_FileHashValue_CRC_CRCVALUE_current equ -0ch FP_Check_FileHashValue_CRC_pBuffer equ -10h FP_Check_FileHashValue_CRC_hFile equ -14h FP_Check_FileHashValue_CRC_szFileName equ -(20h+MAX_PATH) push ebp mov ebp, esp sub esp, FP_Check_FileHashValue_CRC_StackSize push ebx push ecx push edx push esi push edi ;; clear the stack lea edi, [ebp-FP_Check_FileHashValue_CRC_StackSize] mov ecx, FP_Check_FileHashValue_CRC_StackSize xor eax, eax cld rep stosb mov ebx, dword ptr [ebp+FP_Check_FileHashValue_CRC_Arg_Win32Api] assume ebx : ptr WIN32APIBASE push MAX_PATH lea eax, [ebp+FP_Check_FileHashValue_CRC_szFileName] push eax push NULL call dword ptr [ebx].xGetModuleFileNameA push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ push GENERIC_READ lea eax, [ebp+FP_Check_FileHashValue_CRC_szFileName] push eax call dword ptr [ebx].xCreateFileA cmp eax, INVALID_HANDLE_VALUE jz NotFound_FP_Check_FileHashValue_CRC mov dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile], eax push NULL push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile] call dword ptr [ebx].xGetFileSize ;cmp eax, INVALID_FILE_SIZE ;jz NotFound_FP_Check_FileHashValue_CRC mov dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize], eax push PAGE_READWRITE push MEM_COMMIT push eax push NULL call dword ptr [ebx].xVirtualAlloc mov dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer], eax ;; read file push NULL lea eax, [ebp+FP_Check_FileHashValue_CRC_NumberOfBytesRW] push eax push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize] push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer] push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile] call dword ptr [ebx].xReadFile push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile] call dword ptr [ebx].xCloseHandle ;; get CRC value push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize] push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer] mov eax, dword ptr [ebp+FP_Check_FileHashValue_CRC_Arg_FuncAddr] assume eax : ptr FUNCADDRTBL call dword ptr [eax].xCRC32 mov esi, eax ;; release the memory push MEM_RELEASE push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize] push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer] call dword ptr [ebx].xVirtualFree call Get_FP_Check_FileHashValue_CRC_Eip Get_FP_Check_FileHashValue_CRC_Eip: pop edi add edi, offset FP_Check_FileHashValue_Orig_CRC - offset Get_FP_Check_FileHashValue_CRC_Eip sub edi, esi jz NotFound_FP_Check_FileHashValue_CRC jmp Found_FP_Check_FileHashValue_CRC Exit_FP_Check_FileHashValue_CRC: pop edi pop esi pop edx pop ecx pop ebx assume eax : nothing assume ebx : nothing mov esp, ebp pop ebp retn 08h NotFound_FP_Check_FileHashValue_CRC: xor eax, eax jmp Exit_FP_Check_FileHashValue_CRC Found_FP_Check_FileHashValue_CRC: mov eax, 1 jmp Exit_FP_Check_FileHashValue_CRC FP_Check_FileHashValue_Orig_CRC: dd 0 End_FP_Check_FileHashValue_CRC:
Windows NT系列平台支持一个会话多个桌面,将线程的桌面交换到自己的桌面。
目前还没有有效的方法转换到旧的桌面。
AD_SwitchDesktop: AD_SwitchDesktop_Arg_Win32Api equ 04h mov eax, dword ptr [esp+AD_SwitchDesktop_Arg_Win32Api] push ebx push ecx push edx push edi push esi mov ebx, eax assume ebx : ptr WIN32APIBASE call dword ptr [ebx].xGetCurrentThreadId push eax call dword ptr [ebx].xGetThreadDesktop mov ecx, eax push DESKTOP_SWITCHDESKTOP push FALSE push 0 call dword ptr [ebx].xOpenInputDesktop mov edx, eax push NULL push GENERIC_ALL push 0 push NULL push NULL call AD_SwitchDesktop_Str db "MyDesktop",0 AD_SwitchDesktop_Str: call dword ptr [ebx].xCreateDesktopA mov esi, eax push esi call dword ptr [ebx].xSetThreadDesktop push esi call dword ptr [ebx].xSwitchDesktop push 5000 call dword ptr [ebx].xSleep push edx call dword ptr [ebx].xSwitchDesktop push ecx call dword ptr [ebx].xSetThreadDesktop push esi call dword ptr [ebx].xCloseDesktop mov eax, 1 pop esi pop edi pop edx pop ecx pop ebx assume ebx : nothing retn 04h End_AD_SwitchDesktop:
BlockInput函数阻止了鼠标和键盘事件到达应用程序。
AD_BlockInput: AD_BlockInput_Arg_Win32Api equ 04h mov eax, dword ptr [esp+AD_BlockInput_Arg_Win32Api] push ebx mov ebx, eax assume ebx : ptr WIN32APIBASE push TRUE call dword ptr [ebx].xBlockInput test eax, eax jz Exit_AD_BlockInput push 5000 call dword ptr [ebx].xSleep push FALSE call dword ptr [ebx].xBlockInput xor eax, eax Exit_AD_BlockInput: pop ebx assume ebx : nothing retn 04h End_AD_BlockInput:
利用Nativa API函数ZwSetInformationThread函数通过ThreadHideFromDebugger来达到隐藏调试器的目的
AD_ZwSetInfomationThread: AD_ZwSetInfomationThread_Arg_Win32Api equ 04h mov eax, dword ptr [esp+AD_ZwSetInfomationThread_Arg_Win32Api] push ebx mov ebx, eax assume ebx : ptr WIN32APIBASE push 0 push NULL push ThreadHideFromDebugger call dword ptr [ebx].xGetCurrentThread push eax call dword ptr [ebx].xZwSetInformationThread cmp eax, STATUS_SUCCESS Exit_AD_ZwSetInfomationThread: pop ebx assume ebx : nothing retn 04h End_AD_ZwSetInfomationThread:
通过系统索引号调用NtSetInformationThread使用ThreadHideFromDebugger来达到隐藏调试器的目的
AD_INT_2e: AD_INT_2e_Arg_Win32Api equ 08h AD_INT_2e_StackSize equ 10h + sizeof OSVERSIONINFOEX AD_INT_2e_bOsVersionInfoEx equ -04h AD_INT_2e_osvi equ -(10h+sizeof OSVERSIONINFOEX) push ebp mov ebp, esp sub esp, AD_INT_2e_StackSize push ebx push ecx push edx push edi push esi mov ebx, dword ptr [ebp+AD_INT_2e_Arg_Win32Api] assume ebx : ptr WIN32APIBASE lea edx, [ebp+AD_INT_2e_osvi] assume edx : ptr OSVERSIONINFOEX ;; clear the stack lea edi, [ebp-AD_INT_2e_StackSize] mov ecx, AD_INT_2e_StackSize xor eax, eax cld rep stosb push sizeof OSVERSIONINFOEX pop dword ptr [edx].dwOSVersionInfoSize call dword ptr [ebx].xGetCurrentThread mov esi, eax lea edx, [ebp+AD_INT_2e_osvi] push edx call dword ptr [ebx].xGetVersionExA test eax, eax jz Exit_AD_INT_2e ;; Test for the Windows NT product family lea edx, [ebp+AD_INT_2e_osvi] cmp dword ptr [edx].dwPlatformId, VER_PLATFORM_WIN32_NT jnz Exit_AD_INT_2e push 0 push 0 push ThreadHideFromDebugger push esi mov eax, 0EEh ; NtSetInformationThread mov edx, esp int 2eh add esp, 10h push 0 push 0 push ThreadHideFromDebugger push esi mov eax, 0E5h ; NtSetInformationThread mov edx, esp int 2eh add esp, 10h ;jmp Found_AD_INT_2e ;AD_INT_2e_Tmp1: ;; Test for the Windows 95 product family ;cmp dword ptr [edx].dwPlatformId, VER_PLATFORM_WIN32_WINDOWS ;jnz AD_INT_2e_Tmp2 ;AD_INT_2e_Tmp2: ;; Microsoft Win32s Exit_AD_INT_2e: xor eax, eax pop esi pop edi pop edx pop ecx pop ebx assume ebx : nothing assume edx : nothing mov esp, ebp pop ebp retn 04h End_AD_INT_2e:
Ollydbg传递用户定义的数据给msvcrt的vsprintf函数,如果这些数据包含
格式字符串标记.如果包换多个%s标记,这样会指向一个无效的内存导致
ollydbg失效。
AS_OD_OutputDebugString: AS_OD_OutputDebugString_Arg_Win32Api equ 04h mov eax, dword ptr [esp+AS_OD_OutputDebugString_Arg_Win32Api] assume eax : ptr WIN32APIBASE call AS_OD_OutputDebugString_Str db "%s%s",0 AS_OD_OutputDebugString_Str: call dword ptr [eax].xOutputDebugStringA xor eax, eax assume eax : nothing retn 04h End_AS_OD_OutputDebugString:
通过触发异常进入异常处理程序,将调试寄存器清0,清除断点。
Dr0,Dr1,Dr2,Dr3,Dr6和Dr7。Dr0-Dr3用作一般的线性地址,Dr6为调试状态寄存器
Dr7为调试控制寄存器。
AB_HWP_CLR_Exception: call Get_AB_HWP_CLR_Exception_Eip Get_AB_HWP_CLR_Exception_Eip: pop eax add eax, offset AB_HWP_CLR_Exception_Exception - offset Get_AB_HWP_CLR_Exception_Eip push eax assume fs : nothing push dword ptr fs:[0] mov dword ptr fs:[0], esp xor eax, eax ;; throw an exception div eax ;; restore exception handler pop dword ptr fs:[0] add esp, 04h jmp Exit_AB_HWP_CLR_Exception AB_HWP_CLR_Exception_Exception: mov eax, dword ptr [esp+0ch] assume eax : ptr CONTEXT ;; clr debug registers context Dr0-Dr3 mov dword ptr [eax].iDr0, 0 mov dword ptr [eax].iDr1, 0 mov dword ptr [eax].iDr2, 0 mov dword ptr [eax].iDr3, 0 mov dword ptr [eax].iDr6, 0 mov dword ptr [eax].iDr7, 0 ;; set eip upon return add dword ptr [eax].regEip, 02h xor eax, eax retn Exit_AB_HWP_CLR_Exception: xor eax, eax assume eax : nothing retn 0h End_AB_HWP_CLR_Exception:
还有很多反调试的方法没有归结到上面,一开始想做的全些。但是后来发现,太多了。还是以后慢慢归纳吧。
附件中的程序是一个可以添加反调试代码到其他程序的一个小玩意。
使用命令为AntiDebugger 1 2 3 4 file.exe 类似这样 ^_^
终于写完了,累吖。。。