VNCTF2026-Shadow-WP
VNCTF2026 逆向题 Shadow WP
考点
驱动PE手动反射注入
键盘消息监控
PTE hook
动态ShellCode
程序分析
只是最简单的一个迷宫逻辑程序,并且不包含任何其他功能。
走到终点就会弹出”You reached the end!”消息框。

驱动分析
1. PE反射注入Sys
驱动入口开始,先根据一个全局Key,对一个0x5E00大小的数据,进行AES加密(注意是加密)。

其实尝试加密该数据,可以得到MZ文件头,也就是PE头,大概就能猜出来这边加密出来就是一整个的解密后PE文件,

解密PE文件后,进行PE拉伸、重定位修复、IAT修复、调用DriverEntry,一系列操作进行手动加载该PE文件到内存中运行,具体实现原理可以网上搜”反射注入”,实际就是手动实现加载并运行程序。


总结来说表层的这个驱动就是实现了手动加载另一个驱动到内存中运行,是一个壳性质。所以具体需要分析被加载的驱动,
2. Pte hook
原理详见:https://xz.aliyun.com/news/18999
开头解密了一个字符串获取了KeDelayExecutionThread字符串,然后调用MmGetSystemRoutineAddress,获取了该函数在当前ntdll中的地址,然后遍历所有进程找到进程名为Maze的程序,也就是题目附件中的Maze.exe。

然后对该进程,进行对KeDelayExecutionThread函数单独隔离的Pte hook,Pte hook的特性就是对ntdll函数进行hook,但仅对该进程生效,hook替换成另一个函数。

3. 键盘监控
设置了MajorFunction的IRP_MJ_READ消息函数,然后创建了FILE_DEVICE_KEYBOARD设备,解密字符串得到”\Device\KeyboardClass0”,然后附加键盘驱动上。此时dispach_read就会接收到键盘相关消息。


核心回调就是dispatch_read中的CompletionRoutine函数。

获取Shift键,并且判断按键是否按下,将按键消息转换到对应按键字符,存入到Input数组中。并且需要按下F12开启输入,输入后再按F12结束输入。


然后查看Input数组的交叉调用发现在Hook函数中也有用到,接下来就主要分析Hook函数。

4. Shellcode加密
Hook函数开头通过KeDelayExecutionThread的第三个参数进行了密钥派生。

后续分段解密加载了一大段ShellCode。

通过ShellCode对输入的Flag进行加密,最后与密文比对,若比对成功,则出发蓝屏,Code:0x11111111。

通过手动抄伪代码对Shellcode解密,或者动调拿到解密后Shellcode均可,然后加载IDA进行分析。

该算法是一个自定义算法,代码量少,直接逆向解密即可。


解密分析
从上文可知,最后对Input加密的密钥是通过KeDelayExecutionThread的第三个参数派生得到的,那么这个第三个参数是什么呢。

实际上用户态的Sleep函数最终底层会调用这个KeDelayExecutionThread函数,第三个参数就是等待时间,也就是对应Sleep的参数。
1 | NTSTATUS KeDelayExecutionThread( |
那么KeDelayExecutionThread hook需要被触发,就需要有Sleep调用,可以回想之前Maze.exe程序最终走到终点时候会调用一个Sleep(0x32u),这个0x32就是关键数据,但是直接用0x32进行派生密钥会发现解密失败。

原因是这个a3参数Interval并不是原始的ms毫秒标准,而是100ns为单位的负数,所以0x32需要进行以下变换
1 | -50ms = -50000000ns = -500000 × 100ns |
得到a3值为0xFFFFFFFFFFF85EE0,对这个数值进行密钥派生得到的正确密钥:0xE7D1CC85D2172C16

然后再解密发现还是不对,此时最好需要进行一下动调。
动态调试
sxe ld Shadow命令断在驱动启动时,断点下在call DriverEntry部分。


断点,从rax拿到目标加载驱动的DriverEntry地址。

需要断在RtlCompareMemory密文比对处,计算一下该地址和DriverEntry的相对偏移,


按下F12输出[LDriver] on input,然后此时输入flag,再按下F12结束,输出[LDriver] input end.,断到验证的部分。

此时db rcx查看密文的位置,发现和静态时密文不一样,使用动调得到的密文即可成功解密。


这边用了一个小trick,实际密文数组交叉调用除了密文比对处,没有其他地方。

若对密文进行断点,会发现会在PTE函数内部的一个地方被进行了异或处理,不过是对密文前面的四十个字节作为起始地址计算,然后i设置40开始,这样访问就会越界到密文处,导致密文查不到被修改代码的交叉调用。


解密代码
1 |
|