Initial commit of the new Struct2 code
- lots of improvements over Struct / Struct2-perl - lots of bug fixes, and much stronger functionality - added an example test - comments on this all is encouraged git-svn-id: file:///home/svn/incoming/trunk@2330 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
6125a55d6c
commit
cedab15c8f
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# just a shim to load all of the Struct2 libraries
|
||||
|
||||
require 'Rex/Struct2/CStructTemplate'
|
|
@ -0,0 +1,150 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
require 'Rex/Struct2/SStruct'
|
||||
|
||||
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/SString'
|
||||
require 'Rex/Struct2/CStructTemplate'
|
||||
require 'Rex/Struct2/Restraint'
|
||||
|
||||
include Rex::Struct2::Element
|
||||
|
||||
attr_reader :v
|
||||
|
||||
@@dt_table = {
|
||||
'uint8' => [ proc { |*a| Rex::Struct2::Generic.new(*a) }, 'C' ],
|
||||
'uint16v' => [ proc { |*a| Rex::Struct2::Generic.new(*a) }, 'v' ],
|
||||
'uint32v' => [ proc { |*a| Rex::Struct2::Generic.new(*a) }, 'V' ],
|
||||
'uint16n' => [ proc { |*a| Rex::Struct2::Generic.new(*a) }, 'n' ],
|
||||
'uint32n' => [ proc { |*a| Rex::Struct2::Generic.new(*a) }, 'N' ],
|
||||
'string' => [ proc { |*a| Rex::Struct2::SString.new(*a) } ],
|
||||
'sstruct' => [ proc { |*a| Rex::Struct2::SStruct.new(*a) } ],
|
||||
'object' => [ proc { |o| o } ], # no class, treat as object...
|
||||
'struct' => [ proc { |o| o } ], # no class, treat as object...
|
||||
'cstruct' => [ proc { |o| o } ], # no class, treat as object...
|
||||
'template' => [ proc { |o| o.make_struct } ],
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
entry = @@dt_table[type]
|
||||
|
||||
return if !entry
|
||||
|
||||
factory = entry[0]
|
||||
|
||||
obj = factory.call(*(entry[1 .. -1] + 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
|
||||
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
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
class CStructTemplate
|
||||
|
||||
require 'Rex/Struct2/CStruct'
|
||||
|
||||
attr_reader :template, :template_create_restraints, :template_apply_restraint
|
||||
attr_writer :template, :template_create_restraints, :template_apply_restraint
|
||||
|
||||
def initialize(*tem)
|
||||
self.template = tem
|
||||
self.template_create_restraints = [ ]
|
||||
self.template_apply_restraint = [ ]
|
||||
end
|
||||
|
||||
def create_restraints(*ress)
|
||||
self.template_create_restraints = ress
|
||||
return self
|
||||
end
|
||||
|
||||
def apply_restraint(*ress)
|
||||
self.template_apply_restraint = ress
|
||||
return self
|
||||
end
|
||||
|
||||
def make_struct
|
||||
Rex::Struct2::CStruct.new(*self.template).
|
||||
create_restraints(*self.template_create_restraints).
|
||||
apply_restraint(*self.template_apply_restraint)
|
||||
end
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
# this is a "constant" element. It's not actually constant, you can set it
|
||||
# via the constructor and value. It doesn't do from_s/to_s, etc.
|
||||
|
||||
# what use is it? Well it's useful for doing constant restraints (like fix
|
||||
# sized arrays), and probably not a ton more.
|
||||
|
||||
class Constant
|
||||
|
||||
require 'Rex/Struct2/Element'
|
||||
include Rex::Struct2::Element
|
||||
|
||||
def initialize(value)
|
||||
self.value = value
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
module Element
|
||||
|
||||
# elements should have to_s, but we don't define it here because
|
||||
# it will just overlap with inheritence and cause issues
|
||||
|
||||
attr_reader :value, :restraint, :container
|
||||
attr_writer :restraint, :container
|
||||
|
||||
# update the restraints on any value change
|
||||
def value=(newval)
|
||||
@value = newval
|
||||
self.update_restraint
|
||||
end
|
||||
|
||||
# avoid conflicting with normal namespace length()
|
||||
def slength
|
||||
to_s().length()
|
||||
end
|
||||
|
||||
def update_restraint
|
||||
if self.restraint
|
||||
# Sort of a hack, but remove the restraint before we update, so we aren't using
|
||||
# the old restraint during calculating the restraint update value
|
||||
old_restraint, self.restraint = self.restraint, nil
|
||||
old_restraint.update(self.slength)
|
||||
self.restraint = old_restraint
|
||||
end
|
||||
|
||||
if self.container
|
||||
self.container.update_restraint
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
class Generic
|
||||
|
||||
require 'Rex/Struct2/Element'
|
||||
include Rex::Struct2::Element
|
||||
|
||||
attr_reader :default
|
||||
attr_writer :default
|
||||
|
||||
def initialize(packspec, default=nil)
|
||||
@packspec = packspec
|
||||
@default = default
|
||||
reset()
|
||||
end
|
||||
|
||||
def reset
|
||||
self.value = @default
|
||||
end
|
||||
|
||||
def to_s
|
||||
# I realize this will bomb out if this isn't an integer, for
|
||||
# example if it is nil. That should only happen for a user
|
||||
# error so that's what I want it to do...
|
||||
string = [ @value ].pack(@packspec)
|
||||
if restraint && restraint.max
|
||||
return string.slice(0, restraint.max)
|
||||
else
|
||||
return string
|
||||
end
|
||||
# what to do for restraint.min?!?
|
||||
end
|
||||
|
||||
def from_s(bytes)
|
||||
value = bytes.unpack(@packspec)[0]
|
||||
# return nil on unpack error
|
||||
return if !value
|
||||
len = slength()
|
||||
# error on any restraint issues
|
||||
return if restraint && restraint.max && len > restraint.max
|
||||
return if restraint && restraint.min && len < restraint.min
|
||||
# else set our value and return length used for this element
|
||||
self.value = value
|
||||
return(len)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
class Restraint
|
||||
|
||||
attr_reader :max_object, :min_object, :should_update,
|
||||
:max_transform, :min_transform, :max_inv_transform, :min_inv_transform
|
||||
attr_writer :max_object, :min_object, :should_update,
|
||||
:max_transform, :min_transform, :max_inv_transform, :min_inv_transform
|
||||
|
||||
|
||||
def initialize(
|
||||
max_object=nil, min_object=nil, should_update=false,
|
||||
max_transform=nil, min_transform=nil,
|
||||
max_inv_transform=nil, min_inv_transform=nil
|
||||
)
|
||||
@max_object = max_object
|
||||
@min_object = min_object
|
||||
@should_update = should_update
|
||||
|
||||
def_trans = proc {|i| i}
|
||||
|
||||
@max_transform = max_transform == nil ? def_trans : max_transform
|
||||
@min_transform = min_transform == nil ? def_trans : min_transform
|
||||
@max_inv_transform = max_inv_transform == nil ? def_trans : max_inv_transform
|
||||
@min_inv_transform = min_inv_transform == nil ? def_trans : min_inv_transform
|
||||
end
|
||||
|
||||
def min
|
||||
return if !min_object
|
||||
return min_object.value
|
||||
end
|
||||
|
||||
def max
|
||||
return if !max_object
|
||||
return max_object.value
|
||||
end
|
||||
|
||||
# update values if request (ie string set field to its length)
|
||||
def update(value)
|
||||
return if !@should_update
|
||||
|
||||
max_object.value = max_inv_transform.call(value) if max_object
|
||||
min_object.value = min_inv_transform.call(value) if min_object
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
class SString
|
||||
|
||||
require 'Rex/Struct2/Element'
|
||||
include Rex::Struct2::Element
|
||||
|
||||
attr_reader :size, :default, :pad
|
||||
attr_writer :default, :pad
|
||||
|
||||
def initialize(size=nil, default=nil, pad=nil)
|
||||
self.size = size
|
||||
@default = default
|
||||
@pad = pad
|
||||
reset()
|
||||
end
|
||||
|
||||
def size=(newsize)
|
||||
if !newsize
|
||||
self.restraint = nil
|
||||
else
|
||||
res = Rex::Struct2::Constant(size)
|
||||
self.restraint = Rex::Struct2::Restraint(res, res, true)
|
||||
end
|
||||
end
|
||||
|
||||
def reset
|
||||
self.value = @default
|
||||
end
|
||||
|
||||
def to_s
|
||||
string = self.value
|
||||
|
||||
return if !string
|
||||
|
||||
# pad if short
|
||||
if restraint && restraint.min && self.pad && restraint.min > string.length
|
||||
string += self.pad * (restraint.min - string.length)
|
||||
end
|
||||
# truncate if long
|
||||
if restraint && restraint.max
|
||||
string = string.slice(0, restraint.max)
|
||||
end
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
def from_s(bytes)
|
||||
# we don't have enough bytes to satisfy our minimum
|
||||
if restraint && restraint.min && bytes.length < restraint.min
|
||||
return
|
||||
end
|
||||
|
||||
if restraint && restraint.max
|
||||
self.value = bytes.slice(0, restraint.max)
|
||||
else
|
||||
self.value = bytes.dup
|
||||
end
|
||||
|
||||
return(self.slength)
|
||||
end
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/ruby
|
||||
|
||||
# Rex::Struct2
|
||||
module Rex
|
||||
module Struct2
|
||||
|
||||
class SStruct
|
||||
|
||||
require 'Rex/Struct2/Element'
|
||||
include Rex::Struct2::Element
|
||||
|
||||
attr_reader :leftover, :elements
|
||||
attr_writer :leftover, :elements
|
||||
|
||||
private :elements, :elements=
|
||||
|
||||
# watch out!, leftover returns our copy of the string! so don't do
|
||||
# anything stupid like struct.leftover.slice! !!
|
||||
|
||||
def initialize(*opts)
|
||||
self.elements = [ ]
|
||||
self.add_element(*opts)
|
||||
end
|
||||
|
||||
|
||||
def reset
|
||||
elements.each {|e| e.reset}
|
||||
return self
|
||||
end
|
||||
|
||||
def add_element(*objs)
|
||||
objs.each { |o|
|
||||
elements << o
|
||||
o.container = self
|
||||
}
|
||||
return self
|
||||
end
|
||||
|
||||
def <<(obj)
|
||||
self.add_element(obj)
|
||||
end
|
||||
|
||||
def to_s
|
||||
# !!! what do we do on mix restraint issues? just fail?
|
||||
# maybe throw an exception, because that is most likely
|
||||
# a usage error
|
||||
|
||||
if restraint && restraint.max
|
||||
return elements.to_s.slice(0, restraint.max)
|
||||
else
|
||||
return elements.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def length
|
||||
return elements.length
|
||||
end
|
||||
|
||||
def [](obj)
|
||||
return elements[obj]
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
return elements.each(&block)
|
||||
end
|
||||
|
||||
def from_s(obytes)
|
||||
# make my own copy so I can chop it up
|
||||
bytes = obytes.dup
|
||||
length = 0
|
||||
|
||||
# I don't think we should call update_restraint here, but
|
||||
# I could have mis thought or something
|
||||
|
||||
# if we have a restraint (and if there is a val) truncate
|
||||
if restraint
|
||||
max = restraint.max
|
||||
bytes = bytes.slice(0, max) if max
|
||||
end
|
||||
|
||||
elements.each { |e|
|
||||
used = e.from_s(bytes)
|
||||
return if !used
|
||||
bytes.slice!(0, used)
|
||||
length += used
|
||||
}
|
||||
|
||||
# make sure we matched out min restraint, else return failure
|
||||
if restraint
|
||||
min = restraint.min
|
||||
return if min && length < min
|
||||
end
|
||||
|
||||
# I guess this is me getting "set", so I should have a value
|
||||
# and I should update my restraints on set
|
||||
self.value = obytes.slice(0, length)
|
||||
|
||||
self.leftover = bytes
|
||||
return(length)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# end Rex::Struct2
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue