Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python: Convert bytearray to ctypes Struct

I have an 'n' Byte bytearray. This is same as one of the defined ctypes.Sturcture. I want to typecast this bytearray to this sturcture. so that I can access each of this member. How can I do that?

class ABC(Structure):
    _fields_ = [("a", c_uint), ("b", c_ushort), ("c", c_ushort)]

class DEF(Structure):
    _fields_ = [("abc", ABC), ("i", I)]

b = bytearray(b'\x88\x08\xc0\xf9\x02\x85\x10\x00\xcc')

s = DEF()
print(s.abc.a)

How do I get the the correct value of the above print statement?

like image 244
Rachit Agrawal Avatar asked Mar 22 '15 12:03

Rachit Agrawal


1 Answers

You can use from_buffer on the desired type (rather than the object instance):

from ctypes import Structure, c_uint, c_ushort, c_uint8


class ABC(Structure):
    _pack_ = 1
    _fields_ = [("a", c_uint), ("b", c_ushort), ("c", c_ushort)]


class DEF(Structure):
    _pack_ = 1
    _fields_ = [("abc", ABC), ("i", c_uint8)]


def main():
    b = bytearray(b'\x88\x08\xc0\xf9\x02\x85\x10\x00\xcc')

    # check if bytearray can be applied to structure.
    if len(b) < ctypes.sizeof(DEF):
        print("error: bytearray is too short for DEF.")
        return

    s = DEF.from_buffer(b)
    print("abc.a: {:#x}".format(s.abc.a))
    print("abc.b: {:#x}".format(s.abc.b))
    print("abc.c: {:#x}".format(s.abc.c))
    print("i: {:#x}".format(s.i))

if __name__ == '__main__':
    main()

Note that the structure must be packed accordingly, thus I used a _pack_ = 1 so the expected size of DEF structure is 9 bytes (4 + 2 + 2 + 1) rather than 12. I also used c_uint8 for the DEF.i field as this is probably what you meant (I, in your example, is not a type).

Output:

abc.a: 0xf9c00888
abc.b: 0x8502
abc.c: 0x10
i: 0xcc

If you want the value in big endian (rather than the default little endian), change the endianness of the structure by using ctypes.BigEndianStructure.

like image 180
Neitsa Avatar answered Nov 12 '22 10:11

Neitsa