metasm 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -0
  3. data.tar.gz.sig +0 -0
  4. data/Gemfile +3 -2
  5. data/metasm.gemspec +3 -2
  6. data/metasm.rb +4 -1
  7. data/metasm/compile_c.rb +2 -2
  8. data/metasm/cpu/arc/decode.rb +0 -21
  9. data/metasm/cpu/arc/main.rb +4 -4
  10. data/metasm/cpu/arm/decode.rb +1 -5
  11. data/metasm/cpu/arm/main.rb +3 -3
  12. data/metasm/cpu/arm64/decode.rb +2 -6
  13. data/metasm/cpu/arm64/main.rb +5 -5
  14. data/metasm/cpu/bpf/decode.rb +3 -35
  15. data/metasm/cpu/bpf/main.rb +5 -5
  16. data/metasm/cpu/bpf/render.rb +1 -12
  17. data/metasm/cpu/cy16/decode.rb +0 -6
  18. data/metasm/cpu/cy16/main.rb +3 -3
  19. data/metasm/cpu/cy16/render.rb +0 -11
  20. data/metasm/cpu/dalvik/decode.rb +4 -26
  21. data/metasm/cpu/dalvik/main.rb +20 -2
  22. data/metasm/cpu/dalvik/opcodes.rb +3 -2
  23. data/metasm/cpu/{mips/compile_c.rb → ebpf.rb} +5 -2
  24. data/metasm/cpu/ebpf/debug.rb +61 -0
  25. data/metasm/cpu/ebpf/decode.rb +142 -0
  26. data/metasm/cpu/ebpf/main.rb +58 -0
  27. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  28. data/metasm/cpu/ebpf/render.rb +36 -0
  29. data/metasm/cpu/ia32/debug.rb +39 -1
  30. data/metasm/cpu/ia32/decode.rb +111 -90
  31. data/metasm/cpu/ia32/decompile.rb +45 -37
  32. data/metasm/cpu/ia32/main.rb +10 -0
  33. data/metasm/cpu/ia32/parse.rb +6 -0
  34. data/metasm/cpu/mcs51/decode.rb +1 -1
  35. data/metasm/cpu/mcs51/main.rb +11 -0
  36. data/metasm/cpu/mips/decode.rb +8 -18
  37. data/metasm/cpu/mips/main.rb +3 -3
  38. data/metasm/cpu/mips/opcodes.rb +1 -1
  39. data/metasm/cpu/msp430/decode.rb +2 -6
  40. data/metasm/cpu/msp430/main.rb +3 -3
  41. data/metasm/cpu/openrisc.rb +11 -0
  42. data/metasm/cpu/openrisc/debug.rb +106 -0
  43. data/metasm/cpu/openrisc/decode.rb +182 -0
  44. data/metasm/cpu/openrisc/decompile.rb +350 -0
  45. data/metasm/cpu/openrisc/main.rb +70 -0
  46. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  47. data/metasm/cpu/openrisc/render.rb +37 -0
  48. data/metasm/cpu/ppc/decode.rb +0 -25
  49. data/metasm/cpu/ppc/main.rb +6 -6
  50. data/metasm/cpu/ppc/opcodes.rb +3 -4
  51. data/metasm/cpu/python/decode.rb +0 -20
  52. data/metasm/cpu/python/main.rb +1 -1
  53. data/metasm/cpu/sh4/decode.rb +2 -6
  54. data/metasm/cpu/sh4/main.rb +25 -23
  55. data/metasm/cpu/st20/decode.rb +0 -7
  56. data/metasm/cpu/webasm.rb +11 -0
  57. data/metasm/cpu/webasm/debug.rb +31 -0
  58. data/metasm/cpu/webasm/decode.rb +321 -0
  59. data/metasm/cpu/webasm/decompile.rb +386 -0
  60. data/metasm/cpu/webasm/encode.rb +104 -0
  61. data/metasm/cpu/webasm/main.rb +81 -0
  62. data/metasm/cpu/webasm/opcodes.rb +214 -0
  63. data/metasm/cpu/x86_64/compile_c.rb +13 -9
  64. data/metasm/cpu/x86_64/parse.rb +1 -1
  65. data/metasm/cpu/z80/decode.rb +0 -27
  66. data/metasm/cpu/z80/main.rb +3 -3
  67. data/metasm/cpu/z80/render.rb +0 -11
  68. data/metasm/debug.rb +43 -8
  69. data/metasm/decode.rb +62 -14
  70. data/metasm/decompile.rb +793 -466
  71. data/metasm/disassemble.rb +188 -131
  72. data/metasm/disassemble_api.rb +30 -17
  73. data/metasm/dynldr.rb +2 -2
  74. data/metasm/encode.rb +8 -2
  75. data/metasm/exe_format/autoexe.rb +2 -0
  76. data/metasm/exe_format/coff.rb +21 -3
  77. data/metasm/exe_format/coff_decode.rb +12 -0
  78. data/metasm/exe_format/coff_encode.rb +6 -3
  79. data/metasm/exe_format/dex.rb +13 -3
  80. data/metasm/exe_format/elf.rb +12 -2
  81. data/metasm/exe_format/elf_decode.rb +59 -1
  82. data/metasm/exe_format/main.rb +2 -0
  83. data/metasm/exe_format/mz.rb +1 -0
  84. data/metasm/exe_format/pe.rb +25 -3
  85. data/metasm/exe_format/wasm.rb +402 -0
  86. data/metasm/gui/dasm_decomp.rb +171 -95
  87. data/metasm/gui/dasm_graph.rb +61 -2
  88. data/metasm/gui/dasm_hex.rb +2 -2
  89. data/metasm/gui/dasm_main.rb +45 -19
  90. data/metasm/gui/debug.rb +13 -4
  91. data/metasm/gui/gtk.rb +12 -4
  92. data/metasm/main.rb +108 -103
  93. data/metasm/os/emulator.rb +175 -0
  94. data/metasm/os/main.rb +11 -6
  95. data/metasm/parse.rb +23 -12
  96. data/metasm/parse_c.rb +189 -135
  97. data/metasm/preprocessor.rb +16 -1
  98. data/misc/openrisc-parser.rb +79 -0
  99. data/samples/dasm-plugins/scanxrefs.rb +6 -4
  100. data/samples/dasm-plugins/selfmodify.rb +8 -8
  101. data/samples/dbg-plugins/trace_func.rb +1 -1
  102. data/samples/disassemble-gui.rb +14 -3
  103. data/samples/emubios.rb +251 -0
  104. data/samples/emudbg.rb +127 -0
  105. data/samples/lindebug.rb +79 -78
  106. data/samples/metasm-shell.rb +8 -8
  107. data/tests/all.rb +1 -1
  108. data/tests/expression.rb +2 -0
  109. data/tests/graph_layout.rb +1 -1
  110. data/tests/ia32.rb +1 -0
  111. data/tests/mips.rb +1 -1
  112. data/tests/preprocessor.rb +18 -0
  113. metadata +124 -6
  114. metadata.gz.sig +0 -0
@@ -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
- msk = (1 << cpu.size) - 1
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-4).each { |off|
14
- r = raw[off, 4].unpack('V').first
15
- ans << (s_addr + off) if (r + off+4 + s_addr) & msk == target or r == target
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.keys.grep(Symbol).each { |reg|
104
- bt = dasm.backtrace(reg, a_pre, :include_start => true)
105
- init_bd[reg] = bt.first if bt.length == 1 and bt.first != Metasm::Expression::Unknown and bt.first != Metasm::Expression[reg]
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
- if k.kind_of? Metasm::Indirection
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
- bd.update k => Metasm::Expression[v.bind(pre_bd).reduce]
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 = post_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 << [newto, b.list.last.address]
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.disassemble_fast_block(addr)
95
+ disassembler.disassemble_fast(addr)
96
96
  if di = disassembler.di_at(addr)
97
97
  di.block
98
98
  end
@@ -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, ep)
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
+
@@ -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