# This file is part of Metasm, the Ruby assembly manipulation suite # Copyright (C) 2006-2009 Yoann GUILLOT # # Licence is LGPL, see LICENCE in the top-level directory require 'metasm/exe_format/main' require 'metasm/encode' require 'metasm/decode' module Metasm class MZ < ExeFormat MAGIC = 'MZ' # 0x4d5a MAGIC.force_encoding('BINARY') if MAGIC.respond_to?(:force_encoding) class Header < SerialStruct mem :magic, 2, MAGIC words :cblp, :cp, :crlc, :cparhdr, :minalloc, :maxalloc, :ss, :sp, :csum, :ip, :cs, :lfarlc, :ovno mem :unk, 4 def encode(mz, relocs) h = EncodedData.new set_default_values mz, h, relocs h << super(mz) end def set_default_values(mz, h=nil, relocs=nil) return if not h @cblp ||= Expression[[mz.label_at(mz.body, mz.body.virtsize), :-, mz.label_at(h, 0)], :%, 512] # number of bytes used in last page @cp ||= Expression[[mz.label_at(mz.body, mz.body.virtsize), :-, mz.label_at(h, 0)], :/, 512] # number of pages used @crlc ||= relocs.virtsize/4 @cparhdr ||= Expression[[mz.label_at(relocs, 0), :-, mz.label_at(h, 0)], :/, 16] # header size in paragraphs (16o) @minalloc ||= ((mz.body.virtsize - mz.body.rawsize) + 15) / 16 @maxalloc ||= @minalloc @sp ||= 0 # ss:sp points at 1st byte of body => works if body does not reach end of segment (or maybe the overflow make the stack go to header space) @lfarlc ||= Expression[mz.label_at(relocs, 0), :-, mz.label_at(h, 0)] super(mz) end def decode(mz) super(mz) raise InvalidExeFormat, "Invalid MZ signature #{h.magic.inspect}" if @magic != MAGIC end end class Relocation < SerialStruct words :offset, :segment end # encodes a word in 16 bits def encode_word(val) Expression[val].encode(:u16, @endianness) end # decodes a 16bits word from self.encoded def decode_word(edata = @encoded) edata.decode_imm(:u16, @endianness) end def sizeof_word ; 2 ; end attr_accessor :endianness, :header, :source # the EncodedData representing the content of the file attr_accessor :body # an array of Relocations - quite obscure attr_accessor :relocs def initialize(cpu=nil) @endianness = cpu ? cpu.endianness : :little @relocs = [] @header = Header.new @body = EncodedData.new @source = [] super(cpu) end # assembles the source in the body, clears the source def assemble(*a) parse(*a) if not a.empty? @body << assemble_sequence(@source, @cpu) @body.fixup @body.binding # XXX should create @relocs here @source.clear end # sets up @cursource def parse_init @cursource = @source super() end # encodes the header and the relocation table, return them in an array, with the body. def pre_encode relocs = @relocs.inject(EncodedData.new) { |edata, r| edata << r.encode(self) } header = @header.encode self, relocs [header, relocs, @body] end # defines the exe-specific parser instructions: # .entrypoint [