Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save merrybingo/2a019fb421b1dc0611d8bed5e925e616 to your computer and use it in GitHub Desktop.
Save merrybingo/2a019fb421b1dc0611d8bed5e925e616 to your computer and use it in GitHub Desktop.
Using Python ctypes to manipulate binary data
#!/usr/bin/env python3
from __future__ import print_function
from binascii import hexlify
from ctypes import *
class StructHelper(object):
def __get_value_str(self, name, fmt='{}'):
val = getattr(self, name)
if isinstance(val, Array):
val = list(val)
return fmt.format(val)
def __str__(self):
result = '{}:\n'.format(self.__class__.__name__)
maxname = max(len(name) for name, type_ in self._fields_)
for name, type_ in self._fields_:
value = getattr(self, name)
result += ' {name:<{width}}: {value}\n'.format(
name=name,
width=maxname,
value=self.__get_value_str(name),
)
return result
def __repr__(self):
return '{name}({fields})'.format(
name=self.__class__.__name__,
fields=', '.join(
'{}={}'.format(name, self.__get_value_str(name, '{!r}')) for name, _ in self._fields_)
)
@classmethod
def _typeof(cls, field):
"""Get the type of a field
Example: A._typeof(A.fld)
Inspired by stackoverflow.com/a/6061483
"""
for name, type_ in cls._fields_:
if getattr(cls, name) is field:
return type_
raise KeyError
@classmethod
def read_from(cls, f):
result = cls()
if f.readinto(result) != sizeof(cls):
raise EOFError
return result
def get_bytes(self):
"""Get raw byte string of this structure
ctypes.Structure implements the buffer interface, so it can be used
directly anywhere the buffer interface is implemented.
https://stackoverflow.com/q/1825715
"""
# Works for either Python2 or Python3
return bytearray(self)
# Python 3 only! Don't try this in Python2, where bytes() == str()
# return bytes(self)
################################################################################
class Vehicle(LittleEndianStructure, StructHelper):
"""
Define a little-endian structure, and add our StructHelper mixin.
C structure definition:
__attribute__((packed))
struct Vehicle
{
uint32_t doors;
uint32_t price;
uint32_t miles;
uint32_t air_pressure[4];
char name[16];
}
"""
# Tell ctypes that this structure is "packed",
# i.e. no padding is inserted between fields for alignment
_pack_ = 1
# Lay out the fields, in order
_fields_ = [
('doors', c_uint32),
('price', c_uint32),
('miles', c_uint32),
('air_pressure', c_uint32 * 4),
('name', c_char * 16),
]
def main():
# First, let's create an object
car = Vehicle(
doors=2,
price=15000,
miles=33700,
air_pressure=Vehicle._typeof(Vehicle.air_pressure)(31, 30, 31, 29),
name=b'Brad',
)
# Print the representation of the object
print('repr(car):', repr(car))
# Print the object as a nicely-formatted string
print('car:', car)
with open('output_python', 'wb') as f:
# Write the object to a file:
f.write(car)
print("Wrote car to file ({} bytes)".format(f.tell()))
# Read the file back into a new object
# f.seek(0)
# car2 = Vehicle.read_from(f)
# print('car2:', car2)
# Get the raw bytes of the object
buf = car.get_bytes()
print('Buffer ({}) (hex): {}'.format(type(buf), hexlify(buf)))
# Create an object from some bytes
# car3 = Vehicle.from_buffer_copy(buf)
# print('car3:', car3)
if __name__ == '__main__':
main()
#include "stdio.h"
#include "stdint.h"
struct Vehicle
{
uint32_t doors;
uint32_t price;
uint32_t miles;
uint32_t air_pressure[4];
char name[16];
};
int main() {
struct Vehicle s = { 2, 15000, 33700, {31, 30, 31, 29}, "Brad" };
FILE *f = fopen("output_c", "wb");
fwrite(&s, sizeof(s), 1, f);
fclose(f);
return 0;
}
repr(car): Vehicle(doors=2, price=15000, miles=33700, air_pressure=[31, 30, 31, 29], name=b'Brad')
car: Vehicle:
doors : 2
price : 15000
miles : 33700
air_pressure: [31, 30, 31, 29]
name : b'Brad'
Wrote car to file (44 bytes)
Buffer (<class 'bytearray'>) (hex): b'02000000983a0000a48300001f0000001e0000001f0000001d00000042726164000000000000000000000000'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment