IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> 3-折腾python:1+1=3 -> 正文阅读

[Python知识库]3-折腾python:1+1=3

目标

今天的目标很简单:让python的计算出现奇奇怪怪的"错误".
从今天起,我们正式跨入python虚拟机 运行时部分 的恶搞.

vs的使用

要阅读python庞大的源码,你得拥有足够的耐心,因为他的函数调用特别复杂,甚至多达几十层才能实现一个在我们眼中特别简单的功能(如今天的恶搞对象:加减乘除).
这时候,我们的工具就得派上用场了:vs的查找.

这里可不是普普通通的查找哦,我们在一个单词上右键,然后点击Find all references,也就是"找到所有引用".下方就会显示出所有引用了这种对象的列表,如下:
点击"查找引用"
找到啦
因为vs的某些局限,你无法直接ctrl+单击查看函数的实现,这里为了搞事情我们得找到加法处理的函数,所以只能查找所有引用,然后找到代表着"实现声明式"的那一个,就像上图标蓝的.根据常识,我们知道其他的都是调用这个函数,因此找到实现只能这么办!
(或者用眼睛找个半个小时也不是不行,何况我自己找这个函数已经找了半个小时了)

恶作剧1:1+1=3

不想看过程的话,直接看最后,节约时间吧如果不想了解具体的话:传送
今天的练手恶搞啦.我们知道加法的英文,ADD,因此在我半个小时劳动成果的文件Python/ceval.c中查找字符串"ADD",(记得打开大写锁定,也就是说Add不会被搜索).
跟着直觉走,1+1应该属于二元运算,也就是BINARY,这么说,给出结果吧:Python/ceval.c:2068.相关case如下

PyObject *right = POP();
PyObject *left = TOP();
PyObject *sum;
/* NOTE(vstinner): Please don't try to micro-optimize int+int on
   CPython using bytecode, it is simply worthless.
   See http://bugs.python.org/issue21955 and
   http://bugs.python.org/issue10044 for the discussion. In short,
   no patch shown any impact on a realistic benchmark, only a minor
   speedup on microbenchmarks. */
if (PyUnicode_CheckExact(left) &&
         PyUnicode_CheckExact(right)) {
    sum = unicode_concatenate(tstate, left, right, f, next_instr);
    /* unicode_concatenate consumed the ref to left */
}
else {
    sum = PyNumber_Add(left, right);
    Py_DECREF(left);
}
Py_DECREF(right);
SET_TOP(sum);
if (sum == NULL)
    goto error;
DISPATCH();

最主要的是那个if判断句.前半部分,应该是字符串的拼接,后文会讲到.先看else,应该是处理数字相关(复数,浮点数和整数,都应该算在内).看到他调用了PyNumber_Add函数,也足以证明这是数字加法.
根据前一节讲到的操作技巧,一步步找出这个函数的实现,在Objects/abstract.c:1069,如下:

PyNumber_Add(PyObject *v, PyObject *w)
{
    PyObject *result = BINARY_OP1(v, w, NB_SLOT(nb_add), "+");
    if (result != Py_NotImplemented) {
        return result;
    }
    Py_DECREF(result);

    PySequenceMethods *m = Py_TYPE(v)->tp_as_sequence;
    if (m && m->sq_concat) {
        result = (*m->sq_concat)(v, w);
        assert(_Py_CheckSlotResult(v, "+", result != NULL));
        return result;
    }

    return binop_type_error(v, w, "+");
}

关注到那个if (result != Py_NotImplemented),猜着就是正常的情况,因为有个not,然后是不等于,因此最常用的分支应该在这里了,也就是正常返回.result是个PyObject*,仍然一步步进去,找到BINARY_OP1宏展开式:(同文件)
L912!
找到binary_op1在同文件的L863函数体开始
简单翻译一下,就是"槽函数"(学过qt的,眼不眼熟?),通过对象的类型动态查找相应的方法,然后变成函数指针并调用,那么下面的任务就简单了,针对我们需要的1+1对象改掉它的槽函数不就完啦!
L865有一段,应该是数字处理的:

slotv = NB_BINOP(Py_TYPE(v)->tp_as_number, op_slot);

找到tp_as_number到底是个啥:object.h:208,发现是个PyNumberMethods指针.顾名思义,Methods后缀的结构体应该就是把这个对象的内置方法函数指针都存起来,然后供给虚拟机调用.跳到这个结构体的定义,在object.h:104
我们只关心第一个字段:nb_add(就是number 的 add 方法 的意思)
这里来个特殊手段,不能直接访问这个字段,而且我们也不应该这么做.为什么?因为我们要的是针对long的这个域,得想办法找到处理long的add方法.那怎么办?找引用呗.可以看见结果差不多是这样:
查找的结果
稍加思考,static静态的结构体,约摸着存的就是方法映射表.按照它的命名,xxx_as_number就是xxx类型的number方法映射表.很见到找到long的部分:
在这里!
转跳到了Objects/longobject.c:5585看来很有希望嘛,你们看第一个字段填的啥?没错!long_add处理方法!

终于找到了…

找到它的实现,如下,在Objects/longobject.c:3064.根据实践咱只用看最开头的那个if成立部分:

return PyLong_FromLong(MEDIUM_VALUE(a) + MEDIUM_VALUE(b));

看出来没?那么大个加号诶!ok开始动手脚.比如说…加个+1?
像这样,
来吧看看效果:
成功啦!
可见整型运算确实…哈哈哈字符串不会受影响.这时候把这解释器打包给朋友人家还以为电脑抽风呢…(使坏)

折腾下str吧

终极目标:在每个字符串的拼接处加一个"!#!"
还是一样,不想看过程跳最后:转跳

看到刚刚发现的那个BINARY_ADD case,没讲到的那个if分支就是处理UTF-8拼接的.转跳到Python/ceval.c:6313unicode_concatenate函数.不管前面的switch,那是特殊情况.只看后面主调用PyUnicode_Append(&res, w)

这个函数体我看了下,只需要关注下面的部分Objects/unicodeobject.c:11850
代码段
把这个函数名翻译一下就知道怎么做了(滑稽);多跳几次函数调用,找到根在这里呢,同文件的L1567,“复制字符(s,复数)”

找到str拼接的关键点啦

看到关键的L1620,memcpy调用.这个大家都熟悉吧,memcpy(dst, src, cnt)把src开始的cnt个字节复制到dst处
在这里动手脚,思路是加一段把"!#!"复制到to的结尾,然后指针后推三个字节,再复制from.但是经过实际测试我发现啊,这样做根本行不通.因为如果你在这里加个printf就会发现啊,python虚拟机在初始化的时候也会调用这个函数特别多次,那么如果我们在这里植入漏洞,就会害惨了自己,所以还是跳过这个部分吧…

总结

今天还算是成功,虽然str拼接的漏洞没玩成功,但int加法自动+1导致的1+1=3已经足够好玩了(笑).今天利用这个机会,我们第一次深入python运行时,大致在寻找一层层的函数调用中,摸索出虚拟机运行的原理和约定(当然如果你跳过细节就会错过这些).在把1+1变成3的过程中,我们学会vs的一个重要功能:查找引用;也对python虚拟机源码有了一个更感性的认识.

点击查看上一章

  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-01-30 18:53:50  更:2022-01-30 18:54:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:58:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码