逆向学习笔记(3)——静态反调试技术

1.PEB结构

+0x002 BeingDebugged     : UChar
+0x00c Ldr               : Ptr32 _PEB_LDR_DATA
+0x018 ProcessHeap       : Ptr32 Void
+0x068 NtGlobalFlag      : Uint4B
...

1)BeingDebugged(+0x002)

当进程处于调试状态时,该标识为1,反之为0。

对应的API是IsDebuggerPresent(),用于获取当前进程的调试状态。

2)Ldr(+0x00c)

当进程处于调试状态时,堆内存区域会出现一些特殊标识,如0xEEFEEEFE等。而PEB.Ldr则是在堆内存中创建的,所以扫描该区域查看是否存在特殊标识。Ldr的结构类型是_PEB_LDR_DATA。

3)Process Heap(+0x18)

该成员的结构类型为HEAP如下:

0x000 Entry                      : _HEAP_ENTRY
0x008 Signature                  : Uint4B
0x00c Flags                      : Uint4B
0x010 ForceFlags                 : Uint4B
0x014 VirtualMemoryThreshold     : Uint4B
0x018 SegmentReserve             : Uint4B
0x01c SegmentCommit              : Uint4B
0x020 DeCommitFreeBlockThreshold : Uint4B

对应获取该结构的API是GetProcessHeap();

其中Flags(+0xC)和Force Flags(+0x10)这两个变量常用来识别调试状态。当处于正常状态下,Flags的值为0x2,ForceFlags的值为0x0。当处于调试状态时,这两个值会发生变化。

4)NtGlobalFlag(+0x68)

当处于调试状态时,该变量的值为0x70。

2.NtQueryInformationProcess()

该函数可以获取到与进程调试相关的信息。定义如下:

NTSTATUS WINAPI NtQueryInformationProcess(
    __in HANDLE ProcessHandle,
    __in PROCESSINFOCLASS ProcessInformationClass,
    __out PVOID ProcessInformation,
    __in ULONG ProcessInformationLength,
    __out_opt PULONG ReturnLength
);

其中第二个参数是用来获取指定信息的枚举类型,定义如下:

enum PROCESSINFOCLASS
{
    ProcessBasicInformation = 0,
    ProcessQuotaLimits,
    ProcessIoCounters,
    ProcessVmCounters,
    ProcessTimes,
    ProcessBasePriority,
    ProcessRaisePriority,
    ProcessDebugPort = 7,
    ProcessExceptionPort,
    ProcessAccessToken,
    ProcessLdtInformation,
    ProcessLdtSize,
    ProcessDefaultHardErrorMode,
    ProcessIoPortHandlers,
    ProcessPooledUsageAndLimits,
    ProcessWorkingSetWatch,
    ProcessUserModeIOPL,
    ProcessEnableAlignmentFaultFixup,
    ProcessPriorityClass,
    ProcessWx86Information,
    ProcessHandleCount,
    ProcessAffinityMask,
    ProcessPriorityBoost,
    MaxProcessInfoClass,
    ProcessWow64Information = 26,
    ProcessImageFileName = 27,
    ProcessDebugObjectHandle = 30,
    ProcessDebugFlags = 31,
};

1)ProcessDebugPort(0x7)

当进程处于调试状态时,获取的值为0xFFFFFFFF,如果处于非调试状态,则获取的值为0.

对应的API为CheckRemoteDebuggerPresent(),该函数还可以获取其他进程的调试状态。

2)ProcessDebugObjectHandle(0x1E)

当进程处于调试状态时,会生成调试对象(Debug Object)。使用ProcessDebugObjectHandle则可以获取调试对象的句柄,如果处于非调试状态,返回结果为NULL。

3)ProcessDebugFlags(0x1F)

当进程处于调试状态时,获取的值为0。反之为1。

3.NtQuerySystemInformation()

该函数可以用来获取系统环境是否处于调试状态。类似的winDbg调试虚拟机,则虚拟机系统就处于一个被调试状态。因为想要调试虚拟机需要配置系统为调试状态。xp修改C:\boot.ini,win7使用bcdedit.exe来修改调试状态。

因此利用这种方式,可以知道当前进程是否在一个可调试系统中运行,因为常规电脑是不会被作为调试机的。

函数定义如下:

NTSTATUS WINAPI NtQuerySystemInformation(
    __in SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout PVOID SystemInformation,
    __in ULONG SystemInformationLength,
    __out_opt PULONG ReturnLength
);

其中第一个参数是一个枚举类型,如下:

typedef enum _SYSTEM_INFORMATION_CLASS{
    SystemBasicInformation = 0,
    SystemPerformanceInformation = 2,
    SystemKernelDebuggerInformation = 35,
    ...
};

其中的SystemKernelDebuggerInformation可以获取系统的调试状态信息。返回结构如下:

typedef struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION
{
    BOOLEAN DebuggerEnabled;
    BOOLEAN DebuggerNotPresent;
}SYSTEM_KERNEL_DEBUGGER_INFORMATION;

当系统处于调试状态时,DebuggerEnabled的值为1,DebuggerNotPresent的值恒为1。

4.NtQueryObject()

系统中的某个调试器调试进程时,会创建一个调试对象类型的内核对象。检测该对象是否存在即可判断是否有进程正在被调试。

而NtQueryObject()用来获取系统中各类型内核对象信息。定义如下:

NTSTATUS NtQueryObject(
    __in_opt HANDLE Handle,
    __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
    __out_opt PVOID ObjectInformation,
    __in ULONG ObjectInformationLength,
    __out_opt PULONG ReturnLength
);

其中第二个参数是一个枚举类型,定义如下:

typedef enum _OBJECT_INFORMATION_CLASS{
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectAllTypesInformation,
    ObjectHandleInformation
}OBJECT_INFORMATION_CLASS;

使用ObjectAllTypesInformation可以获取到系统中所有类型的对象信息。信息结构定义如下:

typedef struct _LSA_Unicode_STRING{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
}LSA_Unicode_STRING, *PLSA_Unicode_STRING, Unicode_STRING, *PUnicode_STRING;

typedef struct _OBJECT_TYPE_INFORMATION{
    Unicode_STRING TypeName;
    ULONG TotalNumberOfHandles;
    ULONG TotalNumberOfObjects;
}OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef struct _OBJECT_ALL_INFORMATION{
    ULONG NumberOfObjectsTypes;
    OBJECT_TYPE_INFORMATION ObjectTypeInformation[1];
}OBJECT_ALL_INFORMATION, *POBJECT_ALL_INFORMATION;

关键判断代码:

for(UINT i = 0; i<pObjectAllInfo->NumberOfObjectsTypes; i++)
{
    pObjectTypeInfo = (POBJECT_TYPE_INFORMATION)pObjectInfoLocation;
    if(wcscmp(L"DebugObject", pObjectTypeInfo->TypeName.Buffer) == 0)
    {
        bDebugging = (pObjectTypeInfo->TotalNumberOfObjects > 0) ? TRUE : FALSE;
        break;
    }
    
    //计算下一个结构体
    pObjectInfoLocation = (UCHAR*)pObjectTypeInfo->TypeName.Buffer;
    pObjectInfoLocation += pObjectTypeInfo->TypeName.Length;
    pObjectInfoLocation = (UCHAR*)(((ULONG)pObjInfoLocation & 0xFFFFFFFC)+sizeof(ULONG));
}

5.ZwSetInformationThread()

用来强制分离(Detach)被调试者和调试器。可以将自身从调试器中分离出来。函数定义如下:

typedef enum _THREAD_INFORMATION_CLASS{
    ThreadBasicInformation,
    ThreadTimes,
    ThreadPriority,
    ThreadBasePriority,
    ThreadAffinityMask,
    ThreadImpersonationToken,
    ThreadDescriptorTableEntry,
    ThreadEnableAlignmentFaultFixup,
    ThreadZeroTlsCell,
    ThreadPerformanceCount,
    ThreadAmILastThread,
    ThreadIdealProcessor,
    ThreadPriorityBoost,
    ThreadSetTlsArrayAddress,
    ThreadIsIoPending,
    ThreadHideFromDebugger = 17
}

NTSTATUS ZwSetInformationThread(
    __in HANDLE ThreadHandle,
    __in THREADINFOCLASS ThreadInformationClass,
    __in PVOID ThreadInformation,
    __in ULONG ThreadInformationLength
);

其中ThreadHandle用来接收当前线程句柄,ThreadInformationClass表示线程信息类型,如果将其设置为ThreadHideFromDebugger(0x11),则调用函数后,调试进程会被分离出来。如果是正常的进程则不会受到影响。如果是调试中的进程,调试器和被调试进程都会终止。

6.TLS回调函数

由于TLS回调函数可以先于EP代码运行,所以常用来做反调试使用。可以在回调函数中使用类似IsDebuggerPresent()来检测调试状态。

7.ETC

通过检测自身所处环境来达到反调试的目的,不需要刻意检测是否处于调试状态。检测周边环境的方法有如下几种:

1)检测OllyDbg窗口->FindWindow()

2)检测OllyDbg进程->CreateToolhelp32Snapshot()

3)检查计算机名称是否为“TEST”、“ANALYSIS”等->GetComputerName()

4)检查程序运行路径中是否存在“TEST”、“SAMPLE”等名称->GetCommandLine()

5)检测虚拟机是否处于运行状态(查看虚拟机特有的进程名称->VMWareService.exe、WMWareTray.exe、WMWareUser.exe等)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

孤月丶星辰

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值