CTF-RE 从0到N: windows反调试-获取Process Environment Block(PEB)信息来检测调试
在Windows操作系统中,Process Environment Block (PEB,进程环境块) 是一个包含特定进程信息的数据结构。它可以被用于反调试中
如何获取PEB指针?
在Windows操作系统中,获取PEB指针的常见方法主要有以下几种。:
1. 使用 NtCurrentPeb
获取 PEB
NtCurrentPeb
是Windows内核提供的一个函数,它返回当前进程的PEB指针。这个方法通常用于内核模式或通过非公开的API调用。在用户模式中,你可以使用 NtCurrentPeb
来获取PEB指针。
PEB* GetPEB()
{
return NtCurrentPeb(); // 获取当前进程的PEB指针
}
注:NtCurrentPeb
是Windows的NT内核函数,通常不公开给用户模式程序直接调用,但可以通过动态加载 ntdll.dll
并使用 GetProcAddress
来调用。
2. 使用 NtQueryInformationProcess
获取 PEB
通过 NtQueryInformationProcess
函数,可以查询关于进程的信息,其中包括PEB的指针。这是通过NT内部函数来实现的。
#include <Windows.h>
#include <winternl.h>
typedef struct _PROCESS_BASIC_INFORMATION {
ULONG Reserved;
ULONG PebBaseAddress; // PEB的地址
ULONG AffinityMask;
ULONG BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef NTSTATUS(WINAPI* NtQueryInformationProcess_t)(
HANDLE ProcessHandle,
PROCESS_INFORMATION_CLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
PEB* GetPEB(HANDLE hProcess)
{
NtQueryInformationProcess_t NtQueryInformationProcess =
(NtQueryInformationProcess_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION pbi;
ULONG len = 0;
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &len);
if (NT_SUCCESS(status))
{
return (PEB*)pbi.PebBaseAddress;
}
return NULL;
}
注:此方法需要依赖动态加载 ntdll.dll
,并调用 NtQueryInformationProcess
函数。这个函数是Windows的NT内核接口,返回关于进程的详细信息。
3. 直接通过 NtQueryInformationProcess
查询 PEB 地址
也可以通过调用 NtQueryInformationProcess
来获取进程的PEB地址。你可以查询有关进程的基本信息,其中就包含PEB的地址。
#include <windows.h>
#include <winternl.h>
typedef enum _PROCESSINFOCLASS {
ProcessBasicInformation = 0
} PROCESSINFOCLASS;
typedef struct _PROCESS_BASIC_INFORMATION {
ULONG Reserved;
ULONG PebBaseAddress; // PEB地址
ULONG AffinityMask;
ULONG BasePriority;
ULONG UniqueProcessId;
ULONG InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef NTSTATUS(WINAPI* NtQueryInformationProcess_t)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
PEB* GetPEB(HANDLE processHandle)
{
PROCESS_BASIC_INFORMATION pbi;
ULONG len = 0;
NtQueryInformationProcess_t NtQueryInformationProcess =
(NtQueryInformationProcess_t)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
NTSTATUS status = NtQueryInformationProcess(processHandle, ProcessBasicInformation, &pbi, sizeof(pbi), &len);
if (NT_SUCCESS(status))
{
return (PEB*)pbi.PebBaseAddress;
}
return NULL;
}
检测PEB的哪些位置来检测调试?
-
检查BeingDebugged标志:
- PEB结构中的
BeingDebugged
字段直接指示进程是否正在被调试。该字段是一个布尔值,当进程在调试器下运行时,它的值为1,否则为0。
BOOL IsDebuggerPresent() { PEB* peb = (PEB*)__readfsdword(0x30); // 获取PEB地址 return peb->BeingDebugged; // 返回BeingDebugged字段的值 }
- PEB结构中的
-
检查NtGlobalFlag标志:
- PEB中的
NtGlobalFlag
字段包含一些系统标志,其中某些标志在进程被调试器附加时会被设置。例如,FLG_HEAP_ENABLE_TAIL_CHECK
,FLG_HEAP_ENABLE_FREE_CHECK
和FLG_HEAP_VALIDATE_PARAMETERS
会在调试器附加时被设置。
BOOL CheckNtGlobalFlag() { PEB* peb = (PEB*)__readfsdword(0x30); // 获取PEB地址 DWORD NtGlobalFlag = peb->NtGlobalFlag; return (NtGlobalFlag & 0x70) != 0; // 检查特定的调试标志 }
- PEB中的
-
检查Heap Flags和ForceFlags:
- 调试器通常会修改PEB中的Heap Flags和ForceFlags,以启用更严格的堆检查。在PEB中的
ProcessHeap
字段指向进程的默认堆,这些标志位于该堆结构中。
BOOL CheckHeapFlags() { PEB* peb = (PEB*)__readfsdword(0x30); // 获取PEB地址 PVOID heap = peb->ProcessHeap; // 获取默认堆地址 DWORD heapFlags = *(DWORD*)((BYTE*)heap + 0x40); DWORD forceFlags = *(DWORD*)((BYTE*)heap + 0x44); return (heapFlags & 2) || (forceFlags != 0); }
- 调试器通常会修改PEB中的Heap Flags和ForceFlags,以启用更严格的堆检查。在PEB中的
-
通过PEB中的StartupInfo检查调试器:
- PEB中的
StartupInfo
结构包含有关进程启动的详细信息。可以通过检查StartupInfo
中的特定字段来判断是否有调试器存在。
BOOL CheckStartupInfo() { PEB* peb = (PEB*)__readfsdword(0x30); // 获取PEB地址 STARTUPINFO* startupInfo = &peb->ProcessParameters->StartupInfo; return startupInfo->dwFlags & STARTF_FORCEONFEEDBACK; // 检查特定标志 }
- PEB中的
示例crackMe
以下是反调试代码的反编译示例
if ( *((_DWORD *)NtCurrentPeb()->ProcessHeap + 3) != 2 )
a2[v6] = 34;
if ( (NtCurrentPeb()->NtGlobalFlag & 0x70) != 0 )
v12 = v10 + v11;
if ( (unsigned __int8)*(_DWORD *)&NtCurrentPeb()->BeingDebugged )
{
v10 = -83;
v11 = 43;
}