metasm 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|