#include <Ntifs.h>
#include <WinDef.h>
#include "LDE64x64.h" // 识别硬编码的长度
// 实现内核钩子(针对PsLookupProcessByProcessId函数)
// 导出函数声明
PCHAR PsGetProcessImageFileName(PEPROCESS Process); // 获取内核对象的进程名称
typedef NTSTATUS
(*pPsLookupProcessByProcessId)(
_In_ HANDLE ProcessId,
_Outptr_ PEPROCESS *Process
);
pPsLookupProcessByProcessId OriFun;
ULONG PachSize;
KIRQL irQl;
// 修改Cr0寄存器, 去除写保护(内存保护机制)
KIRQL RemovWP()
{
DbgPrint("RemovWP\n");
// (PASSIVE_LEVEL)提升 IRQL 等级为DISPATCH_LEVEL,并返回旧的 IRQL
// 需要一个高的IRQL才能修改
irQl = KeRaiseIrqlToDpcLevel();
ULONG_PTR cr0 = __readcr0(); // 内联函数:读取Cr0寄存器的值, 相当于: mov eax, cr0;
// 将第16位(WP位)清0,消除写保护
cr0 &= ~0x10000; // ~ 按位取反
_disable(); // 清除中断标记, 相当于 cli 指令,修改 IF标志位
__writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
DbgPrint("退出RemovWP\n");
return irQl;
}
// 复原Cr0寄存器
KIRQL UndoWP()
{
DbgPrint("UndoWP\n");
ULONG_PTR cr0 = __readcr0();
cr0 |= 0x10000; // WP复原为1
_disable(); // 清除中断标记, 相当于 cli 指令,清空 IF标志位
__writecr0(cr0); // 将cr0变量数据重新写入Cr0寄存器中,相当于: mov cr0, eax
// 恢复IRQL等级
KeLowerIrql(irQl);
DbgPrint("退出UndoWP\n");
return irQl;
}
NTSTATUS
HookPsLookupProcessByProcessId(
_In_ HANDLE ProcessId,
_Outptr_ PEPROCESS *Process
)
{
DbgPrint("进入HookPsLookupProcessByProcessId\n");
NTSTATUS status = STATUS_SUCCESS;
status = OriFun(ProcessId, Process); // 执行原来的函数代码
if (!NT_SUCCESS(status))
{
return status;
}
if (strcmp(PsGetProcessImageFileName(*Process), "calc.exe") == 0) // 如果目标为计算器
{
return STATUS_ACCESS_DENIED; // 返回拒绝访问
}
return status;
}
VOID HookKernelRoutine(PVOID ApiAddress/*目标函数*/, PVOID Proxy_Address/*我们的函数*/,
PVOID * Ori_Addresss/*存储原来的汇编代码空间*/, ULONG * PatchSize/*存储的汇编指令长度*/)
{
KdBreakPoint();
DbgPrint("进入HookKernelRoutine\n");
// 构建 ShellCode 代码
char jmp_Code[14] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
// 跳回源代码的 Code
char jmp_OriCode[14] = "\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
DWORD AsmLength = 0;
DWORD CodeLength = 0;
PCHAR OriCodeStartAddr = (PCHAR)ApiAddress;
while (CodeLength < 14) // 循环判断指令长度是否能存下 ShellCode
{
// 通过 LDE 函数获取,当前汇编指令长度
AsmLength = LDE(ApiAddress/*函数地址*/, 64/*系统位数*/);
OriCodeStartAddr += AsmLength; // 获取返回到原程序的起始执行地址
CodeLength += AsmLength;
}
*PatchSize = CodeLength; // 存储的汇编指令长度
*Ori_Addresss = ExAllocatePool(NonPagedPool, CodeLength + sizeof(jmp_OriCode)); // 开辟空间存储原来的代码 + 返回原代码地址Code
// 复制原本的硬编码
RtlMoveMemory(*Ori_Addresss, ApiAddress, CodeLength);
// 放入跳回地址
*(PVOID *)&jmp_OriCode[6] = (PCHAR)ApiAddress + *PatchSize;
// 将其拷贝到 开辟空间存储的原来的代码 的尾部, 这样就组成了原来的函数代码
RtlCopyMemory((PCHAR)*Ori_Addresss + *PatchSize, jmp_OriCode, sizeof(jmp_OriCode));
// 将我们的函数地址存入到 ShellCode 中
*(PVOID *)&jmp_Code[6] = HookPsLookupProcessByProcessId;
// 关闭写保护
RemovWP();
RtlCopyMemory(ApiAddress, jmp_Code, sizeof(jmp_Code)); // 将 ShellCode 写入目标函数中
// 恢复写保护
UndoWP();
DbgPrint("退出HookKernelRoutine\n");
}
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
// 恢复源代码
// 关闭写保护
RemovWP();
RtlCopyMemory(PsLookupProcessByProcessId, OriFun, PachSize); // 将 ShellCode 写入目标函数中
// 恢复写保护
UndoWP();
DbgPrint("卸载驱动\n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegeditPath)
{
DbgPrint("加载驱动\n");
NTSTATUS status = STATUS_SUCCESS;
DriverObject->DriverUnload = Unload;
// 初始化反汇编引擎
LDE_init();
HookKernelRoutine(PsLookupProcessByProcessId, HookPsLookupProcessByProcessId, &((PVOID)OriFun), &PachSize);
return status;
}