初探windows内核代码

前言

最近在研究win游戏外挂,想自己也飞天入地,于是有此文。

环境搭建

老规矩,vs2019,windows10 sdk与wdk。ps:这一步卡了我好久,在闲鱼找人弄好了。

第一个驱动

众所周知,大厂的游戏为了反作弊,都有反作弊系统,会借助r0层权限保护游戏进程。
这也就是为什么你用x64dbg,ce这种调试读写工具看不到内存,甚至直接闪退的原因,反作弊通过合法手段在r0层对所有可疑函数调用做了hook,不返回内存数据。
而过掉这层反作弊的关键只能靠驱动,也就是在0环编写驱动程序,直接操作内存cpu寄存器以一种诡谲的方式hook没被监控的函数实现绕过反作弊监管,返回你想要的内存数据。

前置条件

说完原理,那么写驱动需要什么呢?
需要coding(废话)。
你需要了解windows10内核运行的机制,以及cpu、内存是怎么与内核交换数据的。
你还需要掌握一些内核函数库与硬件知识,

你需要了解cpp,至少能看懂。
你需要时常光顾各大论坛,看雪,吾爱,先知等,因为逆向圈不开源,好东西大家都想藏在自己手里,没人愿意上来就把成品注入器分享出来。所以你就得经常看看大家最新的思路,攻防最前沿。
你需要懂github搜索,至少知道去哪ctrlc,ctrlv。
如果你能用ai加快开发节奏,那更好(但ai很蠢,你知道的)。

第一次尝试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <ntifs.h>
#include <ntddk.h>
#include <intrin.h>

void drv_unload(PDRIVER_OBJECT DriverObject)
{
DbgPrintEx(77, 0, "卸载了傻逼。\r\n");
};

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrintEx(77, 0, "加载了傻逼。\r\n");
DriverObject->DriverUnload = drv_unload;
return STATUS_SUCCESS;
};

相信每个驱动圈的朋友都从这段代码开始,有头文件,有加载函数,有卸载函数,还有输出日志。
编译吧,别忘了切换x64平台,然后去虚拟机测试。

第二个驱动

经历了第一次尝试,你或许对驱动没那么陌生了。没错,驱动都有固定的写法,只是一些具体的实现不同。
现在你需要了解一些库函数了,再写一个稍微复杂的驱动。相信我,你可以的。

我们用到的函数有

函数

ntddk.h ✓

内存管理函数:

  • IoAllocateMdl

    • 作用:分配一块内存映射。
    • 类型:指向 MDL 的指针PMDL,分配失败返回 NULL 。
    • 参数:
      • VirtualAddress(可选):指向 MDL 描述的缓冲区基虚拟地址的指针
      • Length:指定 MDL 描述的缓冲区的字节长度
      • SecondaryBuffer:指示缓冲区是主/辅助缓冲区,无关联 IRP 时设为 FALSE
      • ChargeQuota:系统保留参数,驱动程序必须设为 FALSE
      • Irp(可选):指向要关联 MDL 的 IRP 指针
    • 例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      #include <wdm.h>
      #include <ntddk.h>

      // 驱动卸载例程声明(C++ 需显式声明为 C 链接)
      extern "C" VOID DriverUnload(_In_ PDRIVER_OBJECT DriverObject);

      // 资源释放辅助函数(C++ 封装,简化资源清理)
      template <typename T>
      inline void SafeFreePool(T*& ptr, ULONG tag) noexcept
      {
      if (ptr)
      {
      ExFreePoolWithTag(ptr, tag);
      ptr = nullptr;
      }
      }

      inline void SafeFreeMdl(PMDL& mdl) noexcept
      {
      if (mdl)
      {
      IoFreeMdl(mdl);
      mdl = nullptr;
      }
      }

      // MDL 操作示例函数
      NTSTATUS SimpleMdlDemo() noexcept
      {
      // 定义常量(C++ constexpr 提升类型安全)
      constexpr SIZE_T BUFFER_SIZE = 512;
      constexpr ULONG POOL_TAG = 'emoD'; // 反向 TAG(驱动开发规范)

      // 1. 分配分页池内存(C++ 类型推导)
      PVOID buffer = ExAllocatePoolWithTag(PagedPool, BUFFER_SIZE, POOL_TAG);
      if (!buffer)
      {
      DbgPrint("SimpleMdlDemo: 缓冲区分配失败\n");
      return STATUS_INSUFFICIENT_RESOURCES;
      }

      // 2. 为缓冲区分配 MDL(无 IRP 关联)
      PMDL mdl = IoAllocateMdl(
      buffer, // 虚拟地址
      BUFFER_SIZE, // 缓冲区长度
      FALSE, // 非辅助缓冲区
      FALSE, // 不占用配额(系统保留)
      nullptr // 无关联 IRP(C++ nullptr 替代 NULL)
      );

      if (!mdl)
      {
      DbgPrint("SimpleMdlDemo: MDL 分配失败\n");
      SafeFreePool(buffer, POOL_TAG); // 安全释放
      return STATUS_INSUFFICIENT_RESOURCES;
      }

      // 3. 锁定 MDL 并获取系统地址(C++ 类型安全)
      PVOID mdlSystemAddr = MmGetSystemAddressForMdlSafe(mdl, HighPagePriority);
      if (mdlSystemAddr)
      {
      // C++ 方式清空内存(等价于 RtlZeroMemory)
      RtlZeroMemory(mdlSystemAddr, BUFFER_SIZE);
      DbgPrint("SimpleMdlDemo: 缓冲区已清空\n");
      }
      else
      {
      DbgPrint("SimpleMdlDemo: 获取 MDL 系统地址失败\n");
      }

      // 4. 按逆序释放资源(C++ 资源管理最佳实践)
      SafeFreeMdl(mdl);
      SafeFreePool(buffer, POOL_TAG);

      return STATUS_SUCCESS;
      }

      // 驱动入口(必须 extern "C" 避免 C++ 名称修饰)
      extern "C" NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
      {
      UNREFERENCED_PARAMETER(RegistryPath);

      // 设置卸载例程
      DriverObject->DriverUnload = DriverUnload;

      // 执行 MDL 示例
      NTSTATUS status = SimpleMdlDemo();
      DbgPrint("SimpleMdlDemo 执行结果: 0x%08X\n", status);

      return STATUS_SUCCESS;
      }

      // 驱动卸载例程
      extern "C" VOID DriverUnload(_In_ PDRIVER_OBJECT DriverObject)
      {
      UNREFERENCED_PARAMETER(DriverObject);
      DbgPrint("MDL Demo 驱动已卸载\n");
      }
  • IoFreeMdl

    • 作用:释放调用方分配的内存描述符列表(MDL);若为部分MDL,会取消映射关联页面;需先解锁MDL描述的已锁定物理页面,且需单独释放MDL链中的每个MDL。
    • 类型:VOID(无返回值)
    • 参数:[in] PMDL Mdl,指向要释放的MDL的指针。
    • 例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      #include <wdm.h>

      // 假设已通过IoAllocateMdl分配了一个MDL
      PMDL pMyMdl = IoAllocateMdl(NULL, 1024, FALSE, FALSE, NULL);
      if (pMyMdl != NULL)
      {
      // 此处可添加使用MDL的相关操作
      // ...

      // 释放分配的MDL
      IoFreeMdl(pMyMdl);
      pMyMdl = NULL; // 避免野指针
      }
  • MmProbeAndLockPages

    • 作用:探测指定虚拟内存页并使其驻留,同时锁定页面(多用于DMA传输),防止驱动或硬件使用期间页面被释放和重新分配;成功后会更新MDL以描述物理页。仅分层驱动程序链中使用直接I/O的最高级别驱动程序可调用。
    • 类型:VOID(无返回值)
    • 参数:
      1. MemoryDescriptorList:输入输出参数,指向描述虚拟内存缓冲区的MDL指针,成功锁定后会被更新。
      2. AccessMode:输入参数,访问模式,可选KernelMode(内核模式)或UserMode(用户模式)。
      3. Operation:输入参数,探测和锁定的操作类型,可选IoReadAccess(只读)、IoWriteAccess/IoModifyAccess(读写)。
    • 例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      #include <wdm.h>

      // 假设已有虚拟内存地址和长度
      PVOID pBuffer = NULL;
      SIZE_T bufferSize = 1024;
      PMDL pMdl = NULL;

      // 1. 分配并初始化MDL
      pMdl = IoAllocateMdl(pBuffer, bufferSize, FALSE, FALSE, NULL);
      if (pMdl == NULL)
      {
      // 处理分配失败逻辑
      return;
      }

      // 2. 构建MDL以描述虚拟内存
      __try
      {
      MmProbeAndLockPages(pMdl, UserMode, IoModifyAccess);
      }
      __except(EXCEPTION_EXECUTE_HANDLER)
      {
      // 处理探测或锁定失败的异常
      IoFreeMdl(pMdl);
      return;
      }

      // 3. 使用锁定的页面(例如用于DMA操作)
      // ...

      // 4. 解锁页面并释放MDL
      MmUnlockPages(pMdl);
      IoFreeMdl(pMdl);
  • MmUnlockPages

    • 作用:解锁由指定内存描述符列表(MDL)描述的物理页面;若该MDL映射到系统地址空间,会先释放此映射再解锁页面。
    • 类型:VOID(无返回值)
    • 参数:[in, out] PMDL MemoryDescriptorList - 指向目标内存描述符列表(MDL)的指针。
    • 例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      #include <wdm.h>

      // 假设已经通过 MmProbeAndLockPages 锁定了 MDL
      VOID UnlockExample(PMDL pMdl)
      {
      if (pMdl != NULL)
      {
      // 解锁 MDL 对应的物理页面
      MmUnlockPages(pMdl);
      }
      }
  • MmMapLockedPagesSpecifyCache

  • MmUnmapLockedPages
  • MmProtectMdlSystemAddress
  • MmGetVirtualForPhysical
  • MmAllocateContiguousMemorySpecifyCache
  • ExAllocatePoolWithTag

进程线程函数:

  • PsLookupProcessByProcessId
  • KeStackAttachProcess
  • KeUnstackDetachProcess
  • ObfDereferenceObject
  • KeFlushEntireTb

内存操作函数:

  • RtlCopyMemory
  • RtlZeroMemory

调试函数:

  • DbgPrint
  • DbgPrintEx

ntifs.h ✓

系统调用函数:

  • NtOpenProcess
  • NtCreateFile

intrin.h ✓

CPU指令:

  • __readcr3

代码

相信你已经看懂了这些函数,现在要愉快的调用了~
完成一个MDL实现Ptehook吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//MDL.h
#pragma once
#include<ntifs.h>
#include<ntddk.h>

typedef struct _REPROTECT_CONTEXT {
PMDL Mdl;
PUCHAR Lockedva;

}REPROTECT_CONTEXT,*PREPROTECT_CONTEXT;

NTSTATUS MmLockVaForWrite(
PVOID Va,
ULONG Length,
__out PREPROTECT_CONTEXT ReprotectContext
);


NTSTATUS MmUnlockVaForWrite(
__out PREPROTECT_CONTEXT ReprotectContext
);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//MDL.cpp
#include "MDL.h"

/*
MDL 内存描述页表; 用 MDL 来描述物理内存, for DMA 设备(更快 )
*/


NTSTATUS MmLockVaForWrite(PVOID Va, ULONG Length, __out PREPROTECT_CONTEXT ReprotectContext)
{ //自己写一个内存函数,作为IoAllocateMdl,MmProbeAndLockPages,GetExceptionCode,MmMapLockedPagesSpecifyCache,IoFreeMdl,MmProtectMdlSystemAddress,NT_SUCCESS,MmUnmapLockedPages,MmUnlockPages的上层封装
NTSTATUS status;
status = STATUS_SUCCESS;//加载驱动成功

ReprotectContext->Mdl = 0;//创了一个mdl结构并清空
ReprotectContext->Lockedva = 0;
/*
IoAllocateMdl:
功能:用于分配一个描述内存页信息的 MDL(Memory Descriptor List)结构。
参数:通常需要传递要描述的内存区域的虚拟地址(Va)和长度(Length),以及其他参数如是否分配辅助表、是否从非分页池中分配等。
使用场景:主要用于创建描述内存页信息的 MDL 结构,但并不进行内存页的映射操作。
*/
ReprotectContext->Mdl = IoAllocateMdl(Va, Length, FALSE, FALSE, NULL); //分配缓冲区申请了一块内存出来

if (!ReprotectContext->Mdl) {
return STATUS_INSUFFICIENT_RESOURCES;//如果申请失败
};

__try{//把容易异常的代码放到try中
MmProbeAndLockPages(ReprotectContext->Mdl, KernelMode, IoWriteAccess); // access or write 可能会蓝

}
__except(EXCEPTION_EXECUTE_HANDLER) {
return GetExceptionCode();//捕获异常并返回异常处理接管seh防止蓝屏
}
/*
MmMapLockedPagesSpecifyCache:
功能:用于将已经描述的内存页映射到系统地址空间中,并返回映射后的虚拟地址。
参数:需要传递已经分配好的 MDL 结构(ReprotectContext->Mdl)、映射的访问模式(KernelMode)、缓存类型(MmCached)、映射的虚拟地址和是否是正常优先级等。
使用场景:主要用于将已经描述的内存页映射到系统地址空间中,以便进行读写操作等。常见的使用场景包括将用户空间的缓冲区映射到内核空间,或者将内核空间的缓冲区映射到用户空间。
*/
ReprotectContext->Lockedva = (PUCHAR)MmMapLockedPagesSpecifyCache(ReprotectContext->Mdl,
KernelMode, MmCached, NULL, FALSE, NormalPagePriority); //真正实现映射 分配虚拟地址
if (!ReprotectContext->Lockedva) {
IoFreeMdl(ReprotectContext->Mdl);
ReprotectContext->Mdl = 0;
return STATUS_UNSUCCESSFUL;
}

status = MmProtectMdlSystemAddress(ReprotectContext->Mdl, PAGE_EXECUTE_READWRITE);

if (!NT_SUCCESS(status)) {
MmUnmapLockedPages(ReprotectContext->Lockedva, ReprotectContext->Mdl);
MmUnlockPages(ReprotectContext->Mdl);
IoFreeMdl(ReprotectContext->Mdl);
ReprotectContext->Lockedva = 0;
ReprotectContext->Mdl = 0;
}

return status;
}

NTSTATUS MmUnlockVaForWrite(__out PREPROTECT_CONTEXT ReprotectContext)
{
NTSTATUS status;
status = STATUS_SUCCESS;

MmUnmapLockedPages(ReprotectContext->Lockedva, ReprotectContext->Mdl);
MmUnlockPages(ReprotectContext->Mdl);
IoFreeMdl(ReprotectContext->Mdl);
ReprotectContext->Lockedva = 0;
ReprotectContext->Mdl = 0;

return status;
}

接下来,我们需要调用一些封装好的库。不需要你自己写,直接下载贴上就好,程序员最宝贵的品质是cv,不是么。
ia32源自https://github.com/ia32-doc/ia32-doc,hde我没找到完整出处。

姑且打包点击下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//HookManager.h
#pragma once
#include"structer.h"
#include"MDL.h"
#include"ia32/ia32.hpp"

class HookManager
{
// 单例模式
public:
bool InstallInlinehook(HANDLE pid, __inout void** originAddr, void* hookAddr );
bool RemoveInlinehook(HANDLE pid, void* hookAddr);
static HookManager* GetInstance();

private:
bool IsolationPageTable(PEPROCESS process, void* isolateioAddr);
bool SplitLargePage(pde_64 InPde, pde_64& OutPde ); // 大页分割成小页
bool ReplacePageTable(cr3 cr3, void* replaceAlignAddr, pde_64* pde);

public:
ULONG64 VaToPa(void* va);
void* PaToVa(ULONG64 pa);
void offPGE();

UINT32 mHookCount = 0;

HOOK_INFO mHookInfo[MAX_HOOK_COUNT] = { 0 };

char* mTrampLinePool = 0;
UINT32 mPoolUSED = 0;

static HookManager* mInstance;
};


struct PAGE_TABLE
{
struct
{
pte_64* Pte;
pde_64* Pde;
pdpte_64* Pdpte;
pml4e_64* Pml4e;
}Entry;
void* VirtualAddress;
};



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//HookManager.cpp
#include "HookManager.h"
#include<intrin.h>
#include"hde64.h"
#include"PageTable.h"

HookManager* HookManager::mInstance;

#pragma warning (disable : 4838)
#pragma warning (disable : 4309)
#pragma warning (disable : 4244)
#pragma warning (disable : 6328)
#pragma warning (disable : 6066)
#pragma warning (disable : 4996)

EXTERN_C VOID
KeFlushEntireTb(
__in BOOLEAN Invalid,
__in BOOLEAN AllProcessors
);

bool HookManager::InstallInlinehook(HANDLE pid, __inout void** originAddr, void* hookAddr)
{
static bool bFirst = true;
if (bFirst) {
mTrampLinePool = (char*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE * 4, 'Jmp'); // ExAllocatePool2 蓝屏!!!!!!!

if (!mTrampLinePool) {
DbgPrint("Error InstallInlinehook");
return false;
};

RtlZeroMemory(mTrampLinePool, PAGE_SIZE * 4);
mPoolUSED = 0;
bFirst = false;

}

if (mHookCount == MAX_HOOK_COUNT) {
DbgPrint("FULL");
return false;
};
PEPROCESS process;
if (!NT_SUCCESS(PsLookupProcessByProcessId(pid, &process))) return false ;

if (!IsolationPageTable(process, *originAddr)) {
ObDereferenceObject(process);
return false;
}


const UINT32 trampLineByteCount = 20;
const UINT32 fnBreakByteLeast = 12;

/*
push 0
mov dword ptr ds : [rsp] , 0
mov dword ptr ds : [rsp + 4] , 0
*/
char TrampLineCode[trampLineByteCount] = {
0x6A,0x00 ,0x3E ,0xC7 ,0x04 ,0x24 ,0x00 ,0x00 ,0x00 ,
0x00 ,0x3E ,0xC7 ,0x44 ,0x24 ,0x04 ,0x00 ,0x00 ,0x00 ,0x00 ,0xC3 };

/*
mov rax, 0
Jmp rax
*/
char AbsoluteJmpCode[fnBreakByteLeast] = {
0x48,0xB8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0xE0
};



char* curTrampLinePool = mTrampLinePool + mPoolUSED;
char* startJmpAddr = (char*)*originAddr; // 要HOOK函数的首地址
UINT32 uBreakBytes = 0;
hde64s hdeinfo = { 0 };

while (uBreakBytes < fnBreakByteLeast) {
if (!hde64_disasm(startJmpAddr + uBreakBytes, &hdeinfo)) {
DbgPrint("hde64_disasm error \n");
return false;
};
uBreakBytes += hdeinfo.len;
};

*(PUINT32)&TrampLineCode[6] = (UINT32)((UINT64)(startJmpAddr + uBreakBytes) & 0xFFFFFFFF); // 取高位
*(PUINT32)&TrampLineCode[15] = (UINT32)((UINT64)(startJmpAddr + uBreakBytes)>>32 & 0xFFFFFFFF); //取低位

memcpy(curTrampLinePool, startJmpAddr, uBreakBytes); //保存原函数的 内容
memcpy(curTrampLinePool + uBreakBytes, TrampLineCode, trampLineByteCount); //return 语句


for (int i = 0; i < MAX_HOOK_COUNT; i++) {
if (mHookInfo[i].pid != pid) {
mHookInfo[i].pid = pid;
mHookInfo[i].originAddr = startJmpAddr;
memcpy(mHookInfo[i].originBytes, startJmpAddr, uBreakBytes);
mHookCount++;
break;
}
}

*(void**)&AbsoluteJmpCode[2] = hookAddr; // 数组地址转位一级指针:数组本身就是地址,& 取一次值就变成了耳机指针, 在 * 取一次值
REPROTECT_CONTEXT Content = { 0 };

KAPC_STATE apc;
KeStackAttachProcess(process, &apc);
if (!NT_SUCCESS(MmLockVaForWrite(startJmpAddr, PAGE_SIZE, &Content))) {
return false;
}

RtlCopyMemory(Content.Lockedva, AbsoluteJmpCode, fnBreakByteLeast);

if (!NT_SUCCESS(MmUnlockVaForWrite(&Content))) {
return false;
}

KeUnstackDetachProcess(&apc);


*originAddr = curTrampLinePool;
mPoolUSED += (uBreakBytes + trampLineByteCount);
ObDereferenceObject(process);
return true;
}

bool HookManager::RemoveInlinehook(HANDLE pid, void* hookAddr)
{
pid;
UNREFERENCED_PARAMETER(hookAddr);
return false;
}

HookManager* HookManager::GetInstance()
{
if (mInstance == nullptr) {
mInstance = (HookManager*)ExAllocatePoolWithTag(NonPagedPool, sizeof(HookManager), 'test');
}
return mInstance;
}

bool HookManager::IsolationPageTable(PEPROCESS process, void* isolateioAddr)
{
bool bRet = false;
KAPC_STATE apc;
KeStackAttachProcess(process, &apc);
pde_64 NewPde = { 0 };
void* alignAddrr; // ??

alignAddrr= PAGE_ALIGN(isolateioAddr); // 0x1000 对齐
PAGE_TABLE page_table = { 0 };
page_table.VirtualAddress = alignAddrr;
GetPageTable(page_table);

while (true) {
if (page_table.Entry.Pde->large_page) {
DbgPrint("size is 2MB \n");
bRet = SplitLargePage(*page_table.Entry.Pde, NewPde);
if (!bRet) break;
}
else if (page_table.Entry.Pdpte->large_page) {
DbgPrint("size is 1GB \n");
break;
}
else {
DbgPrint("size is 4KB \n");
}
cr3 Cr3;
Cr3.flags = __readcr3();
bRet = ReplacePageTable(Cr3, alignAddrr, &NewPde);

if (bRet) {
DbgPrint("isolation successfully \n");
}
else {
DbgPrint("Failed isolation \n");
}
break;
}

KeUnstackDetachProcess(&apc);



return bRet;
}

bool HookManager::SplitLargePage(pde_64 InPde, pde_64& OutPde)
{
PHYSICAL_ADDRESS MaxAddrPA{ 0 }, LowAddrPa{ 0 };
MaxAddrPA.QuadPart = MAXULONG64;
LowAddrPa.QuadPart = 0 ;
pt_entry_64* Pt;
uint64_t StartPfn = InPde.page_frame_number;

Pt = (pt_entry_64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddrPa, MaxAddrPA, LowAddrPa, MmCached); // 与MmAllocateContiguousMemory ?
if (!Pt) {
DbgPrint("failed to MmAllocateContiguousMemorySpecifyCache");
return false;
}

for (int i = 0; i < 512; i++) {
Pt[i].flags = InPde.flags;
Pt[i].large_page = 0;
Pt[i].page_frame_number = StartPfn + i;
}

OutPde.flags = InPde.flags;
OutPde.large_page = 0;
OutPde.page_frame_number = VaToPa(Pt) / PAGE_SIZE;
return true;
}

bool HookManager::ReplacePageTable(cr3 cr3, void* replaceAlignAddr, pde_64* pde)
{
uint64_t *Va4kb, *Vapt, *VaPdt, *VaPdpt, *VaPml4t;
PHYSICAL_ADDRESS MaxAddrPA{ 0 }, LowAddrPa{ 0 };
MaxAddrPA.QuadPart = MAXULONG64;
LowAddrPa.QuadPart = 0;
PAGE_TABLE pagetable = { 0 };

Va4kb = (uint64_t*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddrPa, MaxAddrPA, LowAddrPa, MmCached);
Vapt = (uint64_t*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddrPa, MaxAddrPA, LowAddrPa, MmCached);
VaPdt = (uint64_t*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddrPa, MaxAddrPA, LowAddrPa, MmCached);
VaPdpt = (uint64_t*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddrPa, MaxAddrPA, LowAddrPa, MmCached);

VaPml4t = (uint64_t*)PaToVa(cr3.address_of_page_directory * PAGE_SIZE);

if (!Va4kb || !Vapt || !VaPdt || !VaPdpt) {
DbgPrint(" Apply mm failed \n");
return false;
}

pagetable.VirtualAddress = replaceAlignAddr;
GetPageTable(pagetable);

UINT64 pml4eindex = ((UINT64)replaceAlignAddr & 0xFF8000000000) >> 39;
UINT64 pdpteindex = ((UINT64)replaceAlignAddr & 0x7FC0000000) >> 30;
UINT64 pdeindex = ((UINT64)replaceAlignAddr & 0x3FE00000) >> 21;
UINT64 pteindex = ((UINT64)replaceAlignAddr & 0x1FF000) >> 12;

if (pagetable.Entry.Pde->large_page) {
MmFreeContiguousMemorySpecifyCache(Vapt, PAGE_SIZE, MmCached);
Vapt = (uint64_t*)PaToVa(pde->page_frame_number * PAGE_SIZE);
}
else {
memcpy(Vapt, pagetable.Entry.Pte - pteindex, PAGE_SIZE);
}
memcpy(Va4kb, replaceAlignAddr, PAGE_SIZE);
memcpy(VaPdt, pagetable.Entry.Pde - pdeindex, PAGE_SIZE);
memcpy(VaPdpt, pagetable.Entry.Pdpte - pdpteindex, PAGE_SIZE);

auto pReplacePte = (pte_64*) &Vapt[pteindex]; // &
pReplacePte->page_frame_number = VaToPa(Va4kb) / PAGE_SIZE;

auto pReplacePde = (pde_64*)&VaPdt[pdeindex]; // &
pReplacePde->page_frame_number = VaToPa(Vapt) / PAGE_SIZE;
pReplacePde->large_page = 0;

auto pReplacePdpte = (pdpte_64*)&VaPdpt[pdpteindex]; // &
pReplacePdpte->page_frame_number = VaToPa(VaPdt) / PAGE_SIZE;

auto pReplacePml4e = (pml4e_64*)&VaPml4t[pml4eindex]; // &
pReplacePml4e->page_frame_number = VaToPa(VaPdpt) / PAGE_SIZE;

KeFlushEntireTb(true, false);
offPGE();
return true;
}

ULONG64 HookManager::VaToPa(void* va)
{
PHYSICAL_ADDRESS pa;
pa = MmGetPhysicalAddress(va);
return pa.QuadPart;
}

void* HookManager::PaToVa(ULONG64 pa)
{
PHYSICAL_ADDRESS Pa{ 0 };
Pa.QuadPart = pa;

return MmGetVirtualForPhysical(Pa);
}

ULONG_PTR KipiBroadcastWorker(
ULONG_PTR Argument
)
{
Argument;
KIRQL irql = KeRaiseIrqlToDpcLevel(); //提升进程特权级 , 防止切换 cpu 打断
_disable(); //屏蔽中断
ULONG64 cr4 = __readcr4();
cr4 &= 0xffffffffffffff7f;
__writecr4(cr4);
_enable();

KeLowerIrql(irql);
return 0;

}
void HookManager::offPGE()
{
KeIpiGenericCall(KipiBroadcastWorker, NULL);
}

1
2
3
4
5
//PageTable.h
#pragma once
#include"HookManager.h"
bool GetPageTable(PAGE_TABLE& table);
void* GetPteBase();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//PageTable.cpp
#include<ntifs.h>
#include<ntddk.h>
#include<intrin.h>
#include"ia32/ia32.hpp"
#include"PageTable.h"
#include"HookManager.h"

#pragma warning(disable:4389)
void* GetPteBase() {
cr3 CR3;
PHYSICAL_ADDRESS cr3_pa = { 0 };
CR3.flags = __readcr3();
cr3_pa.QuadPart = CR3.address_of_page_directory * PAGE_SIZE;
PULONG64 cr3_va = (PULONG64)MmGetVirtualForPhysical(cr3_pa);

UINT64 nCount = 0;
while ((*cr3_va & 0x000FFFFFFFFFF000) != cr3_pa.QuadPart) {
if (++nCount >= 512) {
return nullptr;
}
cr3_va++;
}
return (void*)(0xffff000000000000 | (nCount << 39));
}

bool GetPageTable(PAGE_TABLE& table) {
ULONG64 PteBase = 0;
ULONG64 pdeBase = 0;
ULONG64 pdpteBase = 0;
ULONG64 pml4eBase = 0;

PteBase = (ULONG64)GetPteBase();
DbgPrint("PteBase :%p\n", PteBase);

if (PteBase == NULL) return false;
pdeBase = (((PteBase & 0xffffffffffff) >> 12) << 3) + PteBase;
pdpteBase = (((pdeBase & 0xffffffffffff) >> 12) << 3) + PteBase;
pml4eBase = (((pdpteBase & 0xffffffffffff) >> 12) << 3) + PteBase;

table.Entry.Pte = (pte_64*)(((((ULONG64)table.VirtualAddress & 0xffffffffffff) >> 12) << 3) + PteBase);
table.Entry.Pde = (pde_64*)(((((ULONG64)table.VirtualAddress & 0xffffffffffff) >> 21) << 3) + pdeBase);
table.Entry.Pdpte = (pdpte_64*)(((((ULONG64)table.VirtualAddress & 0xffffffffffff) >> 30) << 3) + pdpteBase);
table.Entry.Pml4e = (pml4e_64*)(((((ULONG64)table.VirtualAddress & 0xffffffffffff) >> 39) << 3) + pml4eBase);

return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//structer.h
#pragma once
#include<ntifs.h>
#include<ntddk.h>

#define MAX_HOOK_COUNT 10

typedef struct _HOOK_INFO {
HANDLE pid;
char originBytes[14];
void* originAddr;


}HOOK_INFO, *PHOOK_INFO;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//main.cpp
#include<ntifs.h>
#include<ntddk.h>
#include"HookManager.h"

typedef NTSTATUS(NTAPI* pfnNtOpenProcess)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PCLIENT_ID);
typedef NTSTATUS(NTAPI* pfnNtCreateFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);

pfnNtOpenProcess g_oriNtOpenProcess;
pfnNtCreateFile g_oriNtCreateFile;
HANDLE g_pid = (HANDLE) 1808;

NTSTATUS NTAPI FakeNtOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId) {
DbgPrintEx(102,0, "Fake NtOpenProcess \n"); // 没有 \n 在windbg 的log 中看不到

return g_oriNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
};

NTSTATUS NTAPI FakeNtCreateFile(
_Out_ PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize,
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
_In_ ULONG EaLength) {

DbgPrint("Fake Ntfakeopenfile");

return g_oriNtCreateFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, AllocationSize, FileAttributes, ShareAccess, CreateDisposition, CreateOptions, EaBuffer, EaLength);

};



void DriverUnload(PDRIVER_OBJECT DriverObject) {
UNREFERENCED_PARAMETER(DriverObject);
HookManager::GetInstance()->RemoveInlinehook(g_pid, (void*)FakeNtOpenProcess);
HookManager::GetInstance()->RemoveInlinehook(g_pid, (void*)FakeNtCreateFile);

}

EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath){
UNREFERENCED_PARAMETER(RegisterPath);
DriverObject->DriverUnload = DriverUnload;
g_oriNtOpenProcess = NtOpenProcess;
g_oriNtCreateFile = NtCreateFile;
//DbgPrintEx(102, 0, "success main");
DbgPrintEx(102, 0, "1 \n");
if (HookManager::GetInstance()->InstallInlinehook(g_pid, (void**)&g_oriNtOpenProcess, (void*)FakeNtOpenProcess)) {
DbgPrintEx(102, 0, "success main \n");
};
//if (HookManager::GetInstance()->InstallInlinehook((void**)&g_oriNtCreateFile, (void*)FakeNtCreateFile)) {
// DbgPrintEx(102, 0, "success main");
//};
return STATUS_SUCCESS;
}