恶意样本自动化配置提取初探
字数 1054 2025-08-06 18:07:33

恶意样本自动化配置提取技术详解

0x00 前言

本文详细讲解如何通过自动化方式从Emotet恶意软件样本中提取ECC加密密钥和C2服务器配置信息。通过分析CAPEv2沙箱的提取代码,结合PE文件结构分析技术,使用Python实现自动化配置提取。

0x01 环境准备

开发环境

  • 编程语言: Python
  • 外部库:
    • yara-python: 用于特征匹配
    • pycryptodomex: 处理加密密钥
    • pefile: 解析PE文件结构
  • 标准库:
    • struct: 处理二进制数据
    • socket: 处理IP地址转换
    • itertools: 提供循环迭代功能
  • 开发工具: VSCode

样本信息

  • MD5: 4e22717b48f2f75fcfd47531c780b218
  • SHA1: 0b637e95b1f2d14faaa71085b7e26321bfeeb6d
  • SHA256: 7f94107c9becbcc6ca42070fca7e1e63f29cdd85cbbd8953bbca32a1b4f91219

0x02 ECC密钥提取技术

特征定位技术

  1. Yara规则编写:

    • 分析IDA Pro和x64dbg中的解密代码特征区
    • 将地址部分模糊查询,指令码部分精确匹配
    • 示例特征规则:
      rule Emotet {
          meta:
              description = "Emotet ECC Extra"
          strings:
              $ref_ecc = {FF B4 [3] 00 00 FF B4 [3] 00 00 8B 94 [3] 00 00 E8 [4] 83 C4 0C 89 84 [3] 00 00 8D 84 [3] 00 00 B9 [4] 50 FF B4 [3]00 00 FF B4 [3]00 00 8B 94 [3]00 00 E8}
          condition:
              $ref_ecc            
      }
      
  2. Yara扫描实现:

    import yara
    
    def yara_scan(raw_data):
        addresses = {}
        yara_rules = yara.compile(source=rule_source)
        matches = yara_rules.match(data=raw_data)
        for match in matches:
            for item in match.strings:
                addresses[item[1]] = hex(item[0])  # 转为16进制
        return addresses
    

数据定位技术

  1. PE文件结构解析:

    • 使用pefile库获取ImageBase
    • 计算RVA(相对虚拟地址)和FOA(文件偏移地址)转换
  2. 关键数据偏移计算:

    def positioning_data(filebuf):
        pe = pefile.PE(data=filebuf, fast_load=False)
        image_base = pe.OPTIONAL_HEADER.ImageBase
        yara_matches = yara_scan(filebuf)
    
        if yara_matches.get("$ref_ecc"):
            ref_ecc_offset = int(yara_matches["$ref_ecc"],16)
            delta1 = -5
            delta2 = 44
            ref_eck_rva = struct.unpack("I", filebuf[ref_ecc_offset + delta1 : ref_ecc_offset + delta1 + 4])[0] - image_base
            ref_ecs_rva = struct.unpack("I", filebuf[ref_ecc_offset + delta2 : ref_ecc_offset + delta2 + 4])[0] - image_base
            eck_offset = pe.get_offset_from_rva(ref_eck_rva)
            ecs_offset = pe.get_offset_from_rva(ref_ecs_rva)
            return eck_offset, ecs_offset
    

ECC密钥解密技术

  1. XOR解密函数:

    from itertools import cycle
    
    def xor_data(data, key):
        return bytes(c ^ k for c, k in zip(data, cycle(key)))
    
  2. ECC密钥提取与格式化:

    from Cryptodome.PublicKey import ECC
    
    def extract_ecc(filebuf):
        conf_dict = {}
        # 获取eck_offset和ecs_offset...
    
        # 提取ECK密钥
        key = filebuf[eck_offset : eck_offset + 4]
        size = struct.unpack("I", filebuf[eck_offset + 4 : eck_offset + 8])[0] ^ struct.unpack("I", key)[0]
        eck_offset += 8
        eck_key = xor_data(filebuf[eck_offset : eck_offset + size], key)
        key_len = struct.unpack("<I", eck_key[4:8])[0]
    
        conf_dict.setdefault(
            "ECC ECK1",
            ECC.construct(
                curve="p256",
                point_x=int.from_bytes(eck_key[8 : 8 + key_len], "big"),
                point_y=int.from_bytes(eck_key[8 + key_len :], "big"),
            ).export_key(format="PEM"),
        )
    
        # 类似方法提取ECS密钥...
        return conf_dict
    

0x03 C2配置提取技术

C2特征定位

  1. Yara规则:

    rule Emotet {
        meta:
            description = "Emotet C2 Extra"
        strings:
            $ref_c2 = {FF 74 [2] FF 74 [2] 8B 54 [2] E8 [4] 8B 54 [2] 83 C4 0C 89 44 [2] 8B F8 03 44 [2] B9 [4] 89 44 [2] E9}
        condition:
            $ref_c2     
    }
    
  2. C2列表定位:

    def positioning_c2_data(filebuf):
        pe = pefile.PE(data=filebuf, fast_load=False)
        image_base = pe.OPTIONAL_HEADER.ImageBase
        yara_matches = yara_scan2(filebuf)
    
        if yara_matches.get("$ref_c2"):
            delta = -5
            c2list_va_offset = int(yara_matches["$ref_c2"],16)
            c2_list_va = struct.unpack("I", filebuf[c2list_va_offset + delta : c2list_va_offset + delta + 4])[0]
            c2_list_rva = c2_list_va - image_base
            c2_list_offset = pe.get_offset_from_rva(c2_list_rva)
            return c2_list_offset
    

C2配置解密

  1. IP和端口处理:
    import socket
    
    def extra_c2_data(filebuf):
        conf_dict = {}
        # 获取c2_list_offset...
    
        key = filebuf[c2_list_offset : c2_list_offset + 4]
        presize = filebuf[c2_list_offset + 4 : c2_list_offset + 8]
        size = struct.unpack("I", presize)[0] ^ struct.unpack("I", key)[0]
        c2_list_offset += 8
        c2_list = xor_data(filebuf[c2_list_offset:], key)
    
        offset = 0
        while offset < size:
            ip = struct.unpack(">I", c2_list[offset : offset + 4])[0]
            c2_address = socket.inet_ntoa(struct.pack("!L", ip))
            port = str(struct.unpack(">H", c2_list[offset + 4 : offset + 6])[0])
            if not c2_address or not port:
                break
            conf_dict.setdefault("address", []).append(f"{c2_address}:{port}")
            offset += 8
        return conf_dict
    

0x04 完整实现代码

import yara
import pefile
import struct
from Cryptodome.PublicKey import ECC
from itertools import cycle
import socket

rule_source = """
rule Emotet {
    meta:
        description = "Emotet Config Extra"
    strings:
        $ref_c2 = {FF 74 [2] FF 74 [2] 8B 54 [2] E8 [4] 8B 54 [2] 83 C4 0C 89 44 [2] 8B F8 03 44 [2] B9 [4] 89 44 [2] E9}
        $ref_ecc = {FF B4 [3] 00 00 FF B4 [3] 00 00 8B 94 [3] 00 00 E8 [4] 83 C4 0C 89 84 [3] 00 00 8D 84 [3] 00 00 B9 [4] 50 FF B4 [3]00 00 FF B4 [3]00 00 8B 94 [3]00 00 E8}
    condition:
        $ref_c2 or $ref_ecc         
}
"""

def yara_scan(raw_data):
    addresses = {}
    yara_rules = yara.compile(source=rule_source)
    matches = yara_rules.match(data=raw_data)
    for match in matches:
        for item in match.strings:
            addresses[item[1]] = hex(item[0])
    return addresses

def xor_data(data, key):
    return bytes(c ^ k for c, k in zip(data, cycle(key)))

def emotet_extract(filebuf):
    conf_dict = {}
    pe = pefile.PE(data=filebuf, fast_load=False)
    image_base = pe.OPTIONAL_HEADER.ImageBase
    yara_matches = yara_scan(filebuf)

    # C2配置提取
    if yara_matches.get("$ref_c2"):
        delta = -5
        c2list_va_offset = int(yara_matches["$ref_c2"],16)
        c2_list_va = struct.unpack("I", filebuf[c2list_va_offset + delta : c2list_va_offset + delta + 4])[0]
        c2_list_rva = c2_list_va - image_base
        c2_list_offset = pe.get_offset_from_rva(c2_list_rva)
        
        key = filebuf[c2_list_offset : c2_list_offset + 4]
        presize = filebuf[c2_list_offset + 4 : c2_list_offset + 8]
        size = struct.unpack("I", presize)[0] ^ struct.unpack("I", key)[0]
        c2_list_offset += 8
        c2_list = xor_data(filebuf[c2_list_offset:], key)
        
        offset = 0
        while offset < size:
            ip = struct.unpack(">I", c2_list[offset : offset + 4])[0]
            c2_address = socket.inet_ntoa(struct.pack("!L", ip))
            port = str(struct.unpack(">H", c2_list[offset + 4 : offset + 6])[0])
            if not c2_address or not port:
                break
            conf_dict.setdefault("address", []).append(f"{c2_address}:{port}")
            offset += 8

    # ECC密钥提取
    if yara_matches.get("$ref_ecc"):
        ref_ecc_offset = int(yara_matches["$ref_ecc"],16)
        delta1 = -5
        delta2 = 44
        ref_eck_rva = struct.unpack("I", filebuf[ref_ecc_offset + delta1 : ref_ecc_offset + delta1 + 4])[0] - image_base
        ref_ecs_rva = struct.unpack("I", filebuf[ref_ecc_offset + delta2 : ref_ecc_offset + delta2 + 4])[0] - image_base
        eck_offset = pe.get_offset_from_rva(ref_eck_rva)
        ecs_offset = pe.get_offset_from_rva(ref_ecs_rva)

        # ECK密钥处理
        key = filebuf[eck_offset : eck_offset + 4]
        size = struct.unpack("I", filebuf[eck_offset + 4 : eck_offset + 8])[0] ^ struct.unpack("I", key)[0]
        eck_offset += 8
        eck_key = xor_data(filebuf[eck_offset : eck_offset + size], key)
        key_len = struct.unpack("<I", eck_key[4:8])[0]
        conf_dict.setdefault(
            "ECC ECK1",
            ECC.construct(
                curve="p256",
                point_x=int.from_bytes(eck_key[8 : 8 + key_len], "big"),
                point_y=int.from_bytes(eck_key[8 + key_len :], "big"),
            ).export_key(format="PEM"),
        )

        # ECS密钥处理
        key = filebuf[ecs_offset : ecs_offset + 4]
        size = struct.unpack("I", filebuf[ecs_offset + 4 : ecs_offset + 8])[0] ^ struct.unpack("I", key)[0]
        ecs_offset += 8
        ecs_key = xor_data(filebuf[ecs_offset : ecs_offset + size], key)
        key_len = struct.unpack("<I", ecs_key[4:8])[0]
        conf_dict.setdefault(
            "ECC ECS1",
            ECC.construct(
                curve="p256",
                point_x=int.from_bytes(ecs_key[8 : 8 + key_len], "big"),
                point_y=int.from_bytes(ecs_key[8 + key_len :], "big"),
            ).export_key(format="PEM"),
        )
    return conf_dict

if __name__ == "__main__":
    import sys
    with open(sys.argv[1], "rb") as f:
        file_data = f.read()
    print(emotet_extract(file_data))

0x05 技术总结

  1. 关键学习点:

    • Yara规则编写需要结合静态分析和动态调试结果
    • PE文件结构理解是二进制分析的基础
    • 官方文档是解决库使用问题的最佳资源
  2. 技术要点:

    • 特征码定位技术
    • RVA与FOA转换技术
    • XOR循环解密技术
    • ECC密钥格式化技术
    • IP地址处理技术
  3. 应用场景:

    • 恶意软件自动化分析
    • 威胁情报提取
    • 安全检测规则生成

0x06 参考资源

  1. CAPEv2沙箱
  2. 奇安信攻防社区-APT恶意DLL分析
  3. Python socket文档
  4. pefile文档
  5. Yara文档
  6. itertools文档

通过本教程,您已经掌握了从Emotet恶意软件样本中自动化提取配置信息的关键技术,这些技术同样适用于其他恶意软件家族的分析工作。

恶意样本自动化配置提取技术详解 0x00 前言 本文详细讲解如何通过自动化方式从Emotet恶意软件样本中提取ECC加密密钥和C2服务器配置信息。通过分析CAPEv2沙箱的提取代码,结合PE文件结构分析技术,使用Python实现自动化配置提取。 0x01 环境准备 开发环境 编程语言 : Python 外部库 : yara-python : 用于特征匹配 pycryptodomex : 处理加密密钥 pefile : 解析PE文件结构 标准库 : struct : 处理二进制数据 socket : 处理IP地址转换 itertools : 提供循环迭代功能 开发工具 : VSCode 样本信息 MD5 : 4e22717b48f2f75fcfd47531c780b218 SHA1 : 0b637e95b1f2d14faaa71085b7e26321bfeeb6d SHA256 : 7f94107c9becbcc6ca42070fca7e1e63f29cdd85cbbd8953bbca32a1b4f91219 0x02 ECC密钥提取技术 特征定位技术 Yara规则编写 : 分析IDA Pro和x64dbg中的解密代码特征区 将地址部分模糊查询,指令码部分精确匹配 示例特征规则: Yara扫描实现 : 数据定位技术 PE文件结构解析 : 使用pefile库获取ImageBase 计算RVA(相对虚拟地址)和FOA(文件偏移地址)转换 关键数据偏移计算 : ECC密钥解密技术 XOR解密函数 : ECC密钥提取与格式化 : 0x03 C2配置提取技术 C2特征定位 Yara规则 : C2列表定位 : C2配置解密 IP和端口处理 : 0x04 完整实现代码 0x05 技术总结 关键学习点 : Yara规则编写需要结合静态分析和动态调试结果 PE文件结构理解是二进制分析的基础 官方文档是解决库使用问题的最佳资源 技术要点 : 特征码定位技术 RVA与FOA转换技术 XOR循环解密技术 ECC密钥格式化技术 IP地址处理技术 应用场景 : 恶意软件自动化分析 威胁情报提取 安全检测规则生成 0x06 参考资源 CAPEv2沙箱 奇安信攻防社区-APT恶意DLL分析 Python socket文档 pefile文档 Yara文档 itertools文档 通过本教程,您已经掌握了从Emotet恶意软件样本中自动化提取配置信息的关键技术,这些技术同样适用于其他恶意软件家族的分析工作。