
理解Windows x64 SSDT与win32k影子服务表NtCall64系统调用Fuzzer背后的理论基石【免费下载链接】NtCall64Windows NT x64 syscall fuzzer项目地址: https://gitcode.com/gh_mirrors/nt/NtCall64NtCall64 是一个面向 Windows x64 的系统调用 Fuzzersyscall fuzzer它通过模糊测试内核系统调用表——ntoskrnl 的 SSDTKiServiceTable和 win32k 的影子服务表Shadow SSDT——来挖掘稳定性问题与潜在漏洞。要读懂这个工具为什么敢直接乱调系统调用必须先搞懂三件事SSDT 是什么、win32k 影子表为什么叫影子、以及 Fuzzer 如何在用户态找到并遍历这些内核表。本文就是这套理论基石的完整导读。 什么是 SSDT从用户态进入内核的总目录在 Windows 中用户态代码不能直接执行内核函数必须通过一条syscall指令切换模式由内核查表决定要执行哪个服务例程。这个查表的目录就是SSDTSystem Service Descriptor Table在 x64 内核中对应 ntoskrnl 的KiServiceTable。一个服务表其实由三部分组成组件作用NtCall64 中的字段服务数量Count表中有多少个服务CountOfEntries服务表ServiceTable每个服务 ID → 内核函数地址ServiceTable栈参数字节表ArgumentTable每个服务有几个参数需要压栈StackArgumentTable这三者被封装在一个结构里定义见Source/NtCall64/global.htypedef struct _RAW_SERVICE_TABLE { ULONG CountOfEntries; LPVOID* ServiceTable; PBYTE StackArgumentTable; } RAW_SERVICE_TABLE;关键点ArgumentTable 是 Fuzzer 的弹药清单——知道每个服务需要几个参数才能构造出合理的模糊参数。x64 系统调用约定参数怎么传x64 上 Windows 的系统调用约定与 C 语言调用约定略有不同第 1~4 个参数rcx、rdx、r8、r9第 5 个及以后的参数压入栈r10额外保存第 1 个参数服务 ID供内核侧使用最后执行syscall指令进入内核NtCall64 用一个约 30 行的汇编函数实现了这个系统调用大门源码在Source/NtCall64/syscall.asm函数ntSyscallGate。它接收三个输入服务 IDrcx参数个数rdx参数指针数组r8当参数超过 4 个时它先把第 5 个起的参数逐个拷贝到栈上再统一调用syscall。这就是所有模糊测试最终落地的发射井。 win32k 影子服务表为什么叫影子图形子系统窗口、GDI/DirectX 消息处理住在win32k.sys里它也有自己的一套服务表由三个导出符号组成W32pServiceTable—— 服务函数地址表W32pArgumentTable—— 每服务栈参数字节W32pServiceLimit—— 服务总数它被称为影子 SSDTShadow SSDT原因在于系统启动时内核会把 win32k 的服务表整体影子化地挂接到主 SSDT 的高半区。用户态调用 win32k 服务时用的仍是同一条syscall指令只是服务 ID 从 0x1000 起步即第 0x1000 号服务对应 win32k 表的第 0 号。NtCall64 在Source/NtCall64/fuzz.h中直接定义了这条分界线#define W32SYSCALLSTART 0x1000也就是说Fuzzer 眼里只有一张大表ID 小于 0x1000 走 ntoskrnl大于等于 0x1000 走 win32k 影子表——这正是main.c里FuzzInitPhase1根据-call参数自动判断目标表Source/NtCall64/main.c的原理。 NtCall64 如何在内核表里寻宝这两张表都位于内核模块内部用户态不能直接读取内核内存。NtCall64 的解法非常聪明把系统镜像映射到自己的用户地址空间然后在副本里定位表。第一步映射系统镜像FuzzInitPhase2Source/NtCall64/main.c根据模式把\SystemRoot\System32\ntoskrnl.exe或\SystemRoot\System32\win32k.sys以不可执行方式映射进本进程RtlInitUnicodeString(usModule, szBuffer); ntStatus supMapImageNoExecute(usModule, Context-SystemModuleBase);第二步定位 KiServiceTablentoskrnl 表KiServiceTable不是导出符号无法直接查。supFindKiServiceTableSource/NtCall64/sup.c采用字节模式匹配找到.text代码段扫描系统调用分发例程KiSystemService的固定开头字节45 33 C9 44 8B 05即xor rcx,rcx; mov r8,[...]从匹配点解析出三条相对跳转指令依次还原出CountOfEntries、StackArgumentTable和ServiceTable三个字段。第三步定位 W32pServiceTablewin32k 影子表win32k 的表是导出符号省事得多。supFindW32pServiceTableSource/NtCall64/sup.c直接解析 PE 导出目录取回三件套ServiceLimit (ULONG*)supGetProcAddressEx(MappedImageBase, W32pServiceLimit); ServiceTable-CountOfEntries *ServiceLimit; ServiceTable-StackArgumentTable (PBYTE)supGetProcAddressEx(MappedImageBase, W32pArgumentTable); ServiceTable-ServiceTable (LPVOID*)supGetProcAddressEx(MappedImageBase, W32pServiceTable);到这里用户态手里就有了一份完整的服务目录 参数规格为下一步的自动化模糊测试铺平了道路。 从表到 Fuzz模糊测试是怎么跑起来的拿到表之后Fuzzer 的核心循环在Source/NtCall64/fuzz.c的DoSystemCall中完成每个服务 ID 的处理流程是查参数个数读 ArgumentTable 中该服务对应的字节类型推断可选-h选项按服务名启发式推断每个参数的类型。Source/NtCall64/fuzz.h定义了 20 余种参数类型提示例如句柄、指针、UNICODE_STRING、安全描述符、超时值等让随机参数看起来像真的生成参数FuzzGenerateParameter为每个参数生成模糊值必要时构造出结构体指针如 fuzzed 安全描述符、UNICODE_STRING发射调用交给ntSyscallGate执行syscall监控结果统计成功、错误、超时、崩溃等FUZZ_STATS每个服务默认跑 65536 轮。常用命令行速查ntcall64.exe -win32k 模糊测试 win32k 影子表 ntcall64.exe -log -o COM2 开启日志写到串口硬件调试用 ntcall64.exe -call 4097 -pc 1000 只测第 4097 号系统调用 ntcall64.exe -s 以 LocalSystem 身份运行黑名单给危险操作上保险有些调用天生危险如关机、挂起进程、死循环等待。NtCall64 用 INI 格式黑名单跳过它们配置文件位于Source/badcalls.ini按[ntos]和[win32k]两个分区分别列名例如[ntos] NtShutdownSystem NtSuspendProcess NtTerminateThread [win32k] NtUserLockWorkStation NtUserSwitchDesktop⚠️注意黑名单只影响批量模式用-call指定单个服务 ID 时会被有意忽略方便研究者定向复现此时线程超时也被设为无限需要格外小心。 理论落地的成果真实漏洞这套读表 → 猜参数 → 乱调的方法论并不只是纸上谈兵。NtCall64 已经协助发现并报告过多处 win32k/ntoskrnl 缺陷包括NtUserCreateActivationObject、NtUserOpenDesktop、NtUserSetWindowsHookEx、NtGdiDdDDISetHwProtectionTeardownRecovery等问题详见项目 README 中的 Bugs Found 章节。对安全研究者而言这证明了理解 SSDT 结构 系统化参数模糊 稳定的漏洞发现流水线。⚠️ 使用前的安全须知事项建议运行环境只在虚拟机或可控环境中运行可能触发蓝屏、死机、数据丢失权限建议以管理员或-s以 LocalSystem运行否则部分特权无法开启崩溃转储提前配置内核崩溃转储Full/Kernel dump方便事后调试轮数控制快速验证时用小-pc值例如-pc 1000 延伸阅读路径想深入源码的同学建议按这个顺序读Source/NtCall64/main.c—— 三阶段初始化Phase0/1/2与命令行解析Source/NtCall64/sup.c—— 表定位supFindKiServiceTable/supFindW32pServiceTableSource/NtCall64/syscall.asm—— 系统调用大门ntSyscallGateSource/NtCall64/fuzz.c与fuzz.h—— 参数启发式与模糊生成Source/NtCall64/ntos.h—— NTSTATUS、结构体等内核 API 定义Source/badcalls.ini—— 黑名单配置常见问题Q为什么 win32k 的服务 ID 从 4096 开始A内核启动时把 W32pServiceTable 附加到主服务表的高半区服务号加上 0x1000 偏移用户态无需第二条调用路径。QFuzzer 需要驱动或内核补丁吗A不需要。它只在用户态映射系统镜像文件读取表结构真正的调用走标准syscall指令对系统零侵入。QNT 6 以下Vista/XP能跑吗ANtCall64 面向 64 位 Windows NT 6Windows 7 及以后官方要求 Windows 10/11 x64 环境。✅总结SSDT 是 Windows 系统调用的总目录win32k 影子表是它的图形扩展卷。NtCall64 的全部魔法都建立在这份目录之上——映射、定位、推断参数、批量发射。理解了这个基石你就不仅会用它还能看懂它每一个设计决策背后的为什么。【免费下载链接】NtCall64Windows NT x64 syscall fuzzer项目地址: https://gitcode.com/gh_mirrors/nt/NtCall64创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考