声明
本文为结合网上资料+自己理解+LLM辅助学习的笔记,在解释部分为AI生成内容,复现部分截图为本人复现过程。
一句话概括
基于Python打包的exe恶意软件通常是通过将恶意代码隐藏在看似正常的Python脚本中,再利用工具如PyInstaller打包成独立可执行文件,以此绕过安全检测并实现恶意功能。
学习网站
脚本类恶意程序分析技巧汇总_恶意启动脚本有哪些-CSDN博客
驱动人生挖矿木马分析与处置_驱动人生恶意程序分析-CSDN博客
PyInstaller
工具安装:pip install pyinstaller
工具使用:pyinstaller your_program.py
以下是我们简单的一个Malware的示例python文件。
# Import required modules for OS operations and subprocess management
import os
import subprocess
def execute_command(command):
"""Execute command and return (stdout, stderr, returncode)"""
# Create a new process to execute the command via cmd.exe
process = subprocess.Popen(
['cmd.exe', '/c', command], # Command execution through cmd interpreter
shell=True, # Enable shell execution environment
stdout=subprocess.PIPE, # Capture standard output
stderr=subprocess.PIPE, # Capture error output
universal_newlines=True # Use universal newline decoding
)
try:
# Wait for process completion with 5-second timeout
stdout, stderr = process.communicate(timeout=5)
return stdout, stderr, process.returncode
except subprocess.TimeoutExpired:
# Terminate process if timeout occurs
process.kill()
return "", "Command timeout", -1
# Create minimal PE file stub in C:\ (requires admin privileges)
create_file_cmd = 'echo "MZ" > C:\\svch0st.exe'
# Execute file creation command and capture results
stdout, stderr, code = execute_command(create_file_cmd)
# Verify file creation success/failure
if code == 0:
print("File creation succeeded")
else:
print(f"File creation failed: {stderr}")
# Command to launch Windows calculator
launch_calc_cmd = 'calc.exe'
# Execute calculator launch command
stdout, stderr, code = execute_command(launch_calc_cmd)
# Verify calculator launch success/failure
if code == 0:
print("Calculator launched successfully")
else:
print(f"Calculator launch failed: {stderr}")
# Test command execution using subprocess.run()
test_command = 'echo Hello Python!'
result = subprocess.run(
['cmd.exe', '/c', test_command], # Command list for execution
shell=True, # Maintain shell context
capture_output=True, # Capture both stdout/stderr
text=True # Return output as string instead of bytes
)
# Check test command execution status
if result.returncode == 0:
print("Test command succeeded")
else:
print(f"Test command failed: {result.stderr}")
# Optional cleanup command (disabled by default)
# os.remove("C:\\svch0st.exe")
利用Administrator权限运行这个命令,因为我们会在C目录下创建一个文件,写入一些我们想写入的东西。
然后我们尝试打包一下。
pyinstaller --onefile --nowindowed my_malicious_py.py
用pyinstaller打包的exe文件,默认图标是带有“Python”字样的图案,辨识度很高。虽然方便开发者快速生成程序,但可能显得不够专业。为避免这一问题,可通过--icon
参数指定自定义图标,替换默认图标。这不仅能提升视觉效果和品牌辨识度,还能让程序更显专业。不过,是否更换图标需根据实际需求决定。
字符串特征
我们将二进制程序拖到IDA里面去看一下。
看到__main__
和__file__
以及一堆以py
开头的函数,这些字符串可能会让人感到困惑。明明只是很简单的几十行代码,为什么会有这么多看似多余的字符串呢?这是因为Python是一种解释型语言,它的运行依赖于解释器来逐行解析和执行代码。而当你使用工具如PyInstaller将Python脚本打包成可执行文件(即.exe
文件)时,实际上这个过程不仅仅是简单地将你的代码转换为二进制文件,而是将整个Python解释器环境与你的代码一起打包到最终的可执行文件中。这样一来,生成的文件会包含许多与Python解释器相关的特征字符串,比如__main__
、__file__
以及其他以py
开头的内部函数名称。
这些字符串的存在是不可避免的,因为它们是Python运行机制的一部分。例如,__main__
通常用于标识程序的入口点,而__file__
则指向当前脚本的文件路径。在打包过程中,PyInstaller需要保留这些信息以便正确加载和运行你的代码。此外,由于Python本身的动态特性,许多内部函数和模块的名字也会被嵌入到最终的可执行文件中。这种现象不仅限于PyInstaller,其他类似的打包工具也会有类似的行为。因此,即使你的原始代码非常简洁,最终生成的可执行文件仍然会显得“臃肿”,并且包含大量与Python解释器相关的字符串特征。这也是为什么通过反编译工具分析Python打包后的程序时,很容易发现这些明显的语言特征。
静态分析
我们尝试用DIE分析一下。
通过 Detect It Easy 的扫描结果,可以关注与 PyInstaller 相关的关键信息并结合上下文分析样本。打包工具识别中,Packer 字段显示为 PyInstaller(modified)
,直接证明文件由 PyInstaller 打包且可能经过修改,攻击者常滥用 PyInstaller 将 Python 脚本伪装成正常程序以隐藏真实意图。
内部语言特征方面,Language 字段显示为 Python
,表明文件包含 Python 解释器和依赖库,其行为由嵌入的 Python 脚本驱动,分析师可通过提取嵌入的 .pyc
字节码或逆向 Python 字符串追踪恶意逻辑。
编译环境与工具链显示 Compiler 为 Microsoft Visual C/C++ (19.36.34436),说明 PyInstaller 使用 Visual Studio 2022(v17.6)编译底层 C/C++ 代码,可能涉及 Windows API 调用或系统级操作。
压缩与数据覆盖层部分,Overlay 显示使用 ZLIB 压缩算法,PyInstaller 默认将 Python 字节码和资源压缩到 PE 文件末尾的覆盖层,攻击者可能利用这一特性隐藏恶意负载。
异常标志与风险提示中,Strange overlay
和 Compressed or packed data
警告提示文件结构异常,需进一步检查覆盖层内容,结合样本中创建 svch0st.exe
的行为,可能隐藏持久化机制或隐藏模块。
通过这些信息,可快速分类排查 Python 源于的恶意活动,聚焦覆盖层和 Python 字符串提取原始脚本逻辑,并监控 PE 文件中的 Python 特征以拦截异常文件创建行为。
那么接下来我们可以尝试开始反编译。
反编译python打包的exe-pyc
接下来我们将这个my_malicious_py的exe反编译成python源码,这需要两个步骤,首先由exe获取pyc文件,再由pyc获取py文件。
在这个过程中,我们需要借助一些工具和脚本来完成操作,同时需要注意的是,反编译的过程可能会因原始代码的复杂性或加密手段而有所不同。
例如,如果开发者在生成exe文件时使用了混淆技术或者对pyc文件进行了额外的保护措施,那么反编译的难度会显著增加。
因此,在实际操作中,我们可能需要根据具体情况调整方法。
反编译python生成的exe需要用到pyinstaller库里的archive_viewer.py脚本。
archive_viewer.py是一个非常实用的工具,它可以帮助我们从打包好的exe文件中提取出嵌入的pyc文件。
这些pyc文件实际上是python字节码文件,包含了程序运行所需的指令信息。
通过archive_viewer.py,我们可以逐步分析exe文件的结构,并定位到目标pyc文件的位置。
archive_viewer.py一共有四个可用命令:
U: go up one level
O <name>: open embedded archive with given name
X <name>: extract file with given name
S: list the contents of current archive again
Q: quit
首先请参考我的路径C:\Users\__\AppData\Roaming\Python\Python310\site-packages\PyInstaller\utils\cliutils
找到archive_viewer.py
文件。
然后运行
python archive_viewer.py C:\Users\__\Desktop\Malware\PyInstaller\dist\my_malicious_py.exe
除了其余的几个系统模块之外,重点关注中间的主程序,使用下面一条命令提取主程序的pyc文件。
? X my_malicious_py
Output filename? my_malicious_py.pyc
由于使用 PyInstaller 打包 Python 程序后,生成的 pyc 文件的前 8 个字节会被自动抹掉,因此在某些情况下需要手动将这些字节添加回去,以确保文件能够被正确识别和运行。这 8 个字节的作用非常重要,其中前 4 个字节表示 Python 编译器的版本信息,而后 4 个字节则是时间戳。时间戳可以是任意值,通常不会影响程序的功能,但编译器版本信息必须与实际使用的 Python 版本严格匹配,否则可能会导致解释器无法正确解析 pyc 文件。
例如,在 Python 2.7 中,编译器版本的前 4 个字节固定为 03 f3 0d 0a
,而在 Python 3.4 中,对应的字节则为 ee 0c 0d 0a
。这些字节实际上是 Python 解释器用来验证 pyc 文件是否与其版本兼容的关键标识。如果这些字节缺失或错误,Python 解释器可能会拒绝加载该文件,甚至抛出异常。因此,在修复 pyc 文件时,必须根据所使用的 Python 版本准确地补充这些字节。
对于如果不知道的。我们可以自己尝试一下之间先编译一个.py
文件然后再看前8位就行了。(注意32为是8位,64位是16位,不过只需要关注前面4为magic就行了)
在这里我们就知道是6F 0D 0D 0A
,时间戳全部写0。
然后用010Editor在前面添加一下。(其实这是64位的,这个图后面E3之前还需要添加8个0)。
使用Pycdc对pyc解混淆-py
最后使用GitHub - zrax/pycdc: C++ python bytecode disassembler and decompiler这个软件从pyc文件得到py文件。这个工具是一款功能强大的开源工具,专门用于将Python编译后的字节码(pyc文件)反汇编和反编译为原始的Python源代码(py文件)。它支持多种Python版本,并且能够处理复杂的字节码结构,因此在逆向工程、代码恢复以及学习Python内部机制方面非常有用。例如,如果你有一个丢失了源代码的项目,但保留了编译后的pyc文件,那么通过这个工具就可以尝试还原出接近原始的代码逻辑。此外,它还支持对损坏或不完整的pyc文件进行一定程度的解析,尽管结果可能不完全准确,但在许多情况下仍然能提供有价值的参考。
这个工具是基于C++开发的,性能高效且稳定,适合需要频繁处理字节码的开发者使用。它的命令行界面简单易用,只需输入目标pyc文件路径即可快速生成对应的py文件。同时,社区中也有不少用户分享了如何扩展其功能,比如通过修改源码来适配特定需求,或者结合其他工具实现更复杂的工作流。例如,有人会将pycdc与静态分析工具配合使用,以进一步优化反编译后的代码质量。
我们直接拼接这个成功的文件:在实际操作过程中,如果发现反编译后的代码存在部分缺失或错误,可以手动调整并补充完整。例如,当某些变量名被替换为默认值时,可以根据上下文重新命名这些变量,使其更具可读性。另外,在处理多个pyc文件时,可以先分别反编译每个文件,然后根据模块之间的依赖关系进行整合,最终形成一个完整的项目结构。这种拼接方式不仅提高了效率,还能确保各个模块间的兼容性和一致性。对于大型项目来说,这种方法尤为重要,因为它可以帮助开发者更快地恢复整个系统的运行状态,而无需从零开始重写代码。
pycdc.exe my_malicious_py.pyc > origin.py
这是对于没有混淆的代码的演示效果。
混淆源代码
如果有些样本他们会进行混淆,例如利用Pyarmor 9.0 Documentation — Pyarmor 9.0.8 documentation工具来混淆他们源代码。
Before Version 8.0+
pip install pyarmor==7.7.4
pyarmor obfuscate my_malicious_py.py
PS C:\Users\__\Desktop\Malware\PyInstaller\Demo> tree /F ./dist
Folder PATH listing
Volume serial number is 000000D9 2AE4:6391
C:\USERS\__\DESKTOP\MALWARE\PYINSTALLER\DEMO\DIST
│ my_malicious_py.py
│
└───pytransform
_pytransform.dll
__init__.py
这里所提到的问题主要针对PyArmor小于8.0版本的最后一个稳定版本,也就是7.7.4版本。
针对PyArmor Version 7.x的解混淆
GitHub - Svenskithesource/PyArmor-Unpacker: A deobfuscator for PyArmor.
PyArmor-Unpacker 的解密过程通常需要结合动态调用和注入等方法来完成对 PyArmor 加密代码的反混淆。由于 PyArmor 是一种广泛使用的 Python 代码保护工具,其加密机制涉及字节码混淆、运行时解密以及环境限制等多种技术手段,因此针对它的反混淆操作也需要采用多层次的技术策略。具体来说:
-
注入法通过动态注入 Python 代码,调用 PyArmor 的解密函数(如 pyarmor_runtime
)并修改字节码,提取解密后的代码。其核心是利用 PyArmor 运行时解密的特性,拦截还原的字节码并导出为明文。这需要深入理解 PyArmor 的解密逻辑、模块加载机制及与 Python 解释器的交互方式,同时可能借助调试工具(如 ctypes
或 inspect
模块)动态修改内存中的代码对象。
-
修改限制模式法则通过绕过 PyArmor 的保护机制(如禁用保护模式或动态调整运行环境),使解密函数可被正常访问。PyArmor 通常检测环境篡改、限制设备运行或阻止调试器附加。攻击者可能修改其核心模块或修补字节码,跳过检查,例如分析源码找到验证函数并替换为始终成功的逻辑,或调整 Python 运行时配置以欺骗检测机制。
-
审计日志法虽偏向静态分析,但需利用 Python 的审计日志功能,在运行时捕获解密生成的代码对象以提取明文代码。Python 3.8 起引入该功能,可监控模块加载、函数调用等关键事件。通过分析审计日志,攻击者可定位 PyArmor 解密时刻并捕获解密后的代码对象,尤其是与 exec
或 eval
相关的事件。捕获后,可用 dis
模块反编译为可读形式。此方法无需修改 PyArmor 核心逻辑,适合不熟悉动态注入或限制模式的场景。
-
动态调用和运行时操作在破解 PyArmor 加密保护中起关键作用。
-
核心思想是利用 PyArmor 运行时解密代码的特性,通过注入、拦截或记录解密过程获取原始代码。
-
动态注入通过修改运行时环境直接访问解密后的字节码。
-
审计日志法通过监控解释器行为间接捕获解密结果。
-
结合动态与静态技术使 PyArmor-Unpacker 能有效破解加密保护。
-
方法的成功依赖于对 PyArmor 内部机制和 Python 运行时特性的深入理解。
-
随着 PyArmor 更新加密算法,破解工具需持续改进以应对新挑战。
Version 8.0+-9.0+
pip install pyarmor==9.1.3
pyarmor gen .\my_malicious_py.py
PS C:\Users\__\Desktop\Malware\PyInstaller\Demo> tree /F .\dist\
Folder PATH listing
Volume serial number is 0000003E 2AE4:6391
C:\USERS\__\DESKTOP\MALWARE\PYINSTALLER\DEMO\DIST
│ my_malicious_py.py
│
└───pyarmor_runtime_000000
pyarmor_runtime.pyd
__init__.py
PyArmor 8.0及以上版本的加密很难破解,但有人找到了方法。例如,一篇文章(Unpacking Pyarmor v8+ scripts | cyber.wtf)详细讲解了如何解密用PyArmor加密的Python脚本。
文章中提到,PyArmor是一种保护代码的工具,它使用强大的加密技术来保证脚本安全。比如,它用了一个叫libtomcrypt的加密库和一种叫AES-GCM的加密模式。libtomcrypt是一个轻量级的加密工具,支持多种加密方式。AES-GCM是一种高级加密模式,不仅能加密数据,还能检查数据是否被篡改。加密时,PyArmor会生成一个随机密钥和初始化向量(IV),并将它们嵌入到加密后的脚本中。解密时,需要提取这些信息,并通过AES-GCM模式还原原始脚本。
解密后,作者发现脚本中使用了很多混淆技术来隐藏恶意功能。攻击者常用的方法包括更改变量名、打乱代码结构以及插入无意义的代码片段等。这些方法让分析变得非常困难。此外,脚本还包含一些恶意功能,比如从远程服务器下载其他恶意软件、窃取用户的敏感信息(如密码、浏览器记录等),甚至可能获取更高的系统权限。这些行为不仅威胁用户隐私,还会引发更严重的网络安全问题。
文章最后总结了恶意软件的工作流程,从感染设备到执行恶意行为,强调了PyArmor在其中的作用。由于PyArmor的加密功能强大,攻击者可以用它把恶意脚本伪装成普通程序,从而绕过传统安全检测。
为了应对这种威胁,作者开发了一款专门的解密工具(GitHub - GDATAAdvancedAnalytics/Pyarmor-Tooling)。这款工具可以自动提取加密脚本中的密钥和IV,并完成解密。它不仅能帮助研究人员快速分析恶意软件,还能为制定防御策略提供重要参考。
(下图为github仓库原图)
GDATAAdvancedAnalytics/Pyarmor-Tooling 仓库,该仓库提供用于静态解包 Pyarmor v8+ 的脚本,并详细说明了解包步骤,包括准备环境、获取密钥、解密和反汇编等过程。亮点包括使用 IDA Pro 和 ida_getkey.py
脚本提取 Pyarmor 的密钥,利用 decrypt_gcm.py
脚本解密 Python 代码,以及通过 Docker 容器在隔离环境中运行脚本,同时页面还提供了详细的步骤说明和使用指南以便操作。
RealWorld Malware Analysis
自2018年12月“驱动人生”挖矿木马爆发以来,其持续活跃并不断更新,已发布超20个版本,成为国内挖矿木马领域的“第一”。该木马最初利用“驱动人生”升级通道传播,借助合法软件分发机制提升隐蔽性。早期为单一可执行文件,后进化为基于Python的exe版本,甚至采用PowerShell实现无文件攻击,降低被检测风险。
该木马从简单入侵发展为复合攻击策略,包括利用“永恒之蓝”漏洞、横向渗透和暴力破解等手段,尤其在未修补漏洞的企业环境中破坏力显著。其功能模块逐渐丰富,集成Mimikatz窃取登录凭据,并嵌入SQL弱口令扫描工具,成功后连接云端服务器下载载荷,运行门罗币挖矿程序。
这款木马不仅威胁个人用户,还危及企业网络,具备跨主机传播能力,尤其对安全防护薄弱的小型企业威胁更大。用户需采取多层次防御措施,包括更新补丁、加强密码管理、部署终端安全方案及提升员工安全意识,以减少损失。
MSDOS
Operation system: MS-DOS[8086, 16-bit, EXE]
PE32
Operation system: Windows(XP)[I386, 32-bit, Console]
Linker: Microsoft Linker(14.00.24210)
Compiler: Microsoft Visual C/C++(19.00.24210)[C]
Language: Python
Tool: Visual Studio(2015)
Packer: PyInstaller
Overlay: Binary[Offset=0x0003ba00,Size=0x00669628]
Archive: Zlib stream (.zlib)[@19h,lv:fastest]
Data: ZLIB data[ZLIB compression best]
该木马样本的行为特征可按攻击流程分为多个阶段。初始执行阶段,样本通过 PyInstaller 打包的 exe 文件启动,并利用 pyinstxtractor.py 反编译为加密的 pyd 源码以规避静态检测。
样本的具体行为如下:
- 样本释放 Mimikatz 等黑客工具以隐藏恶意行为,并清理挖矿相关进程和竞品文件。
- 预置 IP 段、用户名、密码字典及 MSSQL 爆破字典,为后续攻击做准备。
- 通过伪装合法进程(如 svchost.exe)、创建计划任务和恶意服务实现持久化控制。
- 设置防火墙规则,开放 65533 端口并代理转发流量至 1.1.1.1:53,隐蔽通信行为。
- 利用永恒之蓝漏洞攻击 445 端口,扫描 1433 端口进行 MSSQL 爆破,使用 psexec 远程执行代码。
- 添加管理员用户 k8h3d 并开放 1433 端口入站规则,扩大控制范围。
- 使用 Mimikatz 抓取系统密码,收集 HASH 值、域账户信息和网络配置数据存储在 mkatz.ini 文件中。
- 执行侦察命令(如 ipconfig、netstat、wmic 等)全面掌握目标系统状态。
- 创建 m2.ps1 PowerShell 脚本自动化扫描存活主机、收集域信息并更新密码字典。
- 通过 postgres.exe 遍历数据库目录寻找攻击目标。
- 伪装合法进程名、端口转发至知名 DNS 端口(53)、在 Temp 目录释放文件以规避检测。
- 利用注册表操作隐藏恶意行为,通过隐蔽通道将扫描结果写入日志文件并定期访问 C2 服务器。
- 支持多系统版本适配,通过网络偏移技术判断操作系统版本并选择合适攻击载荷。
但是呢,我们今天主要是分析PyInstaller的反混淆研究。
Options in 'MtwBVJRF.exe' (PKG/CArchive):
pyi-windows-manifest-filename ii.exe.manifest
Contents of 'MtwBVJRF.exe' (PKG/CArchive):
position, length, uncompressed_length, is_compressed, typecode, name
0, 168, 234, 1, 'm', 'struct'
168, 1165, 2767, 1, 'm', 'pyimod01_os_path'
1333, 4425, 12791, 1, 'm', 'pyimod02_archive'
5758, 7555, 23658, 1, 'm', 'pyimod03_importers'
13313, 1888, 5919, 1, 's', 'pyiboot01_bootstrap'
15201, 1138, 2446, 1, 's', 'pyi_rth_multiprocessing'
16339, 1466456, 1936660, 1, 's', 'ii'
1482795, 16873, 29184, 1, 'b', 'Crypto.Cipher._AES.pyd'
1499668, 4152, 8704, 1, 'b', 'Crypto.Cipher._ARC4.pyd'
1503820, 17801, 54272, 1, 'b', 'Crypto.Cipher._DES.pyd'
1521621, 17840, 54784, 1, 'b', 'Crypto.Cipher._DES3.pyd'
1539461, 5493, 10752, 1, 'b', 'Crypto.Hash._MD4.pyd'
1544954, 5454, 10240, 1, 'b', 'Crypto.Hash._SHA256.pyd'
1550408, 4713, 9728, 1, 'b', 'Crypto.Random.OSRNG.winrandom.pyd'
1555121, 4571, 10240, 1, 'b', 'Crypto.Util._counter.pyd'
1559692, 3857, 7680, 1, 'b', 'Crypto.Util.strxor.pyd'
1563549, 544, 1050, 1, 'b', 'Microsoft.VC90.CRT.manifest'
1564093, 35537, 73216, 1, 'b', '_ctypes.pyd'
1599630, 137063, 287232, 1, 'b', '_hashlib.pyd'
1736693, 217522, 497152, 1, 'b', '_mssql.pyd'
1954215, 11116, 23552, 1, 'b', '_multiprocessing.pyd'
1965331, 19324, 40960, 1, 'b', '_socket.pyd'
1984655, 354339, 721408, 1, 'b', '_ssl.pyd'
2338994, 36825, 71680, 1, 'b', 'bz2.pyd'
2375819, 463, 1006, 1, 'b', 'ii.exe.manifest'
2376282, 67070, 225280, 1, 'b', 'msvcm90.dll'
2443352, 157574, 569680, 1, 'b', 'msvcp90.dll'
2600926, 317309, 653136, 1, 'b', 'msvcr90.dll'
2918235, 73747, 153088, 1, 'b', 'pyexpat.pyd'
2991982, 1203463, 2640384, 1, 'b', 'python27.dll'
4195445, 42781, 110080, 1, 'b', 'pywintypes27.dll'
4238226, 5956, 11776, 1, 'b', 'select.pyd'
4244182, 258305, 688128, 1, 'b', 'unicodedata.pyd'
4502487, 39806, 99328, 1, 'b', 'win32api.pyd'
4542293, 7063, 17408, 1, 'b', 'win32event.pyd'
4549356, 10236, 23040, 1, 'b', 'win32pipe.pyd'
4559592, 11076, 24064, 1, 'b', 'win32wnet.pyd'
4570668, 6492, 21451, 1, 'x', 'Include\\pyconfig.h'
4577160, 2140106, 2140106, 0, 'z', 'PYZ-00.pyz'
?
首先我们直接将这个archive_viewer.py
工具移动到跟样本一个目录下面。
然后查看里面的exe里面的层次结构。
对于每个基本层次,我们结合typecode和name来分析可能的恶意代码所在之处。
typecode
在 PyInstaller 的 CArchive 结构中用于标识不同类型的打包内容。
m
(Module) 表示 Python 模块的字节码文件(如 .pyc
),是动态导入的依赖模块。
s
(Script/Executable) 表示可执行脚本或引导程序,如启动器和运行时钩子。
b
(Binary) 表示二进制文件,包括动态链接库(.pyd
, .dll
)和资源文件。
x
(Extra File) 表示非代码的附加文件,如头文件或配置文件,不参与代码执行。
z
(PYZ Archive) 表示压缩的 Python 字节码归档(PYZ
),运行时解压到内存中执行。
- 示例分析展示了不同类型的实际应用,如
pyimod02_archive
(m
类型)、_ssl.pyd
(b
类型)和 PYZ-00.pyz
(z
类型)。
- 类型码帮助 PyInstaller 按需加载内容,优化启动速度和内存占用。
“ii”太可疑了,因为其他的模块大部分都是加密和网络相关的,还有多进程和数据相关的内容,因此综合分析,我们"s"类型的“ii”很可疑。
我们尝试提取出来
? X ii
Output filename? ii.pyc
但是Magic是多少呢?我们注意到
2991982, 1203463, 2640384, 1, 'b', 'python27.dll'
所以我们猜测是python2.7的,回顾前文,magic是03 f3 0d 0a
。
或者用最新的GitHub - extremecoders-re/pyinstxtractor: PyInstaller Extractor也可以直接得到ii.pyc,而且自动补全了magic。
然后就可以利用pycdc解混淆了,或者用uncompyle6都可以(因为这是2.7的)。
pycdc.exe ii.pyc > ii.py
import bz2
import base64
print(bz2.decompress(base64.b64decode('Ql...==')).decode('utf-8'))
解压出来之后会有很多乱七八糟的东西,把一些没用的import去掉,然后直接按照这个写法解压。
python ii.py > ii_decode.py
- 混淆过的代码可读性极差,阅读和理解起来令人头疼。
- 函数开头部分揭示了核心操作:仅为关键字起别名。
- 别名增加了代码复杂性,但未改变逻辑结构。
- 可通过文本编辑器的查找替换功能将别名还原为原始关键字。
- 替换时需勾选“匹配大小写”以避免误替换或不完全替换。
- 此方法能有效去除混淆部分,但无法还原自定义函数名。
- 清理后代码条理分明,逻辑清晰,降低了阅读和分析难度。