Hello so I have a string in python type POINTER(wintypes.BYTE) I am using DATA_BLOB in python(
class CREATE_DATA_BLOB(Structure):
_fields_ = [('cbData', wintypes.DWORD), ('pbData', POINTER(wintypes.BYTE))]
) I have a DLL that encrypts the data. after it encrypts the data, the data is saved inside the pbData of the data_blob structure. The problem is the values inside the pbData(pbData[0]) for example has -42 in it, another example is that some of them are between 0 to 255 - they are good but some are completely random numbers and I can't figure out how to turn these non-ASCII number to a character. In c++ I use writeFile function and I just send pbData and everything works great in python is not the case I have this error if I am trying to write pbData to a text file:
file.write(data_out.pbData)
TypeError: write() argument must be str, not LP_c_byte
I really don't know how to fix this problem.
Listing [Python 3.Docs]: ctypes - A foreign function library for Python.
There are several problems:
wintypes.BYTE is signed ([Python.Bugs]: wrong type for wintypes.BYTE)
file.write works with Python strings (in your case) not ctypes pointers (and there's no implicit conversion between them)
Going further (this would appear after solving the other 2): you have "special" chars in your buffer. That means that you shouldn't treat is as a "normal string", but as a binary sequence (otherwise you may get encode / decode errors). As a consequence, open the file where you want to dump its contents to, in binary mode (e.g.: file = open(file_name, "wb")).
>>> import ctypes as ct
>>> from ctypes import wintypes as wt
>>>
>>> class CREATE_DATA_BLOB(ct.Structure):
... _fields_ = [
... ("cbData", wt.DWORD),
... ("pbData", ct.POINTER(ct.c_ubyte)), # wt.BYTE is signed !!!
... ]
...
>>>
>>> buf_initial = b"AB\xD6CD\xD9EF\x9CGH" # Contains the 3 chars you mentioned
>>> buf_initial
b'AB\xd6CD\xd9EF\x9cGH'
>>> # Populate the structure as it was done from C++
...
>>> blob = CREATE_DATA_BLOB(len(buf_initial), ct.cast(ct.create_string_buffer(buf_initial), ct.POINTER(ct.c_ubyte)))
>>> blob.cbData, blob.pbData
(11, <__main__.LP_c_ubyte object at 0x00000154FF6998C8>)
>>>
>>> buf_final = bytes(blob.pbData[:blob.cbData]) # Convert the pointer explicitly to Python bytes
>>> buf_final
b'AB\xd6CD\xd9EF\x9cGH'
>>> buf_initial == buf_final
True
>>>
>>> with open("q058436070_out.bin", "wb") as file:
... file.write(buf_final)
...
11
Related
I am sending strings to my BPF C code and I am not sure if the strings passed in are null-terminated. If they are not, is there a way to make them null terminated? I am sending in my lines of code to BPF so I can count them manually using my stringCounter function but I keep hitting a forever loop sadly. Here is what my Python code looks like:
b = BPF(src_file="hello.c")
lookupTable = b["lookupTable"]
#add hello.csv to the lookupTable array
f = open("hello copy.csv","r")
contents = f.readlines()
for i in range(0,len(contents)):
string = contents[i].encode('utf-8')
lookupTable[ctypes.c_int(i)] = ctypes.create_string_buffer(string, len(string))
And here is the code I found for my null terminated string counter
int stringLength(char* txt)
{
int i=0,count=0;
while(txt[i++]!='\0'){
count+=1;
}
return count;
}
ctypes.create_string_buffer(string, len(string)) is not zero-terminated. But ctypes.create_string_buffer(string) is. It's easy to see that, since ctypes.create_string_buffer(string)[-1] is b'\x00', whereas ctypes.create_string_buffer(string, len(string))[-1] is the last byte in string.
In other words, if you want a zero-terminated buffer, let create_string_buffer figure out the length. (It uses the actual length from the Python bytes object, so it doesn't get fooled by internal NUL bytes, if you were worried about that.)
I'm unfamiliar with BPF but for ctypes, if your string isn't modified by the C code you don't need create_string_buffer as it is used to create mutable buffers, and Python Unicode and byte strings are both always passed nul-terminated wchar_t* or char*, respectively, to C code. Assuming your function is in test.dll or test.so:
import ctypes as ct
dll = ct.CDLL('./test')
dll.stringLength.argtypes = ct.c_char_p,
dll.stringLength.restype = ct.c_int
print(dll.stringLength('somestring'.encode())) # If string is Unicode
print(dll.stringLength(b'someotherstring')) # If already a byte string
Output:
10
15
Note this doesn't preclude having a nul in the string itself, but your count function will return a shorter value in that case:
print(dll.stringLength(b'some\0string')) # Output: 4
Your code could be probably be written as the following assuming there isn't some requirement that a BPF object have hard-coded ctypes types as indexes and values.
with open("hello copy.csv") as file:
for i,line in enumerate(file):
lookupTable[i] = string.encode()
I am writing a wrapper for a C library in Python. I am trying to properly annotate all of the methods, so my IDE can help me catch errors. I am stuck annotating one method, can you help me figure out the proper annotation?
One of the methods in the C library works as follows:
Takes one arg: pointer to a character buffer
Buffer is made via: char_buffer = ctypes.create_string_buffer(16)
Populates the char buffer with the output value
Done via CMethod(char_buffer)
One then parses the buffer by doing something like char_buffer.value.
How can I annotate the wrapper method to look for a pointer to a character buffer? Currently, I have the below, but I think this is incorrect, since POINTER seems to be just a function in _ctypes.py.
from ctypes import POINTER
def wrapped_method(char_buffer: POINTER):
CMethod(char_buffer)
According to [Python.Docs]: ctypes.create_string_buffer(init_or_size, size=None):
This function creates a mutable character buffer. The returned object is a ctypes array of c_char.
Example:
>>> import ctypes
>>>
>>> CharArr16 = ctypes.c_char * 16
>>> s = ctypes.create_string_buffer(16)
>>>
>>> isinstance(s, CharArr16)
True
>>> isinstance(s, ctypes.c_char * 15)
False
>>> isinstance(s, ctypes.c_char * 17)
False
>>>
>>> # A more general form, but it WILL FAIL for non array instances
...
>>> isinstance(s, s._type_ * s._length_)
True
>>>
>>> # A more general form that WILL WORK
...
>>> issubclass(CharArr16, ctypes.Array)
True
>>> isinstance(s, ctypes.Array)
True
I'm trying to use PyQt5's DBus module to interact with the KDE PowerManagerAgent. When calling the AddInhibition method I need to send the first paramter as an uint32 (Unsigned int), but the code sends the value as a singed int.
The code is written using Python 3
self.dBus = QtDBus.QDBusConnection.sessionBus()
msg = QtDBus.QDBusMessage.createMethodCall(self.dBusService, self.dBusPath,self.dBusInterface,'AddInhibition')
msg << 1 << who << reason
reply = QtDBus.QDBusReply(self.dBus.call(msg))
Looking at the output from dbus-monitor I can tell that the code does indeed contact the powermonitor but fails to find the correct AddInhibition method due to the first parameter being type as int32
Output from dbus-monitor when trying to call AddInhibition
Call
method call time=1549706946.073218 sender=:1.172 -> destination=org.kde.Solid.PowerManagement.PolicyAgent serial=5 path=/org/kde/Solid/PowerManagement/PolicyAgent; interface=org.kde.Solid.PowerManagement.PolicyAgent; member=AddInhibition
int32 1
string "This"
string "fails"
Reply
error time=1549706946.073536 sender=:1.29 -> destination=:1.172 error_name=org.freedesktop.DBus.Error.UnknownMethod reply_serial=5
string "No such method 'AddInhibition' in interface 'org.kde.Solid.PowerManagement.PolicyAgent' at object path '/org/kde/Solid/PowerManagement/PolicyAgent' (signature 'iss')"
Output from dbus-monitor when using QDBusViewer application
Call
method call time=1549723045.320128 sender=:1.82 -> destination=org.kde.Solid.PowerManagement.PolicyAgent serial=177 path=/org/kde/Solid/PowerManagement/PolicyAgent; interface=org.kde.Solid.PowerManagement.PolicyAgent; member=AddInhibition
uint32 1
string "This"
string "Works"
Reply
method return time=1549723045.320888 sender=:1.29 -> destination=:1.82 serial=1370 reply_serial=177
uint32 30
Since Python is not strongly typed how do I specify the the parameter must be typed as an unsigned int?
You can use the DBusArgument class to do this by specifying the QMetaType of the argument.
For example, say you want to use the RequestName method from org.freedesktop.DBus (see the spec). The flags argument is an unsigned int, so you'll run into this problem:
>>> from PyQt5.QtDBus import QDBusConnection, QDBusInterface
>>> sessionbus = QDBusConnection.sessionBus()
>>> iface = QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", sessionbus)
>>> c = iface.call('RequestName', 'com.mydomain.myapp', 4)
>>> c.arguments()
['Call to RequestName has wrong args (si, expected su)\n']
So, it's saying it got a string and an integer (si), but it wanted a string and an unsigned integer (su). So, we'll use the QDBusArgument class and specify QMetaType.UInt:
>>> from PyQt5.QtCore import QMetaType
>>> from PyQt5.QtDBus import QDBusConnection, QDBusInterface, QDBusArgument
>>> sessionbus = QDBusConnection.sessionBus()
>>> iface = QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", sessionbus)
>>> a1 = QDBusArgument()
>>> a1.add('com.mydomain.myapp', QMetaType.QString)
>>> a2 = QDBusArgument(4, QMetaType.UInt)
>>> c = iface.call('RequestName', a1, a2)
>>> c.arguments()
[1]
Since the string was fine, that didn't have to be a QDBusArgument. I just wanted to show the two ways of constructing it (with the .add() method and just using the constructor).
I am attempting to read my players health. I have been on a roll but have run into a problem. I am able to read what type of information is at a certain address but can't read what the actual value is, for example here is the response I receive.
<ctypes.c_char_Array_64 object at 0x0000000002EBF9C8>
I am looking for what information is held in the c_char_Array_64 object but have no idea how I would go about it.
Here is my code:
class User:
ctypes.wintypes.DWORD = "Entity"
ctypes.wintypes.c_int = "Team"
ctypes.wintypes.c_int = "Health"
ctypes.wintypes.c_int = "Player"
def getSelfInfo(self):
adr1 = clientdll + dw_LocalPlayer
adr2 = ctypes.create_string_buffer(64)
bytes_read = ctypes.c_size_t()
(rPM(PROCESS.handle, adr1, adr2, sys.getsizeof(ctypes.wintypes.DWORD), ctypes.byref(bytes_read)))
print adr2
t = User()
t.getSelfInfo()
You need to get the value:
print(ar2.value)
From the docs:
If you need mutable memory blocks, ctypes has a create_string_buffer()
function which creates these in various ways. The current memory block
contents can be accessed (or changed) with the raw property; if you
want to access it as NUL terminated string, use the value property:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print sizeof(p), repr(p.raw)
3 '\x00\x00\x00'
>>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string
>>> print sizeof(p), repr(p.raw)
6 'Hello\x00'
>>> print repr(p.value)
'Hello'
>>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer
>>> print sizeof(p), repr(p.raw)
10 'Hello\x00\x00\x00\x00\x00'
>>> p.value = "Hi"
>>> print sizeof(p), repr(p.raw)
10 'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
The empty slice of most ctypes array types will return the Python equivalent type. So to convert your 64 byte buffer to a str (in Py3 bytes), you can do:
print ar2[:]
That will read the full raw 64 bytes mind you. If you want to read it as a C-style string (so the first NUL byte terminates the Python equivalent str), you'd use .value:
print ar2.value
I am using the Python construct parser to process some binary data but am not managing to obtain strings in the way I expected.
Note that in the simplified example below I could use unpack or even just a slice, but the real data I am parsing does not align neatly to byte boundaries.
Some example code:
from construct import BitStruct, BitField, Padding, String
struct = BitStruct("foo",
BitField("bar", 8),
BitField("baz", 16),
Padding(4),
BitField("bat", 4)
)
struct2 = BitStruct("foo",
BitField("bar", 8),
String("baz", 16),
Padding(4),
BitField("bat", 4)
)
data = "\x01AB\xCD"
print struct.parse(data)
print struct2.parse(data)
This prints the output:
Container:
bar = 1
baz = 16706
bat = 13
Container:
bar = 1
baz = '\x00\x01\x00\x00\x00\x00\x00\x01\x00\x01\x00\x00\x00\x00\x01\x00'
bat = 13
I was expecting that String would give me back AB as an actual string. However it is returning the equivalent binary string instead.
How can I persuade construct to return me the actual ASCII string?
I solved this by creating an Adapter. The original ASCII values are parsed into a list of integers which can then be converted into a string representation.
It's not the most elegant way but due to BitStruct operating only on bit values it seems to be the easiest workaround. An improved version would parse different length strings (e.g. 7-bit ASCII).
from binascii import hexlify
from construct import BitStruct, BitField, Padding, Array, Octet, Adapter
class BitStringAdapter(Adapter):
def _encode(self, obj, context):
return list(ord(b) for b in obj)
def _decode(self, obj, context):
return "".join(chr(b) for b in obj)
struct = BitStruct("foo",
BitField("bar", 8),
BitStringAdapter(Array(2, Octet("baz"))),
Padding(4),
BitField("bat", 4)
)
data = "\x01AB\xCD"
out = struct.parse(data)
print hexlify(struct.build(out))
This outputs:
Container:
bar = 1
baz = 16706
bat = 13
0141420d
Which is correct - the C byte is discarded because it's marked as padding, this is fine.
The python module bitstruct can also be used to parse bit fields. It uses format strings, just like the standard library struct module.
The format specifier 't' is for text.
>>> from bitstruct import unpack
>>> data = b'\x01AB\xCD'
>>> unpack("u8u16p4u4", data)
(1, 16706, 13)
>>> unpack("u8t16p4u4", data)
(1, u'AB', 13)