SCTF-cython-wp
记录下打SCTF遇到的一个python题,cython编译的pyd文件如果直接用IDA分析的话会非常的复杂,可能python代码一行就会在IDA里多出几十行出来,一些参数检查和类型转换的伪代码中嵌入很多goto的奇奇怪怪的控制流,无厘头的跳转会让人头皮发麻,我尝试用IDA静态分析结合动态调试调一下午大概分析出了sub14514会调用这几个用户定义的函数sub_50804,sub50520,然后最终的加密始终没办法很好的还原,只是察觉到这是个类似TEA的加密,但是循环次数又不对,最后在这放弃了,看到站队里大佬WP使用python注入的方式还原加密处代码,感觉挺有意思的,在这里复现下解题过程
获取py源码
附件是python打包的exe,按照流程正常解包,用pyinstxtractor.py脚本解包(最好用高版本的,低版本的解包出的pyc还需要自己修补文件头),运行指令
.\pyinstxtractor2.py .\ez_cython.exe
在ez_cython.exe_extracted里找到ez_cython.pyc文件进行反编译
uncompyle6 .\ez_cython.pyc
拿到ez_cython.py源代码
1 | import cy |
发现有个报错信息mainParse error at or near `COME_FROM’ instruction at offset 108_0,dis提取字节码分析
1 | 1 0 LOAD_CONST 0 (0) |
得到python字节码,扔给GPT要一份py源码
1 | import cy |
可以看到这里导入了cy这个库,在解包文件夹里找到 cy.cp38-win_amd64.pyd 文件,放到py文件的同级目录下运行,运行报错发现GPT给的str_hex函数有问题,替换成uncompyle6给的定义,成功运行
分析程序
main函数大致运行逻辑是让用户输入一个字符,然后将字符加入到列表list中,然后在用户输入end之后对这个列表转换成十六进制,并在cy模块里的sub14514函数进行验证,不成功则提示是否重新输入。所以主要验证逻辑在cy模块中,先进入IDA里分析该pyd模块,找到字符串sub14514交叉引用定位到该函数执行入口,对应的是IDA里的sub_180009420函数
1 | __int64 __fastcall sub_180009420(__int64 a1, __int64 a2) |
在这里可以看出这个pyd的编写应该也是用python写的,而不是C,根据GPT描述,
虽然
.pyd
文件通常是用C或C++编写的,但你可以使用Python来生成.pyd
文件,通过使用工具和库如Cython或pybind11,它们允许你将Python代码编译成C扩展,从而生成.pyd
文件。
既然源码是用python写的,而且这个sub_14514接受的参数是python里的list类,那么就可以考虑自己定义一个类似list的类,然后重写list的成员函数,从而在加密过程的时候如果调用了这个成员函数,那么就会进入到我们自己定义的函数中,如果我们自己在定义的时候打印调试信息显示这一步在干什么,我们是不是就能通过调试信息获取整个加密流程呢?
怎么重写呢?在C++里我们可以先父类继承,子类定义同名函数(参数列表也必须相同),从而实现重写函数,那么在python中也可以通过类似的方法实现重写,比如说定义一个自己的AList类继承自list
1 | class AList(list): |
随后在定义一个列表里的数据类型,重写数据间的运算方法
1 | class Symbol: |
把列表类和数据类定义好之后,准备注入到sub14514函数。注入前我们还需要知道明文(str2hex之后的)长度,在IDA里动调分析一下,xdbg附加,下断,定位,发现程序验证的时候会首先调用某个get_key函数
直接动调或者源码调用这个函数可以看到该函数返回了一个list的类型,值为SyC10VeRf0RVer
,接着往下定位到对比函数
把参数密文copy下来,发现刚好32个
1 | 00000212D2E33 00000212D329B850 P¸)Ó.... |
有此信息我们就可以编写脚本注入函数查看32个值的加密流程,运行的脚本如下
1 | import cy |
有关列表的加密流程:
1 | s5[0] = ((a[0] + ((((a[31] >> 3) ^ (a[1] << 3)) + ((a[1] >> 4) ^ (a[31] << 2))) ^ ((a[1] ^ 2654435769) + (a[31] ^ 49)))) & 4294967295) |
可以看到就是一个类似TEA的加密,把算法逆过来就行,解密脚本
EXP
1 | var1 = [2654435769,1013904242,3668340011,2027808484,387276957] |
学习使用Cython编译pyd
安装
1 | pip install cython |
编写一个py脚本实现pyd里加密的函数
1 | #main2.py |
创建一个编译脚本setup.py
1 | from distutils.core import setup |
命令行运行编译指令
1 | python setup.py build_ext --inplace |
生成main2.cp311-win_amd64.pyd文件,再取一个python文件import即可调用里面的myPrint函数
1 | import main2 |
参考链接: