【问题标题】:Evil ctypes hack in python邪恶的 ctypes 在 python 中破解
【发布时间】:2014-06-05 13:02:49
【问题描述】:

首先我想说这个问题纯粹是出于兴趣,我绝不打算在任何严肃的项目中使用如此邪恶的东西。 (是的,就是这样的问题)

我一直试图在 CPython 的内部工作中拼凑一些信息,据我所知,应该可以操纵小整数的实际值,这样(对于例如)1 + 2 可以评估为 3 以外的东西。我几乎不是这种低级黑客的专家,我所能达到的只是段错误。这是我到目前为止所得到的:

import ctypes
ctypes.c_int8.from_address(id(1) + 8).value = 2

我的印象是这样可以解决问题,但这只会导致任何试图评估 1 的语句因段错误而崩溃。虽然这是一个有趣的成就,但这并不是我想要的。我错过了什么吗?难道该行中的 c_int8 和 + 8 仅适用于某些平台?如果我确切地知道要查找什么,我会很乐意查找此内容,尽管我认为答案可能隐藏在 CPython 源代码中的某个地方。

【问题讨论】:

  • 值不是只读的吗?
  • 嗯,值肯定可以写入,我知道的就这么多了。如果我尝试设置它,然后使用相同的语法再次读取 .value ,我确实得到了新值......并且只有在我实际尝试直接使用数字 1 时才会出现段错误。所以,它显然设法改变一些东西;)

标签: python ctypes low-level


【解决方案1】:

8 在 32 位平台上是“正确的”,其中 ob_refcntob_type 各占 4 个字节;在 64 位平台上,这将有所不同。本质上,您是在尝试将 PyObject_HEAD 传递到整数对象的其余部分,因此请尝试在编译器或调试器中检查 PyObject 的大小。

显然,这在 Python 3 上会有所不同,其中只有 long 类型,所以即使是小整数也是可变长度的;在这种情况下,您需要PyObject_VAR_HEAD(和PyVarObject)而不是PyObject_HEAD

一个很好的起点是object.h 中的文档,也可以在https://docs.python.org/2/c-api/structures.html 的C API 参考手册中阅读,然后在intobject.hlongintrepr.h 阅读Python 3。


注意:更改1 的值仍然会出现段错误,但原因不同。不过,更改较大的小整数(例如 10)的值应该是安全的。

【讨论】:

  • 这听起来正是我正在寻找的信息 :) 谢谢!直到今天晚些时候我才能对其进行测试,但是当我得到它的工作时,我会接受并扩展答案。
  • 这通常不起作用,即使在 2.x 中使用正确定义的 PyIntObject 结构,以确保正确的偏移量和数据类型。 Python 代码和解释器实现中有无数对这些小整数的引用,如果您尝试修改int(1),无疑会触发段错误。查找没有现有引用的小整数,例如PyIntObject.from_address(id(251)).ob_ival = 1000; 251 + 1 == 1001
  • 为了“有趣”,您可以在调试器下运行 Python 来跟踪崩溃。如果您修改int(10),请尝试exec '["a","b","c","d","e", "f","g","h","i","j"]',它将None 移出到代码对象的co_consts[10]。编译器使用常量字典,因此当makecode 尝试将常量排序为列表时,这可能会在dict_keys_inorderlistextend 中崩溃。
  • @eryksun 是的,这就是我得到的:) - 似乎问题是PyDict_Next 将序数作为 Python 整数提供给您,所以当它尝试使用它时,它的值是错误的。不过,交换45 可能会很有趣。
  • 是的,存储在字典中的序号是int。因此,如果您将int(10) 更改为一个较大的值,您可能会在dict_keys_inorder 中得到段错误,因为PyTuple_SET_ITEM 试图写入未分配的地址。较小的值(例如 100)可能不会出现段错误。但是现在索引 10 处的 PyObject * 是随机垃圾。当listextend 尝试在其上执行Py_INCREF 时,您将在那里得到一个段错误。
猜你喜欢
  • 2010-11-30
  • 2010-10-11
  • 1970-01-01
  • 2016-11-18
  • 2011-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多