如果您想了解如何访问存储在通过ctypes返回给Python的C结构内的数组中的值?的相关知识,那么本文是一篇不可错过的文章,我们将对如何访问存储器中的数据进行全面详尽的解释,并且为您提供关于ctyp
如果您想了解如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?的相关知识,那么本文是一篇不可错过的文章,我们将对如何访问存储器中的数据进行全面详尽的解释,并且为您提供关于ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行、ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?、CVE-2021-3177:Python ctypes 缓冲区溢出漏洞分析、Python 3.5,ctypes:TypeError:应使用字节或整数地址(而非str实例)的有价值的信息。
本文目录一览:- 如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?(如何访问存储器中的数据)
- ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行
- ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?
- CVE-2021-3177:Python ctypes 缓冲区溢出漏洞分析
- Python 3.5,ctypes:TypeError:应使用字节或整数地址(而非str实例)
如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?(如何访问存储器中的数据)
如何解决如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?
我正在尝试学习使用 ctypes
填充 C 结构的链接列表并将列表的头部返回给 Python 进行后期处理。为此,我有一个简单的 C 结构,定义如下:
struct Checkpoint
{
double *state;
struct Checkpoint *next;
};
typedef struct Checkpoint checkpoint;
我有一个简单的匹配 Python 3 类,定义如下:
class Checkpoint(ct.Structure):
pass
Checkpoint._fields_ = [(''state'',ct.POINTER(ct.c_double)),(''next'',ct.POINTER(Checkpoint))]
在通过 ctypes 调用 C 代码以填充此结构的一些示例后,我尝试在 python 端打印 state
数组中的值。
def main():
# Load the shared library into c types.
libc = ct.CDLL("./structs.so")
entry = wrap_function(libc,''pymain'',ct.POINTER(Checkpoint),None)
get_state = wrap_function(libc,''get_state'',ct.POINTER(ct.c_double),[ct.POINTER(Checkpoint)])
get_next = wrap_function(libc,''get_next'',[ct.POINTER(Checkpoint)])
start = entry()
print(get_state(start).contents);
start = get_next(start)
print(get_state(start).contents);
get_state
和 get_next
函数如下:
double *get_state(checkpoint *current)
{
return current->state;
}
checkpoint *get_next(checkpoint *current)
{
return current->next;
}
这些函数正在返回对正确内存的引用,但我无法访问如此定义的结构中数组第一个元素之外的任何内容。
两个问题:
-
我是否需要
get_state
和get_next
函数,还是可以在定义和填充结构后直接在 Python 中访问这些参数而无需回调 C?如果是这样,如何? -
我在 python 中的
main
函数可以按照我的需要填充结构,但state
调用仅打印print
数组的第一个值。如何访问数组中的其余值?
解决方法
回答您的第一个问题,我认为 get_state
和 get_next
不是必需的,因为您可以直接访问 Checkpoint 的字段
start = entry()
state = start.contents.state
next = start.contents.next
关于第二个问题,在指针的情况下,.contents
将提供存储在指针内存地址的值。这意味着它只会打印数组中的第一项而不是整个数组。要打印整个数组,您必须循环遍历它,打印每个元素。当只使用指针时,数组的大小是未知的,所以第一步是存储这个计数。我认为 Checkpoint 结构将是一个不错的位置。
struct Checkpoint
{
double *state;
int state_len;
struct Checkpoint *next;
};
typedef struct Checkpoint checkpoint;
然后,您需要更新python类
class Checkpoint(ct.Structure):
pass
Checkpoint._fields_ = [(''state'',ct.POINTER(ct.c_double)),(''state_len'',ct.c_int),(''next'',ct.POINTER(Checkpoint))]
我不确定您的 state
数组在哪里填充,但您需要在添加元素的任何位置更新 state_len
。
一旦完成,您应该能够使用如下循环打印内容:
start = entry()
for i in range(start.contents.state_len):
print(start.contents.state[i])
ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行
如何解决ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行
我有一个关于 ctypes 的使用的奇怪问题: ctypes 的行为取决于我是使用 anaconda 的 python 安装还是 python.org 的“原始”python 安装(均在 Windows 10 上)。
复制:只需转到 anaconda.com,通过默认安装程序安装 anaconda(和 anaconda python)。转到 python.org 并通过默认安装程序安装 python。
anaconda 安装附带了 mingw(x64) 二进制文件,这些文件至少是一些 .dll 所需的(我在这里不太了解,但至少我正在使用的 dll 需要它们)。 “原始”python 没有这些,因此需要手动添加它们(需要部分或全部(未检查):libatomic-1.dll、libgccc_s_seh-1.dll、libgomp-1.dll、libquadmath- 0.dll、libssp-0.dll、libstdc++-6.dll、libwinpthread-1.dll,只需将它们复制到您正在工作的目录中或将它们的目录添加到PATH)。
现在您有两个版本的 python,每个版本都有自己的 ctypes 版本(我的 anaconda python 版本为 3.8.8,“raw”python 版本为 3.9.5,ctypes 版本均为 1.1.0)。>
尝试通过 anaconda python 加载 dll,例如:
import ctypes
dll = ctypes.CDLL(<insertdllname>)
完全没问题(前提是你在dll的目录下工作,否则你需要指定完整路径)。
尝试相同的“原始”python 安装失败。问题是来自“原始”python 的 ctypes 找不到要加载的 dll 所依赖的 mingw 库。尽管这些库位于工作目录中(或明确地将它们的文件夹添加到 PATH)(两者都在使用 anaconda python 时工作)。仅当将附加库放入 python 安装文件夹(“python.exe”所在的位置)时,才会找到它们。
现在,当我将 ctypes 文件夹从 anaconda 安装复制到“raw”安装的 ctypes 文件夹所在的位置(将 ctypes 从“raw”安装重命名为 ctypes_raw)时,我有两个版本的 ctypes 可用。
使用:
import ctypes_roh
ctypes_roh.CDLL(<insertdllname>)
失败,如上。
import ctypes
ctypes.CDLL(<insertdllname>)
有效,所以显然两个 ctypes 库是不同的,anaconda 安装中的 ctypes 能够在工作目录和 PATH 中查找 dll,而“原始”python 安装中的 ctypes 不能这样做。
import ctypes
import ctypes_raw
ctypes.CDLL(<insertdllname>)
ctypes_raw.CDLL(<insertdllname>)
现在两个调用都有效。显然,从 anaconda ctypes 调用 CDLL 修复了阻止 CDLL 从“原始”ctypes 在工作目录或 PATH 中查找 dll 的任何问题。
所以我的问题是:
- python anaconda 安装中的 ctypes 和 python.org 中的“原始”python 安装中的 ctypes 是否应该不同?
- 有什么区别?为什么来自“原始”python 的 ctypes 无法在工作目录或 PATH 中查找 dll?为什么从 anaconda python 调用 ctypes 会“修复”这个问题?
编辑:在发现“原始”python 安装没有所需的 mingw 库后,我重写了帖子以说明这一点并(希望)澄清剩余的问题。
ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?
如何解决ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?
这两种方法似乎都有效(对我来说),但似乎 CDLL()
方法返回一个具有 _handle
属性的对象,该对象可用于通过 ctypes.windll.kernel32.FreeLibrary()
卸载库(至少在 Windows 上 - 我还不知道如何在 Linux 上做到这一点)。
这两种方法有什么区别 - 为什么我会选择一种而不是另一种?
最终,我的目标是能够在 Linux 上的两个 Windows 上加载和卸载库(因为我有一个 3rd 方库,有时似乎会进入损坏状态 - 我希望卸载/重新加载会重置它) .
解决方法
一切都在[Python.Docs]: ctypes - A foreign function library for Python中得到了很好的解释:
class ctypes.CDLL(名称,mode=DEFAULT_MODE,handle=None,use_errno=False,use_last_error=False,winmode=0 )
此类的实例表示加载的共享库。这些库中的函数使用标准的 C 调用约定,并假定返回 int。
...
class ctypes.LibraryLoader(dlltype)
...
LoadLibrary(名称)
将共享库加载到进程中并返回。此方法始终返回库的新实例。
这些预制的库加载器可用:
ctypes.cdll
创建 CDLL 个实例。
所以,第 2nd 表单只是一个方便的包装器,它们之间绝对没有功能上的区别,如下所示:
>>> import ctypes as ct
>>>
>>>
>>> k32_1 = ct.CDLL("kernel32.dll") # 1.
>>> k32_2 = ct.cdll.LoadLibrary("kernel32.dll") # 2.1.
>>> k32_3 = ct.cdll.kernel32 # 2.2.
>>>
>>> k32_1,k32_2,k32_3
(<CDLL ''kernel32.dll'',handle 7fff59100000 at 0x2335c444ee0>,<CDLL ''kernel32.dll'',handle 7fff59100000 at 0x2335b44bc10>,<CDLL ''kernel32'',handle 7fff59100000 at 0x2335c45a790>)
>>> type(k32_1),type(k32_2),type(k32_3)
(<class ''ctypes.CDLL''>,<class ''ctypes.CDLL''>,<class ''ctypes.CDLL''>)
>>>
>>> k32_1._handle == k32_2._handle == k32_3._handle
True
使用最适合您的方式。
2nd 形式(#2.2.)更短(我想这就是它的目的)。
#1. 和 #2.1. 是相同的(#2.1. 可能更具解释性(因为它有 LoadLibrary)) 并且它们允许您从自定义路径或具有与默认值不同的扩展名加载库。就我个人而言,#1. 是我更喜欢的。
更多细节,你可以看看[GitHub]: python/cpython - (master) cpython/Lib/ctypes/__init__.py,特别是LibraryLoader在实现中(cdll实际上是,并且)很容易理解.
提醒一下(可能您已经知道自己在做什么):加载和卸载库有时会很棘手。
CVE-2021-3177:Python ctypes 缓冲区溢出漏洞分析
起步
相关的 issue 与 修复 ,从描述来看,这是一个 sprintf
函数引发的缓冲区溢出漏洞。
补丁获取链接:https://python-security.readthedocs.io/vuln/ctypes-buffer-overflow-pycarg_repr.html
复现
使用 3.8.7
进行复现:
这个漏洞在 3.8.8
便已修复:
分析
通过生成的 coredump
文件,查看异常堆栈:
注意到 Python-3.8.7/Modules/_ctypes/callproc.c
:
PyCArg_repr(PyCArgObject *self)
{
char buffer[256];
switch(self->tag) {
...
case ''d'':
sprintf(buffer, "<cparam ''%c'' (%f)>", // 这行引发异常
self->tag, self->value.d); // value.d 的值是 1e300
break;
变量 buffer
的长度为 256 ,因此长度不够使得 sprintf
的缓冲区溢出,造成内存的破坏。
写个简单的 C 代码复现这个问题:
#include <stdio.h>
int main(int argc, char **argv) {
char buffer[256];
char tag = ''d'';
double value = 1e300;
sprintf(buffer, "<cparam ''%c'' (%f)>", tag, value);
return 0;
}
我在中间加了一行 printf("%f\n", value);
再次执行:
这个长度已经超过 buffer
定义的长度了。double
类型,表示的小数位数能很大,所以至少可以覆盖 300 个字节;如果是 float
类型,就要小的多了。
在新版本之中该问题已经得到修复,已经将 sprintf
换成 PyUnicode_FromFormat
。
参考链接
- https://bugs.python.org/issue42938
- https://blog.python.org/2021/02/python-392-and-388-are-now-available.html
- https://www.randori.com/blog/cve-2021-3177-vulnerability-analysis/
- http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-202101-1467
- https://access.redhat.com/security/cve/cve-2021-3177
Python 3.5,ctypes:TypeError:应使用字节或整数地址(而非str实例)
我的ctypes有问题。我认为我的类型转换是正确的,并且该错误对我来说没有意义。第“ arg-ct.c_char_p(logfilepath)行错误”
TypeError:预期为字节或整数地址,而不是str实例
我在python 3.5和3.4中都尝试过。
我正在调用的功能:
stream_initialize(''stream_log.txt'')
Stream_initialize代码”
def stream_initialize(logfilepath): f = shim.stream_initialize arg = ct.c_char_p(logfilepath) result = f(arg) if result: print(find_shim_error(result))
答案1
小编典典c_char_p
需要bytes
对象,因此您必须先将其转换string
为bytes
:
ct.c_char_p(logfilepath.encode(''utf-8''))
另一种解决方案是使用c_wchar_p
带有的类型string
。
今天关于如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?和如何访问存储器中的数据的介绍到此结束,谢谢您的阅读,有关ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行、ctypes.CDLL() 和 ctypes.cdll.LoadLibrary() 有什么区别?、CVE-2021-3177:Python ctypes 缓冲区溢出漏洞分析、Python 3.5,ctypes:TypeError:应使用字节或整数地址(而非str实例)等更多相关知识的信息可以在本站进行查询。
本文标签: