这篇是为了回应:驱动obcallback反附加的一次尝试【Second_BC の BloG】 的最新的解决方法;

隔了一年没更新是因为忙事情去了(

之前我们提到过,使用OB回调的方法进行进程打开时的修改操作的这种方法,非常不稳定,会掉线,附加会脱落;

目前的版本也不支持这种姿势绕过了,一进入游戏进程就会直接被检测到异常,弹窗报错然后直接游戏进程死掉;

同时之前的劫持驱动文件的方法非常的不优雅,而且目前版本也不支持这种低级绕过姿势;

针对这些个问题有了今天的这个补充篇;

环境和时间铺垫:

最新测试结果可行,时间节点为 2026.7.30 以前,往后没有进行过测试,不保证该方法仍然有效;

测试平台为 win10 旗舰版,win11肯定干不起,除非你能在win11上把过期签名的驱动跑起来,或者其他手段能给win11签名绕过或者正版签名;

最好不要使用虚拟机,现在的版本对虚拟机有检测,除非你有信息能绕过虚拟机检测;

由于过去了一段时间我才开始写这篇内容,所以有些实验结论我就直接给了,实验作证截图确实是找不到了(

初步实验猜想

第一处疑点

之前的篇子里提到过,保护驱动主要做的事情进行降权,降权是什么意思呢,如何降的呢?

降权的本质是在保护驱动开启的OB回调里面,检测当每次打开的进程是目标游戏进程的时候,就会让打开句柄的权限降低;

主要就是把可写的权限位给抹除了;

举个例子,当你编写程序使用 OpenProcess API 的时候,第一个参数一般我们会填入 PROCESS_ALL_ACCESS ,意思是以可读可写的方式打开这个文件或者进程句柄;

但经过我的测试,使用R3编程的控制台程序,直接打开这个游戏的进程句柄,返回的句柄通过 NtQueryObject 查询句柄访问权限的掩码,可以发现读写的权限都被抹除了;

这就是为什么使用CE打开进程后,地址出现问号,进程图标加载不出来的原因,因为CE也是使用 OpenProcess 这个API打开进程句柄,并通过后续的一系列API对句柄进行使用以读取内存地址,查询地址空间等操作,比如:ReadProcessMemory,WriteProcessMemory,等;

之前那个篇子中使用的思路是我们自己也写一组OB回调,分别放在OB回调的开始和结束(用优先级设定),针对目标游戏进程句柄的权限进行恢复操作,在这样的一个恢复过程后,CE确实能够看到进程图标,并且能够访问内存空间,这本身没有问题;但保护驱动做了什么措施,导致这样的姿势绕过以后会被检测到,导致游戏死掉;

我推测是保护驱动或者是游戏模块内某块区域持续检查(或者称呼为管理)目标进程被打开的句柄;检查这些被打开的句柄的权限是否异常,如果异常,就杀掉游戏进程,所以我们不能想着去改动打开的游戏进程句柄的权限了;(目前的版本就算使用SYSTEM级权限的CE也会报错闪退,或者根本打不开)

第二处疑点

我使用 PChunter 这玩意儿对游戏进程进行模块查看,发现能够正常返回游戏的每一个模块的基址,我就很好奇,为什么 PChunter 能看到被限制权限的游戏进程的模块基址,而CE不行;

同时,用 PChunter 访问了游戏进程的模块基址后,游戏并没有出现异常或者报错,或者闪退,非常稳定;

于是我开始思考他们两者的差异,即:

PChunter 会挂载驱动,它应该是通过驱动内的手段进行的内核级别的内存扫描;

而CE这东西,直接就是用的R3下的OpenProcess和其他API来操作的,这肯定会受限制;

思路

所以有了想法,即不改变CE打开游戏进程句柄的逻辑,但是改变CE读内存和写内存的逻辑;

至于调试器会不会受影响,我的答案是肯定会,但是我们仍然有CE的dbvm可以用,dbvm调试模式也是走的驱动层面,这条路可行;

由于我们只改变了内存读写逻辑,而没给操作的进程句柄提权;所以线程级操作肯定干不起,比如调试中的线程断点是没有效果的,但是仍然可以选择使用dbvm调试,打断点到对应位置,依旧会正常停下;

结论

这种思路打开了局面,经过我的测试,虽然CE的某些功能受到了限制,但是我们的主要操作已经绕过了,大部分的逆向操作都能直接进行工作,比如:扫描内存,扫描指针,查找是什么访问/改写了这个地址,打断点,单步调试,dump内存等等;这些操作不会触发反调试(因为使用了dbvm),也不会触发异常报错(没有动核心检测点:句柄);

经过我的测试我可以得出一个很明显的结论:这款游戏的保护模块根本没有强检测CE这个程序(但是检测了虚拟机),它并不像腾讯的一些驱动保护会查CE进程的特征;它也没有设置很多内核钩子去监控比如驱动链表,异常驱动模块,驱动回调,你建的异常OB回调等等;有反调试,但对于dbvm的CE来说相当于没有;主要还是针对于自身模块和句柄的权限维持;

接下来就是我写的源代码了,总共有三个模块:

一个是SYS驱动模块,主要是实现内核级内存查询,内存写,内存读;

第二个是DLL模块,用于进行和驱动设备的交互,接口的作用,把CE函数调用约定给到驱动,再由驱动返回CE函数应该得到的返回结果;并在附加到CE进程后对CE的关键函数进行HOOK,替换为这些接口函数,使CE每次调用关键函数走驱动实现;

第三个就是注入器了,把编译好的DLL注入到CE进程,改变CE的执行逻辑,使其绕过检测,正常工作;

值得说明的是,如果游戏的保护驱动检测力度更狠,比如检测到了我上面提到的一些内核级的内容;那么就要考虑其他绕过姿势了,详见:驱动设备引入-Windows驱动编程二【Second_BC の BloG】 ;

三个模块是让 AI 辅助去写的,风格不像之前一样保留大多数注释,但是思路就是上述提到的三个模块的设计思路;

虽然AI时代,AI确实能够代替老时代的残党进行大量的脑力工作和枯燥的逆向过程和漏洞挖掘过程,极大优化分析时间(CTF已死);但是这些绕过思路,对抗思路,长时间内,是AI不可能告诉你的,也是风控严格的AI不会去做的;除非你很吊,非常有刀,自己训练一个属于自己的还算聪明的大模型语言,那我没话说了,你随意;或者使用 agent 阵列组或者设计 harness 控制一些风控较低的但是笨的AI,让高级别的更聪明的AI给他们布置任务和写提示词来实现思路的产生或是exp的编写,也行;

所以往后的学习道路,Agent 是肯定躲不过去的,必须学的内容;老时代的课程也得看,还是得让自己变得更有思路才行;

最后附有上述仅凭外部测试通过的源码;

接下来是一些关于保护驱动的逆向结果,为什么之前没逆向?之前谁能手逆VMP我叫他逆向爷爷,现在 AI 是我爹,都能直接把 VMP 还原成idb可识别的伪代码,太伟大了;

逆向分析结果很有价值,解释了几个问题,也说明了虚拟化检测的一种途径;

针对目前的逆向分析结果还没什么有想法的思路进行绕过,只能说学的东西还多,记的东西也还多;

内部驱动模块逆向分析

这次分析的驱动模块的版本的对应的时间就是这篇内容更新的时间,各位可以参考;

接下来我会选择放 AI 识别的伪代码,因为IDA里全是VM混淆的内容;

初始化入口

在上篇其实我们已经看过驱动入口的样子了,里面被我改名为 true_entry的函数,实际上是一个受保护的初始化函数,新名称我叫他为:VMPcodeEntry1 ,AI 爹还原的伪代码如下所示:

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
NTSTATUS __fastcall VMPcodeEntry1(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
NTSTATUS v2; // 当前步骤的返回状态;失败尾部最终返回此值。
_QWORD *v3; // 处理表管理对象;内部包含两个容器指针。
ULONG MajorVersion;
ULONG MinorVersion;
ULONG BuildNumber;

/* 当前已分析的 sub_1400455C0 实现返回 0;此处忽略返回值。
* 不据此为它附加尚未证实的业务含义,保留原调用。
*/
sub_1400455C0(0);
/* 日志对象相关的一次性初始化标志:只检查最低位。
* 先置位,再通过 sub_140045520 登记清理回调;其登记结果未检查。
* sub_140045520 使用链表保存回调,失败尾部的 sub_140045570 执行该链。
* 这里没有显式锁或原子比较交换,不应把这个标志解释成线程同步机制。
*/
if ( (dword_14009817C & 1) == 0 )
{
dword_14009817C |= 1u;
sub_140045520((__int64)nullsub_24);
}
// LastState:阶段 100,表示本次入口初始化开始;不检查日志操作是否成功。
sub_140018C80(&unk_140098178, RegistryPath, 100);

/* sub_140013510:获取/按需构造全局上下文(已定位到 0x140094850)。
* sub_140013620:以该上下文为第一个参数执行入口相关初始化。
* 已确认的内部效果:
* ctx + 0x220:保存 DriverObject;
* ctx + 0x238、ctx + 0x250:初始化两个 NotificationEvent,初始未触发;
* ctx + 0x210:初始化保存注册表路径的内部对象,复制 RegistryPath。
* 后续 LoadTime/UnLoadTime 操作使用内部保存的路径缓冲区。
*/
v2 = sub_140013620(sub_140013510(), DriverObject, RegistryPath);
// 按有符号 32 位 NTSTATUS 判断失败;非负状态继续执行。
if ( v2 < 0 )
{
// 上下文/路径初始化失败:记录原错误;公共尾部保留 v2,不改成固定错误码。
sub_140018BB0(&unk_140098178, RegistryPath, 106, v2);
goto LABEL_FAILURE;
}
/* 第二参数为 0:选择 LoadTime。
* 子函数先检查内部保存的路径缓冲区是否存在,再取得时间并更新注册表。
* 记录时间失败不会使本入口立即失败,因为这里没有检查其返回值。
*/
sub_140018C20((__int64)&unk_140098178, 0);

/* sub_14001ADE0:获取/按需构造内核信息对象(0x140095BE0)。
* sub_140018480:初始化/检查内核映像信息,包括已观察到的模块查询与映像检查。
* 关键:这里仅测试返回寄存器 AL,即最低 8 位,不能按 NTSTATUS 判断。
* 例如返回 0x100 时 AL 为 0,仍进入失败分支;高位是否非零无关。
*/
if ( !(unsigned __int8)sub_140018480(sub_14001ADE0()) )
{
sub_140018BB0(&unk_140098178, RegistryPath, 101, 0);
// 日志的 status 参数是上面的 0;入口返回值另行设为 0xC0000355。
v2 = 0xC0000355;
goto LABEL_FAILURE;
}
/* 获取系统版本;第四参数为空,不请求 CSD 字符串。
* 本入口使用三个输出值,不检查 PsGetVersion 的返回值。
* 以下 qword 名称来自 IDA 数据定义;实际按两个相邻 DWORD 存储字段,
* 不应把这些赋值理解为对同一个 64 位计数器做运算。
*/
PsGetVersion(&MajorVersion, &MinorVersion, &BuildNumber, 0);
LODWORD(qword_140095C38) = MajorVersion; // 0x140095C38:主版本。
HIDWORD(qword_140095C38) = MinorVersion; // 0x140095C3C:次版本。
LODWORD(qword_140095BE0) = BuildNumber; // 0x140095BE0:系统 Build。
HIDWORD(qword_140095BE0) = 10 * MajorVersion + MinorVersion; // 例如 6.3 -> 63,10.0 -> 100。

/* 获取处理表管理对象(0x1400946E0),再创建其两个容器。
* getter 的一次性构造与 sub_140011D50 的容器初始化是两个不同步骤。
*/
v3 = (_QWORD *)sub_14003BA90();
v2 = sub_140011D50(v3);
// 按有符号 32 位 NTSTATUS 判断失败;非负状态继续执行。
if ( v2 < 0 )
{
// 处理表初始化失败。这里没有显式调用 sub_140011DD0,直接走公共清理。
sub_140018BB0(&unk_140098178, RegistryPath, 102, v2);
goto LABEL_FAILURE;
}

/* 按原始执行顺序登记 54 个处理项,供后续配置处理使用。
* 参数恢复:
* a1 = v3:表管理对象;
* a2 = sub_xxx:实际处理函数地址;
* a3 = 32 位 ID:该项的查找标识;
* a4 = 0:登记项中的附加参数,本入口固定为零,具体业务语义未确认;
* a5 = 1:控制参数,本入口固定为一;完整枚举含义未确认。
* 在 x64 调用中 a5 位于栈上,漏掉它会改变登记行为。
* 已通过原始堆内容核对回调、ID 和其余字段。
*
* 0x140011F80 是完整登记入口;0x140011E30 只是内部使用的查找函数。
* 这些调用都忽略返回值:某项登记失败不会在本函数中立即触发错误分支。
* 目前只确认 ID 与函数的对应关系,不能直接把这些 ID 当作 IOCTL,
* 也不能仅凭常量值断言其哈希算法或把各回调命名为具体恶意功能。
* 行末序号仅用于核对原始顺序,不是程序参数。
*/
sub_140011F80(v3, sub_14003B4A0, 0x3F4F91DCu, 0, 1); // 登记项 01/54
sub_140011F80(v3, sub_14003B440, 0xAE84343Au, 0, 1); // 登记项 02/54
sub_140011F80(v3, sub_14003B3E0, 0x891431ABu, 0, 1); // 登记项 03/54
sub_140011F80(v3, sub_14003B640, 0xAE847396u, 0, 1); // 登记项 04/54
sub_140011F80(v3, sub_14003B310, 0x4BAE93B2u, 0, 1); // 登记项 05/54
sub_140011F80(v3, sub_14003AB30, 0x2F8E58DFu, 0, 1); // 登记项 06/54
sub_140011F80(v3, sub_14003A2E0, 0xE5FD96E7u, 0, 1); // 登记项 07/54
sub_140011F80(v3, sub_14003A050, 0x23726080u, 0, 1); // 登记项 08/54
sub_140011F80(v3, sub_14003A0E0, 0x6CA21C08u, 0, 1); // 登记项 09/54
sub_140011F80(v3, sub_14003A140, 0x7F1217F4u, 0, 1); // 登记项 10/54
sub_140011F80(v3, sub_140039FF0, 0xB1BA11C0u, 0, 1); // 登记项 11/54
sub_140011F80(v3, sub_14003A280, 0xA9E723C1u, 0, 1); // 登记项 12/54
sub_140011F80(v3, sub_14003AD10, 0x91BED27Cu, 0, 1); // 登记项 13/54
sub_140011F80(v3, sub_14003B570, 0xEED943E4u, 0, 1); // 登记项 14/54
sub_140011F80(v3, sub_14003A440, 0xF01DF74Au, 0, 1); // 登记项 15/54
sub_140011F80(v3, sub_14003A4A0, 0xC68AFD95u, 0, 1); // 登记项 16/54
sub_140011F80(v3, sub_14003A210, 0xF23F5DE0u, 0, 1); // 登记项 17/54
sub_140011F80(v3, sub_14003B1D0, 0x370F5935u, 0, 1); // 登记项 18/54
sub_140011F80(v3, sub_14003A820, 0x2E437CE7u, 0, 1); // 登记项 19/54
sub_140011F80(v3, sub_14003A660, 0xFF32D6D5u, 0, 1); // 登记项 20/54
sub_140011F80(v3, sub_14003AD70, 0x34FD9C3Eu, 0, 1); // 登记项 21/54
sub_140011F80(v3, sub_14003A6D0, 0x8BF9FD78u, 0, 1); // 登记项 22/54
sub_140011F80(v3, sub_14003A740, 0xE4B5E2F3u, 0, 1); // 登记项 23/54
sub_140011F80(v3, sub_14003B5D0, 0x4A5B07F5u, 0, 1); // 登记项 24/54
sub_140011F80(v3, sub_14003A900, 0x1381D5E6u, 0, 1); // 登记项 25/54
sub_140011F80(v3, sub_14003B0F0, 0xD43588A8u, 0, 1); // 登记项 26/54
sub_140011F80(v3, sub_14003B160, 0x07E376E2u, 0, 1); // 登记项 27/54
sub_140011F80(v3, sub_14003A890, 0xB39D8A0Eu, 0, 1); // 登记项 28/54
sub_140011F80(v3, sub_14003A7B0, 0x49C9F49Du, 0, 1); // 登记项 29/54
sub_140011F80(v3, sub_14003A5F0, 0xD2CE7AB9u, 0, 1); // 登记项 30/54
sub_140011F80(v3, sub_14003B230, 0x9A30844Au, 0, 1); // 登记项 31/54
sub_140011F80(v3, sub_14003A580, 0x2D7D53ADu, 0, 1); // 登记项 32/54
sub_140011F80(v3, sub_14003B500, 0xB76059B9u, 0, 1); // 登记项 33/54
sub_140011F80(v3, sub_14003A9E0, 0xEF8ADD55u, 0, 1); // 登记项 34/54
sub_140011F80(v3, sub_14003AA50, 0x879B385Cu, 0, 1); // 登记项 35/54
sub_140011F80(v3, sub_14003A510, 0x732AC124u, 0, 1); // 登记项 36/54
sub_140011F80(v3, sub_14003AAC0, 0x340928F0u, 0, 1); // 登记项 37/54
sub_140011F80(v3, sub_14003B2A0, 0xEE3119EDu, 0, 1); // 登记项 38/54
sub_140011F80(v3, sub_14003AC30, 0xA22920EBu, 0, 1); // 登记项 39/54
sub_140011F80(v3, sub_14003ACA0, 0x08140C91u, 0, 1); // 登记项 40/54
sub_140011F80(v3, sub_14003B010, 0xCE95CE43u, 0, 1); // 登记项 41/54
sub_140011F80(v3, sub_14003ABC0, 0x17E29E90u, 0, 1); // 登记项 42/54
sub_140011F80(v3, sub_14003B370, 0x444C46F1u, 0, 1); // 登记项 43/54
sub_140011F80(v3, sub_14003A3D0, 0x6938CF1Cu, 0, 1); // 登记项 44/54
sub_140011F80(v3, sub_14003A390, 0x9C5A573Bu, 0, 1); // 登记项 45/54
sub_140011F80(v3, sub_14003A350, 0x4E91A762u, 0, 1); // 登记项 46/54
sub_140011F80(v3, sub_14003A970, 0x9E5470ABu, 0, 1); // 登记项 47/54
sub_140011F80(v3, sub_14003B080, 0xA1D0B8C4u, 0, 1); // 登记项 48/54
sub_140011F80(v3, sub_14003ADE0, 0x6C62B11Bu, 0, 1); // 登记项 49/54
sub_140011F80(v3, sub_14003AFA0, 0x03A96E6Cu, 0, 1); // 登记项 50/54
sub_140011F80(v3, sub_14003AF30, 0x1F13E498u, 0, 1); // 登记项 51/54
sub_140011F80(v3, sub_14003AEC0, 0x32ED9C4Bu, 0, 1); // 登记项 52/54
sub_140011F80(v3, sub_14003AE50, 0xAE24A70Bu, 0, 1); // 登记项 53/54
sub_140011F80(v3, sub_14003A1A0, 0x4E63099Fu, 0, 1); // 登记项 54/54

/* 所有处理函数登记后,才开始读取、处理服务键中的 ConfigData。
* 实参仍是 UNICODE_STRING 结构指针,不能改为 RegistryPath->Buffer。
* 配置格式、各处理项的业务规则属于该子函数及其回调的内部逻辑。
*/
v2 = sub_1400124C0(RegistryPath);
// 按有符号 32 位 NTSTATUS 判断失败;非负状态继续执行。
if ( v2 < 0 )
{
sub_140011DD0(sub_14003BA90());
// 上一调用销毁处理表的两个容器;随后记录配置错误,传播原始状态。
sub_140018BB0(&unk_140098178, RegistryPath, 103, v2);
goto LABEL_FAILURE;
}

/* 配置处理成功后创建本驱动的设备与 DOS 符号链接。
* 已观察到该子函数调用 IoCreateDevice、IoCreateSymbolicLink,
* 并初始化设备扩展及相关资源。设备扩展大小为 0x200。
* 名称构造涉及配置;空配置模拟中看到的路径前缀不代表真实完整设备名。
* 派遣回调在该调用成功之后才安装,见后续 DriverObject 赋值。
*/
v2 = sub_140013300(sub_140013510(), DriverObject);
// 按有符号 32 位 NTSTATUS 判断失败;非负状态继续执行。
if ( v2 < 0 )
{
sub_140030330(sub_14002F320());
sub_140011DD0(sub_14003BA90());
// 此分支先清理 sub_14002F320 返回的对象,再销毁处理表;记录设备错误。
sub_140018BB0(&unk_140098178, RegistryPath, 104, v2);
goto LABEL_FAILURE;
}

/* 安装 IRP 派遣入口;此处赋值只登记地址,不立即执行处理函数。
* 下列偏移是当前 x64 DRIVER_OBJECT 布局下相对于 DriverObject 的偏移。
*/
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)sub_14003B6F0; // +0x70,IRP_MJ_CREATE:打开设备。
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)sub_14003B670; // +0x80,IRP_MJ_CLOSE:关闭设备。
DriverObject->MajorFunction[14] = (PDRIVER_DISPATCH)sub_14003B8C0; // +0xE0,IRP_MJ_DEVICE_CONTROL:设备控制请求。 NtDeviceIoControlFile
DriverObject->DriverUnload = (PDRIVER_UNLOAD)DriverUnload; // +0x68,卸载回调;不是 0x140001040 的外层 WDF 包装。

/* 获取/按需初始化包含 40 个 DWORD 的全局常量表,并登记相关清理回调。
* 原入口中的对应内联初始化,在伪代码中用这个现存等价 getter 表达。
* 返回指针在此处未使用;调用目的在于其初始化副作用。
*/
sub_140013BD0();
/* 后续初始化对象 0x140098170 的一次性构造及清理登记。
* 已分析的 sub_14001DED0 实现仅返回传入指针;不要将实际子系统启动
* 归因于这一步,真正的初始化调用是下面的 sub_14001DF70。
*/
if ( (dword_140098174 & 1) == 0 )
{
dword_140098174 |= 1u;
sub_14001DED0(&unk_140098170);
sub_140045520((__int64)sub_140047C90);
}

/* 启动后续子系统初始化。调用方只根据 NTSTATUS 决定成功或回滚。
* 该子函数内部包含更多初始化/清理;本文件不将尚未逐项确认的内部操作
* 概括成隐藏、注入、绕过等具体行为。
*/ //core
v2 = next_init(&unk_140098170);


// 按有符号 32 位 NTSTATUS 判断失败;非负状态继续执行。
if ( v2 < 0 )
{
sub_140030330(sub_14002F320());
// 前一步清理另一个全局对象;本步继续清理驱动上下文。
// 第二参数原值为十进制 10000(0x2710),未确认单位,不擅自标为毫秒。
sub_140013AD0(sub_140013510(), 10000);
sub_140018BB0(&unk_140098178, RegistryPath, 105, v2);
// 先将原始错误 v2 记入 LastStatus,再将对调用者的返回值改为固定值。
// 因此日志中的错误码与本入口最终返回码可能不同。
v2 = 0xC0000142;
goto LABEL_FAILURE;
}

// 所有受检查步骤均通过:LastState 阶段 102,随后返回成功。
// 此路径保留已建立的设备、派遣入口和运行所需状态,供后续请求使用。
sub_140018C80(&unk_140098178, RegistryPath, 102);
return 0;

/* 六类失败最终汇合到此处。不同分支在跳转前完成各自额外清理。
* 这里不能无条件补上处理表销毁或设备销毁:应保留原始路径的清理顺序,
* 同时区分本函数的显式清理与被调用函数内部已经执行的回滚。
*/
LABEL_FAILURE:
sub_140018C80(&unk_140098178, RegistryPath, 101); // LastState:本次初始化失败。
// 第二参数为 1:选择 UnLoadTime。内部路径缓冲区不存在时不会执行相同的注册表更新。
sub_140018C20((__int64)&unk_140098178, 1);
// 清理上下文:已确认将 ctx + 0x268 的 DWORD 清零,并释放 ctx + 0x210 的路径对象。
// 不应把它直接解释为释放整个全局上下文对象。
sub_140013A50(sub_140013510());
// 遍历先前登记的清理回调链,调用回调并释放链表节点。
// 登记采用头插,遍历从表头开始,因此回调按登记的逆序执行。
sub_140045570();
return v2; // 原始失败状态,或前面明确映射的 0xC0000355 / 0xC0000142。
}

里面做了一些大量的初始化,但实际上有意义的地方是 三个驱动回调,和next_init函数;

next_init函数后续被确定为业务初始化函数,在这个函数里就注册了我们之前在PChunter里发现的4个OB回调,3个系统回调,还创建了一个系统线程;

三个驱动回调

驱动回调还原的伪代码框架如下所示,需要提一嘴的是,创建和关闭回调没什么值得注意的;

但是控制回调,他做了一些事情:

  • 添加受保护线程和进程到对应管理表,后续OB模块和其他模块可能使用到;
  • 打开目标进程并终止(很可能检测到异常后对目标游戏进程杀死)
  • 遍历指定进程的句柄,对指向受保护进程及其线程的句柄降权(和我们之前外部的推测相匹配,算是之前绕过姿势的一个补丁)
  • 还会统计鼠标频率
  • 拥有虚拟机检测功能(和外部实验相呼应)
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
// 0x14003B6F0
NTSTATUS __fastcall CreateCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS Status;
unsigned __int64 v4;
_OWORD v5;
PEPROCESS Process;
__int64 Manager;
__int64 Context;
bool Created;

Status = 0;
Irp->IoStatus.Information = 0;
if ( ExGetPreviousMode() )
{
v4 = 0;
v5 = 0;
if ( !(unsigned __int8)sub_140012CC0(&v4, &v5) )
{
Status = (NTSTATUS)0x80000016;
goto COMPLETE;
}

Process = IoGetCurrentProcess();
Manager = sub_1400135D0();
Context = sub_1400194C0(Manager, Process, DeviceObject);
Created = false;
if ( !Context )
{
Process = IoGetCurrentProcess();
Context = sub_140018DF0(
Manager, Process, &v5, v4, &Status, DeviceObject);
if ( !Context )
goto COMPLETE;
Created = true;
}

Status = sub_14001A3F0(
sub_140035390(), Irp->Tail.Overlay.OriginalFileObject);
if ( Status )
{
if ( Created )
{
Process = IoGetCurrentProcess();
sub_140018F20(sub_1400135D0(), Process);
}
}
else
{
Process = IoGetCurrentProcess();
sub_140029F70(sub_14001DEF0(), Process);
}
}

COMPLETE:
Irp->IoStatus.Status = Status;
IofCompleteRequest(Irp, 0);
return Status;
}

// 0x14003B670
NTSTATUS __fastcall CloseCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
ExGetPreviousMode();
sub_14001A530(
sub_140035390(), Irp->Tail.Overlay.OriginalFileObject);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = 0;
IofCompleteRequest(Irp, 0);
return 0;
}

// 0x14003B8C0
NTSTATUS __fastcall ControlCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
PIO_STACK_LOCATION Stack;
NTSTATUS Status;

Stack = Irp->Tail.Overlay.CurrentStackLocation;
if ( (dword_140098184 & 1) == 0 )
{
dword_140098184 |= 1u;
sub_140013BB0(&unk_140098180);
sub_140045520(sub_140047CB0);
}

Status = sub_140013D80(&unk_140098180, DeviceObject, Irp, Stack);
if ( Status )
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
IofCompleteRequest(Irp, 0);
return Status;
}

next_init函数我就不放出来了,它实际上就是在初始化上述提到的7个回调,同时在最后还开启了一个系统线程;

虚拟化检测

我对它的虚拟化检测技术很好奇,进行了AI逆向,得出了如下的表;

检测手段中不存在匹配 VMware 等其他厂商字符串的手段;

其中,控制命令有三类,代表三种入口,可以不用理会,输出位也可以不用理会,主要是查看它的虚拟化检测手段:

控制命令 输出位/结果 检测手段 具体判定逻辑
0x80002034 0x1 CPUID Hypervisor 标志 CPUID(1).ECX 第 31 位为 1,则命中。
0x80002034 0x2 特殊 CPUID 叶比较 CPUID(0x13371337) 与 CPUID(0x40000000) 的 EAX、EBX、ECX、EDX 任意不同,则命中;不匹配厂商名称。
0x80002034 0x4 虚拟化 MSR 范围探测 遍历读取 MSR 0x40000000~0x400000FF,任意一次读取不触发异常,则命中。
0x80002034 0x8 CPU 能力与实际行为一致性检查 CPUID(6).ECX[0] 为 0,但 MSR 0xE7/0xE8 至少一个可读,则命中。
0x80002034 0x20 CR4.VMXE 检查 CR4 第 13 位为 1,则命中;只能说明 VMX 使能状态,不能独立证明处于虚拟机中。
0x80002034 0x40 TSC 测量 CPUID 耗时 关中断,以 LFENCE/RDTSC 测量 CPUID(0) 共 10 次;出现负差值,或最小差值大于 500,则命中。
0x80002034 0x80 APERF 测量 CPUID 耗时 要求 CPUID(6).ECX[0] 为 1;用 MSR 0xE8 测量 10 次,出现负差值或最小值不在 [11,500],则命中;异常提前结束后仍比较已保存的最小值。
0x80002034 0x100000 固定性能计数器测量 配置 MSR 0x38D/0x38F,用 MSR 0x30B 测量 10 次;负差值或最小值大于 500,则命中;异常返回 0。
0x80002034 0x200000 非法 XCR 编号测试 读取 XCR0 后尝试 XSETBV(0x45, 原XCR0值);不触发异常,则命中。
0x80002034 0x400000 不支持的 XCR0 位测试 根据 CPUID(0xD,0),逐个尝试设置不支持的 XCR0 位;设置成功,或异常后发现 XCR0 被改变,则命中并尝试恢复。
0x80002034 0x10、0x100、0x200 占位检测 对应函数直接返回 0,没有实质检测。 (意思是这条不用管)
0x80002035 类别 1 单步检测入口执行去向 对 NtWaitForDebugEvent、NtDebugActiveProcess 临时接管 #DB 并设置 TF,获取第一条指令执行后的 RIP;其低 32 位与“入口低 32 位+指令长度”不同,则命中。受配置及 Driver Verifier 门控影响,可能跳过。
0x80002035 类别 2 修改代码后验证执行结果 对上述两个目标保存入口 8 字节,把前 6 字节改成 mov eax,77777777h; ret,调用后恢复;返回值不等于 0x77777777,则命中。用于检查可读写代码与实际执行是否一致,不能单独确定为 EPT 隐藏钩子。
0x80002035 结果控制 两种代码一致性检测的组合 Mask bit0 启用单步检测,bit1 启用改码检测;类别 1 命中即结束,类别 2 不再执行。结果不是 OR 位图;接口解析失败不报告阳性。核心机制已识别,部分受保护边界及失败路径尚未完全核验。
0x80002036 状态 0/1/2/3 VMCALL 可执行性探测 先尝试参数 0x10F2C00,成功记 2;异常后尝试 0x4D3,成功记 3;均异常保持 0。最后尝试 0xFFFFFFFF,成功则把已有非零状态归一为 1,异常保留原状态;不校验响应内容或识别厂商。

下面我们给出七个系统回调;

七个系统回调

七个回调这里只给出框架,具体业务逻辑省略,不过我会口头叙述它们的业务逻辑;

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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
OB_PREOP_CALLBACK_STATUS __fastcall OBProcessFirst(
void *RegistrationContext, POB_PRE_OPERATION_INFORMATION Information)
{
_BYTE *manager = (_BYTE *)RegistrationContext;
_QWORD contexts[32];
_QWORD policyResult;
int count;
unsigned __int8 apcsDisabled;
void (__fastcall *callback)(_QWORD, POB_PRE_OPERATION_INFORMATION, _QWORD *);

if ( *manager )
{
policyResult = 0;

_InterlockedIncrement((volatile LONG *)(manager + 4));

if ( (dword_140097FB0 & 1) == 0 )
{

word_140097EF0 = 256;
dword_140097FB0 |= 1;
byte_140097EF2 = 0;

sub_140045520(nullsub_21);
}

if ( (unsigned __int8)sub_14002FE60(Information, &policyResult) )
{

memset(contexts, 0, sizeof(contexts));

count = sub_140018FB0(sub_1400135D0(), contexts);

for ( int i = 0; i < count; ++i )
{

apcsDisabled = sub_140019F40(sub_1400135D0());

if ( contexts[i] )
{
callback = *(void (__fastcall **)(_QWORD,
POB_PRE_OPERATION_INFORMATION, _QWORD *))(manager + 16);

if ( callback )

callback(contexts[i], Information, &policyResult);
}

sub_140018F00(sub_1400135D0(), apcsDisabled);
}
}

_InterlockedDecrement((volatile LONG *)(manager + 4));
}

return OB_PREOP_SUCCESS;
}

void __fastcall OBProcessLast(
void *RegistrationContext, POB_POST_OPERATION_INFORMATION Information)
{
_BYTE *manager = (_BYTE *)RegistrationContext;

if ( *manager )
{

_InterlockedIncrement((volatile LONG *)(manager + 4));

if ( (dword_140097FB0 & 1) == 0 )
{

word_140097EF0 = 256;
dword_140097FB0 |= 1;
byte_140097EF2 = 0;

sub_140045520(nullsub_21);
}

sub_14002FD60(Information);

_InterlockedDecrement((volatile LONG *)(manager + 4));
}
}

OB_PREOP_CALLBACK_STATUS __fastcall OBThreadFirst(
void *RegistrationContext, POB_PRE_OPERATION_INFORMATION Information)
{
_BYTE *manager = (_BYTE *)RegistrationContext;

if ( *manager )
{

_InterlockedIncrement((volatile LONG *)(manager + 4));

if ( (dword_140097FB0 & 1) == 0 )
{

word_140097EF0 = 256;
dword_140097FB0 |= 1;
byte_140097EF2 = 0;

sub_140045520(nullsub_21);
}

sub_1400301C0(Information);

_InterlockedDecrement((volatile LONG *)(manager + 4));
}

return OB_PREOP_SUCCESS;
}

void __fastcall OBThreadLast(
void *RegistrationContext, POB_POST_OPERATION_INFORMATION Information)
{
_BYTE *manager = (_BYTE *)RegistrationContext;

if ( *manager )
{

_InterlockedIncrement((volatile LONG *)(manager + 4));

if ( (dword_140097FB0 & 1) == 0 )
{

word_140097EF0 = 256;
dword_140097FB0 |= 1;
byte_140097EF2 = 0;

sub_140045520(nullsub_21);
}

sub_140030100(Information);

_InterlockedDecrement((volatile LONG *)(manager + 4));
}
}

void __fastcall SystemProcessCallback(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
_BYTE *manager;
_QWORD contexts[32];
_QWORD context;
int count;
unsigned __int8 apcsDisabled;
void (__fastcall *contextCallback)(_QWORD, PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO);
void (__fastcall **it)(PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO);
void (__fastcall **end)(PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO);

if ( !*(_BYTE *)sub_14001B5F0() )
return;

manager = (_BYTE *)sub_14001B5F0();

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 16));

memset(contexts, 0, sizeof(contexts));

count = sub_140018FB0(sub_1400135D0(), contexts);

for ( int i = 0; i < count; ++i )
{

apcsDisabled = sub_140019F40(sub_1400135D0());

context = contexts[i];

if ( context )
{
contextCallback = *(void (__fastcall **)(_QWORD, PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO))(manager + 8);

if ( contextCallback )

contextCallback(context, Process, ProcessId, CreateInfo);
}

sub_140018F00(sub_1400135D0(), apcsDisabled);

PERESOURCE resource = sub_1400135D0();

if ( !CreateInfo && context && MmIsAddressValid((void *)context)

&& *(PEPROCESS *)(context + 0x28) == Process )
{

IoGetCurrentProcess();

PDEVICE_OBJECT device = *(PDEVICE_OBJECT *)((_BYTE *)resource + 0x68);

_QWORD *slots = (_QWORD *)device->DeviceExtension;

unsigned __int8 cleanupLock = sub_140019F80(resource);
for ( unsigned int j = 0; j < 32; ++j )
{

if ( slots[2 * j + 1] == (_QWORD)Process )
{
if ( slots[2 * j] )
{

sub_14001A110(resource, &slots[2 * j]);

ExFreePoolWithTag((void *)slots[2 * j], 0);
}

slots[2 * j] = 0;

slots[2 * j + 1] = 0;
break;
}
}

sub_140018F00(resource, cleanupLock);

sub_140029E90(sub_14001DEF0(), Process);
}
}

sub_140018720(Process, ProcessId, CreateInfo);

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 16));

if ( *((_BYTE *)sub_140013BD0() + 0x20) )
{
manager = (_BYTE *)sub_14001B5F0();

if ( manager[24] )
{

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 16));
apcsDisabled = 0;

if ( *(LONG *)(manager + 160) >= 0 )

apcsDisabled = sub_140019F40((PERESOURCE)(manager + 56));

it = *(void (__fastcall ***)(PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO))(manager + 32);

end = *(void (__fastcall ***)(PEPROCESS, HANDLE, PPS_CREATE_NOTIFY_INFO))(manager + 40);

while ( it != end )
{

(*it)(Process, ProcessId, CreateInfo);
++it;
}

if ( *(LONG *)(manager + 160) >= 0 )

sub_140018F00((PERESOURCE)(manager + 56), apcsDisabled);

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 16));
}
}
}

void __fastcall SystemThreadCallback(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create)
{
_BYTE *manager;
_QWORD contexts[32];
_QWORD context;
int count;
unsigned __int8 apcsDisabled;
void (__fastcall *contextCallback)(_QWORD, HANDLE, HANDLE, BOOLEAN);
void (__fastcall **it)(HANDLE, HANDLE, BOOLEAN);
void (__fastcall **end)(HANDLE, HANDLE, BOOLEAN);

if ( !*(_BYTE *)sub_14002CA30() )
return;

manager = (_BYTE *)sub_14002CA30();

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 16));

memset(contexts, 0, sizeof(contexts));

count = sub_140018FB0(sub_1400135D0(), contexts);

for ( int i = 0; i < count; ++i )
{

apcsDisabled = sub_140019F40(sub_1400135D0());

context = contexts[i];

if ( context )
{
contextCallback = *(void (__fastcall **)(_QWORD, HANDLE, HANDLE, BOOLEAN))(manager + 8);

if ( contextCallback )

contextCallback(context, ProcessId, ThreadId, Create);

if ( (dword_140094DC0 & 1) == 0 )
{
dword_140094DC0 |= 1;

sub_140020880(&unk_140094CF0);

sub_140045520(sub_140047860);
}

_BYTE *timing = (_BYTE *)&unk_140094CF0;

if ( !_InterlockedCompareExchange((volatile LONG *)(timing + 0xC8), 1, 0) )
{

if ( !*(_QWORD *)(timing + 0x28) )
{
*(_QWORD *)(timing + 0x28) = sub_140041880();
}

else if ( (unsigned __int64)(sub_140041880()
- *(_QWORD *)(timing + 0x28)) >= 5000 )
{
*(_QWORD *)(timing + 0x28) = sub_140041880();

if ( !(unsigned __int8)sub_140021080(timing) )
{

LONG bits = *(LONG *)((_BYTE *)sub_14002F360() + 0x14) | 1;
_InterlockedExchange(
(volatile LONG *)((_BYTE *)sub_14002F360() + 0x14), bits);

if ( (dword_140094DC0 & 1) == 0 )
{
dword_140094DC0 |= 1;

sub_140020880(&unk_140094CF0);

sub_140045520(sub_140047860);
}
}
}

_InterlockedExchange((volatile LONG *)(timing + 0xC8), 0);
}
}

sub_140018F00(sub_1400135D0(), apcsDisabled);
}

sub_140015800();

sub_140032BA0(ProcessId, ThreadId, Create);

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 16));

if ( *((_BYTE *)sub_140013BD0() + 0x20) )
{
manager = (_BYTE *)sub_14002CA30();

if ( manager[24] )
{

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 16));
apcsDisabled = 0;

if ( *(LONG *)(manager + 160) >= 0 )

apcsDisabled = sub_140019F40((PERESOURCE)(manager + 56));

it = *(void (__fastcall ***)(HANDLE, HANDLE, BOOLEAN))(manager + 32);

end = *(void (__fastcall ***)(HANDLE, HANDLE, BOOLEAN))(manager + 40);

while ( it != end )
{

(*it)(ProcessId, ThreadId, Create);
++it;
}

if ( *(LONG *)(manager + 160) >= 0 )

sub_140018F00((PERESOURCE)(manager + 56), apcsDisabled);

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 16));
}
}
}

void __fastcall SystemLoadimageCallBack(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
{
_BYTE *manager;
_QWORD contexts[32];
_QWORD context;
int count;
unsigned __int8 apcsDisabled;
void (__fastcall *contextCallback)(_QWORD, PUNICODE_STRING, HANDLE, PIMAGE_INFO);
void (__fastcall **it)(PUNICODE_STRING, HANDLE, PIMAGE_INFO);
void (__fastcall **end)(PUNICODE_STRING, HANDLE, PIMAGE_INFO);

if ( !*(_BYTE *)sub_14002C9B0() )
return;

manager = (_BYTE *)sub_14002C9B0();

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 8));

memset(contexts, 0, sizeof(contexts));

count = sub_140018FB0(sub_1400135D0(), contexts);

for ( int i = 0; i < count; ++i )
{

apcsDisabled = sub_140019F40(sub_1400135D0());

context = contexts[i];

if ( context )
{
contextCallback = *(void (__fastcall **)(_QWORD, PUNICODE_STRING, HANDLE, PIMAGE_INFO))(manager + 16);

if ( contextCallback )

contextCallback(context, FullImageName, ProcessId, ImageInfo);
}

sub_140018F00(sub_1400135D0(), apcsDisabled);
}

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 8));

if ( *((_BYTE *)sub_140013BD0() + 0x20) )
{
manager = (_BYTE *)sub_14002C9B0();

if ( manager[24] )
{

ExAcquireRundownProtection((PEX_RUNDOWN_REF)(manager + 8));
apcsDisabled = 0;

if ( *(LONG *)(manager + 160) >= 0 )

apcsDisabled = sub_140019F40((PERESOURCE)(manager + 56));

it = *(void (__fastcall ***)(PUNICODE_STRING, HANDLE, PIMAGE_INFO))(manager + 32);

end = *(void (__fastcall ***)(PUNICODE_STRING, HANDLE, PIMAGE_INFO))(manager + 40);

while ( it != end )
{

(*it)(FullImageName, ProcessId, ImageInfo);
++it;
}

if ( *(LONG *)(manager + 160) >= 0 )

sub_140018F00((PERESOURCE)(manager + 56), apcsDisabled);

ExReleaseRundownProtection((PEX_RUNDOWN_REF)(manager + 8));
}
}
}

OB进程回调-上篇姿势失效的本质

先说两个OB进程回调吧,第一个First做的核心功能就是削弱降权,核心步骤为:

假设进程 A 要打开进程 B:

  1. 先检查 B 是否受保护(是否位于驱动管理的保护进程表中)。 不受保护,就不削减权限。
  2. 检查 A 是否符合内置系统进程规则。 名称匹配六种系统进程(比如lsass.exe),并通过账户判断,就豁免。
  3. 如果上一步没有放行,再查 A 的名称哈希是否在可配置白名单中。 命中也豁免,白名单可以包含非系统进程。
  4. 两层都没放行,且过滤开关开启,才削减 A 这次申请的句柄权限。

First和Last的OB回调高度都是 310520,它和Last回调还不一样,Last是Post类型,First是Pre类型;

Last的核心功能是:

1
2
3
4
5
if (GrantedAccess & 0xA7A)
{
LONG value = *GetCallbackStatusFlags() | 4;
InterlockedExchange(GetCallbackStatusFlags(), value);
}

检查最终的GrantedAccess是否真的被降权了,如果被恢复了权力,那么就会动一个标志位,或上4,这里就是也许为什么恢复句柄权限后,会被检测到的一个核心点;这个标志位在这个驱动模块内部,是个全局变量;

这里确实涉及到我的知识盲区了,还没深入学习系统回调,不知道OB回调有Pre和Post的区分;

所以这里解释了为什么之前写的那种绕过姿势不太行,之前虽然设置了一上一下的两个OB回调进行恢复权的操作,但都是Pre类型,确实恢复了句柄权限,但是被Post回调检测到了,而我们改Pre的DesiredAccess是真实改变句柄权限,在Post里面改GrantedAccess是没用的,GrantedAccess只是个通知字段,并不有效;

同时First的保护逻辑也有问题,如果把CE改名为csrss.exe或者lsass.exe这种它判断逻辑的白名单进程名,并以SYSTEM权限启动获取对应的SID,也能直接通过正常读写权限的句柄打开;但是目前这种方法貌似也存在判断点,以这种方法打开也会被检测到,这种方法的检测点还尚不清楚;

OB线程回调

线程的First也进行了权限削弱,判断条件分两路,一路是跨进程打开线程句柄,另一路是同进程打开线程句柄;两路都会遭到削权,削弱的幅度不一样;这也就解释了为什么CE里面线程调试功能失效的原因;

不过目前版本的线程Last并没有做和进程一样的处理,即设置异常标志位,用于后续操作,所以理论上,如果不存在其他检测点,那么线程绕过姿势还是可以通过上篇的方式绕过;

全局进程回调

它作用于每个进程创建和销毁时;

销毁时就是把OB设置的保护给撤销了,会操作同一个标志变量,和其他一些垃圾回收,资源清理工作;

创建时会记录创建进程的信息,比如PID和对应父进程PID,以及完整路径等等信息;把记录的信息放入一个共享内存区域;外部模块可以访问,但是这个驱动本身不会对这些记录的进程信息有任何行为;

至于线程回调的逻辑和进程回调的逻辑类似,加载镜像回调也是进行上报无其他特殊业务逻辑,就先不进一步跟踪了;

系统线程

它主要的功能:

  • 看有没有其他驱动碰它关注的进程内存,比如,它关注进程 A,发现某段内核代码访问了 A 的内存,就尝试记下:“哪个线程干的、来自哪个驱动、碰了哪个地址。”
    这里查到的是记录线索,没有看到它在这个部分直接杀掉对方。它也不是每次内存访问都能发现;
  • 它保护的线程是不是被暂停了,尝试让线程继续运行。比如某个受保护线程被挂起,它会尝试恢复,并记录这件事;
  • 看被检查的驱动有没有被别人改过,例如:代码内容变没变;原本应该执行的函数,有没有被换成其他地址;有没有其他驱动接到它的设备上。发现不一致就记录下来;
  • 还会做一些超时和鼠标统计检查;

这个线程主要是监控、记录、通知,维护,以及干预受保护线程,不会主动出击妨碍其他外部进程和驱动模块,也不会去进行杀死保护进程的操作;

下面就是7月份的时候仅通过外部实验,测试出的当时可行的版本;

外部实验通过代码

SYS模块

myMem.c

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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
#include <ntifs.h>
#include <ntddk.h>
#include <ntimage.h>

#define DEVICE_NAME L"\\Device\\MemReader"
#define SYMLINK_NAME L"\\DosDevices\\MemReader"

#ifndef PROCESS_VM_OPERATION
#define PROCESS_VM_OPERATION 0x0008
#endif

#ifndef PROCESS_QUERY_INFORMATION
#define PROCESS_QUERY_INFORMATION 0x0400
#endif

NTKERNELAPI
NTSTATUS NTAPI
MmCopyVirtualMemory(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize
);

NTKERNELAPI
NTSTATUS NTAPI
ZwProtectVirtualMemory(
HANDLE ProcessHandle,
PVOID* BaseAddress,
PSIZE_T NumberOfBytesToProtect,
ULONG NewAccessProtection,
PULONG OldAccessProtection
);

NTKERNELAPI
PVOID NTAPI
PsGetProcessSectionBaseAddress(
PEPROCESS Process
);

NTKERNELAPI
PCHAR NTAPI
PsGetProcessImageFileName(
PEPROCESS Process
);

#define IOCTL_READ_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_WRITE_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_GET_PROCESS_IMAGE \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_ALLOCATE_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_FREE_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_PROTECT_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_QUERY_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef struct _MEMORY_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
UCHAR Data[1];
} MEMORY_REQUEST, * PMEMORY_REQUEST;

typedef struct _PROCESS_IMAGE_REQUEST
{
ULONG ProcessId;
ULONGLONG BaseAddress;
ULONG ImageSize;
CHAR ImageName[16];
} PROCESS_IMAGE_REQUEST, * PPROCESS_IMAGE_REQUEST;

typedef struct _VIRTUAL_ALLOC_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG AllocationType;
ULONG Protect;
} VIRTUAL_ALLOC_REQUEST, * PVIRTUAL_ALLOC_REQUEST;

typedef struct _VIRTUAL_FREE_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG FreeType;
} VIRTUAL_FREE_REQUEST, * PVIRTUAL_FREE_REQUEST;

typedef struct _VIRTUAL_PROTECT_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG NewProtect;
ULONG OldProtect;
} VIRTUAL_PROTECT_REQUEST, * PVIRTUAL_PROTECT_REQUEST;

typedef struct _VIRTUAL_QUERY_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;

ULONGLONG BaseAddress;
ULONGLONG AllocationBase;
ULONG AllocationProtect;
SIZE_T RegionSize;
ULONG State;
ULONG Protect;
ULONG Type;
} VIRTUAL_QUERY_REQUEST, * PVIRTUAL_QUERY_REQUEST;

PDEVICE_OBJECT g_DeviceObject = NULL;

NTSTATUS OpenProcessById(
ULONG pid,
ACCESS_MASK desiredAccess,
PHANDLE processHandle
)
{
OBJECT_ATTRIBUTES objectAttributes;
CLIENT_ID clientId;

if (pid == 0 || processHandle == NULL)
{
return STATUS_INVALID_PARAMETER;
}

*processHandle = NULL;

InitializeObjectAttributes(
&objectAttributes,
NULL,
OBJ_KERNEL_HANDLE,
NULL,
NULL
);

clientId.UniqueProcess =
(HANDLE)(ULONG_PTR)pid;

clientId.UniqueThread = NULL;

return ZwOpenProcess(
processHandle,
desiredAccess,
&objectAttributes,
&clientId
);
}

NTSTATUS KernelReadMemory(
ULONG pid,
PVOID address,
PVOID buffer,
SIZE_T size,
PSIZE_T readSize
)
{
PEPROCESS process = NULL;
NTSTATUS status;

if (readSize)
{
*readSize = 0;
}

if (pid == 0 ||
address == NULL ||
buffer == NULL ||
size == 0 ||
readSize == NULL)
{
return STATUS_INVALID_PARAMETER;
}

status = PsLookupProcessByProcessId(
(HANDLE)(ULONG_PTR)pid,
&process
);

if (!NT_SUCCESS(status))
{
return status;
}

status = MmCopyVirtualMemory(
process,
address,
PsGetCurrentProcess(),
buffer,
size,
KernelMode,
readSize
);

ObDereferenceObject(process);
return status;
}

NTSTATUS KernelWriteMemory(
ULONG pid,
PVOID address,
PVOID buffer,
SIZE_T size,
PSIZE_T writeSize
)
{
PEPROCESS process = NULL;
HANDLE processHandle = NULL;

PVOID protectBase = address;
SIZE_T protectSize = size;
ULONG oldProtect = 0;
SIZE_T written = 0;
NTSTATUS status;

if (writeSize)
{
*writeSize = 0;
}

if (pid == 0 ||
address == NULL ||
buffer == NULL ||
size == 0 ||
writeSize == NULL)
{
return STATUS_INVALID_PARAMETER;
}

status = PsLookupProcessByProcessId(
(HANDLE)(ULONG_PTR)pid,
&process
);

if (!NT_SUCCESS(status))
{
return status;
}

status = OpenProcessById(
pid,
PROCESS_VM_OPERATION,
&processHandle
);

if (!NT_SUCCESS(status))
{
ObDereferenceObject(process);
return status;
}

status = ZwProtectVirtualMemory(
processHandle,
&protectBase,
&protectSize,
PAGE_EXECUTE_READWRITE,
&oldProtect
);

if (status == STATUS_INVALID_PAGE_PROTECTION)
{
protectBase = address;
protectSize = size;
oldProtect = 0;

status = ZwProtectVirtualMemory(
processHandle,
&protectBase,
&protectSize,
PAGE_READWRITE,
&oldProtect
);
}

if (NT_SUCCESS(status))
{
status = MmCopyVirtualMemory(
PsGetCurrentProcess(),
buffer,
process,
address,
size,
KernelMode,
&written
);

{
PVOID restoreBase = protectBase;
SIZE_T restoreSize = protectSize;
ULONG ignoredProtect = 0;

ZwProtectVirtualMemory(
processHandle,
&restoreBase,
&restoreSize,
oldProtect,
&ignoredProtect
);
}

if (NT_SUCCESS(status) &&
written != size)
{
status = STATUS_PARTIAL_COPY;
}
}

if (NT_SUCCESS(status))
{
*writeSize = written;
}

ZwClose(processHandle);
ObDereferenceObject(process);

return status;
}

NTSTATUS KernelAllocateVirtualMemory(
ULONG pid,
PULONGLONG address,
PSIZE_T size,
ULONG allocationType,
ULONG protect
)
{
HANDLE processHandle = NULL;
PVOID baseAddress;
SIZE_T regionSize;
NTSTATUS status;

if (pid == 0 ||
address == NULL ||
size == NULL ||
*size == 0)
{
return STATUS_INVALID_PARAMETER;
}

status = OpenProcessById(
pid,
PROCESS_VM_OPERATION,
&processHandle
);

if (!NT_SUCCESS(status))
{
return status;
}

baseAddress =
(PVOID)(ULONG_PTR)(*address);

regionSize = *size;

status = ZwAllocateVirtualMemory(
processHandle,
&baseAddress,
0,
&regionSize,
allocationType,
protect
);

if (NT_SUCCESS(status))
{
*address =
(ULONGLONG)(ULONG_PTR)baseAddress;

*size = regionSize;
}

ZwClose(processHandle);
return status;
}

NTSTATUS KernelFreeVirtualMemory(
ULONG pid,
PULONGLONG address,
PSIZE_T size,
ULONG freeType
)
{
HANDLE processHandle = NULL;
PVOID baseAddress;
SIZE_T regionSize;
NTSTATUS status;

if (pid == 0 ||
address == NULL ||
*address == 0 ||
size == NULL)
{
return STATUS_INVALID_PARAMETER;
}

status = OpenProcessById(
pid,
PROCESS_VM_OPERATION,
&processHandle
);

if (!NT_SUCCESS(status))
{
return status;
}

baseAddress =
(PVOID)(ULONG_PTR)(*address);

regionSize = *size;

status = ZwFreeVirtualMemory(
processHandle,
&baseAddress,
&regionSize,
freeType
);

if (NT_SUCCESS(status))
{
*address =
(ULONGLONG)(ULONG_PTR)baseAddress;

*size = regionSize;
}

ZwClose(processHandle);
return status;
}

NTSTATUS KernelProtectVirtualMemory(
ULONG pid,
PULONGLONG address,
PSIZE_T size,
ULONG newProtect,
PULONG oldProtect
)
{
HANDLE processHandle = NULL;
PVOID baseAddress;
SIZE_T regionSize;
NTSTATUS status;

if (pid == 0 ||
address == NULL ||
*address == 0 ||
size == NULL ||
*size == 0 ||
oldProtect == NULL)
{
return STATUS_INVALID_PARAMETER;
}

*oldProtect = 0;

status = OpenProcessById(
pid,
PROCESS_VM_OPERATION,
&processHandle
);

if (!NT_SUCCESS(status))
{
return status;
}

baseAddress =
(PVOID)(ULONG_PTR)(*address);

regionSize = *size;

status = ZwProtectVirtualMemory(
processHandle,
&baseAddress,
&regionSize,
newProtect,
oldProtect
);

if (NT_SUCCESS(status))
{
*address =
(ULONGLONG)(ULONG_PTR)baseAddress;

*size = regionSize;
}

ZwClose(processHandle);
return status;
}

NTSTATUS KernelQueryVirtualMemory(
ULONG pid,
ULONGLONG address,
PMEMORY_BASIC_INFORMATION information
)
{
HANDLE processHandle = NULL;
SIZE_T returnedLength = 0;
NTSTATUS status;

if (pid == 0 || information == NULL)
{
return STATUS_INVALID_PARAMETER;
}

RtlZeroMemory(
information,
sizeof(*information)
);

status = OpenProcessById(
pid,
PROCESS_QUERY_INFORMATION,
&processHandle
);

if (!NT_SUCCESS(status))
{
return status;
}

status = ZwQueryVirtualMemory(
processHandle,
(PVOID)(ULONG_PTR)address,
MemoryBasicInformation,
information,
sizeof(*information),
&returnedLength
);

ZwClose(processHandle);
return status;
}

NTSTATUS KernelGetProcessImage(
ULONG pid,
PULONGLONG baseAddress,
PULONG imageSize,
PCHAR imageName,
SIZE_T imageNameLength
)
{
PEPROCESS process = NULL;
PVOID imageBase;
PCHAR processName;

IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS64 ntHeaders;

SIZE_T bytesRead = 0;
SIZE_T index;
NTSTATUS status;

if (pid == 0 ||
baseAddress == NULL ||
imageSize == NULL ||
imageName == NULL ||
imageNameLength == 0)
{
return STATUS_INVALID_PARAMETER;
}

*baseAddress = 0;
*imageSize = 0;

RtlZeroMemory(
imageName,
imageNameLength
);

status = PsLookupProcessByProcessId(
(HANDLE)(ULONG_PTR)pid,
&process
);

if (!NT_SUCCESS(status))
{
return status;
}

imageBase =
PsGetProcessSectionBaseAddress(process);

if (imageBase == NULL)
{
ObDereferenceObject(process);
return STATUS_NOT_FOUND;
}

*baseAddress =
(ULONGLONG)(ULONG_PTR)imageBase;

processName =
PsGetProcessImageFileName(process);

if (processName != NULL)
{
__try
{
for (index = 0;
index + 1 < imageNameLength &&
processName[index] != '\0';
++index)
{
imageName[index] =
processName[index];
}

imageName[index] = '\0';
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
RtlZeroMemory(
imageName,
imageNameLength
);
}
}

status = KernelReadMemory(
pid,
imageBase,
&dosHeader,
sizeof(dosHeader),
&bytesRead
);

if (!NT_SUCCESS(status) ||
bytesRead != sizeof(dosHeader) ||
dosHeader.e_magic != IMAGE_DOS_SIGNATURE ||
dosHeader.e_lfanew <= 0 ||
dosHeader.e_lfanew > 0x100000)
{
ObDereferenceObject(process);

return NT_SUCCESS(status)
? STATUS_INVALID_IMAGE_FORMAT
: status;
}

bytesRead = 0;

status = KernelReadMemory(
pid,
(PVOID)(
(ULONG_PTR)imageBase +
(ULONG_PTR)dosHeader.e_lfanew
),
&ntHeaders,
sizeof(ntHeaders),
&bytesRead
);

if (!NT_SUCCESS(status) ||
bytesRead <
FIELD_OFFSET(
IMAGE_NT_HEADERS64,
OptionalHeader
) +
FIELD_OFFSET(
IMAGE_OPTIONAL_HEADER64,
SizeOfImage
) +
sizeof(
ntHeaders.OptionalHeader.SizeOfImage
) ||
ntHeaders.Signature != IMAGE_NT_SIGNATURE)
{
ObDereferenceObject(process);

return NT_SUCCESS(status)
? STATUS_INVALID_IMAGE_FORMAT
: status;
}

*imageSize =
ntHeaders.OptionalHeader.SizeOfImage;

ObDereferenceObject(process);
return STATUS_SUCCESS;
}

NTSTATUS DispatchCreateClose(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
UNREFERENCED_PARAMETER(DeviceObject);

Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;

IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);

return STATUS_SUCCESS;
}

NTSTATUS DispatchDeviceControl(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PIO_STACK_LOCATION stack;
ULONG code;
ULONG inputLength;
ULONG outputLength;

NTSTATUS status =
STATUS_INVALID_DEVICE_REQUEST;

UNREFERENCED_PARAMETER(DeviceObject);

stack =
IoGetCurrentIrpStackLocation(Irp);

code =
stack->Parameters.DeviceIoControl.IoControlCode;

inputLength =
stack->Parameters.DeviceIoControl.InputBufferLength;

outputLength =
stack->Parameters.DeviceIoControl.OutputBufferLength;

Irp->IoStatus.Information = 0;

switch (code)
{
case IOCTL_READ_MEMORY:
{
PMEMORY_REQUEST request =
(PMEMORY_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

SIZE_T headerSize =
FIELD_OFFSET(MEMORY_REQUEST, Data);

SIZE_T bytesRead = 0;

if (request == NULL ||
inputLength < headerSize ||
outputLength < headerSize ||
request->ProcessId == 0 ||
request->Address == 0 ||
request->Size == 0 ||
request->Size >
outputLength - headerSize)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelReadMemory(
request->ProcessId,
(PVOID)(ULONG_PTR)request->Address,
request->Data,
request->Size,
&bytesRead
);

if (NT_SUCCESS(status))
{
Irp->IoStatus.Information =
headerSize + bytesRead;
}

break;
}

case IOCTL_WRITE_MEMORY:
{
PMEMORY_REQUEST request =
(PMEMORY_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

SIZE_T headerSize =
FIELD_OFFSET(MEMORY_REQUEST, Data);

SIZE_T bytesWritten = 0;

if (request == NULL ||
inputLength < headerSize ||
request->ProcessId == 0 ||
request->Address == 0 ||
request->Size == 0 ||
request->Size >
inputLength - headerSize)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelWriteMemory(
request->ProcessId,
(PVOID)(ULONG_PTR)request->Address,
request->Data,
request->Size,
&bytesWritten
);

break;
}

case IOCTL_GET_PROCESS_IMAGE:
{
PPROCESS_IMAGE_REQUEST request =
(PPROCESS_IMAGE_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

if (request == NULL ||
inputLength < sizeof(*request) ||
outputLength < sizeof(*request) ||
request->ProcessId == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelGetProcessImage(
request->ProcessId,
&request->BaseAddress,
&request->ImageSize,
request->ImageName,
sizeof(request->ImageName)
);

if (NT_SUCCESS(status))
{
Irp->IoStatus.Information =
sizeof(*request);
}

break;
}

case IOCTL_ALLOCATE_VIRTUAL_MEMORY:
{
PVIRTUAL_ALLOC_REQUEST request =
(PVIRTUAL_ALLOC_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

if (request == NULL ||
inputLength < sizeof(*request) ||
outputLength < sizeof(*request) ||
request->ProcessId == 0 ||
request->Size == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelAllocateVirtualMemory(
request->ProcessId,
&request->Address,
&request->Size,
request->AllocationType,
request->Protect
);

if (NT_SUCCESS(status))
{
Irp->IoStatus.Information =
sizeof(*request);
}

break;
}

case IOCTL_FREE_VIRTUAL_MEMORY:
{
PVIRTUAL_FREE_REQUEST request =
(PVIRTUAL_FREE_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

if (request == NULL ||
inputLength < sizeof(*request) ||
request->ProcessId == 0 ||
request->Address == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelFreeVirtualMemory(
request->ProcessId,
&request->Address,
&request->Size,
request->FreeType
);

if (NT_SUCCESS(status) &&
outputLength >= sizeof(*request))
{
Irp->IoStatus.Information =
sizeof(*request);
}

break;
}

case IOCTL_PROTECT_VIRTUAL_MEMORY:
{
PVIRTUAL_PROTECT_REQUEST request =
(PVIRTUAL_PROTECT_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

if (request == NULL ||
inputLength < sizeof(*request) ||
outputLength < sizeof(*request) ||
request->ProcessId == 0 ||
request->Address == 0 ||
request->Size == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

status = KernelProtectVirtualMemory(
request->ProcessId,
&request->Address,
&request->Size,
request->NewProtect,
&request->OldProtect
);

if (NT_SUCCESS(status))
{
Irp->IoStatus.Information =
sizeof(*request);
}

break;
}

case IOCTL_QUERY_VIRTUAL_MEMORY:
{
PVIRTUAL_QUERY_REQUEST request =
(PVIRTUAL_QUERY_REQUEST)
Irp->AssociatedIrp.SystemBuffer;

MEMORY_BASIC_INFORMATION information;

if (request == NULL ||
inputLength < sizeof(*request) ||
outputLength < sizeof(*request) ||
request->ProcessId == 0)
{
status = STATUS_BUFFER_TOO_SMALL;
break;
}

RtlZeroMemory(
&information,
sizeof(information)
);

status = KernelQueryVirtualMemory(
request->ProcessId,
request->Address,
&information
);

if (NT_SUCCESS(status))
{
request->BaseAddress =
(ULONGLONG)(ULONG_PTR)
information.BaseAddress;

request->AllocationBase =
(ULONGLONG)(ULONG_PTR)
information.AllocationBase;

request->AllocationProtect =
information.AllocationProtect;

request->RegionSize =
information.RegionSize;

request->State =
information.State;

request->Protect =
information.Protect;

request->Type =
information.Type;

Irp->IoStatus.Information =
sizeof(*request);
}

break;
}
}

Irp->IoStatus.Status = status;

IoCompleteRequest(
Irp,
IO_NO_INCREMENT
);

return status;
}

VOID DriverUnload(
PDRIVER_OBJECT DriverObject
)
{
UNICODE_STRING link;

RtlInitUnicodeString(
&link,
SYMLINK_NAME
);

IoDeleteSymbolicLink(&link);

if (DriverObject->DeviceObject)
{
IoDeleteDevice(
DriverObject->DeviceObject
);
}
}

NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
UNICODE_STRING deviceName;
UNICODE_STRING linkName;

UNREFERENCED_PARAMETER(RegistryPath);

RtlInitUnicodeString(
&deviceName,
DEVICE_NAME
);

RtlInitUnicodeString(
&linkName,
SYMLINK_NAME
);

status = IoCreateDevice(
DriverObject,
0,
&deviceName,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&g_DeviceObject
);

if (!NT_SUCCESS(status))
{
return status;
}

status = IoCreateSymbolicLink(
&linkName,
&deviceName
);

if (!NT_SUCCESS(status))
{
IoDeleteDevice(g_DeviceObject);
g_DeviceObject = NULL;

return status;
}

DriverObject->MajorFunction[IRP_MJ_CREATE] =
DispatchCreateClose;

DriverObject->MajorFunction[IRP_MJ_CLOSE] =
DispatchCreateClose;

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
DispatchDeviceControl;

DriverObject->DriverUnload =
DriverUnload;

return STATUS_SUCCESS;
}

DLL模块

这个DLL是注入CE进程的,不是目标游戏进程,目的是为了改变CE的功能;

dllmain.cpp

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
#include <Windows.h>

#include "Hook.h"
#include "Driver.h"

BOOL APIENTRY DllMain(
HMODULE hModule,
DWORD reason,
LPVOID
)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);

OutputDebugStringA(
"[CEHook] DLL loaded"
);

if (!InitDriver())
{
OutputDebugStringA(
"[CEHook] initialization stopped: driver unavailable"
);

return TRUE;
}

if (!InitHook())
{
OutputDebugStringA(
"[CEHook] initialization stopped: API lookup failed"
);

CloseDriver();
return TRUE;
}

if (!Hook() ||
!HookWriteProcessMemory() ||
!HookModuleEnumeration() ||
!HookVirtualMemory())
{
OutputDebugStringA(
"[CEHook] one or more API hooks failed"
);
}
}
else if (reason == DLL_PROCESS_DETACH)
{
UnHookVirtualMemory();
UnHookModuleEnumeration();
UnHookWriteProcessMemory();
UnHook();

CloseDriver();
}

return TRUE;
}

Driver.h

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
#pragma once

#include <Windows.h>

#define IOCTL_READ_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_WRITE_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_GET_PROCESS_IMAGE \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_ALLOCATE_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_FREE_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_PROTECT_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)

#define IOCTL_QUERY_VIRTUAL_MEMORY \
CTL_CODE(FILE_DEVICE_UNKNOWN, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef struct _MEMORY_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
UCHAR Data[1];
} MEMORY_REQUEST, * PMEMORY_REQUEST;

typedef struct _PROCESS_IMAGE_REQUEST
{
ULONG ProcessId;
ULONGLONG BaseAddress;
ULONG ImageSize;
CHAR ImageName[16];
} PROCESS_IMAGE_REQUEST, * PPROCESS_IMAGE_REQUEST;

typedef struct _VIRTUAL_ALLOC_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG AllocationType;
ULONG Protect;
} VIRTUAL_ALLOC_REQUEST, * PVIRTUAL_ALLOC_REQUEST;

typedef struct _VIRTUAL_FREE_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG FreeType;
} VIRTUAL_FREE_REQUEST, * PVIRTUAL_FREE_REQUEST;

typedef struct _VIRTUAL_PROTECT_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;
SIZE_T Size;
ULONG NewProtect;
ULONG OldProtect;
} VIRTUAL_PROTECT_REQUEST, * PVIRTUAL_PROTECT_REQUEST;

typedef struct _VIRTUAL_QUERY_REQUEST
{
ULONG ProcessId;
ULONGLONG Address;

ULONGLONG BaseAddress;
ULONGLONG AllocationBase;
ULONG AllocationProtect;
SIZE_T RegionSize;
ULONG State;
ULONG Protect;
ULONG Type;
} VIRTUAL_QUERY_REQUEST, * PVIRTUAL_QUERY_REQUEST;

BOOL InitDriver();
VOID CloseDriver();

BOOL DriverReadMemory(
DWORD pid,
ULONGLONG address,
LPVOID buffer,
SIZE_T size
);

BOOL DriverWriteMemory(
DWORD pid,
ULONGLONG address,
LPCVOID buffer,
SIZE_T size
);

BOOL DriverGetProcessImage(
DWORD pid,
PULONGLONG baseAddress,
PDWORD imageSize,
LPSTR imageName,
SIZE_T imageNameLength
);

BOOL DriverAllocateVirtualMemory(
DWORD pid,
PULONGLONG address,
PSIZE_T size,
DWORD allocationType,
DWORD protect
);

BOOL DriverFreeVirtualMemory(
DWORD pid,
ULONGLONG address,
SIZE_T size,
DWORD freeType
);

BOOL DriverProtectVirtualMemory(
DWORD pid,
ULONGLONG address,
SIZE_T size,
DWORD newProtect,
PDWORD oldProtect
);

BOOL DriverQueryVirtualMemory(
DWORD pid,
ULONGLONG address,
PMEMORY_BASIC_INFORMATION information
);

Driver.cpp

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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
#include "Driver.h"

#include <stdio.h>
#include <stdlib.h>

static HANDLE g_hDevice = NULL;

BOOL InitDriver()
{
if (g_hDevice)
{
return TRUE;
}

g_hDevice = CreateFileA(
"\\\\.\\MemReader",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);

if (g_hDevice == INVALID_HANDLE_VALUE)
{
g_hDevice = NULL;
OutputDebugStringA("[CEHook] Driver open failed");
return FALSE;
}

OutputDebugStringA("[CEHook] Driver connected");
return TRUE;
}

VOID CloseDriver()
{
if (g_hDevice)
{
CloseHandle(g_hDevice);
g_hDevice = NULL;
}
}

BOOL DriverReadMemory(
DWORD pid,
ULONGLONG address,
LPVOID buffer,
SIZE_T size
)
{
const SIZE_T headerSize =
FIELD_OFFSET(MEMORY_REQUEST, Data);

SIZE_T requestSize;
PMEMORY_REQUEST request;
DWORD returnedLength = 0;
BOOL result;

if (!g_hDevice ||
pid == 0 ||
address == 0 ||
buffer == NULL ||
size == 0 ||
size > MAXDWORD ||
headerSize > MAXDWORD ||
size > MAXDWORD - headerSize)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

requestSize = headerSize + size;
request = (PMEMORY_REQUEST)malloc(requestSize);

if (!request)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}

ZeroMemory(request, requestSize);

request->ProcessId = pid;
request->Address = address;
request->Size = size;

result = DeviceIoControl(
g_hDevice,
IOCTL_READ_MEMORY,
request,
(DWORD)requestSize,
request,
(DWORD)requestSize,
&returnedLength,
NULL
);

if (result &&
returnedLength >= headerSize + size)
{
memcpy(buffer, request->Data, size);
SetLastError(ERROR_SUCCESS);
}
else
{
if (result)
{
SetLastError(ERROR_PARTIAL_COPY);
}

result = FALSE;
}

free(request);
return result;
}

BOOL DriverWriteMemory(
DWORD pid,
ULONGLONG address,
LPCVOID buffer,
SIZE_T size
)
{
const SIZE_T headerSize =
FIELD_OFFSET(MEMORY_REQUEST, Data);

SIZE_T requestSize;
PMEMORY_REQUEST request;
DWORD returnedLength = 0;
BOOL result;

if (!g_hDevice ||
pid == 0 ||
address == 0 ||
buffer == NULL ||
size == 0 ||
size > MAXDWORD ||
headerSize > MAXDWORD ||
size > MAXDWORD - headerSize)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

requestSize = headerSize + size;
request = (PMEMORY_REQUEST)malloc(requestSize);

if (!request)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}

ZeroMemory(request, requestSize);

request->ProcessId = pid;
request->Address = address;
request->Size = size;

memcpy(request->Data, buffer, size);

result = DeviceIoControl(
g_hDevice,
IOCTL_WRITE_MEMORY,
request,
(DWORD)requestSize,
NULL,
0,
&returnedLength,
NULL
);

if (result)
{
SetLastError(ERROR_SUCCESS);
}

free(request);
return result;
}

BOOL DriverGetProcessImage(
DWORD pid,
PULONGLONG baseAddress,
PDWORD imageSize,
LPSTR imageName,
SIZE_T imageNameLength
)
{
PROCESS_IMAGE_REQUEST request = { 0 };
DWORD returnedLength = 0;

if (!g_hDevice ||
pid == 0 ||
baseAddress == NULL ||
imageSize == NULL ||
imageName == NULL ||
imageNameLength == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

request.ProcessId = pid;

if (!DeviceIoControl(
g_hDevice,
IOCTL_GET_PROCESS_IMAGE,
&request,
sizeof(request),
&request,
sizeof(request),
&returnedLength,
NULL) ||
returnedLength < sizeof(request))
{
return FALSE;
}

*baseAddress = request.BaseAddress;
*imageSize = request.ImageSize;

strncpy_s(
imageName,
imageNameLength,
request.ImageName,
_TRUNCATE
);

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL DriverAllocateVirtualMemory(
DWORD pid,
PULONGLONG address,
PSIZE_T size,
DWORD allocationType,
DWORD protect
)
{
VIRTUAL_ALLOC_REQUEST request = { 0 };
DWORD returnedLength = 0;

if (!g_hDevice ||
pid == 0 ||
address == NULL ||
size == NULL ||
*size == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

request.ProcessId = pid;
request.Address = *address;
request.Size = *size;
request.AllocationType = allocationType;
request.Protect = protect;

if (!DeviceIoControl(
g_hDevice,
IOCTL_ALLOCATE_VIRTUAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request),
&returnedLength,
NULL) ||
returnedLength < sizeof(request))
{
return FALSE;
}

*address = request.Address;
*size = request.Size;

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL DriverFreeVirtualMemory(
DWORD pid,
ULONGLONG address,
SIZE_T size,
DWORD freeType
)
{
VIRTUAL_FREE_REQUEST request = { 0 };
DWORD returnedLength = 0;

if (!g_hDevice ||
pid == 0 ||
address == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

request.ProcessId = pid;
request.Address = address;
request.Size = size;
request.FreeType = freeType;

if (!DeviceIoControl(
g_hDevice,
IOCTL_FREE_VIRTUAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request),
&returnedLength,
NULL))
{
return FALSE;
}

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL DriverProtectVirtualMemory(
DWORD pid,
ULONGLONG address,
SIZE_T size,
DWORD newProtect,
PDWORD oldProtect
)
{
VIRTUAL_PROTECT_REQUEST request = { 0 };
DWORD returnedLength = 0;

if (!g_hDevice ||
pid == 0 ||
address == 0 ||
size == 0 ||
oldProtect == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

request.ProcessId = pid;
request.Address = address;
request.Size = size;
request.NewProtect = newProtect;

if (!DeviceIoControl(
g_hDevice,
IOCTL_PROTECT_VIRTUAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request),
&returnedLength,
NULL) ||
returnedLength < sizeof(request))
{
return FALSE;
}

*oldProtect = request.OldProtect;

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL DriverQueryVirtualMemory(
DWORD pid,
ULONGLONG address,
PMEMORY_BASIC_INFORMATION information
)
{
VIRTUAL_QUERY_REQUEST request = { 0 };
DWORD returnedLength = 0;

if (!g_hDevice ||
pid == 0 ||
information == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

request.ProcessId = pid;
request.Address = address;

if (!DeviceIoControl(
g_hDevice,
IOCTL_QUERY_VIRTUAL_MEMORY,
&request,
sizeof(request),
&request,
sizeof(request),
&returnedLength,
NULL) ||
returnedLength < sizeof(request))
{
return FALSE;
}

ZeroMemory(information, sizeof(*information));

information->BaseAddress =
(PVOID)(ULONG_PTR)request.BaseAddress;

information->AllocationBase =
(PVOID)(ULONG_PTR)request.AllocationBase;

information->AllocationProtect =
request.AllocationProtect;

information->RegionSize =
request.RegionSize;

information->State =
request.State;

information->Protect =
request.Protect;

information->Type =
request.Type;

SetLastError(ERROR_SUCCESS);
return TRUE;
}

Hook.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once

BOOL InitHook();

BOOL Hook();
BOOL UnHook();

BOOL HookWriteProcessMemory();
BOOL UnHookWriteProcessMemory();

BOOL HookModuleEnumeration();
BOOL UnHookModuleEnumeration();

BOOL HookVirtualMemory();
BOOL UnHookVirtualMemory();

Hook.cpp

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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
#include <stdio.h>
#include <Windows.h>
#include <TlHelp32.h>

#include "Hook.h"
#include "Driver.h"

typedef unsigned long long QWORD;

typedef struct _API_HOOK
{
QWORD Address;
BYTE Original[12];
BYTE Jump[12];
BOOL Installed;
} API_HOOK, * PAPI_HOOK;

static API_HOOK g_readHook = { 0 };
static API_HOOK g_writeHook = { 0 };

static API_HOOK g_createSnapshotHook = { 0 };
static API_HOOK g_moduleFirstWHook = { 0 };
static API_HOOK g_moduleFirstAHook = { 0 };

static API_HOOK g_virtualAllocExHook = { 0 };
static API_HOOK g_virtualFreeExHook = { 0 };
static API_HOOK g_virtualProtectExHook = { 0 };
static API_HOOK g_virtualQueryExHook = { 0 };

static const BYTE g_absoluteJumpTemplate[12] =
{
0x48, 0xB8,
0, 0, 0, 0, 0, 0, 0, 0,
0xFF, 0xE0
};

typedef struct _MODULEENTRY32A_COMPAT
{
DWORD dwSize;
DWORD th32ModuleID;
DWORD th32ProcessID;
DWORD GlblcntUsage;
DWORD ProccntUsage;
BYTE* modBaseAddr;
DWORD modBaseSize;
HMODULE hModule;
CHAR szModule[MAX_MODULE_NAME32 + 1];
CHAR szExePath[MAX_PATH];
} MODULEENTRY32A_COMPAT, * PMODULEENTRY32A_COMPAT;

typedef struct _MODULE_SNAPSHOT_RECORD
{
HANDLE Snapshot;
DWORD ProcessId;
} MODULE_SNAPSHOT_RECORD;

static SRWLOCK g_snapshotLock = SRWLOCK_INIT;
static MODULE_SNAPSHOT_RECORD g_snapshotRecords[64] = { 0 };
static ULONG g_nextSnapshotSlot = 0;

typedef HANDLE(WINAPI* CreateToolhelp32SnapshotP)(
DWORD flags,
DWORD processId
);

typedef BOOL(WINAPI* Module32FirstWP)(
HANDLE snapshot,
LPMODULEENTRY32W entry
);

typedef BOOL(WINAPI* Module32FirstAP)(
HANDLE snapshot,
PMODULEENTRY32A_COMPAT entry
);

static BOOL PatchCode(
QWORD address,
const BYTE* code,
SIZE_T codeSize
)
{
DWORD oldProtect = 0;
DWORD ignoredProtect = 0;

if (!address ||
!code ||
codeSize == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}

if (!VirtualProtect(
(LPVOID)(ULONG_PTR)address,
codeSize,
PAGE_EXECUTE_READWRITE,
&oldProtect))
{
return FALSE;
}

memcpy(
(LPVOID)(ULONG_PTR)address,
code,
codeSize
);

FlushInstructionCache(
GetCurrentProcess(),
(LPCVOID)(ULONG_PTR)address,
codeSize
);

VirtualProtect(
(LPVOID)(ULONG_PTR)address,
codeSize,
oldProtect,
&ignoredProtect
);

return TRUE;
}

static BOOL InitializeApiHook(
PAPI_HOOK hook,
HMODULE module,
LPCSTR apiName,
PVOID destination
)
{
FARPROC address;

if (!hook ||
!module ||
!apiName ||
!destination)
{
return FALSE;
}

address = GetProcAddress(module, apiName);

if (!address)
{
return FALSE;
}

ZeroMemory(hook, sizeof(*hook));

hook->Address =
(QWORD)(ULONG_PTR)address;

memcpy(
hook->Original,
address,
sizeof(hook->Original)
);

memcpy(
hook->Jump,
g_absoluteJumpTemplate,
sizeof(hook->Jump)
);

*(PULONGLONG)(hook->Jump + 2) =
(ULONGLONG)(ULONG_PTR)destination;

return TRUE;
}

static BOOL InstallApiHook(PAPI_HOOK hook)
{
if (!hook || !hook->Address)
{
return FALSE;
}

if (hook->Installed)
{
return TRUE;
}

if (!PatchCode(
hook->Address,
hook->Jump,
sizeof(hook->Jump)))
{
return FALSE;
}

hook->Installed = TRUE;
return TRUE;
}

static BOOL RemoveApiHook(PAPI_HOOK hook)
{
if (!hook || !hook->Address)
{
return FALSE;
}

if (!hook->Installed)
{
return TRUE;
}

if (!PatchCode(
hook->Address,
hook->Original,
sizeof(hook->Original)))
{
return FALSE;
}

hook->Installed = FALSE;
return TRUE;
}

static DWORD TrustedProcessId(HANDLE process)
{
DWORD pid;

if (!process ||
process == INVALID_HANDLE_VALUE)
{
SetLastError(ERROR_INVALID_HANDLE);
return 0;
}

/*
* CE 提供的句柄只在这里用于取得 PID。
* 该句柄不会被用于任何进程内存操作。
*/
pid = GetProcessId(process);

if (pid == 0)
{
return 0;
}

return pid;
}

static VOID RegisterModuleSnapshot(
HANDLE snapshot,
DWORD pid
)
{
ULONG index;
ULONG slot = ARRAYSIZE(g_snapshotRecords);

AcquireSRWLockExclusive(&g_snapshotLock);

for (index = 0;
index < ARRAYSIZE(g_snapshotRecords);
++index)
{
if (g_snapshotRecords[index].Snapshot == snapshot ||
g_snapshotRecords[index].Snapshot == NULL)
{
slot = index;
break;
}
}

if (slot == ARRAYSIZE(g_snapshotRecords))
{
slot =
g_nextSnapshotSlot++ %
ARRAYSIZE(g_snapshotRecords);
}

g_snapshotRecords[slot].Snapshot = snapshot;
g_snapshotRecords[slot].ProcessId = pid;

ReleaseSRWLockExclusive(&g_snapshotLock);
}

static BOOL FindModuleSnapshot(
HANDLE snapshot,
PDWORD pid
)
{
ULONG index;
BOOL found = FALSE;

if (!pid)
{
return FALSE;
}

AcquireSRWLockShared(&g_snapshotLock);

for (index = 0;
index < ARRAYSIZE(g_snapshotRecords);
++index)
{
if (g_snapshotRecords[index].Snapshot == snapshot)
{
*pid = g_snapshotRecords[index].ProcessId;
found = TRUE;
break;
}
}

ReleaseSRWLockShared(&g_snapshotLock);
return found;
}

static VOID FillModuleEntryW(
LPMODULEENTRY32W entry,
DWORD pid,
ULONGLONG baseAddress,
DWORD imageSize,
LPCSTR imageName
)
{
DWORD structureSize = entry->dwSize;

ZeroMemory(entry, sizeof(*entry));

entry->dwSize = structureSize;
entry->th32ModuleID = 1;
entry->th32ProcessID = pid;
entry->GlblcntUsage = 1;
entry->ProccntUsage = 1;
entry->modBaseAddr =
(BYTE*)(ULONG_PTR)baseAddress;
entry->modBaseSize = imageSize;
entry->hModule =
(HMODULE)(ULONG_PTR)baseAddress;

MultiByteToWideChar(
CP_ACP,
0,
imageName,
-1,
entry->szModule,
ARRAYSIZE(entry->szModule)
);

wcsncpy_s(
entry->szExePath,
ARRAYSIZE(entry->szExePath),
entry->szModule,
_TRUNCATE
);
}

static VOID FillModuleEntryA(
PMODULEENTRY32A_COMPAT entry,
DWORD pid,
ULONGLONG baseAddress,
DWORD imageSize,
LPCSTR imageName
)
{
DWORD structureSize = entry->dwSize;

ZeroMemory(entry, sizeof(*entry));

entry->dwSize = structureSize;
entry->th32ModuleID = 1;
entry->th32ProcessID = pid;
entry->GlblcntUsage = 1;
entry->ProccntUsage = 1;
entry->modBaseAddr =
(BYTE*)(ULONG_PTR)baseAddress;
entry->modBaseSize = imageSize;
entry->hModule =
(HMODULE)(ULONG_PTR)baseAddress;

strncpy_s(
entry->szModule,
ARRAYSIZE(entry->szModule),
imageName,
_TRUNCATE
);

strncpy_s(
entry->szExePath,
ARRAYSIZE(entry->szExePath),
imageName,
_TRUNCATE
);
}

BOOL WINAPI MyReadProcessMemory(
HANDLE process,
LPCVOID baseAddress,
LPVOID buffer,
SIZE_T size,
SIZE_T* bytesRead
)
{
DWORD pid = TrustedProcessId(process);

if (bytesRead)
{
*bytesRead = 0;
}

if (pid == 0)
{
return FALSE;
}

if (!DriverReadMemory(
pid,
(ULONGLONG)(ULONG_PTR)baseAddress,
buffer,
size))
{
return FALSE;
}

if (bytesRead)
{
*bytesRead = size;
}

return TRUE;
}

BOOL WINAPI MyWriteProcessMemory(
HANDLE process,
LPVOID baseAddress,
LPCVOID buffer,
SIZE_T size,
SIZE_T* bytesWritten
)
{
DWORD pid = TrustedProcessId(process);

if (bytesWritten)
{
*bytesWritten = 0;
}

if (pid == 0)
{
return FALSE;
}

if (!DriverWriteMemory(
pid,
(ULONGLONG)(ULONG_PTR)baseAddress,
buffer,
size))
{
return FALSE;
}

if (bytesWritten)
{
*bytesWritten = size;
}

return TRUE;
}

LPVOID WINAPI MyVirtualAllocEx(
HANDLE process,
LPVOID address,
SIZE_T size,
DWORD allocationType,
DWORD protect
)
{
DWORD pid = TrustedProcessId(process);
ULONGLONG resultAddress =
(ULONGLONG)(ULONG_PTR)address;
SIZE_T resultSize = size;

if (pid == 0 || size == 0)
{
if (size == 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
}

return NULL;
}

if (!DriverAllocateVirtualMemory(
pid,
&resultAddress,
&resultSize,
allocationType,
protect))
{
return NULL;
}

return (LPVOID)(ULONG_PTR)resultAddress;
}

BOOL WINAPI MyVirtualFreeEx(
HANDLE process,
LPVOID address,
SIZE_T size,
DWORD freeType
)
{
DWORD pid = TrustedProcessId(process);

if (pid == 0)
{
return FALSE;
}

return DriverFreeVirtualMemory(
pid,
(ULONGLONG)(ULONG_PTR)address,
size,
freeType
);
}

BOOL WINAPI MyVirtualProtectEx(
HANDLE process,
LPVOID address,
SIZE_T size,
DWORD newProtect,
PDWORD oldProtect
)
{
DWORD pid = TrustedProcessId(process);

if (oldProtect)
{
*oldProtect = 0;
}

if (pid == 0 ||
address == NULL ||
size == 0 ||
oldProtect == NULL)
{
if (pid != 0)
{
SetLastError(ERROR_INVALID_PARAMETER);
}

return FALSE;
}

return DriverProtectVirtualMemory(
pid,
(ULONGLONG)(ULONG_PTR)address,
size,
newProtect,
oldProtect
);
}

SIZE_T WINAPI MyVirtualQueryEx(
HANDLE process,
LPCVOID address,
PMEMORY_BASIC_INFORMATION information,
SIZE_T informationLength
)
{
DWORD pid = TrustedProcessId(process);

if (pid == 0)
{
return 0;
}

if (!information ||
informationLength < sizeof(MEMORY_BASIC_INFORMATION))
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}

if (!DriverQueryVirtualMemory(
pid,
(ULONGLONG)(ULONG_PTR)address,
information))
{
return 0;
}

return sizeof(MEMORY_BASIC_INFORMATION);
}

HANDLE WINAPI MyCreateToolhelp32Snapshot(
DWORD flags,
DWORD processId
)
{
BOOL moduleSnapshot =
(flags &
(TH32CS_SNAPMODULE |
TH32CS_SNAPMODULE32)) != 0;

if (!moduleSnapshot || processId == 0)
{
HANDLE snapshot;
DWORD error;

RemoveApiHook(&g_createSnapshotHook);

snapshot =
((CreateToolhelp32SnapshotP)
(ULONG_PTR)g_createSnapshotHook.Address)(
flags,
processId
);

error = GetLastError();

InstallApiHook(&g_createSnapshotHook);

SetLastError(error);
return snapshot;
}

HANDLE snapshot =
CreateEventW(NULL, TRUE, FALSE, NULL);

if (!snapshot)
{
return INVALID_HANDLE_VALUE;
}

RegisterModuleSnapshot(snapshot, processId);

SetLastError(ERROR_SUCCESS);
return snapshot;
}

BOOL WINAPI MyModule32FirstW(
HANDLE snapshot,
LPMODULEENTRY32W entry
)
{
DWORD pid = 0;
ULONGLONG baseAddress = 0;
DWORD imageSize = 0;
CHAR imageName[16] = { 0 };

if (!entry ||
entry->dwSize < sizeof(MODULEENTRY32W) ||
!FindModuleSnapshot(snapshot, &pid))
{
BOOL result;
DWORD error;

RemoveApiHook(&g_moduleFirstWHook);

result =
((Module32FirstWP)
(ULONG_PTR)g_moduleFirstWHook.Address)(
snapshot,
entry
);

error = GetLastError();

InstallApiHook(&g_moduleFirstWHook);

SetLastError(error);
return result;
}

if (!DriverGetProcessImage(
pid,
&baseAddress,
&imageSize,
imageName,
sizeof(imageName)))
{
return FALSE;
}

FillModuleEntryW(
entry,
pid,
baseAddress,
imageSize,
imageName
);

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL WINAPI MyModule32FirstA(
HANDLE snapshot,
PMODULEENTRY32A_COMPAT entry
)
{
DWORD pid = 0;
ULONGLONG baseAddress = 0;
DWORD imageSize = 0;
CHAR imageName[16] = { 0 };

if (!entry ||
entry->dwSize < sizeof(MODULEENTRY32A_COMPAT) ||
!FindModuleSnapshot(snapshot, &pid))
{
BOOL result;
DWORD error;

RemoveApiHook(&g_moduleFirstAHook);

result =
((Module32FirstAP)
(ULONG_PTR)g_moduleFirstAHook.Address)(
snapshot,
entry
);

error = GetLastError();

InstallApiHook(&g_moduleFirstAHook);

SetLastError(error);
return result;
}

if (!DriverGetProcessImage(
pid,
&baseAddress,
&imageSize,
imageName,
sizeof(imageName)))
{
return FALSE;
}

FillModuleEntryA(
entry,
pid,
baseAddress,
imageSize,
imageName
);

SetLastError(ERROR_SUCCESS);
return TRUE;
}

BOOL InitHook()
{
HMODULE kernel32 =
GetModuleHandleA("kernel32.dll");

if (!kernel32)
{
return FALSE;
}

if (!InitializeApiHook(
&g_readHook,
kernel32,
"ReadProcessMemory",
(PVOID)&MyReadProcessMemory) ||
!InitializeApiHook(
&g_writeHook,
kernel32,
"WriteProcessMemory",
(PVOID)&MyWriteProcessMemory) ||
!InitializeApiHook(
&g_createSnapshotHook,
kernel32,
"CreateToolhelp32Snapshot",
(PVOID)&MyCreateToolhelp32Snapshot) ||
!InitializeApiHook(
&g_moduleFirstWHook,
kernel32,
"Module32FirstW",
(PVOID)&MyModule32FirstW) ||
!InitializeApiHook(
&g_moduleFirstAHook,
kernel32,
"Module32First",
(PVOID)&MyModule32FirstA) ||
!InitializeApiHook(
&g_virtualAllocExHook,
kernel32,
"VirtualAllocEx",
(PVOID)&MyVirtualAllocEx) ||
!InitializeApiHook(
&g_virtualFreeExHook,
kernel32,
"VirtualFreeEx",
(PVOID)&MyVirtualFreeEx) ||
!InitializeApiHook(
&g_virtualProtectExHook,
kernel32,
"VirtualProtectEx",
(PVOID)&MyVirtualProtectEx) ||
!InitializeApiHook(
&g_virtualQueryExHook,
kernel32,
"VirtualQueryEx",
(PVOID)&MyVirtualQueryEx))
{
return FALSE;
}

return TRUE;
}

BOOL Hook()
{
return InstallApiHook(&g_readHook);
}

BOOL UnHook()
{
return RemoveApiHook(&g_readHook);
}

BOOL HookWriteProcessMemory()
{
return InstallApiHook(&g_writeHook);
}

BOOL UnHookWriteProcessMemory()
{
return RemoveApiHook(&g_writeHook);
}

BOOL HookModuleEnumeration()
{
if (!InstallApiHook(&g_createSnapshotHook))
{
return FALSE;
}

if (!InstallApiHook(&g_moduleFirstWHook))
{
RemoveApiHook(&g_createSnapshotHook);
return FALSE;
}

if (!InstallApiHook(&g_moduleFirstAHook))
{
RemoveApiHook(&g_moduleFirstWHook);
RemoveApiHook(&g_createSnapshotHook);
return FALSE;
}

return TRUE;
}

BOOL UnHookModuleEnumeration()
{
BOOL result = TRUE;

result =
RemoveApiHook(&g_moduleFirstAHook) &&
result;

result =
RemoveApiHook(&g_moduleFirstWHook) &&
result;

result =
RemoveApiHook(&g_createSnapshotHook) &&
result;

return result;
}

BOOL HookVirtualMemory()
{
if (!InstallApiHook(&g_virtualQueryExHook))
{
return FALSE;
}

if (!InstallApiHook(&g_virtualAllocExHook))
{
RemoveApiHook(&g_virtualQueryExHook);
return FALSE;
}

if (!InstallApiHook(&g_virtualProtectExHook))
{
RemoveApiHook(&g_virtualAllocExHook);
RemoveApiHook(&g_virtualQueryExHook);
return FALSE;
}

if (!InstallApiHook(&g_virtualFreeExHook))
{
RemoveApiHook(&g_virtualProtectExHook);
RemoveApiHook(&g_virtualAllocExHook);
RemoveApiHook(&g_virtualQueryExHook);
return FALSE;
}

return TRUE;
}

BOOL UnHookVirtualMemory()
{
BOOL result = TRUE;

result =
RemoveApiHook(&g_virtualFreeExHook) &&
result;

result =
RemoveApiHook(&g_virtualProtectExHook) &&
result;

result =
RemoveApiHook(&g_virtualAllocExHook) &&
result;

result =
RemoveApiHook(&g_virtualQueryExHook) &&
result;

return result;
}

注入模块

直接用的以前的远程线程注入姿势;

main.cpp

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
#include<Windows.h>
#include<iostream>
using namespace std;

int main()
{
int pid;
printf("PID: ");
scanf_s("%d", &pid);
//打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (hProcess == NULL)
{
cout << "open fail !! " << endl;
system("pause");
return 0;
}

char buffer[] = "C:\\Users\\lenovo\\Desktop\\inject\\dlltest.dll";
SIZE_T bufferSize = strlen(buffer) + 1;
SIZE_T realWrite = 0;

//申请进程内存空间
char* str = (char*)VirtualAllocEx(hProcess, 0, bufferSize, MEM_COMMIT, PAGE_READWRITE);
if (str == NULL)
{
cout << "malloc err !!" << endl;
system("pause");

return 0;
}
//将字符串写入该空间
WriteProcessMemory(hProcess, str, buffer, bufferSize, &realWrite);

//创建进程中的线程
HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, str, 0, 0);
if (hThread == NULL)
{
cout << "thread create err !!" << endl;
system("pause");

return 0;
}

//一直等待线程结束
WaitForSingleObject(hThread, -1);

//扫尾
VirtualFreeEx(hProcess, str, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
cout << "Done." << endl;
system("pause");

return 0;
}