#!/usr/bin/env ruby # Rex::Struct2 module Rex module Struct2 require 'rex/struct2/s_struct' class CStruct_Values def initialize(obj) @obj = obj end def [](*args) o = @obj[*args] return if !o return o.value end def []=(*args) o = @obj[*args[0 .. -2]] return if !o o.value = args[-1] end # this one is for HD, the whiniest girl around... # allow for like v.field = whatever def method_missing(sym, *args) if sym.to_s[-1] == "="[0] return self[sym.to_s[0 .. -2]] = args[0] else return self[sym.to_s] end end end class CStruct < SStruct require 'rex/struct2/element' require 'rex/struct2/generic' require 'rex/struct2/s_string' require 'rex/struct2/c_struct_template' require 'rex/struct2/restraint' include Rex::Struct2::Element attr_reader :v @@dt_table = { 'int8' => proc { |*a| Rex::Struct2::Generic.new('C', true, *a) }, 'uint8' => proc { |*a| Rex::Struct2::Generic.new('C', false, *a) }, 'int16v' => proc { |*a| Rex::Struct2::Generic.new('v', true, *a) }, 'uint16v' => proc { |*a| Rex::Struct2::Generic.new('v', false, *a) }, 'int32v' => proc { |*a| Rex::Struct2::Generic.new('V', true, *a) }, 'uint32v' => proc { |*a| Rex::Struct2::Generic.new('V', false, *a) }, 'int64v' => proc { |*a| Rex::Struct2::Generic.new('q', true, *a) }, 'uint64v' => proc { |*a| Rex::Struct2::Generic.new('Q', false, *a) }, 'int16n' => proc { |*a| Rex::Struct2::Generic.new('n', true, *a) }, 'uint16n' => proc { |*a| Rex::Struct2::Generic.new('n', false, *a) }, 'int32n' => proc { |*a| Rex::Struct2::Generic.new('N', true, *a) }, 'uint32n' => proc { |*a| Rex::Struct2::Generic.new('N', false, *a) }, 'string' => proc { |*a| Rex::Struct2::SString.new(*a) }, 'sstruct' => proc { |*a| Rex::Struct2::SStruct.new(*a) }, 'object' => proc { |o| o }, 'template' => proc { |o| o.make_struct }, } # CStruct.typedef(name, factory, ... ) def CStruct.typedef(*args) while args.length >= 2 name = args.shift factory = args.shift @@dt_table[name] = factory end end def initialize(*dts) super() @name_table = [ ] @v = Rex::Struct2::CStruct_Values.new(self) return self.add_from_dt(*dts) end def add_from_dt(*dts) dts.each { | dt | return if !dt.kind_of?(Array) || dt.length < 2 type = dt[0] name = dt[1] factory = @@dt_table[type] return if !factory # call with the arguments passed in obj = factory.call(*(dt[2 .. -1])) self.add_object(name, obj) } return dts.length end def add_object(*objs) while objs.length >= 2 @name_table << objs.shift self << objs.shift end end # apply_restraint( name, restraint, name2, restraint2 ... ) def apply_restraint(*ress) while ress.length >= 2 name = ress.shift res = ress.shift self[name].restraint = res # update the restrainted object, so it will update the value # of the restrainter, with the initial size. If you don't # want this behavior, um, you'll have to be careful with what # you supply as default values... self[name].update_restraint end return self end # create_restraints( [ name, stuff_to_restraint_constructor ] ... ) def create_restraints(*ress) ress.each { |r| # make a copy before we modify... r = r.dup # resolve names into objects r[1] = self[r[1]] if r[1] r[2] = self[r[2]] if r[2] # build and apply the restraint self.apply_restraint(r[0], Rex::Struct2::Restraint.new(*r[1 .. -1])) } return self end # ya ya, I know, these are weird. I'm not sure why I even bothered # to inherit from array... def [](index, *other) if index.kind_of?(String) i = @name_table.index(index) return if !i return super(i) else return super(index, *other) end end def []=(index, *other) if index.kind_of?(String) i = @name_table.index(index) return if !i return super(i, *other) else return super(index, *other) end end # Produce a list of field names def keys @name_table end # Iterate through all fields and values def each_pair(&block) @name_table.each do |k| block.call(k, self.v[k]) end end end # end Rex::Struct2 end end