hook一个内核函数,不修改API对应的物理页,而是把与API相关的物理页拷贝一份(PTE,PDE,PPE,PXE四个页),然后修改目标进程的页表映射到拷贝的物理页,使目标进程对此API的操作与其他进程分离,让对此API的hook不会影响到其他进程
结构体
enum
{
PhyPage,
PT,
PDT,
PPT,
PXT,
};
typedef struct _PAGE_INFO
{
UINT64 PteBase;//页表基址
UINT32 PXEIndex;//线性地址所属的PXE存储在PXT位置的索引
UINT64 Pxe;//线性地址所属的PXE的值
PVOID pPageArray[5];//物理页,PT,PDT,PPT,PXT
}PAGE_INFO,* PPAGE_INFO;
函数
UINT64 GetPTEBase();
UINT64 GetXXXAddress(UINT64 VirtualAddress, UINT64 PTEBase);
VOID CopyPhysicalPage(PVOID DestPage, UINT64 SourcePagePTE);
BOOLEAN InitHookPage(UINT64 VirtualAddress, PPAGE_INFO pPageInfo,CHAR ShellCode[],UINT32 Length);
VOID SetHookPage(UINT64 DirectoryTableBase, PAGE_INFO pPageInfo);
VOID LinkPhysicalPages(PVOID ChildPage, PVOID ParentPage, UINT32 Index, UINT64 Attribute);
详情
驱动加载时调用InitHookPage()初始化hook页,然后在目标进程创建时(PsSetCreateProcessNotifyRoutine),将此hook页替换到目标进程中
//==================================================================
//内部函数
//函数名:GetPTEBase
//功能:获取页表基址
//返回值:UINT64,返回页表的基址
//==================================================================
UINT64 GetPTEBase()
{
PUCHAR BaseAddr = (PUCHAR)MmGetVirtualForPhysical;
return *(PUINT64)(BaseAddr + 0x22);
}
//==================================================================
//内部函数
//函数参数1 UINT64,一个虚拟地址
//函数参数2 UINT64,页表基址,由GetPTEBase()获取
//函数名:GetXXXAddress
//功能:获取一个虚拟地址所属的PTE,PDE,PPE,PXE之一的虚拟地址
//返回值:UINT64,返回PTE,PDE,PPE,PXE之一的虚拟地址
//==================================================================
UINT64 GetXXXAddress(UINT64 VirtualAddress, UINT64 PTEBase)
{
//VA+PTEBase->PTE
//PTE+PTEBase->PDE
//PDE+PTEBase->PPE
//PPE+PTEBase->PXE
return ((VirtualAddress & 0x0000FFFFFFFFF000) >> 12) * 8 + PTEBase;
}
//==================================================================
//内部函数
//函数名:CopyPhysicalPage
//函数参数1 PVOID 指向目标物理页的虚拟地址
//函数参数2 UINT64 原物理页的物理地址
//功能:拷贝原物理页的内容到目标物理页
//返回值:VOID
//==================================================================
VOID CopyPhysicalPage(PVOID DestPage, UINT64 SourcePagePhyAddr)
{
PHYSICAL_ADDRESS Low = { 0 };
PHYSICAL_ADDRESS High = { MAXULONG64 };
PVOID TempPage = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, Low, High, Low, MmCached);
UINT64 PTEAddress = GetXXXAddress((UINT64)TempPage, GetPTEBase());
UINT64 OldPTE = *(PUINT64)PTEAddress;
*(PULONG64)PTEAddress = SourcePagePhyAddr;
RtlCopyMemory(DestPage, TempPage, PAGE_SIZE);
*(PUINT64)PTEAddress = OldPTE;
MmFreeContiguousMemory(TempPage);
}
//==================================================================
//内部函数
//函数名:LinkPhysicalPages
//函数参数1 PVOID 子物理页的虚拟地址
//函数参数2 PVOID 父物理页的虚拟地址
//函数参数3 UINT32 子物理页插入到父物理页位置的索引
//函数参数4 UINT64 子物理页的属性
//功能:连接两个物理页
//返回值:VOID
//==================================================================
VOID LinkPhysicalPages(PVOID ChildPage,PVOID ParentPage,UINT32 Index ,UINT64 Attribute)
{
UINT64 ChildPagePhyAddr = *(PUINT64)(GetXXXAddress((UINT64)ChildPage, GetPTEBase()));
*(PUINT64)((UINT64)ParentPage+ (UINT64)Index * 8) = ((UINT64)ChildPagePhyAddr & 0xFFFFFFFFF000) | (Attribute & ~(0xFFFFFFFFF000));
}
//==================================================================
//函数名:InitHookPage
//函数参数1 UINT64,一个要hook的函数的虚拟地址
//函数参数2 OUT PPAGE_INFO结构体,供SetHookPage使用
//函数参数3 CHAR[] 要替换的二进制代码数组
//函数参数4 UINT32 数组长度
//功能:初始化hook页
//返回值:BOOLEAN,安装成功返回TRUE,失败返回FALSE
//==================================================================
BOOLEAN InitHookPage(UINT64 VirtualAddress,PPAGE_INFO pPageInfo,CHAR ShellCode[],UINT32 Length)
{
//按9-9-9-9-12拆分VirtualAddres这个线性地址
pPageInfo->PXEIndex = (VirtualAddress >> 0x27) & 0x1FF; //PXEIndex
UINT32 PPEIndex = (VirtualAddress >> 0x1E) & 0x1FF; //PPEIndex
UINT32 PDEIndex = (VirtualAddress >> 0x15) & 0x1FF; //PDEIndex
UINT32 PTEIndex = (VirtualAddress >> 0xC) & 0x1FF; //PTEIndex
UINT32 FuncOffset = VirtualAddress & 0xFFF;
//获得VirtualAddress对应的Pte Pde Ppe Pxe的值
pPageInfo->PteBase = GetPTEBase();
UINT64 PteAddr = GetXXXAddress(VirtualAddress, pPageInfo->PteBase); //VirtualAddress所属的物理页的PTE的指针
UINT64 PdeAddr = GetXXXAddress(PteAddr, pPageInfo->PteBase); //VirtualAddress所属的物理页的PDE的指针
UINT64 PpeAddr = GetXXXAddress(PdeAddr, pPageInfo->PteBase); //VirtualAddress所属的物理页的PPE的指针
UINT64 PxeAddr = GetXXXAddress(PpeAddr, pPageInfo->PteBase); //VirtualAddress所属的物理页的PXE的指针
UINT64 Pte = *(PUINT64)PteAddr;
UINT64 Pde = *(PUINT64)PdeAddr;
UINT64 Ppe= *(PUINT64)PpeAddr;
pPageInfo->Pxe = *(PUINT64)PxeAddr;
//分配一块物理页并初始化
PHYSICAL_ADDRESS Low = { 0 };
PHYSICAL_ADDRESS High = { MAXULONG64 };
for (int i = 0; i < 5; i++)
{
//成功返回虚拟地址,失败返回NULL
pPageInfo->pPageArray[i] = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, Low, High, Low, MmCached);
if (!pPageInfo->pPageArray[i])
{
DbgPrint("物理页分配失败\n");
return FALSE;
}
RtlZeroMemory(pPageInfo->pPageArray[i], PAGE_SIZE);
}
//拷贝VirtualAddress所属的PhyPage,PT,PDT,PPT
CopyPhysicalPage(pPageInfo->pPageArray[PhyPage], Pte);
CopyPhysicalPage(pPageInfo->pPageArray[PT], Pde);
CopyPhysicalPage(pPageInfo->pPageArray[PDT], Ppe);
CopyPhysicalPage(pPageInfo->pPageArray[PPT], pPageInfo->Pxe);
//连接PhyPage,PT,PDT,PPT
LinkPhysicalPages(pPageInfo->pPageArray[PhyPage], pPageInfo->pPageArray[PT], PTEIndex, Pte);
LinkPhysicalPages(pPageInfo->pPageArray[PT], pPageInfo->pPageArray[PDT], PDEIndex, Pde);
LinkPhysicalPages(pPageInfo->pPageArray[PDT], pPageInfo->pPageArray[PPT], PPEIndex, Ppe);
if (ShellCode == NULL||Length == 0)
{
return TRUE;
}
//__debugbreak();
//UINT64 aaaaaaa = (UINT64)pPageInfo->pPageArray[PhyPage] + FuncOffset + 0XB;
for (UINT32 i = 0; i < Length; i++)
{
*(PCHAR)((UINT64)pPageInfo->pPageArray[PhyPage] + FuncOffset+ i) = ShellCode[i];
}
return TRUE;
}
//==================================================================
//函数名:SetHookPage
//函数参数1 UINT64,目标进程的cr3
//函数参数2 PAGE_INFO 结构体,PAGE_INFO由InitHookPage获得
//功能:安装hook页
//返回值:VOID
//==================================================================
VOID SetHookPage(UINT64 DirectoryTableBase, PAGE_INFO PageInfo)
{
DirectoryTableBase = DirectoryTableBase & (~0xFFF) | 0x063;
UINT64 PtePXTAddress = GetXXXAddress((UINT64)PageInfo.pPageArray[PXT], PageInfo.PteBase);
UINT64 PtePXT = *(PUINT64)PtePXTAddress;
//修改了映射
*(PUINT64)PtePXTAddress = DirectoryTableBase;
LinkPhysicalPages(PageInfo.pPageArray[PPT],PageInfo.pPageArray[PXT], PageInfo.PXEIndex, PageInfo.Pxe);
*(PUINT64)PtePXTAddress = PtePXT;
DbgPrint("HOOK 成功\n");
}