Files
Willi Ballenthin debc9a1e45 Key cached EmuStruct types by their field layout
EmuStruct caches each generated ctypes type by class name and pointer
size. PROCESSENTRY32, MODULEENTRY32, WIN32_FIND_DATA and SID take a
second constructor argument, a character width or a sub-authority count,
that changes their fields. The first instance in the process fixed the
layout for every later instance, so a wide call after an ANSI call wrote
the ANSI layout. The cache key now includes the pack value and the field
list.
2026-10-11 19:45:24 +01:00

74 lines
2.1 KiB
Python

import ctypes as ct
from speakeasy.struct import EmuStruct, Ptr
EXPECTED_32BIT_BYTES = (
b"\x01\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x05\x05"
b"\x06\x06\x06\x06\x07\x07\x07\x07AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
b"\x08\x08\t\t\t\t\n\n\n\nBBBBBBBBBBBBBBBBBBBBBBBB"
)
class DEEP_NEST(EmuStruct):
def __init__(self, ptr_size):
super().__init__(ptr_size)
self.Field1 = ct.c_uint32
self.Field2 = ct.c_uint32
self.DeepData = ct.c_uint8 * 32
class SHALLOW_NEST(EmuStruct):
def __init__(self, ptr_size):
super().__init__(ptr_size, pack=1)
self.Field1 = ct.c_uint16
self.DeepStruct = DEEP_NEST
self.Field2 = ct.c_uint16
class TOP_OBJECT(EmuStruct):
def __init__(self, ptr_size):
super().__init__(ptr_size=ptr_size, pack=1)
self.Field1 = ct.c_uint16
self.Field2 = ct.c_uint16
self.Field3 = ct.c_uint32
self.Field4 = Ptr
self.NestedStruct = SHALLOW_NEST
self.Field5 = Ptr
self.Field6 = ct.c_uint32
self.Field7 = ct.c_uint8 * 24
def test_32bit_emit():
ptr_size = 4
top = TOP_OBJECT(ptr_size)
top.Field1 = 0x0101
top.Field2 = 0x0202
top.Field3 = 0x03030303
top.Field4 = 0x04040404
top.NestedStruct.Field1 = 0x0505
top.NestedStruct.DeepStruct.Field1 = 0x06060606
top.NestedStruct.DeepStruct.Field2 = 0x07070707
top.NestedStruct.DeepStruct.DeepData = b"A" * 32
top.NestedStruct.Field2 = 0x0808
top.Field5 = 0x09090909
top.Field6 = 0x0A0A0A0A
top.Field7 = b"B" * 24
bytez = top.get_bytes()
assert bytez == EXPECTED_32BIT_BYTES
class WIDE_OR_NARROW(EmuStruct):
def __init__(self, ptr_size, width):
super().__init__(ptr_size)
self.Size = ct.c_uint32
self.Name = ct.c_uint8 * (4 * width)
def test_struct_layout_follows_constructor_arguments():
assert WIDE_OR_NARROW(4, 1).sizeof() == 8
assert WIDE_OR_NARROW(4, 2).sizeof() == 12
assert WIDE_OR_NARROW(4, 1).sizeof() == 8
assert type(WIDE_OR_NARROW(4, 2).get_cstruct()) is type(WIDE_OR_NARROW(4, 2).get_cstruct())