第84章 恶意软件分析(四):勒索软件分析、加密逻辑还原、解密工具开发

勒索软件,说白了就是“绑架你的数据,拿钱来赎”。我这些年跟勒索软件打过不少交道,从早期的简单文件重命名,到现在的军工级加密算法,花样越来越多。但万变不离其宗——只要你能还原它的加密逻辑,就能写出解密工具。

这一章,我们就拿一个真实的勒索样本开刀。我会带你一步步拆解它的加密流程,然后手写一个解密器。嗯,过程可能会有点烧脑,但相信我,搞通之后你会对加密、文件I/O、PE结构有全新的理解。

样本初探:它到底干了什么?

先说说这个样本的行为。我拿到的是一个Windows PE文件,运行后会做以下几件事:

  • 遍历所有本地磁盘和可移动驱动器
  • 筛选特定后缀名的文件(.docx、.xlsx、.pdf、.jpg等)
  • 对每个文件进行AES-256加密
  • 用RSA-2048加密AES密钥
  • 在原文件同目录下生成.encrypted文件
  • 删除原始文件
  • 弹出勒索信(通常是README.txt或HELP_DECRYPT.html)

你看,套路其实很清晰。关键就在于它怎么生成AES密钥、怎么用RSA保护它。只要我们能拿到RSA私钥或者找到密钥生成的漏洞,解密就成功了一半。

核心要点:勒索软件通常采用“混合加密”方案——对称加密(AES)处理大文件,非对称加密(RSA)保护对称密钥。破解的关键往往不在算法本身,而在密钥管理。

静态分析:从入口点开始

我习惯先用IDA Pro打开样本,看看导入表。这个样本导入了CryptAcquireContextCryptGenKeyCryptEncrypt等Windows CryptoAPI函数。嗯,它用的是系统自带的加密库,不是自己实现的。

再往下翻,我注意到一个可疑的字符串:"C:\\ProgramData\\Microsoft\\Crypto\\RSA\\MachineKeys\\"。这是Windows存储RSA密钥对的地方。样本很可能在这里生成了一个临时密钥对,然后用公钥加密AES密钥,私钥……它可能直接丢弃了,也可能藏在某个注册表项里。

为什么会这样?因为勒索软件作者自己也需要解密能力——他们得给交了赎金的受害者恢复数据。所以私钥要么上传到C2服务器,要么用硬编码的密钥加密后藏在样本里。

个人经验:我在分析一个变种时,发现它把RSA私钥用XOR加密后嵌在资源节里。XOR密钥就是勒索信里的一个固定字符串。这种“掩耳盗铃”式的保护,其实很容易破解。

加密逻辑还原:一步步拆解

动态调试一下。我在CryptGenKey下断点,观察它生成的AES密钥。发现每次调用都生成一个32字节的随机数——这就是AES-256的密钥。然后它调用CryptExportKey把这个密钥导出为SIMPLBLOB格式,再用RSA公钥加密。

加密流程大致如下:

  1. 生成随机AES密钥(32字节)
  2. 生成随机IV(16字节)
  3. 用AES-256-CBC模式加密文件内容
  4. 用RSA-2048公钥加密AES密钥+IV
  5. 将加密后的密钥块(256字节)写入文件头部
  6. 将加密后的文件内容追加在后面

文件结构变成了这样:

偏移 大小 内容
0x00 4字节 魔数(0xDEADBEEF)
0x04 4字节 原始文件大小
0x08 256字节 RSA加密后的AES密钥+IV
0x108 剩余 AES加密后的文件数据

这个结构很关键。我们写解密工具时,就要按照这个格式去解析。

注意:有些勒索软件会在加密前对文件进行分块处理,或者使用不同的IV。一定要通过动态调试确认每个细节,不要想当然。

解密工具开发:从理论到代码

好,现在假设我们已经拿到了RSA私钥(比如通过内存dump或者C2通信截获)。接下来就是写解密程序了。我个人喜欢用Python,因为cryptography库用起来很顺手。

核心逻辑如下:

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding
import struct, os

def decrypt_file(encrypted_path, private_key_path, output_dir):
    # 1. 加载RSA私钥
    with open(private_key_path, 'rb') as f:
        private_key = serialization.load_pem_private_key(
            f.read(),
            password=None
        )
    
    # 2. 读取加密文件
    with open(encrypted_path, 'rb') as f:
        data = f.read()
    
    # 3. 解析头部
    magic = struct.unpack('<I', data[0:4])[0]
    if magic != 0xDEADBEEF:
        print(f"无效的魔数: {encrypted_path}")
        return
    
    orig_size = struct.unpack('<I', data[4:8])[0]
    encrypted_key = data[8:264]  # 256字节的RSA加密块
    encrypted_content = data[264:]
    
    # 4. 解密AES密钥和IV
    aes_key_iv = private_key.decrypt(
        encrypted_key,
        padding.OAEP(
            mgf=padding.MGF1(algorithm=hashes.SHA256()),
            algorithm=hashes.SHA256(),
            label=None
        )
    )
    
    aes_key = aes_key_iv[:32]
    aes_iv = aes_key_iv[32:48]
    
    # 5. AES解密
    cipher = Cipher(algorithms.AES(aes_key), modes.CBC(aes_iv))
    decryptor = cipher.decryptor()
    decrypted_padded = decryptor.update(encrypted_content) + decryptor.finalize()
    
    # 6. 去除PKCS7填充,恢复原始大小
    decrypted = decrypted_padded[:orig_size]
    
    # 7. 写回文件
    output_path = os.path.join(output_dir, os.path.basename(encrypted_path).replace('.encrypted', ''))
    with open(output_path, 'wb') as f:
        f.write(decrypted)
    
    print(f"解密成功: {output_path}")

这段代码看起来不长,但每一步都有坑。我曾经在解析头部时搞错了字节序——样本用的是小端序,我默认用了大端序,结果解密出来的文件全是乱码。排查了整整两个小时才找到问题。

避坑指南:我曾经在解析头部时搞错了字节序——样本用的是小端序,我默认用了大端序,结果解密出来的文件全是乱码。排查了整整两个小时才找到问题。建议你在写解析代码时,先用010 Editor或者HxD看一眼原始二进制,确认字段布局。

自动化批量解密

单个文件解密没问题了,但受害者通常有成千上万个文件。我们需要批量处理。我写了一个简单的遍历脚本:

import os
import glob

def batch_decrypt(root_dir, private_key_path, output_dir):
    pattern = os.path.join(root_dir, '**', '*.encrypted')
    for encrypted_file in glob.glob(pattern, recursive=True):
        try:
            decrypt_file(encrypted_file, private_key_path, output_dir)
        except Exception as e:
            print(f"解密失败: {encrypted_file} - {str(e)}")

这里要注意异常处理。有些文件可能在加密过程中损坏了,或者被其他程序占用了。不要因为一个文件失败就中断整个流程。

知识体系总览

下面这张图概括了勒索软件分析的核心流程,从样本获取到解密工具开发,每一步都环环相扣:

勒索软件分析核心流程 样本获取 静态分析 动态调试 加密逻辑还原 密钥提取 解密工具开发 批量解密 验证与恢复 导入表/字符串分析 PE结构/资源节 加密API调用识别

你看,整个流程就像一条流水线。从样本获取开始,经过静态分析、动态调试,到加密逻辑还原和密钥提取,最后才是解密工具开发。每一步都依赖前一步的输出。我见过很多人一上来就写解密代码,结果连加密算法都没搞清楚,白白浪费时间。

写在最后

勒索软件分析,说白了就是一场“猫鼠游戏”。作者不断升级加密手段,我们不断寻找破解方法。但万变不离其宗——只要它还需要在受害者机器上执行,就一定会留下痕迹。密钥生成、内存中的明文密钥、文件操作模式……这些都是我们可以利用的突破口。

我个人觉得,做这行最重要的不是技术有多牛,而是耐心和细心。一个字节的偏移错误,一个填充模式的误解,都可能导致解密失败。嗯,慢慢来,比较快。


公众号:蓝海资料掘金营,微信deep3321