python编译后的pyd爆破

  • A+
所属分类:移动安全
python编译后的pyd爆破

本文为看雪论坛优秀文章

看雪论坛作者ID:rushmaster



‍‍
最近接触一个国外某app的协议软件,是python3.8写的,它把关键模块都编译成了pyd,然后使用pyinstaller打包发布给用户。软件启动后检查机器码,然后需要输入授权码才可使用,看着很是恼火,所以想尝试破解。其中关键应该是需要爆破pyd里的逻辑,修改汇编代码来实现绕过授权。


python编译后的pyd爆破

前提知识

python编译后的pyd爆破


1. py、pyc、pyo、pyd

py: python 脚本文件(source code)

pyc: 脚本文件编译得到的字节码, 二进制文件,python文件经过编译器编译之后的文件。可以提高文件加载速度。

pyo: 脚本文件开启优化编译选项(-O)编译得到的字节码,二进制文件,优化编译后的文件。可以通过python -O file.py生成。

pyd: 基本的Windows DLL文件,python的动态链接库。

2. 编译pyd

要编译的脚本:uitl1.py
def fun_hello(s):    if s == 1:        return 'hello world'    elif s == 2:        return '222222222'

提供编译脚本:setup.py
from setuptools import setupfrom Cython.Build import cythonize setup(    name='test',    ext_modules=cythonize('util1.py'))

在setup.py文件所在目录下进行如下命令:
python setup.py build_ext --inplace

这样就能看到同级目录下生成pyd文件了。32位的python生成pyd文件是32位的,64位的python生成的是64位的。
python编译后的pyd爆破

3. 使用pyd  

test.py
import util1 if __name__ == '__main__':    print(util1.fun_hello(2))

4. pyinstaller打包py到exe
pip install pyinstaller
pyinstaller test.py

5. 解包pyinstaller打包的exe

pyinstxtractor.py即可。这个代码不长,可以调试看看,熟悉下打包的exe组成。需要注意的是,被打包的文件都是zlib.compress压缩过后,再按照固定格式组成exe的,所以直接修改打包后的exe的16进制码来爆破貌似不好操作。只能解包后修改pyd,然后找齐依赖的库,重新pyinstaller打包,实现爆破。

https://github.com/countercept/python-exe-unpacker

6. pyc反编译

uncompyle6支持python3.8的pyc的反编译。

需要注意的是,如果是pyinstaller解包后取到的pyc文件,文件头部的magic被抹除过了,所以需要把对应版本python的magic加上来,可以装对应版本python,然后到安装目录下随便找个pyc文件,看一下头部,然后用010Editor复制到解包后的pyc,就可以正常反编译了。

下图是python3.8_32位的magic头:
python编译后的pyd爆破
https://github.com/rocky/python-uncompyle6
      

python编译后的pyd爆破

pyd文件汇编代码和python脚本的对应关系分析

python编译后的pyd爆破

前面的前提知识,随便搜搜都能找到。但是如何才能直接修改pyd的汇编代码,实现python脚本流程的更改呢?

我百度谷歌搜了半天也没找到合适的资料,也许很少有人破解python编译打包的exe吧。

那么下面就是我做的工作了,也是本帖的价值所在了。
     
我自己写了一个python小脚本,然后编译成了pyd,它会生成一个中间的util1.c文件,代码大概有3000多行。只要花时间精力熟悉这个c文件,然后对照着ida就可以了解python脚本转成C然后编译成汇编指令,它们3者之间大概对应关系了。

下面略过大概1天的工作量,直接给出我们拿到一个pyd后,怎么快速找到我们要找的关键python代码。然后直接爆破。

把要分析的pyd文件拖到对应32位或64位的IDA:
python编译后的pyd爆破
大概所有的pyd都只有这个一个导出函数,当这个pyd模块被其他py脚本import时会调用这个导出函数进行模块初始化。
python编译后的pyd爆破
跳转到dword_1000634C可以看到一个结构体,里面有一个关键的成员__pyx_moduledef_slots。
python编译后的pyd爆破
python编译后的pyd爆破
这个成员是一个结构体数组。
python编译后的pyd爆破
python编译后的pyd爆破

里面有个关键函数__pyx_pymod_exec_util1负责初始化python脚本里的所有变量,函数,常量等等,把他们都对应到pyobject,然后就只使用这些pyobject了。所以汇编里看流程就很难,因为没有明显的明文了。


定位到 __pyx_pymod_exec_util1后,我们主要的目的是找常量和pyobject的对照表,python脚本里的函数名和汇编函数的对照表,有这2个表,python脚本和汇编的对应关系就明朗了。这里就只能手动往下翻了。
python编译后的pyd爆破
翻到类似调用 PyUnicode_InternFromString 的地方,大概就是我们要找的常量对照表了。
python编译后的pyd爆破
也就是C文件里的这个表。
python编译后的pyd爆破
其中offset dword_10006DFC就是代表字符串"222222222"的pyobject,直接找它的交叉引用就可以定位一些关键代码了。

我们继续在 __pyx_pymod_exec_util1 里找python脚本函数对应汇编函数的那个表。
python编译后的pyd爆破
跳过去:
python编译后的pyd爆破
aFunHello指向python脚本里的函数名。

__pyx_pf_5util1_fun_hello就是对应的汇编函数。

可以看到,只要找到这个表,就很容易定位我们要找的python脚本函数对应的汇编实现了。

其实我们也可以不必如上这么麻烦。只要在.data段里翻一翻。或者string窗口找到感兴趣的字符串交叉引用也能很快找到这个表。

需要知道的就是, aFunHello下面就是对应的汇编实现函数。

现在终于可以去分析fun_hello这个python脚本函数对应的汇编函数了。
python编译后的pyd爆破
可以看到脚本里的 s == 1 对应的汇编就是  __Pyx_PyInt_EqObjC 然后下面会使用PyObject_IsTrue判断这个函数的返回值。
python编译后的pyd爆破
那么爆破点就找到了。把 jz short loc_10004753 改成jnz short loc_10004753即可。

IDA-》edit-》Patch program-》Assemble修改,然后 IDA-》edit-》Patch program-》Apply patches to input file即可得到修改后的pyd文件。

这样就实现了修改python脚本的执行逻辑了。
python编译后的pyd爆破
正常脚本应该是输出22222222才对,因为我们的爆破,输出了hello world!

这里我只是简单分析了if语句的修改,可以多写几个例子。实现修改其他流程。

在此只是抛转引玉,给大家一点点参考。省一点点时间。

另:大家看了半天以为我是分析那个国外app协议软件,其实我还没有搞定那个破解,所以只把自己这段时间的分析工作贴了上来,仅供参考。



python编译后的pyd爆破

- End -


python编译后的pyd爆破


看雪ID:rushmaster

https://bbs.pediy.com/user-home-830514.htm

  *本文由看雪论坛 rushmaster 原创,转载请注明来自看雪社区。




# 往期推荐





python编译后的pyd爆破
公众号ID:ikanxue
官方微博:看雪安全
商务合作:[email protected]



python编译后的pyd爆破

球分享

python编译后的pyd爆破

球点赞

python编译后的pyd爆破

球在看



python编译后的pyd爆破

点击“阅读原文”,了解更多!

本文始发于微信公众号(看雪学院):python编译后的pyd爆破

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: