Home > front end >  How can I read back from a buffer using ctypes?
How can I read back from a buffer using ctypes?

Time:09-30

I have a third-party library, and I need to use one function in python script from it. Here it is:

ReadFromBlob(PVOID blob, INT blob_size, PCSTR section, PCSTR key, const void **buffer, UINT * size)
  • blob - some pointer? to bytes to read from
  • blob_size - blob size in bytes
  • section and key - string values like "Image"
  • buffer - bytes to read to
  • size - buffer size

The documentation gives an example of how to use it:

UINT size = 0;
PVOID buffer = NULL;
ReadFromBlob(<blob>, <blob_size>, "MainImage", "Image", &buffer, &size);

I'm not familiar with C, so argument types confusing me. I need to be able to read values from the buffer in python. This is what I have so far:

from ctypes import *
lib = cdll.LoadLibrary(path_to_lib)
with open(filepath, 'rb') as file:
    data = file.read()
blob_size = c_int(len(data))
blob = cast(c_char_p(data), POINTER(c_char * blob_size.value))
b = bytes()
size = c_uint(len(b))
buffer = cast(cast(b, c_void_p), POINTER(c_char * size.value))
lib.ReadFromBlob(blob, blob_size, b"MainImage", b"Image", buffer, pointer(size))

But I still get an empty buffer in the end. Please help me.

CodePudding user response:

Some documentation on the function would be helpful, otherwise it's just a matter of guessing. As a matter of fact, check [SO]: How to create a Minimal, Reproducible Example (reprex (mcve)).

Listing [Python.Docs]: ctypes - A foreign function library for Python.

The example (and the fact that buffer is a double pointer (**)), is a hint that the actual buffer is allocated by the function (so you'll have to call a deallocation function from the library, when you're done with it).

Also, you're missing a crucial step (described in [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)).

Anyway, I'll give it a try.

code00.py:

#!/usr/bin/env python

import ctypes as ct


path_to_lib = "path to lib"
lib = ct.CDLL(path_to_lib)
ReadFromBlob = lib.ReadFromBlob
ReadFromBlob.argtypes = (ct.c_void_p, ct.c_int, ct.c_char_p, ct.c_char_p, ct.POINTER(ct.c_void_p), ct.POINTER(ct.c_uint))
ReadFromBlob.restype = None  # Assuming the function returns void (which I doubt)

path_to_file = "path to file"
data = open(path_to_file, "rb").read()

buffer = ct.c_void_p()
buffer_size = ct.c_uint(0)

lib.ReadFromBlob(data, len(data), b"MainImage", b"Image", ct.byref(buffer), ct.byref(buffer_size))

print(bool(buffer))

# ...

CodePudding user response:

It looks like the function searches the blob for data based on the section and key and returns a pointer into the blob data and a size, so I made a test function that just echoes back the blob and size as the output parameters:

#include <windows.h>
#include <stdio.h>

__declspec(dllexport)
void ReadFromBlob(PVOID blob, INT blob_size, PCSTR section, PCSTR key, const void **buffer, UINT * size) {
    printf("section=\"%s\" key=\"%s\"\n",section,key);
    *buffer = blob;            // just echo back input data for example
    *size = (UINT)blob_size;
}

The types look like Windows types, and ctypes has a submodule wintypes with Windows definitions that help get the types right. Make sure to set the .argtypes and .restype correctly with parallel ctypes types for the Windows types. This helps ctypes check that arguments are passed correctly.

import ctypes as ct
from ctypes import wintypes as w

dll = ct.CDLL('./test')

# Note the parallels between C types and ctypes types.
# PVOID is just "pointer to void" and LPVOID mean the same thing, etc.
dll.ReadFromBlob.argtypes = w.LPVOID,w.INT,w.LPCSTR,w.LPCSTR,ct.POINTER(w.LPCVOID),w.LPUINT
dll.ReadFromBlob.restype = None

# storage for returned values, passed by reference as output parameters
buffer = w.LPCVOID()
size = w.UINT()
dll.ReadFromBlob(b'filedata',8,b'Section',b'Key',ct.byref(buffer),ct.byref(size))
print(ct.cast(buffer,ct.c_char_p).value,size.value)

Output showing the received section and key, and printing the returned blob data and size:

section="Section" key="Key"
b'filedata' 8
  • Related