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.
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