py编译逆向学习
PYC 文件
pyc 文件是 python 在编译过程中出现的主要中间过程文件。pyc 文件是二进制的,类似 java 的字节码,可以由 python 虚拟机直接执行的。
PyCodeObject
实际上,pyc 文件就是 PyCodeObject 对象在硬盘上的保存形式。
而 PyCodeObject 的结构如下:
1 |
|
PyObject_HEAD
不同的 Python 版本会有不同的 PyObject_HEAD,以下是各版本的文件头:
Python 版本 | 十六进制文件头 |
---|---|
Python 2.7 | 03f30d0a00000000 |
Python 3.0 | 3b0c0d0a00000000 |
Python 3.1 | 4f0c0d0a00000000 |
Python 3.2 | 6c0c0d0a00000000 |
Python 3.3 | 9e0c0d0a0000000000000000 |
Python 3.4 | ee0c0d0a0000000000000000 |
Python 3.5 | 170d0d0a0000000000000000 |
Python 3.6 | 330d0d0a0000000000000000 |
Python 3.7 | 420d0d0a000000000000000000000000 |
Python 3.8 | 55 0d 0d 0a 00 00 00 00 00 00 00 00 00 00 00 00 |
Python 3.9 | 610d0d0a000000000000000000000000 |
Python 3.10 | 6f0d0d0a000000000000000000000000 |
Python 3.11 | a70d0d0a000000000000000000000000 |
反反编译
更改魔术头
当想要保护我们的 pyc 文件不被反编译,最简单的做法就是更改魔术头,即 PyObject_HEAD。
可能是完全删除魔术头,也可能是修改为不是原生版本的魔术头,我们只需要根据情况添加或修改即可。
PYD 文件
pyd 文件相当于 python 的运行时 dll,在 python 代码中可以直接使用 import
将 pyd 文件当作模块导入。
对于 pyd 的逆向,我们需要借助 ida 的 attach
动态调试跟静态分析。
pyexe的逆向
PyInstaller打包后,pyc文件的前8个字节会被抹掉,所以最后要自己添加回去
逆向:
用pyinstxtractor来解pyc包
安装
1 |
|
执行脚本
1 |
|
就会解包得到一个文件夹(一般命名为**.exe_extracted)
里面只用注意两个文件,其他的一般是库脚本
一个是1.pyc,另一个是struct.pyc,
同时会有提示:
1 |
|
如果解包出来发现原来的pyc脚本是用与自己的python的版本不一样的话,会有警告:
1 |
|
这个是告诉你版本号不同,魔术头被修改了;
只需要自己将魔术头给改正,一般struct.pyc的前16字节就是所要的魔术头;
魔术头依照版本号来确定:
在刚刚的提示里面就含有这个文件对应的版本号
1 |
|
E.P.:
原:
改:
E3这个是个可以作为本文件的标记,所以可见多了4个字节,复制粘贴struct.pyc的头时顺便把那4个给覆盖了。
修改完之后就简单了,对1.pyc使用 uncompyle 可以将 pyc 文件完美反编译。uncompyle6 Github主页
安装:
1 |
|
而 uncompyle 用法如下:
1 |
|
uncomyle6 会直接将反编译后的源码输出在标准输出中,推荐用法:
1 |
|
将源码输出到文件里面,比如说:
1 |
|
输出后就直接看1.py即可。
直接输出pyc
1 |
|