用Rust为Windows Defender打造沙盒防护:Flying Sandbox Monster技术解析

本文详细介绍了如何使用Rust语言构建AppJailLauncher-rs框架,成功为微软未沙盒化的Windows Defender添加安全隔离层,包含技术架构实现、Windows API集成及Rust在系统编程中的实践心得。

Microsoft未给Windows Defender沙盒化,所以我做了 - Trail of Bits博客

微软在发布Windows Defender时未采用沙盒技术,使用户面临巨大风险。这令我惊讶,因为沙盒是最有效的安全加固技术之一。为何微软沙盒化了其他高价值攻击面(如Microsoft Edge中的JIT代码),却让Windows Defender毫无防护?

作为概念验证,我为微软沙盒化了Windows Defender,并开源了我的代码——Flying Sandbox Monster。Flying Sandbox Monster的核心是AppJailLauncher-rs,一个基于Rust的框架,用于在AppContainers中隔离不可信应用。它还允许您将应用程序的I/O包装在TCP服务器后面,使沙盒化应用能在完全不同的机器上运行,增加隔离层。

在本博客文章中,我描述了创建此工具的过程和结果,以及对Rust在Windows上使用的思考。

计划

Windows Defender对其主机机器的无限制访问和对危险文件格式的广泛接受,使其成为恶意黑客的理想目标。核心Windows Defender进程MsMpEng以SYSTEM权限运行服务。扫描组件MpEngine支持解析大量文件格式,还捆绑了全系统模拟器(适用于各种架构)和解释器(适用于各种语言)。所有这些都在Windows系统上以最高权限执行。哎呀。

这让我思考:用两年前我为CTF社区沙盒化挑战使用的相同工具集来沙盒化MpEngine有多难?

沙盒化Windows Defender的第一步是能够启动AppContainers。我想重用AppJailLauncher,但有个问题。原始AppJailLauncher是作为概念验证示例编写的。如果当时我有意识,我会用C++ Core编写它,而不是处理内存管理的痛苦。过去两年,我尝试用C++重写它,但都以失败告终(为什么依赖总是这么痛苦?)。

但后来灵感来了:为什么不直接用Rust重写AppContainer启动代码?

构建沙盒

几个月后,在快速学习Rust教程并编写了大量示例Rust代码后,我拥有了在Rust中启动AppContainers的三个支柱支持:SimpleDacl、Profile和WinFFI。

  • Profile:实现AppContainer配置文件和进程的创建。从配置文件中,我们可以获得一个SID,用于在AppContainer需要访问的资源上创建ACE。
  • WinFFI:包含winapi-rs未实现的大部分函数和结构,以及有用的实用类/函数。我努力将每个原始HANDLE和指针包装在Rust对象中,以管理它们的生命周期。

接下来,我需要了解如何与Windows Defender的扫描组件交互。Tavis Ormandy的loadlibrary仓库已经提供了一个C实现示例和启动MsMpEng扫描的说明。将结构和函数原型移植到Rust是自动化简单事务,尽管我最初忘记了数组字段和函数指针,这导致了各种问题;然而,借助Rust的内置测试功能,我快速解决了所有移植错误,并有了一个最小测试用例来扫描EICAR测试文件。

我们的概念验证Flying Sandbox Monster包括一个沙盒包装器和恶意软件保护引擎(MpEngine)。单个可执行文件有两种模式:父进程和子进程。模式由包含要扫描文件的HANDLE和子/父通信的环境变量存在决定。父进程在创建AppContainer子进程之前填充这两个HANDLE值。现在沙盒化的子进程加载恶意软件保护引擎库并扫描输入文件中的恶意软件。

这还不足以让概念验证工作。恶意软件保护引擎拒绝在AppContainer内初始化。最初,我认为是访问控制问题。在ProcMon中进行广泛的差异调试(比较AppContainer与非AppContainer执行)后,我意识到问题可能实际上与检测到的Windows版本有关。Tavis的代码总是自报告Windows版本为Windows XP。我的代码报告真实底层操作系统;在我案例中是Windows 10。通过WinDbg验证证明这确实是导致初始化失败的唯一个问题。我需要向MpEngine谎报底层Windows版本。当使用C/C++时,我会用Detours编写一些函数钩子代码。不幸的是,Windows上没有等效的Rust函数钩子库(可用的少数钩子库似乎比我需要的“重量级”得多)。自然,我在Rust中实现了一个简单的IAT钩子库(仅限32位Windows PE)。

介绍AppJailLauncher-rs

既然我已经在Rust中实现了AppJailLauncher的核心组件,为什么不完成工作并将其全部包装在Rust TCP服务器中?我做了,现在我很高兴宣布AppJailLauncher的“版本2”——AppJailLauncher-rs。

AppJailLauncher是一个TCP服务器,监听指定端口并为每个接受的TCP连接启动一个AppContainer进程。我试图不重新发明轮子,但mio(Rust的轻量级IO库)就是不行。首先,mio的TcpClient在Windows上不提供原始“socket HANDLEs”访问;其次,这些原始“socket HANDLEs”不能被子AppContainer进程继承。由于这些问题,我不得不引入另一个“支柱”来支持appjaillauncher-rs:TcpServer。

TcpServer负责实例化一个异步TCP服务器,其客户端套接字与STDIN/STDOUT/STDERR重定向兼容。通过socket调用创建的套接字不能重定向进程的标准输入/输出流。正常工作的标准输入/输出重定向需要“本机”套接字(通过WSASocket构建)。为了允许重定向,TcpServer创建这些“本机”套接字,并不显式禁用它们的继承。

我的Rust体验

尽管有 minor setbacks,我的整体Rust体验非常积极。让我描述一些在AppJailLauncher开发过程中真正突出的关键特性。

  • Cargo:在Windows上用C++进行依赖管理繁琐复杂,尤其是在链接第三方库时。Rust通过cargo包管理系统巧妙解决依赖管理。Cargo有广泛的包,解决许多常见问题,如参数解析(clap-rs)、Windows FFI(winapi-rs等)和处理宽字符串(widestring)。
  • 内置测试:C++应用程序的单元测试需要第三方库和费力的人工努力。这就是为什么像原始AppJailLauncher这样的小项目很少编写单元测试。在Rust中,单元测试功能内置在cargo系统中,单元测试与核心功能共存。
  • 宏系统:Rust的宏系统在抽象语法树(AST)级别工作,不同于C/C++中的简单文本替换引擎。虽然有点学习曲线,但Rust宏完全消除了C/C++宏的烦恼,如命名和范围冲突。
  • 调试:在Windows上调试Rust just works。Rust生成WinDbg兼容的调试符号(PDB文件),提供无缝源级调试。
  • 外部函数接口:Windows API用C/C++代码编写,并 meant to be called from C/C++代码。其他语言(如Rust)必须使用外部函数接口(FFI)调用Windows API。Rust对Windows的FFI(winapi-rs crate)大部分完整。它有核心API,但缺少一些较少使用的子系统,如访问控制列表修改API。
  • 属性:设置属性非常繁琐,因为它们只适用于下一行。压制特定代码格式警告需要在程序代码中散布属性。
  • 借用检查器:所有权概念是Rust实现内存安全的方式。理解借用检查器如何工作充满了 cryptic、独特错误,花了数小时阅读文档和教程。最终是值得的:一旦“点击”,我的Rust编程 dramatically improved。
  • 向量:在C++中,std::vector可以将其支持缓冲区暴露给其他代码。即使支持缓冲区被修改,原始向量仍然有效。Rust的Vec不是这样。Rust的Vec需要从旧Vec的“原始部分”形成新的Vec对象。
  • Option和Result类型:本机option和result类型应该使错误检查更容易,但错误检查似乎更冗长。可以假装错误永远不会存在,只调用unwrap,但当错误(或None)不可避免地返回时,这将导致运行时失败。
  • 拥有类型和切片:拥有类型及其互补切片(如String/str、PathBuf/Path)需要一点适应。它们成对出现,名称相似,但行为不同。在Rust中,拥有类型表示可增长、可变对象(通常是字符串)。切片是不可变字符缓冲区(也通常是字符串)的视图。

未来

Windows的Rust生态系统仍在成熟。新的Rust库有充足空间简化Windows上安全软件的开发。我已经实现了几个Rust库的初始版本,用于Windows沙盒化、PE解析和IAT钩子。我希望这些对新兴的Windows Rust社区有用。

我用Rust和AppJailLauncher沙盒化了Windows Defender,微软的旗舰反病毒产品。我的成就既伟大又有点可耻:伟大的是Windows强大的沙盒机制暴露给第三方软件;可耻的是微软没有自行沙盒化Defender。微软在2004年购买了最终成为Windows Defender的产品。早在2004年,这些错误和设计决策是不可接受的,但可以理解。过去13年,微软开发了伟大的安全工程组织、高级模糊测试和程序测试,并沙盒化了Internet Explorer的关键部分。不知何故,Windows Defender卡在了2004年。与其采取Project Zero的方法通过不断指出此固有缺陷的症状,让我们将Windows Defender带回未来。

如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News

页面内容 计划 构建沙盒 介绍AppJailLauncher-rs 我的Rust体验 未来 最近帖子 构建安全消息传递很难:对Bitchat安全辩论的 nuanced take 用Deptective调查您的依赖项 系好安全带,Buttercup,AIxCC的评分回合正在进行中! 使您的智能合约超越私钥风险成熟 Go解析器中意外的安全 footguns © 2025 Trail of Bits。 用Hugo和Mainroad主题生成。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计