class Metasm::COFF::ResourceDirectory
tree-like structure, holds all misc data the program might need (icons, cursors, version information) conventionnally structured in a 3-level depth structure:
I resource type (icon/cursor/etc, see +TYPES+) II resource id (icon n1, icon 'toto', ...) III language-specific version (icon n1 en, icon n1 en-dvorak...)
for the icon, the one that appears in the explorer is
(NT) the one with the lowest ID (98) the first to appear in the table
Constants
- ACCELERATOR_BITS
- TYPE
Attributes
curoff_label[RW]
entries[RW]
Public Class Methods
from_hash(h, depth=0)
click to toggle source
# File metasm/exe_format/coff.rb, line 330 def self.from_hash(h, depth=0) map = case depth when 0; TYPE when 1; {} # resource-id when 2; {} # lang else {} end ret = new ret.entries = h.map { |k, v| e = Entry.new k.kind_of?(Integer) ? (e.id = k) : map.index(k) ? (e.id = map.index(k)) : (e.name = k) # name_w ? v.kind_of?(Hash) ? (e.subdir = from_hash(v, depth+1)) : (e.data = v) e } ret end
Public Instance Methods
decode(coff, edata = coff.curencoded, startptr = edata.ptr, maxdepth=3)
click to toggle source
Calls superclass method
Metasm::SerialStruct#decode
# File metasm/exe_format/coff_decode.rb, line 177 def decode(coff, edata = coff.curencoded, startptr = edata.ptr, maxdepth=3) super(coff, edata) @entries = [] nrnames = @nr_names if $DEBUG (@nr_names+@nr_id).times { e = Entry.new e_id = coff.decode_word(edata) e_ptr = coff.decode_word(edata) if not e_id.kind_of? Integer or not e_ptr.kind_of? Integer puts 'W: COFF: relocs in the rsrc directory?' if $VERBOSE next end tmp = edata.ptr if (e_id >> 31) == 1 if $DEBUG nrnames -= 1 puts "W: COFF: rsrc has invalid id #{e_id}" if nrnames < 0 end e.name_p = e_id & 0x7fff_ffff edata.ptr = startptr + e.name_p namelen = coff.decode_half(edata) e.name_w = edata.read(2*namelen) if (chrs = e.name_w.unpack('v*')).all? { |c| c >= 0 and c <= 255 } e.name = chrs.pack('C*') end else if $DEBUG puts "W: COFF: rsrc has invalid id #{e_id}" if nrnames > 0 end e.id = e_id end if (e_ptr >> 31) == 1 # subdir e.subdir_p = e_ptr & 0x7fff_ffff if startptr + e.subdir_p >= edata.length puts 'W: COFF: invalid resource structure: directory too far' if $VERBOSE elsif maxdepth > 0 edata.ptr = startptr + e.subdir_p e.subdir = ResourceDirectory.new e.subdir.decode coff, edata, startptr, maxdepth-1 else puts 'W: COFF: recursive resource section' if $VERBOSE end else e.dataentry_p = e_ptr edata.ptr = startptr + e.dataentry_p e.data_p = coff.decode_word(edata) sz = coff.decode_word(edata) e.codepage = coff.decode_word(edata) e.reserved = coff.decode_word(edata) if coff.sect_at_rva(e.data_p) e.data = coff.curencoded.read(sz) else puts 'W: COFF: invalid resource body offset' if $VERBOSE break end end edata.ptr = tmp @entries << e } end
decode_version(coff, lang=nil)
click to toggle source
# File metasm/exe_format/coff_decode.rb, line 247 def decode_version(coff, lang=nil) vers = {} decode_tllv = lambda { |ed, state| sptr = ed.ptr len, vlen = coff.decode_half(ed), coff.decode_half(ed) coff.decode_half(ed) # type tagname = '' while c = coff.decode_half(ed) and c != 0 tagname << (c&255) end ed.ptr = (ed.ptr + 3) / 4 * 4 case state when 0 raise if tagname != 'VS_VERSION_INFO' dat = ed.read(vlen) dat.unpack('V*').zip([:signature, :strucversion, :fileversionm, :fileversionl, :prodversionm, :prodversionl, :fileflagsmask, :fileflags, :fileos, :filetype, :filesubtype, :filedatem, :filedatel]) { |v, k| vers[k] = v } raise if vers[:signature] != 0xfeef04bd vers.delete :signature vers[:fileversion] = (vers.delete(:fileversionm) << 32) | vers.delete(:fileversionl) vers[:prodversion] = (vers.delete(:prodversionm) << 32) | vers.delete(:prodversionl) vers[:filedate] = (vers.delete(:filedatem) << 32) | vers.delete(:filedatel) nstate = 1 when 1 nstate = case tagname when 'StringFileInfo'; :strtable when 'VarFileInfo'; :var else raise end when :strtable nstate = :str when :str val = ed.read(vlen*2).unpack('v*') val.pop if val[-1] == 0 val = val.pack('C*') if val.all? { |c_| c_ > 0 and c_ < 256 } vers[tagname] = val when :var val = ed.read(vlen).unpack('V*') vers[tagname] = val end ed.ptr = (ed.ptr + 3) / 4 * 4 len = ed.length-sptr if len > ed.length-sptr while ed.ptr < sptr+len decode_tllv[ed, nstate] ed.ptr = (ed.ptr + 3) / 4 * 4 end } return if not e = @entries.find { |e_| e_.id == TYPE.index('VERSION') } e = e.subdir.entries.first.subdir e = e.entries.find { |e_| e_.id == lang } || e.entries.first ed = EncodedData.new(e.data) decode_tllv[ed, 0] vers #rescue end
encode(coff, edata = nil)
click to toggle source
compiles ressource directories
Calls superclass method
Metasm::SerialStruct#encode
# File metasm/exe_format/coff_encode.rb, line 243 def encode(coff, edata = nil) if not edata # init recursion edata = {} subtables = %w[table names dataentries data] subtables.each { |n| edata[n] = EncodedData.new } encode(coff, edata) return subtables.inject(EncodedData.new) { |sum, n| sum << edata[n] } end label = lambda { |n| coff.label_at(edata[n], 0, n) } # data 'rva' are real rvas (from start of COFF) rva_end = lambda { |n| Expression[[label[n], :-, coff.label_at(coff.encoded, 0)], :+, edata[n].virtsize] } # names and table 'rva' are relative to the beginning of the resource directory off_end = lambda { |n| Expression[[label[n], :-, coff.label_at(edata['table'], 0)], :+, edata[n].virtsize] } # build name_w if needed @entries.each { |e| e.name_w = e.name.unpack('C*').pack('v*') if e.name and not e.name_w } # fixup forward references to us, as subdir edata['table'].fixup @curoff_label => edata['table'].virtsize if defined? @curoff_label @nr_names = @entries.find_all { |e| e.name_w }.length @nr_id = @entries.find_all { |e| e.id }.length edata['table'] << super(coff) # encode entries, sorted by names nocase, then id @entries.sort_by { |e| e.name_w ? [0, e.name_w.downcase] : [1, e.id] }.each { |e| if e.name_w edata['table'] << coff.encode_word(Expression[off_end['names'], :|, 1 << 31]) edata['names'] << coff.encode_half(e.name_w.length/2) << e.name_w else edata['table'] << coff.encode_word(e.id) end if e.subdir e.subdir.curoff_label = coff.new_label('rsrc_curoff') edata['table'] << coff.encode_word(Expression[e.subdir.curoff_label, :|, 1 << 31]) else # data entry edata['table'] << coff.encode_word(off_end['dataentries']) edata['dataentries'] << coff.encode_word(rva_end['data']) << coff.encode_word(e.data.length) << coff.encode_word(e.codepage || 0) << coff.encode_word(e.reserved || 0) edata['data'] << e.data end } # recurse @entries.find_all { |e| e.subdir }.each { |e| e.subdir.encode(coff, edata) } end
to_hash(depth=0)
click to toggle source
# File metasm/exe_format/coff.rb, line 316 def to_hash(depth=0) map = case depth when 0; TYPE when 1; {} # resource-id when 2; {} # lang else {} end @entries.inject({}) { |h, e| k = e.id ? map.fetch(e.id, e.id) : e.name ? e.name : e.name_w v = e.subdir ? e.subdir.to_hash(depth+1) : e.data h.update k => v } end
to_s()
click to toggle source
returns a string with the to_hash
key tree
# File metasm/exe_format/coff.rb, line 348 def to_s to_s_a(0).join("\n") end
to_s_a(depth)
click to toggle source
# File metasm/exe_format/coff.rb, line 352 def to_s_a(depth) @entries.map { |e| ar = [] ar << if e.id if depth == 0 and TYPE.has_key?(e.id); "#{e.id.to_s} (#{TYPE[e.id]})".ljust(18) else e.id.to_s.ljust(5) end else (e.name || e.name_w).inspect end if e.subdir sa = e.subdir.to_s_a(depth+1) if sa.length == 1 ar.last << " | #{sa.first}" else ar << sa.map { |s| ' ' + s } end elsif e.data.length > 16 ar.last << " #{e.data[0, 8].inspect}... <#{e.data.length} bytes>" else ar.last << ' ' << e.data.inspect end ar }.flatten end