# 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' module Metasm # a shellcode is a simple sequence of instructions class Shellcode < ExeFormat # the array of source elements (Instr/Data etc) attr_accessor :source # the base address of the shellcode (nil if unspecified) attr_accessor :base_addr def initialize(cpu=nil, base_addr=nil) @base_addr = base_addr @source = [] super(cpu) end def parse_init @cursource = @source super() end # allows definition of the base address def parse_parser_instruction(instr) case instr.raw.downcase when '.base', '.baseaddr', '.base_addr' # ".base_addr " # expression should #reduce to integer @lexer.skip_space raise instr, 'syntax error' if not @base_addr = Expression.parse(@lexer).reduce raise instr, 'syntax error' if tok = @lexer.nexttok and tok.type != :eol else super(instr) end end def get_section_at(addr) base = @base_addr || 0 if not addr.kind_of? Integer [@encoded, addr] if @encoded.ptr = @encoded.export[addr] elsif addr >= base and addr < base + @encoded.virtsize @encoded.ptr = addr - base [@encoded, addr] end end def each_section yield @encoded, (@base_addr || 0) end def addr_to_fileoff(addr) addr - (base_addr || 0) end def fileoff_to_addr(foff) foff + (base_addr || 0) end # encodes the source found in self.source # appends it to self.encoded # clears self.source # the optional parameter may contain a binding used to fixup! self.encoded # uses self.base_addr if it exists def assemble(*a) parse(*a) if not a.empty? @encoded << assemble_sequence(@source, @cpu) @source.clear self end def encode(binding={}) @encoded.fixup! binding if binding.kind_of? Hash @encoded.fixup @encoded.binding(@base_addr) @encoded.fill @encoded.rawsize self end def decode end def self.disassemble(cpu, str, eip=0) sc = decode(str, cpu) sc.disassemble(eip) end def init_disassembler d = super() d.function[:default] = @cpu.disassembler_default_func d end def compile_setsection(src, section) end def dump_section_header(addr, edata) '' end def get_default_entrypoints [@base_addr || 0] end # returns a virtual subclass of Shellcode whose cpu_from_headers will return cpu def self.withcpu(cpu) c = Class.new(self) c.send(:define_method, :cpu_from_headers) { cpu = Metasm.const_get(cpu) if cpu.kind_of?(::String) cpu = cpu.new if cpu.kind_of?(::Class) and cpu.ancestors.include?(CPU) cpu } c end end end