首页 > 其他分享 >Cython二进制逆向系列(二)变量与数据结构

Cython二进制逆向系列(二)变量与数据结构

时间:2024-12-10 21:20:45浏览次数:7  
标签:pyx Cython 二进制 L1 int __ error PYX 数据结构

Cython二进制逆向系列(二)变量与数据结构

  在这篇文章里,我们会讨论Cython是如何存储变量(整数、小数、字符串、布尔值)以及数据结构(列表、元组、集合、字典)。Cython 对变量存储的方式与 Python 相似,但在 Cython 中,可以使用 C 类型的变量来显著提高性能。此外,由于Cython仍然依托于Python的虚拟机运行,因此Cython编译后的文件在底层仍然离不开对虚拟机接口的调用。在逆向时,我们可以通过调用的接口函数来判断变量的类型。

  Cython编译规则中会先把硬编码中的数字先解析了一遍,我们写的普通整数识别为long类型,小数则是识别成了double类型(无论长短),太长的整数识别为PyObject类型,字符串也是PyObject类型,布尔值是int类型,列表、元组、字典都是PyObject类型。



变量类型

整数

def integer():
	var1=1
	var2=437593479587349875983475987349587324895
	var3=-1
integer()

  编译后在c文件的2269行我们可以看到函数的实现:

static PyObject *__pyx_pf_4test_integer(CYTHON_UNUSED PyObject *__pyx_self) {
  CYTHON_UNUSED long __pyx_v_var1;
  CYTHON_UNUSED PyObject *__pyx_v_var2 = NULL;
  CYTHON_UNUSED long __pyx_v_var3;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("integer", 1);

  /* "test.py":2
 * def integer():
 * 	var1=1             # <<<<<<<<<<<<<<
 * 	var2=437593479587349875983475987349587324895
 * 	var3=-1
 */
  __pyx_v_var1 = 1;

  /* "test.py":3
 * def integer():
 * 	var1=1
 * 	var2=437593479587349875983475987349587324895             # <<<<<<<<<<<<<<
 * 	var3=-1
 * integer()
 */
  __Pyx_INCREF(__pyx_int_0x149357046d142e1e6e1948884dc976fdf);
  __pyx_v_var2 = __pyx_int_0x149357046d142e1e6e1948884dc976fdf;

  /* "test.py":4
 * 	var1=1
 * 	var2=437593479587349875983475987349587324895
 * 	var3=-1             # <<<<<<<<<<<<<<
 * integer()
 */
  __pyx_v_var3 = -1L;

  /* "test.py":1
 * def integer():             # <<<<<<<<<<<<<<
 * 	var1=1
 * 	var2=437593479587349875983475987349587324895
 */

  /* function exit code */
  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  __Pyx_XDECREF(__pyx_v_var2);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

  var1 被赋值为 1,这是一个普通的 C 整数。它通过 long 类型存储。

  var2 是一个非常大的整数,它的值是 437593479587349875983475987349587324895。这个整数被赋值为 __pyx_int_0x149357046d142e1e6e1948884dc976fdf,它表示 Cython 为这个大整数生成的 Python 对象。

  var3 被赋值为 -1,是一个 long 类型的整数。由此可知,无论正负,只要不超过long的范围,都会被当作long来存储。
  而在1379行我们可以看到

 __pyx_int_0x149357046d142e1e6e1948884dc976fdf = PyInt_FromString((char *)"0x149357046d142e1e6e1948884dc976fdf", 0, 0); if (unlikely(!__pyx_int_0x149357046d142e1e6e1948884dc976fdf)) __PYX_ERR(0, 1, __pyx_L1_error)

  长整数在编译之后储存的是的字符串,在使用的时候再将字符串转为数字。


小数

  写一个覆盖小数、字符串、布尔的函数

def decimalStringBool():
	var1=1.012
	var2="????"
	var3=True

  同样的我们找到函数的实现:

  /* "test.py":2
 * def decimalStringBool():
 * 	var1=1.012             # <<<<<<<<<<<<<<
 * 	var2="????"
 * 	var3=True
 */
  __pyx_v_var1 = 1.012;

  由此看来,不管小数多长,都会被当作double来存储。然后在ida中,我们看一下反编译的效果:


  好像并没有看到参数?

  这是因为xmm0是浮点数寄存器,一般浮点数都会放入xmm系列寄存器中计算,而在正常的程序参数列表中没有xmm寄存器,所以ida没有识别出来,我们查看qword_180006CC8指向地址就可以看到我们输入的浮点数


字符串

  全局字符串(以及函数名、一些具有特殊意义的字符串)赋值一般在_Pyx_CreateStringTabAndInitStrings中,该函数中使用的字符串定义数组形如:

static const char __pyx_k__4[] = "?";

然后在函数的实现中,对局部变量var2进行赋值。

  __Pyx_INCREF(__pyx_kp_s_);
  __pyx_v_var2 = __pyx_kp_s_;

  部分字符串变量也有通过__Pyx_StringTabEntry的数组进行初始化的。

  不管是反编译还是c的代码,其字符串都是通过偏移量的形式指向该字符串。在后续调用中,调用到“????”字符串的地方都会用偏移量指代,这也是为什么对字符串交叉引用找不到真正调用地方的原因。


布尔值

  布尔值就是直接赋值了。在c中是直接赋值的0或者1,在反编译的文件中是调用了接口。



数据结构

  首先写一个能够覆盖所有数据结构的函数。

def dataStruct():
    var = 123456789
    var1 = [9999, 1.012, -437593479587349875983475987349587324895, 340759348759834759853842759, "hello", False, var]
    var2 = []
    var3 = (1, 2, 3)
    var4 = {1, 2, 3, 4}
    var5 = {"key1": "value1", "key2": "value2"}
    print(var1, var2, var3, var4, var5)


dataStruct()

列表

  列表中的内容全部在其他函数 __Pyx_InitConstants(void)中进行了初始化,然后放入了off_180009668数据块中统一管理

初始化元素:

static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {
  if (__Pyx_CreateStringTabAndInitStrings() < 0) __PYX_ERR(0, 1, __pyx_L1_error);
  __pyx_float_1_012 = PyFloat_FromDouble(1.012); if (unlikely(!__pyx_float_1_012)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_3 = PyInt_FromLong(3); if (unlikely(!__pyx_int_3)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_9999 = PyInt_FromLong(9999); if (unlikely(!__pyx_int_9999)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_0x119de994f9e3728a1652147 = PyInt_FromString((char *)"0x119de994f9e3728a1652147", 0, 0); if (unlikely(!__pyx_int_0x119de994f9e3728a1652147)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_large_neg_43759347958734_xxx_475987349587324895 = PyInt_FromString((char *)"-437593479587349875983475987349587324895", 0, 0); if (unlikely(!__pyx_int_large_neg_43759347958734_xxx_475987349587324895)) __PYX_ERR(0, 1, __pyx_L1_error)
  return 0;
  __pyx_L1_error:;
  return -1;
}

列表赋值:

  /* "test.py":3
 * def dataStruct():
 *     var = 123456789
 *     var1 = [9999, 1.012, -437593479587349875983475987349587324895, 340759348759834759853842759, "hello", False, var]             # <<<<<<<<<<<<<<
 *     var2 = []
 *     var3 = (1, 2, 3)
 */  
  __pyx_t_1 = __Pyx_PyInt_From_long(__pyx_v_var); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = PyList_New(7); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 3, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(__pyx_int_9999);
  __Pyx_GIVEREF(__pyx_int_9999);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 0, __pyx_int_9999)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_INCREF(__pyx_float_1_012);
  __Pyx_GIVEREF(__pyx_float_1_012);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 1, __pyx_float_1_012)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_INCREF(__pyx_int_large_neg_43759347958734_xxx_475987349587324895);
  __Pyx_GIVEREF(__pyx_int_large_neg_43759347958734_xxx_475987349587324895);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 2, __pyx_int_large_neg_43759347958734_xxx_475987349587324895)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_INCREF(__pyx_int_0x119de994f9e3728a1652147);
  __Pyx_GIVEREF(__pyx_int_0x119de994f9e3728a1652147);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 3, __pyx_int_0x119de994f9e3728a1652147)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_INCREF(__pyx_n_s_hello);
  __Pyx_GIVEREF(__pyx_n_s_hello);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 4, __pyx_n_s_hello)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_INCREF(Py_False);
  __Pyx_GIVEREF(Py_False);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 5, Py_False)) __PYX_ERR(0, 3, __pyx_L1_error);
  __Pyx_GIVEREF(__pyx_t_1);
  if (__Pyx_PyList_SET_ITEM(__pyx_t_2, 6, __pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error);
  __pyx_t_1 = 0;
  __pyx_v_var1 = ((PyObject*)__pyx_t_2);
  __pyx_t_2 = 0;
 /* "test.py":4
 *     var = 123456789
 *     var1 = [9999, 1.012, -437593479587349875983475987349587324895, 340759348759834759853842759, "hello", False, var]
 *     var2 = []             # <<<<<<<<<<<<<<
 *     var3 = (1, 2, 3)
 *     var4 = {1, 2, 3, 4}
 */
  __pyx_t_2 = PyList_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 4, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_v_var2 = ((PyObject*)__pyx_t_2);
  __pyx_t_2 = 0;

  首先PyList_New(7) 创建了一个长度为 7 的空列表,使用 __Pyx_PyList_SET_ITEM 为列表的每个位置设置值。对应的值通过 __Pyx_INCREF__Pyx_GIVEREF 管理引用计数的不多解释。对于不同的数据类型,处理方式与上文差不多。比如长整数__pyx_int_large_neg_43759347958734_xxx_475987349587324895 表示一个大负数。布尔 使用 Py_FalsePy_True 来表示 FalseTrue。字符串 "hello" 被转换为 __pyx_n_s_hello静态字符串对象。

  在ida里看一下就是先PyList_New(7)然后直接用数组赋值



元组

  元组和列表类似,但是会先打包成tuple,保证了元组内容不可变性(回忆上篇文章,在函数调用的时候)

  /* "test.py":5
 *     var1 = [9999, 1.012, -437593479587349875983475987349587324895, 340759348759834759853842759, "hello", False, var]
 *     var2 = []
 *     var3 = (1, 2, 3)             # <<<<<<<<<<<<<<
 *     var4 = {1, 2, 3, 4}
 *     var5 = {"key1": "value1", "key2": "value2"}
 */
  __pyx_tuple_ = PyTuple_Pack(3, __pyx_int_1, __pyx_int_2, __pyx_int_3); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);

  在打包前先拿到数据_pyx_int_1 _pyx_int_2 _pyx_int_3 _pyx_int_4都是从长整型转换而来的,对应元组三个元素1,2,3 。这第四个元素是接下来集合用到的。(跟列表用的初始化函数是一个哦 包括下文集合的前三个元素也是1,2,3 所以直接拿来用 编译器又不傻,相同的工作做一次就好了)

static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {
  if (__Pyx_CreateStringTabAndInitStrings() < 0) __PYX_ERR(0, 1, __pyx_L1_error);
  __pyx_float_1_012 = PyFloat_FromDouble(1.012); if (unlikely(!__pyx_float_1_012)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_3 = PyInt_FromLong(3); if (unlikely(!__pyx_int_3)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_9999 = PyInt_FromLong(9999); if (unlikely(!__pyx_int_9999)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_0x119de994f9e3728a1652147 = PyInt_FromString((char *)"0x119de994f9e3728a1652147", 0, 0); if (unlikely(!__pyx_int_0x119de994f9e3728a1652147)) __PYX_ERR(0, 1, __pyx_L1_error)
  __pyx_int_large_neg_43759347958734_xxx_475987349587324895 = PyInt_FromString((char *)"-437593479587349875983475987349587324895", 0, 0); if (unlikely(!__pyx_int_large_neg_43759347958734_xxx_475987349587324895)) __PYX_ERR(0, 1, __pyx_L1_error)
  return 0;
  __pyx_L1_error:;
  return -1;
}

  ida中也用的偏移量,不过没关系,上面给出了偏移量的赋值(偏移量最良心的一局)



集合

  /* "test.py":6
 *     var2 = []
 *     var3 = (1, 2, 3)
 *     var4 = {1, 2, 3, 4}             # <<<<<<<<<<<<<<
 *     var5 = {"key1": "value1", "key2": "value2"}
 *     print(var1, var2, var3, var4, var5)
 */
  __pyx_t_2 = PySet_New(0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  if (PySet_Add(__pyx_t_2, __pyx_int_1) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
  if (PySet_Add(__pyx_t_2, __pyx_int_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
  if (PySet_Add(__pyx_t_2, __pyx_int_3) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
  if (PySet_Add(__pyx_t_2, __pyx_int_4) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
  __pyx_v_var4 = ((PyObject*)__pyx_t_2);
  __pyx_t_2 = 0;

  管你几个元素,新建的时候都是PySet_New(0)新建一个空集合,然后挨个PySet_Add()添加进集合中(为了集合元素的互异性)


字典

  /* "test.py":7
 *     var3 = (1, 2, 3)
 *     var4 = {1, 2, 3, 4}
 *     var5 = {"key1": "value1", "key2": "value2"}             # <<<<<<<<<<<<<<
 *     print(var1, var2, var3, var4, var5)
 * 
 */
  __pyx_t_2 = __Pyx_PyDict_NewPresized(2); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 7, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_key1, __pyx_n_s_value1) < 0) __PYX_ERR(0, 7, __pyx_L1_error)
  if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_key2, __pyx_n_s_value2) < 0) __PYX_ERR(0, 7, __pyx_L1_error)
  __pyx_v_var5 = ((PyObject*)__pyx_t_2);
  __pyx_t_2 = 0;

  字典就没什么好说的了,,使用PyDict_New创建空字典,PyDict_SetItem函数是添加字典中的键值。


标签:pyx,Cython,二进制,L1,int,__,error,PYX,数据结构
From: https://www.cnblogs.com/PaperPlaneFly/p/18598034/Cython_Variables_and_Data_Structures

相关文章

  • 渗透利器-kali工具 (第四章-3) Python数据结构学习
    数据结构:拥有足够多的东西,需要一个存储东西的容器,数据越多,就会越重视数据结构的作用。1.列表、元素、字典、集合:1.列表:[]1.列表最显著的特征是:1.列表中的每一个元素都是可变的;2.列表中的元素是有序的,也就是每一个元素都一个位置;......
  • 【数据结构与算法】回溯算法:LeetCode“排列问题” 求解,解释并模拟递归+回溯的遍历过程
      【作者自述:记录学习笔记,既然写了就让更多的人看到吧!欢迎大家关注交流学习,一步一个脚印持续更新!】【更多推荐笔记】【数据结构与算法】动态规划:解密“完全背包问题”的真相!附LeetCode四大问题的实现-CSDN博客【数据结构与算法】动态规划:解密“0-1背包问题”的真相!附LeetC......
  • 数据结构6.4——归并排序
    基本思想:归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为,称为二路归并。核心思想:将两个已经排好序的数组,合成......
  • Python知识分享第二十一天-数据结构入门
    数据结构“”"基础概念:程序=数据结构+算法数据结构=存储和组织数据的方式.算法=解决问题的思维,思路,方式.算法的特性:独立性:算法=思维,是解决问题的思路和方式,不依赖语言.5大特性:有输入,有输出,有穷性,确定性,可行性.问:如何衡......
  • 【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
    目录......
  • 【数据结构】 堆(二叉堆)详解
    定义:堆的底层数据结构是树,一般不引起歧义的情况下,堆指的是二叉堆,其底层数据结构是完全二叉树,堆分为大根堆和小根堆,大根堆的每个节点的父亲都大于当前节点,小根堆反之,本文以小根堆为例二叉堆插入思路:将要插入的树放在数组最后,令数组原来的大小为\(size\),堆数组的名为\(heap\)......
  • 数据结构--排序
    排序是计算机科学与技术领域中的一项基本操作,旨在将一组数据按某种顺序排列。以下是几种常见排序算法的具体解释:一、冒泡排序(BubbleSort)工作原理冒泡排序算法的工作原理如下:比较相邻的元素。如果第一个比第二个大(对于升序排序,如果是降序则相反),就交换它们两个。对每一对......
  • java 打印整数的二进制数
    任何类型的数在计算机底层存储都是以二进制的形式,那么如何知道一个数的二进制数是多少呢?lpublicclassPrintBinary{publicstaticvoidprintBinary(Objectnum){if(num==null){return;}if(numinstanceofInteger){......
  • 数据结构实验8
    1#include<iostream>2#include<string>3usingnamespacestd;4#defineMax_size10056voidselectSort(int*arr,intn)//简单选择排序7{8inti,j,k,temp;9for(i=0;i<n;i++){10k=i;11for(j......
  • 求解赫夫曼编码的算法 数据结构算法6.12、6.13
    一.问题描述定义赫夫曼树和赫夫曼编码的存储结构,并写出求解赫夫曼编码的算法。二.问题分析1.赫夫曼树的逻辑结构赫夫曼树(HuffmanTree)是一种用于数据压缩的二叉树,也称为最优二叉树。其逻辑结构主要包括以下特点:节点类型:赫夫曼树包含两种类型的节点,即内部节点(也称为非叶子......