pat 1051 pop sequence

本文介绍了一种通过模拟入栈和出栈操作来判断给定序列是否能由特定栈操作产生的算法实现。该算法使用了一个辅助栈来跟踪入栈操作,并通过逐个比较目标出栈序列与当前栈顶元素来验证序列的合法性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题对于自己,意义不小,编程能力还是如此之弱!sigh!  用cur标记入栈序列的最大值,逐个扫描出栈序列x,当x与当前栈顶元素不一致(大于或小于)时就将cur++入栈(即使是x大于栈顶元素也将cur++入栈,它必将超过栈容量)。当x与当前栈顶元素一致时,将栈顶元素出栈即可。若栈内元素超过栈容量,则不合法。

AC代码:

#include<iostream>
#include<stack>
using namespace std;

int main()
{
	int n,m,k,i,j,x;
	
	//freopen("C:\\Documents and Settings\\Administrator\\桌面\\input.txt","r",stdin);

	scanf("%d%d%d",&m,&n,&k);
	for(i=0;i<k;i++){
		stack<int> c;
		int cur=1,fg=1;
		for(j=0;j<n;j++){
			scanf("%d",&x);

			if(fg){
				while(c.empty()||c.top()!=x){
					c.push(cur);
					if(c.size()>m){
						fg=0;
						break;
					}
					cur++;
				}
				if(fg&&c.size()>=1&&c.top()==x)
					c.pop();
				
			}
			
		}
		if(fg==0)
			printf("NO\n");
		else printf("YES\n");

	}
	

	return 0;
}


 

#include <ntifs.h> #include <ntddk.h> #include <intrin.h> #include "ptehook.h" #define CR0_WP (1 << 16) HANDLE targetProcessId = NULL; typedef INT(*LDE_DISASM)(PVOID address, INT bits); typedef unsigned long DWORD; typedef unsigned __int64 ULONG64; // 使用WDK标准类型 typedef unsigned char BYTE; typedef LONG NTSTATUS; // 修正后的跳转指令结构 #pragma pack(push, 1) typedef struct _JMP_ABS { BYTE opcode[6]; // FF 25 00 00 00 00 ULONG64 address; // 8字节绝对地址 } JMP_ABS, * PJMP_ABS; #pragma pack(pop) LDE_DISASM lde_disasm; // 初始化引擎 VOID lde_init() { lde_disasm = (LDE_DISASM)ExAllocatePool(NonPagedPool, 12800); memcpy(lde_disasm, szShellCode, 12800); } // 得到完整指令长度,避免截断 ULONG GetFullPatchSize(PUCHAR Address) { ULONG LenCount = 0, Len = 0; // 至少需要14字节 while (LenCount <= 14) { Len = lde_disasm(Address, 64); Address = Address + Len; LenCount = LenCount + Len; } return LenCount; } #define PROCESS_NAME_LENGTH 16 #define DRIVER_TAG 'HKOB' EXTERN_C char* PsGetProcessImageFileName(PEPROCESS process); char target_process_name[] = "oxygen.exe"; 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 = NULL; // PTE Hook Framework #define MAX_G_BIT_RECORDS 128 #define MAX_HOOK_COUNT 64 #define PAGE_ALIGN(va) ((PVOID)((ULONG_PTR)(va) & ~0xFFF)) #define PDPTE_PS_BIT (1 << 7) #define PDE_PS_BIT (1 << 7) #define PTE_NX_BIT (1ULL << 63) #define CACHE_WB (6ULL << 3) // 页表结构定义 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; 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; }*HookedPte; 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; }*HookedPde; } 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); 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); void logger(const char* info, bool is_err, LONG err_code = 0); void PrintPageTableInfo(const PAGE_TABLE& table); void PrintHookInfo(const HOOK_INFO& hookInfo); void PrintGBitInfo(const G_BIT_INFO& gbitInfo); static constexpr SIZE_T MAX_HOOKS = 256; // 根据需求调整 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 & ~0x10000); // 清除CR0.WP位 _mm_mfence(); return oldIrql; } __forceinline void PteHookManager::EnableWriteProtection(KIRQL oldIrql) { _mm_mfence(); UINT64 cr0 = __readcr0(); __writecr0(cr0 | 0x10000); // 设置CR0.WP位 KeLowerIrql(oldIrql); } void PteHookManager::logger(const char* info, bool is_err, LONG err_code) { if (is_err) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] ERROR: %s (0x%X)\n", info, err_code); } else { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] INFO: %s\n", info); } } void PteHookManager::PrintPageTableInfo(const PAGE_TABLE& table) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] Page Table Info for VA: 0x%p\n", (void*)table.LineAddress); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PML4E: 0x%llx (Address: 0x%p)\n", table.OriginalPml4e, table.Pml4Address); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PDPTE: 0x%llx (Address: 0x%p), Is1GBPage: %d\n", table.OriginalPdpte, table.PdpteAddress, table.Is1GBPage); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PDE: 0x%llx (Address: 0x%p), IsLargePage: %d\n", table.OriginalPde, table.PdeAddress, table.IsLargePage); if (!table.IsLargePage && !table.Is1GBPage) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PTE: 0x%llx (Address: 0x%p)\n", table.OriginalPte, table.PteAddress); } } void PteHookManager::PrintHookInfo(const HOOK_INFO& hookInfo) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] Hook Info:\n"); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Original Address: 0x%p\n", hookInfo.OriginalAddress); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Hook Address: 0x%p\n", hookInfo.HookAddress); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Hook Length: %d\n", hookInfo.HookLength); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Is Hooked: %d\n", hookInfo.IsHooked); // 打印原始字节 DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Original Bytes: "); for (UINT32 i = 0; i < sizeof(hookInfo.OriginalBytes); i++) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%02X ", hookInfo.OriginalBytes[i]); } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "\n"); // 打印Hook字节 DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Hook Bytes: "); for (UINT32 i = 0; i < sizeof(hookInfo.HookBytes); i++) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "%02X ", hookInfo.HookBytes[i]); } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "\n"); } void PteHookManager::PrintGBitInfo(const G_BIT_INFO& gbitInfo) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] G-Bit Info:\n"); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " Align Address: 0x%p\n", gbitInfo.AlignAddress); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " IsLargePage: %d\n", gbitInfo.IsLargePage); if (gbitInfo.PdeAddress) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PDE: 0x%llx (Address: 0x%p)\n", gbitInfo.PdeAddress->value, gbitInfo.PdeAddress); } if (gbitInfo.PteAddress) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, " PTE: 0x%llx (Address: 0x%p)\n", gbitInfo.PteAddress->value, gbitInfo.PteAddress); } } 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; // 打印页表信息 PrintPageTableInfo(table); 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; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 正在拆分大页: 输入PDE=0x%llx, 输出PDE=0x%p\n", in_pde->value, out_pde); PHYSICAL_ADDRESS LowAddr = { 0 }, HighAddr = { 0 }; HighAddr.QuadPart = MAXULONG64; auto pt = (decltype(PAGE_TABLE::PteAddress))MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); if (!pt) { logger("分配连续内存失败 (用于拆分大页)", true); 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; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 大页拆分完成: 新PTE表物理地址=0x%llx\n", fn_va_to_pa(pt)); return true; } 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; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 开始隔离页表: CR3=0x%llx, 地址=0x%p\n", 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); logger("分配连续内存失败 (用于隔离页表)", true); 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); logger("获取页表信息失败", true, 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); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 页表隔离完成: 新PFN=0x%llx\n", 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))) { logger("查找进程失败", true); return false; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 开始隔离页面: PID=%d, 地址=0x%p\n", (ULONG)(ULONG_PTR)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); logger("获取目标进程页表失败", true); 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); logger("拆分大页失败", true); return false; } if (Table.PdeAddress->flags.global) { Table.PdeAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, Table.PdeAddress, nullptr); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 清除大页G位: PDE=0x%llx\n", 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); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 清除PTE G位: PTE=0x%llx\n", Table.PteAddress->value); success = fn_isolation_pagetable(__readcr3(), AlignAddr, split_pde); if (split_pde) ExFreePoolWithTag(split_pde, 'pdeS'); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 页表状态: IsLargePage=%d, Is1GBPage=%d\n", Table.IsLargePage, Table.Is1GBPage); if (Table.PteAddress) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] PTE 值: 0x%llx (G位=%d)\n", Table.PteAddress->value, Table.PteAddress->flags.global); } KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); if (success) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 页面隔离成功\n"); } else { logger("页面隔离失败", true); } return success; } KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); return true; } bool PteHookManager::WriteTrampolineInstruction(void* trampoline, const JMP_ABS& jmpCmd) { if (!MmIsAddressValid(trampoline)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 错误: 内存地址无效 (VA=%p)\n", trampoline); return false; } PHYSICAL_ADDRESS physAddr = MmGetPhysicalAddress(trampoline); if (physAddr.QuadPart == 0) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 错误: 无法获取物理地址 (VA=%p)\n", trampoline); return false; } KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); BOOLEAN wpEnabled = (__readcr0() & CR0_WP); if (wpEnabled) { __writecr0(__readcr0() & ~CR0_WP); _mm_mfence(); } PMDL pMdl = IoAllocateMdl(trampoline, sizeof(JMP_ABS), FALSE, FALSE, NULL); if (!pMdl) { if (wpEnabled) __writecr0(__readcr0() | CR0_WP); KeLowerIrql(oldIrql); return false; } NTSTATUS status = STATUS_SUCCESS; __try { MmBuildMdlForNonPagedPool(pMdl); MmProtectMdlSystemAddress(pMdl, PAGE_READWRITE); // 正确写入 FF25 00000000 和 8字节地址 memcpy(trampoline, jmpCmd.opcode, 6); // FF25 00000000 *(ULONG64*)((BYTE*)trampoline + 6) = jmpCmd.address; // 地址写入 RIP+0 的位置 _mm_sfence(); _mm_clflush(trampoline); _mm_clflush((BYTE*)trampoline + 8); __invlpg(trampoline); _mm_mfence(); } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 异常: 写入跳板失败 (代码: 0x%X)\n", status); } IoFreeMdl(pMdl); if (wpEnabled) { __writecr0(__readcr0() | CR0_WP); _mm_mfence(); } KeLowerIrql(oldIrql); if (!NT_SUCCESS(status)) return false; // 验证写入结果 if (*(USHORT*)trampoline != 0x25FF || *(ULONG64*)((BYTE*)trampoline + 6) != jmpCmd.address) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 验证失败: 跳板内容不匹配\n" " 预期: FF25 [%p]\n" " 实际: %02X%02X %02X%02X%02X%02X [%p]\n", jmpCmd.address, ((BYTE*)trampoline)[0], ((BYTE*)trampoline)[1], ((BYTE*)trampoline)[2], ((BYTE*)trampoline)[3], ((BYTE*)trampoline)[4], ((BYTE*)trampoline)[5], *(ULONG64*)((BYTE*)trampoline + 6)); return false; } return true; } bool PteHookManager::fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr) { // [1] 页表隔离 if (!fn_isolation_pages(process_id, *ori_addr)) { return false; } // [2] 获取目标进程上下文 PEPROCESS targetProcess; if (!NT_SUCCESS(PsLookupProcessByProcessId(process_id, &targetProcess))) { return false; } KAPC_STATE apcState; KeStackAttachProcess(targetProcess, &apcState); // [3] 构造跳转指令 JMP_ABS jmpCmd = {}; memcpy(jmpCmd.opcode, "\xFF\x25\x00\x00\x00\x00", 6); // FF25 00000000 jmpCmd.address = reinterpret_cast<ULONG64>(hk_addr); // [4] 直接写入被隔离页 void* targetFunc = *ori_addr; bool success = false; // 禁用写保护 KIRQL oldIrql = DisableWriteProtection(); __try { // 保存原始指令 (用于卸载) RtlCopyMemory(m_HookInfo[m_HookCount].OriginalBytes, targetFunc, sizeof(jmpCmd)); // 写入跳转指令到隔离页 memcpy(targetFunc, &jmpCmd, 6); *(ULONG64*)((BYTE*)targetFunc + 6) = jmpCmd.address; // 刷新缓存 _mm_sfence(); _mm_clflush(targetFunc); __invlpg(targetFunc); _mm_mfence(); success = true; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 直接写入隔离页成功: VA=%p -> Hook=%p\n", targetFunc, hk_addr); } __except (EXCEPTION_EXECUTE_HANDLER) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 写入隔离页异常: 0x%X\n", GetExceptionCode()); } // 恢复写保护 EnableWriteProtection(oldIrql); // [5] 记录Hook信息 if (success) { m_HookInfo[m_HookCount].OriginalAddress = targetFunc; m_HookInfo[m_HookCount].HookAddress = hk_addr; m_HookInfo[m_HookCount].ProcessId = process_id; m_HookInfo[m_HookCount].IsHooked = TRUE; m_HookCount++; } // [6] 清理 KeUnstackDetachProcess(&apcState); ObDereferenceObject(targetProcess); return success; } // 析构函数清理资源 PteHookManager::~PteHookManager() { if (m_TrampLinePool) { MmFreeContiguousMemory(m_TrampLinePool); m_TrampLinePool = nullptr; } } bool PteHookManager::fn_remove_hook(HANDLE process_id, void* hook_addr) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 尝试移除Hook: Hook地址=0x%p\n", hook_addr); for (UINT32 i = 0; i < m_HookCount; i++) { if (m_HookInfo[i].HookAddress == hook_addr && m_HookInfo[i].IsHooked) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 找到匹配的Hook: 原始地址=0x%p\n", m_HookInfo[i].OriginalAddress); KIRQL oldIrql = DisableWriteProtection(); memcpy(m_HookInfo[i].OriginalAddress, m_HookInfo[i].OriginalBytes, sizeof(m_HookInfo[i].OriginalBytes)); EnableWriteProtection(oldIrql); m_HookInfo[i].IsHooked = FALSE; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] Hook已成功移除\n"); return true; } } logger("未找到匹配的Hook", true); return false; } void PteHookManager::fn_add_g_bit_info(void* align_addr, void* pde_address, void* pte_address) { if (m_GbitCount >= MAX_G_BIT_RECORDS) { logger("达到最大G位记录数量限制", true); 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 && ((decltype(PAGE_TABLE::PdeAddress))pde_address)->flags.large_page); // 打印G位信息 PrintGBitInfo(*record); } bool PteHookManager::fn_resume_global_bits(void* align_addr) { KIRQL oldIrql = DisableWriteProtection(); bool found = false; DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_INFO_LEVEL, "[PTE_HOOK] 开始恢复G位: 对齐地址=0x%p\n", 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); DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_INFO_LEVEL, " 恢复PTE G位: PTE=0x%llx, 地址=0x%p\n", record->PteAddress->value, record->AlignAddress); } if (record->PdeAddress) { record->PdeAddress->flags.global = 1; if (record->IsLargePage) { __invlpg(record->AlignAddress); } DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_INFO_LEVEL, " 恢复PDE G位: PDE=0x%llx, 地址=0x%p, 大页=%d\n", record->PdeAddress->value, record->AlignAddress, record->IsLargePage); } found = true; if (align_addr) break; } EnableWriteProtection(oldIrql); if (found) { DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_INFO_LEVEL, "[PTE_HOOK] G位恢复完成\n"); } else { logger("未找到匹配的G位记录", true); } return found; } PteHookManager* PteHookManager::GetInstance() { if (!m_Instance) { m_Instance = static_cast<PteHookManager*>( ExAllocatePoolWithTag(NonPagedPool, sizeof(PteHookManager), 'tpHk')); if (m_Instance) { RtlZeroMemory(m_Instance, sizeof(PteHookManager)); DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_INFO_LEVEL, "[PTE_HOOK] PTE Hook管理器实例已创建: 地址=0x%p\n", m_Instance); } else { DbgPrintEx(DPFLTR_ERROR_LEVEL, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] 创建PTE Hook管理器实例失败\n"); } } return m_Instance; } // 全局PTE Hook管理器实例 PteHookManager* g_PteHookManager = nullptr; // 辅助函数:检查是否为目标进程 BOOLEAN IsTargetProcess(CHAR* imageName) { CHAR currentName[16]; // 复制到本地缓冲区并确保 NULL 终止 RtlCopyMemory(currentName, imageName, 16); currentName[15] = '\0'; // 确保终止 // 修剪尾部空格 for (int i = 15; i >= 0; i--) { if (currentName[i] == ' ') currentName[i] = '\0'; else if (currentName[i] != '\0') break; } return (strcmp(currentName, target_process_name) == 0); } // 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); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[!] [HookFunction] 进入 Hook 函数! 当前进程: %s\n", imageName); __debugbreak(); // 强制中断,确认是否执行到这里 if (IsTargetProcess(imageName)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[!] [HookFunction] 拒绝访问目标进程 PID=%d\n", HandleToULong(PsGetCurrentProcessId())); return STATUS_ACCESS_DENIED; } return g_OriginalObReferenceObjectByHandleWithTag( Handle, DesiredAccess, ObjectType, AccessMode, Tag, Object, HandleInformation ); } NTSTATUS InstallHook() { UNICODE_STRING funcName; RtlInitUnicodeString(&funcName, L"ObReferenceObjectByHandleWithTag"); g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)MmGetSystemRoutineAddress(&funcName); if (!g_OriginalObReferenceObjectByHandleWithTag) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] [InstallHook] 找不到 ObReferenceObjectByHandleWithTag\n"); return STATUS_NOT_FOUND; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [InstallHook] 找到目标函数地址: %p\n", g_OriginalObReferenceObjectByHandleWithTag); void* targetFunc = (void*)g_OriginalObReferenceObjectByHandleWithTag; void* hookFunc = (void*)MyObReferenceObjectByHandleWithTag; if (!g_PteHookManager->fn_pte_inline_hook_bp_pg(targetProcessId, &targetFunc, hookFunc)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] [InstallHook] PTE Hook 安装失败\n"); return STATUS_UNSUCCESSFUL; } g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)targetFunc; DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [InstallHook] Hook 成功安装. 跳板地址: %p\n", targetFunc); __debugbreak(); // 强制中断,验证是否执行到这里 return STATUS_SUCCESS; } // 移除 Hook VOID RemoveHook() { if (g_OriginalObReferenceObjectByHandleWithTag && g_PteHookManager) { g_PteHookManager->fn_remove_hook(PsGetCurrentProcessId(), (void*)MyObReferenceObjectByHandleWithTag); } } // 工作线程函数 VOID InstallHookWorker(PVOID Context) { targetProcessId = (HANDLE)Context; DbgPrint("[+] Worker thread started for hook installation on PID: %d\n", HandleToULong(targetProcessId)); InstallHook(); 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); CHAR currentName[16]; RtlCopyMemory(currentName, imageName, 16); currentName[15] = '\0'; for (int i = 15; i >= 0; i--) { if (currentName[i] == ' ') currentName[i] = '\0'; else if (currentName[i] != '\0') break; } if (strcmp(currentName, target_process_name) == 0) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [ProcessNotifyCallback] 目标进程 %s 创建 (PID: %d)\n", currentName, HandleToULong(ProcessId)); HANDLE threadHandle; NTSTATUS status = PsCreateSystemThread( &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, InstallHookWorker, (PVOID)ProcessId // 关键:传递目标进程ID ); if (NT_SUCCESS(status)) { ZwClose(threadHandle); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [ProcessNotifyCallback] 工作线程已创建\n"); } else { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] [ProcessNotifyCallback] 创建线程失败: 0x%X\n", status); } } ObDereferenceObject(process); } } } // 驱动卸载函数 VOID DriverUnload(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); DbgPrint("[+] Driver unloading...\n"); // 移除进程通知回调 PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyCallback, TRUE); // 移除Hook RemoveHook(); // 清理PTE Hook资源 if (g_PteHookManager) { DbgPrint("[PTE_HOOK] Cleaning up PTE...\n"); // 恢复所有被修改的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); } } // 释放跳板池内存 char* trampLinePool = g_PteHookManager->GetTrampLinePool(); if (trampLinePool) { ExFreePoolWithTag(trampLinePool, 'JmpP'); } // 释放管理器实例 ExFreePoolWithTag(g_PteHookManager, 'tpHk'); g_PteHookManager = nullptr; } DbgPrint("[+] Driver unloaded successfully\n"); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [DriverEntry] 驱动加载开始\n"); DriverObject->DriverUnload = DriverUnload; g_PteHookManager = PteHookManager::GetInstance(); if (!g_PteHookManager) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] [DriverEntry] 初始化 PteHookManager 失败\n"); return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)ProcessNotifyCallback, FALSE); if (!NT_SUCCESS(status)) { DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[-] [DriverEntry] 注册进程通知失败 (0x%X)\n", status); return status; } DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[+] [DriverEntry] 驱动加载成功\n"); return STATUS_SUCCESS; } 现在这个代码能正常运行吗?我这个页表隔离分割大页的原理是什么?分割之后影响原虚拟地址吗?
最新发布
06-26
<think>我们分析一下代码,特别是页表隔离和分割大页的部分。1.页表隔离(fn_isolation_pages)的流程:-首先通过PsLookupProcessByProcessId获取目标进程的PEPROCESS。-然后通过KeStackAttachProcess附加到目标进程的地址空间。-获取目标进程的CR3(通过PEPROCESS+0x28,这是x64系统的结构偏移,但请注意不同系统版本可能偏移不同,这里存在潜在问题)。-调用get_page_table获取目标地址的页表信息(PML4E,PDPTE,PDE,PTE)。-如果目标地址处于大页(2MB)中,则调用fn_split_large_pages将大页拆分为4KB的小页。-清除大页的G位(全局位),并记录清除的信息(以便恢复)。-然后调用fn_isolation_pagetable进行页表隔离(但注意,在非大页情况下,如果PTE的G位被清除,也会调用fn_isolation_pagetable)。2.分割大页(fn_split_large_pages)的原理:-大页(2MB)是指一个PDE直接指向一个2MB的物理页,没有下一级PTE。-分割大页的目的是将其拆分为512个4KB的小页,这样我们就可以单独修改其中一页(目标地址所在的页)而不影响其他页。-具体步骤:a.分配一个4KB的连续内存(用作新的PTE表)。b.将原来大页的2MB物理页拆分为512个4KB的物理页,并在新的PTE表中建立512个PTE项,每个指向4KB的物理页。c.修改原来的PDE,使其指向新分配的PTE表(即不再是大页,而是指向PTE表),并设置相应的标志位(清除大页标志)。3.分割大页后对原虚拟地址的影响:-分割大页后,原来大页对应的虚拟地址范围(2MB)现在被拆分为512个4KB的小页,每个小页都有独立的PTE项。-因此,原来的虚拟地址仍然有效,只是现在它对应的物理页是原来大页物理地址范围内的一个4KB页。-也就是说,分割后,原来的虚拟地址映射的物理地址不变(只是从一个大页中的一个地址变成了一个小页中的同一个地址),所以不会影响原虚拟地址的访问。4.页表隔离(fn_isolation_pagetable)的作用:-在分割大页后,我们还需要将目标页(我们想要修改的4KB页)进行隔离,即创建一个新的物理页,并将原内容复制过去,然后修改页表指向这个新的物理页。-这样,我们修改这个新物理页的内容(比如写入Hook跳转指令)就不会影响其他使用原来物理页的进程(因为其他进程仍然映射到原来的物理页)。-具体步骤:a.分配4个4KB的连续内存(Va4kb,VaPt,VaPdt,VaPdpt),分别用于:-Va4kb:存放目标页(4KB)的副本。-VaPt:存放新的PTE表(512个PTE项)。-VaPdt:存放新的PDE表(512个PDE项)。-VaPdpt:存放新的PDPTE表(512个PDPTE项)。b.复制原目标页的内容到Va4kb。c.复制原PTE表到VaPt(如果是大页分割后,则复制分割后的PTE表,否则复制原来的PTE表)。d.复制原PDE表到VaPdt,复制原PDPTE表到VaPdpt。e.修改VaPt中目标页对应的PTE项,使其指向Va4kb的物理地址(即新分配的物理页)。f.修改VaPdt中对应的PDE项,使其指向VaPt(新的PTE表)的物理地址。g.修改VaPdpt中对应的PDPTE项,使其指向VaPdt(新的PDE表)的物理地址。h.修改PML4E,使其指向VaPdpt(新的PDPTE表)的物理地址。i.刷新TLB(__invlpg)。5.问题:在fn_isolation_pages函数中,对于大页的情况,我们只进行了分割和清除G位,但没有调用fn_isolation_pagetable?实际上,在代码中,大页分割后并没有调用fn_isolation_pagetable,而只有在非大页且清除PTE的G位后才调用。这样可能导致大页分割后并没有进行页表隔离,那么后续直接写入目标地址仍然会影响其他进程(因为其他进程可能还映射到同一个物理页)。6.因此,我们需要确保在分割大页后也要进行页表隔离(即调用fn_isolation_pagetable)。修改fn_isolation_pages函数,在大页分割后也进行隔离。7.另外,在fn_isolation_pages函数中,分割大页后,我们清除了大页的G位,然后记录,但并没有进行隔离。所以我们需要在大页分割后也调用fn_isolation_pagetable。8.修改建议:在fn_isolation_pages函数中,处理大页分割后的部分,增加调用fn_isolation_pagetable的代码。9.另外,在fn_isolation_pages函数中,分割大页后,我们使用了一个split_pde指针,这个指针是在非分页池中分配的,并在隔离后释放。但是在调用fn_isolation_pagetable时,我们传递了这个split_pde指针,而在隔离函数中,它被用来复制PTE表。但是,注意在分割大页后,原来的PDE已经被修改(指向了新分配的PTE表),所以我们应该使用这个新的PDE(即split_pde)来进行隔离操作。10.修改后的fn_isolation_pages函数(大页部分):if(Table.IsLargePage){//分割大页...//清除G位并记录...//进行页表隔离success=fn_isolation_pagetable(target_cr3,AlignAddr,split_pde);//释放split_pde?注意在fn_isolation_pagetable中并没有释放,所以我们在隔离后释放ExFreePoolWithTag(split_pde,'pdeS');//然后根据success返回}11.但是,在现有的代码中,大页分割后并没有调用fn_isolation_pagetable,所以我们需要修改代码,确保大页分割后也进行隔离。12.再看非大页的情况(PTE的G位清除后),调用了fn_isolation_pagetable,但是传入的CR3是__readcr3(),这是当前系统线程的CR3(即系统进程的CR3),而不是目标进程的CR3。这显然是个错误,应该使用目标进程的CR3(target_cr3)。13.因此,在非大页情况下,调用fn_isolation_pagetable时,第一个参数应该是target_cr3,而不是__readcr3()。14.总结问题:-大页分割后没有进行页表隔离(需要增加调用fn_isolation_pagetable)。-非大页情况下调用fn_isolation_pagetable时使用了错误的CR3(当前进程的CR3而不是目标进程的CR3)。15.修改代码:在fn_isolation_pages函数中,大页分割后的部分:if(Table.IsLargePage){//...分割大页//清除G位Table.PdeAddress->flags.global=0;fn_add_g_bit_info(AlignAddr,Table.PdeAddress,nullptr);//进行页表隔离,使用目标进程的CR3(target_cr3)和分割后的PDE(split_pde)success=fn_isolation_pagetable(target_cr3,AlignAddr,split_pde);ExFreePoolWithTag(split_pde,'pdeS');//释放split_pdeif(!success){KeUnstackDetachProcess(&ApcState);ObDereferenceObject(Process);logger("页表隔离失败",true);returnfalse;}}在非大页情况下:elseif(Table.PteAddress&&Table.PteAddress->flags.global){Table.PteAddress->flags.global=0;fn_add_g_bit_info(AlignAddr,nullptr,Table.PteAddress);//使用目标进程的CR3(target_cr3)进行隔离,split_pde为nullptr(因为不是大页)success=fn_isolation_pagetable(target_cr3,AlignAddr,nullptr);//注意:这里没有split_pde需要释放}16.另外,在fn_isolation_pages函数的最后,我们返回true,但是在大页分割并隔离后,我们并没有返回,而是继续执行。所以我们需要在成功隔离后返回。17.修改后的fn_isolation_pages函数(大页和非大页部分)应返回隔离操作的结果。18.另外,在fn_isolation_pages函数中,大页分割并隔离后,我们是否还需要做其他事情?然后我们继续执行到函数末尾?注意,在大页分割并隔离后,我们释放了split_pde,然后解除了附加并返回。19.但是,在非大页情况下,如果进行了隔离(清除了PTE的G位并调用了隔离函数),那么我们在隔离后解除了附加并返回了隔离的结果(success)。而在大页情况下,我们也应该做同样的处理:在隔离后解除附加并返回。20.因此,修改后的代码结构:if(Table.IsLargePage){//分割大页//清除G位//调用隔离函数//释放split_pde//解除附加并返回隔离结果}elseif(Table.PteAddress&&Table.PteAddress->flags.global){//清除PTE的G位//调用隔离函数//解除附加并返回隔离结果}//如果没有进行隔离(比如不是大页且PTE没有G位),则直接解除附加并返回true21.因此,我们需要调整代码,将大页和非大页隔离后的返回提前。22.修改后的fn_isolation_pages函数(仅关键部分):if(Table.IsLargePage){//分割大页split_pde=...;if(!split_pde||!fn_split_large_pages(Table.PdeAddress,split_pde)){//失败处理}//清除G位Table.PdeAddress->flags.global=0;fn_add_g_bit_info(AlignAddr,Table.PdeAddress,nullptr);//进行隔离success=fn_isolation_pagetable(target_cr3,AlignAddr,split_pde);ExFreePoolWithTag(split_pde,'pdeS');KeUnstackDetachProcess(&ApcState);ObDereferenceObject(Process);returnsuccess;}elseif(Table.PteAddress&&Table.PteAddress->flags.global){Table.PteAddress->flags.global=0;fn_add_g_bit_info(AlignAddr,nullptr,Table.PteAddress);success=fn_isolation_pagetable(target_cr3,AlignAddr,nullptr);KeUnstackDetachProcess(&ApcState);ObDereferenceObject(Process);returnsuccess;}//其他情况(比如不需要隔离)直接返回trueKeUnstackDetachProcess(&ApcState);ObDereferenceObject(Process);returntrue;23.这样修改后,确保了大页和非大页在隔离操作后直接返回,避免继续执行。24.关于分割大页是否影响原虚拟地址:不会影响,因为分割后虚拟地址映射的物理地址不变,只是页表结构更细化了(从大页变成小页)。25.最后,在fn_pte_inline_hook_bp_pg函数中,我们调用了fn_isolation_pages后,附加到目标进程,然后直接写入目标地址(隔离后的地址)。由于隔离后,目标地址对应的物理页已经是独立的(复制出来的新页),所以写入不会影响其他进程。26.但是,在写入之前,我们还需要确保该页是可写的。在隔离页表的时候,我们并没有设置页的写标志。在隔离过程中,我们复制了页表项,所以原来的标志位被保留。如果原来的页是可写的,那么隔离后的页也是可写的。但是,如果原来的页不可写,我们需要在隔离后设置写标志?不过,我们是要写入这个页,所以必须确保它是可写的。27.在隔离函数(fn_isolation_pagetable)中,我们只是修改了物理页号(指向新分配的页),并没有修改标志位。所以标志位还是原来的。因此,在写入之前,我们可能需要检查并设置写标志位。不过,在Hook的场景中,通常目标函数在可执行页中,可能没有写标志。所以我们需要在隔离后修改PTE的写标志位。28.可以在隔离函数中,在设置新PTE项的时候,设置可写标志。或者在fn_pte_inline_hook_bp_pg函数中,写入之前修改PTE的写标志。29.建议在隔离函数中,当设置新的PTE项时,设置可写标志(并保留其他标志)。例如:new_pte[pte_index].flags.write=1;30.但是,在fn_isolation_pagetable函数中,我们修改PTE项的地方:new_pte[pte_index].flags.page_frame_number=fn_va_to_pa(Va4kb)>>12;这里只修改了页帧号,没有修改标志位。所以我们需要将原来的标志位(除了G位)复制过来,并设置写标志位。31.在fn_isolation_pagetable函数中,我们并没有记录原来的PTE标志(除了页帧号)。所以我们需要修改。32.由于时间关系,这个问题可能比较复杂,我们暂时不深入。但请注意,如果目标页原本不可写,那么隔离后仍然不可写,导致写入失败。33.所以,在fn_pte_inline_hook_bp_pg函数中,写入之前,我们需要确保目标页是可写的。如果不是,则修改PTE使其可写(并记录原始权限,以便恢复)。34.因此,在fn_pte_inline_hook_bp_pg函数中,写入之前,可以添加如下代码://获取目标地址的PTE//如果不可写,则设置可写标志,并记录原始权限35.但注意,我们刚刚进行了隔离,所以目标地址的PTE在目标进程的页表中。我们可以通过get_page_table函数再次获取(但效率不高),或者我们在隔离函数中返回新的PTE指针。36.考虑到复杂度,我们暂时假设隔离后目标页是可写的(因为隔离时我们复制了原来的标志,而原来的标志可能包括写标志)。如果目标函数在可执行代码页,通常没有写标志,所以我们需要修改。37.一个简单的做法:在隔离函数中,当我们设置新的PTE项时,强制设置写标志:new_pte[pte_index].value=Table.OriginalPte;//恢复原来的PTE值new_pte[pte_index].flags.page_frame_number=fn_va_to_pa(Va4kb)>>12;//修改页帧号new_pte[pte_index].flags.write=1;//添加写标志38.这样,隔离后的页就是可写的了。同时,我们保留了原来的其他标志(比如执行标志、用户标志等)。39.在fn_isolation_pagetable函数中,修改PTE项的地方:autonew_pte=(decltype(PAGE_TABLE::PteAddress))VaPt;//保存原来的PTE值,但修改页帧号,并设置写标志new_pte[pte_index].value=Table.OriginalPte;//先恢复原来的值new_pte[pte_index].flags.page_frame_number=fn_va_to_pa(Va4kb)>>12;new_pte[pte_index].flags.write=1;//确保可写40.注意:如果原来就是大页,那么Table.OriginalPte是无效的(因为大页没有PTE),那么我们需要使用原来的PDE来构造一个PTE?但是在大页分割后,我们已经有了PTE表,所以Table.OriginalPte是有效的(在分割大页时,我们构造了PTE表,并复制了原来大页的标志,除了G位被清除)。41.因此,在fn_isolation_pagetable函数中,我们使用Table.OriginalPte是合理的(对于大页分割后,这个OriginalPte是原来大页分割后对应小页的PTE值)。42.但是,在get_page_table函数中,如果是大页,它不会获取PTE(直接返回)。所以我们在fn_isolation_pages函数中分割大页后,在调用fn_isolation_pagetable之前,应该重新获取页表信息(以得到PTE)?或者,在分割大页后,我们已经有新的PTE表,那么我们可以直接设置Table.OriginalPte为新的PTE项的值。43.由于时间有限,这个修改可能比较繁琐。我们暂时不修改,但请注意,如果目标页不可写,那么写入会失败。因此,在写入前,我们可以在fn_pte_inline_hook_bp_pg函数中修改页的写权限。44.在fn_pte_inline_hook_bp_pg函数中,附加到目标进程后,写入之前://获取目标地址的PTEPAGE_TABLEtable={0};table.LineAddress=(UINT64)targetFunc;NTSTATUSstatus=get_page_table(target_cr3,table);if(!NT_SUCCESS(status)){//错误处理}//保存原始的PTE权限(用于卸载时恢复)m_HookInfo[m_HookCount].OriginalPteValue=table.PteAddress->value;//如果不可写,则设置可写标志if(!table.PteAddress->flags.write){KIRQLoldIrql=DisableWriteProtection();table.PteAddress->flags.write=1;__invlpg(targetFunc);EnableWriteProtection(oldIrql);}45.卸载Hook时,恢复原始权限。46.但是,由于我们进行了页表隔离,目标地址的PTE已经被修改(指向了新的物理页),所以这里获取的PTE就是隔离后的PTE。隔离后的PTE我们是否设置了可写?在隔离函数中,我们直接复制了原来的PTE值(Table.OriginalPte)到新的PTE项,然后修改了页帧号和写标志(按照第37点建议修改)。所以如果按照第37点修改了隔离函数,那么新的PTE已经是可写的,这里就不需要再修改。47.综上所述,我们有两个关键修改点:a.在fn_isolation_pages函数中,确保大页分割后进行页表隔离,并且非大页情况下使用正确的CR3(目标进程的CR3)。b.在隔离函数(fn_isolation_pagetable)中,设置新PTE项时,确保设置写标志(以便后续写入)。48.另外,在非大页情况下,我们调用fn_isolation_pagetable时传入的split_pde参数为nullptr,而在隔离函数中,对于非大页,我们使用的是Table.PdeAddress->flags.page_frame_number来复制原来的PDE表。所以没有问题。49.最后,由于代码较长,修改需要谨慎。建议先测试,用WinDbg调试。50.关于代码是否能正常运行:按照上述分析,存在两个问题(大页未隔离和CR3错误),需要修改。修改后,基本原理是正确的。51.另外,在DriverEntry中,我们注册了进程通知回调,当目标进程创建时,会创建工作线程去安装Hook。在安装Hook时,我们通过PTEHook修改了目标进程中的内核函数ObReferenceObjectByHandleWithTag。但是注意,目标进程可能不是当前进程,而我们在InstallHookWorker中调用InstallHook时,是在系统线程的上下文(没有附加到目标进程),但是在fn_pte_inline_hook_bp_pg函数中,我们附加到了目标进程,所以写入目标地址是在目标进程的地址空间进行的。52.因此,如果上述页表隔离和写入操作都正确,那么Hook应该能生效。53.但是,请注意,ObReferenceObjectByHandleWithTag是一个系统服务,可能被多个进程调用。我们Hook后,在MyObReferenceObjectByHandleWithTag中,我们判断如果是目标进程(比如oxygen.exe)则拒绝访问。其他进程则正常调用。54.最后,由于内核代码的复杂性,建议使用虚拟机进行测试,并准备好内核调试工具(WinDbg)。55.由于篇幅限制,这里不提供完整的修改代码,但给出了需要修改的关键点。请根据上述分析修改代码。56.如果测试中遇到问题,请检查:-页表隔离是否成功(通过WinDbg查看目标地址的PTE)-写入的跳转指令是否正确(用WinDbg查看目标地址的指令)-是否触发了异常(DbgPrint输出)57.由于代码中使用了大量的DbgPrint,可以通过调试信息来追踪执行流程。58.希望这些分析能帮助你完善代码。</think>### 代码分析与页表隔离原理 #### 当前代码状态分析 你的代码整体结构良好,但在页表隔离和Hook安装逻辑上存在几个关键问题: 1. **页表隔离流程不完整**: - 大页(2MB)分割后没有进行页表隔离 - 非大页情况下使用了错误的CR3(当前进程而非目标进程) 2. **Hook安装逻辑问题**: - 隔离后没有确保目标页可写 - 没有正确处理原始指令的保存 3. **潜在稳定性风险**: - 缺少对目标地址可写性的检查 - 没有处理页表项的其他标志位(如NX位) #### 页表隔离原理(大页分割) 大页分割是页表隔离的关键步骤,原理如下: ```mermaid graph LR A[2MB大页] --> B[分割为512个4KB页] B --> C[创建新的PTE表] C --> D[修改PDE指向新PTE表] D --> E[隔离目标页] ``` 1. **为什么需要分割大页**: - 大页(2MB)映射了整个2MB物理内存区域 - 要单独修改其中某个4KB页面,需要将其拆分为小页 - 分割后每个4KB页有自己的PTE项,可独立修改 2. **分割后对虚拟地址的影响**: - ✅ 虚拟地址保持不变 - ✅ 物理地址映射保持不变 - ✅ 只是页表结构更细化(PDE → PTE) - ❗ 但原大页的全局性(G位)被清除,需要记录以便恢复 #### 关键问题修复 以下是需要修改的关键部分: ##### 1. 修复页表隔离流程(`fn_isolation_pages`) ```cpp bool PteHookManager::fn_isolation_pages(HANDLE process_id, void* ori_addr) { // ... [原有代码] ... if (Table.IsLargePage) { // ... [分割大页] ... // 清除G位并记录 Table.PdeAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, Table.PdeAddress, nullptr); // 关键修复:大页分割后进行隔离 success = fn_isolation_pagetable(target_cr3, AlignAddr, split_pde); ExFreePoolWithTag(split_pde, 'pdeS'); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); return success; // 明确返回隔离结果 } else if (Table.PteAddress && Table.PteAddress->flags.global) { Table.PteAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, nullptr, Table.PteAddress); // 关键修复:使用目标进程的CR3 success = fn_isolation_pagetable(target_cr3, AlignAddr, nullptr); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); return success; // 明确返回隔离结果 } // ... [其他情况] ... } ``` ##### 2. 确保隔离页可写(`fn_isolation_pagetable`) ```cpp bool PteHookManager::fn_isolation_pagetable(UINT64 cr3_val, void* replace_align_addr, void* split_pde_ptr) { // ... [原有代码] ... // 修改PTE项时添加写标志 new_pte[pte_index].value = Table.OriginalPte; new_pte[pte_index].flags.page_frame_number = fn_va_to_pa(Va4kb) >> 12; new_pte[pte_index].flags.write = 1; // 确保可写 // ... [其余代码不变] ... } ``` ##### 3. 完善Hook安装(`fn_pte_inline_hook_bp_pg`) ```cpp bool PteHookManager::fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr) { // ... [原有代码] ... __try { // 1. 保存原始指令(完整指令长度) ULONG patchSize = GetFullPatchSize((PUCHAR)targetFunc); RtlCopyMemory(m_HookInfo[m_HookCount].OriginalBytes, targetFunc, patchSize); m_HookInfo[m_HookCount].HookLength = patchSize; // 2. 构造跳转指令 JMP_ABS jmpCmd = { {0xFF, 0x25, 0x00, 0x00, 0x00, 0x00}, (ULONG64)hk_addr }; // 3. 写入跳转指令 memcpy(targetFunc, &jmpCmd, sizeof(jmpCmd)); // 4. 刷新缓存 _mm_sfence(); _mm_clflush(targetFunc); __invlpg(targetFunc); _mm_mfence(); // 5. 保存Hook字节 RtlCopyMemory(m_HookInfo[m_HookCount].HookBytes, &jmpCmd, sizeof(jmpCmd)); success = true; } __except (...) { // ... [异常处理] ... } // ... [其余代码] ... } ``` ### 完整工作流程 ```mermaid sequenceDiagram participant A as 目标进程创建 participant B as 工作线程 participant C as PTE管理器 participant D as 页表隔离 participant E as Hook安装 A->>B: 触发进程回调 B->>C: 调用 fn_pte_inline_hook_bp_pg C->>D: fn_isolation_pages D->>D: 检查页表类型 alt 大页(2MB) D->>D: 分割大页 D->>D: 清除G位 D->>D: 页表隔离 else 小页(4KB) D->>D: 清除G位 D->>D: 页表隔离 end D-->>C: 返回隔离结果 C->>E: 写入跳转指令 E->>E: 刷新缓存 E-->>B: 返回Hook结果 ``` ### 代码能否正常运行? 当前代码在以下条件下**可以运行**: 1. 目标函数在4KB小页中 2. 目标页已设置可写标志 3. 没有触发异常 但**存在风险**: 1. 大页处理不完整(已在上方修复) 2. 缺少对NX(No-Execute)位的处理 3. 多处理器环境下的TLB刷新不完整 #### 推荐改进 1. 添加NX位处理: ```cpp // 在写入跳转前 if (table.PteAddress->flags.execute_disable) { table.PteAddress->flags.execute_disable = 0; __invlpg(targetFunc); } ``` 2. 增强多处理器TLB刷新: ```cpp KeIpiGenericCall([](PVOID context) { _mm_clflush(context); __invlpg(context); return 0; }, targetFunc); ``` 3. 指令长度计算优化: ```cpp ULONG GetFullPatchSize(PUCHAR Address) { ULONG total = 0; while (total < 14) { // 跳转指令需要14字节 ULONG len = lde_disasm(Address + total, 64); if (len == 0) break; // 防止无限循环 total += len; } return total; } ``` ### 总结 你的页表隔离原理正确,但实现需要完善: 1. **大页分割**:将2MB大页拆分为512个4KB页,虚拟地址不变 2. **页表隔离**:创建目标页的副本,修改PTE指向新物理页 3. **Hook安装**:直接在隔离页写入跳转指令 修复上述问题后,代码应能稳定运行。建议在虚拟机环境中详细测试,特别是大页处理场景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值