ExAllocatePoolWithTag

本文深入解析ExAllocatePoolWithTag函数,介绍其如何在Windows操作系统中高效地分配内存,并通过参数控制内存类型、大小及标记。阐述了函数返回值及其注意事项,同时说明了内存分配的策略和优化技巧。

ExAllocatePoolWithTag

The ExAllocatePoolWithTag routine allocates pool memory of the specified type and returns a pointer to the allocated block.

PVOID
ExAllocatePoolWithTag(
IN POOL_TYPE PoolType ,
IN SIZE_T NumberOfBytes ,
IN ULONG Tag
    );

Parameters
PoolType
Specifies the type of pool memory to allocate. Each allocation code path should use a unique pool tag to help debuggers and verifiers identify the code path. You can modify the PoolType value by using a bitwise OR with the POOL_RAISE_IF_ALLOCATION_FAILURE flag. This flag causes an exception to be raised if the request cannot be satisfied.

Similarly, you can modify PoolType by using a bitwise OR with the POOL_COLD_ALLOCATION flag as a hint to the kernel to allocate the memory from pages that are likely to be paged out quickly. To reduce the amount of resident pool memory as much as possible, you should not reference these allocations frequently. The POOL_COLD_ALLOCATION flag is only advisory and is available for Windows XP and later versions of the Windows operating system. For a description of the available pool memory types, see POOL_TYPE .

NumberOfBytes
Specifies the number of bytes to allocate.
Tag
Specifies the pool tag for the allocated memory. Specify the pool tag as a character literal of up to four characters delimited by single quotation marks (for example, 'Tag1'). The string is usually specified in reverse order (for example, '1gaT'). The ASCII value of each character in the tag must be between 0 and 127. Every allocation code path should use a unique pool tag to ensure that debuggers and verifiers identify a distinct allocated block.
Return Value

ExAllocatePoolWithTag returns NULL if there is insufficient memory in the free pool to satisfy the request. Use of POOL_RAISE_IF_ALLOCATION_FAILURE is not recommended because it is costly. Any successful allocation that requests NumberOfBytes > PAGE_SIZE wastes all unused bytes on the last-allocated page. However, on Windows Vista and later, the unused bytes are no longer wasted.

Comments

This routine is used for the general pool allocation of memory.

If NumberOfBytes is PAGE_SIZE or greater, a page-aligned buffer is allocated. Memory allocations of PAGE_SIZE or less are allocated within a page and do not cross page boundaries. Memory allocations of less than PAGE_SIZE are not necessarily page-aligned but are aligned to 8-byte boundaries in 32-bit systems and to 16-byte boundaries in 64-bit systems.

A successful allocation requesting NumberOfBytes < PAGE_SIZE of nonpaged pool gives the caller exactly the number of requested bytes of memory. Any successful allocation that requests NumberOfBytes > PAGE_SIZE wastes all unused bytes on the last-allocated page.

The system associates the pool tag with the allocated memory. Programming tools, such as WinDbg, can display the pool tag associated with each allocated buffer. Gflags, a tool included in Debugging Tools for Windows, turns on a system feature that requests allocation from special pool for a particular pool tag. Poolmon, which is included in the WDK, tracks memory by pool tag.

The value of Tag is stored, and sometimes displayed, in reverse (little-endian) order. For example, if a caller passes 'Fred' as a Tag , it appears as 'derF' in a pool dump and in pool usage tracking in the debugger, and as 0x64657246 in the registry and in tool displays.

The allocated buffer can be freed with either ExFreePool or ExFreePoolWithTag .

The system automatically sets certain standard event objects when the amount of pool (paged or nonpaged) is high or low. Drivers can wait for these events to tune their pool usage. For more information, see Standard Event Objects.

Callers of ExAllocatePoolWithTag must be executing at IRQL <= DISPATCH_LEVEL. A caller executing at DISPATCH_LEVEL must specify a NonPaged Xxx value for PoolType . A caller executing at IRQL <= APC_LEVEL can specify any POOL_TYPE value, but the IRQL and environment must also be considered for determining the page type.

Avoid calling with NumberOfBytes == 0. Doing so will result in pool header wastage.

Warning  Memory that ExAllocatePoolWithTag allocates is uninitialized. A kernel-mode driver must first zero this memory if it is going to make it visible to user-mode software (to avoid leaking potentially privileged contents).

Requirements

IRQL: <= DISPATCH_LEVEL (see Comments section)

Headers: Declared in Wdm.h . Include Wdm.h , Ntddk.h , or Ntifs.h .

#include <ntifs.h> #include <ntddk.h> #include <intrin.h> #include "ptehook.h" #define CR0_WP (1 << 16) #define DRIVER_TAG 'HKOB' #define MAX_G_BIT_RECORDS 128 #define MAX_HOOK_COUNT 64 #define PAGE_ALIGN(va) ((PVOID)((ULONG_PTR)(va) & ~0xFFF)) #define PTE_NX_BIT (1ULL << 63) // 调试打印宏 #define LOG_ERROR(fmt, ...) \ DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] ERROR: " fmt "\n", ##__VA_ARGS__) #define LOG_INFO(fmt, ...) \ DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] INFO: " fmt "\n", ##__VA_ARGS__) #define LOG_TRACE(fmt, ...) \ DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "[PTE_HOOK] TRACE: " fmt "\n", ##__VA_ARGS__) // 修正的跳板指令结构 #pragma pack(push, 1) typedef struct _JMP_ABS { BYTE opcode[6]; // FF 25 00 00 00 00 = jmp [rip+0] ULONG64 address; // 目标地址 (紧跟在指令后) } JMP_ABS, * PJMP_ABS; #pragma pack(pop) static_assert(sizeof(JMP_ABS) == 14, "JMP_ABS size must be 14 bytes"); // 页表结构定义 typedef struct _PAGE_TABLE { UINT64 LineAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 pat : 1; UINT64 global : 1; UINT64 ignored_1 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_2 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PteAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 large_page : 1; UINT64 global : 1; UINT64 ignored_2 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdeAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 ignored_1 : 1; UINT64 page_size : 1; UINT64 ignored_2 : 4; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdpteAddress; UINT64* Pml4Address; BOOLEAN IsLargePage; BOOLEAN Is1GBPage; UINT64 OriginalPte; UINT64 OriginalPde; UINT64 OriginalPdpte; UINT64 OriginalPml4e; HANDLE ProcessId; } PAGE_TABLE, * PPAGE_TABLE; // G位信息记录结构体 typedef struct _G_BIT_INFO { void* AlignAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 large_page : 1; UINT64 global : 1; UINT64 ignored_2 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdeAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 pat : 1; UINT64 global : 1; UINT64 ignored_1 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_2 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PteAddress; BOOLEAN IsLargePage; } G_BIT_INFO, * PG_BIT_INFO; typedef struct _HOOK_INFO { void* OriginalAddress; void* HookAddress; UINT8 OriginalBytes[20]; UINT8 HookBytes[20]; UINT32 HookLength; BOOLEAN IsHooked; HANDLE ProcessId; UINT64 OriginalPteValue; UINT64 HookedPteValue; } HOOK_INFO; class PteHookManager { public: bool fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr); bool fn_remove_hook(HANDLE process_id, void* hook_addr); static PteHookManager* GetInstance(); HOOK_INFO* GetHookInfo() { return m_HookInfo; } char* GetTrampLinePool() { return m_TrampLinePool; } UINT32 GetHookCount() { return m_HookCount; } bool fn_resume_global_bits(void* align_addr); ~PteHookManager(); private: bool WriteTrampolineInstruction(void* trampoline, const JMP_ABS& jmpCmd); bool SetPageExecution(void* address, bool executable); void fn_add_g_bit_info(void* align_addr, void* pde_address, void* pte_address); bool fn_isolation_pagetable(UINT64 cr3_val, void* replace_align_addr, void* split_pde); bool fn_isolation_pages(HANDLE process_id, void* ori_addr); bool fn_split_large_pages(void* in_pde, void* out_pde); NTSTATUS get_page_table(UINT64 cr3, PAGE_TABLE& table); void* fn_pa_to_va(UINT64 pa); UINT64 fn_va_to_pa(void* va); __forceinline KIRQL DisableWriteProtection(); __forceinline void EnableWriteProtection(KIRQL oldIrql); bool ModifyPteAttribute(void* address, UINT64 mask, UINT64 value); void FlushCacheAndTlb(void* address); G_BIT_INFO m_GbitRecords[MAX_G_BIT_RECORDS]; UINT32 m_GbitCount = 0; void* m_PteBase = 0; HOOK_INFO m_HookInfo[MAX_HOOK_COUNT] = { 0 }; DWORD m_HookCount = 0; char* m_TrampLinePool = nullptr; UINT32 m_PoolUsed = 0; static PteHookManager* m_Instance; }; PteHookManager* PteHookManager::m_Instance = nullptr; // 实现部分 __forceinline KIRQL PteHookManager::DisableWriteProtection() { KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); __writecr0(cr0 & ~CR0_WP); // 清除CR0.WP位 _mm_mfence(); return oldIrql; } __forceinline void PteHookManager::EnableWriteProtection(KIRQL oldIrql) { _mm_mfence(); UINT64 cr0 = __readcr0(); __writecr0(cr0 | CR0_WP); // 设置CR0.WP位 KeLowerIrql(oldIrql); } void* PteHookManager::fn_pa_to_va(UINT64 pa) { PHYSICAL_ADDRESS physAddr; physAddr.QuadPart = pa; return MmGetVirtualForPhysical(physAddr); } UINT64 PteHookManager::fn_va_to_pa(void* va) { PHYSICAL_ADDRESS physAddr = MmGetPhysicalAddress(va); return physAddr.QuadPart; } NTSTATUS PteHookManager::get_page_table(UINT64 cr3_val, PAGE_TABLE& table) { UINT64 va = table.LineAddress; UINT64 pml4e_index = (va >> 39) & 0x1FF; UINT64 pdpte_index = (va >> 30) & 0x1FF; UINT64 pde_index = (va >> 21) & 0x1FF; UINT64 pte_index = (va >> 12) & 0x1FF; // PML4 UINT64 pml4_pa = cr3_val & ~0xFFF; UINT64* pml4_va = (UINT64*)fn_pa_to_va(pml4_pa); if (!pml4_va) return STATUS_INVALID_ADDRESS; table.Pml4Address = &pml4_va[pml4e_index]; table.OriginalPml4e = *table.Pml4Address; if (!(table.OriginalPml4e & 1)) return STATUS_ACCESS_VIOLATION; // PDPTE UINT64 pdpte_pa = table.OriginalPml4e & ~0xFFF; UINT64* pdpte_va = (UINT64*)fn_pa_to_va(pdpte_pa); if (!pdpte_va) return STATUS_INVALID_ADDRESS; table.PdpteAddress = (decltype(table.PdpteAddress))&pdpte_va[pdpte_index]; table.OriginalPdpte = table.PdpteAddress->value; table.Is1GBPage = (table.PdpteAddress->flags.page_size) ? TRUE : FALSE; if (!(table.OriginalPdpte & 1)) return STATUS_ACCESS_VIOLATION; if (table.Is1GBPage) return STATUS_SUCCESS; // PDE UINT64 pde_pa = table.OriginalPdpte & ~0xFFF; UINT64* pde_va = (UINT64*)fn_pa_to_va(pde_pa); if (!pde_va) return STATUS_INVALID_ADDRESS; table.PdeAddress = (decltype(table.PdeAddress))&pde_va[pde_index]; table.OriginalPde = table.PdeAddress->value; table.IsLargePage = (table.PdeAddress->flags.large_page) ? TRUE : FALSE; if (!(table.OriginalPde & 1)) return STATUS_ACCESS_VIOLATION; if (table.IsLargePage) return STATUS_SUCCESS; // PTE UINT64 pte_pa = table.OriginalPde & ~0xFFF; UINT64* pte_va = (UINT64*)fn_pa_to_va(pte_pa); if (!pte_va) return STATUS_INVALID_ADDRESS; table.PteAddress = (decltype(table.PteAddress))&pte_va[pte_index]; table.OriginalPte = table.PteAddress->value; if (!(table.OriginalPte & 1)) return STATUS_ACCESS_VIOLATION; LOG_TRACE("Page Table Info: VA=0x%p, PTE=0x%llX", (void*)table.LineAddress, table.OriginalPte); return STATUS_SUCCESS; } bool PteHookManager::fn_split_large_pages(void* in_pde_ptr, void* out_pde_ptr) { auto in_pde = (decltype(PAGE_TABLE::PdeAddress))in_pde_ptr; auto out_pde = (decltype(PAGE_TABLE::PdeAddress))out_pde_ptr; LOG_INFO("Splitting large page: Input PDE=0x%llx", in_pde->value); PHYSICAL_ADDRESS LowAddr = { 0 }, HighAddr = { 0 }; HighAddr.QuadPart = MAXULONG64; auto pt = (decltype(PAGE_TABLE::PteAddress))MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); if (!pt) { LOG_ERROR("Failed to allocate contiguous memory for page splitting"); return false; } UINT64 start_pfn = in_pde->flags.page_frame_number; for (int i = 0; i < 512; i++) { pt[i].value = 0; pt[i].flags.present = 1; pt[i].flags.write = in_pde->flags.write; pt[i].flags.user = in_pde->flags.user; pt[i].flags.write_through = in_pde->flags.write_through; pt[i].flags.cache_disable = in_pde->flags.cache_disable; pt[i].flags.accessed = in_pde->flags.accessed; pt[i].flags.dirty = in_pde->flags.dirty; pt[i].flags.global = 0; pt[i].flags.page_frame_number = start_pfn + i; } out_pde->value = in_pde->value; out_pde->flags.large_page = 0; out_pde->flags.page_frame_number = fn_va_to_pa(pt) >> 12; LOG_INFO("Large page split complete: New PTE table at PA=0x%llx", fn_va_to_pa(pt)); return true; } bool PteHookManager::SetPageExecution(void* address, bool executable) { PMDL pMdl = IoAllocateMdl(address, 1, FALSE, FALSE, NULL); if (!pMdl) { LOG_ERROR("IoAllocateMdl failed"); return false; } bool success = false; __try { MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess); // 获取PTE PT_ENTRY* pte = MmGetMdlPteArray(pMdl); if (pte) { KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); PT_ENTRY oldPte = *pte; // 设置/清除NX位 pte->ExecuteDisable = executable ? 0 : 1; // 刷新TLB __invlpg(address); _mm_mfence(); KeLowerIrql(oldIrql); success = true; LOG_TRACE("PTE modified for 0x%p: Old=0x%llX, New=0x%llX", address, oldPte.Value, pte->Value); } } __except (EXCEPTION_EXECUTE_HANDLER) { LOG_ERROR("Exception modifying PTE"); } MmUnlockPages(pMdl); IoFreeMdl(pMdl); return success; } bool PteHookManager::fn_isolation_pagetable(UINT64 cr3_val, void* replace_align_addr, void* split_pde_ptr) { PHYSICAL_ADDRESS LowAddr = { 0 }, HighAddr = { 0 }; HighAddr.QuadPart = MAXULONG64; LOG_INFO("Isolating page table: CR3=0x%llx, Address=0x%p", cr3_val, replace_align_addr); auto Va4kb = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); auto VaPt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); auto VaPdt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); auto VaPdpt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); if (!VaPt || !Va4kb || !VaPdt || !VaPdpt) { if (VaPt) MmFreeContiguousMemory(VaPt); if (Va4kb) MmFreeContiguousMemory(Va4kb); if (VaPdt) MmFreeContiguousMemory(VaPdt); if (VaPdpt) MmFreeContiguousMemory(VaPdpt); LOG_ERROR("Failed to allocate contiguous memory for isolation"); return false; } PAGE_TABLE Table = { 0 }; Table.LineAddress = (UINT64)replace_align_addr; NTSTATUS status = get_page_table(cr3_val, Table); if (!NT_SUCCESS(status)) { MmFreeContiguousMemory(VaPt); MmFreeContiguousMemory(Va4kb); MmFreeContiguousMemory(VaPdt); MmFreeContiguousMemory(VaPdpt); LOG_ERROR("get_page_table failed with status 0x%X", status); return false; } UINT64 pte_index = (Table.LineAddress >> 12) & 0x1FF; UINT64 pde_index = (Table.LineAddress >> 21) & 0x1FF; UINT64 pdpte_index = (Table.LineAddress >> 30) & 0x1FF; UINT64 pml4e_index = (Table.LineAddress >> 39) & 0x1FF; memcpy(Va4kb, replace_align_addr, PAGE_SIZE); if (Table.IsLargePage && split_pde_ptr) { auto split_pde = (decltype(PAGE_TABLE::PdeAddress))split_pde_ptr; memcpy(VaPt, (void*)(split_pde->flags.page_frame_number << 12), PAGE_SIZE); } else { memcpy(VaPt, (void*)(Table.PdeAddress->flags.page_frame_number << 12), PAGE_SIZE); } memcpy(VaPdt, (void*)(Table.PdpteAddress->flags.page_frame_number << 12), PAGE_SIZE); memcpy(VaPdpt, (void*)(Table.Pml4Address[pml4e_index] & ~0xFFF), PAGE_SIZE); auto new_pte = (decltype(PAGE_TABLE::PteAddress))VaPt; new_pte[pte_index].flags.page_frame_number = fn_va_to_pa(Va4kb) >> 12; auto new_pde = (decltype(PAGE_TABLE::PdeAddress))VaPdt; new_pde[pde_index].value = Table.OriginalPde; new_pde[pde_index].flags.large_page = 0; new_pde[pde_index].flags.page_frame_number = fn_va_to_pa(VaPt) >> 12; auto new_pdpte = (decltype(PAGE_TABLE::PdpteAddress))VaPdpt; new_pdpte[pdpte_index].flags.page_frame_number = fn_va_to_pa(VaPdt) >> 12; auto new_pml4 = (UINT64*)fn_pa_to_va(cr3_val & ~0xFFF); new_pml4[pml4e_index] = (new_pml4[pml4e_index] & 0xFFF) | (fn_va_to_pa(VaPdpt) & ~0xFFF); __invlpg(replace_align_addr); LOG_INFO("Page table isolation complete: New PFN=0x%llx", fn_va_to_pa(Va4kb) >> 12); return true; } bool PteHookManager::fn_isolation_pages(HANDLE process_id, void* ori_addr) { PEPROCESS Process; if (!NT_SUCCESS(PsLookupProcessByProcessId(process_id, &Process))) { LOG_ERROR("PsLookupProcessByProcessId failed"); return false; } LOG_INFO("Isolating pages: PID=%d, Address=0x%p", HandleToUlong(process_id), ori_addr); KAPC_STATE ApcState; KeStackAttachProcess(Process, &ApcState); void* AlignAddr = PAGE_ALIGN(ori_addr); PAGE_TABLE Table = { 0 }; Table.LineAddress = (UINT64)AlignAddr; UINT64 target_cr3 = *(UINT64*)((UCHAR*)Process + 0x28); if (!NT_SUCCESS(get_page_table(target_cr3, Table))) { KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); LOG_ERROR("get_page_table failed"); return false; } bool success = false; decltype(PAGE_TABLE::PdeAddress) split_pde = nullptr; if (Table.IsLargePage) { split_pde = (decltype(PAGE_TABLE::PdeAddress))ExAllocatePoolWithTag(NonPagedPool, sizeof(*split_pde), 'pdeS'); if (!split_pde || !fn_split_large_pages(Table.PdeAddress, split_pde)) { if (split_pde) ExFreePoolWithTag(split_pde, 'pdeS'); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); LOG_ERROR("Splitting large page failed"); return false; } if (Table.PdeAddress->flags.global) { Table.PdeAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, Table.PdeAddress, nullptr); LOG_INFO("Cleared G-bit for large page PDE: 0x%llx", Table.PdeAddress->value); } } else if (Table.PteAddress && Table.PteAddress->flags.global) { Table.PteAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, nullptr, Table.PteAddress); LOG_INFO("Cleared G-bit for PTE: 0x%llx", Table.PteAddress->value); } success = fn_isolation_pagetable(__readcr3(), AlignAddr, split_pde); if (split_pde) ExFreePoolWithTag(split_pde, 'pdeS'); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); if (success) { LOG_INFO("Page isolation successful"); } else { LOG_ERROR("Page isolation failed"); } return success; } bool PteHookManager::WriteTrampolineInstruction(void* trampoline, const JMP_ABS& jmpCmd) { // 确保8字节对齐 if (reinterpret_cast<ULONG_PTR>(trampoline) & 0x7) { LOG_ERROR("Trampoline address not aligned: 0x%p", trampoline); return false; } // 禁用写保护 KIRQL oldIrql = DisableWriteProtection(); // 直接写入内存 RtlCopyMemory(trampoline, &jmpCmd, sizeof(JMP_ABS)); // 刷新缓存 __invlpg(trampoline); _mm_mfence(); // 恢复写保护 EnableWriteProtection(oldIrql); LOG_TRACE("Trampoline instruction written at 0x%p", trampoline); return true; } void PteHookManager::fn_add_g_bit_info(void* align_addr, void* pde_address, void* pte_address) { if (m_GbitCount >= MAX_G_BIT_RECORDS) { LOG_ERROR("Max G-bit records reached"); return; } PG_BIT_INFO record = &m_GbitRecords[m_GbitCount++]; record->AlignAddress = align_addr; record->PdeAddress = (decltype(G_BIT_INFO::PdeAddress))pde_address; record->PteAddress = (decltype(G_BIT_INFO::PteAddress))pte_address; record->IsLargePage = (pde_address != nullptr); LOG_TRACE("Added G-bit record: Address=0x%p, PDE=0x%p, PTE=0x%p", align_addr, pde_address, pte_address); } bool PteHookManager::fn_resume_global_bits(void* align_addr) { KIRQL oldIrql = DisableWriteProtection(); bool found = false; LOG_INFO("Restoring G-bits for address: 0x%p", align_addr); for (UINT32 i = 0; i < m_GbitCount; i++) { PG_BIT_INFO record = &m_GbitRecords[i]; if (align_addr && record->AlignAddress != align_addr) continue; if (record->PteAddress) { record->PteAddress->flags.global = 1; __invlpg(record->AlignAddress); LOG_TRACE("Restored G-bit for PTE: 0x%p", record->AlignAddress); } if (record->PdeAddress) { record->PdeAddress->flags.global = 1; if (record->IsLargePage) { __invlpg(record->AlignAddress); } LOG_TRACE("Restored G-bit for PDE: 0x%p", record->AlignAddress); } found = true; if (align_addr) break; } EnableWriteProtection(oldIrql); if (found) { LOG_INFO("G-bits restoration complete"); } else { LOG_ERROR("No matching G-bit record found"); } return found; } bool PteHookManager::fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr) { if (!ori_addr || !hk_addr || !*ori_addr) { LOG_ERROR("Invalid parameters: ori_addr=%p, hk_addr=%p", ori_addr, hk_addr); return false; } // 分配跳板池(如果尚未分配) if (!m_TrampLinePool) { PHYSICAL_ADDRESS LowAddr = { .QuadPart = 0x100000 }; PHYSICAL_ADDRESS HighAddr = { .QuadPart = ~0ull }; m_TrampLinePool = (char*)MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE * 8, LowAddr, HighAddr, LowAddr, MmNonCached); if (!m_TrampLinePool) { LOG_ERROR("Failed to allocate trampoline pool"); return false; } // 设置跳板池可执行 if (!SetPageExecution(m_TrampLinePool, true)) { MmFreeContiguousMemory(m_TrampLinePool); m_TrampLinePool = nullptr; LOG_ERROR("Failed to set trampoline pool executable"); return false; } LOG_INFO("Trampoline pool allocated: Address=0x%p, Size=%d bytes", m_TrampLinePool, PAGE_SIZE * 8); } // 计算跳板位置(16字节对齐确保RIP相对寻址) void* trampoline = (void*)((ULONG_PTR)(m_TrampLinePool + m_PoolUsed + 15) & ~15); SIZE_T requiredSize = ((char*)trampoline - m_TrampLinePool) + sizeof(JMP_ABS); // 检查跳板池空间 if (requiredSize > PAGE_SIZE * 8) { LOG_ERROR("Trampoline pool out of space: Used=%d, Required=%d", m_PoolUsed, requiredSize); return false; } // 构造跳转指令 JMP_ABS jmpCmd = {}; memcpy(jmpCmd.opcode, "\xFF\x25\x00\x00\x00\x00", 6); // jmp [rip+0] jmpCmd.address = reinterpret_cast<ULONG64>(hk_addr); LOG_INFO("Constructing jump instruction: Target=0x%p, Trampoline=0x%p", hk_addr, trampoline); // 写入跳板指令 if (!WriteTrampolineInstruction(trampoline, jmpCmd)) { return false; } // 设置目标函数不可执行(触发页错误) if (!SetPageExecution(*ori_addr, false)) { LOG_ERROR("Failed to set target function non-executable"); return false; } // 记录Hook信息 bool hookRecorded = false; for (UINT32 i = 0; i < MAX_HOOK_COUNT; i++) { if (!m_HookInfo[i].IsHooked) { m_HookInfo[i].OriginalAddress = *ori_addr; m_HookInfo[i].HookAddress = trampoline; m_HookInfo[i].ProcessId = process_id; m_HookInfo[i].IsHooked = TRUE; m_HookInfo[i].HookLength = sizeof(JMP_ABS); RtlCopyMemory(m_HookInfo[i].HookBytes, &jmpCmd, sizeof(jmpCmd)); // 保存原始PTE值 PAGE_TABLE table = { 0 }; table.LineAddress = (UINT64)*ori_addr; if (NT_SUCCESS(get_page_table(__readcr3(), table))) { m_HookInfo[i].OriginalPteValue = table.OriginalPte; } m_HookCount++; hookRecorded = true; LOG_INFO("Hook recorded #%d: Original=0x%p, Trampoline=0x%p", i, *ori_addr, trampoline); break; } } if (!hookRecorded) { LOG_ERROR("Exceeded max hook count (%d)", MAX_HOOK_COUNT); return false; } // 更新原始地址为跳板地址 *ori_addr = trampoline; m_PoolUsed = (UINT32)requiredSize; LOG_INFO("Hook installed successfully: Trampoline=0x%p -> Hook=0x%p", trampoline, hk_addr); return true; } bool PteHookManager::fn_remove_hook(HANDLE process_id, void* hook_addr) { LOG_INFO("Removing hook: HookAddr=0x%p", hook_addr); for (UINT32 i = 0; i < m_HookCount; i++) { if (m_HookInfo[i].HookAddress == hook_addr && m_HookInfo[i].IsHooked) { LOG_INFO("Found matching hook: OriginalAddr=0x%p", m_HookInfo[i].OriginalAddress); // 恢复目标函数执行权限 SetPageExecution(m_HookInfo[i].OriginalAddress, true); // 恢复原始PTE值 if (m_HookInfo[i].OriginalPteValue) { PAGE_TABLE table = { 0 }; table.LineAddress = (UINT64)m_HookInfo[i].OriginalAddress; if (NT_SUCCESS(get_page_table(__readcr3(), table))) { KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); table.PteAddress->value = m_HookInfo[i].OriginalPteValue; __invlpg(m_HookInfo[i].OriginalAddress); KeLowerIrql(oldIrql); LOG_TRACE("Restored PTE for 0x%p: 0x%llX", m_HookInfo[i].OriginalAddress, m_HookInfo[i].OriginalPteValue); } } m_HookInfo[i].IsHooked = FALSE; LOG_INFO("Hook removed successfully"); return true; } } LOG_ERROR("No matching hook found"); return false; } PteHookManager::~PteHookManager() { if (m_TrampLinePool) { MmFreeContiguousMemory(m_TrampLinePool); m_TrampLinePool = nullptr; LOG_INFO("Trampoline pool freed"); } } PteHookManager* PteHookManager::GetInstance() { if (!m_Instance) { m_Instance = static_cast<PteHookManager*>( ExAllocatePoolWithTag(NonPagedPool, sizeof(PteHookManager), 'tpHk')); if (m_Instance) { RtlZeroMemory(m_Instance, sizeof(PteHookManager)); LOG_INFO("PTE Hook Manager instance created: 0x%p", m_Instance); } else { LOG_ERROR("Failed to create PTE Hook Manager instance"); } } return m_Instance; } // 全局PTE Hook管理器实例 PteHookManager* g_PteHookManager = nullptr; // 目标进程名称 const char target_process_name[] = "oxygen.exe"; // 辅助函数:检查是否为目标进程 BOOLEAN IsTargetProcess(CHAR* imageName) { CHAR currentName[16]; RtlZeroMemory(currentName, sizeof(currentName)); strncpy(currentName, imageName, sizeof(currentName) - 1); return (_stricmp(currentName, target_process_name) == 0); } // 函数类型定义 typedef NTSTATUS(*fn_ObReferenceObjectByHandleWithTag)( HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, ULONG Tag, PVOID* Object, POBJECT_HANDLE_INFORMATION HandleInformation ); fn_ObReferenceObjectByHandleWithTag g_OriginalObReferenceObjectByHandleWithTag = nullptr; // Hook 函数 NTSTATUS MyObReferenceObjectByHandleWithTag( HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, ULONG Tag, PVOID* Object, POBJECT_HANDLE_INFORMATION HandleInformation ) { PEPROCESS currentProcess = PsGetCurrentProcess(); CHAR* imageName = PsGetProcessImageFileName(currentProcess); LOG_INFO("Hook function called by process: %s", imageName); if (IsTargetProcess(imageName)) { LOG_INFO("Access denied for target process: %s", imageName); return STATUS_ACCESS_DENIED; } return g_OriginalObReferenceObjectByHandleWithTag( Handle, DesiredAccess, ObjectType, AccessMode, Tag, Object, HandleInformation ); } NTSTATUS InstallHook(HANDLE targetProcessId) { UNICODE_STRING funcName; RtlInitUnicodeString(&funcName, L"ObReferenceObjectByHandleWithTag"); g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)MmGetSystemRoutineAddress(&funcName); if (!g_OriginalObReferenceObjectByHandleWithTag) { LOG_ERROR("ObReferenceObjectByHandleWithTag not found"); return STATUS_NOT_FOUND; } LOG_INFO("Target function found: 0x%p", g_OriginalObReferenceObjectByHandleWithTag); void* targetFunc = (void*)g_OriginalObReferenceObjectByHandleWithTag; void* hookFunc = (void*)MyObReferenceObjectByHandleWithTag; if (!g_PteHookManager->fn_pte_inline_hook_bp_pg(targetProcessId, &targetFunc, hookFunc)) { LOG_ERROR("PTE Hook installation failed"); return STATUS_UNSUCCESSFUL; } g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)targetFunc; LOG_INFO("Hook installed successfully. Trampoline: 0x%p", targetFunc); return STATUS_SUCCESS; } // 移除 Hook VOID RemoveHook() { if (g_OriginalObReferenceObjectByHandleWithTag && g_PteHookManager) { g_PteHookManager->fn_remove_hook(PsGetCurrentProcessId(), (void*)MyObReferenceObjectByHandleWithTag); } } // 工作线程上下文 typedef struct _HOOK_THREAD_CONTEXT { HANDLE TargetProcessId; } HOOK_THREAD_CONTEXT, * PHOOK_THREAD_CONTEXT; // 工作线程函数 VOID InstallHookWorker(PVOID Context) { PHOOK_THREAD_CONTEXT ctx = (PHOOK_THREAD_CONTEXT)Context; if (ctx) { LOG_INFO("Installing hook for PID: %d", HandleToUlong(ctx->TargetProcessId)); InstallHook(ctx->TargetProcessId); ExFreePoolWithTag(ctx, 'CtxH'); } PsTerminateSystemThread(STATUS_SUCCESS); } // 进程创建回调 VOID ProcessNotifyCallback( _In_ HANDLE ParentId, _In_ HANDLE ProcessId, _In_ BOOLEAN Create ) { UNREFERENCED_PARAMETER(ParentId); if (Create) { PEPROCESS process = NULL; if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &process))) { CHAR* imageName = PsGetProcessImageFileName(process); if (IsTargetProcess(imageName)) { LOG_INFO("Target process created: %s (PID: %d)", imageName, HandleToUlong(ProcessId)); // 创建工作线程上下文 PHOOK_THREAD_CONTEXT ctx = (PHOOK_THREAD_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOK_THREAD_CONTEXT), 'CtxH'); if (ctx) { ctx->TargetProcessId = ProcessId; HANDLE threadHandle; NTSTATUS status = PsCreateSystemThread( &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, InstallHookWorker, ctx ); if (NT_SUCCESS(status)) { ZwClose(threadHandle); LOG_INFO("Worker thread created for hook installation"); } else { ExFreePoolWithTag(ctx, 'CtxH'); LOG_ERROR("Failed to create worker thread: 0x%X", status); } } else { LOG_ERROR("Failed to allocate thread context"); } } ObDereferenceObject(process); } } } // 驱动卸载函数 VOID DriverUnload(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); LOG_INFO("Driver unloading..."); // 移除进程通知回调 PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, TRUE); // 移除Hook RemoveHook(); // 清理PTE Hook资源 if (g_PteHookManager) { LOG_INFO("Cleaning up PTE Hook Manager"); // 恢复所有被修改的G位 g_PteHookManager->fn_resume_global_bits(nullptr); // 移除所有活动的Hook HOOK_INFO* hookInfo = g_PteHookManager->GetHookInfo(); UINT32 hookCount = g_PteHookManager->GetHookCount(); for (UINT32 i = 0; i < hookCount; i++) { if (hookInfo[i].IsHooked) { g_PteHookManager->fn_remove_hook(PsGetCurrentProcessId(), hookInfo[i].HookAddress); } } // 释放管理器实例 ExFreePoolWithTag(g_PteHookManager, 'tpHk'); g_PteHookManager = nullptr; } LOG_INFO("Driver unloaded successfully"); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); LOG_INFO("Driver loading..."); DriverObject->DriverUnload = DriverUnload; g_PteHookManager = PteHookManager::GetInstance(); if (!g_PteHookManager) { LOG_ERROR("Failed to initialize PteHookManager"); return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, FALSE); if (!NT_SUCCESS(status)) { LOG_ERROR("Failed to register process callback: 0x%X", status); return status; } LOG_INFO("Driver loaded successfully"); return STATUS_SUCCESS; } 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0167 "void (*)(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)" 类型的实参与 "PCREATE_PROCESS_NOTIFY_ROUTINE_EX" (aka "void (*)(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)") 类型的形参不兼容 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 898 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0167 "void (*)(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)" 类型的实参与 "PCREATE_PROCESS_NOTIFY_ROUTINE_EX" (aka "void (*)(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)") 类型的形参不兼容 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 941 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2039 "opcode": 不是 "_JMP_ABS" 的成员 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 614 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2059 语法错误:“=” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 192 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "PT_ENTRY" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 328 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "pte" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 328 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "PsGetProcessImageFileName" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 764 把错误都改了严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2065 “m_HookCount”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 648 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C3861 “MmGetMdlPteArray”: 找不到标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 328 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "PsGetProcessImageFileName" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 764 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "oldPte" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 343 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "MmGetMdlPteArray" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 328 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "DWORD" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 192 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "BYTE" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 26 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2238 意外的标记位于“;”之前 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 26 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C7555 使用指定的初始值设定项至少需要“/std:c++20” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 579 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2397 从“unsigned __int64”转换到“LONGLONG”需要收缩转换 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 580 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2065 “PT_ENTRY”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 331 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C3861 “PsGetProcessImageFileName”: 找不到标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 764 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C3646 “opcode”: 未知重写说明符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 26 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2664 “NTSTATUS PsSetCreateProcessNotifyRoutineEx(PCREATE_PROCESS_NOTIFY_ROUTINE_EX,BOOLEAN)”: 无法将参数 1 从“void (__cdecl *)(HANDLE,HANDLE,BOOLEAN)”转换为“PCREATE_PROCESS_NOTIFY_ROUTINE_EX” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 941 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误 C2065 “m_HookCount”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 169 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0167 "void (*)(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)" 类型的实参与 "PCREATE_PROCESS_NOTIFY_ROUTINE_EX" (aka "void (*)(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)") 类型的形参不兼容 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 898 错误 C2338 static_assert failed: 'JMP_ABS size must be 14 bytes' obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 30
06-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值