python - 使用 ctypes 模块和 GDB 会话更改内存中的 Python 整数
问题描述
我的问题是基于这个reddit 帖子。那里的示例显示了如何使用模块中cast
的函数更改内存中的整数ctypes
:
>>> import ctypes
>>> ctypes.cast(id(29), ctypes.POINTER(ctypes.c_long))[3] = 100
>>> 29
100
我对这里的低级内部感兴趣,我已经通过在以下cast
函数上设置断点在 GDB 会话中检查了这一点CPython
:
(gdb) break cast
Function "cast" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (cast) pending.
(gdb) run test.py
Starting program: /root/.pyenv/versions/3.8.0-debug/bin/python test.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
0x7ffff00e7b40
Breakpoint 1, cast (ptr=0x9e6e40 <small_ints+1088>, src=10382912, ctype=<_ctypes.PyCPointerType at remote 0xa812a0>) at /root/.pyenv/sources/3.8.0-debug/Python-3.8.0/Modules/_ctypes/_ctypes.c:5540
5540 if (0 == cast_check_pointertype(ctype))
(gdb) p *(PyLongObject *) ptr
$38 = {
ob_base = {
ob_base = {
ob_refcnt = 12,
ob_type = 0x9b8060 <PyLong_Type>
},
ob_size = 1
},
ob_digit = {100}
}
(gdb) p *((long *) ptr + 3)
$39 = 100
(gdb) p ((long *) ptr + 3)
$40 = (long *) 0x9e6e58 <small_ints+1112>
(gdb) p *((char *) ptr + 3 * 8)
$41 = 100 'd'
(gdb) p ((char *) ptr + 3 * 8)
$42 = 0x9e6e58 <small_ints+1112> "d"
(gdb) set *((long *) ptr + 3) = 29
(gdb) p *((long *) ptr + 3)
$46 = 29
(gdb) p *((char *) ptr + 3 * 8)
$47 = 29 '\035'
我想知道是否可以在 GDB 会话中使用 Python 获取内存地址,因为我无法访问返回的地址:
(gdb) python print("{:#x}".format(ctypes.addressof(ctypes.c_int(29))))
0x7f1053c947f0
(gdb) python print("{:#x}".format(id(29)))
0x22699d8
(gdb) p *0x7f1053c947f0
Cannot access memory at address 0x7f1053c947f0
(gdb) p *0x22699d8
Cannot access memory at address 0x22699d8
索引也与 Python REPL 不同,我猜这与字节序有关?
(gdb) python print(ctypes.cast(id(29), ctypes.POINTER(ctypes.c_long))[3])
9
(gdb) python print (ctypes.cast(id(29), ctypes.POINTER(ctypes.c_long))[2])
29
问题:
- 为什么 GDB 会话中来自 Python 的内存地址不可访问,值不在进程内存范围内(
info proc mappings
)? - 为什么索引与 Python REPL 不同?
- (奖金问题)我希望函数
src
中的参数CPython
cast
包含对象的地址,但它似乎是ptr
相反,并且在memcpyresult->b_ptr
指向不同的值之后&ptr
?这是实际的演员阵容吗?
解决方案
- 您的 Python 进程不是真正的 Python 进程,而是 GDB 正在为您运行 Python REPL。把它想象成 GDB 中的另一个线程。当然,这是一个简化,您应该查看文档
- 我无法重现这种行为:
我想不出这种行为会发生的任何原因(至少是字节序,在整个系统中都是一样的*)(gdb) python >import ctypes >print(ctypes.cast(id(29), ctypes.POINTER(ctypes.c_long))[3]) >end 29
- 该
src
参数似乎用作原始类型,而不是原始对象。作为参考,请参阅ctypes.h和ctypes/__init__.py(_SimpleCData 只是 CDataObject 与一些帮助,如索引和 repr)。是的,在这种情况下,memcpy 是实际的转换,尽管如果您在两种数据类型之间进行转换,则需要事先进行额外的工作。
* 除了在 ARM 上,您可以通过指令更改字节顺序
推荐阅读
- php - 获取多维 PHP 数组大小的最便宜方法
- ionic-framework - Ionic 3 - 如何打开富文本内容的 url (hrefs)
- java - 从基于 Java 的应用程序的角度来看,安装在服务器上的 oracle 客户端的目的是什么
- android - 在相同 SSID AP 之间切换时 Android 网络延迟
- visual-studio - 关于正确使用 TFS 发布 Web 应用程序的说明
- odoo - 如何在odoo的one2many中隐藏字段
- amazon-web-services - 何时通过 AWS Glue ETL 使用 Amazon Redshift 频谱来查询 Amazon S3 数据
- javascript - 将常规二维矩形坐标转换为梯形
- wpf - 如何将 Tooltip 的 DataTemplate 绑定到其父级?
- php - 如何在 woocommerce wordpress 中使用 xml 填充自定义列