对于cython的基础逆向分析(1)
字数 3700 2025-08-22 12:23:00

Cython基础逆向分析教学文档

1. Cython编译基础

1.1 编译流程

  1. 创建.pyx文件:Python扩展模块的源代码文件

    def test(a, b):
        c = a + b
        print(c)
    
  2. 创建setup.py文件:

    from distutils.core import setup
    from Cython.Build import cythonize
    setup(ext_modules=cythonize("test.pyx"))  # test.pyx是创建的pyx文件名
    
  3. 编译方法:

    • 使用cython模块编译:cython test.pyx
    • 使用cythonize模块编译:cythonize -a -i test.pyx
    • 使用setup.py编译:python setup.py build_ext --inplace
  4. 输出文件:

    • .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代码关键部分:

  1. 普通整数:
__pyx_v_tmp = 19;
// IDA中显示为:
v3 = PyLong_FromLong(19LL);
  1. 小数:
__pyx_v_tmp1 = 21.3;
// IDA中显示为浮点数加载到xmm寄存器
  1. 长整数:
__Pyx_INCREF(__pyx_int_0x149357046d142e1e6e1948884dc976fdf);
__pyx_v_tmp2 = __pyx_int_0x149357046d142e1e6e1948884dc976fdf;
// IDA中显示为:
v4 = PyLong_FromString("-437593479587349875983475987349587324895", 0LL, 0LL);
  1. 字符串:
__Pyx_INCREF(__pyx_n_s_tmp1);
__pyx_v_tmp3 = __pyx_n_s_tmp1;
// 使用预定义的全局字符串变量
  1. 布尔值:
__pyx_v_tmp4 = 1;  // True
__pyx_v_tmp5 = 0;  // False
  1. 列表:
__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);
// ... 其他元素类似
  1. 元组:
__Pyx_INCREF(__pyx_tuple__3);
__pyx_v_tmp8 = __pyx_tuple__3;
// 使用预定义的全局元组变量
  1. 字典:
__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代码关键部分:

  1. 加法:
__pyx_t_1 = PyNumber_Add(__pyx_v_x, __pyx_v_y);
  1. 减法:
__pyx_t_1 = PyNumber_Subtract(__pyx_v_x, __pyx_v_y);
  1. 乘法:
__pyx_t_1 = PyNumber_Multiply(__pyx_v_x, __pyx_v_y);
  1. 除法:
__pyx_t_1 = __Pyx_PyNumber_Divide(__pyx_v_x, __pyx_v_y);
  1. 整除:
__pyx_t_1 = PyNumber_FloorDivide(__pyx_v_x, __pyx_v_y);
  1. 异或:
__pyx_t_1 = PyNumber_Xor(__pyx_v_x, __pyx_v_y);
  1. 按位与:
__pyx_t_1 = PyNumber_And(__pyx_v_x, __pyx_v_y);
  1. 按位或:
__pyx_t_1 = PyNumber_Or(__pyx_v_x, __pyx_v_y);
  1. 求模:
__pyx_t_1 = PyNumber_Remainder(__pyx_v_x, __pyx_v_y);
  1. 幂运算:
__pyx_t_1 = PyNumber_Power(__pyx_v_x, __pyx_v_y, Py_None);
  1. 取反:
__pyx_t_1 = PyNumber_Invert(__pyx_v_x);
  1. 右移:
__pyx_t_1 = __Pyx_PyInt_RshiftObjC(__pyx_v_x, __pyx_int_4, 4, 0, 0);
// IDA中可能显示为:
v27 = PyLong_FromLongLong(*(int *)(v4 + 24) >> 4, ...);
  1. 左移:
__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代码关键部分:

  1. 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);
}
  1. 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);
}
  1. 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);
    }
}
  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);
}
  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);
}
  1. 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);
}
  1. 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中,条件语句的表现形式:

  1. 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块内容
    }
}
  1. 简单if结构:
if (condition) {
    // if块内容
}

6. 总结

关键点回顾

  1. 编译产物分析

    • Cython会生成.c文件和.pyd/.so文件
    • 不同平台生成的代码可能有差异
  2. 比较操作

    • 使用PyObject_RichCompare函数
    • 通过第三个参数区分比较类型(Py_EQ/Py_NE等)
  3. 变量类型

    • 普通数字直接使用原生类型
    • 大数字、字符串、容器类型使用PyObject*
    • 常量会在初始化函数中预先创建
  4. 运算符

    • 每种运算符对应特定的PyNumber_*函数
    • 位运算和算术运算使用不同函数
  5. 条件语句

    • in/not in使用PySequence_Contains
    • and/or通过嵌套if实现
    • is/is not直接比较指针
  6. 逆向特征

    • 大量使用Python C API函数(PyNumber_, PySequence_等)
    • 常量数据集中管理
    • 类型转换和引用计数操作明显

逆向技巧

  1. 识别Python C API函数调用
  2. 跟踪常量数据块的引用
  3. 注意PyObject*类型的传递和转换
  4. 关注引用计数操作(INCREF/DECREF)
  5. 分析初始化函数中的常量创建过程

通过理解这些Cython的编译特征和代码模式,可以更有效地分析由Cython编译生成的二进制文件。

Cython基础逆向分析教学文档 1. Cython编译基础 1.1 编译流程 创建 .pyx 文件:Python扩展模块的源代码文件 创建 setup.py 文件: 编译方法: 使用cython模块编译: cython test.pyx 使用cythonize模块编译: cythonize -a -i test.pyx 使用setup.py编译: python setup.py build_ext --inplace 输出文件: .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代码: 对应的C代码关键部分: 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代码: 对应的C代码关键部分: 普通整数: 小数: 长整数: 字符串: 布尔值: 列表: 元组: 字典: 3.3 变量初始化 Cython会在 __Pyx_InitConstants 函数中初始化所有常量: 在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代码: 对应的C代码关键部分: 加法: 减法: 乘法: 除法: 整除: 异或: 按位与: 按位或: 求模: 幂运算: 取反: 右移: 左移: 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代码: 对应的C代码关键部分: in运算: not in运算: and运算: or运算: not运算: is运算: is not和None判断: 5.3 条件语句结构 在IDA中,条件语句的表现形式: if-elif-else结构: 简单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编译生成的二进制文件。