Pack, Unpack & C Structs

Pack, Unpack & C Structs

Built-in Pack/Unpack

ql.pack(value) / ql.unpack(data)

Architecture-width aware: automatically uses 16/32/64-bit variant based on ql.arch.bits.

1packed = ql.pack(value)
2value  = ql.unpack(data)

Fixed-width variants

 1# Unsigned
 2ql.pack64(v)   # Q — unsigned long long, 8 bytes
 3ql.pack32(v)   # I — unsigned int, 4 bytes (endian-aware)
 4ql.pack16(v)   # H — unsigned short, 2 bytes (endian-aware)
 5
 6ql.unpack64(d)
 7ql.unpack32(d)
 8ql.unpack16(d)
 9
10# Signed
11ql.pack64s(v)  # q — long, 8 bytes
12ql.pack32s(v)  # i — int, 4 bytes (endian-aware)
13ql.pack16s(v)  # h — short, 2 bytes (endian-aware)
14
15ql.unpack64s(d)
16ql.unpack32s(d)
17ql.unpack16s(d)

Python struct Module for C Structs

For complex or unknown structures, use Python’s struct module directly.

Format string prefix controls byte order:

Common format characters:

CharC typeSize
b/Bsigned/unsigned char1
h/Hsigned/unsigned short2
i/Isigned/unsigned int4
q/Qsigned/unsigned long long8
schar[]n (prefix with count)
ppointerarch-dependent

Reference: https://docs.python.org/3/library/struct.html

Example: Parsing sockaddr_in from Memory

Target: Netgear R6220, MIPS32 little-endian, bind() syscall.

C structure:

1struct sockaddr_in {
2    sa_family_t sin_family;  /* address family: AF_INET */
3    in_port_t   sin_port;    /* port in network byte order */
4    struct in_addr sin_addr; /* internet address */
5};
 1import struct
 2from qiling.os.const import UINT, POINTER
 3
 4def my_bind(ql, *args, **kw):
 5    params = ql.os.resolve_fcall_params({
 6        'fd':      UINT,
 7        'addr':    POINTER,
 8        'addrlen': UINT
 9    })
10
11    data = ql.mem.read(params['addr'], params['addrlen'])
12
13    # sin_family: little-endian short (arch endian)
14    sin_family = struct.unpack("<h", data[:2])[0]
15
16    # sin_port + sin_addr: big-endian (network byte order)
17    port, host = struct.unpack(">HI", data[2:8])

Note: Network stack fields (sin_port, sin_addr) are always big-endian regardless of CPU architecture, as mandated by POSIX. The architecture endianness only affects the sin_family field.