打开CE,打开原神启动器。
至此,游戏未加载驱动。启动器可以被CE正常读写。
打开原神,CE中选择YuanShen.exe,发现原神并不能被读写。
尝试分析不能读写的原因。
CE调用的读取内存函数是NtReadVirtualMemory
猜测有三种可能
1.NtReadVirtualMemory被HOOK
2.ObCallBack
3.infinityhook
第一种情况因为X64系统PatchGuard的存在排除。当然VT也是有可能的,现在先不考虑。
第二种情况是最有可能的。
第三种情况感觉不大靠谱。
为了验证猜测,打开ARK工具。
好家伙,果然注册了线程和进程的ObCallBack,同样也注册了下图三个回调用于收集信息云云
直接把内存dump下来,修复好,直接拖IDA。
直接定位到Process ObCallback函数。
继续跟进。
F5大法失败。只是为了初步分析,没必要看头疼的汇编,而且Thread和Process的处理八九不离十,去康康Thread先
进Thread ObCallback F5
发现这里判断了是否操作的是游戏进程就把句柄读权限抹掉。
抹除写权限之前先判断了操作进程是不是csrss.exe如果是就不抹除。
因此可以利用更改进程名为csrss.exe来绕过写内存ObCallback保护,但还是没有读的权限。
游戏是通过句柄降权的方式保护游戏,那么我们也可以注册ObCallback直接把权限升回去。
在我们的回调中执行,当然执行之前需要判断操作游戏的是不是我们的调试器,如果不是就别把提权了。
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess = PROCESS_ALL_ACCESS_THREAD;
pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess = PROCESS_ALL_ACCESS_THREAD;
加载驱动后上游戏,发现CE读出了错误的内存。猜测是用了假的CR3导致读了错误的物理页
在回调中读取CR3,与eprocess中的DirectoryTableBase进行比较发现并不相同。哦吼
1: kd> dt _KPROCESS
nt!_KPROCESS+0x000 Header : _DISPATCHER_HEADER+0x018 ProfileListHead : _LIST_ENTRY+0x028 DirectoryTableBase : Uint8B
那就纠正回去。
if (MmIsAddressValid((PVOID)((ULONG64)pe + 0x28))){if (__readcr3() != *(PULONG64)((ULONG64)pe + 0x28)){PULONG64 FakeCR3 = Mapped_Memory_Addr(*(PULONG64)((ULONG64)pe + 0x28), 0x1000, 0);PULONG64 TrueCR3 = Mapped_Memory_Addr(__readcr3(), 0x1000, 0);if (FakeCR3[0] != TrueCR3[0]){_disable();if (FakeCR3 != NULL && TrueCR3 != NULL){RtlCopyMemory(FakeCR3, TrueCR3, 0x1000);}MmUnmapIoSpace(FakeCR3, 0x1000);MmUnmapIoSpace(TrueCR3, 0x1000);_enable();}}}
上游戏测试。已经能显示正常数据了。至此读写保护绕过。
PS:禁止读写的保护在后面分析驱动的时候我是没发现痕迹(我可能太菜了)。
有读写权限了,VEH可以正常附加调试。当然这不代表没有检测,至少硬件断点没有处理,还是很容易利用NtGetContextThread检测的。
PS:瞄了眼驱动发现了遍历进程模块基址、名称,并将信息返回给R3层的代码,想不封号VEH调试、注入估计还是得处理下。当然还有其他检测。
CE换成Windows调试器。调试直接闪退。看遍了驱动代码也没发现在哪里有能阻止调试器附加的地方。驱动层先放着,去康康用户层
想了想调试流程,发现用户层关键代码被处理的可能性很大,直接扫描HOOK,发现DbgUiRemoteBreakin被处理了。
此处跳转到了原神的代理函数。代理函数会执行记录信息并退出游戏操作。
无法附加的原因找到了,直接恢复HOOK。调试器成功附加,并在此处下内存断点,查找校验。
并未发现校验。
至此用户调试器已经能正常附加。想要实现不封号调试还需处理其他检测,例如校验
PEB+2、NtQueryInformationThread等等。
下面粗略讲讲关于内核调试器检测
驱动在这里创建了个线程,跟进去
发现驱动通过KdDebuggerEnabled置0,调用 KdDisableDebugger 实现禁用双机调试。把调用KdDebuggerEnabled的KdDebuggerEnabled操作移位即可轻松绕过。