微慑信息网

[破解] 软件保护壳技术专题 - 反调试器技术

标 题: 【原创】软件保护壳技术专题 – 反调试器技术
作 者: 玩命
时 间: 2008-08-21,10:38
链 接: http://bbs.pediy.com/showthread.php?t=71113
反调试是软件保护壳的最基本的功能之一。
反调试方法也是多种多样。通过调用标准的API接口,计算指令时间差。查看当调试器加载后的
内存的一些标志,还有就是判断当前运行环境是否合乎逻辑等方法。这里收集了一些反调试的方法,其中的命名规则使用了壳狼的反调试程序的方式,希望不要和我收取版权的费用。^_^,其中借鉴了不少壳狼的函数。增加了一些,也删除了一些。大部分的参考资料来自<<脱壳的艺术>>,<>与<<加密与解密第三版>>。
这里要说明的一点是。每个函数编写都是自己建立堆栈了,看的不习惯的多看下就习惯了 呵呵。
原因也很简单,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 |= FLG_HEAP_ENABLE_FREE_CHECK |
FLG_HEAP_ENABLE_TAIL_CHECK |
FLG_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 类似这样 ^_^
终于写完了,累吖。。。

赞(0) 打赏
转载请附本站链接,未经允许不得转载,,谢谢:微慑信息网-VulSee.com » [破解] 软件保护壳技术专题 - 反调试器技术

评论 抢沙发

微慑信息网 专注工匠精神

微慑信息网-VulSee.com-关注前沿安全态势,聚合网络安全漏洞信息,分享安全文档案例

访问我们联系我们

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册