51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

CPython逆向实战分析

Python代码转换为C代码的时候,将会大大增加框架代码量。

基础教程 | Cython 官方文档中文版(gitbooks.io)

1、正向py->c

先有正向,再有逆向

              pip install cython
            

写一个简单的pyx文件

.pyx 文件是由 Cython 编程语言 "编写" 而成的 Python 扩展模块源代码文件

              print("hello")
            

写一个 setup.py文件

              
                from distutils.core import setup
              
              

              
                from Cython.Build import cythonize
              
              

              
                
                
              
              

              
                setup(
              
              

              
                    ext_modules = cythonize("test.pyx")
              
              

              
                )
              
            

使用命令开始编译

              python setup.py build_ext --inplace
            

image

生成如下文件

image

打开test.c发现有几千行代码

image

单纯的一行python代码,生成为c代码就几千行

调用so文件

image

2、逆向分析

2.1 字符串类型

_Pyx_CreateStringTabAndInitStrings

全局字符串赋值一般在 _Pyx_CreateStringTabAndInitStrings 中,该函数中使用的字符串定义数组形如:

              
                typedef struct {
              
              

              
                    PyObject **p;
              
              

              
                    const char *s;
              
              

              
                    const Py_ssize_t n;
              
              

              
                    const char* encoding;
              
              

              
                    const char is_unicode;
              
              

              
                    const char is_str;
              
              

              
                    const char intern;
              
              

              
                } __Pyx_StringTabEntry;
              
            

而字符串是通过 __Pyx_StringTabEntry 的数组进行初始化的,也就是说当我们在该函数中看到以下伪代码时:

              
                v8 = _mm_unpacklo_epi64(&qword_28A98, "AttributeError");
              
              

              
                v9 = 15LL;
              
              

              
                v10 = 0LL;
              
              

              
                v11 = 0x100;
              
              

              
                v12 = 1;
              
            

就代表这是一个 {&qword_28A98, "AttributeError", 15, 0, 1, 0, 1}__Pyx_StringTabEntry ,也就是说 qword_28A98 中将要初始化一个内容是 "AttributeError" 的字符串对象的地址,在后续调用中,调用到AttributeError字符串的地方都会用 &qword_28A98 指代



2.2 整数类型

_pyx_pymod_exec_chal

              
                qword_29170 = PyLong_FromLong(113LL, v9, v244, v245);
              
              

              
                if ( qword_29170 )
              
            

qword_29170 中将存储一个值为 113 的整数类型的Python对象。

              
                qword_29600 = PyLong_FromString("2654435769", 0LL, 0LL);
              
              

              
                if ( qword_29600 )
              
            

大数会用 PyLong_FromString 函数来初始化,这里 qword_29600 中将存储一个值为 2654435769 的整数类型的Python对象,后续用到2654435769的地方将使用 qword_29600

2.3 import写法

              
                v539 = _Pyx_ImportDottedModule_constprop_0(random);
              
              

              
                if ( PyDict_SetItem(_pyx_mstate_global_static, random, v539) < 0 )
              
              

              
                {
              
            

导入``random``模块,同``import random

3、实战分析

这里提供一道自己出的题目,采用了R**加密,流程很简单。

让我们开干

image

把提供的so文件拖进IDA中

image

而且这个函数 _Pyx_CreateStringTabAndInitStrings() 非常大,不能反编译

目前不知道这个函数的加密,我们先打印其相关的属性,看看能不能找到蛛丝马迹

              
                import test
              
              

              
                dir(test)
              
            

image

发现是R**加密,这样逻辑就清晰了

所以现在的目标是获得R**的秘钥和密文咯,假设R**没有魔改

刚才我们在函数_Pyx_CreateStringTabAndInitStrings 找到了非常类似密文的值

              9d7422eabf8baf369c09121f02e940099d9c6b538d88e30aac08
            

但是没有找到 秘钥,说明秘钥可能就不是字符串,而是byte类型!

我们先搜索R**相关函数

image

image

发现代码非常多,暂时先不去分析R**算法

看看哪里调用了我们的R**算法

image

函数:_pyx_pymod_exec_test

image

但是byte类型怎么初始化呢?

我们编写一个demo,然后反编译去查看初始化方式即可

demo.pyx

              
                key = b'mykekekeke'
              
              

              
                en_flag = b'12312312312312'
              
            

demo_setup.pyx

              
                from distutils.core import setup
              
              

              
                from Cython.Build import cythonize
              
              

              
                
                
              
              

              
                setup(
              
              

              
                    ext_modules = cythonize("demo.pyx")
              
              

              
                )
              
            

运行命令

              python demo_setup.py build_ext --inplace
            

先看看c文件

image

还是很清晰的,直接IDA分析so文件

image

发现byte类型也存储在函数_Pyx_CreateStringTabAndInitStrings

所以我们再翻阅一下,成功找到类似key的代码

image

              DASCTF{cpython_is_so_easy}
            

image


本文作者: dingjiacan@antvsion.com

本文为安全脉搏专栏作者发布,转载请注明: https://www.secpulse.com/archives/205557.html

赞(0)
未经允许不得转载:工具盒子 » CPython逆向实战分析