现在的位置: 首页 > 综合 > 正文

软件保护壳技术专题 – 反调试器技术

2013年08月23日 ⁄ 综合 ⁄ 共 43625字 ⁄ 字号 评论关闭

 作 者: 玩命
时 间: 2008-08-21,10:38
链 接: http://bbs.pediy.com/showthread.php?t=71113

反调试是软件保护壳的最基本的功能之一。
反调试方法也是多种多样。通过调用标准的API接口,计算指令时间差。查看当调试器加载后的
内存的一些标志,还有就是判断当前运行环境是否合乎逻辑等方法。这里收集了一些反调试的方法,其中的命名规则使用了壳狼的反调试程序的方式,希望不要和我收取版权的费用。^_^,其中借鉴了不少壳狼的函数。增加了一些,也删除了一些。大部分的参考资料来自<<脱壳的艺术>>,<<Anti-UnPacker Tricks>>与<<加密与解密第三版>>。
这里要说明的一点是。每个函数编写都是自己建立堆栈了,看的不习惯的多看下就习惯了 呵呵。
原因也很简单,MASM不允许在函数内定义函数了。
这些函数还有一个要讲的是。因为最后这些函数要在以后的章节中用到
为了能允许用户自定义反调试的功能。免去重定位的麻烦,所以
利用栈传递了API集合地址和外部函数集合的地址。
朋友们还是先看代码了。。。

利用IsDebuggerPresent确定是否存在,IsDebuggerPresent是WIN提供的一个标准调试API
用于确定是否存在调试器。这个方法很简单TRUE为存在,FASLE则为不存在。

代码:
FD_IsDebuggerPresent:
FD_IsDebugger_Arg_Win32Api      equ 04h
    mov eax, dword ptr [esp+FD_IsDebugger_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    call dword ptr [eax].xIsDebuggerPresent
Exit_FD_IsDebuggerPresent:
    assume eax : nothing
    retn 04h
End_FD_IsDebuggerPresent:

我们更进一步的剖析IsDebuggerPresent函数,逆向它后即可得知。
这个函数读取当前进程的PEB中的BeingDebugger标志。
如果确定这个标志呢?
当进程加载的时候FS寄存器总是被设置成当前线程的TEB。这下就都OK
而在TEB的30h偏移处就是ProcessEnvironmentBlock了。
而PEB的偏移02h出就是BeingDebugged了。当BeingDebugger为0则没有调试器
不为0时则存在调试器。

代码:
FD_PEB_BeingDebuggedFlag:
    assume fs : nothing
    mov eax, fs:[30h]   ; eax = TEB.ProcessEnvironmentBlock
    inc eax
    inc eax
    mov eax, dword ptr [eax]
    and eax, 000000FFh  ; al = PEB.BeingDebugged
    test eax, eax
    jnz FD_PEB_BeingDebuggedFlag_Found
Exit_PEB_BeingDebuggedFlag:
    retn 0    
FD_PEB_BeingDebuggedFlag_Found:
    mov eax, 1
    jmp  Exit_PEB_BeingDebuggedFlag
End_FD_PEB_BeingDebuggedFlag:

在当BeingDebugged被设置为TRUE时,存在与PEB中的
NtGlobalFlag也会做出相应的改变。查看WIN2K代码的LdrpInitialize
会发现以下代码
if (Peb->BeingDebugged)
Peb->NtGlobalFlag |= ***_HEAP_ENABLE_FREE_CHECK |
                     ***_HEAP_ENABLE_TAIL_CHECK |
                     ***_HEAP_VALIDATE_PARAMETERS;
这个组合值为70h。所以我们又得到一个反调试的函数

代码:
FD_PEB_NtGlobalFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+68h]
    and eax, 070h
    test eax, eax
    jnz FD_PEB_NtGlobalFlags_Found
Exit_FD_PEB_NtGlobalFlags:
    retn 0
FD_PEB_NtGlobalFlags_Found:
    mov eax, 1
    jmp  Exit_FD_PEB_NtGlobalFlags
End_FD_PEB_NtGlobalFlags:

BeingDebugger标志还会影响ProcessHeap.Flags
如果这个标志不为0则存在调试器。

代码:
FD_Heap_ForceFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+18h]    ; PEB.ProcessHeap
    mov eax, dword ptr [eax+10h]    ; PEB.ProcessHeap.Flags
    test eax, eax
    jnz Found_FD_Heap_ForceFlags
Exit_FD_Heap_ForceFlag:
    retn 0
Found_FD_Heap_ForceFlags:
    mov eax, 1
    jmp Exit_FD_Heap_ForceFlag           
End_FD_Heap_ForceFlags:

在BeingDebugger下ProcessHeap.ForceFlags也受到影响
如果不为2则存在调试器。细节部分可以查看WIN2K的代码。
由于版本影响这里就不给出其中的代码了。

代码:
FD_Heap_HeapFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+18h]    ; PEB.ProcessHeap
    mov eax, dword ptr [eax+0ch]    ; PEB.ProcessHeap.ForceFlags
    cmp eax, 2
    jnz Found_FD_Heap_HeapFlags
Exit_FD_Heap_HeapFlags:
    retn 0  
Found_FD_Heap_HeapFlags:
    mov eax, 1
    jmp Exit_FD_Heap_HeapFlags  
End_FD_Heap_HeapFlags:

通过另一个API。CheckRemoteDebuggerPresent,这个API可以检测任何进程是否被调试器
加载。这里通过GetCurrentProcess取得自身进程的句柄。进行鉴别

代码:
FD_CheckRemoteDebuggerPresent:
FD_CheckRemoteDebuggerPresent_Arg_Win32Api    equ 04h
    mov eax, dword ptr [esp+FD_CheckRemoteDebuggerPresent_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    push esp
    push esp
    call dword ptr [eax].xGetCurrentProcess
    push eax
    call dword ptr [eax].xCheckRemoteDebuggerPresent
    pop esp
    assume eax : nothing
    retn 04h
End_FD_CheckRemoteDebuggerPresent:

如果逆向了CheckRemoteDebuggerPresent函数就可以明白,其实最终它是调用
NtQueryInformationProcess,查询进程的ProcessDebugPort。此值是用来维持系统
与调试器通讯的,在程序被调试器加载时系统会设置这个值为非0。

代码:
FD_NtQueryInfoProc_DbgPort:
FD_NtQueryInfoProc_DbgPort_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgPort_StackSize    equ sizeof PROCESS_DEBUG_PORT_INFO
FD_NtQueryInfoProc_DbgPort_ProcessInfo  equ -(FD_NtQueryInfoProc_DbgPort_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgPort_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgPort_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_PORT_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo]
    push eax
    push ProcessDebugPort
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgPort_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_PORT_INFO
    mov eax, dword ptr [eax].DebugPort
    test eax, eax
    jnz Found_FD_NtQueryInfoProc_DbgPort
FD_NtQueryInfoProc_DbgPort_Tmp1:
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgPort:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgPort:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgPort         
End_FD_NtQueryInfoProc_DbgPort:

当Windows系统创建一个调试会话开始,一个调试对象也被创建并产生一个
调试句柄。我们可以查询这个句柄的值来确定是否存在调试器。

代码:
FD_NtQueryInfoProc_DbgObjHandle:
FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgObjHandle_StackSize        equ sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO
FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo     equ -(FD_NtQueryInfoProc_DbgObjHandle_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgObjHandle_StackSize
    
    push ebx
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo]
    push eax    
    push SystemNotImplemented8
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgObjHandle_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_OBJECTHANDLE_INFO
    mov eax, dword ptr [eax].ObjectHandle
    test eax, eax
    jnz Found_FD_NtQueryInfoProc_DbgObjHandle
FD_NtQueryInfoProc_DbgObjHandle_Tmp1:
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgObjHandle:

    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgObjHandle:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgObjHandle
End_FD_NtQueryInfoProc_DbgObjHandle:

也可以通过使用ZwQueryInformationProcess函数,利用SystemNotImplemented9(1fh)
返回的PROCESS_DEBUG_FLAGS_INFO结构,如果DebugFlags返回0则检测到调试器。返回非0
则没发现调试器。

代码:
FD_NtQueryInfoProc_DbgFlags:
FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgFlags_StackSize    equ sizeof PROCESS_DEBUG_FLAGS_INFO
FD_NtQueryInfoProc_DbgFlags_ProcessInfo  equ -(FD_NtQueryInfoProc_DbgFlags_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgFlags_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_FLAGS_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo]
    push eax    
    push SystemNotImplemented9
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgFlags_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_FLAGS_INFO
    mov eax, dword ptr [eax].DebugFlags
    test eax, eax
    jz Found_FD_NtQueryInfoProc_DbgFlags
FD_NtQueryInfoProc_DbgFlags_Tmp1: 
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgFlags:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgFlags:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgFlags
End_FD_NtQueryInfoProc_DbgFlags:

如果Windows以调试方式启动,并与系统调试器建立通讯。
通过ZwQuerySystemInformation对SystemKernelDebuggerInformation进行查询
系统中是否存在系统调试器。

代码:
FD_NtQueryInfoProc_SysKrlDbgInfo:
FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize      equ sizeof PROCESS_DEBUG_FLAGS_INFO
FD_NtQueryInfoProc_SysKrlDbgInfo_Info           equ -(sizeof PROCESS_DEBUG_FLAGS_INFO)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_FLAGS_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info]
    push eax    
    push SystemKernelDebuggerInformation
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQuerySystemInformation
    test eax, eax
    jnz FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info]
    assume eax : ptr PROCESS_DEBUG_FLAGS_INFO
    mov eax, dword ptr [eax].DebugFlags
    test eax, eax
    jz Found_FD_NtQueryInfoProc_SysKrlDbgInfo
FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1:    
    xor eax, eax
Exit_FD_NtQueryInfoProc_SysKrlDbgInfo:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_SysKrlDbgInfo:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_SysKrlDbgInfo
End_FD_NtQueryInfoProc_SysKrlDbgInfo:

当调试会话被创建,这个标志会影响堆的创建。初始化中的堆内存填充了
很多类似0ABABABABh,0BAADF00Dh,0FEEEFEEEh这三个值。可以通过检测内存看
是否存在过多的这样值。判断调试器的存在,而正常启动的程序则不会被填充。

代码:
FD_Heap_Magic:
FD_Heap_Magic_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+FD_Heap_Magic_Arg_Win32Api]
    
    push ebx
    push ecx
    push edx
    push esi
    push edi
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    
    push 100h
    push NULL
    call dword ptr [ebx].xGetProcessHeap
    mov edi, eax    ; HeapHandle
    push eax
    call dword ptr [ebx].xHeapAlloc
    mov esi, eax    ; HeapMem
    xor ecx, ecx
    mov edx, 100h
    cld
    FD_Heap_Magic_Loop:
    lodsd
    cmp eax, 0ABABABABh
    jnz FD_Heap_Magic_Tmp1
    inc ecx
    FD_Heap_Magic_Tmp1:
    cmp eax, 0BAADF00Dh
    jnz FD_Heap_Magic_Tmp2
    inc ecx        
    FD_Heap_Magic_Tmp2:
    cmp eax, 0FEEEFEEEh
    jnz FD_Heap_Magic_Tmp3
    inc ecx
    FD_Heap_Magic_Tmp3:
    sub edx, 04h
    jnz FD_Heap_Magic_Loop
    push ecx    
    ;; free heap
    push esi
    push HEAP_NO_SERIALIZE
    push edi
    call dword ptr [ebx].xHeapFree
    pop ecx
    ;; judge count
    cmp ecx, 10h
    jae Found_FD_Heap_Magic
    xor eax, eax
Exit_FD_Heap_Magic:    
    pop edi    
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    retn 04h
Found_FD_Heap_Magic:
    mov eax, 1
    jmp Exit_FD_Heap_Magic
End_FD_Heap_Magic:

一般程序是没有被设置SeDebugPrivilege,如果一个当前进程被设置SeDebugPrivilege后
它就拥有了完全控制CSRSS.EXE的权限。通过进程表快照取得CSRSS.EXE进程的PID,之后
通过OpenProcess以PROCESS_ALL_ACCESS打开。开是否能打开此进程。

代码:
FD_SeDebugPrivilege:
FD_SeDebugPrivilege_Arg_Win32Api    equ 08h
FD_SeDebugPrivilege_StackSize       equ 10h + sizeof PROCESSENTRY32
FD_SeDebugPrivilege_hProcessSnap    equ -04h
FD_SeDebugPrivilege_PID_csrss       equ -08h
FD_SeDebugPrivilege_FingFlag        equ -0ch
FD_SeDebugPrivilege_pe32            equ -(10h+sizeof PROCESSENTRY32)
    push ebp
    mov ebp, esp
    sub esp, FD_SeDebugPrivilege_StackSize
    
    push ebx
    push ecx
    push edi
    
    ;; clear stack
    lea edi, [ebp-FD_SeDebugPrivilege_StackSize]
    mov ecx, FD_SeDebugPrivilege_StackSize
    xor eax, eax
    cld
    rep stosb
    
    mov ebx, dword ptr [ebp+FD_SeDebugPrivilege_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    lea edi, [ebp+FD_SeDebugPrivilege_pe32]
    assume edi : ptr PROCESSENTRY32
                     
    push 0
    push TH32CS_SNAPPROCESS
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    cmp eax, INVALID_HANDLE_VALUE
    jz NotFound_FD_SeDebugPrivilege
    mov dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap], eax
    push sizeof PROCESSENTRY32
    pop dword ptr [edi].dwSize
    
    push edi
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xProcess32First
    test eax, eax
    jnz FD_SeDebugPrivilege_Loop
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_SeDebugPrivilege
    
    FD_SeDebugPrivilege_Loop:
    call FD_SeDebugPrivilege_Str
        db 'CSRSS.EXE',0
    FD_SeDebugPrivilege_Str:
    lea eax, [edi].szExeFile
    push eax    
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jnz FD_SeDebugPrivilege_Tmp2
    push dword ptr [edi].th32ProcessID
    pop dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss]
    push TRUE
    pop dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    FD_SeDebugPrivilege_Tmp2:
    mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    test eax, eax
    jnz FD_SeDebugPrivilege_Tmp3
    push edi
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jnz FD_SeDebugPrivilege_Loop
    
    FD_SeDebugPrivilege_Tmp3:
    mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    test eax, eax
    jz FD_SeDebugPrivilege_Tmp4
    push dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss]
    push FALSE
    push PROCESS_QUERY_INFORMATION
    call dword ptr [ebx].xOpenProcess
    test eax, eax
    jz FD_SeDebugPrivilege_Tmp4
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_SeDebugPrivilege
    FD_SeDebugPrivilege_Tmp4:
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_SeDebugPrivilege
Exit_FD_SeDebugPrivilege:
    pop edi
    pop ecx
    pop ebx
    assume ebx : nothing
    assume edi : nothing
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_SeDebugPrivilege:
    xor eax, eax
    jmp Exit_FD_SeDebugPrivilege
Found_FD_SeDebugPrivilege:
    mov eax, 1
    jmp Exit_FD_SeDebugPrivilege
End_FD_SeDebugPrivilege:

通过逻辑的判断也可以找到调试器所在,一般来讲程序都是有explorer.exe进程启动的(通过双击)
如果我们的进程的父进程不是explorer.exe则发现调试器。如果有调试的名称冒名是explorer.exe
那么我们判断父进程ID后进一步判断explorer.exe进程的路径是否存在于Windows目录下。如果不是
则发现调试器。此类方法也可以也被病毒用作穿透仿真机。

代码:
FD_Parent_Process:
FD_Parent_Process_Arg_Win32Api      equ 08h
FD_Parent_Process_StackSize         equ MAX_PATH + sizeof PROCESSENTRY32 + sizeof MODULEENTRY32 + 20h
FD_Parent_Process_hParnet           equ -04h
FD_Parent_Process_PIDExplorer       equ -08h
FD_Parent_Process_PIDParent         equ -0ch
FD_Parent_Process_PIDChild          equ -10h
FD_Parent_Process_hSnapshot         equ -14h
FD_Parent_Process_pe32              equ -(20h + PROCESSENTRY32)
FD_Parent_Process_me32              equ -(20h + PROCESSENTRY32 + MODULEENTRY32)
FD_Parent_Process_lpszSystemInfo    equ -(20h + PROCESSENTRY32 + MODULEENTRY32 + MAX_PATH)
    push ebp
    mov ebp, esp
    sub esp, FD_Parent_Process_StackSize
    
    push ebx
    push ecx
    push edi
    push esi
    
    ;; clear the stack
    lea edi, [ebp-FD_Parent_Process_StackSize]
    xor eax, eax
    mov ecx, FD_Parent_Process_StackSize
    cld
    rep stosb
    
    mov ebx, dword ptr [ebp+FD_Parent_Process_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    lea eax, [ebp+FD_Parent_Process_pe32]
    assume eax : ptr PROCESSENTRY32
    push sizeof PROCESSENTRY32
    pop dword ptr [eax].dwSize
    
    call dword ptr [ebx].xGetCurrentProcessId
    mov dword ptr [ebp+FD_Parent_Process_PIDChild], eax
    
    push 0
    push TH32CS_SNAPPROCESS
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax
    
    lea eax, [ebp+FD_Parent_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xProcess32First
    test eax, eax
    jz FD_Parent_Process_Tmp1
    FD_Parent_Process_Loop1:
    lea eax, [ebp+FD_Parent_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jz FD_Parent_Process_Tmp2
    call FD_Parent_Process_Str1
        db "EXPLORER.EXE",0
    FD_Parent_Process_Str1:
    lea eax, [ebp+FD_Parent_Process_pe32]
    lea eax, [eax].szExeFile    
    push eax
    call dword ptr [ebx].xlstrcmpiA
    jnz FD_Parent_Process_Tmp3
    mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    test eax, eax
    jnz FD_Parent_Process_Tmp3
    lea eax, [ebp+FD_Parent_Process_pe32]
    assume eax : ptr PROCESSENTRY32
    push dword ptr [eax].th32ProcessID
    pop dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    FD_Parent_Process_Tmp3:
    lea eax, [ebp+FD_Parent_Process_pe32]
    mov eax, dword ptr [eax].th32ProcessID
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDChild]
    jnz FD_Parent_Process_Tmp4
    lea eax, [ebp+FD_Parent_Process_pe32]
    push dword ptr [eax].th32ParentProcessID
    pop dword ptr [ebp+FD_Parent_Process_PIDParent]
    FD_Parent_Process_Tmp4:
    jmp FD_Parent_Process_Loop1
    FD_Parent_Process_Tmp1:
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_Parent_Process
    FD_Parent_Process_Tmp2:
    mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDParent]
    jz FD_Parent_Process_Tmp5
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_Parent_Process
    FD_Parent_Process_Tmp5:
    lea eax, [ebp+FD_Parent_Process_me32]
    assume eax : ptr MODULEENTRY32
    push sizeof MODULEENTRY32
    pop dword ptr [eax].dwSize  
    push dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    push TH32CS_SNAPMODULE
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax
    lea eax, [ebp+FD_Parent_Process_me32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xModule32First
    test eax, eax
    jz FD_Parent_Process_Tmp6
    FD_Parent_Process_Loop2:
    lea eax, [ebp+FD_Parent_Process_me32]
    mov eax, dword ptr [eax].th32ProcessID
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    jnz FD_Parent_Process_Tmp7
    push MAX_PATH
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xGetWindowsDirectoryA
    call FD_Parent_Process_Str2
        db '/',0
    FD_Parent_Process_Str2:
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xlstrcatA
    call FD_Parent_Process_Str3
        db "EXPLORER.EXE",0
    FD_Parent_Process_Str3:
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xlstrcatA
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    lea eax, [ebp+FD_Parent_Process_me32]
    lea eax, [eax].szExePath
    push eax
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz FD_Parent_Process_Tmp6
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_Parent_Process
    FD_Parent_Process_Tmp7:
    lea eax, [ebp+FD_Parent_Process_me32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xModule32Next
    test eax, eax
    jnz FD_Parent_Process_Loop2
    FD_Parent_Process_Tmp6:
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_Parent_Process
Exit_FD_Parent_Process:
    pop esi
    pop edi
    pop ecx
    pop ebx
    assume eax : nothing
    assume ebx : nothing           
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_Parent_Process:
    xor eax, eax
    jmp Exit_FD_Parent_Process
Found_FD_Parent_Process:
    mov eax, 1
    jmp Exit_FD_Parent_Process    
End_FD_Parent_Process:

当调试会话创建,将产生一个调试对象,我们通过ntdll中的
NtQueryObject函数参看调试对象的个数是否不为零,来确定调试器的存在
以ObjectAllTypeInformation使用NtQueryObject查询后会返回一个
OBJECT_ALL_INFORMATION的结构,其中NumberOfObjectsTypes成员为所有的
对象类型在ObjectTypeInformation数组中的计数
此对象如下
typedef struct _OBJECT_ALL_INFORMATION
ULONG    NumberOfObjectsTypes;
OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];
}
typedef strcut _OBJECT_TYPE_INFORMATION {
[00]UNICODE_STRING TypeName;
[08]ULONG TotalNumberofHandles;
[0c]ULONG TotalNumberofObjects;
...
}
循环遍历ObjectTypeInformation对比类型的名字,如有类型名为DebugObject则
检测TotalNumberofHandles与TotalNumberofObjects如果不为0则存在调试器。

代码:
FD_DebugObject_NtQueryObject:
FD_DebugObject_NtQueryObject_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+FD_DebugObject_NtQueryObject_Arg_Win32Api]
        
    push ebx
    push ecx
    push edx
    push edi
    push esi
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    push edx    ; alloc the stack
    
    push esp    ; ReturnLength
    push 0
    push 0
    push ObjectAllTypeInformation
    push 0
    call dword ptr [ebx].xNtQueryObject
    pop ecx
    ;; make a tmp stack
    push ebp
    mov ebp, esp
    sub esp, ecx
    mov esi, esp
    ;; ObjectInformationLength
    push 0
    push ecx
    push esi
    push ObjectAllTypeInformation
    push 0
    call dword ptr [ebx].xNtQueryObject
    cld
    ;; NumberOfObjectsTypes
    lodsd
    xchg ecx, eax ; ecx = NumberOfObjectsTypes
    FD_DebugObject_NtQueryObject_Loop:    
    ;; load string lengths
    lodsd
    movzx edx, ax
    ;; pointer to TypeName
    lodsd
    xchg esi, eax
    ;; sizeof(L"DebugObject")
    ;; avoids superstrings
    ;; like "DebugObjective"
    cmp edx, 16h
    jnz FD_DebugObject_NtQueryObject_Tmp2
    xchg ecx, edx
    FD_DebugObject_NtQueryObject_Tmp1:
    call FD_DebugObject_NtQueryObject_UnicodeStr1
        dw 'D','e','b','u','g'
        dw 'O','b','j','e','c','t'
    FD_DebugObject_NtQueryObject_UnicodeStr1:
    pop edi
    repe cmpsb
    xchg ecx, edx
    jnz FD_DebugObject_NtQueryObject_Tmp2
    ;; TotalNumberOfObjects
    cmp dword ptr [eax], edx
    jnz Found_FD_DebugObject_NtQueryObject
    ;; point to trailing  null
    FD_DebugObject_NtQueryObject_Tmp2:
    add esi, edx
    ;; round down to dword
    and esi, -4
    ;; skip trailing null
    ;; and any alignment bytes
    lodsd
    loop FD_DebugObject_NtQueryObject_Loop
    xor eax, eax
Exit_FD_DebugObject_NtQueryObject:
    ;; clear the tmp stack
    mov esp, ebp
    pop ebp
    
    pop esi
    pop edi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing    
    retn 04h
Found_FD_DebugObject_NtQueryObject:    
    mov eax, 1
    jmp Exit_FD_DebugObject_NtQueryObject
End_FD_DebugObject_NtQueryObject:

此类方法,利用FindWindow函数通过寻找是否存在一些常见调试软件的Title。这里收集了一些
如果有其他的Title,请朋友们告知。。。

代码:
FD_Find_Debugger_Window:
FD_Find_Debugger_Window_Arg_WinApi32   equ 08h
    push ebp
    mov ebp, esp
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_Find_Debugger_Window_Arg_WinApi32]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    call FD_Find_Debugger_Window_Str1
        db "1212121",0
    FD_Find_Debugger_Window_Str1:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window
    
    push NULL
    call FD_Find_Debugger_Window_Str2
        db "icu_dbg",0
    FD_Find_Debugger_Window_Str2:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window    
    
    push NULL
    call FD_Find_Debugger_Window_Str3
        db "pe--diy",0
    FD_Find_Debugger_Window_Str3:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window     
    
    push NULL
    call FD_Find_Debugger_Window_Str5
        db "ollydbg",0
    FD_Find_Debugger_Window_Str5:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window 

    push NULL
    call FD_Find_Debugger_Window_Str6
        db "odbydyk",0
    FD_Find_Debugger_Window_Str6:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window

    push NULL
    call FD_Find_Debugger_Window_Str7
        db "WinDbgFrameClass",0
    FD_Find_Debugger_Window_Str7:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window

    push NULL
    call FD_Find_Debugger_Window_Str8
        db "TDeDeMainForm",0
    FD_Find_Debugger_Window_Str8:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window

    push NULL
    call FD_Find_Debugger_Window_Str9
        db "TIdaWindow",0
    FD_Find_Debugger_Window_Str9:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window

    push NULL
    call FD_Find_Debugger_Window_StrA
        db "TESTDBG",0
    FD_Find_Debugger_Window_StrA:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window

    push NULL
    call FD_Find_Debugger_Window_StrB
        db "kk1",0
    FD_Find_Debugger_Window_StrB:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window
    
    push NULL
    call FD_Find_Debugger_Window_StrC
        db "Eew75",0
    FD_Find_Debugger_Window_StrC:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window  
    
    push NULL
    call FD_Find_Debugger_Window_StrD
        db "Shadow",0
    FD_Find_Debugger_Window_StrD:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window       
   
    push NULL
    call FD_Find_Debugger_Window_StrE
        db "PEiD v0.94",0
    FD_Find_Debugger_Window_StrE:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window    
   
    push NULL
    call FD_Find_Debugger_Window_StrF
        db "Registry Monitor - Sysinternals: www.sysinternals.com",0
    FD_Find_Debugger_Window_StrF:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window    
 
    push NULL
    call FD_Find_Debugger_Window_Str10
        db "File Monitor - Sysinternals: www.sysinternals.com",0
    FD_Find_Debugger_Window_Str10:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window    

    push NULL
    call FD_Find_Debugger_Window_Str11
        db "Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF",0
    FD_Find_Debugger_Window_Str11:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window
    jmp NotFound_Found_FD_Find_Debugger_Window
Exit_FD_Find_Debugger_Window:
    pop ebx
    assume ebx : nothing
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_Found_FD_Find_Debugger_Window:
    xor eax, eax
    jmp Exit_FD_Find_Debugger_Window
Found_FD_Find_Debugger_Window:
    mov eax, 1
    jmp Exit_FD_Find_Debugger_Window    
End_FD_Find_Debugger_Window:

这种方面的核心思想是比对在进程表中是否是出现了调试器进程名,如果出现则退出。
不过这种方法很容易躲过,而且也可能造成误判。

代码:
FD_Find_Debugger_Process:
FD_Find_Debugger_Process_Arg_Win32Api   equ 08h
FD_Find_Debugger_Process_StackSize      equ 10h + sizeof PROCESSENTRY32
FD_Find_Debugger_Process_hSnapshot      equ -04h
FD_Find_Debugger_Process_hParnet        equ -08h
FD_Find_Debugger_Process_pe32           equ -(10+sizeof PROCESSENTRY32)

    push ebp
    mov ebp, esp
    sub esp, FD_Find_Debugger_Process_StackSize
    
    push edi
    push esi
    push edx
    push ecx
    push ebx
    
    ;; clear the stack
      lea edi, [ebp-FD_Find_Debugger_Process_StackSize]
      mov ecx, FD_Find_Debugger_Process_StackSize
      xor eax, eax
      cld
      rep stosb
    
    mov ebx, dword ptr [ebp+FD_Find_Debugger_Process_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    lea esi, [ebp+FD_Find_Debugger_Process_pe32]
    assume esi : ptr PROCESSENTRY32
    push sizeof PROCESSENTRY32
    pop dword ptr [esi].dwSize
    
    push 0
    push TH32CS_SNAPPROCESS
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    mov dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot], eax
    lea eax, [ebp+FD_Find_Debugger_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot]
    call dword ptr [ebx].xProcess32First
    test eax, eax
    jz NotFound_FD_Find_Debugger_Process
    FD_Find_Debugger_Process_Loop:
    lea eax, [esi].szExeFile
    mov edi, eax
    call FD_Find_Debugger_Process_Str1
        db "OLLYICE.EXE",0
    FD_Find_Debugger_Process_Str1:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process
    
    call FD_Find_Debugger_Process_Str2
        db "IDAG.EXE",0
    FD_Find_Debugger_Process_Str2:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process 
    
    call FD_Find_Debugger_Process_Str3
        db "OLLYDBG.EXE",0
    FD_Find_Debugger_Process_Str3:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process    
    
    call FD_Find_Debugger_Process_Str4
        db "PEID.EXE",0
    FD_Find_Debugger_Process_Str4:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process    
    
    call FD_Find_Debugger_Process_Str5
        db "SOFTICE.EXE",0
    FD_Find_Debugger_Process_Str5:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process

    call FD_Find_Debugger_Process_Str6
        db "LORDPE.EXE",0
    FD_Find_Debugger_Process_Str6:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process

    call FD_Find_Debugger_Process_Str7
        db "IMPORTREC.EXE",0
    FD_Find_Debugger_Process_Str7:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process
    
    call FD_Find_Debugger_Process_Str8
        db "W32DSM89.EXE",0
    FD_Find_Debugger_Process_Str8:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process    
    
    call FD_Find_Debugger_Process_Str9
        db "WINDBG.EXE",0
    FD_Find_Debugger_Process_Str9:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process
    lea eax, [ebp+FD_Find_Debugger_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jnz FD_Find_Debugger_Process_Loop
    jmp NotFound_FD_Find_Debugger_Process
Exit_FD_Find_Debugger_Process:
    ;; close the Shotsnap handle
    push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    assume esi : nothing
    mov esp, ebp
    pop ebp 
    retn 04h
NotFound_FD_Find_Debugger_Process:
    xor eax, eax
    jmp Exit_FD_Find_Debugger_Process
Found_FD_Find_Debugger_Process:
    mov eax, 1
    jmp Exit_FD_Find_Debugger_Process        
End_FD_Find_Debugger_Process:

这种方法通过打开一些调试软件加载到系统中驱动的句柄,从而判断是否有
调试软件的存在。

代码:
FD_Find_Device_Driver:
FD_Find_Device_Driver_Arg_Win32Api      equ 08h

    push ebp
    mov ebp, esp
    
    push ebx
    push ecx
    push edx
    push esi
    push edi
    
    mov ebx, dword ptr [ebp+FD_Find_Device_Driver_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    ;; check softice on unknow system
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str1
        db "//./SIWVID",0
    FD_Find_Device_Driver_Str1:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check softice 4.05 on win2k
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str2
        db "//./NTICE",0
    FD_Find_Device_Driver_Str2:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check softice on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str3
        db "//./SICE",0
    FD_Find_Device_Driver_Str3:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver    
    
    ;; check softice on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str4
        db "//./SIWDEBUG",0
    FD_Find_Device_Driver_Str4:
    call dword ptr [ebx].xCreateFileA
    push eax
    call dword ptr [ebx].xGetLastError
    test al, 032h
    pop eax
    jz Found_FD_Find_Device_Driver
    
    ;; check regmon on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str5
        db "//./REGVXD",0
    FD_Find_Device_Driver_Str5:
    call dword ptr [ebx].xCreateFileA    
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check RegMON
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str6
        db "//./FILEM",0
    FD_Find_Device_Driver_Str6:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check TRW
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str7
        db "//./TRW",0
    FD_Find_Device_Driver_Str7:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver       
    
    ;; check softice extender
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str8
        db "//./ICEEXT",0
    FD_Find_Device_Driver_Str8:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver 
    jmp NotFound_FD_Find_Device_Driver
    
Exit_FD_Find_Device_Driver:    
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing    
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_Find_Device_Driver:
    xor eax, eax
    jmp Exit_FD_Find_Device_Driver
Found_FD_Find_Device_Driver:
    push eax
    assume ebx : ptr WIN32APIBASE
    call dword ptr [ebx].xCloseHandle
    assume ebx : nothing
    mov eax, 1
    jmp Exit_FD_Find_Device_Driver
End_FD_Find_Device_Driver:

利用int3中断引起异常。如果,正常运行则触发异常。

代码:
FD_Exception_Int3:
    call Get_FD_Exception_Int3_Eip
    Get_FD_Exception_Int3_Eip:
    pop eax
    add eax, offset FD_Exception_Int3_Exception - offset Get_FD_Exception_Int3_Eip
    ;; setup exception
    assume fs : nothing
    push eax
    push dword ptr fs : [0]
    mov dword ptr fs : [0], esp
    ;; reset eax
    xor eax, eax
    int 03h
    ;; unsetup exception
    pop dword ptr fs : [0]
    add esp, 04h
    
    ;; check the flag
    test eax, eax
    jz Found_FD_Exception_Int3
    jmp NotFound_FD_Exception_Int3
FD_Exception_Int3_Exception:
    mov eax, dword ptr [esp+0ch]
    ;; eax = ContextRecord
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    inc dword ptr [eax].regEip
    xor eax, eax
    assume eax : nothing
    retn
Exit_FD_Exception_Int3:
    retn 0h
NotFound_FD_Exception_Int3:
    xor eax, eax
    jmp Exit_FD_Exception_Int3
Found_FD_Exception_Int3:
    mov eax, 1
    jmp Exit_FD_Exception_Int3        
End_FD_Exception_Int3:

通过对EFLAGS中的TF的检测来确定是否存在调试器。当TF=1的时会触发一个
单步执行异常,可以通过安装异常处理针来捕获。

代码:
FD_Exception_Popf:
    push ebx

    call Get_FD_Exception_Popf_Eip
    Get_FD_Exception_Popf_Eip:
    pop eax
    add eax, offset FD_Exception_Popf_Exception - offset Get_FD_Exception_Popf_Eip
    assume fs : nothing    
    push eax
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    ;; reset eax
    xor eax, eax
    pushf
    mov dword ptr [esp], 0100h
    popf
    nop
FD_Exception_Popf_NextEip:
    pop dword ptr fs:[0]
    add esp, 04h
    
    ;; check the flag
    test eax, eax
    jz Found_FD_Exception_Popf
    jmp NotFound_FD_Exception_Popf
    
FD_Exception_Popf_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    call Get_FD_Exception_Popf_Exception_Eip2
    Get_FD_Exception_Popf_Exception_Eip2:
    pop ebx
    sub ebx, offset Get_FD_Exception_Popf_Exception_Eip2 - offset FD_Exception_Popf_NextEip
    mov dword ptr [eax].regEip, ebx
    xor eax, eax
    retn
    assume eax : nothing
Exit_FD_Exception_Popf:
    pop ebx
    retn 0h
NotFound_FD_Exception_Popf:
    xor eax, eax
    jmp Exit_FD_Exception_Popf
Found_FD_Exception_Popf:
    mov eax, 1
    jmp Exit_FD_Exception_Popf
End_FD_Exception_Popf:

触发OutputDebugString的错误。如果调试器存在则GetLastError为0。

代码:
FD_OutputDebugString:
FD_OutputDebugString_Arg_Win32Api       equ 04h
    
    mov eax, dword ptr [esp+FD_OutputDebugString_Arg_Win32Api]
    push ebx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    call FD_OutputDebugString_Str
        db 0
    FD_OutputDebugString_Str:
    call dword ptr [ebx].xOutputDebugStringA
    call dword ptr [ebx].xGetLastError 
    test eax, eax   
    jnz NotFound_FD_OutputDebugString
    inc eax
    pop ebx
    assume ebx : nothing
    retn 04h
NotFound_FD_OutputDebugString:
    pop ebx
    xor eax, eax
    retn 04h
    
End_FD_OutputDebugString:

Ollydbg允许设置一个内存访问/写入断点。它通过页面保护来实现。
页面保护提供了当应用程序的某块内存被访问时获得通知。
页面保护通过PAGE_GUARD页面保护修改符来设置。如果访问的内存
是受保护的将会产生一个STATUS_GUARD_PAGE_VIOLATION(0x80000001)异常
如果进程被Ollydbg调试并且受保护的页面被访问,将不会抛出异常。访问
将会被当作内存断点来处理。

代码:
FS_OD_Exception_GuardPages:
FS_OD_Exception_GuardPages_Arg_Win32Api     equ 04h

    mov eax, dword ptr [esp+FS_OD_Exception_GuardPages_Arg_Win32Api]
    
    push edi    ; pAllocatedMem
    push ebx
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
     
    push PAGE_READWRITE
    push MEM_COMMIT
    push 01000h
    push NULL
    call dword ptr [ebx].xVirtualAlloc
    test eax, eax
    jz NotFound_FS_OD_Exception_GuardPages
    mov edi, eax
    ;; store a RENT on the allocated memory
    mov byte ptr [edi], 0c3h
    push edi        ; alloc stack
    
    push esp
    push PAGE_EXECUTE_READ + PAGE_GUARD
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualProtect
    ;; set eax to 0
    xor eax, eax
    ;; trigger a STATUS_GUARD_PAGE_VIOLATION exception
    ;; setup expcetion
    call Get_FS_OD_Exception_GuardPages_Eip
    Get_FS_OD_Exception_GuardPages_Eip:
    pop eax
    add eax, offset FS_OD_Exception_GuardPages_Exception - offset Get_FS_OD_Exception_GuardPages_Eip
    assume fs : nothing
    push eax
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    call edi
FS_OD_Exception_GuardPages_Continue:
    pop edi
    ;; unsetup exception
    pop dword ptr fs:[0]
    add esp, 04h
    test eax, eax
    jz Found_FS_OD_Exception_GuardPages
    jmp NotFound_FS_OD_Exception_GuardPages
FS_OD_Exception_GuardPages_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    call Get_FS_OD_Exception_GuardPages_Exception_Eip
    Get_FS_OD_Exception_GuardPages_Exception_Eip:
    pop ebx
    sub ebx, offset Get_FS_OD_Exception_GuardPages_Exception_Eip - offset FS_OD_Exception_GuardPages_Continue  
    mov dword ptr [eax].regEip, ebx
    assume eax : nothing
    xor eax, eax
    retn
    
Exit_FS_OD_Exception_GuardPages:
    pop ebx
    pop edi
    assume ebx : nothing
    retn 04h
    
NotFound_FS_OD_Exception_GuardPages:
    assume ebx : ptr WIN32APIBASE
    push MEM_DECOMMIT
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    xor eax, eax
    jmp Exit_FS_OD_Exception_GuardPages
Found_FS_OD_Exception_GuardPages:
    assume ebx : ptr WIN32APIBASE
    push MEM_DECOMMIT
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    mov eax, 1
    jmp Exit_FS_OD_Exception_GuardPages
End_FS_OD_Exception_GuardPages:

Softice调试器会对UnhandleExceptionFilter进行修改,将第一个字节修改为CCh(int 3)

代码:
FS_SI_UnhandledExceptionFilter:
FS_SI_UnhandledExceptionFilter_Arg_Win32Api     equ 04h
    mov eax, dword ptr [esp+FS_SI_UnhandledExceptionFilter_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xUnhandledExceptionFilter
    mov al, byte ptr [eax]
    sub al, 0cch
    jz Found_FS_SI_UnhandledExceptionFilter
    xor eax, eax
Exit_FS_SI_UnhandledExceptionFilter:   
    assume eax : nothing
    retn 04h
Found_FS_SI_UnhandledExceptionFilter:
    mov eax, 1
    jmp Exit_FS_SI_UnhandledExceptionFilter    
End_FS_SI_UnhandledExceptionFilter:

Ollydbg的插件对几个函数的首部进行修改。通过对齐的检测来判断系统中是否存在调试器

代码:
FS_ODP_Process32NextW:
FS_ODP_Process32NextW_Arg_Win32Api          equ 04h
    mov eax, dword ptr [esp+FS_ODP_Process32NextW_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xProcess32NextW
    mov ax, word ptr [eax]
    sub ax, 0FF88h
    jnz Found_FS_ODP_Process32NextW
    xor eax, eax
Exit_FS_ODP_Process32NextW:
    assume eax : nothing 
    retn 04h
Found_FS_ODP_Process32NextW:
    mov eax, 1
    jmp Exit_FS_ODP_Process32NextW
End_FS_ODP_Process32NextW:


FS_ODP_OutputDebugStringA:
FS_ODP_OutputDebugStringA_Arg_Win32Api      equ 04h
    mov eax, dword ptr [ebp+FS_ODP_OutputDebugStringA_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xOutputDebugStringA
    mov ax, word ptr [eax]
    sub ax, 03468h
    jnz Found_FS_ODP_OutputDebugStringA
    xor eax, eax
Exit_FS_ODP_OutputDebugStringA:
    assume eax : nothing
    retn 04h
Found_FS_ODP_OutputDebugStringA:
    mov eax, 1
    jmp Exit_FS_ODP_OutputDebugStringA            
End_FS_ODP_OutputDebugStringA:


FS_ODP_OpenProcess:
FS_ODP_OpenProcess_Arg_Win32Api     equ 04h
    mov eax, dword ptr [ebp+FS_ODP_OpenProcess_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xOpenProcess
    mov al, byte ptr [eax+06h]
    ;; Hide Debugger Plugin of OD is present
    sub al, 0eah
    jnz Found_FS_ODP_OpenProcess
    xor eax, eax
Exit_FS_ODP_OpenProcess:
    assume eax : nothing
    retn 04h  
Found_FS_ODP_OpenProcess:
    mov eax, 1
    jmp Exit_FS_ODP_OpenProcess      
End_FS_ODP_OpenProcess:

FB_HWBP_Exception:
    call Get_FB_HWBP_Exception_Eip
    Get_FB_HWBP_Exception_Eip:
    pop eax
    add eax, offset FB_HWBP_Exception_Exception - offset FB_HWBP_Exception
    assume fs : nothing
    push eax
    push fs:[0]
    mov dword ptr fs:[0], esp
    ;; reset eax
    xor eax, eax
    int 01h
    pop dword ptr fs:[0]
    add esp, 04h
    test eax, eax
    jnz Found_FB_HWBP_Exception
    jmp NotFound_FB_HWBP_Exception
FB_HWBP_Exception_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    ;; check if debug Registers Context.Dr0-Dr3 is not zero
    cmp dword ptr [eax].iDr0, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    cmp dword ptr [eax].iDr1, 0
    jnz FB_HWBP_Exception_HardwareBp_Found  
    cmp dword ptr [eax].iDr2, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    cmp dword ptr [eax].iDr3, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    jmp FB_HWBP_Exception_Exception_Ret     
FB_HWBP_Exception_HardwareBp_Found:
    ;; set Context.Eax to signal breakpoint found
    mov dword ptr [eax].regEax, 0FFFFFFFFh
FB_HWBP_Exception_Exception_Ret:
    ;; set Context.Eip upon return
    add dword ptr [eax].regEip, 02h
    xor eax, eax
    retn
Exit_FB_HWBP_Exception:
    retn 0h
NotFound_FB_HWBP_Exception:
    xor eax, eax
    jmp Exit_FB_HWBP_Exception
Found_FB_HWBP_Exception:
    mov eax, 1
    jmp Exit_FB_HWBP_Exception                
End_FB_HWBP_Exception:

通过对代码节做CRC值的验证来判断是否存在内存补丁

代码:
FB_SWBP_Memory_CRC:
FB_SWBP_Memory_CRC_Arg_Win32Api         equ 08h
FB_SWBP_Memory_CRC_Arg_FuncAddr         equ 0ch
FB_SWBP_Memory_CRC_StackSize            equ 10h+MAX_PATH
FB_SWBP_Memory_CRC_fileSize             equ -04h
FB_SWBP_Memory_CRC_NumberOfBytesRW      equ -08h
FB_SWBP_Memory_CRC_pBuffer              equ -0ch
FB_SWBP_Memory_CRC_szFileName           equ -(10h+MAX_PATH)
    push ebp
    mov ebp, esp
    sub esp, FB_SWBP_Memory_CRC_StackSize
    
    push esi
    push edi
    push ecx
    push edx
    push ebx
    
    
    ;; clear the stack
    lea edi, [ebp+FB_SWBP_Memory_CRC_szFileName]
    mov ecx, FB_SWBP_Memory_CRC_StackSize
    xor eax, eax
    cld
    rep stosb
    
    
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    push MAX_PATH
    lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
    push eax
    push NULL
    call dword ptr [ebx].xGetModuleFileNameA
    ;; open file
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ
    push GENERIC_READ
    lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
    push eax
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jz Error_FB_SWBP_Memory_CRC
    mov edi, eax
    p

【上篇】
【下篇】

抱歉!评论已关闭.