0%
毅种循环

模块 18 - Payload 加密 - RC4

恶意软件开发课程 - Payload 加密 - RC4


模块 18 - Payload 加密 - RC4#

[!IMPORTANT] 本知识库声明

本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示实践心得

  1. 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
  2. 版权归属:原始知识产权归原作者/官方所有。
  3. 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程
  4. 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。

Payload 加密 - RC4#

简介#

RC4 是一种快速高效的流加密算法,也是一种双向加密算法,允许使用相同的函数进行加密和解密。网上有多种 RC4 的 C 语言实现,但本模块将演示三种执行 RC4 加密的方法。

需要注意的是,深入研究 RC4 算法的工作原理并不是本模块的目标,也不需要完全深入理解它。相反,目标是加密 payload 以规避检测


RC4 加密 - 方法 1#

此方法使用这里找到的 RC4 实现,因其稳定性和代码质量良好而被选择。有两个函数 rc4Initrc4Cipher,分别用于初始化 Rc4Context 结构和执行 RC4 加密。

RC4 上下文结构#

typedef struct
{
	unsigned int i;              // RC4 状态变量 i
	unsigned int j;              // RC4 状态变量 j
	unsigned char s[256];        // RC4 的 S-box (置换表)

} Rc4Context;
c

RC4 初始化函数#

[!IMPORTANT] 📚 知识扩展 - RC4 初始化过程 (KSA - Key Scheduling Algorithm)

RC4 初始化分为两个阶段:

阶段 1: 初始化 S-box

for (i = 0; i < 256; i++)
    s[i] = i;  // S = [0, 1, 2, ..., 255]
c

阶段 2: 密钥调度

for (i = 0, j = 0; i < 256; i++) {
    j = (j + s[i] + key[i % length]) % 256;
    swap(s[i], s[j]);  // 交换 S[i] 和 S[j]
}
c

为什么要这样做?

  • 将密钥的影响”扩散”到整个 S-box
  • 即使密钥很短(如 8 字节),也能生成 256 字节的复杂状态
  • 交换操作确保 S-box 成为 0-255 的一个随机排列

示例 (简化):

密钥: [0xAA, 0xBB, 0xCC]
初始 S: [0, 1, 2, 3, 4, ...]

第 1 步: j = (0 + 0 + 0xAA) % 256 = 170, 交换 S[0] 和 S[170]
第 2 步: j = (170 + 1 + 0xBB) % 256 = 76, 交换 S[1] 和 S[76]
...

最终 S: [170, 76, 219, ...] (完全随机化的排列)
plaintext

RC4 加密/解密函数#


RC4 加密示例#

以下代码展示了如何使用 rc4Initrc4Cipher 函数加密 payload。

// 初始化 RC4 上下文结构
Rc4Context ctx = { 0 };

// 用于加密的密钥
unsigned char* key = "maldev123";
rc4Init(&ctx, key, strlen(key));  // 使用密钥初始化

// 加密 //
// plaintext  - 要加密的 payload
// ciphertext - 用于存储输出加密数据的缓冲区
rc4Cipher(&ctx, plaintext, ciphertext, sizeof(plaintext));
c

RC4 解密示例#

以下代码展示了如何使用 rc4Initrc4Cipher 函数解密 payload。

// 初始化 RC4 上下文结构
Rc4Context ctx = { 0 };

// 用于解密的密钥
unsigned char* key = "maldev123";
rc4Init(&ctx, key, strlen(key));  // 使用相同的密钥初始化

// 解密 //
// ciphertext - 要解密的加密 payload
// plaintext  - 用于存储输出明文数据的缓冲区
rc4Cipher(&ctx, ciphertext, plaintext, sizeof(ciphertext));
c

RC4 加密 - 方法 2#

未公开的 Windows NTAPI SystemFunction032 提供了 RC4 算法的更快、更小的实现。有关此 API 的更多信息可以在这个 Wine API 页面上找到。

[!IMPORTANT] 📚 知识扩展 - 未公开的 Windows API

什么是未公开 API?

  • Windows 包含许多未公开的函数,不在官方 MSDN 文档中
  • 这些函数被操作系统内部使用,但没有公开给开发者
  • SystemFunction032 就是这样一个函数

为什么使用未公开 API?

  • 更小的二进制文件 - 不需要包含第三方库
  • 更快的性能 - 由微软优化的原生实现
  • 隐蔽性 - 安全工具可能不熟悉这些 API

风险:

  • ❌ 未来 Windows 版本可能删除或修改
  • ❌ 行为可能在不同 Windows 版本间变化
  • ❌ 缺乏官方文档和支持

SystemFunction032 函数签名#

文档页面指出,函数 SystemFunction032 接受两个 USTRING 类型的参数。

NTSTATUS SystemFunction032
(
  struct ustring*       data,  // 要加密/解密的数据
  const struct ustring* key    // 加密/解密密钥
)
c

USTRING 结构#

不幸的是,由于这是一个未公开的 API,USTRING 的结构定义是未知的。但通过额外的研究,可以在 wine/crypt.h 中找到 USTRING 结构定义。该结构如下所示。有关此结构的更深入信息可以在 Geoff Chappell 找到。

typedef struct
{
	DWORD	Length;         // 要加密/解密的数据大小
	DWORD	MaximumLength;  // 数据的最大大小,通常与 Length 相同 (USTRING.Length = USTRING.MaximumLength = X)
	PVOID	Buffer;         // 要加密/解密的数据的基地址

} USTRING;
c

现在知道了 USTRING 结构,就可以使用 SystemFunction032 函数了。

获取 SystemFunction032 的地址#

要使用 SystemFunction032,必须首先获取其地址。由于 SystemFunction032advapi32.dll 导出,因此必须使用 LoadLibrary 将 DLL 加载到进程中。函数调用的返回值可以直接在 GetProcAddress 中使用。

成功获取 SystemFunction032 的地址后,应将其类型转换为与之前引用的 Wine API 页面上找到的定义匹配的函数指针。但是,返回的地址可以直接从 GetProcAddress 进行转换。所有这些都在下面的代码片段中演示。

fnSystemFunction032 SystemFunction032 = (fnSystemFunction032) GetProcAddress(LoadLibraryA("Advapi32"), "SystemFunction032");
c

SystemFunction032 的函数指针定义为 fnSystemFunction032 数据类型,如下所示。

typedef NTSTATUS(NTAPI* fnSystemFunction032)(
	struct USTRING* Data,   // USTRING 类型的结构,包含要加密/解密的缓冲区信息
	struct USTRING* Key     // USTRING 类型的结构,包含加密/解密时使用的密钥信息
);
c

SystemFunction032 使用方法#

以下代码片段提供了一个工作代码示例,利用 SystemFunction032 函数执行 RC4 加密和解密。

[!IMPORTANT] 📚 知识扩展 - SystemFunction032 的特点

与方法 1 的对比:

特性方法 1 (自实现)方法 2 (SystemFunction032)
代码量~100 行~30 行
依赖advapi32.dll
性能良好优秀(原生实现)
可控性完全可控Windows 控制
隐蔽性中等较高(非常见 API)

重要提示:

  • SystemFunction032就地加密/解密(直接修改 Data.Buffer)
  • 加密和解密使用同一个函数
  • 不需要维护 Rc4Context 结构

RC4 加密 - 方法 3#

实现 RC4 算法的另一种方式是使用 SystemFunction033,它接受与之前展示的 SystemFunction032 函数相同的参数。


加密/解密密钥格式#

本模块和其他加密模块中的代码片段使用了表示加密/解密密钥的一种有效方式。但是,重要的是要知道密钥可以用几种不同的方式表示。

请注意,将明文密钥硬编码到二进制文件中被认为是不好的做法,在分析恶意软件时很容易被提取。未来的模块将提供解决方案以确保密钥不会被轻易获取。

[!WARNING] ⚠️ 密钥硬编码的安全风险

为什么硬编码密钥是危险的?

  1. 静态分析可见

    使用 strings 命令:
    $ strings malware.exe | grep -i "maldev"
    maldev123  ← 密钥暴露!
    plaintext
  2. 内存转储可见

    • 调试器可以轻松找到字符串常量
    • 内存取证工具可以提取密钥
  3. 逆向分析容易

    • IDA Pro / Ghidra 会高亮显示字符串
    • 交叉引用功能可快速定位密钥使用位置

更安全的替代方案:

栈字符串 (方法 4)

unsigned char key[] = { 'm', 'a', 'l', 'd', 'e', 'v', '1', '2', '3' };
// 优势: 不会出现在 .rdata 段,稍微难找一些
c

运行时解密密钥

unsigned char encrypted_key[] = { 0xE7, 0xC3, 0xCF, ... };
// 在运行时用另一个算法解密这个密钥
decrypt_key(encrypted_key);
c

从环境派生密钥

// 基于计算机名、用户名等生成密钥
generate_key_from_computer_name();
c

从网络获取密钥

// 从 C2 服务器下载密钥
fetch_key_from_server();
c

三种 RC4 方法对比#

特性方法 1 (自实现)方法 2 (SystemFunc032)方法 3 (SystemFunc033)
代码量大 (~100 行)小 (~30 行)小 (~30 行)
性能良好优秀优秀
依赖无外部依赖advapi32.dlladvapi32.dll
可移植性仅 Windows仅 Windows
隐蔽性中等较高较高
维护性完全可控依赖 Windows依赖 Windows
推荐度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

总结#

本模块展示了三种实现 RC4 加密的方法:

  1. 自定义实现 - 完全可控,代码较长
  2. SystemFunction032 - Windows 原生,简洁高效 (推荐)
  3. SystemFunction033 - 与方法 2 类似的替代方案

RC4 比 XOR 更安全,比 AES 更简单,是恶意软件开发中的常见选择。然而,对于需要高安全性的场景,仍建议使用 AES256。

[!IMPORTANT] 下一步学习:

在下一个模块中,我们将学习 AES 加密,这是目前最安全的对称加密算法之一!

RC4 vs AES 预告:

算法安全性速度代码复杂度实战应用
RC4⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐中等价值 payload
AES⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐高价值 payload