对于cython的基础逆向分析(1)
字数 3700 2025-08-22 12:23:00
Cython基础逆向分析教学文档
1. Cython编译基础
1.1 编译流程
-
创建
.pyx文件:Python扩展模块的源代码文件def test(a, b): c = a + b print(c) -
创建
setup.py文件:from distutils.core import setup from Cython.Build import cythonize setup(ext_modules=cythonize("test.pyx")) # test.pyx是创建的pyx文件名 -
编译方法:
- 使用cython模块编译:
cython test.pyx - 使用cythonize模块编译:
cythonize -a -i test.pyx - 使用setup.py编译:
python setup.py build_ext --inplace
- 使用cython模块编译:
-
输出文件:
.c文件:生成的C源代码.pyd文件:目标分析文件(Windows平台)
1.2 平台差异
- Windows和Linux平台生成代码架构可能有区别
- Windows使用MSVC编译器,Linux使用GCC
2. 比较指令分析
2.1 比较操作符映射
Python比较操作符在Cython中对应的C函数和常量:
| Python操作符 | C函数 | 常量值 | 常量名 |
|---|---|---|---|
| == | PyObject_RichCompare | 2 | Py_EQ |
| >= | PyObject_RichCompare | 5 | Py_GE |
| <= | PyObject_RichCompare | 1 | Py_LE |
| != | PyObject_RichCompare | 3 | Py_NE |
| > | PyObject_RichCompare | 4 | Py_GT |
| < | PyObject_RichCompare | 0 | Py_LT |
2.2 代码示例
Python代码:
def test(flag):
enc = 19
if flag == enc: pass
if flag >= enc: pass
if flag <= enc: pass
if flag != enc: pass
if flag > enc: pass
if flag < enc: pass
对应的C代码关键部分:
// == 比较
__pyx_t_1 = __Pyx_PyInt_From_long(__pyx_v_enc);
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_EQ); // Py_EQ=2
__pyx_t_3 = __Pyx_PyObject_IsTrue(__pyx_t_2);
if (__pyx_t_3) { }
// >= 比较
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_GE); // Py_GE=5
// <= 比较
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_LE); // Py_LE=1
// != 比较
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_NE); // Py_NE=3
// > 比较
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_GT); // Py_GT=4
// < 比较
__pyx_t_2 = PyObject_RichCompare(__pyx_v_flag, __pyx_t_1, Py_LT); // Py_LT=0
3. 变量类型分析
3.1 变量类型映射
Python变量类型在Cython中的表示:
| Python类型 | Cython/C类型 | 初始化函数 |
|---|---|---|
| 普通整数 | long | PyInt_FromLong |
| 小数 | double | PyFloat_FromDouble |
| 长整数 | PyObject* | PyInt_FromString |
| 字符串 | PyObject* | 全局字符串变量 |
| 布尔值 | int | 直接赋值(1/0) |
| 列表 | PyObject* | PyList_New |
| 元组 | PyObject* | 全局元组变量 |
| 字典 | PyObject* | PyDict_New/PyDict_SetItem |
3.2 代码示例
Python代码:
def test(flag):
tmp = 19 # 普通整数
tmp1 = 21.3 # 小数
tmp2 = 437593479587349875983475987349587324895 # 长整数
tmp3 = "tmp1" # 字符串
tmp4 = True # 布尔值
tmp5 = False # 布尔值
tmp6 = [45, -67, 21.4, 23.7, -437593479587349875983475987349587324895,
340759348759834759853842759, "hello", "guheng", True, False, tmp] # 列表
tmp7 = [] # 空列表
tmp8 = (1, 3, 99) # 元组
tmp9 = {"key": "key", "value": "value"} # 字典
对应的C代码关键部分:
- 普通整数:
__pyx_v_tmp = 19;
// IDA中显示为:
v3 = PyLong_FromLong(19LL);
- 小数:
__pyx_v_tmp1 = 21.3;
// IDA中显示为浮点数加载到xmm寄存器
- 长整数:
__Pyx_INCREF(__pyx_int_0x149357046d142e1e6e1948884dc976fdf);
__pyx_v_tmp2 = __pyx_int_0x149357046d142e1e6e1948884dc976fdf;
// IDA中显示为:
v4 = PyLong_FromString("-437593479587349875983475987349587324895", 0LL, 0LL);
- 字符串:
__Pyx_INCREF(__pyx_n_s_tmp1);
__pyx_v_tmp3 = __pyx_n_s_tmp1;
// 使用预定义的全局字符串变量
- 布尔值:
__pyx_v_tmp4 = 1; // True
__pyx_v_tmp5 = 0; // False
- 列表:
__pyx_t_2 = PyList_New(11); // 创建11个元素的列表
// 逐个设置元素
__Pyx_INCREF(__pyx_int_45);
__Pyx_GIVEREF(__pyx_int_45);
__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_int_45);
// ... 其他元素类似
- 元组:
__Pyx_INCREF(__pyx_tuple__3);
__pyx_v_tmp8 = __pyx_tuple__3;
// 使用预定义的全局元组变量
- 字典:
__pyx_t_2 = __Pyx_PyDict_NewPresized(2);
PyDict_SetItem(__pyx_t_2, __pyx_n_s_key, __pyx_n_s_key);
PyDict_SetItem(__pyx_t_2, __pyx_n_s_value, __pyx_n_s_value);
3.3 变量初始化
Cython会在__Pyx_InitConstants函数中初始化所有常量:
static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {
__pyx_float_21_4 = PyFloat_FromDouble(21.4);
__pyx_float_23_7 = PyFloat_FromDouble(23.7);
__pyx_int_45 = PyInt_FromLong(45);
__pyx_int_neg_67 = PyInt_FromLong(-67);
__pyx_int_large_neg_43759347958734... = PyInt_FromString("-437593...", 0, 0);
// ... 其他初始化
return 0;
}
在IDA中,这些初始化后的值会被放入off_1800095B8数据块中统一管理。
4. 运算符分析
4.1 运算符映射
Python运算符在Cython中对应的C函数:
| Python运算符 | C函数名 |
|---|---|
| + | PyNumber_Add |
| - | PyNumber_Subtract |
| * | PyNumber_Multiply |
| / | PyNumber_TrueDivide |
| // | PyNumber_FloorDivide |
| ^ | PyNumber_Xor |
| & | PyNumber_And |
| | | PyNumber_Or |
| % | PyNumber_Remainder |
| ** | PyNumber_Power |
| ~ | PyNumber_Invert |
| >> | PyNumber_Rshift |
| << | PyNumber_Lshift |
4.2 代码示例
Python代码:
def test(x, y):
a = x + y
b = x - y
c = x * y
d = x / y
e = x // y
f = x ^ y
g = x & y
h = x | y
i = x % y
j = x ** y
k = ~x
l = x >> 4
n = y << 2
对应的C代码关键部分:
- 加法:
__pyx_t_1 = PyNumber_Add(__pyx_v_x, __pyx_v_y);
- 减法:
__pyx_t_1 = PyNumber_Subtract(__pyx_v_x, __pyx_v_y);
- 乘法:
__pyx_t_1 = PyNumber_Multiply(__pyx_v_x, __pyx_v_y);
- 除法:
__pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_v_x, __pyx_v_y);
- 整除:
__pyx_t_1 = PyNumber_FloorDivide(__pyx_v_x, __pyx_v_y);
- 异或:
__pyx_t_1 = PyNumber_Xor(__pyx_v_x, __pyx_v_y);
- 按位与:
__pyx_t_1 = PyNumber_And(__pyx_v_x, __pyx_v_y);
- 按位或:
__pyx_t_1 = PyNumber_Or(__pyx_v_x, __pyx_v_y);
- 求模:
__pyx_t_1 = PyNumber_Remainder(__pyx_v_x, __pyx_v_y);
- 幂运算:
__pyx_t_1 = PyNumber_Power(__pyx_v_x, __pyx_v_y, Py_None);
- 取反:
__pyx_t_1 = PyNumber_Invert(__pyx_v_x);
- 右移:
__pyx_t_1 = __Pyx_PyInt_RshiftObjC(__pyx_v_x, __pyx_int_4, 4, 0, 0);
// IDA中可能显示为:
v27 = PyLong_FromLongLong(*(int *)(v4 + 24) >> 4, ...);
- 左移:
__pyx_t_1 = __Pyx_PyInt_LshiftObjC(__pyx_v_y, __pyx_int_2, 2, 0, 0);
// IDA中可能显示为:
v32 = PyLong_FromLongLong(4 * v31, ...);
5. 条件语句和其他运算符
5.1 特殊运算符
| Python运算符 | C函数/实现方式 |
|---|---|
| in | PySequence_Contains + Py_EQ判断 |
| not in | PySequence_Contains + Py_NE判断 |
| and | 两个if嵌套 |
| or | 第一个条件为真则跳过第二个条件判断 |
| not | PyObject_IsTrue + 取反 |
| is | 直接指针比较(==) |
| is not | 直接指针比较(!=) |
| None | 与Py_NoneStruct比较 |
5.2 代码示例
Python代码:
def test(x, y):
if x in y: x = y
elif x not in y: y = x
else: x = 999999
if x == 0 and y == 0: x = 1
if y == 0 or x == 0: y = 1
if not x: x = 0
if x is y: x = 9
if y is not None: y = 8
对应的C代码关键部分:
- in运算:
__pyx_t_1 = (__Pyx_PySequence_ContainsTF(__pyx_v_x, __pyx_v_y, Py_EQ));
if (__pyx_t_1) { // in
__Pyx_INCREF(__pyx_v_y);
__Pyx_DECREF_SET(__pyx_v_x, __pyx_v_y);
}
- not in运算:
__pyx_t_1 = (__Pyx_PySequence_ContainsTF(__pyx_v_x, __pyx_v_y, Py_NE));
if (__pyx_t_1) { // not in
__Pyx_INCREF(__pyx_v_x);
__Pyx_DECREF_SET(__pyx_v_y, __pyx_v_x);
}
- and运算:
__pyx_t_2 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_x, __pyx_int_0, 0, 0));
if (__pyx_t_2) {
__pyx_t_2 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_y, __pyx_int_0, 0, 0));
if (__pyx_t_2) { // and
__Pyx_INCREF(__pyx_int_1);
__Pyx_DECREF_SET(__pyx_v_x, __pyx_int_1);
}
}
- or运算:
__pyx_t_2 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_y, __pyx_int_0, 0, 0));
if (!__pyx_t_2) {
__pyx_t_2 = (__Pyx_PyInt_BoolEqObjC(__pyx_v_x, __pyx_int_0, 0, 0));
}
if (__pyx_t_2) { // or
__Pyx_INCREF(__pyx_int_1);
__Pyx_DECREF_SET(__pyx_v_y, __pyx_int_1);
}
- not运算:
__pyx_t_1 = __Pyx_PyObject_IsTrue(__pyx_v_x);
__pyx_t_2 = (!__pyx_t_1);
if (__pyx_t_2) { // not
__Pyx_INCREF(__pyx_int_0);
__Pyx_DECREF_SET(__pyx_v_x, __pyx_int_0);
}
- is运算:
__pyx_t_2 = (__pyx_v_x == __pyx_v_y); // is
if (__pyx_t_2) {
__Pyx_INCREF(__pyx_int_9);
__Pyx_DECREF_SET(__pyx_v_x, __pyx_int_9);
}
- is not和None判断:
__pyx_t_2 = (__pyx_v_y != Py_None); // is not
if (__pyx_t_2) {
__Pyx_INCREF(__pyx_int_8);
__Pyx_DECREF_SET(__pyx_v_y, __pyx_int_8);
}
5.3 条件语句结构
在IDA中,条件语句的表现形式:
- if-elif-else结构:
v5 = PySequence_Contains(a3, a2);
if (v5 == 1) { // if
// if块内容
} else {
v8 = PySequence_Contains(v3, v4);
if (v8) { // elif
// elif块内容
} else { // else
// else块内容
}
}
- 简单if结构:
if (condition) {
// if块内容
}
6. 总结
关键点回顾
-
编译产物分析:
- Cython会生成.c文件和.pyd/.so文件
- 不同平台生成的代码可能有差异
-
比较操作:
- 使用PyObject_RichCompare函数
- 通过第三个参数区分比较类型(Py_EQ/Py_NE等)
-
变量类型:
- 普通数字直接使用原生类型
- 大数字、字符串、容器类型使用PyObject*
- 常量会在初始化函数中预先创建
-
运算符:
- 每种运算符对应特定的PyNumber_*函数
- 位运算和算术运算使用不同函数
-
条件语句:
- in/not in使用PySequence_Contains
- and/or通过嵌套if实现
- is/is not直接比较指针
-
逆向特征:
- 大量使用Python C API函数(PyNumber_, PySequence_等)
- 常量数据集中管理
- 类型转换和引用计数操作明显
逆向技巧
- 识别Python C API函数调用
- 跟踪常量数据块的引用
- 注意PyObject*类型的传递和转换
- 关注引用计数操作(INCREF/DECREF)
- 分析初始化函数中的常量创建过程
通过理解这些Cython的编译特征和代码模式,可以更有效地分析由Cython编译生成的二进制文件。