汇编级别反调试(2)

本文详细介绍了多种基于汇编级别的反调试技术,包括IsDebuggerPresent函数检查、PEB结构中的BeingDebugged标志、NtGlobalFlag、堆标志与ForceFlags、Trap Flag、API CheckRemoteDebuggerPresent、NtQueryInformationProcess、ProcessDebugObjectHandle、ProcessDebugFlags以及各种异常处理机制。通过理解这些技术,可以更好地实现或绕过反调试策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础方案和原理
内含 demo,编译环境:gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
1. IsDebuggerPresent
代码示例:
#include<stdio.h>
#include<windows.h>
#include<iostream>
using namespace std;
int main()
{
    if (IsDebuggerPresent())
    {
        cout << "Stop debugging program!" << std::endl;
        exit(-1);
    }
    return 0;
}
通过调试器,查看 IsDebuggerPresent源码可以看到:
0:000> u kernelBase!IsDebuggerPresent L3
KERNELBASE!IsDebuggerPresent:
00007ffb`ebf208d0 65488b042560000000 mov   rax,qword ptr gs:[60h]
00007ffb`ebf208d9 0fb64002        movzx   eax,byte ptr [rax+2]
00007ffb`ebf208dd c3              ret
32为:
0:000< u kernelbase!IsDebuggerPresent L3
KERNELBASE!IsDebuggerPresent:
751ca8d0 64a130000000    mov     eax,dword ptr fs:[00000030h]
751ca8d6 0fb64002        movzx   eax,byte ptr [eax+2]
751ca8da c3              ret
详细 PEB TEB结构可以参考附件内容。
原理
+0x060 ProcessEnvironmentBlock : Ptr64 _PEB gs 指向程序的_PEB结构,其中 _PEB 偏移 0x2 位置是: +0x002 BeingDebugged : UChar
所以该函数原理就是检查 PEB 下的 BeingDebugged 标志位。被调试是1 , 否则0。
PEB
你可以在代码中通过下面获取进程控制块地址:
PVOID GetPEB()
{
#ifdef _WIN64
    return (PVOID)__readgsqword(0x0C * sizeof(PVOID)); # PVOID 大小由编译器系统环境决定 4 or 8 bytes
#else
    return (PVOID)__readfsdword(0x0C * sizeof(PVOID));
#endif
}
如果程序是32位的,但是运行在64位系统上,遇到 WOW64 “天堂门”技术,可以通过下面代码,获取到单独创建的PEB结构: 你可以参考Get 32bit PEB of another process from a x64 process 同样的需求: 64位进程需要获取运行在WOW64中32位程序的PEB环境。
//  WOW64
PVOID GetPEB64()
{
    PVOID pPeb = 0;
#ifndef _WIN64
    if (IsWin8OrHigher()) # S
    {
        BOOL isWow64 = FALSE;
        typedef BOOL(WINAPI *pfnIsWow64Process)(HANDLE hProcess, PBOOL isWow64);
        pfnIsWow64Process fnIsWow64Process = (pfnIsWow64Process)
            GetProcAddress(GetModuleHandleA("Kernel32.dll"), "IsWow64Process");
        if (fnIsWow64Process(GetCurrentProcess(), &isWow64))
        {
            if (isWow64)
            {
                pPeb = (PVOID)__readfsdword(0x0C * sizeof(PVOID));
                pPeb = (PVOID)((PBYTE)pPeb + 0x1000);
            }
        }
    }
#endif
    return pPeb;
}
BYPASS
直接给标志位置0:
mov eax, dword ptr fs:[0x30]  
mov byte ptr ds:[eax+2]
// x64
DWORD64 dwpeb = __readgsqword(0x60);
*((PBYTE)(dwpeb + 2)) = 0;
注意检查 TLS 回调
TLS在程序运行前以及运行了,记得检查是否由TLS回调函数,隐藏了反调试如:
#pragma section(".CRT$XLY", long, read)
__declspec(thread) int var = 0xDEADBEEF;
VOID NTAnopPI TlsCallback(PVOID DllHandle, DWORD Reason, VOID Reserved)
[backcolor=var(--color-canvas-subtle)]{
     var = 0xB15BADB0;
    if (IsDebuggerPresent())
    {
        MessageBoxA(NULL, "Stop debugging program!", "Error", MB_OK | MB_ICONERROR);
        TerminateProcess(GetCurrentProcess(), 0xBABEFACE);
    }
}
__declspec(allocate(".CRT$XLY"))PIMAGE_TLS_CALLBACK g_tlsCallback = TlsCallback;      
2.NtGlobalFlag
在 Windows NT 中,有一组标志存储在全局变量 NtGlobalFlag 中,这在整个系统中是通用的。在启动时,NtGlobalFlag 全局系统变量将使用系统注册表项中的值进行初始化: [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\GlobalFlag]
要检查进程是否已使用调试器启动,请检查 PEB 结构中 NtGlobal 标志字段的值。此字段分别位于 x32 和 x64 系统的PEB 0x068和0x0bc偏移量。
0:000> dt _PEB NtGlobalFlag @$peb
ntdll!_PEB
   +0x068 NtGlobalFlag : 0x70
// x64
0:000> dt _PEB NtGlobalFlag @$peb
ntdll!_PEB
   +0x0bc NtGlobalFlag : 0x70
NtGlobalFlag 和 IMAGE_LOAD_CONFIG_DIRECTORY
可执行文件可以包含IMAGE_LOAD_CONFIG_DIRECTORY结构,该结构包含系统加载程序的其他配置参数。默认情况下,此结构不会内置于可执行文件中,但可以使用修补程序添加它。此结构具有GlobalFlagsClear ,该字段指示应重置 PEB 结构的 NtGlobal 标志字段的哪些标志。如果可执行文件最初是在没有上述结构的情况下创建的,或者 GlobalFlagsClear = 0,则在磁盘或内存中,该字段将具有非零值,表示存在隐藏的调试器。下面的代码示例检查正在运行的进程内存和磁盘上的全局标记清除字段,从而说明一种流行的反调试技术:
代码片段
3. 堆标志和 ForceFlags
在PEB中包含两个特殊标志位结构:
0:000> dt _PEB ProcessHeap @$peb
ntdll!_PEB
   +0x018 ProcessHeap : 0x00440000 Void
0:000> dt _HEAP Flags ForceFlags 00440000
ntdll!_HEAP
   +0x040 Flags      : 0x40000062
   +0x044 ForceFlags : 0x40000060
x64:
0:000> dt _PEB ProcessHeap @$peb
ntdll!_PEB
   +0x030 ProcessHeap : 0x0000009d`94b60000 Void
0:000> dt _HEAP Flags ForceFlags 0000009d`94b60000
ntdll!_HEAP
   +0x070 Flags      : 0x40000062
   +0x074 ForceFlags : 0x40000060
判断条件:
如果 堆标志字段未设置HEAP_GROWABLE(0x00000002)标志,则正在调试进程。
如果ForceFlags值不为 0,则正在调试进程。
但在实际运行中根据操作系统不同可能会有一些偏差,下面是一个demo:
int GetHeapFlagsOffset(bool x64)
{
    return x64 ?
        IsVistaOrHigher() ? 0x70 : 0x14: //x64 offsets
        IsVistaOrHigher() ? 0x40 : 0x0C; //x86 offsets
}
int GetHeapForceFlagsOffset(bool x64)
{
    return x64 ?
        IsVistaOrHigher() ? 0x74 : 0x18: //x64 offsets
        IsVistaOrHigher() ? 0x44 : 0x10; //x86 offsets
}
void CheckHeap()
{
    PVOID pPeb = GetPEB();
    PVOID pPeb64 =
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青月

打赏会让我更加有动力分享!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值