GVKun编程网logo

如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?(如何访问存储器中的数据)

1

如果您想了解如何访问存储在通过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 返回给 Python 的 C 结构内的数组中的值?(如何访问存储器中的数据)

如何解决如何访问存储在通过 ctypes 返回给 Python 的 C 结构内的数组中的值?

我正在尝试学习使用 ctypes 填充 C 结构的链接列表并将列表的头部返回给 Python 进行后期处理。为此,我有一个简单的 C 结构,定义如下:

  1. struct Checkpoint
  2. {
  3. double *state;
  4. struct Checkpoint *next;
  5. };
  6. typedef struct Checkpoint checkpoint;

我有一个简单的匹配 Python 3 类,定义如下:

  1. class Checkpoint(ct.Structure):
  2. pass
  3. Checkpoint._fields_ = [(''state'',ct.POINTER(ct.c_double)),(''next'',ct.POINTER(Checkpoint))]

在通过 ctypes 调用 C 代码以填充此结构的一些示例后,我尝试在 python 端打印 state 数组中的值。

  1. def main():
  2. # Load the shared library into c types.
  3. libc = ct.CDLL("./structs.so")
  4. entry = wrap_function(libc,''pymain'',ct.POINTER(Checkpoint),None)
  5. get_state = wrap_function(libc,''get_state'',ct.POINTER(ct.c_double),[ct.POINTER(Checkpoint)])
  6. get_next = wrap_function(libc,''get_next'',[ct.POINTER(Checkpoint)])
  7. start = entry()
  8. print(get_state(start).contents);
  9. start = get_next(start)
  10. print(get_state(start).contents);

get_stateget_next 函数如下:

  1. double *get_state(checkpoint *current)
  2. {
  3. return current->state;
  4. }
  5. checkpoint *get_next(checkpoint *current)
  6. {
  7. return current->next;
  8. }

这些函数正在返回对正确内存的引用,但我无法访问如此定义的结构中数组第一个元素之外的任何内容。

两个问题:

  1. 我是否需要 get_stateget_next 函数,还是可以在定义和填充结构后直接在 Python 中访问这些参数而无需回调 C?如果是这样,如何?

  2. 我在 python 中的 main 函数可以按照我的需要填充结构,但 state 调用仅打印 print 数组的第一个值。如何访问数组中的其余值?

解决方法

回答您的第一个问题,我认为 get_stateget_next 不是必需的,因为您可以直接访问 Checkpoint 的字段

  1. start = entry()
  2. state = start.contents.state
  3. next = start.contents.next

关于第二个问题,在指针的情况下,.contents 将提供存储在指针内存地址的值。这意味着它只会打印数组中的第一项而不是整个数组。要打印整个数组,您必须循环遍历它,打印每个元素。当只使用指针时,数组的大小是未知的,所以第一步是存储这个计数。我认为 Checkpoint 结构将是一个不错的位置。

  1. struct Checkpoint
  2. {
  3. double *state;
  4. int state_len;
  5. struct Checkpoint *next;
  6. };
  7. typedef struct Checkpoint checkpoint;

然后,您需要更新python类

  1. class Checkpoint(ct.Structure):
  2. pass
  3. Checkpoint._fields_ = [(''state'',ct.POINTER(ct.c_double)),(''state_len'',ct.c_int),(''next'',ct.POINTER(Checkpoint))]

我不确定您的 state 数组在哪里填充,但您需要在添加元素的任何位置更新 state_len

一旦完成,您应该能够使用如下循环打印内容:

  1. start = entry()
  2. for i in range(start.contents.state_len):
  3. print(start.contents.state[i])

ctypes.CDLL() 可从 anaconda python 安装运行,但不能从“原始”python 安装运行

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,例如:

  1. import ctypes
  2. 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 可用。

使用:

  1. import ctypes_roh
  2. ctypes_roh.CDLL(<insertdllname>)

失败,如上。

  1. import ctypes
  2. ctypes.CDLL(<insertdllname>)

有效,所以显然两个 ctypes 库是不同的,anaconda 安装中的 ctypes 能够在工作目录和 PATH 中查找 dll,而“原始”python 安装中的 ctypes 不能这样做。

  1. import ctypes
  2. import ctypes_raw
  3. ctypes.CDLL(<insertdllname>)
  4. 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() 有什么区别?

如何解决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 表单只是一个方便的包装器,它们之间绝对没有功能上的区别,如下所示:

  1. >>> import ctypes as ct
  2. >>>
  3. >>>
  4. >>> k32_1 = ct.CDLL("kernel32.dll") # 1.
  5. >>> k32_2 = ct.cdll.LoadLibrary("kernel32.dll") # 2.1.
  6. >>> k32_3 = ct.cdll.kernel32 # 2.2.
  7. >>>
  8. >>> k32_1,k32_2,k32_3
  9. (<CDLL ''kernel32.dll'',handle 7fff59100000 at 0x2335c444ee0>,<CDLL ''kernel32.dll'',handle 7fff59100000 at 0x2335b44bc10>,<CDLL ''kernel32'',handle 7fff59100000 at 0x2335c45a790>)
  10. >>> type(k32_1),type(k32_2),type(k32_3)
  11. (<class ''ctypes.CDLL''>,<class ''ctypes.CDLL''>,<class ''ctypes.CDLL''>)
  12. >>>
  13. >>> k32_1._handle == k32_2._handle == k32_3._handle
  14. 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 缓冲区溢出漏洞分析

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实例)

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对象,因此您必须先将其转换stringbytes

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实例)等更多相关知识的信息可以在本站进行查询。

本文标签: