class Metasm::COFF::ResourceDirectory

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