metasm_shell.rb 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env ruby
  2. ##
  3. # This module requires Metasploit: https://metasploit.com/download
  4. # Current source: https://github.com/rapid7/metasploit-framework
  5. ##
  6. #
  7. # This tool provides an easy way to see what opcodes are associated with
  8. # certain x86 instructions by making use of Metasm! Also allows to get
  9. # friendly output from a GAS assembler source code file.
  10. #
  11. #
  12. # This file is part of Metasm, the Ruby assembly manipulation suite
  13. # Copyright (C) 2007 Yoann GUILLOT
  14. #
  15. # Licence is LGPL, see LICENCE in the top-level directory
  16. #
  17. begin
  18. msfbase = __FILE__
  19. while File.symlink?(msfbase)
  20. msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
  21. end
  22. gem 'rex-text'
  23. $:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
  24. require 'msfenv'
  25. $:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
  26. require 'rex'
  27. require 'readline'
  28. require 'metasm'
  29. #PowerPC, seems broken for now in metasm
  30. #@Arch = ['Ia32','MIPS','PowerPC','ARM','X86_64']
  31. @Arch = ['Ia32','MIPS','ARM','X86_64']
  32. @Endian = ['little','big']
  33. @architecture = ""
  34. @endianness = ""
  35. def usage
  36. $stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage)
  37. exit
  38. end
  39. $args = Rex::Parser::Arguments.new(
  40. "-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"],
  41. "-e" => [ true, "The endianness to encode as (#{@Endian.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})" ],
  42. "-h" => [ false, "Display this help information" ])
  43. $args.parse(ARGV) { |opt, idx, val|
  44. case opt
  45. when "-a"
  46. found = nil
  47. @Arch.each { |a|
  48. if val.downcase == a.downcase
  49. @architecture = a
  50. found = true
  51. end
  52. }
  53. usage if not found
  54. when "-e"
  55. found = nil
  56. @Endian.each { |e|
  57. if val.downcase == e.downcase
  58. @endianness = e
  59. found = true
  60. end
  61. }
  62. usage if not found
  63. when "-h"
  64. usage
  65. else
  66. usage
  67. end
  68. }
  69. unless @architecture.empty?
  70. if @endianness.empty?
  71. String.class_eval("@@cpu = Metasm::#{@architecture}.new")
  72. else
  73. String.class_eval("@@cpu = Metasm::#{@architecture}.new(:#{@endianness})")
  74. end
  75. end
  76. class String
  77. @@cpu ||= Metasm::Ia32.new
  78. class << self
  79. def cpu() @@cpu end
  80. def cpu=(c) @@cpu=c end
  81. end
  82. # encodes the current string as a Shellcode, returns the resulting EncodedData
  83. def metasm_encode_edata
  84. s = Metasm::Shellcode.assemble @@cpu, self
  85. s.encoded
  86. end
  87. # encodes the current string as a Shellcode, returns the resulting binary String
  88. # outputs warnings on unresolved relocations
  89. def metasm_encode
  90. ed = metasm_encode_edata
  91. if not ed.reloc.empty?
  92. puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ')
  93. end
  94. ed.fill
  95. ed.data
  96. end
  97. # decodes the current string as a Shellcode, with specified base address
  98. # returns the resulting Disassembler
  99. def metasm_decode_blocks(base_addr=0, eip=base_addr)
  100. sc = Metasm::Shellcode.metasm_decode(self, @@cpu)
  101. sc.base_addr = base_addr
  102. sc.metasm_disassemble(eip)
  103. end
  104. # decodes the current string as a Shellcode, with specified base address
  105. # returns the asm source equivallent
  106. def metasm_decode(base_addr=0, eip=base_addr)
  107. metasm_decode_blocks(base_addr, eip).to_s
  108. end
  109. def metasm_disassemble(str, eip=0)
  110. Metasm::Shellcode.metasm_disassemble(@@cpu, str, eip)
  111. end
  112. end
  113. def parse_gas_file(filename)
  114. filename = File.expand_path(filename)
  115. unless ::File.exist?(filename)
  116. puts "File #{filename} not found"
  117. return
  118. end
  119. shellcode = ""
  120. puts "Reading file #{filename}"
  121. ::File.open(filename, "rb") do |f|
  122. f.each_line do |l|
  123. l.gsub!(/#.*$/, "") # Delete comments
  124. l.gsub!(/@.*$/, "") # Delete comments
  125. l.gsub!(/\..*$/, "") # Delete directives
  126. l.gsub!(/(\r|\n)/, '') # Delete newlines... just in case...
  127. next if l.strip.empty?
  128. shellcode << "#{l}\n"
  129. end
  130. end
  131. begin
  132. encoded = shellcode.metasm_encode
  133. puts Rex::Text.to_ruby(encoded)
  134. puts encoded.metasm_disassemble(shellcode.metasm_encode)
  135. rescue Metasm::Exception => e
  136. puts "Error: #{e.class} #{e.message}"
  137. end
  138. end
  139. # Start a pseudo shell and dispatch lines to be assembled and then
  140. # disassembled.
  141. history_file = File.join(Msf::Config.config_directory, 'metasm_history')
  142. shell = Rex::Ui::Text::PseudoShell.new("%bldmetasm%clr", '>', history_file)
  143. shell.init_ui(Rex::Ui::Text::Input::Stdio.new, Rex::Ui::Text::Output::Stdio.new)
  144. shell.history_manager = Rex::Ui::Text::Shell::HistoryManager.new
  145. puts [
  146. 'type "exit" or "quit" to quit',
  147. 'use ";" or "\\n" for newline',
  148. 'type "file <file>" to parse a GAS assembler source file',
  149. '']
  150. shell.run { |l|
  151. l.gsub!(/(\r|\n)/, '')
  152. l.gsub!(/\\n/, "\n")
  153. l.gsub!(';', "\n")
  154. break if %w[quit exit].include? l.chomp
  155. if l.chomp.index(/^file (.*)/)
  156. parse_gas_file($1)
  157. next
  158. end
  159. next if l.strip.empty?
  160. begin
  161. l = l.metasm_encode
  162. puts '"' + l.unpack('C*').map { |c| '\\x%02x' % c }.join + '"'
  163. rescue Metasm::Exception => e
  164. puts "Error: #{e.class} #{e.message}"
  165. end
  166. }
  167. rescue SignalException => e
  168. puts("Aborted! #{e}")
  169. end