CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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