Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
BeatRev — 用于挫败/击败恶意软件分析师的 POC | Kitploit
工具/GitHubGitHub/octoberfest7/beatrev
逆向工程恶意软件分析学习与教育Payload 开发
GitHuboctoberfest7/beatrev

BeatRev

用于挫败/击败恶意软件分析师的 POC

查看仓库
1542244年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

BeatRev 版本 2

免责声明/责任

以下工作是概念验证,旨在使恶意软件能够针对特定受害者进行“密钥绑定”,从而挫败恶意软件分析人员的工作。

我不对本项目中所包含的任何想法或代码的恶意使用承担任何责任。我提供此研究是为了进一步教育信息安全专业人士,并为恶意软件分析师、逆向工程师和蓝队成员提供额外的培训/思考素材。

TLDR

恶意软件首次在受害者机器上运行时,会使用该受害者的环境数据对实际载荷(一个 RDLL)进行 AES 加密。每次后续运行恶意软件时,它会收集相同的环境信息,对存储在恶意软件内字节数组中的载荷进行 AES 解密,然后运行。如果解密失败/载荷无法运行,恶意软件将自行删除。以此防范逆向工程师和恶意软件分析师。

更新于 2022 年 6 月 6 日

image

我觉得这个项目尚未完成,因此我回过头来进行了一次相当大规模的重写。原始的研究和技术手法可参见此处。

主要变更如下:

  1. 我已发布所有源代码
  2. 我将 Stephen Fewer 的 ReflectiveDLL 集成到项目中,以取代 Stage2
  3. 我将该项目中的一些字节数组格式化为字符串形式,并使用 UuidFromStringA 进行解析。此仓库 被用作模板。这样做是为了降低 Stage0 和 Stage1 的熵。
  4. Stage0 内置了相当多的 AV 规避功能。感谢 Cerbersec 的 Project Ares 提供的灵感
  5. 已包含用于生成 Stage0 的构建器应用程序

这个项目的源代码中有许多不同的内容可以提取出来供其他用途。希望它能对某些人有所帮助。

原始版本的问题及缓解措施

原始版本的 BeatRev 存在一些缺陷,我决定尝试加以解决。

Stage2 以前是一个独立的可执行文件,存储为 Stage1 的备用数据流(ADS)。为了实现由受害者决定的 AES 加密以及后续的解密和执行,每次 Stage1 运行时都需要读取 ADS、解密、写回 ADS、调用 CreateProcess,然后重新加密 Stage2 并写回磁盘上的 ADS。这涉及大量的 I/O 操作,而且 CreateProcess 调用当然也不理想。

我偶然看到了 Steven Fewer 关于 Reflective DLL 的研究,认为它很合适。Stage2 现在是一个 RDLL;我们想要保护的恶意软件/Shellcode 加载器/其他任何内容都可以移植为 RDLL 格式,并作为字节数组存储在 Stage1 中,然后在运行时由 Stage1 解密并执行。这消除了版本 1 中的所有 I/O 操作和 CreateProcess 调用,是一个受欢迎的改进。

Stage1 之前没有内置任何真正的 AV 规避措施;这是有意为之,因为这是额外的工作,并非本研究的重点。在重写过程中,我将其视为额外的挑战,并添加了 API 哈希,以从 Stage1 的导入地址表中移除函数。这有助于降低检测率,Stage1 在 VirusTotal 上的检测率为 4/66。我放心地上传了 Stage1,因为它已经绑定到最初运行的机器上,而且由于发生的 AES 加密,文件签名不断变化。

我最近开始关注熵作为检测恶意软件的手段;为了降低一个巨大的 AES 加密二进制 blob 给可执行文件带来的极高熵,我研究了将 Shellcode 存储为 UUID 的方法。由于二进制数据以字符串表示形式存储,可执行文件的整体熵较低。使用这种技术,Stage0 的熵现在约为 6.8,Stage1 约为 4.5(最大值为 8)。

最后,由于需要操作的所有部分,集成并生成完整的 Stage0 是一项艰巨的任务。为了简化这一过程,我制作了一个构建器应用程序,它可以接收一个 Stage0.c 模板文件、一个 Stage1 存根、一个 Stage2 存根和一个原始的 Shellcode 文件(这是围绕 Stage2 作为一个包含 CobaltStrike Shellcode 的 Shellcode 加载器而构建的),并生成一个编译好的 Stage0 载荷供在目标上使用。

技术细节

Stephen Fewer 的 Reflective DLL 代码包含一些 Visual Studio 编译器特定的指令;我确信可以将该技术移植到 MingW,但我没有足够的技能做到这一点。这里的主要问题是 CobaltStrike Shellcode(无阶段版本约为 265K)需要放入 RDLL 并进行编译。为了绕过这个问题并使其与流程的其余部分良好集成,我编写了我的 Stage2 RDLL,使其包含一个与 CS Shellcode 大小相同的全局变量内存块;这个约 265K 的内存块中包含一个占位符,可以在编译后的二进制文件中定位到它。src/Stage2 中的代码已经包含了这一点。

编译后,这个 Stage2stub 被传输到 Kali,在那里可以进行二进制补丁,将真正的 CS Shellcode 粘贴到它所属的内存位置。这样就生成了完整的 Stage2。

为了避免之前描述的 I/O 和 CreateProcess 问题,完整的 Stage2 还必须由 Stage0 修补到编译后的 Stage1 中;这是必需的,以便允许 Stage2 在目标上被加密,同时防止 Stage2 单独存储在磁盘上。Stage0 在目标上执行与 Stage2 之前描述的相同概念,以组装最终的 Stage1 载荷。需要注意的是,使用 memmem 函数来定位每个存根中的占位符;该函数在 Windows 上不可用,因此使用了自定义实现。感谢 Foxik384 提供的代码。

为了执行二进制补丁,我们必须预先分配所需的内存;这会产生连锁效应,因为 Stage1 现在必须足够大以容纳 Stage2。再加上将 Stage2 转换为 UUID 字符串的步骤,Stage2 的大小会膨胀,Stage1 为了容纳它也会膨胀。一个编译后大小约为 290K 的 Stage2 RDLL 会导致 Stage0 载荷约为 1.38M,Stage1 载荷约为 700K。

构建器应用程序仅支持创建 x64 EXE。然而,经过更多的工作,理论上你可以将 Stage0 和 Stage1 都制作为 DLL,从而使整个生命周期作为 DLL 劫持而不是独立的可执行文件存在。

操作说明

以下说明将帮助您开始使用此概念验证。

  1. 使用 gcc -o builder src/Builder/BeatRevV2Builder.c 编译 Builder
  2. 修改 src/Stage2/dll/src/ReflectiveDLL.c 中的 sc_length 变量,使其与构建器使用的原始 Shellcode 文件长度匹配(我提供了 fakesc.bin 作为示例)
  3. 编译 Stage2(在 Visual Studio 中,ReflectiveDLL 项目使用一些 VS 编译器特定的指令)
  4. 将编译后的 stage2stub.dll 移回 Kali,修改 src/Stage1/newstage1.c 并将 stage2size 定义为 stage2stub 的大小
  5. 使用 x86_64-w64-mingw32-gcc newstage1.c -o stage1stub.exe -s -DUNICODE -Os -L /usr/x86_64-w64-mingw32/lib -l:librpcrt4.a 编译 stage1stub
  6. 使用语法运行构建器:./builder src/Stage0/newstage0_exe.c x64 stage1stub.exe stage2stub.dll shellcode.bin
  7. 构建器将生成 dropper.exe。这是一个格式化并编译好的 Stage0 载荷,用于在目标上使用。

BeatRev 原始版本

引言

大约 6 个月前,我突然想到,尽管我在恶意软件有关 AV/EDR 规避方面学到了很多并且做了很多工作,但我很少关注如何尝试规避或挫败逆向工程/恶意软件分析。这有几个很好的理由:

  1. 我对恶意软件分析或逆向工程一无所知
  2. 当涉及到合法、经授权的红队工作时,实际上并不需要尝试挫败或规避逆向工程师,因为活动在到达那个阶段之前很久就应该已经冲突解除了。

尽管如此,这仍然是一个有趣的思维实验,我有一些确实了解恶意软件分析的同事,我可以与他们交流想法。这似乎是比 AV/EDR 规避更大一个数量级的挑战,我决定尝试一下。

前提

我的最初前提是,恶意软件在首次运行时,会以某种方式将其自身“密钥绑定”到该受害者机器上;任何后续尝试运行它时,都会评估目标环境中的某些因素,并将其与恶意软件中的信息进行比较。如果这两个因素匹配,则按预期执行。如果不匹配(例如样本已传输到恶意软件分析人员的沙箱中),恶意软件将自行删除(再次大量借鉴 LloydLabs 及其 delete-self-poc 的工作)。

这个“密钥”必须是受害者计算机的某种“唯一”标识。理想情况下,它将是若干条信息的组合,并进一步进行混淆。例如,我们可以收集计算机的主机名以及安装的 RAM 大小;这两个值可以连接起来(例如 Client018192MB),然后使用用户定义的函数进行哈希处理,生成一个数字(例如 5343823956)。

在收集哪些信息方面有很多选择,但需要考虑蓝队成员能够轻易伪造哪些值;例如,MAC 地址可能看起来是一个有吸引力的受害者“唯一”标识符,但 MAC 地址可以很容易地手动设置,以便逆向工程师将他们的沙箱与原始受害者匹配。理想情况下,选择并枚举的值应该是逆向工程师难以在其环境中复制的值。

利用一些自删除魔法,恶意软件可以将其自身读入缓冲区,定位占位符变量并将其替换为该数字,然后删除自身,最后将修改后的恶意软件写回磁盘上的同一位置。结合 Main 中的 if/else 语句,下一次恶意软件运行时,它会检测到之前已经运行过,然后再次收集主机名和 RAM 大小以生成哈希数字。这将与第一次运行时存储在恶意软件中的数字(5343823956)进行比较。如果匹配(就像恶意软件在与其原始运行相同的机器上运行时的情况),则按预期执行;但如果返回不同的值,它将再次调用自删除函数以从磁盘中删除自身,从而保护作者免受恶意软件分析人员的分析。

理论上,这个想法似乎不错,直到我与一位具有真实恶意软件分析和逆向工程经验的同事交谈。我被告知,逆向工程师能够观察到恶意软件中的条件语句(if ValueFromFirstRun != GetHostnameAndRAM()),并且由于预期值在条件语句的一侧是硬编码的,他们只需修改寄存器以包含预期值,从而完全绕过整个保护机制。

这个新知识完全颠覆了这个思维实验,而且因为我一开始就没有这种能力的实际用途,项目就此停止了大约 6 个月。

概述

在接下来的 6 个月里,这个项目偶尔会被重新提起,但每次都是短暂的想法,因为我对逆向/恶意软件分析没有新的了解,而且同样没有对此类能力的需求。几天前,这个想法再次浮现,尽管这两个因素仍然没有真正改变,但我想我积累了一点更多的知识,这次无法放弃这个想法。

考虑到之前关于硬编码值的问题,我最终决定采用多阶段设计。我将它们称为 Stage0、Stage1 和 Stage2。

Stage0:设置。在初始感染时运行,之后被删除。

Stage1:运行器。每次后续执行恶意软件时运行。

Stage2:载荷。您关心的要保护的恶意软件。生成一个进程并注入 Shellcode 以返回 Beacon。

生命周期

Stage0

Stage0 是由攻击者交付给目标的新的可执行文件。它包含 Stage1 和 Stage2,作为 AES 加密的字节数组;这样做是为了在传输过程中保护恶意软件,或者以防防御者以某种方式获得了 Stage0 的副本(这不应该发生)。AES 密钥和 IV 包含在 Stage0 中,因此实际上这并不能保护 Stage1 或 Stage2 免受有能力的蓝队成员的分析。

Stage0 执行以下操作:

  1. 沙箱规避。
  2. 从磁盘中删除自身。它仍在内存中运行。
  3. 使用存储的 AES 密钥/IV 解密 Stage1 并将其写入磁盘,替换 Stage0。
  4. 收集处理器名称和 Microsoft ProductID。
  5. 对该值进行哈希处理,然后填充以适应 16 字节的 AES 密钥长度。将该值反转后用作 AES IV。
  6. 使用存储的 AES 密钥/IV 解密 Stage2。
  7. 使用新的特定于受害者的 AES 密钥/IV 加密 Stage2。
  8. 将 Stage2 作为 Stage1 的备用数据流写入磁盘。

在这一系列事件结束时,Stage0 退出。因为它在步骤 2 中已从磁盘删除,并且不再在内存中运行,所以 Stage0 实际上消失了;如果没有事先了解这种技术,恶意软件生命周期的其余部分将比它本来的情况更加令人困惑。

在步骤 4 中,收集处理器名称和 Microsoft ProductID;ProductID 从注册表中检索,该值可以手动修改,这为蓝队成员提供了一个轻松的机会来使他们的沙箱匹配目标环境。根据收集的环境信息的不同,这可能会变得更容易或更困难。

Stage1

Stage1 由 Stage0 释放,并且存在于与 Stage0 完全相同的位置(包括文件名)。Stage2 作为 Stage1 的 ADS 存储。当攻击者/持久性机制随后执行恶意软件时,他们执行的是 Stage1。

Stage1 执行以下操作:

  1. 沙箱规避。
  2. 收集处理器名称和 Microsoft ProductID。
  3. 对该值进行哈希处理,然后填充以适应 16 字节的 AES 密钥长度。将该值反转后用作 AES IV。
  4. 将 Stage2 从 Stage1 的 ADS 读取到内存中。
  5. 使用特定于受害者的 AES 密钥/IV 解密 Stage2。
  6. 检查解密后的 Stage2 缓冲区的前两个字节;如果不是 MZ(解密失败),则删除 Stage1/Stage2,退出。
  7. 将解密后的 Stage2 写回磁盘,作为 Stage1 的 ADS。
  8. 在 Stage2 上调用 CreateProcess。如果失败(解密失败),则删除 Stage1/Stage2,退出。
  9. 休眠 5 秒,以允许 Stage2 执行并退出,以便可以覆盖它。
  10. 使用特定于受害者的 AES 密钥/IV 加密 Stage2。
  11. 将加密后的 Stage2 写回磁盘,作为 Stage1 的 ADS。

请注意,Stage2 必须退出才能被覆盖;自删除技巧似乎不适用于已经是 ADS 的文件,因为自删除技术依赖于重命名可执行文件的主数据流。Stage2 理想情况下是一个注入或生成+注入的可执行文件。

Stage1 有两个点可以检测到它不是在同一个受害者上运行,并删除自身/Stage2 以保护威胁行为者。第一个是在使用收集的环境信息解密 Stage2 后检查可执行文件头部;理论上,逆向工程师可以绕过这一步,但这是一个良好的初始检查。第二个保护点是 CreateProcess 调用的结果——如果由于 Stage2 未正确解密而失败,恶意软件同样会被删除。逆向工程师也可以修改此调用的结果以防止删除,但这并不能改变 Stage2 已被加密且无法访问的事实。

Stage2

Stage2 是恶意软件链的核心部分;它本身就是一个完整的 Shellcode 加载器/恶意软件。通过以这种方式加密和保护它,最终状态恶意软件的动作得到了更好的混淆,并受到保护,免受逆向工程师和恶意软件分析人员的分析。在开发过程中,我使用了包含 CobaltStrike Shellcode 的现有 Shellcode 加载器,但这可以是攻击者想要运行和保护的任何内容。

影响、缓解措施和后续工作

那么,像这样的恶意软件生命周期实际上实现了什么?有几个有趣的特性值得一提。

备用数据流是 NTFS 文件系统独有的功能;这意味着初始感染后传输恶意软件的大多数方式都会剥离并丢失 Stage2,因为它是 Stage1 的 ADS。必须特别小心地传输样本以保留 Stage2,因为没有它,许多逆向工程师和恶意软件分析人员将对正在发生的事情感到非常困惑。RAR 存档能够保留 ADS,像 7Z 和 Peazip 这样的工具可以提取文件及其 ADS。

如前所述,当使用这种生命周期的恶意软件到达蓝队成员手中时,它应该是 Stage1 阶段;Stage0 已经来去无踪,而 Stage2 已经使用 Stage0 收集的环境信息进行了加密。不知道 Stage0 甚至存在,将给理解生命周期和解密 Stage2 带来相当大的不确定性。

理论上(因为再次强调,我没有逆向工程经验),Stage1 应该能够被逆向(在蓝队成员因为不断自删除而处理了几个副本之后),并且 Stage1 从目标系统收集的信息应该能够被识别出来。如果有良好的协调响应,蓝队应该能够识别出恶意软件来自的受害者,并从中收集该信息,将其输入程序,以便适当地转换为解密 Stage2 的 AES 密钥/IV。然而,这其中有很多“如果”,与逆向工程师的相对技能以及受害者机器是否可用于恢复该信息有关。

应用程序白名单将显著挫败此生命周期。Stage0/Stage1 可能能够作为 DLL 进行侧加载,但我怀疑 Stage2 作为 ADS 会带来一些问题。我没有测试恶意软件对抗 AWL 的环境,也没有费心将其全部移植到 DLL 格式,所以我无法断言。我相信有创造性的方法可以解决这些问题。

我也相当确信,有比将 Stage2 放到磁盘并调用 CreateProcess 更聪明的方法来运行它;无论是手动映射可执行文件,还是使用像 Donut 这样的工具将其转换为 Shellcode,都是合理的想法。

代码和二进制文件

在开发过程中,我创建了一个构建器应用程序,可以将 Stage1 和 Stage2 输入其中,以生成功能性的 Stage0;但这将不会提供,不过我将提供 大部分 Stage1 的源代码,因为它是最容易被蓝队成员看到的部分。Stage0 将作为给读者的练习而排除,Stage2 则是您想要运行和保护的任何独立可执行文件。这项概念验证可以由有能力的读者自行决定进一步研究。我将提供此恶意软件的编译副本,即 Dropper64.exe。Dropper64.exe 是为 x64 编译的。Dropper64.exe 是 Stage0;它包含 Stage1 和 Stage2。执行时,Stage1 和 Stage2 将释放到磁盘,但不会自动执行,您必须再次运行 Dropper64.exe(此时为 Stage1)。Stage2 是 calc.exe 的 x64 版本。我提供这个以便任何蓝队人员查看,但请注意,在事件响应场景中,99% 的情况下您会得到 Stage1/Stage2,而 Stage0 将消失。

结论

这是一个有趣的小项目,占用了一个长周末。我敢肯定,如果我有调试器和反汇编器的经验,它会更加高级/更完整,但你要利用手头的资源做到最好。我渴望听到蓝队人员和其他恶意软件开发者的看法。我确信,考虑到实际 APT 组织所做的,我在这里过于复杂地重新发明了轮子,但过程中我学到了一些东西。感谢阅读!

下载工具