[转](64)句柄表,遍历所有进程的句柄表实现反调试

 

一、内核对象,句柄

 

这次课讨论的内核对象是指创建时需要指定 LPSECURITY_ATTRIBUTES 参数的对象,例如 Mutex, Thread。

 

调用 CreateThread 等函数会返回一个 HANDLE 类型值,这种就叫句柄,它对应一个内核对象;

 

调用 CloseHandle 函数对某个内核对象计数减一,当内核对象计数为0,这个对象就被销毁了。

 

内核对象在内核存储,直接把地址给3环用很不安全,所以微软设计了句柄(HANDLE)给3环使用,句柄是一个整数,它的值除以4是句柄表的下标,通过下标能找到存储在句柄表里的句柄表项,每个占8字节。

 

二、句柄表,句柄表项

 

1.句柄表结构

 

句柄表存储在 EPROCESS.ObjectTable.TableCode 里:

 

kd> dt _HANDLE_TABLE
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x02c ExtraInfoPages   : Int4B
   +0x030 FirstFree        : Uint4B
   +0x034 LastFree         : Uint4B
   +0x038 NextHandleNeedingPool : Uint4B
   +0x03c HandleCount      : Int4B
   +0x040 Flags            : Uint4B
   +0x040 StrictFIFO       : Pos 0, 1 Bit

 

句柄表项每个占8字节,一个页4KB,所以一个页能存储512个句柄表项,当进程中的句柄数量超过512,句柄表就会以分级形式存储,最多三级:

 

在这里插入图片描述

 

特别留意 TableCode 的第2位,它表明了句柄表的结构,如果第2位是01,表示现在句柄表有两级, TableCode 指向的表存储了 4KB / 4 = 1024 个句柄表的地址,每个地址指向一个句柄表。

 

我们可以编程,构造超过512个句柄,看看 TableCode 的低2位是否是01:

 

#include "stdafx.h"
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD PID;
	HANDLE hPro = NULL;
	HWND hwnd = FindWindowA(NULL, "计算器");
	GetWindowThreadProcessId(hwnd, &PID);

	for (int i = 0; i < 600; i++)
	{
		//hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		printf("句柄:%x\n", hPro);
	}
	SetHandleInformation(hPro, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
	getchar();
	return 0;
}

 

确实是这样的,观察 +0x000 TableCode : 0xe128f001

 

kd> dt 0xe1be0f00 _HANDLE_TABLE
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0xe128f001
   +0x004 QuotaProcess     : 0x81df0da0 _EPROCESS
   +0x008 UniqueProcessId  : 0x00000614 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0x8055c448 - 0xe2307b04 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0x99c
   +0x034 LastFree         : 0
   +0x038 NextHandleNeedingPool : 0x1000
   +0x03c HandleCount      : 0n613
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0

 


 

2.通过句柄表项找到内核对象

 

下面我们编写一个程序,打开计算器的进程句柄,然后在windbg里通过句柄表找到计算器的EPROCESS:

 

#include "stdafx.h"
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	DWORD PID;
	HANDLE hPro = NULL;
	HWND hwnd = FindWindowA(NULL, "计算器");
	GetWindowThreadProcessId(hwnd, &PID);

	for (int i = 0; i < 100; i++)
	{
		//hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		hPro = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, TRUE, PID);
		printf("句柄:%x\n", hPro);
	}
	SetHandleInformation(hPro, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
	getchar();
	return 0;
}

 

在这里插入图片描述

 

以 1c0 为例,除以4得到 70.找到程序的句柄表:

 

kd> dt 0xe14be1e0 _HANDLE_TABLE
ntdll!_HANDLE_TABLE
   +0x000 TableCode        : 0xe11a4000
   +0x004 QuotaProcess     : 0x81e6b020 _EPROCESS
   +0x008 UniqueProcessId  : 0x00000544 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c HandleTableList  : _LIST_ENTRY [ 0x8055c448 - 0xe2307b04 ]
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : (null) 
   +0x02c ExtraInfoPages   : 0n0
   +0x030 FirstFree        : 0x1c8
   +0x034 LastFree         : 0
   +0x038 NextHandleNeedingPool : 0x800
   +0x03c HandleCount      : 0n113
   +0x040 Flags            : 0
   +0x040 StrictFIFO       : 0y0

 

0xe11a4000 低2位是0,表示当前进程的句柄表只有一级,然后我们找一下下标为70的项:

 

kd> dq 0xe11a4000+70*8
e11a4380  0000003a`81dd800b 0200003a`81dd800b
e11a4390  000001cc`00000000 000001d0`00000000
e11a43a0  000001d4`00000000 000001d8`00000000
e11a43b0  000001dc`00000000 000001e0`00000000
e11a43c0  000001e4`00000000 000001e8`00000000
e11a43d0  000001ec`00000000 000001f0`00000000
e11a43e0  000001f4`00000000 000001f8`00000000
e11a43f0  000001fc`00000000 00000200`00000000

 

我们这里打印了下标70的项,同时,+8就是下标71的项,就是我们调用 SetHandleInformation 修改了句柄属性的最后一个项,观察它们的区别,最高字节不相同。

 

关于句柄表项这64位,网上是没有文档的,只能自己分析。这里先介绍低32位,低32位的低3位清零后就是内核对象头(此处是EPROCESS)的虚拟地址,注意,指向内核对象头 OBJECT_HEADER,这个结构大小是 0x18,所以要加上 0x18 才能找到 EPROCESS.

 

所以计算器的 EPROCESS 是在 81dd8008+18, dt 验证一下:

 

kd> dt 81dd8008+18 _EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
...
   +0x174 ImageFileName    : [16]  "calc.exe"
...

 


 

3. 句柄表项其他位

 

句柄表项一个8字节,它的64个位的用途是没有文档说明的,只能自己分析,以下内容属于拓展,不保证内容完整且正确,需要得到更准确的情报,请逆向或阅读源码。

 

在这里插入图片描述

 

63-56 给 SetHandleInformation 函数用,如果参数是HANDLE_FLAG_PROTECT_FROM_CLOSE(0x02),那么这个字节会设置成0x0200,刚才的实验已经验证过了。

 

55-48 这个字节恒为0 存疑!140说 OpenProcess(PROCESS_ALL_ACCESS 就不是0

 

47-32 位存储的是访问掩码,OpenProcess 的第一个参数会影响这里的值。

 

31-3位加上第三位清零存储的是内核对象的地址.

 

低3位是属性,2位默认是0;1位表示该句柄是否可继承;0位默认为1.

 

三、实现用句柄表反调试:当调试器attach时报错

 

思路:遍历所有其他进程句柄表,看哪个进程的句柄表中保护自己的进程,如果有,说明正在被调试。

 

用一个驱动不停地遍历进程链表,然后遍历进程的句柄表,如果发现句柄表项和游戏EPROCESS相等,就意味着被 OpenProcess 了,就认为是被调试了。

 

运行结果:没被调试

 

在这里插入图片描述

 

运行结果:被调试

 

在这里插入图片描述

 

3环代码

 

// Game.cpp : 定义控制台应用程序的入口点。
//


#include "stdafx.h"
#include <windows.h>

//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

typedef LONG  (__stdcall *HBGCOMMUNICATION) (IN ULONG OpCode, IN OUT PVOID p1, IN OUT PVOID p2, IN OUT PVOID p3);

//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

BOOL LoadDriver(PCWSTR lpszDriverName, PCWSTR lpszDriverPath);
void UnLoadDriver(PCWSTR lpszDriverName);

//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

#define DRIVER_NAME L"HbgProtect"
#define DRIVER_PATH L"HbgProtect.sys"

#define OP_CHECK_DEBUG 50

HBGCOMMUNICATION HbgCommunication = NULL;

//-----------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------

int _tmain(int argc, _TCHAR* argv[])
{
	HbgCommunication = (HBGCOMMUNICATION)GetProcAddress(LoadLibraryA("ntdll.dll"),"ZwQuerySystemInformation");
	if (HbgCommunication == NULL) 
	{
		printf("获取函数地址失败. %d\n",GetLastError());
		getchar();
		return 1;
	}

	// 加载游戏保护驱动
	LoadDriver(DRIVER_NAME,DRIVER_PATH);

	while (!GetAsyncKeyState('Q'))
	{
		Sleep(1000);
		BOOL IsDebugged = FALSE;
		HbgCommunication(OP_CHECK_DEBUG,&IsDebugged,NULL,NULL);
		if (IsDebugged)
		{
			printf("-----------------正在被调试!\n");
		}
		else
		{
			printf("没有被调试.\n");
		}
	}

	UnLoadDriver(DRIVER_NAME);
	getchar();
	return 0;
}

BOOL LoadDriver(PCWSTR lpszDriverName, PCWSTR lpszDriverPath)
{
	// 获取驱动完整路径
	WCHAR szDriverFullPath[MAX_PATH] = { 0 };
	GetFullPathNameW(lpszDriverPath,MAX_PATH,szDriverFullPath,NULL);
	//printf("%s\n", szDriverFullPath);
	// 打开服务控制管理器
	SC_HANDLE hServiceMgr = NULL; // SCM管理器句柄	
	hServiceMgr = OpenSCManagerW(NULL,NULL,SC_MANAGER_ALL_ACCESS);
	if (NULL == hServiceMgr)
	{
		printf("OpenSCManagerW 失败, %d\n", GetLastError());
		return FALSE;
	}
	printf("打开服务控制管理器成功.\n");
	// 创建驱动服务
	SC_HANDLE hServiceDDK = NULL; // NT驱动程序服务句柄
	hServiceDDK = CreateServiceW(
		hServiceMgr,
		lpszDriverName,
		lpszDriverName,
		SERVICE_ALL_ACCESS,
		SERVICE_KERNEL_DRIVER,
		SERVICE_DEMAND_START,
		SERVICE_ERROR_IGNORE,
		szDriverFullPath,
		NULL,
		NULL,
		NULL,
		NULL,
		NULL);
	if (NULL == hServiceDDK)
	{
		DWORD dwErr = GetLastError();
		if (dwErr != ERROR_IO_PENDING && dwErr != ERROR_SERVICE_EXISTS)
		{
			printf("创建驱动服务失败, %d\n", dwErr);
			return FALSE;
		}
	}
	printf("创建驱动服务成功.\n");
	// 驱动服务已经创建,打开服务
	hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);
	if (!StartService(hServiceDDK, NULL, NULL))
	{
		DWORD dwErr = GetLastError();
		if (dwErr != ERROR_SERVICE_ALREADY_RUNNING)
		{
			printf("运行驱动服务失败, %d\n", dwErr);
			return FALSE;
		}
	}
	printf("运行驱动服务成功.\n");
	if (hServiceDDK)
	{
		CloseServiceHandle(hServiceDDK);
	}
	if (hServiceMgr)
	{
		CloseServiceHandle(hServiceMgr);
	}
	return TRUE;
}

void UnLoadDriver(PCWSTR lpszDriverName)
{
	SC_HANDLE hServiceMgr = OpenSCManagerW(0,0,SC_MANAGER_ALL_ACCESS);
	SC_HANDLE hServiceDDK = OpenServiceW(hServiceMgr,lpszDriverName,SERVICE_ALL_ACCESS);
	SERVICE_STATUS SvrStatus;
	ControlService(hServiceDDK,SERVICE_CONTROL_STOP,&SvrStatus);
	DeleteService(hServiceDDK);
	if (hServiceDDK)
	{
		CloseServiceHandle(hServiceDDK);
	}
	if (hServiceMgr)
	{
		CloseServiceHandle(hServiceMgr);
	}
}


 

驱动代码

 

#include <ntddk.h>



//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------

typedef NTSTATUS  (__stdcall *NTQUERYSYSTEMINFORMATION) (IN ULONG SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength);

typedef struct _LDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderLinks;
	LIST_ENTRY InMemoryOrderLinks;
	LIST_ENTRY InInitializationOrderLinks;
	PVOID DllBase;
	PVOID EntryPoint;
	ULONG SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	ULONG Flags;
	UINT16 LoadCount;
	UINT16 TlsIndex;
	LIST_ENTRY HashLinks;
	PVOID SectionPointer;
	ULONG CheckSum;
	ULONG TimeDateStamp;
	PVOID LoadedImports;
	PVOID EntryPointActivationContext;
	PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path);
VOID DriverUnload(PDRIVER_OBJECT driver);
ULONG GetProcessEprocessAddr(char *processname);
BOOLEAN CheckProcessDebugged(ULONG EprocessAddress);
VOID PageProtectOff();
VOID PageProtectOn();
VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PULONG uKrnlImageSize);
PVOID MemorySearch(PVOID bytecode, ULONG bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress);
void InlineHookNtQuerySystemInformation();
void UnsetInlineHookNtQuerySystemInformation();
void HbgNtQuerySystemInformation(IN ULONG OpCode, IN OUT PVOID p1, IN OUT PVOID p2, IN OUT PVOID p3);

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------

#define OP_CHECK_DEBUG 50

#define GAMEIMAGENAME "Game.exe"
ULONG g_EprocessAddress;


PDRIVER_OBJECT g_Driver;
NTQUERYSYSTEMINFORMATION NtQuerySystemInformation;
ULONG g_HookRetAddressNtQuerySystemInformation;

BOOLEAN g_IsDebug;

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------

NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
	g_Driver = driver;
	driver->DriverUnload = DriverUnload;

	g_EprocessAddress = GetProcessEprocessAddr(GAMEIMAGENAME);

	//CheckProcessDebugged(g_EprocessAddress);
	// 通过 hook api 的方式实现简易的0-3环通信
	// 在钩子函数里处理3环的通信请求,3环会不停地命令驱动检查进程是否被调试
		
	InlineHookNtQuerySystemInformation();
	

	return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT driver)
{
	UnsetInlineHookNtQuerySystemInformation();
	DbgPrint("HbgProtect Unload Successfully.\n");
}

// 遍历进程列表,获取游戏EPROCESS地址,不考虑多开
ULONG GetProcessEprocessAddr(char *ProcessName)
{
	PEPROCESS pEprocess, pCurProcess;
	PCHAR ImageFileName;
	ULONG GameEprocessAddr; // 游戏EPROCESS地址,用来和句柄表的值比对,如果比对相等说明正在被调试
	// 获取 EPROCESS
	__asm
	{
		mov eax, fs:[0x124];
		mov eax, [eax + 0x220];
		mov pEprocess, eax;
	}
	pCurProcess = pEprocess;
	// 遍历 ActiveProcessLinks
	do
	{		
		ImageFileName = (PCHAR)pCurProcess + 0x174;
		if (strcmp(ImageFileName, ProcessName) == 0)
		{
			GameEprocessAddr = (ULONG)pCurProcess; // 记录游戏EPROCESS地址
			return (ULONG)pCurProcess;
		}				
		pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88);
	} while (pEprocess != pCurProcess);
	return 0;
}

BOOLEAN CheckProcessDebugged(ULONG EprocessAddress)
{
	BOOLEAN IsDebugged = FALSE;
	PEPROCESS pEprocess, pCurProcess;
	PCHAR ImageFileName;
	ULONG TableCode;
	ULONG ObjectTable;	
	// 获取 EPROCESS
	__asm
	{
		mov eax, fs:[0x124];
		mov eax, [eax + 0x220];
		mov pEprocess, eax;
	}
	pCurProcess = pEprocess;
	// 遍历 ActiveProcessLinks
	do
	{		
		ImageFileName = (PCHAR)pCurProcess + 0x174;
		ObjectTable = *(PULONG)((ULONG)pCurProcess + 0xC4);
		if (ObjectTable != 0)
		{
			TableCode = *(PULONG)ObjectTable;
			//DbgPrint("%x %s\n", TableCode, ImageFileName);
			switch(TableCode & 0x00000003)
			{				
			case 0:
				{
					// 一级句柄表
					int i;
					ULONG HandleAddr;
					TableCode &= 0xFFFFFFFC; // 低2位清零
					//DbgPrint("正在检查 [%s] 的句柄表...\n", ImageFileName);
					for (i = 0; i < 512; i+=2)
					{
						HandleAddr = (((PULONG)TableCode)[i] & 0xFFFFFFF8) + 0x18; // 低3位清零 + 0x18(跳过 OBJECT_HEADER ) 就是句柄的地址
						if (HandleAddr == EprocessAddress)
						{
							DbgPrint("游戏正在被 [%s] 调试!\n", ImageFileName);
							IsDebugged = TRUE;
						}
					}
					break;
				}
			case 1:
				{
					// 二级句柄表
					int i,j;
					ULONG HandleAddr;
					ULONG TableCode2;
					TableCode &= 0xFFFFFFFC; // 低2位清零
					//DbgPrint("正在检查 [%s] 的句柄表...\n", ImageFileName);
					for (i = 0; i < 1024; i++)
					{
						TableCode2 = ((PULONG)TableCode)[i];
						if (!MmIsAddressValid((PVOID)TableCode2)) continue; // 跳过无效线性地址
						for (j = 0; j < 512; j+=2)
						{
							HandleAddr = (((PULONG)TableCode2)[j] & 0xFFFFFFF8) + 0x18; // 低3位清零 + 0x18(跳过 OBJECT_HEADER ) 就是句柄的地址
							if (HandleAddr == EprocessAddress)
							{
								DbgPrint("%s 正在调试游戏!\n", ImageFileName);
								IsDebugged = TRUE;
							}
						}
					}
					break;
				}
			case 2:
				{
					// 三级句柄表
					int i,j,k;
					ULONG HandleAddr;
					ULONG TableCode2,TableCode3;
					TableCode &= 0xFFFFFFFC; // 低2位清零
					//DbgPrint("正在检查 [%s] 的句柄表...\n", ImageFileName);
					for (i = 0; i < 1024; i++)
					{
						TableCode2 = ((PULONG)TableCode)[i];
						if (!MmIsAddressValid((PVOID)TableCode2)) continue; // 跳过无效线性地址
						for (j = 0; j < 1024; j++)
						{
							TableCode3 = ((PULONG)TableCode2)[j];
							if (!MmIsAddressValid((PVOID)TableCode3)) continue; // 跳过无效线性地址
							for (k = 0; k < 512; k+=2)
							{
								HandleAddr = (((PULONG)TableCode3)[k] & 0xFFFFFFF8) + 0x18; // 低3位清零 + 0x18(跳过 OBJECT_HEADER ) 就是句柄的地址
								if (HandleAddr == EprocessAddress)
								{
									DbgPrint("%s 正在调试游戏!\n", ImageFileName);
									IsDebugged = TRUE;
								}
							}
						}
						
					}
					break;
				}

			}
		}		
		pCurProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurProcess + 0x88) - 0x88);
	} while (pEprocess != pCurProcess);
	if (!IsDebugged) DbgPrint("游戏没有被调试.\n");
	return IsDebugged;
}

// 关闭页保护
VOID PageProtectOff()
{
	__asm
	{
		cli; // 关闭中断
		mov eax, cr0;
		and eax, not 0x10000; // WP位置0
		mov cr0, eax;
	}
}

// 开启页保护
VOID PageProtectOn()
{
	__asm
	{
		mov eax, cr0;
		or eax, 0x10000; // WP位置1
		mov cr0, eax;
		sti; // 恢复中断
	}
}

// 获取内核基址,大小
VOID GetKernelBase(PDRIVER_OBJECT driver, PVOID *pKrnlBase, PULONG uKrnlImageSize)
{
	PLDR_DATA_TABLE_ENTRY pLdteHead; // 内核模块链表头
	PLDR_DATA_TABLE_ENTRY pLdteCur; // 遍历指针
	UNICODE_STRING usKrnlBaseDllName; // 内核模块名

	RtlInitUnicodeString(&usKrnlBaseDllName,L"ntoskrnl.exe");
	pLdteHead = (PLDR_DATA_TABLE_ENTRY)driver->DriverSection;
	pLdteCur = pLdteHead;
	do 
	{
		PLDR_DATA_TABLE_ENTRY pLdte = CONTAINING_RECORD(pLdteCur, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
		//DbgPrint("DllBase: %p, SizeOfImage: %08X %wZ\n", pLdteCur->DllBase, pLdteCur->SizeOfImage, &(pLdteCur->FullDllName));
		if (RtlCompareUnicodeString(&pLdteCur->BaseDllName, &usKrnlBaseDllName, TRUE) == 0)
		{
			*pKrnlBase = pLdteCur->DllBase;
			*uKrnlImageSize = pLdteCur->SizeOfImage;
			return;
		}
		pLdteCur = (PLDR_DATA_TABLE_ENTRY)pLdteCur->InLoadOrderLinks.Flink;
	} while (pLdteHead != pLdteCur);
	return;
}

// 特征码搜索
PVOID MemorySearch(PVOID bytecode, ULONG bytecodeLen, PVOID pBeginAddress, PVOID pEndAddress)
{
	PVOID pCur = pBeginAddress;
	while (pCur != pEndAddress)
	{
		if (RtlCompareMemory(bytecode,pCur,bytecodeLen) == bytecodeLen)
		{
			return pCur;
		}
		((ULONG)pCur)++;
	}
	return 0;
}

// InlineHook NtQuerySystemInformation
void InlineHookNtQuerySystemInformation()
{	
	// NtQuerySystemInformation 特征码
	ULONG bytecode[] = {
		0x4589c033, 0xdc4589e4, 0x64fc4589 ,0x000124a1,
		0xe8858900, 0x8afffffd, 0x00014080 ,0x90458800
	};
	UCHAR ReplaceByte[5];
	PVOID KrnlBase;
	ULONG KrnlImageSize;

	// 通过特征码获取NtQuerySystemInformation函数地址
	GetKernelBase(g_Driver, &KrnlBase, &KrnlImageSize);	
	NtQuerySystemInformation = (NTQUERYSYSTEMINFORMATION)((ULONG)MemorySearch( \
		bytecode,sizeof(bytecode),KrnlBase,(PVOID)((ULONG)KrnlBase+KrnlImageSize)) - 15);	
	//DbgPrint("%x\n", ((PULONG)NtQuerySystemInformation)[0]);
	// 设置裸函数返回地址
	g_HookRetAddressNtQuerySystemInformation = (ULONG)NtQuerySystemInformation + 5;
	// 计算替换指令
	ReplaceByte[0] = 0xE9;
	*(PULONG)(ReplaceByte + 1) = (ULONG)HbgNtQuerySystemInformation - g_HookRetAddressNtQuerySystemInformation;
	PageProtectOff();
	memcpy(NtQuerySystemInformation, ReplaceByte, 5);
	PageProtectOn();
}

// 卸载钩子
void UnsetInlineHookNtQuerySystemInformation()
{
	UCHAR ReplaceByte[5] = {0x68, 0x10, 0x02, 0x00, 0x00};
	PageProtectOff();
	memcpy(NtQuerySystemInformation, ReplaceByte, 5);
	PageProtectOn();
}

// Hook NtQuerySystemInformation
__declspec(naked) void HbgNtQuerySystemInformation(IN ULONG OpCode, IN OUT PVOID p1, IN OUT PVOID p2, IN OUT PVOID p3)
{
	__asm
	{
		push ebp;
		mov ebp, esp;
		sub esp, 0x50;
	}
	switch (OpCode)
	{
	case OP_CHECK_DEBUG:
		{
			g_IsDebug = CheckProcessDebugged(g_EprocessAddress);
			if (g_IsDebug)
			{
				memset(p1,1,1);
			}
			goto ExitNtQuerySystemInformation;
			break;
		}
	}
	// 正常调用 NtQuerySystemInformation
	__asm
	{
		add esp, 0x50;
		pop ebp;
		push 0x210;
		jmp g_HookRetAddressNtQuerySystemInformation;
	}
ExitNtQuerySystemInformation:
	__asm
	{
		add esp, 0x50;
		pop ebp;
		retn 0x10;
	}
}


---------------------
作者:hambaga
来源:优快云
原文:https://blog.youkuaiyun.com/Kwansy/article/details/109801722
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:优快云,CNBLOG博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值