class Metasm::COFF

the COFF object file format mostly used on windows (PE/COFF)

Constants

BASE_RELOCATION_TYPE
CHARACTERISTIC_BITS
COMIMAGE_FLAGS
DEBUG_TYPE
DIRECTORIES
DLL_CHARACTERISTIC_BITS
MACHINE
ORDINAL_REGEX

NRELOC_OVFL means there are more than 0xffff reloc the reloc count must be set to 0xffff, and the real reloc count is the VA of the first relocation

RELOCATION_TYPE
SECTION_CHARACTERISTIC_BITS
SIGNATURE

PE+ is for 64bits address spaces

SUBSYSTEM
SYMBOL_BTYPE

lsb of symbol type, unused

SYMBOL_SECTION
SYMBOL_STORAGE
SYMBOL_TYPE

Attributes

bitsize[RW]
certificates[RW]
com_header[RW]
cursection[RW]
debug[RW]
delayimports[RW]
directory[RW]
endianness[RW]
export[RW]
header[RW]
imports[RW]
loadconfig[RW]
nodecode_relocs[RW]

boolean, set to true to have decode() ignore the base_relocs directory

optheader[RW]
relocations[RW]
resource[RW]
sections[RW]
symbols[RW]
tls[RW]

Public Class Methods

checksum(str, endianness = :little) click to toggle source

computes the checksum for a given COFF file may not work with overlapping sections

# File metasm/exe_format/coff_encode.rb, line 302
def self.checksum(str, endianness = :little)
        coff = load str
        coff.endianness = endianness
        coff.decode_header
        coff.encoded.ptr = 0

        flen = 0
        csum = 0
        # negate old checksum
        oldcs = coff.encode_word(coff.optheader.checksum)
        oldcs.ptr = 0
        csum -= coff.decode_half(oldcs)
        csum -= coff.decode_half(oldcs)

        # checksum header
        raw = coff.encoded.read(coff.optheader.headers_size)
        flen += coff.optheader.headers_size

        coff.sections.each { |s|
                coff.encoded.ptr = s.rawaddr
                raw << coff.encoded.read(s.rawsize)
                flen += s.rawsize
        }
        raw.unpack(endianness == :little ? 'v*' : 'n*').each { |s|
                csum += s
                csum = (csum & 0xffff) + (csum >> 16) if (csum >> 16) > 0
        }
        csum + flen
end
new(*a) click to toggle source
Calls superclass method Metasm::ExeFormat.new
# File metasm/exe_format/coff.rb, line 404
def initialize(*a)
        cpu = a.grep(CPU).first
        @nodecode_relocs = true if a.include? :nodecode_relocs

        @directory = {}       # DIRECTORIES.key => [rva, size]
        @sections = []
        @endianness = cpu ? cpu.endianness : :little
        @bitsize = cpu ? cpu.size : 32
        @header = Header.new
        @optheader = OptionalHeader.new
        super(cpu)
end

Public Instance Methods

addr_to_fileoff(addr) click to toggle source

address -> file offset handles LoadedPE

# File metasm/exe_format/coff_decode.rb, line 464
def addr_to_fileoff(addr)
        addr -= @load_address ||= @optheader.image_base
        return 0 if addr == 0 # sect_at_rva specialcases 0
        if s = sect_at_rva(addr)
                if s.respond_to? :virtaddr
                        addr - s.virtaddr + s.rawaddr
                else # header
                        addr
                end
        end
end
arch_encode_thunk(edata, import) click to toggle source

encodes a thunk to imported function

# File metasm/exe_format/coff_encode.rb, line 441
def arch_encode_thunk(edata, import)
        case @cpu.shortname
        when 'ia32', 'x64'
                shellcode = lambda { |c| Shellcode.new(@cpu).share_namespace(self).assemble(c).encoded }
                if @cpu.generate_PIC
                        if @cpu.shortname == 'x64'
                                edata << shellcode["#{import.thunk}: jmp [rip-$_+#{import.target}]"]
                                return
                        end
                        # sections starts with a helper function that returns the address of metasm_intern_geteip in eax (PIC)
                        if not @sections.find { |s| s.encoded and s.encoded.export['metasm_intern_geteip'] } and edata.empty?
                                edata << shellcode["metasm_intern_geteip: call 42f\n42:\npop eax\nsub eax, 42b-metasm_intern_geteip\nret"]
                        end
                        edata << shellcode["#{import.thunk}: call metasm_intern_geteip\njmp [eax+#{import.target}-metasm_intern_geteip]"]
                else
                        edata << shellcode["#{import.thunk}: jmp [#{import.target}]"]
                end
        else raise EncodeError, 'E: COFF: encode import thunk: unsupported architecture'
        end
end
assemble(*a) click to toggle source
# File metasm/exe_format/coff_encode.rb, line 952
def assemble(*a)
        parse(*a) if not a.empty?
        @source.each { |k, v|
                raise "no section named #{k} ?" if not s = @sections.find { |s_| s_.name == k }
                s.encoded << assemble_sequence(v, @cpu)
                v.clear
        }
end
autoimport(fallback_append='A') click to toggle source

try to resolve automatically COFF import tables from self.sections.encoded.relocations and WindowsExports::EXPORT if the relocation target is '<symbolname>' or 'iat_<symbolname>, link to the IAT address, if it is '<symbolname> + <expr>', link to a thunk (plt-like) if the relocation is not found, try again after appending 'fallback_append' to the symbol (eg wsprintf => wsprintfA)

# File metasm/exe_format/coff_encode.rb, line 1031
def autoimport(fallback_append='A')
        WindowsExports rescue return  # autorequire
        autoexports = WindowsExports::EXPORT.dup
        @sections.each { |s|
                next if not s.encoded
                s.encoded.export.keys.each { |e| autoexports.delete e }
        }
        @sections.each { |s|
                next if not s.encoded
                s.encoded.reloc.each_value { |r|
                        if r.target.op == :+ and not r.target.lexpr and r.target.rexpr.kind_of?(::String)
                                sym = target = r.target.rexpr
                                sym = sym[4..-1] if sym[0, 4] == 'iat_'
                        elsif r.target.op == :- and r.target.rexpr.kind_of?(::String) and r.target.lexpr.kind_of?(::String)
                                sym = thunk = r.target.lexpr
                        end
                        if not dll = autoexports[sym]
                                sym += fallback_append if sym.kind_of?(::String) and fallback_append.kind_of?(::String)
                                next if not dll = autoexports[sym]
                        end

                        @imports ||= []
                        next if @imports.find { |id| id.imports.find { |ii| ii.name == sym } }
                        if not id = @imports.find { |id_| id_.libname =~ /^#{dll}(\.dll)?$/ }
                                id = ImportDirectory.new
                                id.libname = dll
                                id.imports = []
                                @imports << id
                        end
                        if not i = id.imports.find { |i_| i_.name == sym }
                                i = ImportDirectory::Import.new
                                i.name = sym
                                id.imports << i
                        end
                        if (target and i.target and (i.target != target or i.thunk == target)) or
                           (thunk  and i.thunk  and (i.thunk  != thunk or  i.target == thunk))
                                puts "autoimport: conflict for #{target} #{thunk} #{i.inspect}" if $VERBOSE
                        else
                                i.target ||= new_label(target || 'iat_' + thunk)
                                i.thunk ||= thunk if thunk
                        end
                }
        }
end
cpu_from_headers() click to toggle source

returns a metasm CPU object corresponding to header.machine

# File metasm/exe_format/coff_decode.rb, line 795
def cpu_from_headers
        case @header.machine
        when 'I386'; Ia32.new
        when 'AMD64'; X86_64.new
        when 'R4000'; MIPS.new(:little)
        else raise "unknown cpu #{@header.machine}"
        end
end
create_relocation_tables() click to toggle source

creates the @relocations from sections.encoded.reloc

# File metasm/exe_format/coff_encode.rb, line 493
def create_relocation_tables
        @relocations = []

        # create a fake binding with all exports, to find only-image_base-dependant relocs targets
        # not foolproof, but works in standard cases
        startaddr = curaddr = label_at(@encoded, 0, 'coff_start')
        binding = {}
        @sections.each { |s|
                binding.update s.encoded.binding(curaddr)
                curaddr = Expression[curaddr, :+, s.encoded.virtsize]
        }

        # for each section.encoded, make as many RelocationTables as needed
        @sections.each { |s|

                # rt.base_addr temporarily holds the offset from section_start, and is fixed up to rva before '@reloc << rt'
                rt = RelocationTable.new

                s.encoded.reloc.each { |off, rel|
                        # check that the relocation looks like "program_start + integer" when bound using the fake binding
                        # XXX allow :i32 etc
                        if rel.endianness == @endianness and [:u32, :a32, :u64, :a64].include?(rel.type) and
                        rel.target.bind(binding).reduce.kind_of?(Expression) and
                        Expression[rel.target, :-, startaddr].bind(binding).reduce.kind_of?(::Integer)
                                # winner !

                                # build relocation
                                r = RelocationTable::Relocation.new
                                r.offset = off & 0xfff
                                r.type = { :u32 => 'HIGHLOW', :u64 => 'DIR64', :a32 => 'HIGHLOW', :a64 => 'DIR64' }[rel.type]

                                # check if we need to start a new relocation table
                                if rt.base_addr and (rt.base_addr & ~0xfff) != (off & ~0xfff)
                                        rt.base_addr = Expression[[label_at(s.encoded, 0, 'sect_start'), :-, startaddr], :+, rt.base_addr]
                                        @relocations << rt
                                        rt = RelocationTable.new
                                end

                                # initialize reloc table base address if needed
                                rt.base_addr ||= off & ~0xfff

                                (rt.relocs ||= []) << r
                        elsif $DEBUG and not rel.target.bind(binding).reduce.kind_of?(Integer)
                                puts "W: COFF: Ignoring weird relocation #{rel.inspect} when building relocation tables"
                        end
                }

                if rt and rt.relocs
                        rt.base_addr = Expression[[label_at(s.encoded, 0, 'sect_start'), :-, startaddr], :+, rt.base_addr]
                        @relocations << rt
                end
        }
end
curencoded() click to toggle source
# File metasm/exe_format/coff_decode.rb, line 418
def curencoded
        @cursection.encoded
end
decode() click to toggle source

decodes a COFF file (headers/exports/imports/relocs/sections) starts at encoded.ptr

# File metasm/exe_format/coff_decode.rb, line 780
def decode
        decode_header
        decode_exports
        decode_imports
        decode_resources
        decode_certificates
        decode_debug
        decode_tls
        decode_loadconfig
        decode_delayimports
        decode_com
        decode_relocs unless nodecode_relocs or ENV['METASM_NODECODE_RELOCS'] # decode relocs last
end
decode_byte( edata = curencoded) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 422
def decode_byte( edata = curencoded) ; edata.decode_imm(:u8,  @endianness) end
decode_certificates() click to toggle source

decodes certificate table

# File metasm/exe_format/coff_decode.rb, line 643
def decode_certificates
        if ct = @directory['certificate_table']
                @certificates = []
                @cursection = self
                if ct[0] > @encoded.length or ct[1] > @encoded.length - ct[0]
                        puts "W: COFF: invalid certificate_table #{'0x%X+0x%0X' % ct}" if $VERBOSE
                        ct = [ct[0], 1]
                end
                @encoded.ptr = ct[0]
                off_end = ct[0]+ct[1]
                off_end = @encoded.length if off_end > @encoded.length
                while @encoded.ptr < off_end
                        certlen = decode_word
                        certrev = decode_half
                        certtype = decode_half
                        certdat = @encoded.read(certlen)
                        @certificates << [certrev, certtype, certdat]
                end
        end
end
decode_com() click to toggle source

decode the COM Cor20 header

# File metasm/exe_format/coff_decode.rb, line 665
def decode_com
        if @directory['com_runtime'] and sect_at_rva(@directory['com_runtime'][0])
                @com_header = Cor20Header.decode(self)
                if sect_at_rva(@com_header.entrypoint)
                        curencoded.add_export new_label('com_entrypoint')
                end
                @com_header.decode_all(self)
        end
end
decode_debug() click to toggle source
# File metasm/exe_format/coff_decode.rb, line 741
def decode_debug
        if dd = @directory['debug'] and sect_at_rva(dd[0])
                @debug = []
                p0 = curencoded.ptr
                while curencoded.ptr < p0 + dd[1]
                        @debug << DebugDirectory.decode(self)
                end
                @debug.each { |dbg| dbg.decode_inner(self) }
        end
end
decode_delayimports() click to toggle source
# File metasm/exe_format/coff_decode.rb, line 771
def decode_delayimports
        if di = @directory['delay_import_table'] and sect_at_rva(di[0])
                @delayimports = DelayImportDirectory.decode_all(self)
        end
end
decode_exports() click to toggle source

decodes COFF export table from directory mark exported names as encoded.export

# File metasm/exe_format/coff_decode.rb, line 587
def decode_exports
        if @directory['export_table'] and sect_at_rva(@directory['export_table'][0])
                @export = ExportDirectory.decode(self)
                @export.exports.to_a.each { |e|
                        if e.name and sect_at_rva(e.target)
                                name = e.name
                        elsif e.ordinal and sect_at_rva(e.target)
                                name = "ord_#{@export.libname}_#{e.ordinal}"
                        end
                        e.target = curencoded.add_export new_label(name) if name
                }
        end
end
decode_half( edata = curencoded) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 423
def decode_half( edata = curencoded) ; edata.decode_imm(:u16, @endianness) end
decode_header() click to toggle source

decodes the COFF header, optional header, section headers marks entrypoint and directories as edata.expord

# File metasm/exe_format/coff_decode.rb, line 506
def decode_header
        @cursection ||= self
        @encoded.ptr ||= 0
        @sections = []
        @header.decode(self)
        optoff = @encoded.ptr
        @optheader.decode(self)
        decode_symbols if @header.num_sym != 0 and not @header.characteristics.include? 'DEBUG_STRIPPED'
        curencoded.ptr = optoff + @header.size_opthdr
        decode_sections
        if sect_at_rva(@optheader.entrypoint)
                curencoded.add_export new_label('entrypoint')
        end
        (DIRECTORIES - ['certificate_table']).each { |d|
                if @directory[d] and sect_at_rva(@directory[d][0])
                        curencoded.add_export new_label(d)
                end
        }
end
decode_imports() click to toggle source

decodes COFF import tables from directory mark iat entries as encoded.export

# File metasm/exe_format/coff_decode.rb, line 603
def decode_imports
        if @directory['import_table'] and sect_at_rva(@directory['import_table'][0])
                @imports = ImportDirectory.decode_all(self)
                iatlen = @bitsize/8
                @imports.each { |id|
                        if sect_at_rva(id.iat_p)
                                ptr = curencoded.ptr
                                id.imports.each { |i|
                                        if i.name
                                                name = new_label i.name
                                        elsif i.ordinal
                                                name = new_label "ord_#{id.libname}_#{i.ordinal}"
                                        end
                                        if name
                                                i.target ||= name
                                                r = Metasm::Relocation.new(Expression[name], "u#@bitsize".to_sym, @endianness)
                                                curencoded.reloc[ptr] = r
                                                curencoded.add_export new_label('iat_'+name), ptr, true
                                        end
                                        ptr += iatlen
                                }
                        end
                }
        end
end
decode_loadconfig() click to toggle source
# File metasm/exe_format/coff_decode.rb, line 765
def decode_loadconfig
        if lc = @directory['load_config'] and sect_at_rva(lc[0])
                @loadconfig = LoadConfig.decode(self)
        end
end
decode_reloc_amd64(r) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 722
def decode_reloc_amd64(r)
        case r.type
        when 'ABSOLUTE'
        when 'HIGHLOW'
                addr = decode_word
                if s = sect_at_va(addr)
                        label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
                        Metasm::Relocation.new(Expression[label], :u32, @endianness)
                end
        when 'DIR64'
                addr = decode_xword
                if s = sect_at_va(addr)
                        label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
                        Metasm::Relocation.new(Expression[label], :u64, @endianness)
                end
        else puts "W: COFF: Unsupported amd64 relocation #{r.inspect}" if $VERBOSE
        end
end
decode_reloc_i386(r) click to toggle source

decodes an I386 COFF relocation pointing to encoded.ptr

# File metasm/exe_format/coff_decode.rb, line 703
def decode_reloc_i386(r)
        case r.type
        when 'ABSOLUTE'
        when 'HIGHLOW'
                addr = decode_word
                if s = sect_at_va(addr)
                        label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
                        Metasm::Relocation.new(Expression[label], :u32, @endianness)
                end
        when 'DIR64'
                addr = decode_xword
                if s = sect_at_va(addr)
                        label = label_at(s.encoded, s.encoded.ptr, "xref_#{Expression[addr]}")
                        Metasm::Relocation.new(Expression[label], :u64, @endianness)
                end
        else puts "W: COFF: Unsupported i386 relocation #{r.inspect}" if $VERBOSE
        end
end
decode_relocs() click to toggle source

decode COFF relocation tables from directory

# File metasm/exe_format/coff_decode.rb, line 676
def decode_relocs
        if @directory['base_relocation_table'] and sect_at_rva(@directory['base_relocation_table'][0])
                end_ptr = curencoded.ptr + @directory['base_relocation_table'][1]
                @relocations = []
                while curencoded.ptr < end_ptr
                        @relocations << RelocationTable.decode(self)
                end

                # interpret as EncodedData relocations
                relocfunc = ('decode_reloc_' << @header.machine.downcase).to_sym
                if not respond_to? relocfunc
                        puts "W: COFF: unsupported relocs for architecture #{@header.machine}" if $VERBOSE
                        return
                end
                @relocations.each { |rt|
                        rt.relocs.each { |r|
                                if s = sect_at_rva(rt.base_addr + r.offset)
                                        e, p = s.encoded, s.encoded.ptr
                                        rel = send(relocfunc, r)
                                        e.reloc[p] = rel if rel
                                end
                        }
                }
        end
end
decode_resources() click to toggle source

decodes resources from directory

# File metasm/exe_format/coff_decode.rb, line 630
def decode_resources
        if @directory['resource_table'] and sect_at_rva(@directory['resource_table'][0])
                @resource = ResourceDirectory.decode(self)
        end
end
decode_section_body(s) click to toggle source

decodes a section content (allows simpler LoadedPE override)

# File metasm/exe_format/coff_decode.rb, line 575
def decode_section_body(s)
        raw = EncodedData.align_size(s.rawsize, @optheader.file_align)
        virt = s.virtsize
        virt = raw = s.rawsize if @header.size_opthdr == 0
        virt = raw if virt == 0
        virt = EncodedData.align_size(virt, @optheader.sect_align)
        s.encoded = @encoded[s.rawaddr, [raw, virt].min] || EncodedData.new
        s.encoded.virtsize = virt
end
decode_sections() click to toggle source

decode the COFF sections

# File metasm/exe_format/coff_decode.rb, line 543
def decode_sections
        @header.num_sect.times {
                @sections << Section.decode(self)
        }
        # now decode COFF object relocations
        @sections.each { |s|
                next if s.relocnr == 0
                curencoded.ptr = s.relocaddr
                s.relocs = []
                s.relocnr.times { s.relocs << RelocObj.decode(self) }
                new_label 'pcrel'
                s.relocs.each { |r|
                        next if not r.sym
                        case r.type
                        when 'DIR32'
                                s.encoded.reloc[r.va] = Metasm::Relocation.new(Expression[r.sym.name], :u32, @endianness)
                        when 'REL32'
                                l = new_label('pcrel')
                                s.encoded.add_export(l, r.va+4)
                                s.encoded.reloc[r.va] = Metasm::Relocation.new(Expression[r.sym.name, :-, l], :u32, @endianness)
                        end
                }
        } if not @header.characteristics.include?('RELOCS_STRIPPED')
        symbols.to_a.compact.each { |sym|
                next if not sym.sec_nr.kind_of? Integer
                next if sym.storage != 'EXTERNAL' and (sym.storage != 'STATIC' or sym.value == 0)
                next if not s = @sections[sym.sec_nr-1]
                s.encoded.add_export new_label(sym.name), sym.value
        }
end
decode_strz( edata = curencoded) click to toggle source
Calls superclass method Metasm::ExeFormat#decode_strz
# File metasm/exe_format/coff_decode.rb, line 426
def decode_strz( edata = curencoded) ; super(edata) ; end
decode_symbols() click to toggle source

decode the COFF symbol table (obj only)

# File metasm/exe_format/coff_decode.rb, line 527
def decode_symbols
        endptr = @encoded.ptr = @header.ptr_sym + 18*@header.num_sym
        strlen = decode_word
        @encoded.ptr = endptr
        strtab = @encoded.read(strlen)
        @encoded.ptr = @header.ptr_sym
        @symbols = []
        @header.num_sym.times {
                break if @encoded.ptr >= endptr or @encoded.ptr >= @encoded.length
                @symbols << Symbol.decode(self, strtab)
                # keep the reloc.sym_idx accurate
                @symbols.last.nr_aux.times { @symbols << nil }
        }
end
decode_tls() click to toggle source

decode TLS directory, including tls callback table

# File metasm/exe_format/coff_decode.rb, line 753
def decode_tls
        if @directory['tls_table'] and sect_at_rva(@directory['tls_table'][0])
                @tls = TLSDirectory.decode(self)
                if s = sect_at_va(@tls.callback_p)
                        s.encoded.add_export 'tls_callback_table'
                        @tls.callbacks.each_with_index { |cb, i|
                                @tls.callbacks[i] = curencoded.add_export "tls_callback_#{i}" if sect_at_rva(cb)
                        }
                end
        end
end
decode_version(lang=0x409) click to toggle source

decode the VERSION information from the resources (file version, os, copyright etc)

# File metasm/exe_format/coff_decode.rb, line 637
def decode_version(lang=0x409)
        decode_resources if not resource
        resource.decode_version(self, lang)
end
decode_word( edata = curencoded) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 424
def decode_word( edata = curencoded) ; edata.decode_imm(:u32, @endianness) end
decode_xword(edata = curencoded) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 425
def decode_xword(edata = curencoded) ; edata.decode_imm((@bitsize == 32 ? :u32 : :u64), @endianness) end
dump_section_header(addr, edata) click to toggle source
Calls superclass method Metasm::ExeFormat#dump_section_header
# File metasm/exe_format/coff_decode.rb, line 817
def dump_section_header(addr, edata)
        s = @sections.find { |s_| s_.virtaddr == addr-@optheader.image_base }
        s ? "\n.section #{s.name.inspect} base=#{Expression[addr]}" :
        addr == @optheader.image_base ? "// exe header at #{Expression[addr]}" : super(addr, edata)
end
each_section() { |encoded, l| ... } click to toggle source
# File metasm/exe_format/coff_decode.rb, line 486
def each_section
        if @header.size_opthdr == 0 and not @header.characteristics.include?('EXECUTABLE_IMAGE')
                @sections.each { |s|
                        next if not s.encoded
                        l = new_label(s.name)
                        s.encoded.add_export(l, 0)
                        yield s.encoded, l
                }
                return
        end
        base = @optheader.image_base
        base = 0 if not base.kind_of? Integer
        sz = @optheader.headers_size
        sz = EncodedData.align_size(@optheader.image_size, 4096) if @sections.empty?
        yield @encoded[0, sz], base
        @sections.each { |s| yield s.encoded, base + s.virtaddr }
end
encode(target='exe', want_relocs=true) click to toggle source

encode a COFF file, building export/import/reloc tables if needed creates the base relocation tables (need for references to IAT not known before) defaults to generating relocatable files, eg ALSR-aware pass want_relocs=false to avoid the file overhead induced by this

# File metasm/exe_format/coff_encode.rb, line 711
def encode(target='exe', want_relocs=true)
        @encoded = EncodedData.new
        label_at(@encoded, 0, 'coff_start')
        pre_encode_header(target, want_relocs)
        autoimport
        encode_exports if export
        encode_imports if imports
        encode_resource if resource
        encode_tls if tls
        create_relocation_tables if want_relocs
        encode_relocs if relocations
        encode_header
        encode_sections_fixup
        @encoded.data
end
encode_append_section(s) click to toggle source

adds a new compiler-generated section

# File metasm/exe_format/coff_encode.rb, line 340
def encode_append_section(s)
        if (s.virtsize || s.encoded.virtsize) < 4096
                # find section to merge with
                # XXX check following sections for hardcoded base address ?

                char = s.characteristics.dup
                secs = @sections.dup
                # do not merge non-discardable in discardable
                if not char.delete 'MEM_DISCARDABLE'
                        secs.delete_if { |ss| ss.characteristics.include? 'MEM_DISCARDABLE' }
                end
                # do not merge shared w/ non-shared
                if char.delete 'MEM_SHARED'
                        secs.delete_if { |ss| not ss.characteristics.include? 'MEM_SHARED' }
                else
                        secs.delete_if { |ss| ss.characteristics.include? 'MEM_SHARED' }
                end
                secs.delete_if { |ss| ss.virtsize.kind_of?(::Integer) or ss.rawsize.kind_of?(::Integer) or secs[secs.index(ss)+1..-1].find { |ss_| ss_.virtaddr.kind_of?(::Integer) } }

                # try to find superset of characteristics
                if target = secs.find { |ss| (ss.characteristics & char) == char }
                        target.encoded.align 8
                        puts "PE: merging #{s.name} in #{target.name} (#{target.encoded.virtsize})" if $DEBUG
                        s.encoded = target.encoded << s.encoded
                else
                        @sections << s
                end
        else
                @sections << s
        end
end
encode_byte(w) click to toggle source
# File metasm/exe_format/coff_encode.rb, line 333
def encode_byte(w)   Expression[w].encode(:u8,  @endianness, (caller if $DEBUG)) end
encode_exports() click to toggle source

encodes the export table as a new section, updates directory

# File metasm/exe_format/coff_encode.rb, line 373
def encode_exports
        edata = @export.encode self

        # must include name tables (for forwarders)
        @directory['export_table'] = [label_at(edata, 0, 'export_table'), edata.virtsize]

        s = Section.new
        s.name = '.edata'
        s.encoded = edata
        s.characteristics = %w[MEM_READ]
        encode_append_section s
end
encode_half(w) click to toggle source
# File metasm/exe_format/coff_encode.rb, line 334
def encode_half(w)   Expression[w].encode(:u16, @endianness, (caller if $DEBUG)) end
encode_header() click to toggle source

appends the header/optheader/directories/section table to @encoded

# File metasm/exe_format/coff_encode.rb, line 612
def encode_header
        # encode section table, add CONTAINS_* flags from other characteristics flags
        s_table = EncodedData.new

        @sections.each { |s|
                if s.characteristics.kind_of? Array and s.characteristics.include? 'MEM_READ'
                        if s.characteristics.include? 'MEM_EXECUTE'
                                s.characteristics |= ['CONTAINS_CODE']
                        elsif s.encoded
                                if s.encoded.rawsize == 0
                                        s.characteristics |= ['CONTAINS_UDATA']
                                else
                                        s.characteristics |= ['CONTAINS_DATA']
                                end
                        end
                end
                s.rawaddr = nil if s.rawaddr.kind_of?(::Integer)     # XXX allow to force rawaddr ?
                s_table << s.encode(self)
        }

        # encode optional header
        @optheader.image_size   ||= new_label('image_size')
        @optheader.image_base   ||= label_at(@encoded, 0)
        @optheader.headers_size ||= new_label('headers_size')
        @optheader.checksum     ||= new_label('checksum')
        @optheader.subsystem    ||= 'WINDOWS_GUI'
        @optheader.numrva = nil
        opth = @optheader.encode(self)

        # encode header
        @header.machine ||= 'UNKNOWN'
        @header.num_sect ||= sections.length
        @header.time ||= Time.now.to_i & -255
        @header.size_opthdr ||= opth.virtsize
        @encoded << @header.encode(self) << opth << s_table
end
encode_imports() click to toggle source

encodes the import tables as a new section, updates directory and directory

# File metasm/exe_format/coff_encode.rb, line 387
def encode_imports
        idata, iat = ImportDirectory.encode(self, @imports)

        @directory['import_table'] = [label_at(idata, 0, 'idata'), idata.virtsize]

        s = Section.new
        s.name = '.idata'
        s.encoded = idata
        s.characteristics = %w[MEM_READ MEM_WRITE MEM_DISCARDABLE]
        encode_append_section s

        if @imports.first and @imports.first.iat_p.kind_of?(Integer)
                # ordiat = iat.sort_by { @import[x].iat_p }
                ordiat = @imports.zip(iat).sort_by { |id, it| id.iat_p.kind_of?(Integer) ? id.iat_p : 1<<65 }.map { |id, it| it }
        else
                ordiat = iat
        end

        @directory['iat'] = [label_at(ordiat.first, 0, 'iat'),
                Expression[label_at(ordiat.last, ordiat.last.virtsize, 'iat_end'), :-, label_at(ordiat.first, 0)]] if not ordiat.empty?

        iat_s = nil

        plt = Section.new
        plt.name = '.plt'
        plt.encoded = EncodedData.new
        plt.characteristics = %w[MEM_READ MEM_EXECUTE]

        @imports.zip(iat) { |id, it|
                if id.iat_p.kind_of?(Integer) and @sections.find { |s_| s_.virtaddr <= id.iat_p and s_.virtaddr + (s_.virtsize || s_.encoded.virtsize) > id.iat_p }
                        id.iat = it # will be fixed up after encode_section
                else
                        # XXX should not be mixed (for @directory['iat'][1])
                        if not iat_s
                                iat_s = Section.new
                                iat_s.name = '.iat'
                                iat_s.encoded = EncodedData.new
                                iat_s.characteristics = %w[MEM_READ MEM_WRITE]
                                encode_append_section iat_s
                        end
                        iat_s.encoded << it
                end

                id.imports.each { |i|
                        if i.thunk
                                arch_encode_thunk(plt.encoded, i)
                        end
                }
        }

        encode_append_section plt if not plt.encoded.empty?
end
encode_relocs() click to toggle source

encodes relocation tables in a new section .reloc, updates @directory

# File metasm/exe_format/coff_encode.rb, line 474
def encode_relocs
        if @relocations.empty?
                rt = RelocationTable.new
                rt.base_addr = 0
                rt.relocs = []
                @relocations << rt
        end
        relocs = @relocations.inject(EncodedData.new) { |edata, rt_| edata << rt_.encode(self) }

        @directory['base_relocation_table'] = [label_at(relocs, 0, 'reloc_table'), relocs.virtsize]

        s = Section.new
        s.name = '.reloc'
        s.encoded = relocs
        s.characteristics = %w[MEM_READ MEM_DISCARDABLE]
        encode_append_section s
end
encode_resource() click to toggle source
# File metasm/exe_format/coff_encode.rb, line 547
def encode_resource
        res = @resource.encode self

        @directory['resource_table'] = [label_at(res, 0, 'resource_table'), res.virtsize]

        s = Section.new
        s.name = '.rsrc'
        s.encoded = res
        s.characteristics = %w[MEM_READ]
        encode_append_section s
end
encode_sections_fixup() click to toggle source

append the section bodies to @encoded, and link the resulting binary

# File metasm/exe_format/coff_encode.rb, line 650
def encode_sections_fixup
        if @optheader.headers_size.kind_of?(::String)
                @encoded.fixup! @optheader.headers_size => @encoded.virtsize
                @optheader.headers_size = @encoded.virtsize
        end
        @encoded.align @optheader.file_align

        baseaddr = @optheader.image_base.kind_of?(::Integer) ? @optheader.image_base : 0x400000
        binding = @encoded.binding(baseaddr)

        curaddr = baseaddr + @optheader.headers_size
        @sections.each { |s|
                # align
                curaddr = EncodedData.align_size(curaddr, @optheader.sect_align)
                if s.rawaddr.kind_of?(::String)
                        @encoded.fixup! s.rawaddr => @encoded.virtsize
                        s.rawaddr = @encoded.virtsize
                end
                if s.virtaddr.kind_of?(::Integer)
                        raise "E: COFF: cannot encode section #{s.name}: hardcoded address too short" if curaddr > baseaddr + s.virtaddr
                        curaddr = baseaddr + s.virtaddr
                end
                binding.update s.encoded.binding(curaddr)
                curaddr += s.virtsize

                pre_sz = @encoded.virtsize
                @encoded << s.encoded[0, s.encoded.rawsize]
                @encoded.align @optheader.file_align
                if s.rawsize.kind_of?(::String)
                        @encoded.fixup! s.rawsize => (@encoded.virtsize - pre_sz)
                        s.rawsize = @encoded.virtsize - pre_sz
                end
        }

        # not aligned ? spec says it is, visual studio does not
        binding[@optheader.image_size] = curaddr - baseaddr if @optheader.image_size.kind_of?(::String)

        # patch the iat where iat_p was defined
        # sort to ensure a 0-terminated will not overwrite an entry
        # (try to dump notepad.exe, which has a forwarder;)
        @imports.find_all { |id| id.iat_p.kind_of?(Integer) }.sort_by { |id| id.iat_p }.each { |id|
                s = sect_at_rva(id.iat_p)
                @encoded[s.rawaddr + s.encoded.ptr, id.iat.virtsize] = id.iat
                binding.update id.iat.binding(baseaddr + id.iat_p)
        } if imports

        @encoded.fill
        @encoded.fixup! binding

        if @optheader.checksum.kind_of?(::String) and @encoded.reloc.length == 1
                # won't work if there are other unresolved relocs
                checksum = self.class.checksum(@encoded.data, @endianness)
                @encoded.fixup @optheader.checksum => checksum
                @optheader.checksum = checksum
        end
end
encode_tls() click to toggle source
# File metasm/exe_format/coff_encode.rb, line 462
def encode_tls
        dir, cbtable = @tls.encode(self)
        @directory['tls_table'] = [label_at(dir, 0, 'tls_table'), dir.virtsize]

        s = Section.new
        s.name = '.tls'
        s.encoded = EncodedData.new << dir << cbtable
        s.characteristics = %w[MEM_READ MEM_WRITE]
        encode_append_section s
end
encode_word(w) click to toggle source
# File metasm/exe_format/coff_encode.rb, line 335
def encode_word(w)   Expression[w].encode(:u32, @endianness, (caller if $DEBUG)) end
encode_xword(w) click to toggle source
# File metasm/exe_format/coff_encode.rb, line 336
def encode_xword(w)  Expression[w].encode((@bitsize == 32 ? :u32 : :u64), @endianness, (caller if $DEBUG)) end
fileoff_to_addr(foff) click to toggle source

file offset -> memory address handles LoadedPE

# File metasm/exe_format/coff_decode.rb, line 478
def fileoff_to_addr(foff)
        if s = @sections.find { |s_| s_.rawaddr <= foff and s_.rawaddr + s_.rawsize > foff }
                s.virtaddr + foff - s.rawaddr + (@load_address ||= @optheader.image_base)
        elsif foff >= 0 and foff < @optheader.headers_size
                foff + (@load_address ||= @optheader.image_base)
        end
end
get_default_entrypoints() click to toggle source

returns an array including the PE entrypoint and the exported functions entrypoints TODO filter out exported data, include safeseh ?

# File metasm/exe_format/coff_decode.rb, line 806
def get_default_entrypoints
        ep = []
        ep.concat @tls.callbacks.to_a if tls
        ep << (@optheader.image_base + label_rva(@optheader.entrypoint))
        @export.exports.to_a.each { |e|
                next if e.forwarder_lib or not e.target
                ep << (@optheader.image_base + label_rva(e.target))
        } if export
        ep
end
invalidate_header() click to toggle source

resets the values in the header that may have been modified by your script (eg section count, size, imagesize, etc) call this whenever you decode a file, modify it, and want to reencode it later

# File metasm/exe_format/coff_encode.rb, line 603
def invalidate_header
        # set those values to nil, they will be
        # recomputed during encode_header
        [:code_size, :data_size, :udata_size, :base_of_code, :base_of_data,
         :sect_align, :file_align, :image_size, :headers_size, :checksum].each { |m| @optheader.send("#{m}=", nil) }
        [:num_sect, :ptr_sym, :num_sym, :size_opthdr].each { |m| @header.send("#{m}=", nil) }
end
label_rva(name) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 452
def label_rva(name)
        if name.kind_of? Integer
                name
        elsif s = @sections.find { |s_| s_.encoded.export[name] }
                s.virtaddr + s.encoded.export[name]
        else
               @encoded.export[name]
        end
end
parse_init() click to toggle source
Calls superclass method Metasm::ExeFormat#parse_init
# File metasm/exe_format/coff_encode.rb, line 727
def parse_init
        # ahem...
        # a fake object, which when appended makes us parse '.text', which creates a real default section
        # forwards to it this first appendage.
        # allows the user to specify its own section if he wishes, and to use .text if he doesn't
        if not defined? @cursource or not @cursource
                @cursource = ::Object.new
                class << @cursource
                        attr_accessor :coff
                        def <<(*a)
                                t = Preprocessor::Token.new(nil)
                                t.raw = '.text'
                                coff.parse_parser_instruction t
                                coff.cursource.send(:<<, *a)
                        end
                end
                @cursource.coff = self
        end
        @source ||= {}
        super()
end
parse_parser_instruction(instr) click to toggle source

handles compiler meta-instructions

syntax:

.section "<section name>" <perm list> <base>
  section name is a string (may be quoted)
  perms are in 'r' 'w' 'x' 'shared' 'discard', may be concatenated (in this order), may be prefixed by 'no' to remove the attribute for an existing section
  base is the token 'base', the token '=' and an immediate expression
  default sections:
  .text =   .section '.text' rx
  .data =   .section '.data' rw
  .rodata = .section '.rodata' r
  .bss =    .section '.bss' rw
.entrypoint | .entrypoint <label>
  defines the label as the program entrypoint
  without argument, creates a label used as entrypoint
.libname "<name>"
  defines the string to be used as exported library name (should be the same as the file name, may omit extension)
.export ["<exported_name>"] [<ordinal>] [<label_name>]
  exports the specified label with the specified name (label_name defaults to exported_name)
  if exported_name is an unquoted integer, the export is by ordinal. XXX if the ordinal starts with '0', the integer is interpreted as octal
.import "<libname>" "<import_name|ordinal>" [<thunk_name>] [<label_name>]
  imports a symbol from a library
  if the thunk name is specified and not 'nil', the compiler will generate a thunk that can be called (in ia32, 'call thunk' == 'call [import_name]')
    the thunk is position-independent, and should be used instead of the indirect call form, for imported functions
  label_name is the label to attribute to the location that will receive the address of the imported symbol, defaults to import_name (iat_<import_name> if thunk == iname)
.image_base <base>
  specifies the COFF prefered load address, base is an immediate expression
# File metasm/exe_format/coff_encode.rb, line 777
def parse_parser_instruction(instr)
        readstr = lambda {
                @lexer.skip_space
                raise instr, 'string expected' if not t = @lexer.readtok or (t.type != :string and t.type != :quoted)
                t.value || t.raw
        }
        check_eol = lambda {
                @lexer.skip_space
                raise instr, 'eol expected' if t = @lexer.nexttok and t.type != :eol
        }
        case instr.raw.downcase
        when '.text', '.data', '.rodata', '.bss'
                sname = instr.raw.downcase
                if not @sections.find { |s| s.name == sname }
                        s = Section.new
                        s.name = sname
                        s.encoded = EncodedData.new
                        s.characteristics = case sname
                                when '.text'; %w[MEM_READ MEM_EXECUTE]
                                when '.data', '.bss'; %w[MEM_READ MEM_WRITE]
                                when '.rodata'; %w[MEM_READ]
                                end
                        @sections << s
                end
                @cursource = @source[sname] ||= []
                check_eol[] if instr.backtrace       # special case for magic @cursource

        when '.section'
                # .section <section name|"section name"> [(no)r w x shared discard] [base=<expr>]
                sname = readstr[]
                if not s = @sections.find { |s_| s_.name == sname }
                        s = Section.new
                        s.name = sname
                        s.encoded = EncodedData.new
                        s.characteristics = []
                        @sections << s
                end
                loop do
                        @lexer.skip_space
                        break if not tok = @lexer.nexttok or tok.type != :string
                        case @lexer.readtok.raw.downcase
                        when /^(no)?(r)?(w)?(x)?(shared)?(discard)?$/
                                ar = []
                                ar << 'MEM_READ' if $2
                                ar << 'MEM_WRITE' if $3
                                ar << 'MEM_EXECUTE' if $4
                                ar << 'MEM_SHARED' if $5
                                ar << 'MEM_DISCARDABLE' if $6
                                if $1; s.characteristics -= ar
                                else   s.characteristics |= ar
                                end
                        when 'base'
                                @lexer.skip_space
                                @lexer.unreadtok tok if not tok = @lexer.readtok or tok.type != :punct or tok.raw != '='
                                raise instr, 'invalid base' if not s.virtaddr = Expression.parse(@lexer).reduce or not s.virtaddr.kind_of?(::Integer)
                                if not @optheader.image_base
                                        @optheader.image_base = (s.virtaddr-0x80) & 0xfff00000
                                        puts "Warning: no image_base specified, using #{Expression[@optheader.image_base]}" if $VERBOSE
                                end
                                s.virtaddr -= @optheader.image_base
                        else raise instr, 'unknown parameter'
                        end
                end
                @cursource = @source[sname] ||= []
                check_eol[]

        when '.libname'
                # export directory library name
                # .libname <libname|"libname">
                @export ||= ExportDirectory.new
                @export.libname = readstr[]
                check_eol[]

        when '.export'
                # .export <export name|ordinal|"export name"> [ordinal] [label to export if different]
                @lexer.skip_space
                raise instr, 'string expected' if not tok = @lexer.readtok or (tok.type != :string and tok.type != :quoted)
                exportname = tok.value || tok.raw
                if tok.type == :string and (0..9).include? tok.raw[0]
                        exportname = Integer(exportname) rescue raise(tok, "bad ordinal value, try quotes #{' or rm leading 0' if exportname[0] == ?0}")
                end

                @lexer.skip_space
                tok = @lexer.readtok
                if tok and tok.type == :string and (0..9).include? tok.raw[0]
                        (eord = Integer(tok.raw)) rescue @lexer.unreadtok(tok)
                else @lexer.unreadtok(tok)
                end

                @lexer.skip_space
                tok = @lexer.readtok
                if tok and tok.type == :string
                        exportlabel = tok.raw
                else
                        @lexer.unreadtok tok
                end

                @export ||= ExportDirectory.new
                @export.exports ||= []
                e = ExportDirectory::Export.new
                if exportname.kind_of? Integer
                        e.ordinal = exportname
                else
                        e.name = exportname
                        e.ordinal = eord if eord
                end
                e.target = exportlabel || exportname
                @export.exports << e
                check_eol[]

        when '.import'
                # .import <libname|"libname"> <imported sym|"imported sym"> [label of plt thunk|nil] [label of iat element if != symname]
                libname = readstr[]
                i = ImportDirectory::Import.new

                @lexer.skip_space
                raise instr, 'string expected' if not tok = @lexer.readtok or (tok.type != :string and tok.type != :quoted)
                if tok.type == :string and (0..9).include? tok.raw[0]
                        i.ordinal = Integer(tok.raw)
                else
                        i.name = tok.value || tok.raw
                end

                @lexer.skip_space
                if tok = @lexer.readtok and tok.type == :string
                        i.thunk = tok.raw if tok.raw != 'nil'
                        @lexer.skip_space
                        tok = @lexer.readtok
                end
                if tok and tok.type == :string
                        i.target = tok.raw
                else
                        i.target = ((i.thunk == i.name) ? ('iat_' + i.name) : (i.name ? i.name : (i.thunk ? 'iat_' + i.thunk : raise(instr, 'need iat label'))))
                        @lexer.unreadtok tok
                end
                raise tok, 'import target exists' if i.target != new_label(i.target)

                @imports ||= []
                if not id = @imports.find { |id_| id_.libname == libname }
                        id = ImportDirectory.new
                        id.libname = libname
                        id.imports = []
                        @imports << id
                end
                id.imports << i

                check_eol[]

        when '.entrypoint'
                # ".entrypoint <somelabel/expression>" or ".entrypoint" (here)
                @lexer.skip_space
                if tok = @lexer.nexttok and tok.type == :string
                        raise instr, 'syntax error' if not entrypoint = Expression.parse(@lexer)
                else
                        entrypoint = new_label('entrypoint')
                        @cursource << Label.new(entrypoint, instr.backtrace.dup)
                end
                @optheader.entrypoint = entrypoint
                check_eol[]

        when '.image_base'
                raise instr if not base = Expression.parse(@lexer) or !(base = base.reduce).kind_of?(::Integer)
                @optheader.image_base = base
                check_eol[]

        when '.subsystem'
                @lexer.skip_space
                raise instr if not tok = @lexer.readtok
                @optheader.subsystem = tok.raw
                check_eol[]

        else super(instr)
        end
end
pre_encode_header(target='exe', want_relocs=true) click to toggle source

initialize the header from target/cpu/etc, target in ['exe' 'dll' 'kmod' 'obj']

# File metasm/exe_format/coff_encode.rb, line 560
def pre_encode_header(target='exe', want_relocs=true)
        target = {:bin => 'exe', :lib => 'dll', :obj => 'obj', 'sys' => 'kmod', 'drv' => 'kmod'}.fetch(target, target)

        @header.machine ||= case @cpu.shortname
                        when 'x64'; 'AMD64'
                        when 'ia32'; 'I386'
                        end
        @optheader.signature ||= case @cpu.size
                        when 32; 'PE'
                        when 64; 'PE+'
                        end
        @bitsize = (@optheader.signature == 'PE+' ? 64 : 32)

        # setup header flags
        tmp = %w[LINE_NUMS_STRIPPED LOCAL_SYMS_STRIPPED DEBUG_STRIPPED] +
                case target
                when 'exe';  %w[EXECUTABLE_IMAGE]
                when 'dll';  %w[EXECUTABLE_IMAGE DLL]
                when 'kmod'; %w[EXECUTABLE_IMAGE]
                when 'obj';  []
                end
        if @cpu.size == 32
                tmp << 'x32BIT_MACHINE'
        else
                tmp << 'LARGE_ADDRESS_AWARE'
        end
        tmp << 'RELOCS_STRIPPED' if not want_relocs
        @header.characteristics ||= tmp

        @optheader.subsystem ||= case target
                when 'exe', 'dll'; 'WINDOWS_GUI'
                when 'kmod'; 'NATIVE'
                end

        tmp = []
        tmp << 'NX_COMPAT'
        tmp << 'DYNAMIC_BASE' if want_relocs
        @optheader.dll_characts ||= tmp
end
read_c_attrs(cp) click to toggle source

honors C attributes: export, export_as(foo), import_from(kernel32), entrypoint import by ordinal: extern __stdcall int anyname(int) __attribute__((import_from(ws2_32:28))); can alias imports with int mygpaddr_alias() attr(import_from(kernel32:GetProcAddr))

# File metasm/exe_format/coff_encode.rb, line 975
def read_c_attrs(cp)
        cp.toplevel.symbol.each_value { |v|
                next if not v.kind_of? C::Variable
                if v.has_attribute 'export' or ea = v.has_attribute_var('export_as')
                        @export ||= ExportDirectory.new
                        @export.exports ||= []
                        e = ExportDirectory::Export.new
                        begin
                                e.ordinal = Integer(ea || v.name)
                        rescue ArgumentError
                                e.name = ea || v.name
                        end
                        e.target = v.name
                        @export.exports << e
                end
                if v.has_attribute('import') or ln = v.has_attribute_var('import_from')
                        ln ||= WindowsExports::EXPORT[v.name]
                        raise "unknown library for #{v.name}" if not ln
                        i = ImportDirectory::Import.new
                        if ln.include? ':'
                                ln, name = ln.split(':')
                                begin
                                        i.ordinal = Integer(name)
                                rescue ArgumentError
                                        i.name = name
                                end
                        else
                                i.name = v.name
                        end
                        if v.type.kind_of? C::Function
                                i.thunk = v.name
                                i.target = 'iat_'+i.thunk
                        else
                                i.target = v.name
                        end

                        @imports ||= []
                        if not id = @imports.find { |id_| id_.libname == ln }
                                id = ImportDirectory.new
                                id.libname = ln
                                id.imports = []
                                @imports << id
                        end
                        id.imports << i
                end
                if v.has_attribute 'entrypoint'
                        @optheader.entrypoint = v.name
                end
        }
end
sect_at_rva(rva) click to toggle source

converts an RVA (offset from base address of file when loaded in memory) to the section containing it using the section table updates @cursection and @cursection.encoded.ptr to point to the specified address may return self when rva points to the coff header returns nil if none match, 0 never matches

# File metasm/exe_format/coff_decode.rb, line 432
def sect_at_rva(rva)
        return if not rva or not rva.kind_of?(::Integer) or rva <= 0
        if sections and not @sections.empty?
                if s = @sections.find { |s_| s_.virtaddr <= rva and s_.virtaddr + EncodedData.align_size((s_.virtsize == 0 ? s_.rawsize : s_.virtsize), @optheader.sect_align) > rva }
                        s.encoded.ptr = rva - s.virtaddr
                        @cursection = s
                elsif rva < @sections.map { |s_| s_.virtaddr }.min
                        @encoded.ptr = rva
                        @cursection = self
                end
        elsif rva <= @encoded.length
                @encoded.ptr = rva
                @cursection = self
        end
end
sect_at_va(va) click to toggle source
# File metasm/exe_format/coff_decode.rb, line 448
def sect_at_va(va)
        sect_at_rva(va - @optheader.image_base)
end
section_info() click to toggle source

returns an array of [name, addr, length, info]

# File metasm/exe_format/coff_decode.rb, line 824
def section_info
        [['header', @optheader.image_base, @optheader.headers_size, nil]] +
        @sections.map { |s|
                [s.name, @optheader.image_base + s.virtaddr, s.virtsize, s.characteristics.join(',')]
        }
end
shortname() click to toggle source
# File metasm/exe_format/coff.rb, line 417
def shortname; 'coff'; end
sizeof_byte() click to toggle source
# File metasm/exe_format/coff.rb, line 419
def sizeof_byte ; 1 ; end
sizeof_half() click to toggle source
# File metasm/exe_format/coff.rb, line 420
def sizeof_half ; 2 ; end
sizeof_word() click to toggle source
# File metasm/exe_format/coff.rb, line 421
def sizeof_word ; 4 ; end
sizeof_xword() click to toggle source
# File metasm/exe_format/coff.rb, line 422
def sizeof_xword ; @bitsize == 32 ? 4 : 8 ; end
tune_cparser(cp) click to toggle source
Calls superclass method Metasm::ExeFormat#tune_cparser
# File metasm/exe_format/coff_encode.rb, line 967
def tune_cparser(cp)
        super(cp)
        cp.llp64 if @cpu.size == 64
end
tune_prepro(l) click to toggle source

defines __PE__

# File metasm/exe_format/coff_encode.rb, line 962
def tune_prepro(l)
        l.define_weak('__PE__', 1)
        l.define_weak('__MS_X86_64_ABI__') if @cpu and @cpu.shortname == 'x64'
end