From 4d18c94906338e1f0dbd6e6c60d5f96bbddd6bb8 Mon Sep 17 00:00:00 2001 From: Sigurd Wien Date: Sun, 30 Oct 2011 19:24:00 +0100 Subject: [PATCH] Add support for complex arrays. "config types" not tested. No unittests. --- CSjark/csjark/config.py | 12 +- CSjark/csjark/cparser.py | 51 +++- CSjark/csjark/csjark.py | 1 + CSjark/csjark/dissector.py | 303 ++++++++++++--------- CSjark/csjark/headers/complex_array_test.h | 27 ++ CSjark/csjark/headers/enum_array_test.h | 6 + 6 files changed, 255 insertions(+), 145 deletions(-) create mode 100644 CSjark/csjark/headers/complex_array_test.h create mode 100644 CSjark/csjark/headers/enum_array_test.h diff --git a/CSjark/csjark/config.py b/CSjark/csjark/config.py index 3f58b94e..ec25cdf6 100644 --- a/CSjark/csjark/config.py +++ b/CSjark/csjark/config.py @@ -85,7 +85,17 @@ def create_field(self, proto, name, ctype, size=None, alignment_size = None): # Create basic Field if no rules fired return proto.add_field(name, type_, size, alignment_size) - + + def get_field_attributes(self, name, ctype): + """Create a field depending on rules.""" + # Sort the rules + types = (Bitstring, Enum, Range, Custom) + values = [[], [], [], []] + for rule in self.get_rules(name, ctype): + for i, tmp in enumerate(types): + if isinstance(rule, tmp): + values[i].append(rule) + return values class BaseRule: """A base class for rules referring to protocol fields.""" diff --git a/CSjark/csjark/cparser.py b/CSjark/csjark/cparser.py index 5edd272f..ea26ca1a 100644 --- a/CSjark/csjark/cparser.py +++ b/CSjark/csjark/cparser.py @@ -232,7 +232,7 @@ def handle_array_decl(self, node, depth=None): # String array if (isinstance(child, c_ast.TypeDecl) and - child.children()[0].names[0] == 'char'): + hasattr(child.children()[0], 'names') and child.children()[0].names[0] == 'char'): #hack size *= self.size_of('char') return child.declname, 'string', size, self.alignment('char'), depth @@ -244,29 +244,52 @@ def handle_array_decl(self, node, depth=None): # Single dimensional normal array depth.append(size) - ctype = self._get_type(child.children()[0]) + + if isinstance(child.children()[0], c_ast.IdentifierType): + ctype = self._get_type(child.children()[0]) - # Support for typedef - if ctype in self.aliases: - token, ctype = self.aliases[ctype] - if token == 'array': # Hack I think - return child.declname, ctype[1], ctype[2], ctype[3], depth+ctype[4] - elif token is not None: - print(token, ctype) # TODO - raise ParseError('Incomplete support for array of typedefs') - - return child.declname, ctype, self.size_of(ctype), self.alignment(ctype), depth + # Typedef type, which is an alias for another type + if ctype in self.aliases: + token, base = self.aliases[ctype] + if token in ('struct', 'union'): + subproto = self.all_protocols[base, self.platform] + return child.declname, subproto, subproto.get_size(), subproto.get_alignment_size(), depth + elif token == 'enum': + return child.declname, token, self.size_of(token), self.alignment(token), depth, self.enums[ctype] + elif token == 'array': + tmp, ctype, size, alignment, inner_depth = base + return child.declname, ctype, size, alignment, depth + inner_depth + else: + base_size = self.size_of(base) + base_alignment_size = self.alignment(base) + return child.declname, base, base_size, base_alignment_size, depth + else: + base_size = self.size_of(ctype) + base_alignment_size = self.alignment(ctype) + return child.declname, ctype, base_size, base_alignment_size, depth + # Enum + elif isinstance(child.children()[0], c_ast.Enum): + return child.declname, 'enum', self.size_of('enum'), self.alignment('enum'), depth, self.enums[child.children()[0].name] + # Union and struct + elif isinstance(child.children()[0], c_ast.Union) or isinstance(child.children()[0], c_ast.Struct): + subproto = self.all_protocols[child.children()[0].name, self.platform] + return child.declname, subproto, subproto.get_size(), subproto.get_alignment_size(), depth + # Error + else: + + raise ParseError('Unknown type in array declaration: %s' % repr(child.children()[0])) def handle_protocol(self, proto, name, proto_name): """Add an protocol field or union field to the protocol.""" sub_proto = StructVisitor.all_protocols[(proto_name, self.platform)] return proto.add_protocol(name, sub_proto) - def handle_array(self, proto, name, ctype, size, alignment, depth): + def handle_array(self, proto, name, ctype, size, alignment, depth, enum_members = None): """Add an ArrayField to the protocol.""" if not depth: return self.handle_field(proto, name, ctype, size, alignment) - return proto.add_array(name, self.map_type(ctype), size, alignment, depth) + + return proto.add_array(name, ctype, size, alignment, depth, enum_members) def handle_pointer(self, node, proto): """Find member details in a pointer declaration.""" diff --git a/CSjark/csjark/csjark.py b/CSjark/csjark/csjark.py index fa480161..3486f0fa 100644 --- a/CSjark/csjark/csjark.py +++ b/CSjark/csjark/csjark.py @@ -157,6 +157,7 @@ def create_dissectors(filename): # Silence errors if not in strict mode except Exception as err: + Options.strict = 1 if Options.strict: raise else: diff --git a/CSjark/csjark/dissector.py b/CSjark/csjark/dissector.py index 3d556704..855b51db 100644 --- a/CSjark/csjark/dissector.py +++ b/CSjark/csjark/dissector.py @@ -77,22 +77,37 @@ def __init__(self, proto, name, type, size, alignment_size): self.desc = None # Description of the field self.offset = None # Useful for others to access buffer(offset, size) - def get_definition(self): + def get_definition(self, sequence = None): """Get the ProtoField definition for this field.""" - return self._create_field(self.var, self.type, self.abbr, + variable_name = self.var + if sequence != None: + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + variable_name = "%s%s" % (self.var, postfix) + + return self._create_field(variable_name, self.type, self.abbr, self.name, self.base, self.values, self.mask, self.desc) - def get_code(self, offset, store=None): + def get_code(self, offset, store=None, sequence = None, tree = 'subtree'): """Get the code for dissecting this field.""" + variable_name = self.var + + if sequence != None: + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + variable_name = self.var + postfix + if store: store = 'local {var} = '.format(var=create_lua_var(store)) else: store = '' - offset = self._get_padded_offset(offset) + self.offset = offset - t = '\t{store}subtree:{add}({var}, buffer({offset}, {size}))' - return t.format(store=store, add=self.add_var, - var=self.var, offset=offset, size=self.size) + t = '\t{store}{tree}:{add}({var}, buffer({offset}, {size}))' + return t.format(store=store, tree=tree, add=self.add_var, + var=variable_name, offset=offset, size=self.size) def _get_padded_offset(self, offset): padding = 0 @@ -180,163 +195,176 @@ def __init__(self, proto, name, type, size, alignment_size, values, strict=True) self.values_var = None self.tree_var = create_lua_var(self.name) - def get_definition(self): + def get_definition(self, sequence = None): """Get the ProtoField definition for this field.""" + variable_name = self.var + if sequence != None: + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + variable_name = "%s%s" % (self.var, postfix) data = [] - if self.strict: + if self.strict and (sequence == None or sequence[len(sequence) - 1] == 0): data.append('local {var} = {values}'.format( var=self.values_var, values=self.values)) - data.append(self._create_field(self.var, self.type, self.abbr, + data.append(self._create_field(variable_name, self.type, self.abbr, self.name, self.base, self.values_var, self.mask, self.desc)) return '\n'.join(data) - def get_code(self, offset, store=None): + def get_code(self, offset, store=None, sequence = None, tree = 'subtree'): """Get the code for dissecting this field.""" - offset = self._get_padded_offset(offset) data = [] + postfix = '' + if sequence != None: + for index in sequence: + postfix = '%s_%i' % (postfix, index) # Local var definitions if store is not None: self.tree_var = store if self.strict: - store = self.tree_var - data.append(super().get_code(offset, store=store)) + store = self.tree_var + postfix + data.append(super().get_code(offset, store, sequence, tree)) # Add a test which validates the enum value if self.strict: data.append('\tif (%s[buffer(%i, %i):%s()] == nil) then' % ( self.values_var, offset, self.size, self.func_type)) data.append('\t\t%s:add_expert_info(PI_MALFORMED, PI_WARN, "%s")' - % (self.tree_var, 'Invalid value, not in (%s)' % self.keys)) + % (create_lua_var(self.tree_var + postfix), 'Invalid value, not in (%s)' % self.keys)) data.append('\tend') return '\n'.join(data) - - + class ArrayField(Field): - def __init__(self, proto, name, type, base_size, alignment_size, depth): + def __init__(self, proto, name, type, base_size, alignment_size, depth, enum_members = None): self.base_size = base_size self.depth = depth - self.elements = 1 # Number of total elements in the array + array_size = 1 for size in self.depth: - self.elements *= size - - super().__init__(proto, name, type, self.elements * self.base_size, alignment_size) - self.func_type = self._get_func_type() - - def get_definition(self): - data = ['-- Array definition for %s' % self.name] + array_size *= size + + self.elements = depth.pop() + if not depth: + self.field = self._make_field(proto, name, type, base_size, alignment_size, enum_members) + else: + self.field = ArrayField(proto, name, type, base_size, alignment_size, depth) + + super().__init__(proto, name, type, array_size * base_size, alignment_size) + + def _make_field(self, proto, name, type, size, alignment_size, enum_members): + if isinstance(type, Protocol) or isinstance(type, UnionProtocol): + return ProtocolField(proto, name, type) + + ctype = proto.platform.map_type(type) + if enum_members != None: + return EnumField(proto, name, ctype, size, alignment_size, enum_members) + + if proto.conf: + bits, enums, ranges, customs = proto.conf.get_field_attributes(name, type) + + ctype = proto.platform.map_type(type) + + # Custom field rules + if customs: + custom = customs[0] + if(custom.size != size or custom.alignment_size != alignment_size): + raise "Error: todo" + field = Field(proto, name, custom.field, size, alignment_size) + field.abbr = custom.abbr + field.base = custom.base + if custom.values: + field.values = create_lua_valuestring(custom.values) + field.mask = custom.mask + field.desc = custom.desc + return field + + # Bitstring rules + if bits: + return BitField(proto, name, ctype, size, alignment_size, bits[0].bits) + + # Enum rules + if enums: + rule = enums[0] + return EnumField(proto, name, ctype, size, alignment_size, rule.values, rule.strict) + # Range + if ranges: + rule = ranges[0] + return RangeField(proto, name, ctype, size, alignment_size, rule.min, rule.max) + + return Field(proto, name, ctype, size, alignment_size) + + def get_definition(self, sequence = []): + data = [''] + if sequence == []: + data = ['-- Array definition for %s' % self.name] type_ = self.type if type_ not in ('string', 'stringz'): type_ = 'bytes' - - # Top-level subtree - i = 0 - data.append(self._create_field('%s_%i' % ( - self.var, i), type_, self.abbr, self.name)) - i += 1 - - # Create fields for subtrees in the array - for k, size in enumerate(self.depth): - if len(self.depth) > 1 and k == len(self.depth) - 1: - continue # Multi-dim array, no subtree last depth level - for j in range(size): - data.append(self._create_field('%s_%i' % (self.var, i), - type_, self.abbr, self.name)) - i += 1 - - # Create fields for each element in the array - def traverse(depth, name, j): - size = depth.pop(0) - if len(depth) > 0: - for i in range(size): - j = traverse(depth[:], '%s%i, ' % (name, i), j) - return j - else: - for i in range(size): - data.append(self._create_field( - '%s__%i' % (self.var, j+i), self.type, - '%s.%i' % (self.abbr, j+i), '[%s%i]' % (name, i))) - return j + size - traverse(self.depth[:], '', 0) + + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + + # This subtree definition + data.append(self._create_field('%s%s' % ( + self.var, postfix), type_, self.abbr, self.name)) + + for i in range(0, self.elements): + definition = self.field.get_definition(sequence + [i]) + if definition: + data.append(definition) return '\n'.join(data) - def get_code(self, offset): - data = ['\t-- Array handling for %s' % self.name] - element = 0 # Count of which array element we have created - subtree = 0 # Count of which subtree we have created - offset = self._get_padded_offset(offset) - - def subdefinition(tree, parent, elem, name=''): - """Create a subtree of arrays.""" - nonlocal element, offset, subtree - - # Create the subtree - size = elem * self.base_size - t = '\tlocal {tree} = {old}:{add}({var}_{i}, buffer({off}, {size}))' - data.append(t.format(tree=tree, old=parent, add=self.add_var, - var=self.var, i=subtree, off=offset, size=size)) - - # Set a more usefull text to the subtree - if name: - name += ' ' - t = '\t{tree}:set_text("{name}(array: {size} x {type})")' - data.append(t.format(tree=tree, - name=name, size=elem, type=self.type)) - - subtree += 1 - - def addfield(tree): - """Add value from buffer to the elements field.""" - nonlocal element, offset - t = '\t{tree}:{add}({var}__{i}, buffer({offset}, {size}))' - data.append(t.format(tree=tree, offset=offset, add=self.add_var, - size=self.base_size, var=self.var, i=element)) - element += 1 - offset += self.base_size - - def array(depth, tree, parent): - """Recursively add elements to array tree.""" - if len(depth) == 1: - for i in range(depth[0]): - addfield(tree) + def get_code(self, offset, tree = 'arraytree', parent = 'subtree', name='', sequence=[]): + data = [] + if sequence == []: + data = ['\t-- Array handling for %s' % self.name] + + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + + t = '\tlocal {tree} = {parent}:{add}({var}{postfix}, buffer({off}, {size}))' + data.append(t.format(tree=tree, parent=parent, add=self.add_var, + var=self.var, postfix = postfix, off=offset, size=self.size)) + + t = '\t{tree}:set_text("{type} array: index{index})")' + data.append(t.format(tree=tree, type=self.type, index = postfix)) + for i in range(0, self.elements): + if isinstance(self.field, ArrayField): + data.append(self.field.get_code(offset, 'sub' + tree, tree, name, sequence + [i])) + offset += self.field.size else: - size = depth.pop(0) - elements = 1 - for k in depth: - elements *= k - parent = tree - tree = 'sub%s' % tree - for i in range(size): - subdefinition(tree, parent, elements) - array(depth[:], tree, parent) - - parent = 'subtree' - tree = 'arraytree' - subdefinition(tree, parent, self.elements, self.name) - array(self.depth[:], tree, parent) + data.append(self.field.get_code(offset, None, sequence + [i], tree)) + offset += self.field.size data.append('') return '\n'.join(data) - class ProtocolField(Field): def __init__(self, proto, name, sub_proto): super().__init__(proto, name, sub_proto.name, sub_proto.get_size(), sub_proto.get_alignment_size()) self.proto_name = sub_proto.longname - def get_definition(self): + def get_definition(self, sequence = None): pass - def get_code(self, offset): - offset = self._get_padded_offset(offset) + def get_code(self, offset, store = None, sequence = None, tree = 'subtree'): + variable_name = self.name + if sequence != None: + postfix = ''; + for index in sequence: + postfix = '%s_%i' % (postfix, index) + variable_name = self.var + postfix + t = '\tpinfo.private.caller_def_name = "{name}"\n'\ '\tDissector.get("{proto}"):call('\ - 'buffer({offset},{size}):tvb(), pinfo, subtree)' - return t.format(name=self.name, proto=self.proto_name, - dt=self.proto.DISSECTOR_TABLE, offset=offset, size=self.size) + 'buffer({offset},{size}):tvb(), pinfo, {tree})' + return t.format(name=variable_name, proto=self.proto_name, + dt=self.proto.DISSECTOR_TABLE, offset=offset, size=self.size, tree=tree) class BitField(Field): @@ -350,9 +378,14 @@ def _bit_var(self, name): def _bit_abbr(self, name): return '%s.%s' % (self.abbr, name.replace(' ', '_')) - def get_definition(self): + def get_definition(self, sequence = None): data = ['-- Bitstring definitions for %s' % self.name] - + + postfix = '' + if sequence != None: + for index in sequence: + postfix = '%s_%i' % (postfix, index) + # Bitstrings need to be unsigned for HEX?? Research needed! if 'int' in self.type and not self.type.startswith('u'): type_ = 'u%s' % self.type @@ -360,7 +393,7 @@ def get_definition(self): type_ = self.type # Create bitstring tree definition - data.append(self._create_field(self.var, type_, + data.append(self._create_field(self.var + postfix, type_, self.abbr, '%s (bitstring)' % self.name, base='base.HEX')) # Create definitions for all bits @@ -373,22 +406,26 @@ def get_definition(self): mask = '0x%x' % int(''.join(str(i) for i in tmp), 2) values = create_lua_valuestring(values) - data.append(self._create_field(self._bit_var(name), type_, + data.append(self._create_field(self._bit_var(name) + postfix, type_, self._bit_abbr(name), name, values=values, mask=mask)) return '\n'.join(data) - def get_code(self, offset): - offset = self._get_padded_offset(offset) + def get_code(self, offset, store = None, sequence = None, tree = 'subtree'): data = ['\t-- Bitstring handling for %s' % self.name] + + postfix = '' + if sequence != None: + for index in sequence: + postfix = '%s_%i' % (postfix, index) buff = 'buffer({off}, {size})'.format(off=offset, size=self.size) - t = '\tlocal bittree = subtree:{add}({var}, {buff})' - data.append(t.format(add=self.add_var, var=self.var, buff=buff)) + t = '\tlocal bittree = {tree}:{add}({var}, {buff})' + data.append(t.format(tree = tree, add=self.add_var, var=self.var + postfix, buff=buff)) for i, j, name, values in self.bits: data.append('\tbittree:{add}({var}, {buff})'.format( - add=self.add_var, var=self._bit_var(name), buff=buff)) + add=self.add_var, var=self._bit_var(name) + postfix, buff=buff)) data.append('') return '\n'.join(data) @@ -401,15 +438,19 @@ def __init__(self, proto, name, type, size, alignment_size, min, max): self.max = max self.func_type = self._get_func_type() - def get_code(self, offset): + def get_code(self, offset, sequence = None, tree = 'subtree'): """Get the code for dissecting this field.""" - offset = self._get_padded_offset(offset) data = [] + + postfix = '' + if sequence != None: + for index in sequence: + postfix = '%s_%i' % (postfix, index) # Local var definitions - t = '\tlocal {name} = subtree:{add}({var}, buffer({off}, {size}))' - data.append(t.format(var=self.var, name=create_lua_var(self.name), - add=self.add_var, off=offset, size=self.size)) + t = '\tlocal {name} = {tree}:{add}({var}, buffer({off}, {size}))' + data.append(t.format(var=self.var + postfix, name=create_lua_var(self.name), + tree=tree, add=self.add_var, off=offset, size=self.size)) # Test the value def create_test(value, test, warn): @@ -562,6 +603,7 @@ def _fields_code(self): """Add the code from each field into dissector function.""" offset = 0 for field in self.fields: + offset = field._get_padded_offset(offset) code = field.get_code(offset) if self.conf and self.conf.cnf: code = self._cnf_field_code(field, code) @@ -695,6 +737,7 @@ def _fields_code(self): """Add the code from each field into dissector function.""" offset = 0 for field in self.fields: + offset = field._get_padded_offset(offset) code = field.get_code(offset) if self.conf and self.conf.cnf: code = self._cnf_field_code(field, code) diff --git a/CSjark/csjark/headers/complex_array_test.h b/CSjark/csjark/headers/complex_array_test.h new file mode 100644 index 00000000..fc9d19af --- /dev/null +++ b/CSjark/csjark/headers/complex_array_test.h @@ -0,0 +1,27 @@ +#include "cenum_test.h" + +union testUnion{ + int int_member; + struct cenum_test cenum_test_member; + long long long_long_member; + short short_member; +}; + +typedef union testUnion typedef_union; + +typedef struct { + int a; + int b; +} typedef_struct; + +typedef int int_array[4]; + +struct complex_array_test { + char chararr1[16]; + union testUnion test_union_array[4]; + typedef_union typedef_union_array[6]; + int intarr2[2][2][2][2]; + struct cenum_test struct_array[4]; + typedef_struct typedef_struct_array[4]; + int_array int_array_array[4]; +}; diff --git a/CSjark/csjark/headers/enum_array_test.h b/CSjark/csjark/headers/enum_array_test.h new file mode 100644 index 00000000..0e5c9986 --- /dev/null +++ b/CSjark/csjark/headers/enum_array_test.h @@ -0,0 +1,6 @@ +#include "cenum_test.h" + +struct enum_array_test { + char chararr1[16]; + enum Months month_array[4]; +};