add feature - relations
dzonerzy opened this issue · 3 comments
Would be nice to have some kind of predefined relations to make it more customizable, i mean something like:
RDef("value",
Int | Float | RRef("boolean") | RRef("key") | UInt | UFloat | RRef("null")
)
RDef("key-object",
RRef("key", relation=relation.sizeOf("value")) & ":"
)
Something similar would allow more flexibility!
Regards,
Daniele Linguaglossa
@dzonerzy that could be really useful
@dzonerzy I have things like this in pfp, like this https://pfp.readthedocs.io/en/latest/metadata.html#watch-metadata
However, pfp is best suited to parsing existing data, and then modifying/rebuilding it. I think there could be a straightforward way to do this. Bulding on your example:
# PNG is composed of a header, and multiple chunks. Each chunk has the format
#
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
# -----------------------------------------------------------------
# | LENGTH | TYPE | ... DATA ... | CRC |
#
# Where the LENGTH is the length of the DATA, and the CRC is the CRC of the
# TYPE and the DATA concatenated
BinULong = UInt(min=0, max=0x10000, pack=">L")
def chunk_crc(field):
return calc_crc(field.rel("cname") + field.rel("data"))
RDef("chunk",
BinULong(name="length"),
String(name="cname", min=4, max=4, charset=String.charset_alpha),
String(name="data", min=Rel("length"), max=Rel("length")),
BinULong(data=chunk_crc),
)
# --- OR having the length based on the data, which would have to be generated
# first
RDef("chunk",
BinULong(name="length", data=len(Rel("data"))),
String(name="cname", min=4, max=4, charset=String.charset_alpha),
String(name="data", min=0, max=0x10002),
BinULong(data=chunk_crc),
)
# --- OR Creating a custom Crc type that accepts as parameters the fields to
# calculate the CRC from
class CRC(UInt):
self.pack = ">L"
def __init__(self, *rel_field_names):
self.rel_field_names = rel_field_names
def build(self, pre=None, shortest=False, data=None):
res = And(*list(map(Rel, self.rel_field_names)))
crc = calc_crc_of_data(res)
return UInt.build(self, pre, shortest=False, data=crc)
RDef("chunk",
BinULong(name="length", data=len(Rel("data"))),
String(name="cname", min=4, max=4, charset=String.charset_alpha),
String(name="data", min=0, max=0x10002),
CRC("cname", "data"),
)
To make this work, I think we'd need to add/change/make sure that:
- gramfuzz can track the context that fields are built within
- all fields can accept an optional
name
parameter in their__init__
- this will make them referenceable - the context needs to be tied to the build context, not the field instance (field instances can be reused)
- the
Field
class should have arel()
function that can be used as a way to get a reference to a named field in the current context - the correct order to generate fields based on their dependencies (relations) would have to be determined, and the resulting built values cached in the current scope/context
- a
Rel
top-level field class should exist that can be used to define relationships to other named fields in the current context/scope
- all fields can accept an optional
- All
gramfuzz.fields.Field:build()
functions need to accept an optionaldata
argument, which may be:- a gramfuzz field that needs to be built (like
Rel
) - a python function that will return data to use, instead of randomly creating it
- a raw value
- a gramfuzz field that needs to be built (like
I have a javascript-specific wrapper of gramfuzz that I use for browser fuzzing that I haven't released that has some of this functionality, specifically tracking scope/context that things are built within and being able to reference other fields by name. This would be a decent amount of work, but definitely doable, and I think worthwhile.
Also, PRs are welcome too :^) If you @dzonerzy or anyone else wanted to start work on it, I should be able to be more responsive and provide feedback/direction, as I've taken less stressful employment.