Class: Metasm::SerialStruct
- Inherits:
-
Object
- Object
- Metasm::SerialStruct
- Includes:
- ExeFormat::IntToHash
- Defined in:
- lib/metasm/exe_format/serialstruct.rb,
lib/metasm/exe_format/main.rb
Overview
a class representing a simple structure serialized in a binary
Direct Known Subclasses
AOut::Header, AOut::Relocation, AOut::Symbol, Bflt::Header, COFF::SerialStruct, COFFArchive::ImportHeader, COFFArchive::Member, DEX::SerialStruct, Dol::Header, ELF::SerialStruct, FatELF::SerialStruct, MZ::Header, MZ::Relocation, MachO::SerialStruct, NDS::Header, NDS::Icon, UniversalBinary::FatArch, UniversalBinary::Header, XCoff::SerialStruct
Constant Summary collapse
- NAME =
0- DECODE =
1- ENCODE =
2- DEFVAL =
3- ENUM =
4- BITS =
5- @@fields =
hash shared by all classes key = class, value = array of fields field = array [name, decode…]
{}
Class Method Summary collapse
-
.bitfield(inttype, h) ⇒ Object
define a bitfield: many fields inside a single word/byte/whatever usage: bitfield :word, 0 => :lala, 1 => nil, 4 => :lolo, 8 => :foo => a bitfield read using exe.decode_word, containing 3 subfields: :lala (bits 0…1), (discard 3 bits), :lolo (bits 4…8), and :foo (bits 8..-1) fields default to 0.
-
.decode(*a) ⇒ Object
shortcut to create a new instance and decode it.
-
.decode_hook(before = nil, &b) ⇒ Object
inject a hook to be run during the decoding process.
- .fld_bits(name, bits = nil, &b) ⇒ Object
-
.fld_default(name, default = nil, &b) ⇒ Object
change the default for a field.
- .fld_enum(name, enum = nil, &b) ⇒ Object
-
.fld_get(name) ⇒ Object
field access.
-
.mem(name, len, defval = '') ⇒ Object
a fixed-size memory chunk.
-
.new_field(name, decode, encode, defval, enum = nil, bits = nil) ⇒ Object
defines a new field adds an accessor.
-
.new_int_field(*types) ⇒ Object
creates a field constructor for a simple integer relies on exe implementing (en,de)code_#type.
-
.str(name, len, defval = '') ⇒ Object
a fixed-size string, 0-padded.
-
.strz(name, defval = '') ⇒ Object
0-terminated string.
Instance Method Summary collapse
-
#decode(exe, *args) ⇒ Object
decodes the fields from the exe.
- #dump(e, a) ⇒ Object
-
#encode(exe, *a) ⇒ Object
sets default values, then encodes the fields, returns an EData.
-
#initialize(*a) ⇒ SerialStruct
constructor
set value of fields from argument list, runs int_to_hash if needed.
-
#set_default_values(exe) ⇒ Object
initialize uninitialized fields.
-
#struct_fields(exe = nil) ⇒ Object
returns this classes’ field array uses struct_specialized if defined (a method that returns another SerialStruct class whose fields should be used).
-
#to_s(a = []) ⇒ Object
displays the struct content, ordered by fields.
Methods included from ExeFormat::IntToHash
#bits_from_hash, #bits_to_hash, #int_from_hash, #int_to_hash
Constructor Details
#initialize(*a) ⇒ SerialStruct
set value of fields from argument list, runs int_to_hash if needed
135 136 137 138 139 140 141 142 143 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 135 def initialize(*a) if not a.empty? a.zip(struct_fields.reject { |f| not f[NAME] }).each { |v, f| v = int_to_hash(v, f[ENUM]) if f[ENUM] v = bits_to_hash(v, f[BITS]) if f[BITS] instance_variable_set f[NAME], v } end end |
Class Method Details
.bitfield(inttype, h) ⇒ Object
define a bitfield: many fields inside a single word/byte/whatever usage: bitfield :word, 0 => :lala, 1 => nil, 4 => :lolo, 8 => :foo
=> a bitfield read using exe.decode_word, containing 3 subfields:
:lala (bits 0...1), (discard 3 bits), :lolo (bits 4...8), and :foo (bits 8..-1)
fields default to 0
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 88 def bitfield(inttype, h) # XXX encode/decode very not threadsafe ! this is a Georges Foreman Guarantee. # could use a me.instance_variable.. # decode the value in a temp var d = lambda { |exe, me| @bitfield_val = exe.send("decode_#{inttype}") } # reset a temp var e = lambda { |exe, me, val| @bitfield_val = 0 ; nil } new_field(nil, d, e, nil) h = h.sort h.length.times { |i| # yay closure ! # get field parameters next if not name = h[i][1] off = h[i][0] nxt = h[i+1] mask = (nxt ? (1 << (nxt[0]-off))-1 : -1) # read the field value from the temp var d = lambda { |exe, me| (@bitfield_val >> off) & mask } # update the temp var with the field value, return nil e = lambda { |exe, me, val| @bitfield_val |= (val & mask) << off ; nil } new_field(name, d, e, 0) } # free the temp var d = lambda { |exe, me| @bitfield_val = nil } # return encoded temp var e = lambda { |exe, me, val| val = @bitfield_val @bitfield_val = nil exe.send("encode_#{inttype}", val) } new_field(nil, d, e, nil) end |
.decode(*a) ⇒ Object
shortcut to create a new instance and decode it
213 214 215 216 217 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 213 def self.decode(*a) s = new s.decode(*a) s end |
.decode_hook(before = nil, &b) ⇒ Object
inject a hook to be run during the decoding process
125 126 127 128 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 125 def decode_hook(before=nil, &b) idx = (before ? @@fields[self].index(fld_get(before)) : -1) @@fields[self].insert(idx, [nil, b]) end |
.fld_bits(name, bits = nil, &b) ⇒ Object
81 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 81 def fld_bits(name, bits=nil, &b) fld_get(name)[BITS] = bits||b end |
.fld_default(name, default = nil, &b) ⇒ Object
change the default for a field
76 77 78 79 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 76 def fld_default(name, default=nil, &b) default ||= b fld_get(name)[DEFVAL] = default end |
.fld_enum(name, enum = nil, &b) ⇒ Object
80 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 80 def fld_enum(name, enum=nil, &b) fld_get(name)[ENUM] = enum||b end |
.fld_get(name) ⇒ Object
field access
70 71 72 73 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 70 def fld_get(name) name = "@#{name}".to_sym @@fields[self].find { |f| f[NAME] == name } end |
.mem(name, len, defval = '') ⇒ Object
a fixed-size memory chunk
50 51 52 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 50 def mem(name, len, defval='') new_field(name, lambda { |exe, me| exe.curencoded.read(len) }, lambda { |exe, me, val| val[0, len].ljust(len, 0.chr) }, defval) end |
.new_field(name, decode, encode, defval, enum = nil, bits = nil) ⇒ Object
defines a new field adds an accessor
23 24 25 26 27 28 29 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 23 def new_field(name, decode, encode, defval, enum=nil, bits=nil) if name attr_accessor name name = "@#{name}".to_sym end (@@fields[self] ||= []) << [name, decode, encode, defval, enum, bits] end |
.new_int_field(*types) ⇒ Object
creates a field constructor for a simple integer relies on exe implementing (en,de)code_#type
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 33 def new_int_field(*types) recv = class << self ; self ; end types.each { |type| recv.send(:define_method, type) { |name, *args| new_field(name, "decode_#{type}".to_sym, "encode_#{type}".to_sym, args[0] || 0, args[1]) } # shortcut to define multiple fields of this type with default values recv.send(:define_method, "#{type}s") { |*names| names.each { |name| send type, name } } } end |
.str(name, len, defval = '') ⇒ Object
a fixed-size string, 0-padded
54 55 56 57 58 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 54 def str(name, len, defval='') e = lambda { |exe, me, val| val[0, len].ljust(len, 0.chr) } d = lambda { |exe, me| v = exe.curencoded.read(len) ; v = v[0, v.index(?\0)] if v.index(?\0) ; v } new_field(name, d, e, defval) end |
.strz(name, defval = '') ⇒ Object
0-terminated string
60 61 62 63 64 65 66 67 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 60 def strz(name, defval='') d = lambda { |exe, me| ed = exe.curencoded ed.read(ed.data.index(?\0, ed.ptr)-ed.ptr+1).chop } e = lambda { |exe, me, val| val + 0.chr } new_field(name, d, e, defval) end |
Instance Method Details
#decode(exe, *args) ⇒ Object
decodes the fields from the exe
156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 156 def decode(exe, *args) struct_fields(exe).each { |f| case d = f[DECODE] when Symbol; val = exe.send(d, *args) when Array; val = exe.send(*d) when Proc; val = d[exe, self] when nil; next end next if not f[NAME] if h = f[ENUM]; h = h[exe, self] if h.kind_of? Proc; val = int_to_hash( val, h) end if h = f[BITS]; h = h[exe, self] if h.kind_of? Proc; val = bits_to_hash(val, h) end instance_variable_set(f[NAME], val) } end |
#dump(e, a) ⇒ Object
219 220 221 222 223 224 225 226 227 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 219 def dump(e, a) case e when Integer; e >= 0x100 ? '0x%X'%e : e when String; e.length > 64 ? e[0, 62].inspect+'...' : e.inspect when Array; '[' + e.map { |i| dump(i, a) }.join(', ') + ']' when SerialStruct; a.include?(e) ? '...' : e.to_s(a) else e.inspect end end |
#encode(exe, *a) ⇒ Object
sets default values, then encodes the fields, returns an EData
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 189 def encode(exe, *a) set_default_values(exe, *a) ed = EncodedData.new struct_fields(exe).each { |f| if not f[NAME] ed << f[ENCODE][exe, self, nil] if f[ENCODE] next end val = instance_variable_get(f[NAME]) if h = f[ENUM]; h = h[exe, self] if h.kind_of? Proc; val = int_from_hash( val, h) end if h = f[BITS]; h = h[exe, self] if h.kind_of? Proc; val = bits_from_hash(val, h) end case e = f[ENCODE] when Symbol; val = exe.send(e, val) when Array; val = exe.send(e, *val) when Proc; val = e[exe, self, val] when nil; next end ed << val } ed end |
#set_default_values(exe) ⇒ Object
initialize uninitialized fields
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 172 def set_default_values(exe) struct_fields(exe).each { |f| if not f[NAME] f[DEFVAL][exe, self] if f[DEFVAL] next end # check existence to avoid a "warning: ivar @bla not initialized" next if instance_variables.map { |ivn| ivn.to_sym }.include?(f[NAME]) and instance_variable_get(f[NAME]) val = f[DEFVAL] val = val[exe, self] if val.kind_of? Proc if val.kind_of? Integer and h = f[ENUM]; h = h[exe, self] if h.kind_of? Proc; val = int_to_hash( val, h) end if val.kind_of? Integer and h = f[BITS]; h = h[exe, self] if h.kind_of? Proc; val = bits_to_hash(val, h) end instance_variable_set(f[NAME], val) } end |
#struct_fields(exe = nil) ⇒ Object
returns this classes’ field array uses struct_specialized if defined (a method that returns another
SerialStruct class whose fields should be used)
148 149 150 151 152 153 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 148 def struct_fields(exe=nil) klass = self.class klass = struct_specialized(exe) if respond_to? :struct_specialized raise "SerialStruct: no fields for #{klass}" if $DEBUG and not @@fields[klass] @@fields[klass] end |
#to_s(a = []) ⇒ Object
displays the struct content, ordered by fields
230 231 232 233 234 |
# File 'lib/metasm/exe_format/serialstruct.rb', line 230 def to_s(a=[]) ivs = instance_variables.map { |iv| iv.to_sym } ivs = (struct_fields.to_a.map { |f| f[NAME] }.compact & ivs) | ivs "<#{self.class} " + ivs.map { |iv| "#{iv}=#{dump(instance_variable_get(iv), a+[self])}" }.join(' ') + ">" end |