Cython
一些资料#
- 创建并运行 setup.py _PyCharm中文网
- python编译后的pyd爆破-软件逆向-看雪-安全社区|安全招聘|kanxue.com
- Tutorials - 基础教程 - 《Cython 3.0 中文文档》 - 书栈网 · BookStack
- Cython入门教程 | 陈东的博客 (chend0316.github.io) —— 最全
概述#
Cython可以将Python代码转换成c,再编译成so,以便提高效率。
目前自动化分析Cython elf的工具极少,遇到Cython的逆向题目基本只能手撕。
本篇文章主要是探索的过程,还在寻找Cython的规律,或许需要研究下Cython的编译器。
编译#
pip install cython
helloworld.pyx
print("HelloWorld!")
main.py
import helloworld
setup.py
from setuptools import setup, Extension
module = Extension ('helloworld', sources=['helloworld.pyx'])
setup(
name='cythonTest',
version='1.0',
author='jetbrains',
ext_modules=[module]
)
python3 setup.py build_ext --inplace
python3 main.py
HelloWorld!
同目录下就会生成 helloworld.c
和 helloworld.cpython-310-x86_64-linux-gnu.so
用vsc打开helloworld.c,用ida打开so,开始寻找对应关系
注:
"Python.h"
一开始没导入,在vsc里点“Quick Fix”里有个自动导入的,点一下
开发人员选项#
在main.py的开头使用 import pyximport; pyximport.install()
可以避免每次都要进行编译
可视化C与Python的交互#
编辑 setup.py
加上 annotate=True
from setuptools import setup, Extension
from Cython.Build import cythonize
module = Extension ('ok', sources=['ok.pyx'])
setup(
name='cythonTest',
version='1.0',
author='jetbrains',
ext_modules=cythonize([module], annotate=True)
)
同目录下就会生成一个 html
,打开

黄色的条目就是 C 与 Python 的交互生成的代码,可以点开
如何分析#
Cython翻译Python成C的过程中,会附加一大堆“胶水代码”,比如
def add(a, b):
return a + b
用IDA看对应的函数,有180行,其实对逆向有帮助的只有最后几行

所以需要找到逆向的关键点
Argument unpacking done#
# file ok.pyx
def enc(a,b):
return a + b
带参数的函数,源码和so中会出现 argument_unpacking_done
标签
// file ok.c
__pyx_pw_2ok_1enc(...) {
// 此处省略一大坨代码
goto __pyx_L4_argument_unpacking_done;
// 此处再省略另一坨代码
__pyx_L4_argument_unpacking_done:;
__pyx_r = __pyx_pf_2ok_enc(__pyx_self, __pyx_v_a, __pyx_v_b);
// ...
return __pyx_r;
}
static PyObject *__pyx_pf_2ok_enc(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_a, PyObject *__pyx_v_b) {
// ...
__pyx_r = PyNumber_Add(__pyx_v_a, __pyx_v_b);
// ...
return __pyx_r;
}
可知, __pyx_pf_2ok_enc
才是真正的函数逻辑,前面都是在 Argument unpacking
IDA里
只有
_pyx_pw_2ok_1enc
函数,没有__pyx_pf_2ok_enc
所以应该是内联了
// file ok.cpython-310-x86_64-linux-gnu.so with IDA
PyObject *__fastcall _pyx_pw_2ok_1enc(
PyObject *__pyx_self,
PyObject *const *__pyx_args,
Py_ssize_t __pyx_nargs,
PyObject *__pyx_kwds)
{
// 此处省略380行
LABEL_14:
v10 = values;
v11 = __pyx_v_b;
__pyx_L4_argument_unpacking_done:
result = (PyObject *)PyNumber_Add(v10, v11);
if ( !result )
{
_Pyx_AddTraceback("ok.enc", 2303, 2, "ok.pyx");
return 0LL;
}
return result;
}
注:有些gcc时提高了优化等级,可能没有
unpacking done
PyObject结构体#
// file /usr/include/python3.10/object.h
#define _PyObject_HEAD_EXTRA \
struct _object *_ob_next; \
struct _object *_ob_prev;
/* Nothing is actually declared to be a PyObject, but every pointer to
* a Python object can be cast to a PyObject*. This is inheritance built
* by hand. Similarly every pointer to a variable-size Python object can,
* in addition, be cast to PyVarObject*.
*/
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;
所以 pyObject[2]
就是 ob_refcnt
,特别是 Py_List
, ob_refcnt
的值就是 Py_List
的大小,Cython会以此作为优化,省略一个 len(list)
即
// file /usr/include/python3.10/listobject.h
PyAPI_FUNC(Py_ssize_t) PyList_Size(PyObject *);
实例#
# file ok.pyx
def enc():
r = [1] * 8
result = 0
for i in range(len(r)):
result += r[i]
return result
// file ok.so with IDA
v2 = PyList_New(8LL, unused);
// ...
v3 = (_QWORD *)v2;
// ...
v8 = v3[2];
// ...
while (1) {
// ...
++v10;
_Py_Dealloc(pyx_int_0);
if ( v8 == v10 )
break;
// ...
}
经测试,这是基于已知长度的 Py_List ,即调用了 PyList_New
的,对于未知长度,还是会调用 PyList_Size
或 PyObject_Size
文档#
/usr/include/python3.10/abstract.h
里面是所有Python代码和C函数的对应文档
比如
/* Returns the result of adding o1 and o2, or NULL on failure.
This is the equivalent of the Python expression: o1 + o2. */
PyAPI_FUNC(PyObject *) PyNumber_Add(PyObject *o1, PyObject *o2);
可能考虑把注释导入IDA
工具#
/usr/include/python3.10/abstract.h
里的函数应该是生成的 .c
中包含的所有函数,其他函数都是内部调用。
如果以这个思路的话,其实只需要跟踪:
- 调用
abstract.h
中函数的代码行 - 相关变量
如何debug#
在调用函数之前,加个 input()
,运行python文件,然后 IDA attach,再回车,就可以debug了
多试几下#
1. for 循环#
def enc():
r = 0
for i in range(5):
r += 1
return r


2. Traceback#

在汇编的最后会 __Pyx_AddTraceback
,参数分别是 ok.enc
c_line
py_line
ok.pyx

看看版本#
