- 书接上文:
通过KeStackAttachProcess实现赋值CR3切换线程上下文,但是这个函数会留下使用痕迹,容易被反作弊查到
- 解决思路:
取KeStackAttachProcess函数最主要的行为,手动实现KeStackAttachProcess功能
(读取目标进程的CR3,直接写入 CR3寄存器实现进程上下文切换)
- 预备
CR3:硬件寄存器 装着「当前这个进程页表的物理起始地址」
代表CPU核心正在执行的R3层的一块独立的虚拟内存(目标进程)
内核CR3 :是完整页表,同时映射内核空间和用户空间。
用户CR3 :只映射极少量内核代码,主要映射用户空间。
注意这俩个CR3的值可能相等 目的都是为了把虚拟地址翻译成物理地址
这里的虚拟地址可以是R3或者R0 但是用户层对于翻译R0有点局限(物理页中可能没有此地址)
注意:用户CR3下不能读写内核虚拟内存(代码中就0x28偏移 无其他可选)
- 原理:
通过修改CR3的值使CPU执行目标进程的代码
同时防止物理页发生错页等问题,将中断等级(IRQL)提升至DPC_lEVEL以应对线程调度(中断和APC)
即:当线程调度时 CR3会受到牵连发生变化 (而KeStackAttachProcess中有提等级的部分代码)
- 注意部分
1.由于CR3会切换线程上下文,那么如果中途发生中断(高等级IRQL)会抢占CPU线程,另一个APC会在空闲时(线程等待)处理APC队列 从而使CR3又发生赋值,
进而导致无法读取正确的R3层指针最后蓝屏
- CR3有两种类型 如果切换至R3层的CR3 则无法访问内核虚拟内存,
- MmIsAddressValid此函数用于查询虚拟地址对应的物理分页是否存在,但是只是瞬时,无法在执行拷贝时保证真的存在
- ProbeForRead此函数只能在PASSIVE_LEVEL等级下执行 高等级直接蓝屏,且无法验证所对应的物理页是否存在
5.代码部分
- 关键函数分析
- _disable();暂停中断 解决中断引起的线程调度
- _enable();还原中断
- KeRaiseIrqlToDpcLevel()提升IRQL等级至DPC_LEVEL 应对APC和低等级中断
- KeLowerIrql还原等级
- __readcr3();获取当前CR3的值
- __writecr3;修改CR3的值
注意:_disable()和KeRaiseIrqlToDpcLevel()都可以临时解决线程调度的问题