Donut 的规避型近亲。
Fritter 是 TheWover 和 Odzhan 的 Donut shellcode 生成器的一个重度修改分支。它生成用于在内存中执行 VBScript、JScript、EXE、DLL 和 .NET 程序集的位置无关 shellcode,重点在于规避与抗特征检测。该代码库仅支持 x64。
很多。 Fritter 移除了不太常用的功能,并替换了多年来已形成明显特征码的内部实现。除其他许多方面外,加密、压缩、哈希和 API 解析层均已重新设计。
多态与规避是设计目标。每次输出都是唯一的,而且该工具的每次构建本身也是唯一的。这里有两个不同的层次:
每次输出随机化:在每次调用 fritter 时应用。入口存根、多态解码器、加密密钥以及生成的 shellcode 中的许多结构元素都会在每次 PIC 构建时根据新的熵重新生成。
每次构建随机化:在每次编译 fritter 本身时应用。加解密与哈希轮转常量、API 解析表布局、shim 侧字符串加扰、PEB 遍历方向、执行后擦除模式,以及加载器和 shim 内部的多个结构维度,都会在编译时固化。
在运行时,Fritter 会尽量减小加载器的可执行文件足迹。加载器被划分为各自独立加密的函数,每个函数都有自己的 PE 节、XOR 密钥和调度器。任何时刻只有其中一个函数的字节以明文形式存在。早期的 VEH 滑动窗口模型已被弃用,取而代之的是这种调度模型。
这很重要。 在 releases 中提供用于测试的预构建二进制文件,其每次构建的常量在所有使用该二进制文件的用户之间是共享的。
构建你自己的副本。 每次执行 make 时,这些每次构建的维度都会重新随机化:
# Linux, static-musl ELF, no runtime libc dependency
# Requires: build-essential, mingw-w64, musl-tools
make -f Makefile.linux release
# Windows (MSVC), recommended on Windows
nmake -f Makefile.msvc
在 Windows 上,推荐使用 MSVC 工具链。它会把每个加载器函数放入各自页对齐的 PE 节中,这正是 N>1 按函数调度模型所需要的。mingw 目前会将所有内容放到单个 .text 节中,因此只会有一个覆盖整个加载器的入口,并使用一个 XOR 密钥(功能上与 MSVC 输出相同,但只有一个多态调度器,而不是多个)。如果你没有 Visual Studio,可以在 WSL 下使用 Makefile.linux 构建;它会通过 mingw-w64 交叉编译 Windows 加载器。
每次 make 都会运行 tools/gen_poly 来生成全新的每次构建常量,并运行 tools/gen_api_shuffle 来置换 API 解析表。生成的 fritter 二进制文件本身也是唯一的:不同的加解密常量、不同的哈希常量、不同的 API 表布局、不同的 shim 侧字符串加扰等等。由那个二进制文件生成的每个 shellcode 都会共享那些每次构建常量,但在每次输出的维度上有所不同。
附带的 /test 文件夹中包含 calc.exe 和 inject_local64.exe,可用于测试 Fritter。如需自行重建测试宿主:在 Windows 上执行 nmake -f Makefile.msvc harness,或在 WSL 下执行 make -f Makefile.linux harness。
fritter [options] -i <EXE/DLL/VBS/JS>
INPUT
-i, --input <path> Input file to execute in-memory
-p, --args <args> Parameters / command line for target
-c, --class <name> Class name (required for .NET DLL)
-m, --method <name> Method or function for DLL
-r, --runtime <ver> CLR runtime version
-w, --unicode Pass command line as UNICODE
-t, --thread Run unmanaged EXE entrypoint as thread
OUTPUT
-o, --output <path> Output file (default: loader.bin)
-f, --format <1-8> 1=Bin 2=B64 3=C 4=Ruby 5=Py 6=PS 7=C# 8=Hex
-x, --exit <1-3> 1=Thread (default) 2=Process 3=Block
-y, --fork <offset> Fork thread, continue at RVA offset
LOADER
-e, --entropy <1-3> 1=None 2=Random names 3=Names+Crypto (default)
-k, --headers <1-2> 1=Overwrite (default) 2=Keep all
-g, --chunked <0-1> (deprecated; dispatch shim is always used)
-d, --domain <name> AppDomain name for .NET
-j, --decoy <path> Decoy module for Module Overloading
STAGING
-n, --modname <name> Module name for HTTP staging
-s, --server <url> Server URL (supports basic auth)
fritter -i payload.exe
fritter -i implant.dll -m RunMain -p "arg1 arg2"
fritter -i payload.exe -g 0 -k 2 -o out.bin
Fritter 的 shellcode 载荷以嵌套层次构成,每一层都会解密或暂存下一层:
入口存根。 一个长度可变的随机垃圾前缀、一个每次输出都会生成的 RSP 对齐例程,以及一个生成式跳板。基于 Shikata Ga Nai 的方法。
多态 XOR 解码器。 两遍汇编生成。寄存器分配通过 Fisher-Yates 洗牌从池中抽取。密钥长度按每次输出选取。每条真实指令之间都会插入垃圾指令。热循环中可移动的指令组会在正确性约束内重新排序。
Dispatch shim。 取代了早期的 VEH 滑动窗口 shim。将加载器区域从 RW 切换为 RWX,然后将控制权交给加载器入口。在 N>1 调度下,每次调用都会通过按函数 thunk 路由到相应的按函数调度器,由后者解密、运行并在返回时擦除。每个调度器的操作码布局都会因构建而异 (见 CHANGELOG v1.3)。
加载器。 PE 内存映射器。按哈希解析 API,通过节 API 映射嵌入的 PE,应用导入表 / 重定位 / TLS 回调,调用入口点,然后进行擦除。PEB 遍历方向、执行后擦除字节、MainProc 中的结构盐位点都会在每次构建时随机化。
清理。 使用每次构建的字节模式擦除加载器页面,清除实例,并根据 -x 通过线程或进程终止退出。没有需要清理的 VEH 处理器或上下文结构。
执行后残留的足迹是 dispatch shim 运行过的一个小型 RWX 页面。在线程模式下,映射的 PE 节会被有意保留完整,以便 CRT 回调有后续执行环境。
Fritter 建立在 TheWover 和 Odzhan 的工作之上,他们最初的 Donut 项目使位置无关 shellcode 的生成变得易用且实用。他们的架构、加载器设计和 PIC 框架是这里一切工作的基础。PE 映射、.NET 托管和脚本执行路径大部分是他们的工作,被保留并受到尊重。
BSD 3-Clause。参见 LICENSE。