1993 年发行的 Doom 已经被极客们移植到几乎所有可能的平台上 —— 从 GPS 设备到咖啡机,从示波器到任天堂 Switch。然而安全工程师 Adam Rice 近日完成了一个更为极端的挑战:让 Doom 完整运行在 DNS 协议栈之上,游戏引擎与全部资源文件通过 DNS 查询传输到内存中执行,整个过程不产生任何磁盘写入。这种逆向工程实践不仅展示了 DNS 协议的深度可塑性,也为隐蔽数据通道、应急恢复场景提供了全新的技术思路。
DNS 作为存储与传输介质的技术原理
DNS 协议的原始设计目标是完成域名到 IP 地址的映射,但 TXT 记录类型从 RFC 1035 起就允许存储任意文本数据,这为滥用 DNS 作为通用数据存储提供了技术基础。Adam Rice 在他的博客中指出:「TXT 记录本质上是一个没有任何验证机制的任意文本字段。既然可以存储 payload,就可以存储文件;既然可以存储文件,就可以存储程序;而如果可以存储程序 —— 那它大概率就能运行 Doom。」
整个项目的核心架构可以概括为三个阶段:第一阶段是数据编码与分块,将 Doom 的游戏引擎(使用 C# 移植版本 managed-doom)和资源文件 WAD 打包后进行压缩;第二阶段是通过 DNS TXT 记录进行分布式存储,每个 TXT 记录承载固定大小的数据块;第三阶段是客户端通过递归查询抓取所有记录,在内存中重新组装为完整的可执行流并直接运行。整个过程不依赖任何传统的 HTTP 下载或文件传输协议,完全建立在 DNS 查询 - 响应的基础之上。
实际部署中面临的首要挑战是数据体积控制。原始的 shareware 版本 WAD 文件约为 4MB,完整运行所需的 DLL 绑定文件约为 4.4MB,这个体量直接放入 DNS 区域会产生数千条记录。Adam Rice 采用压缩算法对两部分分别进行处理:WAD 文件从 4MB 压缩至 1.7MB,DLL 绑定从 4.4MB 压缩至 1.2MB。压缩后的总体积约为 2.9MB,这个数据量被分散存储在单个 DNS 区域中的约 1966 条 TXT 记录里,恰好可以放入 CloudFlare Pro 级别的 DNS 区域配置中。
协议工程实践中的关键参数
从协议工程的角度审视这个项目,有几个关键参数值得深入分析。首先是单条 TXT 记录的有效载荷上限:RFC 规范并未强制规定 TXT 记录的最大长度,但主流 DNS 服务商通常将单条记录限制在 255 字节或 512 字节以内,超过此限制需要使用多个字符串拼接。Adam Rice 的实现中采用了折中的分块策略,每条 TXT 记录承载约 1.5KB 的有效数据,在服务商限制与传输效率之间取得平衡。
其次是查询模式的隐蔽性问题。客户端需要发起近两千次独立的 DNS 查询才能完整获取游戏数据,这种高频率的查询模式在网络层面极易被识别为异常流量。一种可行的优化方案是将数据编码为子域名的形式(利用 CNAME 或 A 记录),通过预取机制批量解析多个子域名,从而在一次 DNS 响应中携带更多有效信息。但这种方案会增加 DNS 缓存服务器的负载,且在 DNSSEC 启用环境下可能面临额外的验证开销。
第三是内存加载的安全边界。整个系统依赖在内存中直接执行从 DNS _payload 重建的二进制流,这要求客户端具备可靠的运行时沙箱隔离机制。如果攻击者篡改了 DNS 响应中的数据内容,可能导致代码执行漏洞。生产环境中应当结合 DNS over HTTPS(DoH)或 DNS over TLS(DoT)建立加密通道,并对响应数据实施完整性校验。
工程迁移与应急场景的潜在价值
尽管「在 DNS 上运行 Doom」更多是一项概念验证性质的技术演示,但其背后的协议工程思路具备实际的应用迁移价值。在应急恢复场景中,当目标系统的网络访问受限但 DNS 查询可以正常穿透时,可以利用类似技术将轻量级的恢复工具或诊断代理加载到内存中,而无需建立传统的文件传输通道。
从防御角度看,这类实验也揭示了 DNS 协议被滥用的可能性边界。安全团队可以将类似的检测规则纳入网络流量监控:监测单域名在短时间内触发的异常高频 TXT 查询、分析查询响应中的数据熵值、识别潜在的 DNS 隧道通信模式。理解攻击者如何在 DNS 协议中隐藏有效载荷,是构建纵深防御体系的重要一环。
资料来源:本文技术细节主要参考 Adam Rice 在 Hackster.io 的访谈文章及其 GitHub 仓库(github.com/resumex/doom-over-dns),该项目基于 GPLv2 许可证开源。