Shellcode生成、加载与加密技术全解析:从原理到实践
一、Shellcode概述
Shellcode是一种恶意代码,它通过劫持计算机内存中正在运行的程序的正常流程,重定向流程以执行恶意代码而非正常程序,从而为攻击者提供shell或系统访问权限。Shellcode通常是结合漏洞利用的低级编程代码或机器代码。
Shellcode特征
- 包含执行所需shell的所有指令,同时保持相对较小的体积
- 在内存中"位置独立" - 无法预知将加载到目标进程内存的具体位置
- 不包含可能导致错误或崩溃的内容(如空字符0x00)
- 能够使用注入技术(代码或反射注入)搭载现有内存分配
二、Shellcode生成
Shellcode执行过程分为四步:
- 打开目标进程
- 在进程中分配内存
- 将shellcode写入分配的内存
- 从远程进程创建新线程执行Shellcode
攻击者需要根据目标系统选择适当的可执行文件类型:
- Windows系统:.EXE或.DLL
- Linux系统:.ELF
- Android系统:.APK
三、Shellcode存放位置
1. .data节
PE文件的.data节包含已初始化全局变量和静态变量,可读写,适合需要运行时解密/加密的shellcode。
示例代码:
#include <Windows.h>
#include <stdio.h>
unsigned char Data_RawData[] = {
// shellcode字节数组
};
int main() {
printf("[i] Data_RawData 变量 : 0x%p \n", Data_RawData);
getchar();
return 0;
}
2. .rdata节
使用const限定符指定的变量为"只读"数据,任何修改尝试会导致访问冲突。
示例代码:
const unsigned char Rdata_RawData[] = {
// shellcode字节数组
};
3. .text节
.text节存储具有可执行内存权限的变量,可直接执行而无需编辑内存权限。
示例代码:
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Text_RawData[] = {
// shellcode字节数组
};
4. .rsrc节
.rsrc节适合存储较大的shellcode,避免.data或.rdata节的大小限制。
存储步骤:
- 在Visual Studio中添加资源文件
- 导入重命名为.ico扩展名的原始shellcode
- 指定资源类型为"RCDATA"
访问.rsrc节中shellcode的API:
- FindResourceW - 获取资源位置
- LoadResource - 检索资源数据句柄
- LockResource - 获取资源数据指针
- SizeofResource - 获取资源大小
更新.rsrc节shellcode的方法:
- 使用HeapAlloc分配内存
- 用memcpy将shellcode复制到临时缓冲区
- 在新缓冲区中进行解密等操作
四、Shellcode加载技术
语言开发Loader比较
| 语言 | 可移植性 | 编译大小 | 执行速度 | 检测难度 | 特点 |
|---|---|---|---|---|---|
| Rust | ★★☆☆☆ | ★★★★☆ | ★★★★★ | 静态: ★★★☆☆ 动态: ★★★★☆ |
体积略大于C/C++,无GC,免杀潜力大 |
| Go | ★★★★★ | ★☆☆☆☆ | ★★★★☆ | 静态: ★★★☆☆ 动态: ★★★☆☆ |
编译后体积大(数MB),运行时特征明显 |
| C/C++ | ★★☆☆☆ | ★★★★★ | ★★★★★ | 静态: ★★☆☆☆ 动态: ★★★★☆ |
体积最小(几十KB),直接系统调用能力强 |
| C# | ★★★☆☆ | ★★★☆☆ | ★★★☆☆ | 静态: ★☆☆☆☆ 动态: ★★★☆☆ |
结合D/Invoke后检测率降低 |
| 汇编 | ☆☆☆☆☆ | ★★★★★ | ★★★★★ | 静态: ★★★★★ 动态: ★★★★★ |
体积最小,完全自定义,编写难度最高 |
加载方法
1. 基于内存加载
使用WinAPI系统调用动态分配RWX内存,移动shellcode并启动执行线程。
优点:
- 使用标准WinAPI调用,可靠性高
- 内存区域可执行、可写、可读,便于修改shellcode
缺点:
- 容易被成熟的AV/EDR系统检测
2. 直接函数调用加载
将shellcode数组地址强制转换为函数指针并调用。
3. 使用回调函数加载
通过Windows回调函数执行Shellcode,系统将shellcode当作回调函数运行。
4. 创建线程执行加载
创建新线程并将入口点设置为shellcode地址。
5. 基于汇编加载
直接控制硬件,效率最高,但完全针对特定CPU架构和操作系统。
五、Shellcode加密技术
加密可隐藏恶意代码,使安全解决方案更难检测,并延长恶意软件在系统中的隐藏时间。
加密的利弊
- 优点:规避基于签名的检测
- 缺点:高熵值文件可能被AV/EDR标记为可疑
加密类型
1. XOR异或加密
异或运算特性:
- 反转性质:a XOR b XOR b = a
- 交换律:a XOR b = b XOR a
- 结合律:(a XOR b) XOR c = a XOR (b XOR c)
2. RC4加密
流密码,速度快且高效,使用同一函数进行加密和解密。
实现函数:
- rc4Init - 初始化rc4context结构
- rc4Cipher - 执行RC4加密
示例代码:
// RC4加密示例
rc4Init(&rc4Ctx, key, sizeof(key));
rc4Cipher(&rc4Ctx, shellcode, shellcodeSize);
// RC4解密示例
rc4Init(&rc4Ctx, key, sizeof(key));
rc4Cipher(&rc4Ctx, encryptedShellcode, shellcodeSize);
3. AES加密
对称密钥算法,类型包括:
- AES128 - 128位密钥
- AES192 - 192位密钥
- AES256 - 256位密钥
操作模式:
- CBC - 需要初始化向量(IV)
- GCM
AES结构:
typedef struct _AES {
BCRYPT_ALG_HANDLE hAlgorithm;
BCRYPT_KEY_HANDLE hKey;
PBYTE pbKeyObject;
PBYTE pbIV;
DWORD cbKeyObject;
DWORD cbIV;
DWORD cbBlockLen;
PBYTE pbPlainText;
DWORD cbPlainText;
PBYTE pbCipherText;
DWORD cbCipherText;
} AES, *PAES;
CNG (Cryptographic Next Generation)函数:
- BCryptOpenAlgorithmProvider - 加载CNG提供程序
- BCryptGetProperty - 检索属性值
- BCryptSetProperty - 初始化属性
- BCryptGenerateSymmetricKey - 创建密钥对象
- BCryptEncrypt/BCryptDecrypt - 加密/解密数据
- BCryptDestroyKey - 销毁密钥对象
- BCryptCloseAlgorithmProvider - 关闭算法提供者
六、Shellcode传递技术
1. Web远程文件读取
通过Loader从远程web加载shellcode,避免直接上传工具被拦截。
优点:
- 减少对多种工具的免杀需求
- 仅上传Loader降低被检测风险
2. 图片(BMP)隐写术
将Shellcode隐藏在BMP图片的RGB像素中,利用文件结构特性嵌入数据。
3. Shellcode加载器
将加载程序与实际Shellcode分离到不同进程空间,避免行为检测。
关键点:
- 运行时分析通常不关心程序会做什么
- 避免直接使用函数指针执行解密后的Shellcode
- 使用Loader间接加载和执行Shellcode