天堂W游戏内核驱动保护简单分析(五)

NP 双机调试分析


1. 调用了NtQuerySystemInformation(SystemKernelDebuggerInformation) 来检测os是否是调试模式的进程
    PROCESS ffff920744e6c300
        SessionId: 1  Cid: 1a34    Peb: bc82205000  ParentCid: 111c
        DirBase: 23c33c000  ObjectTable: ffffa90b3ffbbb40  HandleCount: 1098.
        Image: LineageR.exe


    PROCESS ffff920748ec8080
        SessionId: 1  Cid: 05f4    Peb: 00262000  ParentCid: 1a34
        DirBase: 20acef000  ObjectTable: ffffa90b40aae680  HandleCount: 391.
        Image: GameGuard.des

    PROCESS ffff9207489ef080
        SessionId: 1  Cid: 0c04    Peb: 00233000  ParentCid: 1a34
        DirBase: 1a4926000  ObjectTable: ffffa90b3ec27dc0  HandleCount: 469.
        Image: GameMon.des

    GameMon64.des
    PurpleLauncher.exe
    Purple.exe
    purple-agent.exe

2. NtQueryObject() -> DebugObject 未发现

3. 隐藏kdcom.dll和Dgbv.sys

4. 关闭 NtQuerySystemInformation(ProcessBasicInformation 和 ProcessDebugPort) 
        检测当前系统是否处于调试状态, 比如双机调试 
        SystemKernelDebuggerInformation->DebuggerEnabled = FALSE; (双机调试 )
        SystemKernelDebuggerInformation->DebuggerNotPresent = FALSE; (双机调试 )
        dt _KUSER_SHARED_DATA结构成员
            检测 KdDebuggerEnabled 值是否为1                         (双机调试 ) 只要这个为真,则np就会退出进程
            检测 KdEnteredDebugger 值是否为1                             (双机调试 ) 
            检测 KdDebuggerNotPresent 值是否为0,正常应该为1
        KiDebugRoutine = KdpStub 则不可调试, 可调试的是kdpTrap     (双机调试 ) 待测试

    np的保护检测 KdDebuggerEnabled 分析:
        首先将 KdDebuggerEnabled + 0x10 处的值设置为1, 然后把系统所有访问 KdDebuggerEnabled 的地方全部改为 KdDebuggerEnabled + 0x10, 启动游戏断下的就是游戏的驱动在访问了. 
        注意其中有几处mov指令的是保存 KdDebuggerEnabled指针的指针,只需要交里面的指针 + 0x10即可,而不是修改机器码
    fffff801`18cd1e6b 490fb70424     movzx   rax, word ptr [r12]
    fffff801`18cd1e70 488b09         mov     rcx, qword ptr [rcx]
    fffff801`18cd1e73 4889ea         mov     rdx, rbp
    fffff801`18cd1e76 4881c224000000 add     rdx, 24h 
    fffff801`18cd1e7d 0302           add     eax, dword ptr [rdx] // 这里就是在读 KdDebuggerEnabled, 代码应该是vmp混淆了
    fffff801`18cd1e7f 8a09           mov     cl, byte ptr [rcx]  // 这里的cl就是 KdDebuggerEnabled 的值
    fffff801`18cd1e81 0fb6f9         movzx   edi, cl
    fffff801`18cd1e84 418938         mov     dword ptr [r8], edi // 这里将KdDebuggerEnabled写入r8, 而r8又是KdDebuggerEnabled的地址
    fffff801`18cd1e87 4981c004000000 add     r8, 4
    fffff801`18cd1e8e 41c70000000000 mov     dword ptr [r8], 0
    代码附近有看到 0x7FFE0000 所以猜测是使用应用层的检测:
        通过读取 KUSER_SHARED_DATA.KdDebuggerEnabled 的值来检测内核模式调试器是否处于活动状态,
        KUSER_SHARED_DATA 始终位于每个进程的虚拟地址空间中地址 0x7FFE0000 的用户空间中, 偏移固定是0x2D4.
        参考: https://github.com/kyREcon/IsKernelDebuggerPresent

    np调用 MmGetSystemRoutineAddress 函数日志
    nt!_UNICODE_STRING
     "PsSuspendProcess"
       +0x000 Length           : 0x20
       +0x002 MaximumLength    : 0x22
       +0x008 Buffer           : 0xfffff801`6595a3d8  "PsSuspendProcess"
    nt!_UNICODE_STRING
     "PsResumeProcess"
       +0x000 Length           : 0x1e
       +0x002 MaximumLength    : 0x20
       +0x008 Buffer           : 0xfffff801`6595a408  "PsResumeProcess"
    nt!_UNICODE_STRING
     "PsGetThreadId"
       +0x000 Length           : 0x1a
       +0x002 MaximumLength    : 0x1c
       +0x008 Buffer           : 0xfffff801`6595a430  "PsGetThreadId"
    nt!_UNICODE_STRING
     "PsIsProtectedProcess"
       +0x000 Length           : 0x28
       +0x002 MaximumLength    : 0x2a
       +0x008 Buffer           : 0xfffff801`6595a458  "PsIsProtectedProcess"
    nt!_UNICODE_STRING
     "PsIsProtectedProcessLight"
       +0x000 Length           : 0x32
       +0x002 MaximumLength    : 0x34
       +0x008 Buffer           : 0xfffff801`6595a490  "PsIsProtectedProcessLight"
    nt!_UNICODE_STRING
     "ObReferenceObjectSafe"
       +0x000 Length           : 0x2a
       +0x002 MaximumLength    : 0x2c
       +0x008 Buffer           : 0xfffff801`6595a4d0  "ObReferenceObjectSafe"
    nt!_UNICODE_STRING
     "ObRegisterCallbacks"
       +0x000 Length           : 0x26
       +0x002 MaximumLength    : 0x28
       +0x008 Buffer           : 0xfffff801`6595a6c8  "ObRegisterCallbacks"
    nt!_UNICODE_STRING
     "ObUnRegisterCallbacks"
       +0x000 Length           : 0x2a
       +0x002 MaximumLength    : 0x2c
       +0x008 Buffer           : 0xfffff801`6595a6f0  "ObUnRegisterCallbacks"

    解决办法有以下几种: 
        1. 直接双机环境启动完成后,加载我们的驱动,然后将KdDebuggerEnabled的值写为0。
            这种情况下虽然无法在windbg里主动断下,但是使用irp请求通知驱动断下,或者监控进程启动,
            或者之前下的断点都是可以让windbg断下。
            > KdDebuggerEnabled 有两种方式,直接在代码里引用,则是使用导入表链接
            > KdDebuggerEnabled 另一种是使用MmGetSystemRoutine()来获取,这种可以hook EAT表或者直接hook这个函数解决,但要解决PG

        2. 修改KUSER_SHARED_DATA.KdDebuggerEnabled值失败,修改过后立即就被重置,
            尝试了从物理使用pte修改页面属性为可写,也还是会被写回去,
            尝试了使用MmGetPhysicalAddress()转换为物理地址后进行写入,也不能解决。
            其他猜测:
                使用 IoAllocateMdl 读取 KdDebuggerEnabled, 
                Hook KdpTrap 该函数无法断下,会导致虚拟机系统卡死
                而且infinity无法hook非ssdt和sssdt内的函数。
                使用IAT hook系统导出变量, (
                        经测试可行, 在PsCreateImageLoadNotify回调里拦截内核模块,然后IAT Hook KdDebuggerEnabled变量即可
                        但注意不能拦截进程,因为那个时候模块还没有加载,得到的导出RVA只是偏移,不是PE展开后的内存地址
                    )
            要想继续研究的话,可以考虑忽略或者干掉pg,直接使用inline hook KdpTrap等其他函数来检测反获取 KdDebuggerEnabled 的方式。
            干掉patchGuard过后,可以直接对访问KdDebuggerEnabled的系统函数进行位移0x10的访问, 即将KdDebuggerEnabled变量地址位移

5. 删除Purple开机启动的任务计划
    schtasks /delete /tn Purple /f
    
6. NP应用层的dll注入
    dll的注入和驱动的卸载都是由GameMon64.des来完成
    且被注入的npggnt64.des应该还有检测GameMon64.des进程是否存在的检测,不存在则自动卸载自己。


7. 经测试CE7.0能使用,CE7.2不能使用,由于CE的驱动只会加载,不会卸载,
    主要原因就是CE7.2的驱动会被检测到非法,不是驱动模块被发现,而是驱动内的问题.
    经过测试,就算不加载CE7.2的驱动,只开CE7.2也会被检测到, 
    说明有可能是应用层被检测到了, 经验证只加载驱动,可以正常游戏不会被检测到,
    那有可能是游戏驱动下使用KeAttachProcess()切换到用户进程空间检测到进程特征了, 可以尝试只让KeAttachProcess附加游戏目录下的进程.
    所以只有CE7.2的驱动加载过了,就必须重启系统或者手动卸载CE7.2的驱动. 
    总结CE7.2应该是窗口有特征被检测到了,因为使用手动编译CE源码改过的CE7.4都不会被检测到

逆向安全收徒中...

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值