首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
CTF对抗
发新帖
3
7
[原创] 第五届“鹏城杯”初赛 Reverse-MDriver wp
发表于: 2025-12-22 19:49
16861
[原创] 第五届“鹏城杯”初赛 Reverse-MDriver wp
relost
1
2025-12-22 19:49
16861
# 驱动分析 驱动被vmp保护,因此首先需要dump驱动以完成脱壳,对KeRevertToUserAffinityThread下断点后dump驱动程序并分别修复SectionAlignment,SizeOfHeaders,PointerToRawData以便其能被ida识别。 ## 入口点逻辑 脱完壳后首先定位 security_init_cookie找真正的驱动入口点 这里直接用特征码 `48 8B 05 ?? ?? ?? ?? 48 85 C0 74 1B 48`  由此可以得到驱动真实入口点逻辑,我已经恢复部分导入  `_I@vmravY$@vmrav$mjmp$wqggaww%`经xor爆破发现是驱动成功加载的调试信息 `*(_QWORD *)(RCX + 104) = FwpmFilterDeleteById0_0;` RCX 是 `DRIVER_OBJECT` 指针。偏移 `104` (0x68) 在 x64 `DRIVER_OBJECT` 结构中对应 `DriverUnload` 域。它将 `FwpmFilterDeleteById0_0` 注册为卸载函数。当驱动停止时,这个函数会被调用以清理 WFP 过滤器。 因此必定存在初始化wfp的环境,即函数sub_FFFFF80541012AA0,接下来主要分析这个函数。 ## Wfp初始化 可以看到以下都是我恢复符号后的版本  `FwpmEngineOpen0_0`: 建立与过滤引擎的会话。 `FwpmSubLayerAdd0_0`: 创建一个新的子层 (SubLayer),权重设为 64。所有的过滤器都将在这个子层中运行,这有助于控制过滤器的优先级。 `sub_FFFFF80541012C90`: 调用封装好的辅助函数来注册 Callout 并添加过滤器。 参数传递了层 GUID (`&a1`)、Callout GUID (`&unk_...141C0`) 以及回调函数 `sub_FFFFF80541012440`。 `FwpmTransactionCommit0_0`: 如果一切顺利,提交事务,使过滤规则生效  分析得出sub_FFFFF80541012C90将回调函数注册到内核,并绑定到特定的过滤层 ## 回调函数分析 根据前面的分析 我们知道了sub_FFFFF80541012440是wfp注册的回调函数,用来对流量做处理 搜索资料得到其函数原型 为 标准WFPClassifyFn ```c void ClassifyFn( const FWPS_INCOMING_VALUES0 *inFixedValues, // a1 const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues, // a2 void *layerData, // a3 const void *classifyContext, // a4 const FWPS_FILTER1 *filter, // a5 UINT64 flowContext, // a6 (IDA识别为 _DWORD* 有误) FWPS_CLASSIFY_OUT0 *classifyOut // a7 ); ```  根据layerid的过滤可以确定监听范围为layrid=52  即 ALE_FLOW_ESTABLISHED_V4 当一个新的网络流(Flow)建立时触发 inFixedValues->incomingValue[5].value.uint8是过滤协议的操作  第五个是protocol所以检查 v9[2] == 1 是过滤icmp的操作,所以此回调函数响应的是ICMP包  ICMP的Type=8 对应驱动检查 ptr == 8,意味着ICMP包type需要=8  之后解密字符串,打印调试信息并进入最终的工作线程sub_FFFFF805410118A0 ## 校验线程分析  效验线程内执行了四个用于效验的函数,最后执行资源清理工作  sub_FFFFF80541011120取出发送方的pid备用  sub_FFFFF80541011DC0实际上为key1的生成逻辑 ,其生成一个key v10 来和传入的明文(a1)异或进行加密 **key1的生成逻辑如下**: 读取 GS 段寄存器 偏移 0x50 处的值。 与常数 0x020B0000 异或。 循环右移 (ROR) 18 位,计算出 MSR 索引。 最后rdmsr 0xC0000082拿到地址,这个寄存器存放的是系统调用入口函数 `KiSystemCall64` 的内存地址,所以key就是开头的几个字节。 ```c 1: kd> rdmsr C0000082 msr[c0000082] = fffff801`0c211900 1: kd> db fffff801`0c211900 l4 fffff801`0c211900 0f 01 f8 65(key1!!) ```  sub_FFFFF80541011750调用sub_FFFFF80541011C10生成key,做进一步处理后与明文异或。  sub_FFFFF80541011C10实际上为key2的生成逻辑 **key2的生成逻辑如下:** 根据当前irql去计算一个地址(计算地址的脚本就不放了,irql=0),从中取出字符串最终取出的是 L"C:\\Windows",结合前面的部分可以写脚本生成key2并取前0x14字节 ```python def generate_v4_key(): # 1. 准备种子字符串 "C:\WINDOWS" (宽字符, 大写) # 来源于之前的分析:KUSER_SHARED_DATA.NtSystemRoot seed_str = "C:\\WINDOWS" seed_bytes = seed_str.encode('utf-16le') seed_bytes = seed_bytes[:0x14] print(f"[*] 种子数据 (Hex): {seed_bytes.hex().upper()}") v4 = bytearray(256) for k in range(len(seed_bytes)): v4[k] = seed_bytes[k] print("[*] 开始计算密钥流 v4...") for i in range(256): not_i = (~i) & 0xFF v4[i] ^= not_i # 5. 输出结果 print("-" * 30) print(f"[+] 最终密钥 v4 (256 bytes) 生成完成。") print("-" * 30) print("前 40 字节预览 (Hex):") print(v4[:40].hex(' ').upper()) print("\n[C 语言数组格式]:") c_array = ', '.join([f'0x{b:02X}' for b in v4]) print(f"unsigned char v4[256] = {{ {c_array} }};") return v4 if __name__ == "__main__": key_stream = generate_v4_key() ```  工作线程的第四次函数调用进入了最终效验环节,将加密后的输入和密文比对。  如果成功就将flag做一个异或(虚拟化了),申请一块内存写入到发送包的进程中并打印成功提示(用了 sub_FFFFF80541011120取出的pid) ## 驱动分析总结 驱动注册了一个 WFP Callout (`sub_FFFFF80541012C90`),并在 `ClassifyFn` (`sub_FFFFF80541012440`) 中处理网络包。 代码检查 `LayerId`=52( ALE_FLOW_ESTABLISHED_V4)。 检查 `incomingValue[5] == 1`,对应 `IProtocol == ICMP`。 驱动读取 Payload 数据,进入密钥校验逻辑。 key1: 通过 `__readgsbyte` 和 XOR 运算算出 MSR 索引,最终读取 **`LSTAR` (0xC0000082)** 寄存器指向的地址。LSTAR指向 KiSystemCall64,这是系统调用的入口。读取该地址的前 4 字节机器码。标准 Windows 环境下为 `0F 01 F8 65` (`swapgs; mov gs:[10], rsp`)。 key2: 读取 `KUSER_SHARED_DATA` 的 `NtSystemRoot` 字段(即 `C:\WINDOWS`),转换为大写宽字符。然后进行`Key2[i] = SeedBytes[i] ^ (~i)`。 将输入与key1key2异或后与密文比对 当 Payload 校验通过后,驱动执行以下操作: `PsLookupProcessByProcessId` 获取 EPROCESS。 `KeStackAttachProcess` 挂靠到发送者进程空间。 `NtAllocateVirtualMemory` 在发送者进程中申请 4KB RW内存。 将黑箱运算后的 flag 写入该内存。 `DbgPrint` 输出成功信息。 # 反调试分析 分析完驱动后已经注意到有几处反调试,尝试绕过以便在虚拟机成功加载驱动,打印 [MDriver] Driver init success! 即视为加载成功 ## 反调试1  在初始化回调函数的末尾被调用   使用dpc广播检查内核 KdDebuggerEnabled 和KdDebuggerNotPresent 标志位,并再次检查KUSER_SHARED_DATA+2d4  检测到调试器则0xdeaddb9蓝屏 ## 反调试2 此反调试位于key2的生成逻辑中  正常程序运行在 PASSIVE_LEVEL (0)计算出来的地址是正确的,能成功取出字符串 一旦windbg `DbgBreakPoint` 触发 则会提升到 DISPATCH_LEVEL 导致地址计算失败,0x9999999蓝屏。这避免了被动态调试出地址计算流程(实际上并不影响动调获取key2) ## 绕过方法 没什么好说的 base+10A0 直接ret就行,反调试2完全不用管,我们已经分析完了驱动逻辑得到了2个key,接下来加载驱动发包即可。 ret反调试后成功加载驱动  # exp 已经在上文静态分析得到了key1和key2,写个脚本异或就行 ```python def solve_payload(): # 1. 密文 (v41) - 20 字节 cipher = [ 0xD4, 0x90, 0x60, 0xED, 0xC7, 0xA4, 0x30, 0xF4, 0xDF, 0x93, 0x1C, 0xE5, 0xD0, 0x96, 0x19, 0xF3, 0xDB, 0x8E, 0x21, 0xA8 ] # 2. Key 1 (MSR Key) - 4 字节循环 # 来源于 KiSystemCall64 前4字节 key1 = [0x0F, 0x01, 0xF8, 0x65] # 3. Key 2 (Seed Key / v4) - 取前 20 字节 # 来源于 C:\WINDOWS 异或处理 key2 = [ 0xBC, 0xFE, 0xC7, 0xFC, 0xA7, 0xFA, 0xAE, 0xF8, 0xBE, 0xF6, 0xBB, 0xF4, 0xB7, 0xF2, 0xBE, 0xF0, 0xB8, 0xEE, 0xBE, 0xEC ] payload = [] for i in range(len(cipher)): c = cipher[i] k2 = key2[i] k1 = key1[i % 4] # 循环取 Key1 p = c ^ k2 ^ k1 payload.append(p) print("-" * 60) # 输出最终结果 payload_str = "".join([chr(x) for x in payload]) payload_hex = " ".join([f"{x:02X}" for x in payload]) print(f"[+] 最终 Key (Hex) : {payload_hex}") print(f"[+] 最终 Key (String): {payload_str}") if __name__ == "__main__": solve_payload() ``` 得到payload  写个exp去发imcp包,格式按照前面分析的来 ```python import socket import struct import time import os def checksum(data): if len(data) % 2: data += b'\x00' res = sum(struct.unpack('!%sH' % (len(data) // 2), data)) res = (res >> 16) + (res & 0xffff) res += res >> 16 return (~res) & 0xffff def send_final_exploit(): target_ip = "127.0.0.1" print(f"[*] Target IP: {target_ip}") key = b'go_to_find_the_flag!' icmp_type = 8 icmp_code = 0 icmp_id = os.getpid() & 0xFFFF icmp_seq = 1 header_no_chk = struct.pack('!BBHHH', icmp_type, icmp_code, 0, icmp_id, icmp_seq) chk = checksum(header_no_chk + key) # 填入校验和 header = struct.pack('!BBHHH', icmp_type, icmp_code, chk, icmp_id, icmp_seq) packet = header + key try: s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) s.sendto(packet, (target_ip, 0)) print(f"[+] Packet Sent! Type={icmp_type}, Payload='{key.decode()}'") print("[*] 驱动触发") except Exception as e: print(f"[X] 发送失败: {e}") if __name__ == "__main__": send_final_exploit() input("\n[!] 进程已暂停。\n按回车键退出...") ``` 打包一下放虚拟机运行即可 # 最终结果   ``` flag{Y0r_Ar3_W1nKern3l_Mas7er!*} ```
登录后可查看完整内容
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
#Reverse
收藏
・
3
点赞
・
7
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
你的帖子非常有用,感谢分享!
2026-7-16 08:44
Jtian
这个讨论对我很有帮助,谢谢!
2025-12-29 13:39
我的小拇指啊
期待更多优质内容的分享,论坛有你更精彩!
2025-12-24 04:15
5m10v3
+10
你的帖子非常有用,感谢分享!
2025-12-23 13:30
东方玻璃
为你点赞!
2025-12-23 13:17
Liv_T
+10
感谢你的贡献,论坛因你而更加精彩!
2025-12-23 11:45
voidchunk
你的分享对大家帮助很大,非常感谢!
2025-12-22 19:58
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
moshuiD
雪 币:
8892
活跃值:
(4466)
能力值:
(RANK:166 )
在线值:
发帖
9
回帖
35
粉丝
65
关注
私信
moshuiD
3
2
楼
不过可以分享一下IAT修复就更棒了
2025-12-23 11:31
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
relost
1
2
发帖
0
回帖
50
RANK
关注
私信
他的文章
[原创]腾讯游戏安全技术竞赛2026决赛题解
8521
[原创] 第五届“鹏城杯”初赛 Reverse-MDriver wp
16861
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部