背景:最近在帮朋友写一款类似抢票的脚本,朋友有好几个脚本了,但是效果不理想,想让我帮忙,由于这种需要对接口进行详细了解,有些事件没有条件去抓包,然后我就对朋友已有的几个脚本动了心思。首先像这种爬虫类小软件八九不离十,都是python写的,然后正好其中一款表现较好的软件打包的时候连应用程序图标都没改,大大的Python Logo,网上参考了文章来对其进行了反编译,得到了源码,然后再在别人的代码基础上进行修改,大大开发降低了难度。
参考文章:谈谈 Pyinstaller 的编译和反编译,如何保护你的代码
首先,简单写一段Python代码。输出0-49的偶数,我知道range可以通过步长的方式实现,只是加多一个判断语句,显得不至于太过简单。
# 这是一段极为简单的测试代码
def foo():
for i in range(50):
if not i%2:
print(i)
if __name__ == '__main__':
foo()
代码运行结果
PS Python反编译测试> python .\main.py
0
2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
42
44
46
48
PS Python反编译测试>
本次为方便使用使用的是conda创建的新环境,没有多余的包。安装pyinstaller
pip install pyinstaller
使用pyinstaller进行打包
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试> pyinstaller.exe -F .\main.py
674 INFO: PyInstaller: 5.2
674 INFO: Python: 3.8.13 (conda)
690 INFO: Platform: Windows-10-10.0.22000-SP0
692 INFO: wrote C:\Users\lkt\Desktop\Python反编译测试\main.spec
695 INFO: UPX is not available.
705 INFO: Extending PYTHONPATH with paths
['C:\\Users\\lkt\\Desktop\\Python反编译测试']
1101 INFO: checking Analysis
1102 INFO: Building Analysis because Analysis-00.toc is non existent
1102 INFO: Initializing module dependency graph...
1104 INFO: Caching module graph hooks...
1136 INFO: Analyzing base_library.zip ...
5874 INFO: Processing pre-find module path hook distutils from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks\\pre_find_module_path\\hook-distutils.py'.
5875 INFO: distutils: retargeting to non-venv dir 'C:\\ide\\anaconda3\\envs\\blogTest\\lib'
10915 INFO: Caching module dependency graph...
11135 INFO: running Analysis Analysis-00.toc
11159 INFO: Adding Microsoft.Windows.Common-Controls to dependent assemblies of final executable
required by C:\ide\anaconda3\envs\blogTest\python.exe
12028 INFO: Analyzing C:\Users\lkt\Desktop\Python反编译测试\main.py
12031 INFO: Processing module hooks...
12033 INFO: Loading module hook 'hook-difflib.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12035 INFO: Loading module hook 'hook-distutils.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12036 INFO: Loading module hook 'hook-distutils.util.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12038 INFO: Loading module hook 'hook-encodings.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12552 INFO: Loading module hook 'hook-heapq.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12555 INFO: Loading module hook 'hook-lib2to3.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12587 INFO: Loading module hook 'hook-multiprocessing.util.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12589 INFO: Loading module hook 'hook-pickle.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12592 INFO: Loading module hook 'hook-platform.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12594 INFO: Loading module hook 'hook-sysconfig.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12597 INFO: Loading module hook 'hook-xml.etree.cElementTree.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12598 INFO: Loading module hook 'hook-xml.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12718 INFO: Loading module hook 'hook-_tkinter.py' from 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks'...
12952 INFO: checking Tree
12952 INFO: Building Tree because Tree-00.toc is non existent
12952 INFO: Building Tree Tree-00.toc
13013 INFO: checking Tree
13013 INFO: Building Tree because Tree-01.toc is non existent
13014 INFO: Building Tree Tree-01.toc
13107 INFO: checking Tree
13108 INFO: Building Tree because Tree-02.toc is non existent
13108 INFO: Building Tree Tree-02.toc
13147 INFO: Looking for ctypes DLLs
13163 INFO: Analyzing run-time hooks ...
13166 INFO: Including run-time hook 'C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_subprocess.py'
13171 INFO: Looking for dynamic libraries
14067 INFO: Looking for eggs
14068 INFO: Using Python library C:\ide\anaconda3\envs\blogTest\python38.dll
14068 INFO: Found binding redirects:
[]
14072 INFO: Warnings written to C:\Users\lkt\Desktop\Python反编译测试\build\main\warn-main.txt
14113 INFO: Graph cross-reference written to C:\Users\lkt\Desktop\Python反编译测试\build\main\xref-main.html
14130 INFO: checking PYZ
14130 INFO: Building PYZ because PYZ-00.toc is non existent
14131 INFO: Building PYZ (ZlibArchive) C:\Users\lkt\Desktop\Python反编译测试\build\main\PYZ-00.pyz
14419 INFO: Building PYZ (ZlibArchive) C:\Users\lkt\Desktop\Python反编译测试\build\main\PYZ-00.pyz completed successfully.
14434 INFO: checking PKG
14434 INFO: Building PKG because PKG-00.toc is non existent
14435 INFO: Building PKG (CArchive) main.pkg
16921 INFO: Building PKG (CArchive) main.pkg completed successfully.
16924 INFO: Bootloader C:\ide\anaconda3\envs\blogTest\lib\site-packages\PyInstaller\bootloader\Windows-64bit\run.exe
16924 INFO: checking EXE
16925 INFO: Building EXE because EXE-00.toc is non existent
16925 INFO: Building EXE from EXE-00.toc
16925 INFO: Copying bootloader EXE to C:\Users\lkt\Desktop\Python反编译测试\dist\main.exe.notanexecutable
17084 INFO: Copying icon to EXE
17091 INFO: Copying icons from ['C:\\ide\\anaconda3\\envs\\blogTest\\lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-console.ico']
17157 INFO: Writing RT_GROUP_ICON 0 resource with 104 bytes
17158 INFO: Writing RT_ICON 1 resource with 3752 bytes
17158 INFO: Writing RT_ICON 2 resource with 2216 bytes
17158 INFO: Writing RT_ICON 3 resource with 1384 bytes
17159 INFO: Writing RT_ICON 4 resource with 37019 bytes
17159 INFO: Writing RT_ICON 5 resource with 9640 bytes
17159 INFO: Writing RT_ICON 6 resource with 4264 bytes
17159 INFO: Writing RT_ICON 7 resource with 1128 bytes
17164 INFO: Copying 0 resources to EXE
17164 INFO: Embedding manifest in EXE
17165 INFO: Updating manifest in C:\Users\lkt\Desktop\Python反编译测试\dist\main.exe.notanexecutable
17231 INFO: Updating resource type 24 name 1 language 0
17237 INFO: Appending PKG archive to EXE
17248 INFO: Fixing EXE headers
18957 INFO: Building EXE from EXE-00.toc completed successfully.
使用的是单文件打包的形式举例,打包好的文件在dist目录下,运行可执行文件
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试> cd .\dist\
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试\dist> .\main.exe
0
2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
42
44
46
48
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试\dist>
成功运行,接下来对可执行文件进行反编译。反编译我使用的是这个库封装的api
https://github.com/countercept/python-exe-unpacker 当然知乎链接里也有推荐另一个,我没有使用过。或许还会更好用,毕竟这个库有些年头了,而且我在使用过程中遇到了反编译的部分代码不对的问题(if else的逻辑和相关的缩进不对,我通过debug一步步改回来了)
将仓库clone下来
git clone [email protected]:countercept/python-exe-unpacker.git
进入仓库文件夹,下载相应的依赖,会有一些报错,可以不Care
pip install -r .\requirements.txt
然后将可执行文件反编译
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试\python-exe-unpacker> python .\pyinstxtractor.py ..\dist\main.exe
.\pyinstxtractor.py:95: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
import imp
[*] Processing ..\dist\main.exe
[*] Pyinstaller version: 2.1+
[*] Python version: 308
[*] Length of package: 5714476 bytes
[*] Found 58 files in CArchive
[*] Beginning extraction...please standby
[*] Found 78 files in PYZ archive
[*] Successfully extracted pyinstaller archive: ..\dist\main.exe
You can now use a python decompiler on the pyc files within the extracted directory
这样就得到了main.exe_extracted反编译文件
需要注意的是我画框的即为代码文件,当然项目比较大的话,还会有其他的,自己留意一下。其实这个文件是一个pyc文件,pyc文件可以很方便就转成py文件,但是麻烦点在于,他缺少了magic number(魔数),解决方法就是struct这个文件是有魔数的,用能编辑二进制文件的打开main和struct,将struct的第一行,复制粘贴给main即可
将main重命名为main.pyc,使用命令
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试\python-exe-unpacker> uncompyle6 -o main.py .\main.exe_extracted\main.pyc
.\main.exe_extracted\main.pyc --
# Successfully decompiled file
(blogTest) PS C:\Users\lkt\Desktop\Python反编译测试\python-exe-unpacker>
反编译的结果
# uncompyle6 version 3.8.0
# Python bytecode 3.8.0 (3413)
# Decompiled from: Python 3.8.13 (default, Mar 28 2022, 06:59:08) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: main.py
# Compiled at: 2022-03-16 20:22:54
# Size of source mod 2**32: 257 bytes
def foo():
for i in range(50):
if not i % 2:
print(i)
if __name__ == '__main__':
foo()
注释没有了,我自己实际使用的时候只有入口文件的注释会消失。其他文件的还在。
我反编译后的那个脚本我就可以随意修改了,脚本本身设置有密钥一定时间后会过期,拿到源码后我一个拿到了密钥的加密钥匙,可以随意生成密钥,另一个也可以修改程序改成自己的密钥,就跟开挂了一样。
最后如果想要加密打包可以在pyinstaller打包的时候加上–key指定密钥进行打包,不过据了解还是能反编译。或者用Cython打包,总的来说是加大反编译的难度,不然自己写的程序就打水漂了~
========================================================
补充:后续发现这个反编译库可以直接加上魔数,不用自己再去加,会更加好用一点
https://github.com/extremecoders-re/pyinstxtractor