123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- #!/usr/bin/env ruby
- ##
- # This module requires Metasploit: https://metasploit.com/download
- # Current source: https://github.com/rapid7/metasploit-framework
- ##
- #
- # This tool provides an easy way to see what opcodes are associated with
- # certain x86 instructions by making use of Metasm! Also allows to get
- # friendly output from a GAS assembler source code file.
- #
- #
- # This file is part of Metasm, the Ruby assembly manipulation suite
- # Copyright (C) 2007 Yoann GUILLOT
- #
- # Licence is LGPL, see LICENCE in the top-level directory
- #
- begin
- msfbase = __FILE__
- while File.symlink?(msfbase)
- msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
- end
- gem 'rex-text'
- $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
- require 'msfenv'
- $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
- require 'rex'
- require 'readline'
- require 'metasm'
- #PowerPC, seems broken for now in metasm
- #@Arch = ['Ia32','MIPS','PowerPC','ARM','X86_64']
- @Arch = ['Ia32','MIPS','ARM','X86_64']
- @Endian = ['little','big']
- @architecture = ""
- @endianness = ""
- def usage
- $stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage)
- exit
- end
- $args = Rex::Parser::Arguments.new(
- "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"],
- "-e" => [ true, "The endianness to encode as (#{@Endian.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})" ],
- "-h" => [ false, "Display this help information" ])
- $args.parse(ARGV) { |opt, idx, val|
- case opt
- when "-a"
- found = nil
- @Arch.each { |a|
- if val.downcase == a.downcase
- @architecture = a
- found = true
- end
- }
- usage if not found
- when "-e"
- found = nil
- @Endian.each { |e|
- if val.downcase == e.downcase
- @endianness = e
- found = true
- end
- }
- usage if not found
- when "-h"
- usage
- else
- usage
- end
- }
- unless @architecture.empty?
- if @endianness.empty?
- String.class_eval("@@cpu = Metasm::#{@architecture}.new")
- else
- String.class_eval("@@cpu = Metasm::#{@architecture}.new(:#{@endianness})")
- end
- end
- class String
- @@cpu ||= Metasm::Ia32.new
- class << self
- def cpu() @@cpu end
- def cpu=(c) @@cpu=c end
- end
- # encodes the current string as a Shellcode, returns the resulting EncodedData
- def metasm_encode_edata
- s = Metasm::Shellcode.assemble @@cpu, self
- s.encoded
- end
- # encodes the current string as a Shellcode, returns the resulting binary String
- # outputs warnings on unresolved relocations
- def metasm_encode
- ed = metasm_encode_edata
- if not ed.reloc.empty?
- puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ')
- end
- ed.fill
- ed.data
- end
- # decodes the current string as a Shellcode, with specified base address
- # returns the resulting Disassembler
- def metasm_decode_blocks(base_addr=0, eip=base_addr)
- sc = Metasm::Shellcode.metasm_decode(self, @@cpu)
- sc.base_addr = base_addr
- sc.metasm_disassemble(eip)
- end
- # decodes the current string as a Shellcode, with specified base address
- # returns the asm source equivallent
- def metasm_decode(base_addr=0, eip=base_addr)
- metasm_decode_blocks(base_addr, eip).to_s
- end
- def metasm_disassemble(str, eip=0)
- Metasm::Shellcode.metasm_disassemble(@@cpu, str, eip)
- end
- end
- def parse_gas_file(filename)
- filename = File.expand_path(filename)
- unless ::File.exist?(filename)
- puts "File #{filename} not found"
- return
- end
- shellcode = ""
- puts "Reading file #{filename}"
- ::File.open(filename, "rb") do |f|
- f.each_line do |l|
- l.gsub!(/#.*$/, "") # Delete comments
- l.gsub!(/@.*$/, "") # Delete comments
- l.gsub!(/\..*$/, "") # Delete directives
- l.gsub!(/(\r|\n)/, '') # Delete newlines... just in case...
- next if l.strip.empty?
- shellcode << "#{l}\n"
- end
- end
- begin
- encoded = shellcode.metasm_encode
- puts Rex::Text.to_ruby(encoded)
- puts encoded.metasm_disassemble(shellcode.metasm_encode)
- rescue Metasm::Exception => e
- puts "Error: #{e.class} #{e.message}"
- end
- end
- # Start a pseudo shell and dispatch lines to be assembled and then
- # disassembled.
- history_file = File.join(Msf::Config.config_directory, 'metasm_history')
- shell = Rex::Ui::Text::PseudoShell.new("%bldmetasm%clr", '>', history_file)
- shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new)
- shell.history_manager = Rex::Ui::Text::Shell::HistoryManager.new
- puts [
- 'type "exit" or "quit" to quit',
- 'use ";" or "\\n" for newline',
- 'type "file <file>" to parse a GAS assembler source file',
- '']
- shell.run { |l|
- l.gsub!(/(\r|\n)/, '')
- l.gsub!(/\\n/, "\n")
- l.gsub!(';', "\n")
- break if %w[quit exit].include? l.chomp
- if l.chomp.index(/^file (.*)/)
- parse_gas_file($1)
- next
- end
- next if l.strip.empty?
- begin
- l = l.metasm_encode
- puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"'
- rescue Metasm::Exception => e
- puts "Error: #{e.class} #{e.message}"
- end
- }
- rescue SignalException => e
- puts("Aborted! #{e}")
- end
|