metasm 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/Gemfile +3 -2
- data/metasm.gemspec +3 -2
- data/metasm.rb +4 -1
- data/metasm/compile_c.rb +2 -2
- data/metasm/cpu/arc/decode.rb +0 -21
- data/metasm/cpu/arc/main.rb +4 -4
- data/metasm/cpu/arm/decode.rb +1 -5
- data/metasm/cpu/arm/main.rb +3 -3
- data/metasm/cpu/arm64/decode.rb +2 -6
- data/metasm/cpu/arm64/main.rb +5 -5
- data/metasm/cpu/bpf/decode.rb +3 -35
- data/metasm/cpu/bpf/main.rb +5 -5
- data/metasm/cpu/bpf/render.rb +1 -12
- data/metasm/cpu/cy16/decode.rb +0 -6
- data/metasm/cpu/cy16/main.rb +3 -3
- data/metasm/cpu/cy16/render.rb +0 -11
- data/metasm/cpu/dalvik/decode.rb +4 -26
- data/metasm/cpu/dalvik/main.rb +20 -2
- data/metasm/cpu/dalvik/opcodes.rb +3 -2
- data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
- data/metasm/cpu/ebpf/debug.rb +61 -0
- data/metasm/cpu/ebpf/decode.rb +142 -0
- data/metasm/cpu/ebpf/main.rb +58 -0
- data/metasm/cpu/ebpf/opcodes.rb +97 -0
- data/metasm/cpu/ebpf/render.rb +36 -0
- data/metasm/cpu/ia32/debug.rb +39 -1
- data/metasm/cpu/ia32/decode.rb +111 -90
- data/metasm/cpu/ia32/decompile.rb +45 -37
- data/metasm/cpu/ia32/main.rb +10 -0
- data/metasm/cpu/ia32/parse.rb +6 -0
- data/metasm/cpu/mcs51/decode.rb +1 -1
- data/metasm/cpu/mcs51/main.rb +11 -0
- data/metasm/cpu/mips/decode.rb +8 -18
- data/metasm/cpu/mips/main.rb +3 -3
- data/metasm/cpu/mips/opcodes.rb +1 -1
- data/metasm/cpu/msp430/decode.rb +2 -6
- data/metasm/cpu/msp430/main.rb +3 -3
- data/metasm/cpu/openrisc.rb +11 -0
- data/metasm/cpu/openrisc/debug.rb +106 -0
- data/metasm/cpu/openrisc/decode.rb +182 -0
- data/metasm/cpu/openrisc/decompile.rb +350 -0
- data/metasm/cpu/openrisc/main.rb +70 -0
- data/metasm/cpu/openrisc/opcodes.rb +109 -0
- data/metasm/cpu/openrisc/render.rb +37 -0
- data/metasm/cpu/ppc/decode.rb +0 -25
- data/metasm/cpu/ppc/main.rb +6 -6
- data/metasm/cpu/ppc/opcodes.rb +3 -4
- data/metasm/cpu/python/decode.rb +0 -20
- data/metasm/cpu/python/main.rb +1 -1
- data/metasm/cpu/sh4/decode.rb +2 -6
- data/metasm/cpu/sh4/main.rb +25 -23
- data/metasm/cpu/st20/decode.rb +0 -7
- data/metasm/cpu/webasm.rb +11 -0
- data/metasm/cpu/webasm/debug.rb +31 -0
- data/metasm/cpu/webasm/decode.rb +321 -0
- data/metasm/cpu/webasm/decompile.rb +386 -0
- data/metasm/cpu/webasm/encode.rb +104 -0
- data/metasm/cpu/webasm/main.rb +81 -0
- data/metasm/cpu/webasm/opcodes.rb +214 -0
- data/metasm/cpu/x86_64/compile_c.rb +13 -9
- data/metasm/cpu/x86_64/parse.rb +1 -1
- data/metasm/cpu/z80/decode.rb +0 -27
- data/metasm/cpu/z80/main.rb +3 -3
- data/metasm/cpu/z80/render.rb +0 -11
- data/metasm/debug.rb +43 -8
- data/metasm/decode.rb +62 -14
- data/metasm/decompile.rb +793 -466
- data/metasm/disassemble.rb +188 -131
- data/metasm/disassemble_api.rb +30 -17
- data/metasm/dynldr.rb +2 -2
- data/metasm/encode.rb +8 -2
- data/metasm/exe_format/autoexe.rb +2 -0
- data/metasm/exe_format/coff.rb +21 -3
- data/metasm/exe_format/coff_decode.rb +12 -0
- data/metasm/exe_format/coff_encode.rb +6 -3
- data/metasm/exe_format/dex.rb +13 -3
- data/metasm/exe_format/elf.rb +12 -2
- data/metasm/exe_format/elf_decode.rb +59 -1
- data/metasm/exe_format/main.rb +2 -0
- data/metasm/exe_format/mz.rb +1 -0
- data/metasm/exe_format/pe.rb +25 -3
- data/metasm/exe_format/wasm.rb +402 -0
- data/metasm/gui/dasm_decomp.rb +171 -95
- data/metasm/gui/dasm_graph.rb +61 -2
- data/metasm/gui/dasm_hex.rb +2 -2
- data/metasm/gui/dasm_main.rb +45 -19
- data/metasm/gui/debug.rb +13 -4
- data/metasm/gui/gtk.rb +12 -4
- data/metasm/main.rb +108 -103
- data/metasm/os/emulator.rb +175 -0
- data/metasm/os/main.rb +11 -6
- data/metasm/parse.rb +23 -12
- data/metasm/parse_c.rb +189 -135
- data/metasm/preprocessor.rb +16 -1
- data/misc/openrisc-parser.rb +79 -0
- data/samples/dasm-plugins/scanxrefs.rb +6 -4
- data/samples/dasm-plugins/selfmodify.rb +8 -8
- data/samples/dbg-plugins/trace_func.rb +1 -1
- data/samples/disassemble-gui.rb +14 -3
- data/samples/emubios.rb +251 -0
- data/samples/emudbg.rb +127 -0
- data/samples/lindebug.rb +79 -78
- data/samples/metasm-shell.rb +8 -8
- data/tests/all.rb +1 -1
- data/tests/expression.rb +2 -0
- data/tests/graph_layout.rb +1 -1
- data/tests/ia32.rb +1 -0
- data/tests/mips.rb +1 -1
- data/tests/preprocessor.rb +18 -0
- metadata +124 -6
- metadata.gz.sig +0 -0
data/metasm/preprocessor.rb
CHANGED
|
@@ -1176,7 +1176,7 @@ class Preprocessor
|
|
|
1176
1176
|
op = op.dup
|
|
1177
1177
|
op.raw << ntok.raw
|
|
1178
1178
|
# ok
|
|
1179
|
-
when '^', '+', '-', '*', '/', '%', '>>', '<<', '>=', '<=', '||', '&&', '!=', '=='
|
|
1179
|
+
when '^', '+', '-', '*', '/', '%', '>>', '<<', '>=', '<=', '||', '&&', '!=', '==', '?'
|
|
1180
1180
|
# unknown
|
|
1181
1181
|
else
|
|
1182
1182
|
lexer.unreadtok tok
|
|
@@ -1273,6 +1273,21 @@ class Preprocessor
|
|
|
1273
1273
|
stack << Expression.new(opstack.pop, stack.pop, stack.pop)
|
|
1274
1274
|
end
|
|
1275
1275
|
|
|
1276
|
+
if op.value == :'?'
|
|
1277
|
+
a1 = parse(lexer)
|
|
1278
|
+
if not tok = lexer.readtok or tok.type != :punct or tok.raw != ':'
|
|
1279
|
+
raise op, 'expected ":" ternary operator'
|
|
1280
|
+
end
|
|
1281
|
+
a2 = parse(lexer)
|
|
1282
|
+
case Expression[stack.pop].reduce
|
|
1283
|
+
when 0; stack << a2
|
|
1284
|
+
when ::Integer; stack << a1
|
|
1285
|
+
else; stack << a2
|
|
1286
|
+
end
|
|
1287
|
+
|
|
1288
|
+
next
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1276
1291
|
opstack << op.value
|
|
1277
1292
|
|
|
1278
1293
|
raise op, 'need rhs' if not e = parse_value(lexer)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
require 'xml'
|
|
4
|
+
|
|
5
|
+
xml = Xml.parse_file(ARGV.shift || 'openrisc-insn.html')
|
|
6
|
+
|
|
7
|
+
# [name, bin, args]
|
|
8
|
+
addop = []
|
|
9
|
+
# arg => [flds]
|
|
10
|
+
valid_args = {}
|
|
11
|
+
# field => [bitoff, bitmask]
|
|
12
|
+
fields = {}
|
|
13
|
+
|
|
14
|
+
xml.each('ul') { |ul|
|
|
15
|
+
syntax = nil
|
|
16
|
+
bits = []
|
|
17
|
+
vals = []
|
|
18
|
+
trno = 0
|
|
19
|
+
ul.each('li') { |li|
|
|
20
|
+
if li.children[0] == 'syntax:'
|
|
21
|
+
# <li>syntax:<tt><font>l.add $rd, $ra, $rb</font></tt></li>
|
|
22
|
+
syntax = li.children[1].children[0].children[0]
|
|
23
|
+
end
|
|
24
|
+
}
|
|
25
|
+
next if not syntax
|
|
26
|
+
ul.each('tr') { |tr|
|
|
27
|
+
case trno
|
|
28
|
+
when 0; tr.each('td') { |td| bits << td.children[0].split.map { |b| b.to_i } }
|
|
29
|
+
when 2; tr.each('td') { |td| vals << td.children.map { |v| v =~ /^0x/ ? v.to_i(16) : v.gsub('-', '') }.first }
|
|
30
|
+
end
|
|
31
|
+
trno += 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
iname = syntax.split[0].sub(/^l\./, '')
|
|
35
|
+
iargs = syntax.split[1].to_s.split(',').map { |a| a.gsub(/[${}-]/, '').gsub(/(\w+)\((\w+)\)/, '\2_\1') }
|
|
36
|
+
bin = bits.zip(vals).inject(0) { |b, (bt, bv)| bv.kind_of?(Integer) ? b | (bv << bt.last) : b }
|
|
37
|
+
addop << [iname, bin]
|
|
38
|
+
|
|
39
|
+
flds = bits.zip(vals).inject({}) { |h, (bt, bv)|
|
|
40
|
+
next h if bv.kind_of?(Integer)
|
|
41
|
+
blen = bt.first + 1 - bt.last
|
|
42
|
+
h.update bv => [bt.last, (1 << blen) - 1]
|
|
43
|
+
}
|
|
44
|
+
flds.each { |n, (o, m)|
|
|
45
|
+
if not fields[n]
|
|
46
|
+
fields[n] = [o, m]
|
|
47
|
+
elsif fields[n] != [o, m]
|
|
48
|
+
puts "# fields mismatch in #{iname} #{n}"
|
|
49
|
+
end
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
addop.last << iargs
|
|
53
|
+
|
|
54
|
+
iargs.each { |a|
|
|
55
|
+
a.split('_').each { |f|
|
|
56
|
+
if not flds.delete(f)
|
|
57
|
+
puts "# no field #{f} for arg #{a} in #{iname}"
|
|
58
|
+
end
|
|
59
|
+
}
|
|
60
|
+
valid_args[a] ||= a.split('_')
|
|
61
|
+
}
|
|
62
|
+
flds.each_key { |f|
|
|
63
|
+
puts "# no arg using #{f} in #{iname}"
|
|
64
|
+
a_i = "#{f}_ign"
|
|
65
|
+
valid_args[a_i] ||= [f]
|
|
66
|
+
addop.last.last << a_i
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
puts "\tdef init_cpu"
|
|
71
|
+
puts "\t\t@opcode_list = []"
|
|
72
|
+
puts "\t\t@valid_args = { #{valid_args.map { |a, f| ":#{a} => [#{f.map { |ff| ':' + ff }.join(', ')}]" }.join(', ')} }"
|
|
73
|
+
puts "\t\t@fields_off = { #{fields.map { |k, v| ":#{k} => #{v[0]}" }.join(', ')} }"
|
|
74
|
+
puts "\t\t@fields_mask = { #{fields.map { |k, v| ":#{k} => #{'0x%02X' % v[1]}" }.join(', ')} }"
|
|
75
|
+
puts
|
|
76
|
+
addop.each { |op|
|
|
77
|
+
puts "\t\taddop '#{op[0]}', #{'0x%08X' % op[1]}#{op[2].map { |a| ", :#{a}" }.join('')}"
|
|
78
|
+
}
|
|
79
|
+
puts "\tend"
|
|
@@ -7,12 +7,14 @@
|
|
|
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
|
-
|
|
10
|
+
csz = cpu.size
|
|
11
|
+
msk = (1 << csz) - 1
|
|
12
|
+
upq = (csz == 64 ? 'q' : 'V')
|
|
11
13
|
sections.sort.each { |s_addr, edata|
|
|
12
14
|
raw = edata.data.to_str
|
|
13
|
-
(0..raw.length-
|
|
14
|
-
r = raw[off,
|
|
15
|
-
ans << (s_addr + off) if (r + off+
|
|
15
|
+
(0..raw.length-csz/8).each { |off|
|
|
16
|
+
r = raw[off, csz/8].unpack(upq).first
|
|
17
|
+
ans << (s_addr + off) if (r + off+csz/8 + s_addr) & msk == target or r == target
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
ans
|
|
@@ -100,9 +100,9 @@ def self.emu(dasm, addr)
|
|
|
100
100
|
loop_again_cond = Expression[:'!', loop_again_cond] if dasm.decoded[a_cond].next_addr != a_out
|
|
101
101
|
|
|
102
102
|
init_bd = {}
|
|
103
|
-
loop_bd.
|
|
104
|
-
bt = dasm.backtrace(
|
|
105
|
-
init_bd[
|
|
103
|
+
loop_bd.values.map { |v| v.externals }.flatten.uniq.each { |ext|
|
|
104
|
+
bt = dasm.backtrace(ext, a_pre, :include_start => true)
|
|
105
|
+
init_bd[ext] = bt.first if bt.length == 1 and bt.first != Metasm::Expression::Unknown and bt.first != Metasm::Expression[ext]
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
# reject non-determinist memory write
|
|
@@ -115,11 +115,11 @@ def self.emu(dasm, addr)
|
|
|
115
115
|
loop do
|
|
116
116
|
# the effects of the loop
|
|
117
117
|
post_bd = loop_bd.inject({}) { |bd, (k, v)|
|
|
118
|
-
|
|
118
|
+
if k.kind_of? Metasm::Indirection
|
|
119
119
|
k = k.bind(pre_bd).reduce_rec
|
|
120
120
|
raise "bad ptr #{k}" if not dasm.get_section_at(k.pointer.reduce)
|
|
121
121
|
end
|
|
122
|
-
|
|
122
|
+
bd.update k => Metasm::Expression[v.bind(pre_bd).reduce]
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
# the indirections used by the loop
|
|
@@ -139,7 +139,7 @@ def self.emu(dasm, addr)
|
|
|
139
139
|
|
|
140
140
|
break if loop_again_cond.bind(post_bd).reduce == 0
|
|
141
141
|
|
|
142
|
-
pre_bd
|
|
142
|
+
pre_bd.update(post_bd)
|
|
143
143
|
pre_bd.delete_if { |k, v| not k.kind_of? Symbol }
|
|
144
144
|
end
|
|
145
145
|
|
|
@@ -166,7 +166,7 @@ def self.find_loop(dasm, addr)
|
|
|
166
166
|
first = b1.address
|
|
167
167
|
last = b2.list.last.address
|
|
168
168
|
post = (b2.to_normal - [b1.address]).first
|
|
169
|
-
loop_bd = dasm.code_binding(first, post)
|
|
169
|
+
loop_bd = dasm.code_binding(first, post, :include_flags => true)
|
|
170
170
|
|
|
171
171
|
[pre, first, last, post, loop_bd]
|
|
172
172
|
end
|
|
@@ -179,7 +179,7 @@ def self.redirect(dasm, addr)
|
|
|
179
179
|
b.to_normal.map! { |tn| dasm.normalize(tn) == addr ? newto : tn }
|
|
180
180
|
dasm.add_xref(newto, Metasm::Xref.new(:x, b.list.last.address))
|
|
181
181
|
b.list.last.add_comment "x:#{newto}"
|
|
182
|
-
dasm.addrs_todo <<
|
|
182
|
+
dasm.addrs_todo << { :addr => newto, :from => b.list.last.address }
|
|
183
183
|
}
|
|
184
184
|
end
|
|
185
185
|
end
|
|
@@ -92,7 +92,7 @@ end
|
|
|
92
92
|
# retrieve an instructionblock, disassemble if needed
|
|
93
93
|
def trace_get_block(addr)
|
|
94
94
|
# TODO trace all blocks from addr for which we know the target, stop on call / jmp [foo]
|
|
95
|
-
disassembler.
|
|
95
|
+
disassembler.disassemble_fast(addr)
|
|
96
96
|
if di = disassembler.di_at(addr)
|
|
97
97
|
di.block
|
|
98
98
|
end
|
data/samples/disassemble-gui.rb
CHANGED
|
@@ -51,6 +51,7 @@ OptionParser.new { |opt|
|
|
|
51
51
|
opt.on('-d', '--debug') { $DEBUG = $VERBOSE = true }
|
|
52
52
|
opt.on('-S <file>', '--session <sessionfile>', 'save user actions in this session file') { |a| opts[:session] = a }
|
|
53
53
|
opt.on('-N', '--new-session', 'start new session, discard old one') { opts[:newsession] = true }
|
|
54
|
+
opt.on('-A', '--disassemble-all-entrypoints') { opts[:dasm_all] = true }
|
|
54
55
|
}.parse!(ARGV)
|
|
55
56
|
|
|
56
57
|
case exename = ARGV.shift
|
|
@@ -83,6 +84,7 @@ else
|
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
ep = ARGV.map { |arg| (?0..?9).include?(arg[0]) ? Integer(arg) : arg }
|
|
87
|
+
ep += exe.get_default_entrypoints if opts[:dasm_all]
|
|
86
88
|
|
|
87
89
|
if exe
|
|
88
90
|
dasm = exe.disassembler
|
|
@@ -91,8 +93,7 @@ if exe
|
|
|
91
93
|
dasm.parse_c_file opts[:cheader] if opts[:cheader]
|
|
92
94
|
dasm.backtrace_maxblocks_data = -1 if opts[:nodatatrace]
|
|
93
95
|
dasm.debug_backtrace = true if opts[:debugbacktrace]
|
|
94
|
-
dasm.callback_finished = lambda { w.dasm_widget.focus_addr w.dasm_widget.curaddr, :decompile ; dasm.decompiler.finalize } if opts[:decompile]
|
|
95
|
-
dasm.disassemble_fast_deep(*ep) if opts[:fast]
|
|
96
|
+
dasm.callback_finished = lambda { dasm.callback_finished = nil ; w.dasm_widget.focus_addr w.dasm_widget.curaddr, :decompile ; dasm.decompiler.finalize } if opts[:decompile]
|
|
96
97
|
elsif dbg
|
|
97
98
|
dbg.load_map opts[:map] if opts[:map]
|
|
98
99
|
dbg.disassembler.parse_c_file opts[:cheader] if opts[:cheader]
|
|
@@ -104,8 +105,10 @@ elsif dbg
|
|
|
104
105
|
end
|
|
105
106
|
}
|
|
106
107
|
end
|
|
108
|
+
|
|
107
109
|
if dasm
|
|
108
|
-
w.display(dasm
|
|
110
|
+
w.display(dasm)
|
|
111
|
+
w.dasm_widget.focus_addr(ep.first) if not ep.empty?
|
|
109
112
|
opts[:plugin].to_a.each { |p|
|
|
110
113
|
begin
|
|
111
114
|
dasm.load_plugin(p)
|
|
@@ -113,6 +116,13 @@ if dasm
|
|
|
113
116
|
puts "Error with plugin #{p}: #{$!.class} #{$!}"
|
|
114
117
|
end
|
|
115
118
|
}
|
|
119
|
+
ep.each { |eep|
|
|
120
|
+
if opts[:fast]
|
|
121
|
+
w.dasm_widget.disassemble_fast_deep(eep)
|
|
122
|
+
else
|
|
123
|
+
w.dasm_widget.disassemble(eep)
|
|
124
|
+
end
|
|
125
|
+
}
|
|
116
126
|
|
|
117
127
|
if opts[:session]
|
|
118
128
|
if File.exist?(opts[:session])
|
|
@@ -130,3 +140,4 @@ end
|
|
|
130
140
|
opts[:hookstr].to_a.each { |f| eval f }
|
|
131
141
|
|
|
132
142
|
Metasm::Gui.main
|
|
143
|
+
|
data/samples/emubios.rb
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
# Sample to show the EmuDebugger working on X86 16bit realmode code (eg hard disk MBR)
|
|
4
|
+
|
|
5
|
+
require 'metasm'
|
|
6
|
+
include Metasm
|
|
7
|
+
|
|
8
|
+
# use global vars for read_sector()
|
|
9
|
+
$dasm = $dbg = nil
|
|
10
|
+
|
|
11
|
+
$rawname = ARGV.shift || 'mbr.bin'
|
|
12
|
+
cpu = Ia32.new(16)
|
|
13
|
+
# add register tracking for the segment registers
|
|
14
|
+
cpu.dbg_register_list << :cs << :ds << :es << :fs << :gs << :ss
|
|
15
|
+
$dasm = dasm = Shellcode.new(Ia32.new(16), 0).disassembler
|
|
16
|
+
dasm.backtrace_maxblocks_data = -1
|
|
17
|
+
|
|
18
|
+
# initial memory
|
|
19
|
+
dasm.add_section(EncodedData.new("\x00"*0x40000), 0)
|
|
20
|
+
|
|
21
|
+
# read one sector from the drive to memory, invalidate already disassembled instruction from the address range
|
|
22
|
+
def read_sector(addr, fileoff, len)
|
|
23
|
+
e, o = $dasm.get_section_at(addr)
|
|
24
|
+
$dasm.decoded.keys.grep(addr..(addr+len)).each { |k| $dasm.decoded.delete k }
|
|
25
|
+
raw_chunk = File.open($rawname, 'rb') { |fd| fd.pos = fileoff ; fd.read len } || ''
|
|
26
|
+
raw_chunk << 0.chr until raw_chunk.length >= len
|
|
27
|
+
e[e.ptr, len] = raw_chunk
|
|
28
|
+
$dbg.invalidate if $dbg
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# load as BIOS MBR code
|
|
32
|
+
read_sector(0x7c00, 0, 0x200)
|
|
33
|
+
|
|
34
|
+
# buffer used for int 16h read keyboard
|
|
35
|
+
$stdin_buf = "moo\r\n"
|
|
36
|
+
|
|
37
|
+
$dbg = dbg = Metasm::EmuDebugger.new(dasm)
|
|
38
|
+
dbg.set_reg_value(:eip, 0x7c00)
|
|
39
|
+
dbg.set_reg_value(:esp, 0x7c00)
|
|
40
|
+
|
|
41
|
+
# reset trace file
|
|
42
|
+
File.open('emudbg.trace', 'w') {}
|
|
43
|
+
def trace(thing)
|
|
44
|
+
File.open('emudbg.trace', 'a') { |fd| fd.puts thing }
|
|
45
|
+
end
|
|
46
|
+
def puts_trace(str)
|
|
47
|
+
trace str
|
|
48
|
+
puts str
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# custom emulation of various instrs for realmode-specific behavior
|
|
52
|
+
# this is a very bad realmode emulator
|
|
53
|
+
# eg segment selectors are mostly ignored except for a few specific cases described here
|
|
54
|
+
# seems to work for the few crackme i worked on !
|
|
55
|
+
dbg.callback_emulate_di = lambda { |di|
|
|
56
|
+
puts di if $VERBOSE
|
|
57
|
+
trace di
|
|
58
|
+
case di.opcode.name
|
|
59
|
+
when 'jmp'
|
|
60
|
+
tg = di.instruction.args.first
|
|
61
|
+
if di.address == dbg.resolve(tg)
|
|
62
|
+
# break from simulation on ebfe
|
|
63
|
+
puts "EB FE !"
|
|
64
|
+
dbg.bpx(di.address)
|
|
65
|
+
break true
|
|
66
|
+
elsif tg.kind_of?(Ia32::Farptr)
|
|
67
|
+
# handle far jumps
|
|
68
|
+
dbg.pc = dbg.resolve(Expression[[tg.seg, :<<, 4], :+, tg.addr])
|
|
69
|
+
break true
|
|
70
|
+
end
|
|
71
|
+
when 'retf.i16'
|
|
72
|
+
# XXX really ss:esp, but we'd need to fix push/pop etc too, so keep to 0:esp for now
|
|
73
|
+
w1 = dbg.memory_read_int(:esp, 2)
|
|
74
|
+
dbg.set_reg_value(:esp, dbg.resolve(Expression[:esp, :+, 2]))
|
|
75
|
+
w2 = dbg.memory_read_int(:esp, 2)
|
|
76
|
+
dbg.set_reg_value(:esp, dbg.resolve(Expression[:esp, :+, 2]))
|
|
77
|
+
dbg.set_reg_value(:cs, w2)
|
|
78
|
+
dbg.pc = dbg.resolve(Expression[[w2, :<<, 4], :+, w1])
|
|
79
|
+
break true
|
|
80
|
+
when 'ret'
|
|
81
|
+
w1 = dbg.memory_read_int(:esp, 2)
|
|
82
|
+
dbg.set_reg_value(:esp, dbg.resolve(Expression[:esp, :+, 2]))
|
|
83
|
+
dbg.pc = dbg.resolve(Expression[[:cs, :<<, 4], :+, w1])
|
|
84
|
+
break true
|
|
85
|
+
when 'lodsb'
|
|
86
|
+
# read from ds:si instead of 0:esi
|
|
87
|
+
# XXX rep
|
|
88
|
+
dbg.set_reg_value(:eax, dbg.resolve(Expression[[:eax, :&, 0xffffff00], :|, Indirection[[[:ds, :<<, 4], :+, [:esi, :&, 0xffff]], 1]]))
|
|
89
|
+
dbg.set_reg_value(:esi, dbg.resolve(Expression[[:esi, :&, 0xffff0000], :|, [[:esi, :&, 0xffff], :+, 1]]))
|
|
90
|
+
dbg.pc += di.bin_length
|
|
91
|
+
true
|
|
92
|
+
when 'lodsd'
|
|
93
|
+
# read from ds:si instead of 0:esi
|
|
94
|
+
# XXX rep
|
|
95
|
+
dbg.set_reg_value(:eax, dbg.resolve(Expression[Indirection[[[:ds, :<<, 4], :+, [:esi, :&, 0xffff]], 4]]))
|
|
96
|
+
dbg.set_reg_value(:esi, dbg.resolve(Expression[[:esi, :&, 0xffff0000], :|, [[:esi, :&, 0xffff], :+, 4]]))
|
|
97
|
+
dbg.pc += di.bin_length
|
|
98
|
+
true
|
|
99
|
+
when 'stosd'
|
|
100
|
+
# write to es:di instead of 0:edi
|
|
101
|
+
# XXX rep
|
|
102
|
+
dbg.memory_write_int(Expression[[:es, :<<, 4], :+, [:edi, :&, 0xffff]], :eax, 4)
|
|
103
|
+
dbg.set_reg_value(:edi, dbg.resolve(Expression[[:edi, :&, 0xffff0000], :|, [[:edi, :&, 0xffff], :+, 4]]))
|
|
104
|
+
dbg.pc += di.bin_length
|
|
105
|
+
true
|
|
106
|
+
when /movs([bwdq])/
|
|
107
|
+
sz = { 'b' => 1, 'w' => 2, 'd' => 4, 'q' => 8 }[$1]
|
|
108
|
+
# XXX repz
|
|
109
|
+
if di.instruction.prefix[:rep]
|
|
110
|
+
count = dbg[:ecx] & 0xffff
|
|
111
|
+
else
|
|
112
|
+
count = 1
|
|
113
|
+
end
|
|
114
|
+
count.times {
|
|
115
|
+
val = dbg.resolve(Expression[Indirection[[[:ds, :<<, 4], :+, [:esi, :&, 0xffff]], sz]])
|
|
116
|
+
dbg.memory_write_int(Expression[[:es, :<<, 4], :+, [:edi, :&, 0xffff]], val, sz)
|
|
117
|
+
dbg[:esi] = (dbg[:esi] + sz) & 0xffff
|
|
118
|
+
dbg[:edi] = (dbg[:edi] + sz) & 0xffff
|
|
119
|
+
dbg[:ecx] -= 1 if di.instruction.prefix[:rep]
|
|
120
|
+
}
|
|
121
|
+
dbg.pc += di.bin_length
|
|
122
|
+
true
|
|
123
|
+
when 'les'
|
|
124
|
+
dst = di.instruction.args[0].symbolic(di)
|
|
125
|
+
dst = dst.externals.first if dst.kind_of?(Expression)
|
|
126
|
+
src = di.instruction.args[1].symbolic(di)
|
|
127
|
+
dbg.set_reg_value(dst, dbg.resolve(Indirection[[[:es, :<<, 4], :+, src.pointer], 2]))
|
|
128
|
+
dbg.pc += di.bin_length
|
|
129
|
+
true
|
|
130
|
+
when 'div'
|
|
131
|
+
op = di.instruction.args[0].symbolic(di)
|
|
132
|
+
sz = op.kind_of?(Expression) ? { 0xff => 1, 0xffff => 2 }[op.rexpr] : 4
|
|
133
|
+
dv = dbg.resolve(op)
|
|
134
|
+
case sz
|
|
135
|
+
when 1
|
|
136
|
+
dv2 = dbg[:eax] & 0xffff
|
|
137
|
+
dbg[:eax] = ((dv2 / dv) & 0xff) | (((dv2 % dv) & 0xff) << 8)
|
|
138
|
+
when 2
|
|
139
|
+
dv2 = ((dbg[:edx] & 0xffff) << 16) | (dbg[:eax] & 0xffff)
|
|
140
|
+
dbg[:eax] = (dv2 / dv) & 0xffff
|
|
141
|
+
dbg[:edx] = (dv2 % dv) & 0xffff
|
|
142
|
+
when 4
|
|
143
|
+
dv2 = (dbg[:edx] << 32) | dbg[:eax]
|
|
144
|
+
dbg[:eax] = (dv2 / dv)
|
|
145
|
+
dbg[:edx] = (dv2 % dv)
|
|
146
|
+
end
|
|
147
|
+
dbg.pc += di.bin_length
|
|
148
|
+
true
|
|
149
|
+
when 'loop'
|
|
150
|
+
# movzx ecx, cx
|
|
151
|
+
dbg.set_reg_value(:ecx, dbg.resolve(Expression[:ecx, :&, 0xffff]))
|
|
152
|
+
false
|
|
153
|
+
when 'int'
|
|
154
|
+
intnr = di.instruction.args.first.reduce
|
|
155
|
+
eax = dbg[:eax]
|
|
156
|
+
ah = (eax >> 8) & 0xff
|
|
157
|
+
al = eax & 0xff
|
|
158
|
+
case intnr
|
|
159
|
+
when 0x10
|
|
160
|
+
# print screen interrupt
|
|
161
|
+
$screenbuf ||= []
|
|
162
|
+
$screenx ||= 0
|
|
163
|
+
$screeny ||= 0
|
|
164
|
+
case ah
|
|
165
|
+
when 0x00
|
|
166
|
+
$screenbuf = []
|
|
167
|
+
$screenx = 0
|
|
168
|
+
$screeny = 0
|
|
169
|
+
when 0x02
|
|
170
|
+
dh = (dbg[:edx] >> 8) & 0xff
|
|
171
|
+
dl = dbg[:edx] & 0xff
|
|
172
|
+
puts_trace "movc(#{dh}, #{dl})"
|
|
173
|
+
puts $screenbuf
|
|
174
|
+
$screenx = dl
|
|
175
|
+
$screeny = dh
|
|
176
|
+
when 0x0e
|
|
177
|
+
puts_trace "putc(#{[al].pack('C*').inspect})"
|
|
178
|
+
$screenbuf << '' until $screenbuf.length > $screeny
|
|
179
|
+
$screenbuf[$screeny] << '.' until $screenbuf[$screeny].length > $screenx
|
|
180
|
+
$screenbuf[$screeny][$screenx, 1] = [al].pack('C*')
|
|
181
|
+
$screenx += 1
|
|
182
|
+
else
|
|
183
|
+
puts_trace "unk int #{'%02xh' % intnr} #{'%02x' % ah}"
|
|
184
|
+
end
|
|
185
|
+
when 0x13
|
|
186
|
+
# read disk interrupt
|
|
187
|
+
drive_nr = dbg[:edx] & 0xff
|
|
188
|
+
case ah
|
|
189
|
+
when 0x00
|
|
190
|
+
dbg.unset_flag(:c)
|
|
191
|
+
puts_trace "reset_disk_drive #{'%x' % drive_nr}"
|
|
192
|
+
when 0x02
|
|
193
|
+
sect_cnt = al
|
|
194
|
+
sect_c = ((dbg[:ecx] >> 8) & 0xff) | ((dbg[:ecx] << 2) & 0x300)
|
|
195
|
+
sect_h = (dbg[:edx] >> 8) & 0xff
|
|
196
|
+
sect_s = (dbg[:ecx] & 0x3f)
|
|
197
|
+
sect_lba = (sect_c * 16 + sect_h) * 63 + sect_s - 1
|
|
198
|
+
sect_drv = dbg[:edx] & 0xff
|
|
199
|
+
dst_addr = dbg[:es] * 16 + dbg[:ebx]
|
|
200
|
+
puts_trace "read #{sect_cnt} sect at #{'%03X:%02X:%02X' % [sect_c, sect_h, sect_s]} (#{'0x%X' % sect_lba}) to #{'0x%X' % dst_addr} drv #{'%x' % drive_nr}"
|
|
201
|
+
read_sector(dst_addr, sect_lba * 512, sect_cnt * 512)
|
|
202
|
+
when 0x08
|
|
203
|
+
dbg.unset_flag(:c)
|
|
204
|
+
dbg[:eax] = 0 # ah = return code
|
|
205
|
+
dbg[:ebx] = 0 # bl = drive type
|
|
206
|
+
dbg[:ecx] = 0x1010 # ch = cyl_max, cl >> 6 = cyl_max_hi, cl & 3f = sector_per_track
|
|
207
|
+
dbg[:edx] = 0x1001 # dh = head_max, dl = nr_of_drives
|
|
208
|
+
puts_trace "read_drive_parameters #{'%x' % drive_nr}"
|
|
209
|
+
when 0x41
|
|
210
|
+
dbg.unset_flag(:c)
|
|
211
|
+
dbg[:ebx] = 0xaa55
|
|
212
|
+
dbg[:ecx] = 1 # 1: device access through packet structure (cf 42), 2: lock & eject, 4: enhanced drive support
|
|
213
|
+
puts_trace "drive_check_extension_present #{'%x' % drive_nr}"
|
|
214
|
+
when 0x42
|
|
215
|
+
sect_cnt = dbg.memory_read_int(Expression[[:ds, :<<, 4], :+, [[:esi, :+, 2], :&, 0xffff]], 2)
|
|
216
|
+
dst_addr = dbg.memory_read_int(Expression[[:ds, :<<, 4], :+, [[:esi, :+, 4], :&, 0xffff]], 2)
|
|
217
|
+
dst_seg = dbg.memory_read_int(Expression[[:ds, :<<, 4], :+, [[:esi, :+, 6], :&, 0xffff]], 2)
|
|
218
|
+
sect_lba = dbg.memory_read_int(Expression[[:ds, :<<, 4], :+, [[:esi, :+, 8], :&, 0xffff]], 8)
|
|
219
|
+
dst_addr += dst_seg << 4
|
|
220
|
+
puts_trace "read extended #{sect_cnt} sect at #{'0x%X' % sect_lba} to #{'0x%X' % dst_addr} drv #{'%x' % drive_nr}"
|
|
221
|
+
read_sector(dst_addr, sect_lba * 512, sect_cnt * 512)
|
|
222
|
+
else
|
|
223
|
+
puts_trace "unk int #{'%02xh' % intnr} #{'%02x' % ah}"
|
|
224
|
+
end
|
|
225
|
+
when 0x16
|
|
226
|
+
# read keyboard interrupt
|
|
227
|
+
case ah
|
|
228
|
+
when 0x00
|
|
229
|
+
al = $stdin_buf.unpack('C').first || 0
|
|
230
|
+
$stdin_buf[0, 1] = ''
|
|
231
|
+
dbg[:eax] = al
|
|
232
|
+
puts_trace "getc => #{[al].pack('C*').inspect}"
|
|
233
|
+
else
|
|
234
|
+
puts_trace "unk int #{'%02xh' % intnr} #{'%02x' % ah}"
|
|
235
|
+
end
|
|
236
|
+
else
|
|
237
|
+
puts_trace "unk int #{'%02xh' % intnr} #{'%02x' % ah}"
|
|
238
|
+
end
|
|
239
|
+
dbg.pc += di.bin_length
|
|
240
|
+
true
|
|
241
|
+
end
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# Start the GUI
|
|
245
|
+
Gui::DbgWindow.new.display(dbg)
|
|
246
|
+
# some pretty settings for the initial view
|
|
247
|
+
dbg.gui.run_command('wd 16')
|
|
248
|
+
dbg.gui.run_command('wp 6')
|
|
249
|
+
dbg.gui.parent.code.toggle_view(:graph)
|
|
250
|
+
|
|
251
|
+
Gui.main
|