ProcessHeap 是Windows
進程的默認堆,每個進程都有一個默認的堆,用於在進程地址空間中分配內存空間。默認情況下ProcessHeap
由內核進行初始化,該堆中存在一個未公開的屬性,它被設置爲加載器爲進程分配的第一個堆的位置(進程堆標誌),ProcessHeap
標誌位於PEB
結構中偏移爲0x18
處,第一個堆頭部有一個屬性字段,這個屬性叫做ForceFlags
屬性偏移爲0x44
,該屬性爲0說明程序沒有被調試,非0說明被調試,另外的Flags
屬性爲2說明被調試,不爲2則說明沒有被調試。
0:000> dt !_peb
ntdll!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x018 ProcessHeap : Ptr32 Void // 找到Process偏移地址
0:000> !heap // 找出堆區首地址
Heap Address NT/Segment Heap
1270000 NT Heap
0:000> !heap -a 1270000 // 查詢heep的內存
Index Address Name Debugging options enabled
1: 01270000
Segment at 01270000 to 0136f000 (00006000 bytes committed)
Flags: 40000062
ForceFlags: 40000060
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
0:000> dt _HEAP 1270000 // 找到ForceFlags標誌的偏移地址
ntdll!_HEAP
+0x000 Segment : _HEAP_SEGMENT
+0x000 Entry : _HEAP_ENTRY
+0x040 Flags : 0x40000062
+0x044 ForceFlags : 0x40000060
這裏需要注意一點,堆區在不同系統中偏移值是不同的,在Windows10
系統中ForceFlags
屬性位於堆頭部偏移量爲0x44
處,而默認情況如果被調試則ForceFlags
屬性爲0x40000060
,而Flags
標誌爲0x40000062
,有了這些參考那麼通過彙編語言實現將變得很容易,如下代碼則是通過彙編分別讀取這兩個堆頭參數;
#include <stdio.h>
#include <windows.h>
// 兩種方式輸出
int IsDebug(DWORD x)
{
DWORD Debug = 0;
if (x == 1)
{
__asm
{
mov eax, fs:[0x18] // TED基地址
mov eax, [eax + 0x30] // PEB基地址
mov eax, [eax + 0x18] // 定位 ProcessHeap
mov eax, [eax + 0x44] // 定位到 ForceFlags
mov Debug, eax
}
}
if (x == 2)
{
__asm
{
mov eax, fs:[0x18] // TED基地址
mov eax, [eax + 0x30] // PEB基地址
mov eax, [eax + 0x18] // 定位 ProcessHeap
mov eax, [eax + 0x40] // 定位到 Flags
mov Debug, eax
}
}
return Debug;
}
int main(int argc, char * argv[])
{
if (IsDebug(1) && IsDebug(2))
{
printf("[-] 進程正在被調試 \n");
}
else
{
printf("[*] 進程正常 \n");
}
system("pause");
return 0;
}
另一種通過C語言實現的反調試版本,其反調試原理與上方相同,只不過此處我們使用了系統的API來完成檢測標誌位的。
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS(NTAPI *typedef_ZwQueryInformationProcess)(
IN HANDLE ProcessHandle,
IN PROCESSINFOCLASS ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
BOOL IsDebug()
{
HANDLE hProcess = NULL;
DWORD ProcessId = 0;
PROCESS_BASIC_INFORMATION Pbi;
typedef_ZwQueryInformationProcess pZwQueryInformationProcess = NULL;
ProcessId = GetCurrentProcessId();
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (hProcess != NULL)
{
HMODULE hModule = LoadLibrary("ntdll.dll");
pZwQueryInformationProcess = (typedef_ZwQueryInformationProcess)GetProcAddress(hModule, "ZwQueryInformationProcess");
NTSTATUS Status = pZwQueryInformationProcess(hProcess, ProcessBasicInformation, &Pbi, sizeof(PROCESS_BASIC_INFORMATION), NULL);
if (NT_SUCCESS(Status))
{
DWORD ByteRead = 0;
DWORD ProcessHeap = 0;
ULONG PebBase = (ULONG)Pbi.PebBaseAddress;
DWORD ForceFlagsValue = 1;
ReadProcessMemory(hProcess, (LPCVOID)(PebBase + 0x18), &ProcessHeap, 2, &ByteRead);
ReadProcessMemory(hProcess, (LPCVOID)(ProcessHeap + 0x40), &ForceFlagsValue, 4, &ByteRead);
if (ForceFlagsValue != 0)
{
return TRUE;
}
}
CloseHandle(hProcess);
}
return FALSE;
}
int main(int argc, char * argv[])
{
if (IsDebug())
{
printf("[-] 正在被調試 \n");
}
system("pause");
return 0;
}
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/e45d84ae.html
版權聲明: 本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!