metasm 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +1 -0
- data/doc/code_organisation.txt +1 -1
- data/metasm.gemspec +1 -1
- data/metasm.rb +2 -1
- data/metasm/cpu/arc/decode.rb +3 -3
- data/metasm/cpu/arm/decode.rb +2 -2
- data/metasm/cpu/ia32/compile_c.rb +18 -2
- data/metasm/cpu/ia32/decode.rb +9 -4
- data/metasm/cpu/ia32/decompile.rb +22 -8
- data/metasm/cpu/ia32/opcodes.rb +5 -5
- data/metasm/cpu/mcs51.rb +8 -0
- data/metasm/cpu/mcs51/decode.rb +99 -0
- data/metasm/cpu/mcs51/main.rb +76 -0
- data/metasm/cpu/mcs51/opcodes.rb +120 -0
- data/metasm/cpu/mips/decode.rb +5 -4
- data/metasm/cpu/st20.rb +9 -0
- data/metasm/cpu/st20/decode.rb +180 -0
- data/metasm/cpu/st20/decompile.rb +283 -0
- data/metasm/cpu/st20/main.rb +37 -0
- data/metasm/cpu/st20/opcodes.rb +140 -0
- data/metasm/cpu/x86_64/encode.rb +4 -2
- data/metasm/cpu/x86_64/opcodes.rb +4 -2
- data/metasm/decode.rb +16 -15
- data/metasm/decompile.rb +1 -1
- data/metasm/disassemble.rb +3 -1
- data/metasm/disassemble_api.rb +3 -1
- data/metasm/dynldr.rb +9 -3
- data/metasm/encode.rb +2 -2
- data/metasm/exe_format/coff.rb +3 -1
- data/metasm/exe_format/coff_decode.rb +5 -3
- data/metasm/exe_format/elf.rb +4 -0
- data/metasm/exe_format/elf_decode.rb +1 -2
- data/metasm/exe_format/elf_encode.rb +4 -1
- data/metasm/exe_format/macho.rb +20 -6
- data/metasm/exe_format/pe.rb +1 -1
- data/metasm/exe_format/serialstruct.rb +1 -1
- data/metasm/gui.rb +1 -1
- data/metasm/gui/dasm_hex.rb +2 -2
- data/metasm/gui/dasm_main.rb +8 -8
- data/metasm/gui/debug.rb +4 -4
- data/metasm/gui/gtk.rb +1 -1
- data/metasm/gui/qt.rb +2 -2
- data/metasm/gui/win32.rb +1 -1
- data/metasm/main.rb +11 -6
- data/metasm/os/windows.rb +26 -23
- data/misc/hexdump.rb +2 -2
- data/misc/objdiff.rb +4 -1
- data/misc/objscan.rb +1 -1
- data/samples/dasm-plugins/bindiff.rb +1 -1
- data/samples/dasm-plugins/scanxrefs.rb +2 -1
- data/samples/dynamic_ruby.rb +24 -25
- data/samples/elfencode.rb +15 -0
- data/samples/exeencode.rb +2 -2
- data/samples/metasm-shell.rb +67 -55
- data/tests/mcs51.rb +27 -0
- metadata +13 -2
data/misc/objdiff.rb
CHANGED
@@ -22,7 +22,10 @@ def Object.diff(o1, o2)
|
|
22
22
|
h["[#{k.inspect}]"] = d if not d.empty?
|
23
23
|
}
|
24
24
|
else
|
25
|
-
a = (
|
25
|
+
a = ($diff_accessor_cache ||= {})[o1.class] ||= (
|
26
|
+
im = o1.class.public_instance_methods.map { |m| m.to_s }.grep(/^[a-z]/)
|
27
|
+
(im & im.map { |m| m+'=' }).map { |m| m.chop }.find_all { |m| o1.instance_variable_get('@'+m) }
|
28
|
+
)
|
26
29
|
if a.empty?
|
27
30
|
return o1 == o2 ? h : [o1, o2]
|
28
31
|
end
|
data/misc/objscan.rb
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
|
9
9
|
require File.join(Metasm::Metasmdir, 'samples', 'bindiff.rb')
|
10
10
|
|
11
|
-
Gui::DasmWindow.new("bindiff target").promptopen("
|
11
|
+
Gui::DasmWindow.new("bindiff target").promptopen("choose bindiff target") { |w|
|
12
12
|
w.title = "#{w.widget.dasm.program.filename} - metasm bindiff"
|
13
13
|
@bindiff_win = BinDiffWindow.new(self, w.widget.dasm)
|
14
14
|
}
|
@@ -7,11 +7,12 @@
|
|
7
7
|
# metasm dasm plugin: scan for xrefs to the target address, incl. relative offsets (eg near call/jmp)
|
8
8
|
def scanxrefs(target)
|
9
9
|
ans = []
|
10
|
+
msk = (1 << cpu.size) - 1
|
10
11
|
sections.sort.each { |s_addr, edata|
|
11
12
|
raw = edata.data.to_str
|
12
13
|
(0..raw.length-4).each { |off|
|
13
14
|
r = raw[off, 4].unpack('V').first
|
14
|
-
ans << (s_addr + off) if (r + off+4 + s_addr)&
|
15
|
+
ans << (s_addr + off) if (r + off+4 + s_addr) & msk == target or r == target
|
15
16
|
}
|
16
17
|
}
|
17
18
|
ans
|
data/samples/dynamic_ruby.rb
CHANGED
@@ -452,8 +452,8 @@ EOS
|
|
452
452
|
end
|
453
453
|
|
454
454
|
# compile a case/when
|
455
|
-
# create a real C switch() for
|
456
|
-
# XXX will get the wrong order for "case x; when 1; when
|
455
|
+
# create a real C switch() for Integers, and put the others === in the default case
|
456
|
+
# XXX will get the wrong order for "case x; when 1; when Integer; when 3;" ...
|
457
457
|
def compile_case(ast, scope, want_value)
|
458
458
|
# this generates
|
459
459
|
# var = stuff_to_test()
|
@@ -472,7 +472,7 @@ EOS
|
|
472
472
|
# else {
|
473
473
|
# default();
|
474
474
|
# }
|
475
|
-
#
|
475
|
+
#
|
476
476
|
if want_value == true
|
477
477
|
ret = get_new_tmp_var('case', want_value)
|
478
478
|
want_value = ret
|
@@ -498,7 +498,7 @@ EOS
|
|
498
498
|
raise Fail if cs[1][0] != :array
|
499
499
|
|
500
500
|
# numeric case, add a case to body_int
|
501
|
-
if cs[1][1..-1].all? { |cd| cd[0] == :lit and (cd[1].kind_of?
|
501
|
+
if cs[1][1..-1].all? { |cd| cd[0] == :lit and (cd[1].kind_of? Integer or cd[1].kind_of? Range) }
|
502
502
|
cs[1][1..-1].each { |cd|
|
503
503
|
if cd[1].kind_of? Range
|
504
504
|
b = cd[1].begin
|
@@ -520,7 +520,7 @@ EOS
|
|
520
520
|
else
|
521
521
|
cnd = nil
|
522
522
|
cs[1][1..-1].each { |cd|
|
523
|
-
if (cd[0] == :lit and (cd[1].kind_of?(
|
523
|
+
if (cd[0] == :lit and (cd[1].kind_of?(Integer) or cd[1].kind_of?(Symbol))) or
|
524
524
|
[:nil, :true, :false].include?(cd[0])
|
525
525
|
# true C equality
|
526
526
|
cd = C::CExpression[var, :==, ast_to_c(cd, scope)]
|
@@ -541,7 +541,7 @@ EOS
|
|
541
541
|
cb = C::Block.new(scope)
|
542
542
|
v = ast_to_c(cs[2], cb, want_value)
|
543
543
|
cb.statements << C::CExpression[ret, :'=', v] if want_value and v != ret
|
544
|
-
|
544
|
+
|
545
545
|
fu = C::If.new(cnd, cb, nil)
|
546
546
|
|
547
547
|
if body_other
|
@@ -659,7 +659,7 @@ EOS
|
|
659
659
|
end
|
660
660
|
|
661
661
|
# retrieve the current class, from self->klass
|
662
|
-
# XXX will segfault with self.kind_of?
|
662
|
+
# XXX will segfault with self.kind_of? Integer/true/false/nil/sym
|
663
663
|
def rb_selfclass
|
664
664
|
rb_cast_pvalue(rb_self, 1)
|
665
665
|
end
|
@@ -810,7 +810,7 @@ EOS
|
|
810
810
|
# if want_value is a C::Variable, the statements should try to populate this var instead of some random tmp var
|
811
811
|
# eg to simplify :if encoding unless we have 'foo = if 42;..'
|
812
812
|
def ast_to_c(ast, scope, want_value = true)
|
813
|
-
ret =
|
813
|
+
ret =
|
814
814
|
case ast.to_a[0]
|
815
815
|
when :block
|
816
816
|
if ast[1]
|
@@ -1085,13 +1085,13 @@ EOS
|
|
1085
1085
|
case ast[1][0]
|
1086
1086
|
when :ivar
|
1087
1087
|
fcall('rb_ivar_defined', rb_self, rb_intern(ast[1][1]))
|
1088
|
-
else
|
1088
|
+
else
|
1089
1089
|
raise Fail, "unsupported #{ast.inspect}"
|
1090
1090
|
end
|
1091
1091
|
when :masgn
|
1092
1092
|
# parallel assignment: put everything in an Array, then pop everything back?
|
1093
1093
|
rb_masgn(ast, scope, want_value)
|
1094
|
-
|
1094
|
+
|
1095
1095
|
when :evstr
|
1096
1096
|
fcall('rb_obj_as_string', ast_to_c(ast[1], scope))
|
1097
1097
|
when :dot2, :dot3
|
@@ -1138,7 +1138,7 @@ EOS
|
|
1138
1138
|
args = ast[3][1..-1] if ast[3] and ast[3][0] == :array
|
1139
1139
|
arg0 = args[0] if args and args[0]
|
1140
1140
|
|
1141
|
-
if arg0 and arg0[0] == :lit and arg0[1].kind_of?(
|
1141
|
+
if arg0 and arg0[0] == :lit and arg0[1].kind_of?(Integer) and %w[== > < >= <= + -].include?(op)
|
1142
1142
|
# TODO or @optim_hint[ast[1]] == 'fixnum'
|
1143
1143
|
# optimize 'x==42', 'x+42', 'x-42'
|
1144
1144
|
o2 = arg0[1]
|
@@ -1146,7 +1146,7 @@ EOS
|
|
1146
1146
|
# need o2 >= 0 for overflow detection
|
1147
1147
|
op = {'+' => '-', '-' => '+'}[op]
|
1148
1148
|
o2 = -o2
|
1149
|
-
return if not o2.kind_of?
|
1149
|
+
return if not o2.kind_of? Integer # -0x40000000
|
1150
1150
|
end
|
1151
1151
|
|
1152
1152
|
int_v = o2.object_id
|
@@ -1159,11 +1159,11 @@ EOS
|
|
1159
1159
|
|
1160
1160
|
case op
|
1161
1161
|
when '=='
|
1162
|
-
# XXX assume == only return true for full equality: if not
|
1162
|
+
# XXX assume == only return true for full equality: if not Integer, then always false
|
1163
1163
|
# which breaks 1.0 == 1 and maybe others, but its ok
|
1164
1164
|
scope.statements << C::If.new(ce[recv, :'==', [int_v]], ce[tmp, :'=', rb_true], ce[tmp, :'=', rb_false])
|
1165
1165
|
when '>', '<', '>=', '<='
|
1166
|
-
# do the actual comparison on signed >>1 if both
|
1166
|
+
# do the actual comparison on signed >>1 if both Integer
|
1167
1167
|
t = C::If.new(
|
1168
1168
|
ce[[[[recv], int], :>>, [1]], op.to_sym, [[[int_v], int], :>>, [1]]],
|
1169
1169
|
ce[tmp, :'=', rb_true],
|
@@ -1194,7 +1194,7 @@ EOS
|
|
1194
1194
|
end
|
1195
1195
|
end
|
1196
1196
|
tmp
|
1197
|
-
|
1197
|
+
|
1198
1198
|
# Symbol#==
|
1199
1199
|
elsif arg0 and arg0[0] == :lit and arg0[1].kind_of? Symbol and op == '=='
|
1200
1200
|
s_v = ast_to_c(arg0, scope)
|
@@ -1244,13 +1244,13 @@ EOS
|
|
1244
1244
|
|
1245
1245
|
ar = C::Block.new(scope)
|
1246
1246
|
ar.statements << ce[idx, :'=', [[[arg], int], :>>, [1]]]
|
1247
|
-
ar.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_ary_len(recv)]], nil)
|
1247
|
+
ar.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_ary_len(recv)]], nil)
|
1248
1248
|
ar.statements << C::If.new(ce[[idx, :<, [0]], :'||', [idx, :>=, [[rb_ary_len(recv)], int]]],
|
1249
1249
|
ce[tmp, :'=', rb_nil],
|
1250
1250
|
ce[tmp, :'=', rb_ary_ptr(recv, idx)])
|
1251
1251
|
st = C::Block.new(scope)
|
1252
1252
|
st.statements << ce[idx, :'=', [[[arg], int], :>>, [1]]]
|
1253
|
-
st.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_str_len(recv)]], nil)
|
1253
|
+
st.statements << C::If.new(ce[idx, :<, [0]], ce[idx, :'=', [idx, :+, rb_str_len(recv)]], nil)
|
1254
1254
|
st.statements << C::If.new(ce[[idx, :<, [0]], :'||', [idx, :>=, [[rb_str_len(recv)], int]]],
|
1255
1255
|
ce[tmp, :'=', rb_nil],
|
1256
1256
|
ce[tmp, :'=', [[[[rb_str_ptr(recv, idx), :&, [0xff]], :<<, [1]], :|, [1]], value]])
|
@@ -1304,8 +1304,7 @@ EOS
|
|
1304
1304
|
when 'Symbol'
|
1305
1305
|
tmp = get_new_tmp_var('kindof', want_value)
|
1306
1306
|
ce[[ast_to_c(ast[1], scope, tmp), :'&', [0xf]], :'==', [0xe]]
|
1307
|
-
|
1308
|
-
when 'Fixnum'
|
1307
|
+
when 'Integer'
|
1309
1308
|
tmp = get_new_tmp_var('kindof', want_value)
|
1310
1309
|
ce[ast_to_c(ast[1], scope, tmp), :'&', [0x1]]
|
1311
1310
|
when 'Array'
|
@@ -1503,7 +1502,7 @@ puts "shortcut may be incorrect for #{ast.inspect}" if arg0[0] == :const
|
|
1503
1502
|
elsif b_recv[0] == :call and not b_recv[3] and b_recv[2] == 'times'
|
1504
1503
|
limit = get_new_tmp_var('limit')
|
1505
1504
|
recv = ast_to_c(b_recv[1], scope, limit)
|
1506
|
-
scope.statements << C::If.new(C::CExpression[:'!', [recv, :&, 1]], rb_raise('only
|
1505
|
+
scope.statements << C::If.new(C::CExpression[:'!', [recv, :&, 1]], rb_raise('only Integer#times handled'), nil)
|
1507
1506
|
if want_value
|
1508
1507
|
scope.statements << C::CExpression[@iter_break, :'=', recv]
|
1509
1508
|
end
|
@@ -1553,7 +1552,7 @@ puts "shortcut may be incorrect for #{ast.inspect}" if arg0[0] == :const
|
|
1553
1552
|
body.statements << C::CExpression[dvar(b_args[1]), :'=', [rb_ary_ptr(ary), :'[]', [cntr]]]
|
1554
1553
|
end
|
1555
1554
|
# same as #each up to this point (except default retval), now add a 'if (body_value) break ary[cntr];'
|
1556
|
-
# XXX 'find { next true }'
|
1555
|
+
# XXX 'find { next true }'
|
1557
1556
|
|
1558
1557
|
found = ast_to_c(b_body, body)
|
1559
1558
|
t = C::Block.new(body)
|
@@ -1580,7 +1579,7 @@ puts "shortcut may be incorrect for #{ast.inspect}" if arg0[0] == :const
|
|
1580
1579
|
body.statements << C::CExpression[dvar(b_args[1]), :'=', [rb_ary_ptr(ary), :'[]', [cntr]]]
|
1581
1580
|
end
|
1582
1581
|
# same as #each up to this point (except default retval), now add a '@iter_break << body_value'
|
1583
|
-
# XXX 'next' unhandled
|
1582
|
+
# XXX 'next' unhandled
|
1584
1583
|
|
1585
1584
|
val = ast_to_c(b_body, body)
|
1586
1585
|
body.statements << fcall('rb_ary_push', @iter_break, val)
|
@@ -1642,7 +1641,7 @@ static void do_init_once(void) {
|
|
1642
1641
|
// rb_define_method(const_Lol, "method", method, 2);
|
1643
1642
|
}
|
1644
1643
|
|
1645
|
-
int Init_compiledruby(void) __attribute__((export)) {
|
1644
|
+
int Init_compiledruby(void) __attribute__((export)) {
|
1646
1645
|
// use a separate func to avoid having to append statements before the 'return'
|
1647
1646
|
do_init_once();
|
1648
1647
|
return 0;
|
@@ -1664,7 +1663,7 @@ EOS
|
|
1664
1663
|
@compiled_func_cache[[klass, method.to_s, singleton]] = @cur_cfunc
|
1665
1664
|
|
1666
1665
|
cls = rb_const(nil, klass)
|
1667
|
-
|
1666
|
+
|
1668
1667
|
init.statements << fcall("rb_define#{'_singleton' if singleton}_method", cls, method.to_s, @cur_cfunc, method_arity)
|
1669
1668
|
|
1670
1669
|
mname
|
@@ -1699,7 +1698,7 @@ EOS
|
|
1699
1698
|
@cp.toplevel.symbol[n] || declare_newtopvar(n, fcall('rb_intern', sym.to_s), C::BaseType.new(:int, :unsigned))
|
1700
1699
|
end
|
1701
1700
|
|
1702
|
-
# rb_const 'FOO', Bar::Baz ==>
|
1701
|
+
# rb_const 'FOO', Bar::Baz ==>
|
1703
1702
|
# const_Bar = rb_const_get(rb_cObject, rb_intern("Bar"));
|
1704
1703
|
# const_Bar_Baz = rb_const_get(const_Bar, rb_intern("Baz"));
|
1705
1704
|
# const_Bar_Baz_FOO = rb_const_get(const_Bar_Baz, rb_intern("FOO"));
|
data/samples/elfencode.rb
CHANGED
@@ -14,12 +14,27 @@ __END__
|
|
14
14
|
// .nointerp // to disable the dynamic section, eg for stuff with int80 only
|
15
15
|
.text
|
16
16
|
.entrypoint
|
17
|
+
#if defined __i386__
|
17
18
|
push bla
|
18
19
|
push fmt
|
19
20
|
call printf
|
21
|
+
|
20
22
|
push 0
|
21
23
|
call exit
|
22
24
|
|
25
|
+
#elif defined __amd64__
|
26
|
+
lea rsi, [rip+bla-$_]
|
27
|
+
lea rdi, [rip+fmt-$_]
|
28
|
+
xor rax, rax
|
29
|
+
call printf
|
30
|
+
|
31
|
+
mov rdi, 0
|
32
|
+
call exit
|
33
|
+
|
34
|
+
#else
|
35
|
+
unsupported architecture!
|
36
|
+
#endif
|
37
|
+
|
23
38
|
.data
|
24
39
|
bla db "world", 0
|
25
40
|
fmt db "Hello, %s !\n", 0
|
data/samples/exeencode.rb
CHANGED
@@ -94,7 +94,7 @@ if $opts[:to_string]
|
|
94
94
|
end
|
95
95
|
|
96
96
|
if of = $opts[:outfilename]
|
97
|
-
abort "Error: target file #{of.inspect} exists !" if File.
|
97
|
+
abort "Error: target file #{of.inspect} exists !" if File.exist?(of) and $opts[:nooverwrite_outfile]
|
98
98
|
File.open(of, 'w') { |fd| fd.puts str }
|
99
99
|
puts "saved to file #{of.inspect}"
|
100
100
|
else
|
@@ -102,7 +102,7 @@ if $opts[:to_string]
|
|
102
102
|
end
|
103
103
|
else
|
104
104
|
of = $opts[:outfilename] ||= 'a.out'
|
105
|
-
abort "Error: target file #{of.inspect} exists !" if File.
|
105
|
+
abort "Error: target file #{of.inspect} exists !" if File.exist?(of) and $opts[:nooverwrite_outfile]
|
106
106
|
Metasm::DynLdr.compile_binary_module_hack(exe) if $opts[:dldrhack]
|
107
107
|
exe.encode_file(of, $opts[:exetype])
|
108
108
|
puts "saved to file #{of.inspect}"
|
data/samples/metasm-shell.rb
CHANGED
@@ -18,73 +18,85 @@
|
|
18
18
|
# > exit
|
19
19
|
|
20
20
|
require 'metasm'
|
21
|
+
require 'readline'
|
21
22
|
|
22
23
|
class String
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
@@cpu = Metasm::Ia32.new
|
25
|
+
class << self
|
26
|
+
def cpu() @@cpu end
|
27
|
+
def cpu=(c)
|
28
|
+
c = Metasm.const_get(c).new if c.kind_of? String
|
29
|
+
@@cpu=c
|
30
|
+
end
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
# encodes the current string as a Shellcode, returns the resulting EncodedData
|
34
|
+
def encode_edata
|
35
|
+
Metasm::Shellcode.assemble(@@cpu, self).encode.encoded
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
# encodes the current string as a Shellcode, returns the resulting binary String
|
39
|
+
# outputs warnings on unresolved relocations
|
40
|
+
def encode
|
41
|
+
ed = encode_edata
|
42
|
+
if not ed.reloc.empty?
|
43
|
+
puts 'W: encoded string has unresolved relocations: ' + ed.reloc.map { |o, r| r.target.inspect }.join(', ')
|
44
|
+
end
|
45
|
+
ed.fill
|
46
|
+
ed.data
|
47
|
+
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
# decodes the current string as a Shellcode, with specified base address
|
50
|
+
# returns the resulting Disassembler
|
51
|
+
def decode_blocks(base_addr=0, eip=base_addr)
|
52
|
+
sc = Metasm::Shellcode.decode(self, @@cpu)
|
53
|
+
sc.base_addr = base_addr
|
54
|
+
sc.disassemble(eip)
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
57
|
+
# decodes the current string as a Shellcode, with specified base address
|
58
|
+
# returns the asm source equivallent
|
59
|
+
def decode(base_addr=0, eip=base_addr)
|
60
|
+
decode_blocks(base_addr, eip).to_s
|
61
|
+
end
|
61
62
|
end
|
62
63
|
|
63
64
|
# get in interactive assembler mode
|
64
65
|
def asm
|
65
|
-
|
66
|
-
|
67
|
-
break if %w[quit exit].include? l.chomp
|
68
|
-
if l.chomp == 'help'
|
69
|
-
puts "Metasm assembly shell: type in opcodes to see their binary form",
|
70
|
-
"You can use ';' to type multi-line stuff",
|
71
|
-
"e.g. 'nop nop' will display \"\\x90\\x90\""
|
72
|
-
next
|
73
|
-
end
|
74
|
-
|
75
|
-
begin
|
76
|
-
data = l.gsub(';', "\n")
|
77
|
-
next if data.strip.empty?
|
78
|
-
data = data.encode
|
79
|
-
puts '"' + data.unpack('C*').map { |c| '\\x%02x' % c }.join + '"'
|
80
|
-
rescue Metasm::Exception => e
|
81
|
-
puts "Error: #{e.class} #{e.message}"
|
82
|
-
end
|
83
|
-
end
|
66
|
+
puts "[+] Metasm assembly shell"
|
67
|
+
puts "type help for usage..\n\n"
|
84
68
|
|
85
|
-
|
69
|
+
Readline.completion_proc = lambda { |line| %w[help exit quit].find_all { |w| line.downcase == w[0, line.length] } }
|
70
|
+
Readline.completion_append_character = ' '
|
71
|
+
|
72
|
+
while line = Readline.readline('asm> ', true)
|
73
|
+
case line
|
74
|
+
when /^help(\W|$)/
|
75
|
+
puts "",
|
76
|
+
"Type in opcodes to see their binary form",
|
77
|
+
"You can use ';' to type multi-line stuff",
|
78
|
+
"e.g. 'nop nop' will display \"\\x90\\x90\"",
|
79
|
+
"",
|
80
|
+
"exit/quit Quit the console",
|
81
|
+
"help Show this screen",
|
82
|
+
""
|
83
|
+
when /^(quit|exit)(\W|$)/
|
84
|
+
break
|
85
|
+
else
|
86
|
+
begin
|
87
|
+
data = line.gsub(';', "\n")
|
88
|
+
next if data.strip.empty?
|
89
|
+
e_data = data.encode
|
90
|
+
puts '"' + e_data.unpack('C*').map { |c| '\\x%02x' % c }.join + '"'
|
91
|
+
rescue Metasm::Exception => e
|
92
|
+
puts "Error: #{e.class} #{e.message}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
puts
|
86
98
|
end
|
87
99
|
|
88
100
|
if __FILE__ == $0
|
89
|
-
|
101
|
+
asm
|
90
102
|
end
|
data/tests/mcs51.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# This file is part of Metasm, the Ruby assembly manipulation suite
|
2
|
+
# Copyright (C) 2015 Google
|
3
|
+
#
|
4
|
+
# Licence is LGPL, see LICENCE in the top-level directory
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'metasm'
|
8
|
+
|
9
|
+
|
10
|
+
class TestMCS51 < Test::Unit::TestCase
|
11
|
+
def test_mcs51_dec
|
12
|
+
hex_stream = "\x09\x00\x1F" # inc; nop; dec
|
13
|
+
hex_stream += "\x58\xF9\xEC\x32\x36\xc4\xa5\x24\x02\x45\x03"
|
14
|
+
hex_stream += "\x84\xa4\xc5\xa5\x70\xfe"
|
15
|
+
hex_stream += "\xba\x04\x08"
|
16
|
+
hex_stream += "\xc0\x04"
|
17
|
+
hex_stream += "\x11\x23"
|
18
|
+
hex_stream += "\xa1\x88"
|
19
|
+
hex_stream += "\x62\x88"
|
20
|
+
hex_stream += "\x53\x79\x66"
|
21
|
+
hex_stream += "\x02\x12\x34"
|
22
|
+
|
23
|
+
dasm = Metasm::Shellcode.disassemble(Metasm::MCS51.new, hex_stream)
|
24
|
+
#puts dasm
|
25
|
+
assert_equal(23, dasm.decoded.length)
|
26
|
+
end
|
27
|
+
end
|