【翻译】针对广泛的基于NSIS的恶意软件家族的分析
字数 2226 2025-08-22 22:47:39
基于NSIS的恶意软件家族分析与静态解包技术
0X00 前言
恶意软件加壳器(Packers/crypters)是网络犯罪分子广泛使用的工具,用于保护恶意代码免遭检测和静态分析。这些工具通过压缩和加密算法,使攻击者能够为每个活动甚至每个受害者生成独特的恶意软件样本,显著增加了杀毒软件的检测难度。在某些情况下,不使用动态分析就对恶意软件进行分类变得极具挑战性。
0X01 NSIXloader:基于NSIS的壳
NSIS(Nullsoft Scriptable Install System)包本质上是一个自解压存档,配有支持脚本语言的安装系统。它包含压缩文件以及用NSIS脚本语言编写的安装指令。网络犯罪分子利用NSIS的优势在于:
- 可以创建看似合法的安装程序样本
- NSIS自带压缩功能,恶意软件开发者无需实现压缩/解压算法
- 脚本功能允许将恶意功能转移到脚本中,增加分析难度
分析发现,这个被称为"NSIXloader"的恶意软件家族壳至少自2016年就存在,被用于保护多种恶意软件家族,包括:
- AgentTesla
- Remcos
- 404 Keylogger
- Lokibot
- Azorult
- Warzone
- Formbook
- XLoader
0X02 打包样本结构
典型样本的文件结构如下:
根目录/
├── 加密文件1 (如tiejkfis.yp)
├── 加密文件2 (如pvynjhnv.oh)
└── $PLUGINSDIR/
└── Loader.dll (恶意DLL)
NSIS脚本示例:
InstallDir $TEMP
Function .onGUIInit
InitPluginsDir
SetOutPath $INSTDIR
SetOverwrite off
File tiejkfis.yp
File pvynjhnv.oh
rnthgfcoj::HvDeclY
DLL功能分析
恶意DLL的主要功能:
- 读取加密文件(文件名硬编码)
- 使用文本密钥进行XOR解密
- 执行解密后的代码
部分变体在XOR操作前会对加密文本的每个字节进行循环移位:
字节值 = (字节值 >> shift) | (字节值 << (8 - shift)) & 0xFF
壳代码分析
解密文件包含位置无关的shellcode,其执行流程:
- 初始化加密有效负载文件名
- 获取Windows API函数地址(通过计算4字节哈希值)
- 解析kernel32.dll导出表,匹配函数哈希
- 读取并解密有效负载
每个样本使用独特的操作序列解密有效负载。
0X03 自动载荷解包方法
提取和解密步骤
- 使用7-zip从NSIS包提取文件
- 从DLL中提取加密密钥(使用正则表达式
([a-z\d]{10,20})\x00) - 解密shellcode(考虑可能的循环移位)
Python实现示例:
from malduck import procmempe
import re
dll_key_re = re.compile(br"([a-z\d]{10,20})\x00")
def dll_extract_keys(dll_data):
p = procmempe(dll_data)
for section in filter(lambda s: b"data" in s.Name, p.pe.sections):
data = p.readp(section.PointerToRawData, section.SizeOfRawData)
for found in dll_key_re.finditer(data):
yield found.group(1)
def decrypt_loader(data, dll_key):
for shift in range(8):
shifted_data = [(b >> shift) | (b << (8 - shift)) & 0xFF for b in data] if shift else data
dec_data = xor(dll_key, shifted_data)
if shellcode_validation_re.search(dec_data):
return dec_data
解密算法重建
分析汇编代码的关键点:
- 每个操作后更新解密缓冲区当前字节
- 寄存器EAX中的数据会经过转换操作(not, dec, inc, sar, shl等)
- 使用内存操作数映射(0xFF: "b"-当前字节, 0xF8: "i"-当前索引)
Python实现示例:
mem_vars_map = {0xFF: "b", 0xF8: "i"}
var_list = {"eax": 0, "ecx": 0, "b": 0, "i": 0}
def get_operation(name, op1, op2):
def not_op():
var_list[op1] = (~var_list[op1]) & 0xFF
def dec_op():
var_list[op1] = (var_list[op2] - 1) & 0xFF
# ... 其他操作实现
operations = {
"not": not_op, "dec": dec_op, # ... 其他操作
}
return operations[name]
def decrypter(enc_data):
dec_data = []
for _i, _b in enumerate(enc_data):
var_list["eax"] = _b
var_list["ecx"] = 0
var_list["b"] = _b
var_list["i"] = _i
for _op in ops:
_op()
dec_data.append(var_list["eax"])
return bytes(dec_data)
0X04 其他变体分析
1. 嵌入shellcode的DLL
特点:
- shellcode直接嵌入DLL中
- 存储在堆栈数组内
- 使用正则表达式定位代码边界:
re.search(b"\xC7\x85(..\xFF\xFF)(.{4})(\xC7(\x85..\xFF\xFF|\x45.)(.{4})){32,}.*\x8D..\\1", dll_data, re.DOTALL)
2. EXE替代DLL
特点:
- 使用可执行文件代替DLL插件
- NSIS脚本使用ExecWait调用可执行文件
- 文件结构不同(无$PLUGINSDIR目录)
3. 资源中的Shellcode
特点:
- shellcode存储在RT_RCDATA类型资源中
- 其余功能与基础变体相同
4. RC4加密的有效载荷
特点:
- 使用System.dll插件进行API调用
- 内存分配和保护设置(PAGE_EXECUTE_READWRITE)
- 使用修改版RC4加密:
decrypted_data = rc4(rc4_key, enc_data)
decrypted_data = xor(rc4_key, decrypted_data)
- RC4密钥存储在堆栈字符串中
0X05 防御与IOC
检测指标
杀毒软件检测名称:
- Packer.Win.NSISCrypter.*
- Trojan.Win.Shellcode.F
- Trojan.Win.Shellcode.G
样本哈希列表
| SHA256 | 变体 | 载荷 |
|---|---|---|
| 12a06c74a79a595f... | DLL加载器,单独shellcode文件 | XLoader |
| 44e51d311fc72e8c... | EXE加载器,单独shellcode文件 | XLoader |
| 00042ff7bcfa012a... | DLL中嵌入shellcode | XLoader |
| 3f7771dd0f4546c6... | EXE加载器,资源中的shellcode | Remcos |
| 160928216aafe9eb... | RC4加密有效载荷 | XLoader |
| cd7976d9b8330c46... | EXE加载器,单独shellcode文件 | Agent Tesla |
| a3e129f03707f517... | EXE加载器,单独shellcode文件 | 404 Keylogger |
| bb8e87b246b84778... | EXE加载器,单独shellcode文件 | Formbook |
| 178f977beaeb0470... | EXE加载器,单独shellcode文件 | Lokibot |
| 80db5ced29416066... | DLL加载器,单独shellcode文件 | Warzone |
| 090979bcb0f2aeca... | EXE加载器,单独shellcode文件 | Azorult |
结论
基于NSIS的恶意封包器家族因其广泛使用和多样化载荷而构成严重威胁。开发自动化静态解包工具对于快速分析恶意软件、提取配置数据和进行深入调查至关重要。防御者应关注这些封包器的特征和IOC,以增强检测能力。