Skip to content

Commit 59f970e

Browse files
committed
chore: create SKILL.md file for AI agents
1 parent 2e5b7da commit 59f970e

1 file changed

Lines changed: 335 additions & 0 deletions

File tree

SKILL.md

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# libdestruct Skills
2+
3+
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.
4+
5+
## Installation
6+
7+
```bash
8+
pip install git+https://github.com/mrindeciso/libdestruct.git
9+
```
10+
11+
## Core Concepts
12+
13+
All types inherit from `obj`. Every `obj` has:
14+
- `.value` property to read/write the underlying data
15+
- `.address` property for the memory offset
16+
- `.to_bytes()` to serialize back to bytes
17+
- `.freeze()` / `.diff()` / `.reset()` for snapshotting
18+
- `.hexdump()` for a hex dump of the object's bytes
19+
- `.from_bytes(data)` class method to create a read-only instance from raw bytes
20+
21+
Memory is accessed through an `inflater`, which wraps a `bytes` or `bytearray` buffer. Use `bytearray` for read/write access.
22+
23+
## Quick Reference
24+
25+
### Imports
26+
27+
```python
28+
from libdestruct import (
29+
inflater, # memory wrapper
30+
struct, # struct base class
31+
c_int, c_uint, # 32-bit integers (signed/unsigned)
32+
c_long, c_ulong, # 64-bit integers (signed/unsigned)
33+
c_float, c_double, # IEEE 754 floats (32/64-bit)
34+
c_str, # null-terminated C string
35+
ptr, # 8-byte pointer
36+
ptr_to, # typed pointer field descriptor
37+
ptr_to_self, # self-referential pointer field descriptor
38+
array_of, # fixed-size array field descriptor
39+
enum_of, # enum field descriptor
40+
bitfield_of, # bitfield descriptor
41+
offset, # explicit field offset
42+
size_of, # get size in bytes of any type/instance/field
43+
)
44+
```
45+
46+
### Type Sizes
47+
48+
| Type | Size (bytes) |
49+
|---|---|
50+
| `c_int` / `c_uint` | 4 |
51+
| `c_long` / `c_ulong` | 8 |
52+
| `c_float` | 4 |
53+
| `c_double` | 8 |
54+
| `ptr` | 8 |
55+
| `c_str` | variable (reads until null) |
56+
57+
### Reading Primitives from a Buffer
58+
59+
```python
60+
memory = bytearray(b"\x2a\x00\x00\x00\x00\x00\x00\x00")
61+
lib = inflater(memory)
62+
63+
x = lib.inflate(c_int, 0) # inflate c_int at offset 0
64+
print(x.value) # 42
65+
66+
y = lib.inflate(c_long, 0) # inflate c_long at offset 0
67+
print(y.value)
68+
```
69+
70+
### Reading Primitives from Raw Bytes
71+
72+
```python
73+
x = c_int.from_bytes(b"\x2a\x00\x00\x00")
74+
print(x.value) # 42
75+
# Note: from_bytes returns a frozen (read-only) object
76+
```
77+
78+
### Writing Primitives
79+
80+
```python
81+
memory = bytearray(4)
82+
lib = inflater(memory)
83+
x = lib.inflate(c_int, 0)
84+
x.value = -1
85+
print(memory) # bytearray(b'\xff\xff\xff\xff')
86+
```
87+
88+
### Defining Structs
89+
90+
```python
91+
class player_t(struct):
92+
health: c_int
93+
score: c_uint
94+
position_x: c_float
95+
position_y: c_float
96+
```
97+
98+
Struct fields are laid out sequentially. Access members as attributes; each returns a typed `obj` (use `.value` to get the Python value).
99+
100+
### Inflating Structs
101+
102+
```python
103+
import struct as pystruct
104+
105+
memory = bytearray(16)
106+
memory[0:4] = pystruct.pack("<i", 100)
107+
memory[4:8] = pystruct.pack("<I", 5000)
108+
memory[8:12] = pystruct.pack("<f", 1.5)
109+
memory[12:16] = pystruct.pack("<f", -3.0)
110+
111+
lib = inflater(memory)
112+
player = lib.inflate(player_t, 0)
113+
114+
print(player.health.value) # 100
115+
print(player.score.value) # 5000
116+
print(player.position_x.value) # 1.5
117+
```
118+
119+
Or from raw bytes (read-only):
120+
121+
```python
122+
player = player_t.from_bytes(memory)
123+
```
124+
125+
### Pointers
126+
127+
```python
128+
class node_t(struct):
129+
value: c_int
130+
next: ptr = ptr_to_self() # pointer to own type
131+
132+
# Typed pointer to another type:
133+
class container_t(struct):
134+
data: c_int
135+
ref: ptr = ptr_to(c_long)
136+
```
137+
138+
Dereference with `.unwrap()` or safe `.try_unwrap()` (returns `None` if invalid):
139+
140+
```python
141+
node = lib.inflate(node_t, 0)
142+
print(node.value.value)
143+
next_node = node.next.unwrap() # follow pointer
144+
maybe_node = node.next.try_unwrap() # None if invalid
145+
```
146+
147+
Pointer arithmetic (C-style, scaled by element size):
148+
149+
```python
150+
p = lib.inflate(ptr, 0)
151+
p.wrapper = c_int
152+
print(p[0].value) # element at index 0
153+
print(p[1].value) # element at index 1
154+
print((p + 2).unwrap().value) # element at index 2
155+
```
156+
157+
Pointer results are cached; call `.invalidate()` after memory changes.
158+
159+
### Forward References
160+
161+
For mutually referential structs, use `ptr["TypeName"]`:
162+
163+
```python
164+
class tree_t(struct):
165+
value: c_int
166+
left: ptr["tree_t"]
167+
right: ptr["tree_t"]
168+
```
169+
170+
### Arrays
171+
172+
```python
173+
class packet_t(struct):
174+
length: c_int
175+
data: array_of(c_int, 8) # fixed array of 8 c_int
176+
```
177+
178+
Access array elements:
179+
180+
```python
181+
pkt = lib.inflate(packet_t, 0)
182+
print(pkt.data[0].value) # first element
183+
print(pkt.data.count()) # 8
184+
for element in pkt.data:
185+
print(element.value)
186+
```
187+
188+
### Enums
189+
190+
```python
191+
from enum import IntEnum
192+
193+
class Color(IntEnum):
194+
RED = 0
195+
GREEN = 1
196+
BLUE = 2
197+
198+
class pixel_t(struct):
199+
color: c_int = enum_of(Color)
200+
alpha: c_int
201+
```
202+
203+
```python
204+
pixel = lib.inflate(pixel_t, 0)
205+
print(pixel.color.value) # Color.RED
206+
```
207+
208+
### Bitfields
209+
210+
```python
211+
class flags_t(struct):
212+
read: c_int = bitfield_of(c_int, 1)
213+
write: c_int = bitfield_of(c_int, 1)
214+
execute: c_int = bitfield_of(c_int, 1)
215+
reserved: c_int = bitfield_of(c_int, 29)
216+
```
217+
218+
Consecutive bitfields with the same backing type are packed together. The struct above is 4 bytes total, not 16.
219+
220+
### Explicit Field Offsets
221+
222+
```python
223+
class sparse_t(struct):
224+
a: c_int
225+
b: c_int = offset(0x10) # b starts at byte offset 0x10
226+
```
227+
228+
### Nested Structs
229+
230+
```python
231+
class vec2(struct):
232+
x: c_float
233+
y: c_float
234+
235+
class entity_t(struct):
236+
id: c_int
237+
pos: vec2
238+
```
239+
240+
```python
241+
e = lib.inflate(entity_t, 0)
242+
print(e.pos.x.value)
243+
```
244+
245+
### size_of
246+
247+
```python
248+
size_of(c_int) # 4
249+
size_of(c_long) # 8
250+
size_of(player_t) # computed from fields
251+
size_of(array_of(c_int, 10)) # 40
252+
size_of(some_instance) # works on instances too
253+
```
254+
255+
### Hex Dump
256+
257+
```python
258+
player = lib.inflate(player_t, 0)
259+
print(player.hexdump())
260+
# 00000000 64 00 00 00 88 13 00 00 00 00 c0 3f 00 00 40 c0 |d..........?..@.| health, score, position_x, position_y
261+
```
262+
263+
Struct hexdumps annotate lines with field names. Primitive hexdumps show raw bytes.
264+
265+
### Freeze / Diff / Reset
266+
267+
```python
268+
x = lib.inflate(c_int, 0)
269+
x.freeze() # snapshot current value
270+
x.value = 99 # raises ValueError (frozen)
271+
272+
# For non-frozen objects:
273+
x.freeze() # save state
274+
# ... memory changes externally ...
275+
print(x.diff()) # (old_value, new_value)
276+
x.reset() # restore to frozen value
277+
x.update() # update frozen value to current
278+
```
279+
280+
### C Struct Parser
281+
282+
Parse C struct definitions directly (requires `pycparser`):
283+
284+
```python
285+
from libdestruct.c.struct_parser import definition_to_type
286+
287+
player_t = definition_to_type("""
288+
struct player_t {
289+
int health;
290+
unsigned int score;
291+
float x;
292+
double y;
293+
};
294+
""")
295+
296+
player = player_t.from_bytes(data)
297+
```
298+
299+
Supports: nested structs, pointers (including self-referential), arrays, bitfields, typedefs, `#include` directives (requires a C preprocessor), and `__attribute__` stripping.
300+
301+
## Common Patterns
302+
303+
### Parsing a binary format
304+
305+
```python
306+
class header_t(struct):
307+
magic: c_uint
308+
version: c_int
309+
num_entries: c_int
310+
entries_ptr: ptr = ptr_to(entry_t)
311+
312+
with open("file.bin", "rb") as f:
313+
data = bytearray(f.read())
314+
315+
lib = inflater(data)
316+
header = lib.inflate(header_t, 0)
317+
318+
for i in range(header.num_entries.value):
319+
entry = header.entries_ptr[i]
320+
# process entry...
321+
```
322+
323+
### Modifying binary data in-place
324+
325+
```python
326+
data = bytearray(open("save.bin", "rb").read())
327+
lib = inflater(data)
328+
player = lib.inflate(player_t, 0x100)
329+
player.health.value = 999
330+
open("save.bin", "wb").write(data)
331+
```
332+
333+
### Working with libdebug
334+
335+
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`.

0 commit comments

Comments
 (0)