d0c-s4vage / gramfuzz

gramfuzz is a grammar-based fuzzer that lets one define complex grammars to generate text and binary data formats.
https://d0c-s4vage.github.io/gramfuzz/
MIT License
251 stars 39 forks source link

add feature - relations #19

Open dzonerzy opened 7 years ago

dzonerzy commented 7 years ago

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

d0c-s4vage commented 7 years ago

@dzonerzy that could be really useful

d0c-s4vage commented 4 years ago

@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:

d0c-s4vage commented 4 years ago

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.