# libdestruct Skills libdestruct is a Python library for destructuring binary data into typed objects. It maps raw bytes to C-like types (integers, floats, strings, structs, pointers, arrays, enums, bitfields) with read/write support. ## Installation ```bash pip install git+https://github.com/mrindeciso/libdestruct.git ``` ## Core Concepts All types inherit from `obj`. Every `obj` has: - `.value` property to read/write the underlying data - `.address` property for the memory offset - `.to_bytes()` to serialize back to bytes - `.freeze()` / `.diff()` / `.reset()` for snapshotting - `.hexdump()` for a hex dump of the object's bytes - `.from_bytes(data)` class method to create a read-only instance from raw bytes Memory is accessed through an `inflater`, which wraps a `bytes`, `bytearray`, or `mmap.mmap` buffer. Use `bytearray` or writable mmap for read/write access. For file-backed memory, use `inflater_from_file()`. ## Quick Reference ### Imports ```python from typing import Annotated from libdestruct import ( inflater, # memory wrapper (bytearray / mmap) inflater_from_file, # file-backed inflater (convenience) FileInflater, # file-backed inflater class struct, # struct base class c_int, c_uint, # 32-bit integers (signed/unsigned) c_long, c_ulong, # 64-bit integers (signed/unsigned) c_short, c_ushort, # 16-bit integers (signed/unsigned) c_char, c_uchar, # 8-bit integers (signed/unsigned) c_float, c_double, # IEEE 754 floats (32/64-bit) c_str, # null-terminated C string ptr, # 8-byte pointer ptr_to, # typed pointer field descriptor (legacy) ptr_to_self, # self-referential pointer field descriptor (legacy) array, array_of, # array type + field descriptor vla_of, # variable-length array field descriptor enum, enum_of, # enum type + field descriptor flags, flags_of, # bit flags type + field descriptor bitfield_of, # bitfield descriptor union, # union annotation type union_of, # plain union field descriptor tagged_union, # tagged union field descriptor offset, # explicit field offset size_of, # get size in bytes of any type/instance/field alignment_of, # get natural alignment of any type/instance ) ``` ### Type Sizes | Type | Size (bytes) | |---|---| | `c_int` / `c_uint` | 4 | | `c_long` / `c_ulong` | 8 | | `c_float` | 4 | | `c_double` | 8 | | `ptr` | 8 | | `c_str` | variable (reads until null) | ### Reading Primitives from a Buffer ```python memory = bytearray(b"\x2a\x00\x00\x00\x00\x00\x00\x00") lib = inflater(memory) x = lib.inflate(c_int, 0) # inflate c_int at offset 0 print(x.value) # 42 y = lib.inflate(c_long, 0) # inflate c_long at offset 0 print(y.value) ``` ### Reading Primitives from Raw Bytes ```python x = c_int.from_bytes(b"\x2a\x00\x00\x00") print(x.value) # 42 # Note: from_bytes returns a frozen (read-only) object ``` ### Writing Primitives ```python memory = bytearray(4) lib = inflater(memory) x = lib.inflate(c_int, 0) x.value = -1 print(memory) # bytearray(b'\xff\xff\xff\xff') ``` ### Defining Structs ```python class player_t(struct): health: c_int score: c_uint position_x: c_float position_y: c_float ``` Struct fields are laid out sequentially. Access members as attributes; each returns a typed `obj` (use `.value` to get the Python value). ### Inflating Structs ```python import struct as pystruct memory = bytearray(16) memory[0:4] = pystruct.pack(" B -> C) and alignment inheritance both work. Parent fields always appear first in layout and `to_dict()`. ### size_of ```python size_of(c_int) # 4 size_of(c_long) # 8 size_of(player_t) # computed from fields size_of(array_of(c_int, 10)) # 40 size_of(some_instance) # works on instances too ``` ### Hex Dump ```python player = lib.inflate(player_t, 0) print(player.hexdump()) # 00000000 64 00 00 00 88 13 00 00 00 00 c0 3f 00 00 40 c0 |d..........?..@.| health, score, position_x, position_y ``` Struct hexdumps annotate lines with field names. Primitive hexdumps show raw bytes. ### Dict / JSON Export ```python point = point_t.from_bytes(memory) point.to_dict() # {"x": 10, "y": 20} import json json.dumps(entity.to_dict()) # nested structs produce nested dicts ``` `to_dict()` works on all types: primitives return their value, structs return `{name: value}` dicts, arrays return lists, unions return variant values, enums return their int value. ### Freeze / Diff / Reset ```python x = lib.inflate(c_int, 0) x.freeze() # snapshot current value x.value = 99 # raises ValueError (frozen) # For non-frozen objects: x.freeze() # save state # ... memory changes externally ... print(x.diff()) # (old_value, new_value) x.reset() # restore to frozen value x.update() # update frozen value to current ``` ### C Struct Parser Parse C struct definitions directly (requires `pycparser`): ```python from libdestruct.c.struct_parser import definition_to_type player_t = definition_to_type(""" struct player_t { int health; unsigned int score; float x; double y; }; """) player = player_t.from_bytes(data) ``` Supports: nested structs, pointers (including self-referential), arrays, bitfields, typedefs, `#include` directives (requires a C preprocessor), and `__attribute__` stripping. ## Common Patterns ### Parsing a binary format ```python class header_t(struct): magic: c_uint version: c_int num_entries: c_int entries_ptr: ptr[entry_t] with open("file.bin", "rb") as f: data = bytearray(f.read()) lib = inflater(data) header = lib.inflate(header_t, 0) for i in range(header.num_entries.value): entry = header.entries_ptr[i] # process entry... ``` ### Modifying binary data in-place ```python data = bytearray(open("save.bin", "rb").read()) lib = inflater(data) player = lib.inflate(player_t, 0x100) player.health.value = 999 open("save.bin", "wb").write(data) ``` ### File-backed inflater Read (and optionally write) binary files directly via mmap, without loading the entire file into memory: ```python # Read-only with inflater_from_file("firmware.bin") as lib: header = lib.inflate(header_t, 0) print(header.magic.value) # Writable — changes are persisted to the file with inflater_from_file("save.bin", writable=True) as lib: player = lib.inflate(player_t, 0x100) player.health.value = 999 ``` You can also pass an `mmap.mmap` object directly to `inflater()`. ### Working with libdebug libdestruct integrates with [libdebug](https://github.com/libdebug/libdebug) for live process memory inspection. The debugger's memory view can be passed directly to `inflater`.