Partial Structs#
Often when reverse engineering a program you may figure out a struct, but not the entire layout of it.
Often this would then require adding many unknown
fields or adding padding bytes manually.
pyMHF aims to simplify this process by providing the partial_struct
class decorator, as well as the Field
type which is used to annotate the type of field.
The Field
class is defined as follows:
@dataclass
class Field:
datatype: ctypes.Structure
offset: Optional[int] = None
When using this class, the datatype
must be provided, however the offset
doesn’t need to be. If it isn’t provided then the field will be placed directly after the field before it at a location determined based on the usual alignment and packing rules.
If the offset
is provided, then the number of padding bytes required between this field and the previous one (if there is one) will be determined and inserted automatically.
Usage#
Below is an example of a struct which we have mapped out that we know has a 32 bit int at the start, 12 bytes of unknown contents, and then another 32 bit int.
import ctypes
from typing import Annotated
from pymhf.utils.partial_struct import partial_struct, Field
@partial_struct
class Test(ctypes.Structure):
a: Annotated[int, Field(ctypes.c_uint32)]
b: Annotated[int, Field(ctypes.c_uint32, 0x10)]
Advantages#
There are 2 main advantages to using partial structs:
Type hints - Standard
ctypes.Structure
objects do not have type hints, so you often need to add them manually after defining the_field_
attribute, or you don’t bother with them. Since thepartial_struct
creates the_field_
attribute automatically based on the class annotations, you don’t need to worry about defining it manually.Ease of updating - If you are reversing a binary which changes regularly, it can sometimes mean that the structs need regular updates. If these are large you might not always need all the fields, so by using a partial struct, you can save effort by only mapping and updating the fields you care about.