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


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

代码:
FD_IsDebuggerPresent:
FD_IsDebugger_Arg_Win32Api      equ 04h
    mov eax, dword ptr [esp+FD_IsDebugger_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    call dword ptr [eax].xIsDebuggerPresent
Exit_FD_IsDebuggerPresent:
    assume eax : nothing
    retn 04h
End_FD_IsDebuggerPresent:
我们更进一步的剖析IsDebuggerPresent函数,逆向它后即可得知。
这个函数读取当前进程的PEB中的BeingDebugger标志。
如果确定这个标志呢?
当进程加载的时候FS寄存器总是被设置成当前线程的TEB。这下就都OK
而在TEB的30h偏移处就是ProcessEnvironmentBlock了。
而PEB的偏移02h出就是BeingDebugged了。当BeingDebugger为0则没有调试器
不为0时则存在调试器。
代码:
FD_PEB_BeingDebuggedFlag:
    assume fs : nothing
    mov eax, fs:[30h]   ; eax = TEB.ProcessEnvironmentBlock
    inc eax
    inc eax
    mov eax, dword ptr [eax]
    and eax, 000000FFh  ; al = PEB.BeingDebugged
    test eax, eax
    jnz FD_PEB_BeingDebuggedFlag_Found
Exit_PEB_BeingDebuggedFlag:
    retn 0    
FD_PEB_BeingDebuggedFlag_Found:
    mov eax, 1
    jmp  Exit_PEB_BeingDebuggedFlag
End_FD_PEB_BeingDebuggedFlag:
在当BeingDebugged被设置为TRUE时,存在与PEB中的
NtGlobalFlag也会做出相应的改变。查看WIN2K代码的LdrpInitialize
会发现以下代码
if (Peb->BeingDebugged)
Peb->NtGlobalFlag |= ***_HEAP_ENABLE_FREE_CHECK |
                     ***_HEAP_ENABLE_TAIL_CHECK |
                     ***_HEAP_VALIDATE_PARAMETERS;
这个组合值为70h。所以我们又得到一个反调试的函数
代码:
FD_PEB_NtGlobalFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+68h]
    and eax, 070h
    test eax, eax
    jnz FD_PEB_NtGlobalFlags_Found
Exit_FD_PEB_NtGlobalFlags:
    retn 0
FD_PEB_NtGlobalFlags_Found:
    mov eax, 1
    jmp  Exit_FD_PEB_NtGlobalFlags
End_FD_PEB_NtGlobalFlags:
BeingDebugger标志还会影响ProcessHeap.Flags
如果这个标志不为0则存在调试器。
代码:
FD_Heap_ForceFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+18h]    ; PEB.ProcessHeap
    mov eax, dword ptr [eax+10h]    ; PEB.ProcessHeap.Flags
    test eax, eax
    jnz Found_FD_Heap_ForceFlags
Exit_FD_Heap_ForceFlag:
    retn 0
Found_FD_Heap_ForceFlags:
    mov eax, 1
    jmp Exit_FD_Heap_ForceFlag           
End_FD_Heap_ForceFlags:
在BeingDebugger下ProcessHeap.ForceFlags也受到影响
如果不为2则存在调试器。细节部分可以查看WIN2K的代码。
由于版本影响这里就不给出其中的代码了。
代码:
FD_Heap_HeapFlags:
    assume fs : nothing
    mov eax, fs:[30h]
    mov eax, dword ptr [eax+18h]    ; PEB.ProcessHeap
    mov eax, dword ptr [eax+0ch]    ; PEB.ProcessHeap.ForceFlags
    cmp eax, 2
    jnz Found_FD_Heap_HeapFlags
Exit_FD_Heap_HeapFlags:
    retn 0  
Found_FD_Heap_HeapFlags:
    mov eax, 1
    jmp Exit_FD_Heap_HeapFlags  
End_FD_Heap_HeapFlags:
通过另一个API。CheckRemoteDebuggerPresent,这个API可以检测任何进程是否被调试器
加载。这里通过GetCurrentProcess取得自身进程的句柄。进行鉴别
代码:
FD_CheckRemoteDebuggerPresent:
FD_CheckRemoteDebuggerPresent_Arg_Win32Api    equ 04h
    mov eax, dword ptr [esp+FD_CheckRemoteDebuggerPresent_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    push esp
    push esp
    call dword ptr [eax].xGetCurrentProcess
    push eax
    call dword ptr [eax].xCheckRemoteDebuggerPresent
    pop esp
    assume eax : nothing
    retn 04h
End_FD_CheckRemoteDebuggerPresent:
如果逆向了CheckRemoteDebuggerPresent函数就可以明白,其实最终它是调用
NtQueryInformationProcess,查询进程的ProcessDebugPort。此值是用来维持系统
与调试器通讯的,在程序被调试器加载时系统会设置这个值为非0。
代码:
FD_NtQueryInfoProc_DbgPort:
FD_NtQueryInfoProc_DbgPort_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgPort_StackSize    equ sizeof PROCESS_DEBUG_PORT_INFO
FD_NtQueryInfoProc_DbgPort_ProcessInfo  equ -(FD_NtQueryInfoProc_DbgPort_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgPort_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgPort_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_PORT_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo]
    push eax
    push ProcessDebugPort
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgPort_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgPort_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_PORT_INFO
    mov eax, dword ptr [eax].DebugPort
    test eax, eax
    jnz Found_FD_NtQueryInfoProc_DbgPort
FD_NtQueryInfoProc_DbgPort_Tmp1:
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgPort:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgPort:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgPort         
End_FD_NtQueryInfoProc_DbgPort:
当Windows系统创建一个调试会话开始,一个调试对象也被创建并产生一个
调试句柄。我们可以查询这个句柄的值来确定是否存在调试器。
代码:
FD_NtQueryInfoProc_DbgObjHandle:
FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgObjHandle_StackSize        equ sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO
FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo     equ -(FD_NtQueryInfoProc_DbgObjHandle_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgObjHandle_StackSize
    
    push ebx
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgObjHandle_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_OBJECTHANDLE_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo]
    push eax    
    push SystemNotImplemented8
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgObjHandle_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgObjHandle_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_OBJECTHANDLE_INFO
    mov eax, dword ptr [eax].ObjectHandle
    test eax, eax
    jnz Found_FD_NtQueryInfoProc_DbgObjHandle
FD_NtQueryInfoProc_DbgObjHandle_Tmp1:
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgObjHandle:

    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgObjHandle:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgObjHandle
End_FD_NtQueryInfoProc_DbgObjHandle:
也可以通过使用ZwQueryInformationProcess函数,利用SystemNotImplemented9(1fh)
返回的PROCESS_DEBUG_FLAGS_INFO结构,如果DebugFlags返回0则检测到调试器。返回非0
则没发现调试器。
代码:
FD_NtQueryInfoProc_DbgFlags:
FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_DbgFlags_StackSize    equ sizeof PROCESS_DEBUG_FLAGS_INFO
FD_NtQueryInfoProc_DbgFlags_ProcessInfo  equ -(FD_NtQueryInfoProc_DbgFlags_StackSize)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_DbgFlags_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_DbgFlags_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_FLAGS_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo]
    push eax    
    push SystemNotImplemented9
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQueryInformationProcess
    test eax, eax
    jnz FD_NtQueryInfoProc_DbgFlags_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_DbgFlags_ProcessInfo]
    assume eax : ptr PROCESS_DEBUG_FLAGS_INFO
    mov eax, dword ptr [eax].DebugFlags
    test eax, eax
    jz Found_FD_NtQueryInfoProc_DbgFlags
FD_NtQueryInfoProc_DbgFlags_Tmp1: 
    xor eax, eax
Exit_FD_NtQueryInfoProc_DbgFlags:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_DbgFlags:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_DbgFlags
End_FD_NtQueryInfoProc_DbgFlags:
如果Windows以调试方式启动,并与系统调试器建立通讯。
通过ZwQuerySystemInformation对SystemKernelDebuggerInformation进行查询
系统中是否存在系统调试器。
代码:
FD_NtQueryInfoProc_SysKrlDbgInfo:
FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api    equ 08h
FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize      equ sizeof PROCESS_DEBUG_FLAGS_INFO
FD_NtQueryInfoProc_SysKrlDbgInfo_Info           equ -(sizeof PROCESS_DEBUG_FLAGS_INFO)
    push ebp
    mov ebp, esp
    sub esp, FD_NtQueryInfoProc_SysKrlDbgInfo_StackSize
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    push sizeof PROCESS_DEBUG_FLAGS_INFO
    lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info]
    push eax    
    push SystemKernelDebuggerInformation
    call dword ptr [ebx].xGetCurrentProcess
    push eax
    call dword ptr [ebx].xZwQuerySystemInformation
    test eax, eax
    jnz FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1
    lea eax, [ebp+FD_NtQueryInfoProc_SysKrlDbgInfo_Info]
    assume eax : ptr PROCESS_DEBUG_FLAGS_INFO
    mov eax, dword ptr [eax].DebugFlags
    test eax, eax
    jz Found_FD_NtQueryInfoProc_SysKrlDbgInfo
FD_NtQueryInfoProc_SysKrlDbgInfo_Tmp1:    
    xor eax, eax
Exit_FD_NtQueryInfoProc_SysKrlDbgInfo:
    assume eax : nothing
    assume ebx : nothing
    
    pop ebx
    
    mov esp, ebp
    pop ebp
    retn 04h
Found_FD_NtQueryInfoProc_SysKrlDbgInfo:
    mov eax, 1
    jmp Exit_FD_NtQueryInfoProc_SysKrlDbgInfo
End_FD_NtQueryInfoProc_SysKrlDbgInfo:
当调试会话被创建,这个标志会影响堆的创建。初始化中的堆内存填充了
很多类似0ABABABABh,0BAADF00Dh,0FEEEFEEEh这三个值。可以通过检测内存看
是否存在过多的这样值。判断调试器的存在,而正常启动的程序则不会被填充。
代码:
FD_Heap_Magic:
FD_Heap_Magic_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+FD_Heap_Magic_Arg_Win32Api]
    
    push ebx
    push ecx
    push edx
    push esi
    push edi
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    
    push 100h
    push NULL
    call dword ptr [ebx].xGetProcessHeap
    mov edi, eax    ; HeapHandle
    push eax
    call dword ptr [ebx].xHeapAlloc
    mov esi, eax    ; HeapMem
    xor ecx, ecx
    mov edx, 100h
    cld
    FD_Heap_Magic_Loop:
    lodsd
    cmp eax, 0ABABABABh
    jnz FD_Heap_Magic_Tmp1
    inc ecx
    FD_Heap_Magic_Tmp1:
    cmp eax, 0BAADF00Dh
    jnz FD_Heap_Magic_Tmp2
    inc ecx        
    FD_Heap_Magic_Tmp2:
    cmp eax, 0FEEEFEEEh
    jnz FD_Heap_Magic_Tmp3
    inc ecx
    FD_Heap_Magic_Tmp3:
    sub edx, 04h
    jnz FD_Heap_Magic_Loop
    push ecx    
    ;; free heap
    push esi
    push HEAP_NO_SERIALIZE
    push edi
    call dword ptr [ebx].xHeapFree
    pop ecx
    ;; judge count
    cmp ecx, 10h
    jae Found_FD_Heap_Magic
    xor eax, eax
Exit_FD_Heap_Magic:    
    pop edi    
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    retn 04h
Found_FD_Heap_Magic:
    mov eax, 1
    jmp Exit_FD_Heap_Magic
End_FD_Heap_Magic:
一般程序是没有被设置SeDebugPrivilege,如果一个当前进程被设置SeDebugPrivilege后
它就拥有了完全控制CSRSS.EXE的权限。通过进程表快照取得CSRSS.EXE进程的PID,之后
通过OpenProcess以PROCESS_ALL_ACCESS打开。开是否能打开此进程。
代码:
FD_SeDebugPrivilege:
FD_SeDebugPrivilege_Arg_Win32Api    equ 08h
FD_SeDebugPrivilege_StackSize       equ 10h + sizeof PROCESSENTRY32
FD_SeDebugPrivilege_hProcessSnap    equ -04h
FD_SeDebugPrivilege_PID_csrss       equ -08h
FD_SeDebugPrivilege_FingFlag        equ -0ch
FD_SeDebugPrivilege_pe32            equ -(10h+sizeof PROCESSENTRY32)
    push ebp
    mov ebp, esp
    sub esp, FD_SeDebugPrivilege_StackSize
    
    push ebx
    push ecx
    push edi
    
    ;; clear stack
    lea edi, [ebp-FD_SeDebugPrivilege_StackSize]
    mov ecx, FD_SeDebugPrivilege_StackSize
    xor eax, eax
    cld
    rep stosb
    
    mov ebx, dword ptr [ebp+FD_SeDebugPrivilege_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    lea edi, [ebp+FD_SeDebugPrivilege_pe32]
    assume edi : ptr PROCESSENTRY32
                     
    push 0
    push TH32CS_SNAPPROCESS
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    cmp eax, INVALID_HANDLE_VALUE
    jz NotFound_FD_SeDebugPrivilege
    mov dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap], eax
    push sizeof PROCESSENTRY32
    pop dword ptr [edi].dwSize
    
    push edi
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xProcess32First
    test eax, eax
    jnz FD_SeDebugPrivilege_Loop
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_SeDebugPrivilege
    
    FD_SeDebugPrivilege_Loop:
    call FD_SeDebugPrivilege_Str
        db 'CSRSS.EXE',0
    FD_SeDebugPrivilege_Str:
    lea eax, [edi].szExeFile
    push eax    
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jnz FD_SeDebugPrivilege_Tmp2
    push dword ptr [edi].th32ProcessID
    pop dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss]
    push TRUE
    pop dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    FD_SeDebugPrivilege_Tmp2:
    mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    test eax, eax
    jnz FD_SeDebugPrivilege_Tmp3
    push edi
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jnz FD_SeDebugPrivilege_Loop
    
    FD_SeDebugPrivilege_Tmp3:
    mov eax, dword ptr [ebp+FD_SeDebugPrivilege_FingFlag]
    test eax, eax
    jz FD_SeDebugPrivilege_Tmp4
    push dword ptr [ebp+FD_SeDebugPrivilege_PID_csrss]
    push FALSE
    push PROCESS_QUERY_INFORMATION
    call dword ptr [ebx].xOpenProcess
    test eax, eax
    jz FD_SeDebugPrivilege_Tmp4
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_SeDebugPrivilege
    FD_SeDebugPrivilege_Tmp4:
    push dword ptr [ebp+FD_SeDebugPrivilege_hProcessSnap]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_SeDebugPrivilege
Exit_FD_SeDebugPrivilege:
    pop edi
    pop ecx
    pop ebx
    assume ebx : nothing
    assume edi : nothing
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_SeDebugPrivilege:
    xor eax, eax
    jmp Exit_FD_SeDebugPrivilege
Found_FD_SeDebugPrivilege:
    mov eax, 1
    jmp Exit_FD_SeDebugPrivilege
End_FD_SeDebugPrivilege:
通过逻辑的判断也可以找到调试器所在,一般来讲程序都是有explorer.exe进程启动的(通过双击)
如果我们的进程的父进程不是explorer.exe则发现调试器。如果有调试的名称冒名是explorer.exe
那么我们判断父进程ID后进一步判断explorer.exe进程的路径是否存在于Windows目录下。如果不是
则发现调试器。此类方法也可以也被病毒用作穿透仿真机。
代码:
FD_Parent_Process:
FD_Parent_Process_Arg_Win32Api      equ 08h
FD_Parent_Process_StackSize         equ MAX_PATH + sizeof PROCESSENTRY32 + sizeof MODULEENTRY32 + 20h
FD_Parent_Process_hParnet           equ -04h
FD_Parent_Process_PIDExplorer       equ -08h
FD_Parent_Process_PIDParent         equ -0ch
FD_Parent_Process_PIDChild          equ -10h
FD_Parent_Process_hSnapshot         equ -14h
FD_Parent_Process_pe32              equ -(20h + PROCESSENTRY32)
FD_Parent_Process_me32              equ -(20h + PROCESSENTRY32 + MODULEENTRY32)
FD_Parent_Process_lpszSystemInfo    equ -(20h + PROCESSENTRY32 + MODULEENTRY32 + MAX_PATH)
    push ebp
    mov ebp, esp
    sub esp, FD_Parent_Process_StackSize
    
    push ebx
    push ecx
    push edi
    push esi
    
    ;; clear the stack
    lea edi, [ebp-FD_Parent_Process_StackSize]
    xor eax, eax
    mov ecx, FD_Parent_Process_StackSize
    cld
    rep stosb
    
    mov ebx, dword ptr [ebp+FD_Parent_Process_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    lea eax, [ebp+FD_Parent_Process_pe32]
    assume eax : ptr PROCESSENTRY32
    push sizeof PROCESSENTRY32
    pop dword ptr [eax].dwSize
    
    call dword ptr [ebx].xGetCurrentProcessId
    mov dword ptr [ebp+FD_Parent_Process_PIDChild], eax
    
    push 0
    push TH32CS_SNAPPROCESS
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax
    
    lea eax, [ebp+FD_Parent_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xProcess32First
    test eax, eax
    jz FD_Parent_Process_Tmp1
    FD_Parent_Process_Loop1:
    lea eax, [ebp+FD_Parent_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jz FD_Parent_Process_Tmp2
    call FD_Parent_Process_Str1
        db "EXPLORER.EXE",0
    FD_Parent_Process_Str1:
    lea eax, [ebp+FD_Parent_Process_pe32]
    lea eax, [eax].szExeFile    
    push eax
    call dword ptr [ebx].xlstrcmpiA
    jnz FD_Parent_Process_Tmp3
    mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    test eax, eax
    jnz FD_Parent_Process_Tmp3
    lea eax, [ebp+FD_Parent_Process_pe32]
    assume eax : ptr PROCESSENTRY32
    push dword ptr [eax].th32ProcessID
    pop dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    FD_Parent_Process_Tmp3:
    lea eax, [ebp+FD_Parent_Process_pe32]
    mov eax, dword ptr [eax].th32ProcessID
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDChild]
    jnz FD_Parent_Process_Tmp4
    lea eax, [ebp+FD_Parent_Process_pe32]
    push dword ptr [eax].th32ParentProcessID
    pop dword ptr [ebp+FD_Parent_Process_PIDParent]
    FD_Parent_Process_Tmp4:
    jmp FD_Parent_Process_Loop1
    FD_Parent_Process_Tmp1:
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_Parent_Process
    FD_Parent_Process_Tmp2:
    mov eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDParent]
    jz FD_Parent_Process_Tmp5
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_Parent_Process
    FD_Parent_Process_Tmp5:
    lea eax, [ebp+FD_Parent_Process_me32]
    assume eax : ptr MODULEENTRY32
    push sizeof MODULEENTRY32
    pop dword ptr [eax].dwSize  
    push dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    push TH32CS_SNAPMODULE
    call dword ptr [ebx].xCreateToolhelp32Snapshot
    mov dword ptr [ebp+FD_Parent_Process_hSnapshot], eax
    lea eax, [ebp+FD_Parent_Process_me32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xModule32First
    test eax, eax
    jz FD_Parent_Process_Tmp6
    FD_Parent_Process_Loop2:
    lea eax, [ebp+FD_Parent_Process_me32]
    mov eax, dword ptr [eax].th32ProcessID
    sub eax, dword ptr [ebp+FD_Parent_Process_PIDExplorer]
    jnz FD_Parent_Process_Tmp7
    push MAX_PATH
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xGetWindowsDirectoryA
    call FD_Parent_Process_Str2
        db '\',0
    FD_Parent_Process_Str2:
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xlstrcatA
    call FD_Parent_Process_Str3
        db "EXPLORER.EXE",0
    FD_Parent_Process_Str3:
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    call dword ptr [ebx].xlstrcatA
    lea eax, [ebp+FD_Parent_Process_lpszSystemInfo]
    push eax
    lea eax, [ebp+FD_Parent_Process_me32]
    lea eax, [eax].szExePath
    push eax
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz FD_Parent_Process_Tmp6
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp Found_FD_Parent_Process
    FD_Parent_Process_Tmp7:
    lea eax, [ebp+FD_Parent_Process_me32]
    push eax
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xModule32Next
    test eax, eax
    jnz FD_Parent_Process_Loop2
    FD_Parent_Process_Tmp6:
    push dword ptr [ebp+FD_Parent_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    jmp NotFound_FD_Parent_Process
Exit_FD_Parent_Process:
    pop esi
    pop edi
    pop ecx
    pop ebx
    assume eax : nothing
    assume ebx : nothing           
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_Parent_Process:
    xor eax, eax
    jmp Exit_FD_Parent_Process
Found_FD_Parent_Process:
    mov eax, 1
    jmp Exit_FD_Parent_Process    
End_FD_Parent_Process:
当调试会话创建,将产生一个调试对象,我们通过ntdll中的
NtQueryObject函数参看调试对象的个数是否不为零,来确定调试器的存在
以ObjectAllTypeInformation使用NtQueryObject查询后会返回一个
OBJECT_ALL_INFORMATION的结构,其中NumberOfObjectsTypes成员为所有的
对象类型在ObjectTypeInformation数组中的计数
此对象如下
typedef struct _OBJECT_ALL_INFORMATION
ULONG    NumberOfObjectsTypes;
OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];
}
typedef strcut _OBJECT_TYPE_INFORMATION {
[00]UNICODE_STRING TypeName;
[08]ULONG TotalNumberofHandles;
[0c]ULONG TotalNumberofObjects;
...
}
循环遍历ObjectTypeInformation对比类型的名字,如有类型名为DebugObject则
检测TotalNumberofHandles与TotalNumberofObjects如果不为0则存在调试器。
代码:
FD_DebugObject_NtQueryObject:
FD_DebugObject_NtQueryObject_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+FD_DebugObject_NtQueryObject_Arg_Win32Api]
        
    push ebx
    push ecx
    push edx
    push edi
    push esi
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    push edx    ; alloc the stack
    
    push esp    ; ReturnLength
    push 0
    push 0
    push ObjectAllTypeInformation
    push 0
    call dword ptr [ebx].xNtQueryObject
    pop ecx
    ;; make a tmp stack
    push ebp
    mov ebp, esp
    sub esp, ecx
    mov esi, esp
    ;; ObjectInformationLength
    push 0
    push ecx
    push esi
    push ObjectAllTypeInformation
    push 0
    call dword ptr [ebx].xNtQueryObject
    cld
    ;; NumberOfObjectsTypes
    lodsd
    xchg ecx, eax ; ecx = NumberOfObjectsTypes
    FD_DebugObject_NtQueryObject_Loop:    
    ;; load string lengths
    lodsd
    movzx edx, ax
    ;; pointer to TypeName
    lodsd
    xchg esi, eax
    ;; sizeof(L"DebugObject")
    ;; avoids superstrings
    ;; like "DebugObjective"
    cmp edx, 16h
    jnz FD_DebugObject_NtQueryObject_Tmp2
    xchg ecx, edx
    FD_DebugObject_NtQueryObject_Tmp1:
    call FD_DebugObject_NtQueryObject_UnicodeStr1
        dw 'D','e','b','u','g'
        dw 'O','b','j','e','c','t'
    FD_DebugObject_NtQueryObject_UnicodeStr1:
    pop edi
    repe cmpsb
    xchg ecx, edx
    jnz FD_DebugObject_NtQueryObject_Tmp2
    ;; TotalNumberOfObjects
    cmp dword ptr [eax], edx
    jnz Found_FD_DebugObject_NtQueryObject
    ;; point to trailing  null
    FD_DebugObject_NtQueryObject_Tmp2:
    add esi, edx
    ;; round down to dword
    and esi, -4
    ;; skip trailing null
    ;; and any alignment bytes
    lodsd
    loop FD_DebugObject_NtQueryObject_Loop
    xor eax, eax
Exit_FD_DebugObject_NtQueryObject:
    ;; clear the tmp stack
    mov esp, ebp
    pop ebp
    
    pop esi
    pop edi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing    
    retn 04h
Found_FD_DebugObject_NtQueryObject:    
    mov eax, 1
    jmp Exit_FD_DebugObject_NtQueryObject
End_FD_DebugObject_NtQueryObject:
此类方法,利用FindWindow函数通过寻找是否存在一些常见调试软件的Title。这里收集了一些
如果有其他的Title,请朋友们告知。。。
代码:
FD_Find_Debugger_Window:
FD_Find_Debugger_Window_Arg_WinApi32   equ 08h
    push ebp
    mov ebp, esp
    
    push ebx
    
    mov ebx, dword ptr [ebp+FD_Find_Debugger_Window_Arg_WinApi32]
    assume ebx : ptr WIN32APIBASE
    
    push NULL
    call FD_Find_Debugger_Window_Str1
        db "1212121",0
    FD_Find_Debugger_Window_Str1:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window
    
    push NULL
    call FD_Find_Debugger_Window_Str2
        db "icu_dbg",0
    FD_Find_Debugger_Window_Str2:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window    
    
    push NULL
    call FD_Find_Debugger_Window_Str3
        db "pe--diy",0
    FD_Find_Debugger_Window_Str3:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window     
    
    push NULL
    call FD_Find_Debugger_Window_Str5
        db "ollydbg",0
    FD_Find_Debugger_Window_Str5:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window 

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

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

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

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

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

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

    push NULL
    call FD_Find_Debugger_Window_Str11
        db "Import REConstructor v1.6 FINAL (C) 2001-2003 MackT/uCF",0
    FD_Find_Debugger_Window_Str11:
    call dword ptr [ebx].xFindWindowA
    test eax, eax
    jnz Found_FD_Find_Debugger_Window
    jmp NotFound_Found_FD_Find_Debugger_Window
Exit_FD_Find_Debugger_Window:
    pop ebx
    assume ebx : nothing
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_Found_FD_Find_Debugger_Window:
    xor eax, eax
    jmp Exit_FD_Find_Debugger_Window
Found_FD_Find_Debugger_Window:
    mov eax, 1
    jmp Exit_FD_Find_Debugger_Window    
End_FD_Find_Debugger_Window:
这种方面的核心思想是比对在进程表中是否是出现了调试器进程名,如果出现则退出。
不过这种方法很容易躲过,而且也可能造成误判。
代码:
FD_Find_Debugger_Process:
FD_Find_Debugger_Process_Arg_Win32Api   equ 08h
FD_Find_Debugger_Process_StackSize      equ 10h + sizeof PROCESSENTRY32
FD_Find_Debugger_Process_hSnapshot      equ -04h
FD_Find_Debugger_Process_hParnet        equ -08h
FD_Find_Debugger_Process_pe32           equ -(10+sizeof PROCESSENTRY32)

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

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

    call FD_Find_Debugger_Process_Str7
        db "IMPORTREC.EXE",0
    FD_Find_Debugger_Process_Str7:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process
    
    call FD_Find_Debugger_Process_Str8
        db "W32DSM89.EXE",0
    FD_Find_Debugger_Process_Str8:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process    
    
    call FD_Find_Debugger_Process_Str9
        db "WINDBG.EXE",0
    FD_Find_Debugger_Process_Str9:
    push edi
    call dword ptr [ebx].xlstrcmpiA
    test eax, eax
    jz Found_FD_Find_Debugger_Process
    lea eax, [ebp+FD_Find_Debugger_Process_pe32]
    push eax
    push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot]
    call dword ptr [ebx].xProcess32Next
    test eax, eax
    jnz FD_Find_Debugger_Process_Loop
    jmp NotFound_FD_Find_Debugger_Process
Exit_FD_Find_Debugger_Process:
    ;; close the Shotsnap handle
    push dword ptr [ebp+FD_Find_Debugger_Process_hSnapshot]
    call dword ptr [ebx].xCloseHandle
    
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    assume esi : nothing
    mov esp, ebp
    pop ebp 
    retn 04h
NotFound_FD_Find_Debugger_Process:
    xor eax, eax
    jmp Exit_FD_Find_Debugger_Process
Found_FD_Find_Debugger_Process:
    mov eax, 1
    jmp Exit_FD_Find_Debugger_Process        
End_FD_Find_Debugger_Process:
这种方法通过打开一些调试软件加载到系统中驱动的句柄,从而判断是否有
调试软件的存在。
代码:
FD_Find_Device_Driver:
FD_Find_Device_Driver_Arg_Win32Api      equ 08h

    push ebp
    mov ebp, esp
    
    push ebx
    push ecx
    push edx
    push esi
    push edi
    
    mov ebx, dword ptr [ebp+FD_Find_Device_Driver_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    ;; check softice on unknow system
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str1
        db "\\.\SIWVID",0
    FD_Find_Device_Driver_Str1:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check softice 4.05 on win2k
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str2
        db "\\.\NTICE",0
    FD_Find_Device_Driver_Str2:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check softice on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str3
        db "\\.\SICE",0
    FD_Find_Device_Driver_Str3:
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver    
    
    ;; check softice on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str4
        db "\\.\SIWDEBUG",0
    FD_Find_Device_Driver_Str4:
    call dword ptr [ebx].xCreateFileA
    push eax
    call dword ptr [ebx].xGetLastError
    test al, 032h
    pop eax
    jz Found_FD_Find_Device_Driver
    
    ;; check regmon on win9x
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str5
        db "\\.\REGVXD",0
    FD_Find_Device_Driver_Str5:
    call dword ptr [ebx].xCreateFileA    
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check RegMON
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str6
        db "\\.\FILEM",0
    FD_Find_Device_Driver_Str6:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver
    
    ;; check TRW
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str7
        db "\\.\TRW",0
    FD_Find_Device_Driver_Str7:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver       
    
    ;; check softice extender
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ + FILE_SHARE_WRITE
    push GENERIC_READ + GENERIC_WRITE
    call FD_Find_Device_Driver_Str8
        db "\\.\ICEEXT",0
    FD_Find_Device_Driver_Str8:
    call dword ptr [ebx].xCreateFileA      
    cmp eax, INVALID_HANDLE_VALUE
    jnz Found_FD_Find_Device_Driver 
    jmp NotFound_FD_Find_Device_Driver
    
Exit_FD_Find_Device_Driver:    
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing    
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FD_Find_Device_Driver:
    xor eax, eax
    jmp Exit_FD_Find_Device_Driver
Found_FD_Find_Device_Driver:
    push eax
    assume ebx : ptr WIN32APIBASE
    call dword ptr [ebx].xCloseHandle
    assume ebx : nothing
    mov eax, 1
    jmp Exit_FD_Find_Device_Driver
End_FD_Find_Device_Driver:
利用int3中断引起异常。如果,正常运行则触发异常。
代码:
FD_Exception_Int3:
    call Get_FD_Exception_Int3_Eip
    Get_FD_Exception_Int3_Eip:
    pop eax
    add eax, offset FD_Exception_Int3_Exception - offset Get_FD_Exception_Int3_Eip
    ;; setup exception
    assume fs : nothing
    push eax
    push dword ptr fs : [0]
    mov dword ptr fs : [0], esp
    ;; reset eax
    xor eax, eax
    int 03h
    ;; unsetup exception
    pop dword ptr fs : [0]
    add esp, 04h
    
    ;; check the flag
    test eax, eax
    jz Found_FD_Exception_Int3
    jmp NotFound_FD_Exception_Int3
FD_Exception_Int3_Exception:
    mov eax, dword ptr [esp+0ch]
    ;; eax = ContextRecord
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    inc dword ptr [eax].regEip
    xor eax, eax
    assume eax : nothing
    retn
Exit_FD_Exception_Int3:
    retn 0h
NotFound_FD_Exception_Int3:
    xor eax, eax
    jmp Exit_FD_Exception_Int3
Found_FD_Exception_Int3:
    mov eax, 1
    jmp Exit_FD_Exception_Int3        
End_FD_Exception_Int3:
通过对EFLAGS中的TF的检测来确定是否存在调试器。当TF=1的时会触发一个
单步执行异常,可以通过安装异常处理针来捕获。
代码:
FD_Exception_Popf:
    push ebx

    call Get_FD_Exception_Popf_Eip
    Get_FD_Exception_Popf_Eip:
    pop eax
    add eax, offset FD_Exception_Popf_Exception - offset Get_FD_Exception_Popf_Eip
    assume fs : nothing    
    push eax
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    ;; reset eax
    xor eax, eax
    pushf
    mov dword ptr [esp], 0100h
    popf
    nop
FD_Exception_Popf_NextEip:
    pop dword ptr fs:[0]
    add esp, 04h
    
    ;; check the flag
    test eax, eax
    jz Found_FD_Exception_Popf
    jmp NotFound_FD_Exception_Popf
    
FD_Exception_Popf_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    call Get_FD_Exception_Popf_Exception_Eip2
    Get_FD_Exception_Popf_Exception_Eip2:
    pop ebx
    sub ebx, offset Get_FD_Exception_Popf_Exception_Eip2 - offset FD_Exception_Popf_NextEip
    mov dword ptr [eax].regEip, ebx
    xor eax, eax
    retn
    assume eax : nothing
Exit_FD_Exception_Popf:
    pop ebx
    retn 0h
NotFound_FD_Exception_Popf:
    xor eax, eax
    jmp Exit_FD_Exception_Popf
Found_FD_Exception_Popf:
    mov eax, 1
    jmp Exit_FD_Exception_Popf
End_FD_Exception_Popf:
触发OutputDebugString的错误。如果调试器存在则GetLastError为0。
代码:
FD_OutputDebugString:
FD_OutputDebugString_Arg_Win32Api       equ 04h
    
    mov eax, dword ptr [esp+FD_OutputDebugString_Arg_Win32Api]
    push ebx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    call FD_OutputDebugString_Str
        db 0
    FD_OutputDebugString_Str:
    call dword ptr [ebx].xOutputDebugStringA
    call dword ptr [ebx].xGetLastError 
    test eax, eax   
    jnz NotFound_FD_OutputDebugString
    inc eax
    pop ebx
    assume ebx : nothing
    retn 04h
NotFound_FD_OutputDebugString:
    pop ebx
    xor eax, eax
    retn 04h
    
End_FD_OutputDebugString:
Ollydbg允许设置一个内存访问/写入断点。它通过页面保护来实现。
页面保护提供了当应用程序的某块内存被访问时获得通知。
页面保护通过PAGE_GUARD页面保护修改符来设置。如果访问的内存
是受保护的将会产生一个STATUS_GUARD_PAGE_VIOLATION(0x80000001)异常
如果进程被Ollydbg调试并且受保护的页面被访问,将不会抛出异常。访问
将会被当作内存断点来处理。
代码:
FS_OD_Exception_GuardPages:
FS_OD_Exception_GuardPages_Arg_Win32Api     equ 04h

    mov eax, dword ptr [esp+FS_OD_Exception_GuardPages_Arg_Win32Api]
    
    push edi    ; pAllocatedMem
    push ebx
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
     
    push PAGE_READWRITE
    push MEM_COMMIT
    push 01000h
    push NULL
    call dword ptr [ebx].xVirtualAlloc
    test eax, eax
    jz NotFound_FS_OD_Exception_GuardPages
    mov edi, eax
    ;; store a RENT on the allocated memory
    mov byte ptr [edi], 0c3h
    push edi        ; alloc stack
    
    push esp
    push PAGE_EXECUTE_READ + PAGE_GUARD
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualProtect
    ;; set eax to 0
    xor eax, eax
    ;; trigger a STATUS_GUARD_PAGE_VIOLATION exception
    ;; setup expcetion
    call Get_FS_OD_Exception_GuardPages_Eip
    Get_FS_OD_Exception_GuardPages_Eip:
    pop eax
    add eax, offset FS_OD_Exception_GuardPages_Exception - offset Get_FS_OD_Exception_GuardPages_Eip
    assume fs : nothing
    push eax
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    call edi
FS_OD_Exception_GuardPages_Continue:
    pop edi
    ;; unsetup exception
    pop dword ptr fs:[0]
    add esp, 04h
    test eax, eax
    jz Found_FS_OD_Exception_GuardPages
    jmp NotFound_FS_OD_Exception_GuardPages
FS_OD_Exception_GuardPages_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    mov dword ptr [eax].regEax, 0FFFFFFFFh
    call Get_FS_OD_Exception_GuardPages_Exception_Eip
    Get_FS_OD_Exception_GuardPages_Exception_Eip:
    pop ebx
    sub ebx, offset Get_FS_OD_Exception_GuardPages_Exception_Eip - offset FS_OD_Exception_GuardPages_Continue  
    mov dword ptr [eax].regEip, ebx
    assume eax : nothing
    xor eax, eax
    retn
    
Exit_FS_OD_Exception_GuardPages:
    pop ebx
    pop edi
    assume ebx : nothing
    retn 04h
    
NotFound_FS_OD_Exception_GuardPages:
    assume ebx : ptr WIN32APIBASE
    push MEM_DECOMMIT
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    xor eax, eax
    jmp Exit_FS_OD_Exception_GuardPages
Found_FS_OD_Exception_GuardPages:
    assume ebx : ptr WIN32APIBASE
    push MEM_DECOMMIT
    push 01000h
    push edi
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    mov eax, 1
    jmp Exit_FS_OD_Exception_GuardPages
End_FS_OD_Exception_GuardPages:
Softice调试器会对UnhandleExceptionFilter进行修改,将第一个字节修改为CCh(int 3)
代码:
FS_SI_UnhandledExceptionFilter:
FS_SI_UnhandledExceptionFilter_Arg_Win32Api     equ 04h
    mov eax, dword ptr [esp+FS_SI_UnhandledExceptionFilter_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xUnhandledExceptionFilter
    mov al, byte ptr [eax]
    sub al, 0cch
    jz Found_FS_SI_UnhandledExceptionFilter
    xor eax, eax
Exit_FS_SI_UnhandledExceptionFilter:   
    assume eax : nothing
    retn 04h
Found_FS_SI_UnhandledExceptionFilter:
    mov eax, 1
    jmp Exit_FS_SI_UnhandledExceptionFilter    
End_FS_SI_UnhandledExceptionFilter:
Ollydbg的插件对几个函数的首部进行修改。通过对齐的检测来判断系统中是否存在调试器
代码:
FS_ODP_Process32NextW:
FS_ODP_Process32NextW_Arg_Win32Api          equ 04h
    mov eax, dword ptr [esp+FS_ODP_Process32NextW_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    mov eax, dword ptr [eax].xProcess32NextW
    mov ax, word ptr [eax]
    sub ax, 0FF88h
    jnz Found_FS_ODP_Process32NextW
    xor eax, eax
Exit_FS_ODP_Process32NextW:
    assume eax : nothing 
    retn 04h
Found_FS_ODP_Process32NextW:
    mov eax, 1
    jmp Exit_FS_ODP_Process32NextW
End_FS_ODP_Process32NextW:


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


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

FB_HWBP_Exception:
    call Get_FB_HWBP_Exception_Eip
    Get_FB_HWBP_Exception_Eip:
    pop eax
    add eax, offset FB_HWBP_Exception_Exception - offset FB_HWBP_Exception
    assume fs : nothing
    push eax
    push fs:[0]
    mov dword ptr fs:[0], esp
    ;; reset eax
    xor eax, eax
    int 01h
    pop dword ptr fs:[0]
    add esp, 04h
    test eax, eax
    jnz Found_FB_HWBP_Exception
    jmp NotFound_FB_HWBP_Exception
FB_HWBP_Exception_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    ;; check if debug Registers Context.Dr0-Dr3 is not zero
    cmp dword ptr [eax].iDr0, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    cmp dword ptr [eax].iDr1, 0
    jnz FB_HWBP_Exception_HardwareBp_Found  
    cmp dword ptr [eax].iDr2, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    cmp dword ptr [eax].iDr3, 0
    jnz FB_HWBP_Exception_HardwareBp_Found
    jmp FB_HWBP_Exception_Exception_Ret     
FB_HWBP_Exception_HardwareBp_Found:
    ;; set Context.Eax to signal breakpoint found
    mov dword ptr [eax].regEax, 0FFFFFFFFh
FB_HWBP_Exception_Exception_Ret:
    ;; set Context.Eip upon return
    add dword ptr [eax].regEip, 02h
    xor eax, eax
    retn
Exit_FB_HWBP_Exception:
    retn 0h
NotFound_FB_HWBP_Exception:
    xor eax, eax
    jmp Exit_FB_HWBP_Exception
Found_FB_HWBP_Exception:
    mov eax, 1
    jmp Exit_FB_HWBP_Exception                
End_FB_HWBP_Exception:
通过对代码节做CRC值的验证来判断是否存在内存补丁
代码:
FB_SWBP_Memory_CRC:
FB_SWBP_Memory_CRC_Arg_Win32Api         equ 08h
FB_SWBP_Memory_CRC_Arg_FuncAddr         equ 0ch
FB_SWBP_Memory_CRC_StackSize            equ 10h+MAX_PATH
FB_SWBP_Memory_CRC_fileSize             equ -04h
FB_SWBP_Memory_CRC_NumberOfBytesRW      equ -08h
FB_SWBP_Memory_CRC_pBuffer              equ -0ch
FB_SWBP_Memory_CRC_szFileName           equ -(10h+MAX_PATH)
    push ebp
    mov ebp, esp
    sub esp, FB_SWBP_Memory_CRC_StackSize
    
    push esi
    push edi
    push ecx
    push edx
    push ebx
    
    
    ;; clear the stack
    lea edi, [ebp+FB_SWBP_Memory_CRC_szFileName]
    mov ecx, FB_SWBP_Memory_CRC_StackSize
    xor eax, eax
    cld
    rep stosb
    
    
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    push MAX_PATH
    lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
    push eax
    push NULL
    call dword ptr [ebx].xGetModuleFileNameA
    ;; open file
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ
    push GENERIC_READ
    lea eax, [ebp+FB_SWBP_Memory_CRC_szFileName]
    push eax
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jz Error_FB_SWBP_Memory_CRC
    mov edi, eax
    push NULL
    push eax
    call dword ptr [ebx].xGetFileSize
;    cmp eax, INVALID_FILE_SIZE
;    jz Error_FB_SWBP_Memory_CRC    
    mov dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize], eax
    push PAGE_READWRITE
    push MEM_COMMIT
    push eax
    push NULL
    call dword ptr [ebx].xVirtualAlloc
    mov dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer], eax
    push NULL
    lea eax, [ebp+FB_SWBP_Memory_CRC_NumberOfBytesRW]
    push eax
    push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize]
    push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    push edi
    call dword ptr [ebx].xReadFile
    push edi
    call dword ptr [ebx].xCloseHandle
        
    mov esi, dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    add esi, dword ptr [esi+3ch]
    assume esi : ptr IMAGE_NT_HEADERS
    mov edx, dword ptr [esi].OptionalHeader.ImageBase
    mov eax, dword ptr [esi].OptionalHeader.AddressOfEntryPoint
    add eax, edx
    mov cx, word ptr [esi].FileHeader.NumberOfSections
    movzx ecx, cx
    mov edi, esi
    add edi, sizeof IMAGE_NT_HEADERS
    assume edi : ptr IMAGE_SECTION_HEADER
    ;; find the entry section
    ;; eax = AddressOfEntryPoint
    ;; ebx = Each Section Virtual Address
    ;; ecx = NumberOfSections    
    ;; edx = ImageBase
    ;; edi = Section Table Point
    FB_SWBP_Memory_CRC_Loop:
    mov ebx, dword ptr [edi].VirtualAddress
    add ebx, edx
    cmp ebx, eax
    jz FB_SWBP_Memory_CRC_Loop_End
    add edi, sizeof IMAGE_SECTION_HEADER
    dec ecx
    jnz FB_SWBP_Memory_CRC_Loop
    jmp Error_FB_SWBP_Memory_CRC
    FB_SWBP_Memory_CRC_Loop_End:
    mov edi, dword ptr [edi].Misc.VirtualSize
    mov esi, eax
    ;; alloc the memory
    push MEM_DECOMMIT
    push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize]
    push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    call dword ptr [ebx].xVirtualFree
    mov edx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_FuncAddr]
    assume edx : ptr FUNCADDRTBL
    push edi
    push esi
    call dword ptr [edx].xCRC32
    call Get_FB_SWBP_Memory_CRC_Eip
    Get_FB_SWBP_Memory_CRC_Eip:
    pop ecx
    add ecx, offset FB_SWBP_Memory_CRC_Orig_CRC - offset Get_FB_SWBP_Memory_CRC_Eip
    cmp eax, ecx
    jz NotFound_FB_SWBP_Memory_CRC
    jmp Found_FB_SWBP_Memory_CRC
Exit_FB_SWBP_Memory_CRC:
    pop ebx
    pop edx
    pop ecx
    pop edi
    pop esi
    assume ebx : nothing
    assume edx : nothing
    assume edi : nothing
    assume esi : nothing
    mov esp, ebp
    pop ebp
    retn 08h
Error_FB_SWBP_Memory_CRC:
    mov eax, dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    test eax, eax
    jz Error_FB_SWBP_Memory_CRC_Continue
    push MEM_DECOMMIT
    push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize]
    push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE    
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    Error_FB_SWBP_Memory_CRC_Continue:
    xor eax, eax
    jmp Exit_FB_SWBP_Memory_CRC
Found_FB_SWBP_Memory_CRC:
    push MEM_DECOMMIT
    push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize]
    push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE    
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    mov eax, 1
    jmp Exit_FB_SWBP_Memory_CRC
NotFound_FB_SWBP_Memory_CRC:
    push MEM_DECOMMIT
    push dword ptr [ebp+FB_SWBP_Memory_CRC_fileSize]
    push dword ptr [ebp+FB_SWBP_Memory_CRC_pBuffer]
    mov ebx, dword ptr [ebp+FB_SWBP_Memory_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE    
    call dword ptr [ebx].xVirtualFree
    assume ebx : nothing
    xor eax, eax
    jmp Exit_FB_SWBP_Memory_CRC        
FB_SWBP_Memory_CRC_Orig_CRC:
    dd 0   
End_FB_SWBP_Memory_CRC:
通过检测标志位来检测是否存在单步运行标志
代码:
FT_PushSS_PopSS:
    ;; release version
    push ebp
    mov ebp, esp
    push ss
    pop ss
    pushf
    pop eax
    and eax, 00000100h
    jnz Found_FT_PushSS_PopSS
Exit_FT_PushSS_PopSS:
    xor eax, eax
    mov esp, ebp
    pop ebp
    retn 0h    
Found_FT_PushSS_PopSS:
    mov eax, 1
    jmp Exit_FT_PushSS_PopSS   
End_FT_PushSS_PopSS:
以下三个函数都是通过调用取单步运行时间差的原理来检测是否进行单步运行的
分别使用了rdtsc,GetTickCount,timeGetTime三个函数来确定
代码:
FT_RDTSC:
    push ecx
      rdtsc
    xchg ecx, eax
    rdtsc
    sub eax, ecx
    cmp eax, 500h
    jnbe Found_FT_RDTSC
    xor eax, eax
Exit_FT_RDTSC:
    pop ecx
    retn 0h
Found_FT_RDTSC:
    mov eax, 1
    jmp Exit_FT_RDTSC
End_FT_RDTSC:

FT_GetTickCount:
FT_GetTickCount_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+FT_GetTickCount_Arg_Win32Api]
    push ebx
    push ecx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    call dword ptr [ebx].xGetTickCount
    xchg ecx, eax
    call dword ptr [ebx].xGetTickCount
    sub eax, ebx
    cmp eax, 1
    jnb Found_FT_GetTickCount
    xor eax, eax
Exit_FT_GetTickCount:
    pop ecx
    pop ebx
    assume ebx : nothing
    retn 04h    
Found_FT_GetTickCount:
    mov eax, 1
    jmp Exit_FT_GetTickCount    
End_FT_GetTickCount:

FT_timeGetTime:
FT_timeGetTime_Arg_Win32Api            equ 04h
    mov eax, dword ptr [esp+FT_timeGetTime_Arg_Win32Api]
    push ebx
    push ecx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    call dword ptr [ebx].xtimeGetTime
    xchg ecx, eax
    call dword ptr [ebx].xtimeGetTime
    sub eax, ebx
    cmp eax, 10h
    jnb Found_FT_timeGetTime
    xor eax, eax
Exit_FT_timeGetTime:
    pop ecx
    pop ebx
    assume ebx : nothing
    retn 04h    
Found_FT_timeGetTime:
    mov eax, 1
    jmp Exit_FT_timeGetTime
End_FT_timeGetTime:
手动检测位于7FFE0000h地址的SharedUserData数据结构的TickCoutLow与TickCountMultipiler字段
来获取单步运行时间差。
代码:
FT_SharedUserData_TickCount:
    push ecx
    push edx    
    mov edx, 07FFE0000h
    mov eax, dword ptr [edx]
    imul dword ptr [edx+04h]
    shrd eax, edx, 018h
    xchg eax, ecx
    mov edx, 07FFE0000h
    mov eax, dword ptr [edx]
    imul dword ptr [edx+04h]
    shrd eax, edx, 018h
    sub eax, ecx
       cmp eax, 500h
       jnbe Found_FT_SharedUserData_TickCount
       xor eax, eax
Exit_FT_SharedUserData_TickCount:
    pop edx
    pop ecx
    retn 0h
Found_FT_SharedUserData_TickCount:
    mov eax, 1
    jmp Exit_FT_SharedUserData_TickCount    
End_FT_SharedUserData_TickCount:
中断1不能从r3下调用。如果从r3下调用,将产生一个EXECEPTION_ACCESS_VIOLATION(0x0C0000005)的异常。
如果softice存在,它HOOK了中断1并且使它的DPL(descriptor privilege level)到环3下
使得softice能单步处理用户态的程序。如果程序使用中断1,softice不检查它是否在单步运行或者是一软中断。
softice总是调用原始的中断1处理函数。并且使用EXCEPTION_SINGLE_STEP(0x80000004)异常替换原本的EXECEPTION_ACCESS_VIOLATION
(0xC0000005)异常。
代码:
FT_INT1_IceBreakpoint:
    push ebp
    mov ebp, esp
    call Get_FT_INT1_IceBreakpoint_Eip
    Get_FT_INT1_IceBreakpoint_Eip:
    pop eax
    add eax, offset FT_INT1_IceBreakpoint_Exception - offset Get_FT_INT1_IceBreakpoint_Eip
    push eax
    assume fs : nothing
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    ;; reset eax
    xor eax, eax
    db 0CDh, 01h    ; int 1
    pop dword ptr fs:[0]
    add esp, 04h
    test eax, eax
    jz Found_FT_INT1_IceBreakpoint
    jmp NotFound_FT_INT1_IceBreakpoint
FT_INT1_IceBreakpoint_Exception:
    ;mov eax, dword ptr [esp+04h]
    ;cmp dword ptr [eax], 80000004h
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT 
    mov dword ptr [eax].regEax, 1
    inc dword ptr [eax].regEip
    xor eax, eax
    retn
Exit_FT_INT1_IceBreakpoint:
    mov esp, ebp
    pop ebp
    retn 0h
NotFound_FT_INT1_IceBreakpoint:
    xor eax, eax
    jmp Exit_FT_INT1_IceBreakpoint
Found_FT_INT1_IceBreakpoint:
    mov eax, 1
    jmp Exit_FT_INT1_IceBreakpoint
End_FT_INT1_IceBreakpoint:
以下为检测Virtual PC 与 VMWARE的两个例子,这类检测技术大多都是通过与真实环境中不同的
异常,指令实现的。严格的描述可以在其规定的文档中找到。
代码:
FV_VPC_Exception: 

    push ebx
    push ecx
   
    call Get_FV_VPC_Exception_Eip
    Get_FV_VPC_Exception_Eip:
    pop ecx
    add ecx, offset FV_VPC_Exception_Exception - offset Get_FV_VPC_Exception_Eip
    assume fs : nothing        
    push ecx
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    
    ;; Flag
    xor ebx, ebx
    ;; VPC function number
    mov eax, 1
    
    ;; call VPC
    db 0Fh,3Fh,07h,0Bh
    
    pop dword ptr fs:[0]
    add esp, 04h    
    test ebx, ebx
    setz al
    
    pop ecx
    pop ebx    
    retn 0h
    
FV_VPC_Exception_Exception:    
    mov ecx, dword ptr [esp+0ch]
    assume ecx : ptr CONTEXT
    ;; ebx = -1 : not running
    ;; ebx = 0 : running
    mov dword ptr [ecx].regEbx, -1
    ;; skip past the call to VPC
    add dword ptr [ecx].regEip, 04h
    xor eax, eax
    retn
End_FV_VPC_Exception:

FV_VMWare_VMX:
    
    call IsInsideVMWare
    retn 0h
IsInsideVMWare:

    push ebx
    push ecx
    push edx

    assume fs : nothing
    call Get_IsInsideVMWare_Eip
    Get_IsInsideVMWare_Eip:
    pop eax
    add eax, offset IsInsideVMWare_Exception - offset Get_IsInsideVMWare_Eip
    push eax
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    
    mov    eax, 'VMXh'
    mov    ebx, 0         ; any value but MAGIC VALUE
    mov    ecx, 10        ; get VMWare version
    mov    edx, 'VX'     ; port number
    in     eax, dx         ; read port
                           ; on return EAX returns the VERSION
    cmp    ebx, 'VMXh'     ; is it a reply from VMWare?
    setz   al             ; set return value
    movzx eax, al

    pop dword ptr fs:[0]
    add esp, 04h
    
    pop edx
    pop ecx
    pop ebx
    
    retn 0h
IsInsideVMWare_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    inc dword ptr [eax].regEip
    retn       
End_IsInsideVMWare:
    
End_FV_VMWare_VMX:
通过计算文件的长度与原先的对比,来确定是否进行过修改。来发现补丁。
代码:
FP_Check_FileSize:
FP_Check_FileSize_Arg_Win32Api      equ 08h
FP_Check_FileSize_StackSize         equ MAX_PATH
FP_Check_FileSize_szPath            equ -MAX_PATH
    push ebp
    mov ebp, esp
    sub esp, FP_Check_FileSize_StackSize
    
    push ebx
    push ecx
    push edx
    mov ebx, dword ptr [ebp+FP_Check_FileSize_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push MAX_PATH
    lea eax, [ebp+FP_Check_FileSize_szPath]
    push eax
    push NULL
    call dword ptr [ebx].xGetModuleFileNameA
    test eax, eax
    jz NotFound_FP_Check_FileSize
    
    ;; create file
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_ALWAYS
    push NULL
    push FILE_SHARE_READ
    push GENERIC_READ
    lea eax, [ebp+FP_Check_FileSize_szPath]
    push eax
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jz NotFound_FP_Check_FileSize
    mov ecx, eax
    
    push NULL
    push eax
    call dword ptr [ebx].xGetFileSize
    mov edx, eax
    
    push ecx
    call dword ptr [ebx].xCloseHandle
    
    call Get_FP_Check_FileSize_Eip
    Get_FP_Check_FileSize_Eip:
    pop eax
    add eax, offset FP_Check_FileSize_Orig_Size - offset Get_FP_Check_FileSize_Eip
    cmp edx, eax
    jnz Found_FP_Check_FileSize
    jmp NotFound_FP_Check_FileSize

Exit_FP_Check_FileSize:
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    
    mov esp, ebp
    pop ebp
    retn 04h
NotFound_FP_Check_FileSize:
    xor eax, eax
    jmp Exit_FP_Check_FileSize
Found_FP_Check_FileSize:
    mov eax, 1
    jmp Exit_FP_Check_FileSize
FP_Check_FileSize_Orig_Size:
    dd 0   
End_FP_Check_FileSize:
通过计算文件的CRC值来对比是否被Cracker进行过修改打过补丁。
代码:
FP_Check_FileHashValue_CRC:
FP_Check_FileHashValue_CRC_Arg_Win32Api         equ 08h
FP_Check_FileHashValue_CRC_Arg_FuncAddr            equ 0ch
FP_Check_FileHashValue_CRC_StackSize            equ 20h+MAX_PATH
FP_Check_FileHashValue_CRC_fileSize             equ -04h
FP_Check_FileHashValue_CRC_NumberOfBytesRW      equ -08h
FP_Check_FileHashValue_CRC_CRCVALUE_current     equ -0ch
FP_Check_FileHashValue_CRC_pBuffer              equ -10h
FP_Check_FileHashValue_CRC_hFile                equ -14h
FP_Check_FileHashValue_CRC_szFileName           equ -(20h+MAX_PATH)
    
    push ebp
    mov ebp, esp
    sub esp, FP_Check_FileHashValue_CRC_StackSize
    
    push ebx
    push ecx
    push edx
    push esi
    push edi
    
    ;; clear the stack
    lea edi, [ebp-FP_Check_FileHashValue_CRC_StackSize]
    mov ecx, FP_Check_FileHashValue_CRC_StackSize
    xor eax, eax
    cld
    rep stosb
    
    mov ebx, dword ptr [ebp+FP_Check_FileHashValue_CRC_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    push MAX_PATH
    lea eax, [ebp+FP_Check_FileHashValue_CRC_szFileName]
    push eax
    push NULL
    call dword ptr [ebx].xGetModuleFileNameA
    
    push NULL
    push FILE_ATTRIBUTE_NORMAL
    push OPEN_EXISTING
    push NULL
    push FILE_SHARE_READ
    push GENERIC_READ
    lea eax, [ebp+FP_Check_FileHashValue_CRC_szFileName]
    push eax
    call dword ptr [ebx].xCreateFileA
    cmp eax, INVALID_HANDLE_VALUE
    jz NotFound_FP_Check_FileHashValue_CRC
    mov dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile], eax
    
    push NULL
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile]
    call dword ptr [ebx].xGetFileSize
    ;cmp eax, INVALID_FILE_SIZE
    ;jz NotFound_FP_Check_FileHashValue_CRC
    mov dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize], eax
    
    push PAGE_READWRITE
    push MEM_COMMIT
    push eax
    push NULL
    call dword ptr [ebx].xVirtualAlloc
    mov dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer], eax
    
    ;; read file
    push NULL
    lea eax, [ebp+FP_Check_FileHashValue_CRC_NumberOfBytesRW]
    push eax
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize]
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer]
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile]
    call dword ptr [ebx].xReadFile
    
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_hFile]
    call dword ptr [ebx].xCloseHandle
    
    ;; get CRC value
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize]
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer]
    mov eax, dword ptr [ebp+FP_Check_FileHashValue_CRC_Arg_FuncAddr]
    assume eax : ptr FUNCADDRTBL
    call dword ptr [eax].xCRC32
    mov esi, eax
    
    ;; release the memory
    push MEM_RELEASE
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_fileSize]
    push dword ptr [ebp+FP_Check_FileHashValue_CRC_pBuffer]
    call dword ptr [ebx].xVirtualFree    
    
    call Get_FP_Check_FileHashValue_CRC_Eip
    Get_FP_Check_FileHashValue_CRC_Eip:
    pop edi
    add edi, offset FP_Check_FileHashValue_Orig_CRC - offset Get_FP_Check_FileHashValue_CRC_Eip
    sub edi, esi
    jz NotFound_FP_Check_FileHashValue_CRC
    jmp Found_FP_Check_FileHashValue_CRC
    
Exit_FP_Check_FileHashValue_CRC:
    pop edi
    pop esi
    pop edx
    pop ecx
    pop ebx
    assume eax : nothing
    assume ebx : nothing
    mov esp, ebp
    pop ebp
    retn 08h
NotFound_FP_Check_FileHashValue_CRC:
    xor eax, eax
    jmp  Exit_FP_Check_FileHashValue_CRC
Found_FP_Check_FileHashValue_CRC:
    mov eax, 1
    jmp  Exit_FP_Check_FileHashValue_CRC
FP_Check_FileHashValue_Orig_CRC:
    dd 0   
End_FP_Check_FileHashValue_CRC:
Windows NT系列平台支持一个会话多个桌面,将线程的桌面交换到自己的桌面。
目前还没有有效的方法转换到旧的桌面。
代码:
AD_SwitchDesktop:
AD_SwitchDesktop_Arg_Win32Api            equ 04h
    mov eax, dword ptr [esp+AD_SwitchDesktop_Arg_Win32Api]
    
    push ebx
    push ecx
    push edx
    push edi
    push esi
    
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    
    call dword ptr [ebx].xGetCurrentThreadId
    push eax
    call dword ptr [ebx].xGetThreadDesktop
    mov ecx, eax
    
    push DESKTOP_SWITCHDESKTOP
    push FALSE
    push 0
    call dword ptr [ebx].xOpenInputDesktop
    mov edx, eax
    
    push NULL
    push GENERIC_ALL
    push 0
    push NULL
    push NULL
    call AD_SwitchDesktop_Str
        db "MyDesktop",0
    AD_SwitchDesktop_Str:
    call dword ptr [ebx].xCreateDesktopA
    mov esi, eax
    
    push esi
    call dword ptr [ebx].xSetThreadDesktop
    
    push esi
    call dword ptr [ebx].xSwitchDesktop
    
    push 5000
    call dword ptr [ebx].xSleep
    
    push edx
    call dword ptr [ebx].xSwitchDesktop
    
    push ecx
    call dword ptr [ebx].xSetThreadDesktop
    
    push esi
    call dword ptr [ebx].xCloseDesktop
    
    mov eax, 1
    
    pop esi
    pop edi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    retn 04h
End_AD_SwitchDesktop:
BlockInput函数阻止了鼠标和键盘事件到达应用程序。
代码:
AD_BlockInput:
AD_BlockInput_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+AD_BlockInput_Arg_Win32Api]
    
    push ebx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    
    push TRUE
    call dword ptr [ebx].xBlockInput
    test eax, eax
    jz Exit_AD_BlockInput
    push 5000
    call dword ptr [ebx].xSleep
    
    push FALSE
    call dword ptr [ebx].xBlockInput
    xor eax, eax
Exit_AD_BlockInput:
    pop ebx
    assume ebx : nothing
    retn 04h
End_AD_BlockInput:
利用Nativa API函数ZwSetInformationThread函数通过ThreadHideFromDebugger来达到隐藏调试器的目的
代码:
AD_ZwSetInfomationThread:
AD_ZwSetInfomationThread_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+AD_ZwSetInfomationThread_Arg_Win32Api]
    push ebx
    mov ebx, eax
    assume ebx : ptr WIN32APIBASE
    
    push 0
    push NULL
    push ThreadHideFromDebugger
    call dword ptr [ebx].xGetCurrentThread
    push eax
    call dword ptr [ebx].xZwSetInformationThread
    cmp eax, STATUS_SUCCESS
Exit_AD_ZwSetInfomationThread:
    pop ebx
    assume ebx : nothing
    retn 04h
End_AD_ZwSetInfomationThread:
通过系统索引号调用NtSetInformationThread使用ThreadHideFromDebugger来达到隐藏调试器的目的
代码:
AD_INT_2e:
AD_INT_2e_Arg_Win32Api            equ 08h
AD_INT_2e_StackSize                equ 10h + sizeof OSVERSIONINFOEX
AD_INT_2e_bOsVersionInfoEx      equ -04h
AD_INT_2e_osvi                  equ -(10h+sizeof OSVERSIONINFOEX)
    push ebp
    mov ebp, esp
    sub esp, AD_INT_2e_StackSize
    
    push ebx
    push ecx
    push edx
    push edi
    push esi
    
    mov ebx, dword ptr [ebp+AD_INT_2e_Arg_Win32Api]
    assume ebx : ptr WIN32APIBASE
    
    lea edx, [ebp+AD_INT_2e_osvi]
    assume edx : ptr OSVERSIONINFOEX
    
    ;; clear the stack
    lea edi, [ebp-AD_INT_2e_StackSize]
    mov ecx, AD_INT_2e_StackSize
    xor eax, eax
    cld
    rep stosb
    
    push sizeof OSVERSIONINFOEX
    pop dword ptr [edx].dwOSVersionInfoSize
    
    call dword ptr [ebx].xGetCurrentThread
    mov esi, eax
    
    lea edx, [ebp+AD_INT_2e_osvi]
    push edx
    call dword ptr [ebx].xGetVersionExA
    test eax, eax
    jz Exit_AD_INT_2e
    ;; Test for the Windows NT product family
    lea edx, [ebp+AD_INT_2e_osvi]    
    cmp dword ptr [edx].dwPlatformId, VER_PLATFORM_WIN32_NT
    jnz Exit_AD_INT_2e
    push 0
    push 0
    push ThreadHideFromDebugger
    push esi
    mov eax, 0EEh        ; NtSetInformationThread
    mov edx, esp
    int 2eh
    add esp, 10h
    
    push 0
    push 0
    push ThreadHideFromDebugger
    push esi
    mov eax, 0E5h        ; NtSetInformationThread
    mov edx, esp
    int 2eh
    add esp, 10h
    ;jmp Found_AD_INT_2e
    ;AD_INT_2e_Tmp1:
    ;; Test for the Windows 95 product family
    ;cmp dword ptr [edx].dwPlatformId, VER_PLATFORM_WIN32_WINDOWS
    ;jnz AD_INT_2e_Tmp2
    ;AD_INT_2e_Tmp2:
    ;; Microsoft Win32s
Exit_AD_INT_2e:
    xor eax, eax
    pop esi
    pop edi
    pop edx
    pop ecx
    pop ebx
    assume ebx : nothing
    assume edx : nothing
    mov esp, ebp
    pop ebp
    retn 04h
End_AD_INT_2e:
Ollydbg传递用户定义的数据给msvcrt的vsprintf函数,如果这些数据包含
格式字符串标记.如果包换多个%s标记,这样会指向一个无效的内存导致
ollydbg失效。
代码:
AS_OD_OutputDebugString:
AS_OD_OutputDebugString_Arg_Win32Api        equ 04h
    mov eax, dword ptr [esp+AS_OD_OutputDebugString_Arg_Win32Api]
    assume eax : ptr WIN32APIBASE
    call AS_OD_OutputDebugString_Str
        db "%s%s",0
    AS_OD_OutputDebugString_Str:
    call dword ptr [eax].xOutputDebugStringA
    xor eax, eax
    assume eax : nothing
    retn 04h
End_AS_OD_OutputDebugString:
通过触发异常进入异常处理程序,将调试寄存器清0,清除断点。
Dr0,Dr1,Dr2,Dr3,Dr6和Dr7。Dr0-Dr3用作一般的线性地址,Dr6为调试状态寄存器
Dr7为调试控制寄存器。
代码:
AB_HWP_CLR_Exception:
    call Get_AB_HWP_CLR_Exception_Eip
    Get_AB_HWP_CLR_Exception_Eip:
    pop eax
    add eax, offset AB_HWP_CLR_Exception_Exception - offset Get_AB_HWP_CLR_Exception_Eip
    push eax
    assume fs : nothing
    push dword ptr fs:[0]
    mov dword ptr fs:[0], esp
    xor eax, eax
    ;; throw an exception
    div eax
    ;; restore exception handler
    pop dword ptr fs:[0]
    add esp, 04h
    jmp Exit_AB_HWP_CLR_Exception
AB_HWP_CLR_Exception_Exception:
    mov eax, dword ptr [esp+0ch]
    assume eax : ptr CONTEXT
    ;; clr debug registers context Dr0-Dr3
    mov dword ptr [eax].iDr0, 0
    mov dword ptr [eax].iDr1, 0
    mov dword ptr [eax].iDr2, 0
    mov dword ptr [eax].iDr3, 0
    mov dword ptr [eax].iDr6, 0
    mov dword ptr [eax].iDr7, 0
    ;; set eip upon return
    add dword ptr [eax].regEip, 02h
    xor eax, eax
    retn
Exit_AB_HWP_CLR_Exception:
    xor eax, eax
    assume eax : nothing
    retn 0h
End_AB_HWP_CLR_Exception:
还有很多反调试的方法没有归结到上面,一开始想做的全些。但是后来发现,太多了。还是以后慢慢归纳吧。
附件中的程序是一个可以添加反调试代码到其他程序的一个小玩意。
使用命令为AntiDebugger 1 2 3 4 file.exe 类似这样 ^_^
终于写完了,累吖。。。
上传的附件 AntiDebugger_bin.rar [解压密码:pediy.com]
AntiDebugger_src.rar

  • 标 题:答复
  • 作 者:玩命
  • 时 间:2008-08-21 10:43:47

附件的程序有三个选项没有完成 验证文件尺寸,文件CRC,内存CRC 这三个功能的填充原先值的代码没有完成。由于这期拖的时间有点长了。 为了敢时间就没有写填充原先值的功能。(比较麻烦,添加到目标程序ANTI函数是动态的所以。以后添加上)如果各位大大们有兴趣可以利用提供的CRC函数补写完这个功能。CRC函数的标号为xCRC可以到源文件中搜索下得到。
如果代码中出现什么BUG之流的东东 请各位尽情指出咯。。。