metasm 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -0,0 +1,67 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/main'
8
+
9
+ module Metasm
10
+ class Z80 < CPU
11
+ class Reg
12
+ class << self
13
+ attr_accessor :s_to_i, :i_to_s
14
+ end
15
+ @i_to_s = { 8 => { 0 => 'B', 1 => 'C', 2 => 'D', 3 => 'E',
16
+ 4 => 'H', 5 => 'L', 7 => 'A' },
17
+ 16 => { 0 => 'BC', 1 => 'DE', 2 => 'HL', 3 => 'SP',
18
+ 4 => 'AF' } } # AF is 3 too
19
+ @s_to_i = @i_to_s.inject({}) { |h, (sz, rh)|
20
+ h.update rh.inject({}) { |hh, (i, n)|
21
+ hh.update n => [sz, i] } }
22
+
23
+ attr_accessor :sz, :i
24
+ def initialize(sz, i)
25
+ @sz = sz
26
+ @i = i
27
+ end
28
+
29
+ def symbolic(orig=nil) ; to_s.to_sym ; end
30
+
31
+ def self.from_str(s)
32
+ raise "Bad name #{s.inspect}" if not x = @s_to_i[s]
33
+ new(*x)
34
+ end
35
+ end
36
+
37
+ class Memref
38
+ attr_accessor :base, :offset, :sz
39
+ def initialize(base, offset, sz=nil)
40
+ @base = base
41
+ offset = Expression[offset] if offset
42
+ @offset = offset
43
+ @sz = sz
44
+ end
45
+
46
+ def symbolic(orig)
47
+ p = nil
48
+ p = Expression[p, :+, @base.symbolic] if base
49
+ p = Expression[p, :+, @offset] if offset
50
+ Indirection[p.reduce, @sz, orig]
51
+ end
52
+ end
53
+
54
+ def initialize(family = :latest)
55
+ super()
56
+ @endianness = :little
57
+ @size = 16
58
+ @family = family
59
+ end
60
+
61
+ def init_opcode_list
62
+ send("init_#@family")
63
+ @opcode_list
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,224 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+ require 'metasm/cpu/z80/main'
7
+
8
+ module Metasm
9
+
10
+ class Z80
11
+ def addop(name, bin, *args)
12
+ o = Opcode.new name, bin
13
+ args.each { |a|
14
+ o.args << a if @fields_mask[a] or @valid_args[a]
15
+ o.props[a] = true if @valid_props[a]
16
+ o.fields[a] = [bin.length-1, @fields_shift[a]] if @fields_mask[a]
17
+ raise "wtf #{a.inspect}" unless @valid_args[a] or @valid_props[a] or @fields_mask[a]
18
+ }
19
+ @opcode_list << o
20
+ end
21
+
22
+ def addop_macrocc(name, bin, *args)
23
+ %w[nz z nc c po pe p m].each_with_index { |cc, i|
24
+ dbin = bin.dup
25
+ dbin[0] |= i << 3
26
+ addop name + cc, dbin, *args
27
+ }
28
+ end
29
+
30
+ # data from http://www.z80.info/decoding.htm
31
+ def init_z80_common
32
+ @opcode_list = []
33
+ @valid_args.update [:i8, :u8, :i16, :u16, :m16,
34
+ :r_a, :r_af, :r_hl, :r_de, :r_sp, :r_i,
35
+ :m_bc, :m_de, :m_sp, :m_hl, :mf8, :mfc
36
+ ].inject({}) { |h, v| h.update v => true }
37
+ @fields_mask.update :rz => 7, :ry => 7, :rp => 3, :rp2 => 3, :iy => 7, :iy8 => 7
38
+ @fields_shift.update :rz => 0, :ry => 3, :rp => 4, :rp2 => 4, :iy => 3, :iy8 => 3
39
+
40
+ # some opcodes are in init_z80 when they are not part of the GB ABI
41
+ addop 'nop', [0b00_000_000]
42
+ addop 'jr', [0b00_011_000], :setip, :stopexec, :i8
43
+ %w[nz z nc c].each_with_index { |cc, i|
44
+ addop 'jr' + cc, [0b00_100_000 | (i << 3)], :setip, :i8
45
+ }
46
+ addop 'ld', [0b00_000_001], :rp, :i16
47
+ addop 'add', [0b00_001_001], :r_hl, :rp
48
+
49
+ addop 'ld', [0b00_000_010], :m_bc, :r_a
50
+ addop 'ld', [0b00_001_010], :r_a, :m_bc
51
+ addop 'ld', [0b00_010_010], :m_de, :r_a
52
+ addop 'ld', [0b00_011_010], :r_a, :m_de
53
+
54
+ addop 'inc', [0b00_000_011], :rp
55
+ addop 'dec', [0b00_001_011], :rp
56
+ addop 'inc', [0b00_000_100], :ry
57
+ addop 'dec', [0b00_000_101], :ry
58
+ addop 'ld', [0b00_000_110], :ry, :i8
59
+
60
+ addop 'rlca', [0b00_000_111] # rotate
61
+ addop 'rrca', [0b00_001_111]
62
+ addop 'rla', [0b00_010_111]
63
+ addop 'rra', [0b00_011_111]
64
+
65
+ addop 'daa', [0b00_100_111]
66
+ addop 'cpl', [0b00_101_111]
67
+ addop 'scf', [0b00_110_111]
68
+ addop 'ccf', [0b00_111_111]
69
+
70
+ addop 'halt', [0b01_110_110] # ld (HL), (HL)
71
+ addop 'ld', [0b01_000_000], :ry, :rz
72
+
73
+ addop 'add', [0b10_000_000], :r_a, :rz
74
+ addop 'adc', [0b10_001_000], :r_a, :rz
75
+ addop 'sub', [0b10_010_000], :r_a, :rz
76
+ addop 'sbc', [0b10_011_000], :r_a, :rz
77
+ addop 'and', [0b10_100_000], :r_a, :rz
78
+ addop 'xor', [0b10_101_000], :r_a, :rz
79
+ addop 'or', [0b10_110_000], :r_a, :rz
80
+ addop 'cmp', [0b10_111_000], :r_a, :rz # alias cp
81
+ addop 'cp', [0b10_111_000], :r_a, :rz # compare
82
+
83
+ addop_macrocc 'ret', [0b11_000_000], :setip
84
+ addop 'pop', [0b11_000_001], :rp2
85
+ addop 'ret', [0b11_001_001], :stopexec, :setip
86
+ addop 'jmp', [0b11_101_001], :r_hl, :setip, :stopexec # alias jp
87
+ addop 'jp', [0b11_101_001], :r_hl, :setip, :stopexec
88
+ addop 'ld', [0b11_111_001], :r_sp, :r_hl
89
+ addop_macrocc 'j', [0b11_000_010], :setip, :u16 # alias jp
90
+ addop_macrocc 'jp', [0b11_000_010], :setip, :u16
91
+ addop 'jmp', [0b11_000_011], :setip, :stopexec, :u16 # alias jp
92
+ addop 'jp', [0b11_000_011], :setip, :stopexec, :u16
93
+
94
+ addop 'di', [0b11_110_011] # disable interrupts
95
+ addop 'ei', [0b11_111_011]
96
+ addop_macrocc 'call', [0b11_000_100], :u16, :setip, :saveip
97
+ addop 'push', [0b11_000_101], :rp2
98
+ addop 'call', [0b11_001_101], :u16, :setip, :saveip, :stopexec
99
+
100
+ addop 'add', [0b11_000_110], :r_a, :i8
101
+ addop 'adc', [0b11_001_110], :r_a, :i8
102
+ addop 'sub', [0b11_010_110], :r_a, :i8
103
+ addop 'sbc', [0b11_011_110], :r_a, :i8
104
+ addop 'and', [0b11_100_110], :r_a, :i8
105
+ addop 'xor', [0b11_101_110], :r_a, :i8
106
+ addop 'or', [0b11_110_110], :r_a, :i8
107
+ addop 'cp', [0b11_111_110], :r_a, :i8
108
+
109
+ addop 'rst', [0b11_000_111], :iy8 # call off in page 0
110
+
111
+ addop 'rlc', [0xCB, 0b00_000_000], :rz # rotate
112
+ addop 'rrc', [0xCB, 0b00_001_000], :rz
113
+ addop 'rl', [0xCB, 0b00_010_000], :rz
114
+ addop 'rr', [0xCB, 0b00_011_000], :rz
115
+ addop 'sla', [0xCB, 0b00_100_000], :rz # shift
116
+ addop 'sra', [0xCB, 0b00_101_000], :rz
117
+ addop 'srl', [0xCB, 0b00_111_000], :rz
118
+ addop 'bit', [0xCB, 0b01_000_000], :iy, :rz # bit test
119
+ addop 'res', [0xCB, 0b10_000_000], :iy, :rz # bit reset
120
+ addop 'set', [0xCB, 0b11_000_000], :iy, :rz # bit set
121
+ end
122
+
123
+ # standard z80
124
+ def init_z80
125
+ init_z80_common
126
+
127
+ addop 'ex', [0b00_001_000], :r_af # XXX really ex AF, AF' ...
128
+ addop 'djnz', [0b00_010_000], :setip, :i8
129
+
130
+ addop 'ld', [0b00_100_010], :m16, :r_hl
131
+ addop 'ld', [0b00_101_010], :r_hl, :m16
132
+ addop 'ld', [0b00_110_010], :m16, :r_a
133
+ addop 'ld', [0b00_111_010], :r_a, :m16
134
+
135
+ addop 'exx', [0b11_011_001]
136
+ addop 'out', [0b11_010_011], :i8, :r_a
137
+ addop 'in', [0b11_011_011], :r_a, :i8
138
+
139
+ addop 'ex', [0b11_100_011], :m_sp, :r_hl
140
+ addop 'ex', [0b11_101_011], :r_de, :r_hl
141
+
142
+ addop 'sll', [0xCB, 0b00_110_000], :rz
143
+
144
+ addop 'in', [0xED, 0b01_110_000], :u16
145
+ addop 'in', [0xED, 0b01_000_000], :ry, :u16
146
+ addop 'out', [0xED, 0b01_110_001], :u16
147
+ addop 'out', [0xED, 0b01_000_001], :u16, :ry
148
+ addop 'sbc', [0xED, 0b01_000_010], :r_hl, :rp
149
+ addop 'adc', [0xED, 0b01_001_010], :r_hl, :rp
150
+ addop 'ld', [0xED, 0b01_000_011], :m16, :rp
151
+ addop 'ld', [0xED, 0b01_001_011], :rp, :m16
152
+ addop 'neg', [0xED, 0b01_000_100], :r_a, :iy # dummy int field
153
+ addop 'retn', [0xED, 0b01_000_101], :stopexec # dummy int != 1 ? (1 = reti)
154
+ addop 'reti', [0xED, 0b01_001_101], :stopexec, :setip
155
+ addop 'im', [0xED, 0b01_000_110], :iy
156
+ addop 'ld', [0xED, 0b01_000_111], :r_i, :r_a
157
+ addop 'ld', [0xED, 0b01_001_111], :r_r, :r_a
158
+ addop 'ld', [0xED, 0b01_010_111], :r_a, :r_i
159
+ addop 'ld', [0xED, 0b01_011_111], :r_a, :r_r
160
+ addop 'rrd', [0xED, 0b01_100_111]
161
+ addop 'rld', [0xED, 0b01_101_111]
162
+
163
+ addop 'ldi', [0xED, 0b10_100_000]
164
+ addop 'ldd', [0xED, 0b10_101_000]
165
+ addop 'ldir', [0xED, 0b10_110_000]
166
+ addop 'lddr', [0xED, 0b10_111_000]
167
+ addop 'cpi', [0xED, 0b10_100_001]
168
+ addop 'cpd', [0xED, 0b10_101_001]
169
+ addop 'cpir', [0xED, 0b10_110_001]
170
+ addop 'cpdr', [0xED, 0b10_111_001]
171
+ addop 'ini', [0xED, 0b10_100_010]
172
+ addop 'ind', [0xED, 0b10_101_010]
173
+ addop 'inir', [0xED, 0b10_110_010]
174
+ addop 'indr', [0xED, 0b10_111_010]
175
+ addop 'outi', [0xED, 0b10_100_011]
176
+ addop 'outd', [0xED, 0b10_101_011]
177
+ addop 'otir', [0xED, 0b10_110_011]
178
+ addop 'otdr', [0xED, 0b10_111_011]
179
+
180
+ addop 'unk_ed', [0xED], :i8
181
+
182
+ addop 'unk_nop', [], :i8 # undefined opcode = nop
183
+ @unknown_opcode = @opcode_list.last
184
+ end
185
+
186
+ # gameboy processor
187
+ # from http://nocash.emubase.de/pandocs.htm#cpucomparisionwithz80
188
+ def init_gb
189
+ init_z80_common
190
+
191
+ addop 'ld', [0x08], :m16, :r_sp
192
+ addop 'stop', [0x10]
193
+
194
+ addop 'ldi', [0x22], :m_hl, :r_a # (hl++) <- a
195
+ addop 'ldi', [0x2A], :r_a, :m_hl
196
+ addop 'ldd', [0x32], :m_hl, :r_a # (hl--) <- a
197
+ addop 'ldd', [0x3A], :r_a, :m_hl
198
+
199
+ addop 'reti', [0xD9], :setip, :stopexec
200
+
201
+ # override retpo/jpo
202
+ @opcode_list.delete_if { |op| op.bin[0] & 0xE5 == 0xE0 } # rm E0 E2 E8 EA F0 F2 F8 FA
203
+ addop 'ld', [0xE0], :mf8, :r_a # (0xff00 + :i8)
204
+ addop 'ld', [0xE2], :mfc, :r_a # (0xff00 + :r_c)
205
+ addop 'add', [0xE8], :r_sp, :i8
206
+ addop 'ld', [0xEA], :m16, :r_a
207
+ addop 'ld', [0xF0], :r_a, :mf8
208
+ addop 'ld', [0xF2], :r_a, :mfc
209
+ addop 'ld', [0xF8], :r_hl, :r_sp, :i8 # hl <- sp+:i8
210
+ addop 'ld', [0xFA], :r_a, :m16
211
+
212
+ addop 'swap', [0xCB, 0x30], :rz
213
+
214
+ addop 'inv_dd', [0xDD], :stopexec # invalid prefixes
215
+ addop 'inv_ed', [0xED], :stopexec
216
+ addop 'inv_fd', [0xFD], :stopexec
217
+
218
+ addop 'unk_nop', [], :i8 # undefined opcode = nop
219
+ @unknown_opcode = @opcode_list.last
220
+ end
221
+
222
+ alias init_latest init_z80
223
+ end
224
+ end
@@ -0,0 +1,59 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'metasm/cpu/z80/opcodes'
8
+ require 'metasm/render'
9
+
10
+ module Metasm
11
+ class Z80
12
+ class Reg
13
+ include Renderable
14
+ def render ; [self.class.i_to_s[@sz][@i]] end
15
+ end
16
+ class Memref
17
+ include Renderable
18
+ def render
19
+ r = ['(']
20
+ r << @base if @base
21
+ r << '+' if @base and @offset
22
+ r << @offset if @offset
23
+ r << ')'
24
+ end
25
+ end
26
+
27
+ def render_instruction(i)
28
+ r = []
29
+ r << i.opname
30
+ if not i.args.empty?
31
+ r << ' '
32
+ i.args.each { |a_| r << a_ << ', ' }
33
+ r.pop
34
+ end
35
+ r
36
+ end
37
+
38
+ def gui_hilight_word_regexp_init
39
+ ret = {}
40
+
41
+ # { 'B' => 'B|BC', 'BC' => 'B|C|BC' }
42
+
43
+ %w[BC DE HL].each { |w|
44
+ l0, l1 = w.split(//)
45
+ ret[l0] = "#{l0}#{l1}?"
46
+ ret[l1] = "#{l0}?#{l1}"
47
+ ret[w] = "#{l0}|#{l0}?#{l1}"
48
+ }
49
+
50
+ ret
51
+ end
52
+
53
+ def gui_hilight_word_regexp(word)
54
+ @gui_hilight_word_hash ||= gui_hilight_word_regexp_init
55
+ @gui_hilight_word_hash[word] or super(word)
56
+ end
57
+
58
+ end
59
+ end
@@ -3,312 +3,7 @@
3
3
  #
4
4
  # Licence is LGPL, see LICENCE in the top-level directory
5
5
 
6
- require 'metasm/main'
7
-
8
6
  module Metasm
9
- # this module regroups OS-related functions
10
- # (eg. find_process, inject_shellcode)
11
- # a 'class' just to be able to inherit from it...
12
- class OS
13
- # represents a running process with a few information, and defines methods to get more interaction (#memory, #debugger)
14
- class Process
15
- attr_accessor :pid, :path, :modules
16
- class Module
17
- attr_accessor :path, :addr, :size
18
- end
19
-
20
- def initialize(pid=nil)
21
- @pid = pid
22
- end
23
-
24
- def to_s
25
- mod = File.basename(path) rescue nil
26
- "#{pid}: ".ljust(6) << (mod || '<unknown>')
27
- end
28
- def inspect
29
- '<Process:' + ["pid: #@pid", modules.to_a.map { |m| " #{'%X' % m.addr} #{m.path}" }].join("\n") + '>'
30
- end
31
- end
32
-
33
- # returns the Process whose pid is name (if name is an Integer) or first module path includes name (string)
34
- def self.find_process(name)
35
- case name
36
- when nil
37
- when Integer
38
- list_processes.find { |pr| pr.pid == name }
39
- else
40
- list_processes.find { |pr| pr.path.to_s.include? name.to_s } or
41
- (find_process(Integer(name)) if name =~ /^(0x[0-9a-f]+|[0-9]+)$/i)
42
- end
43
- end
44
-
45
- # create a new debuggee process stopped at start
46
- def self.create_process(path)
47
- dbg = create_debugger(path)
48
- pr = open_process(dbg.pid)
49
- pr.debugger = dbg
50
- pr.memory = dbg.memory
51
- pr
52
- end
53
-
54
- # return the platform-specific version
55
- def self.current
56
- case RUBY_PLATFORM
57
- when /mswin|mingw|cygwin/i; WinOS
58
- when /linux/i; LinOS
59
- end
60
- end
61
- end
62
-
63
- # This class implements an objects that behaves like a regular string, but
64
- # whose real data is dynamically fetched or generated on demand
65
- # its size is immutable
66
- # implements a page cache
67
- # substrings are Strings (small substring) or another VirtualString
68
- # (a kind of 'window' on the original VString, when the substring length is > 4096)
69
- class VirtualString
70
- # formats parameters for reading
71
- def [](from, len=nil)
72
- if not len and from.kind_of? Range
73
- b = from.begin
74
- e = from.end
75
- b = b + length if b < 0
76
- e = e + length if e < 0
77
- len = e - b
78
- len += 1 if not from.exclude_end?
79
- from = b
80
- end
81
- from = from + length if from < 0
82
-
83
- return nil if from > length or (from == length and not len)
84
- len = length - from if len and from + len > length
85
- return '' if len == 0
86
-
87
- read_range(from, len)
88
- end
89
-
90
- # formats parameters for overwriting portion of the string
91
- def []=(from, len, val=nil)
92
- raise TypeError, 'cannot modify frozen virtualstring' if frozen?
93
-
94
- if not val
95
- val = len
96
- len = nil
97
- end
98
- if not len and from.kind_of? Range
99
- b = from.begin
100
- e = from.end
101
- b = b + length if b < 0
102
- e = e + length if e < 0
103
- len = e - b
104
- len += 1 if not from.exclude_end?
105
- from = b
106
- elsif not len
107
- len = 1
108
- val = val.chr
109
- end
110
- from = from + length if from < 0
111
-
112
- raise IndexError, 'Index out of string' if from > length
113
- raise IndexError, 'Cannot modify virtualstring length' if val.length != len or from + len > length
114
-
115
- write_range(from, val)
116
- end
117
-
118
- # returns the full raw data
119
- def realstring
120
- ret = ''
121
- addr = 0
122
- len = length
123
- while len > @pagelength
124
- ret << self[addr, @pagelength]
125
- addr += @pagelength
126
- len -= @pagelength
127
- end
128
- ret << self[addr, len]
129
- end
130
-
131
- # alias to realstring
132
- # for bad people checking respond_to? :to_str (like String#<<)
133
- # XXX alias does not work (not virtual (a la C++))
134
- def to_str
135
- realstring
136
- end
137
-
138
- # forwards unhandled messages to a frozen realstring
139
- def method_missing(m, *args, &b)
140
- if ''.respond_to? m
141
- puts "Using VirtualString.realstring for #{m} from:", caller if $DEBUG
142
- realstring.freeze.send(m, *args, &b)
143
- else
144
- super(m, *args, &b)
145
- end
146
- end
147
-
148
- # avoid triggering realstring from method_missing if possible
149
- def empty?
150
- length == 0
151
- end
152
-
153
- # avoid triggering realstring from method_missing if possible
154
- # heavily used in to find 0-terminated strings in ExeFormats
155
- def index(chr, base=0)
156
- return if base >= length or base <= -length
157
- if i = self[base, 64].index(chr) or i = self[base, @pagelength].index(chr)
158
- base + i
159
- else
160
- realstring.index(chr, base)
161
- end
162
- end
163
-
164
- # '=~' does not go through method_missing
165
- def =~(o)
166
- realstring =~ o
167
- end
168
-
169
- # implements a read page cache
170
-
171
- # the real address of our first byte
172
- attr_accessor :addr_start
173
- # our length
174
- attr_accessor :length
175
- # array of [addr, raw data], sorted by first == last accessed
176
- attr_accessor :pagecache
177
- # maximum length of self.pagecache (number of cached pages)
178
- attr_accessor :pagecache_len
179
- def initialize(addr_start, length)
180
- @addr_start = addr_start
181
- @length = length
182
- @pagecache = []
183
- @pagecache_len = 4
184
- @pagelength ||= 4096 # must be (1 << x)
185
- end
186
-
187
- # returns wether a page is valid or not
188
- def page_invalid?(addr)
189
- cache_get_page(@addr_start+addr)[2]
190
- end
191
-
192
- # invalidates the page cache
193
- def invalidate
194
- @pagecache.clear
195
- end
196
-
197
- # returns the @pagelength-bytes page starting at addr
198
- # return nil if the page is invalid/inaccessible
199
- # addr is page-aligned by the caller
200
- # addr is absolute
201
- #def get_page(addr, len=@pagelength)
202
- #end
203
-
204
- # searches the cache for a page containing addr, updates if not found
205
- def cache_get_page(addr)
206
- addr &= ~(@pagelength-1)
207
- i = 0
208
- @pagecache.each { |c|
209
- if addr == c[0]
210
- # most recently used first
211
- @pagecache.unshift @pagecache.delete_at(i) if i != 0
212
- return c
213
- end
214
- i += 1
215
- }
216
- @pagecache.pop if @pagecache.length >= @pagecache_len
217
- c = [addr]
218
- p = get_page(addr)
219
- c << p.to_s.ljust(@pagelength, "\0")
220
- c << true if not p
221
- @pagecache.unshift c
222
- c
223
- end
224
-
225
- # reads a range from the page cache
226
- # returns a new VirtualString (using dup) if the request is bigger than @pagelength bytes
227
- def read_range(from, len)
228
- from += @addr_start
229
- if not len
230
- base, page = cache_get_page(from)
231
- page[from - base]
232
- elsif len <= @pagelength
233
- base, page = cache_get_page(from)
234
- s = page[from - base, len]
235
- if from+len-base > @pagelength # request crosses a page boundary
236
- base, page = cache_get_page(from+len)
237
- s << page[0, from+len-base]
238
- end
239
- s
240
- else
241
- # big request: return a new virtual page
242
- dup(from, len)
243
- end
244
- end
245
-
246
- # rewrites a segment of data
247
- # the length written is the length of the content (a VirtualString cannot grow/shrink)
248
- def write_range(from, content)
249
- invalidate
250
- rewrite_at(from + @addr_start, content)
251
- end
252
-
253
- # overwrites a section of the original data
254
- #def rewrite_at(addr, content)
255
- #end
256
- end
257
-
258
- # on-demand reading of a file
259
- class VirtualFile < VirtualString
260
- # returns a new VirtualFile of the whole file content (defaults readonly)
261
- # returns a String if the file is small (<4096o) and readonly access
262
- def self.read(path, mode='rb')
263
- raise 'no filename specified' if not path
264
- if sz = File.size(path) <= 4096 and (mode == 'rb' or mode == 'r')
265
- File.open(path, mode) { |fd| fd.read }
266
- else
267
- File.open(path, mode) { |fd| new fd, 0, sz }
268
- end
269
- end
270
-
271
- # the underlying file descriptor
272
- attr_accessor :fd
273
-
274
- # creates a new virtual mapping of a section of the file
275
- # the file descriptor must be seekable
276
- def initialize(fd, addr_start = 0, length = nil)
277
- @fd = fd.dup
278
- if not length
279
- @fd.seek(0, File::SEEK_END)
280
- length = @fd.tell - addr_start
281
- end
282
- super(addr_start, length)
283
- end
284
-
285
- def dup(addr = @addr_start, len = @length)
286
- self.class.new(@fd, addr, len)
287
- end
288
-
289
- # reads an aligned page from the file, at file offset addr
290
- def get_page(addr, len=@pagelength)
291
- @fd.pos = addr
292
- @fd.read len
293
- end
294
-
295
- def page_invalid?(addr)
296
- false
297
- end
298
-
299
- # overwrite a section of the file
300
- def rewrite_at(addr, data)
301
- @fd.pos = addr
302
- @fd.write data
303
- end
304
-
305
- # returns the full content of the file
306
- def realstring
307
- @fd.pos = @addr_start
308
- @fd.read(@length)
309
- end
310
- end
311
-
312
7
  # this class implements a high-level debugging API (abstract superclass)
313
8
  class Debugger
314
9
  class Breakpoint
@@ -319,7 +14,7 @@ class Debugger
319
14
  :oneshot,
320
15
  # current bp state: :active, :inactive (internal use), :disabled (user-specified)
321
16
  :state,
322
- # type: type of breakpoint (:bpx = soft, :hw = hard)
17
+ # type: type of breakpoint (:bpx = soft, :hwbp = hard, :bpm = memory)
323
18
  :type,
324
19
  # Expression if this is a conditionnal bp
325
20
  # may be a Proc, String or Expression, evaluated every time the breakpoint hits
@@ -329,6 +24,7 @@ class Debugger
329
24
  :action,
330
25
  # Proc to run to emulate the overwritten instr behavior
331
26
  # used to avoid unset/singlestep/re-set, more multithread friendly
27
+ # may be a DecodedInstruction for lazy initialization, see Debugger#init_bpx/has_emul_instr(bpx)
332
28
  :emul_instr,
333
29
  # internal data, cpu-specific (overwritten byte for a softbp, memory type/size for hwbp..)
334
30
  :internal,
@@ -383,7 +79,7 @@ class Debugger
383
79
  return del_bpm if @type == :bpm
384
80
  @hash_shared.delete self
385
81
  if @hash_shared.empty?
386
- @hash_owner.delete @hash_key
82
+ @hash_owner.delete @hash_key
387
83
  elsif @hash_owner[@hash_key] == self
388
84
  @hash_owner[@hash_key] = @hash_shared.first
389
85
  end
@@ -432,7 +128,7 @@ class Debugger
432
128
 
433
129
  # global debugger callbacks, called whenever such event occurs
434
130
  attr_accessor :callback_singlestep, :callback_bpx, :callback_hwbp, :callback_bpm,
435
- :callback_exception, :callback_newthread, :callback_endthread,
131
+ :callback_exception, :callback_newthread, :callback_endthread,
436
132
  :callback_newprocess, :callback_endprocess, :callback_loadlibrary
437
133
 
438
134
  # global switches, specify wether to break on exception/thread event
@@ -456,15 +152,17 @@ class Debugger
456
152
  @pid_stuff_list = [:memory, :cpu, :disassembler, :symbols, :symbols_len,
457
153
  :modulemap, :breakpoint, :breakpoint_memory, :tid, :tid_stuff,
458
154
  :dead_process]
459
- @tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
155
+ @tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
460
156
  :run_method, :run_args, :breakpoint_cause, :dead_thread]
461
157
  @callback_loadlibrary = lambda { |h| loadsyms(h[:address]) ; continue }
462
- @callback_newprocess = lambda { |h| log "process #{@pid} created" }
158
+ @callback_newprocess = lambda { |h| log "process #{@pid} attached" }
463
159
  @callback_endprocess = lambda { |h| log "process #{@pid} died" }
464
160
  initialize_newpid
465
161
  initialize_newtid
466
162
  end
467
163
 
164
+ def dasm; disassembler; end
165
+
468
166
  def shortname; self.class.name.split('::').last.downcase; end
469
167
 
470
168
  attr_reader :pid
@@ -582,21 +280,27 @@ class Debugger
582
280
  end
583
281
 
584
282
  # delete references to the current thread
585
- # calls del_pid if no tid left
586
283
  def del_tid
587
284
  @tid_stuff.delete @tid
588
285
  if @tid = @tid_stuff.keys.first
589
286
  swapin_tid
590
287
  else
591
- del_pid
288
+ del_tid_notid
592
289
  end
593
290
  end
594
291
 
292
+ # wipe the whole process when no TID is left
293
+ # XXX we may have a pending evt_newthread...
294
+ def del_tid_notid
295
+ del_pid
296
+ end
297
+
298
+
595
299
  # change the debugger to a specific pid/tid
596
300
  # if given a block, run the block and then restore the original pid/tid
597
301
  # pid may be an object that respond to #pid/#tid
598
- def switch_context(npid, ntid=nil)
599
- if npid.respond_to? :pid
302
+ def switch_context(npid, ntid=nil, &b)
303
+ if npid.respond_to?(:pid)
600
304
  ntid ||= npid.tid
601
305
  npid = npid.pid
602
306
  end
@@ -604,12 +308,12 @@ class Debugger
604
308
  oldtid = tid
605
309
  set_pid npid
606
310
  set_tid ntid if ntid
607
- if block_given?
311
+ if b
608
312
  # shortcut begin..ensure overhead
609
- return yield if oldpid == pid and oldtid == tid
313
+ return b.call if oldpid == pid and oldtid == tid
610
314
 
611
315
  begin
612
- yield
316
+ b.call
613
317
  ensure
614
318
  set_pid oldpid
615
319
  set_tid oldtid
@@ -619,31 +323,31 @@ class Debugger
619
323
  alias set_context switch_context
620
324
 
621
325
  # iterate over all pids, yield in the context of this pid
622
- def each_pid
326
+ def each_pid(&b)
623
327
  # ensure @pid is last, so that we finish in the current context
624
328
  lst = @pid_stuff.keys - [@pid]
625
329
  lst << @pid
626
- return lst if not block_given?
330
+ return lst if not b
627
331
  lst.each { |p|
628
332
  set_pid p
629
- yield
333
+ b.call
630
334
  }
631
335
  end
632
336
 
633
337
  # iterate over all tids of the current process, yield in its context
634
- def each_tid
338
+ def each_tid(&b)
635
339
  lst = @tid_stuff.keys - [@tid]
636
340
  lst << @tid
637
- return lst if not block_given?
341
+ return lst if not b
638
342
  lst.each { |t|
639
- set_tid t
640
- yield
343
+ set_tid t rescue next
344
+ b.call
641
345
  }
642
346
  end
643
347
 
644
348
  # iterate over all tids of all pids, yield in their context
645
- def each_pid_tid
646
- each_pid { each_tid { yield } }
349
+ def each_pid_tid(&b)
350
+ each_pid { each_tid { b.call } }
647
351
  end
648
352
 
649
353
 
@@ -655,7 +359,8 @@ class Debugger
655
359
  # returns the Breakpoint object
656
360
  def add_bp(addr, info={})
657
361
  info[:pid] ||= @pid
658
- info[:tid] ||= @tid if info[:pid] == @pid
362
+ # dont define :tid for bpx, otherwise on del_bp we may switch_context to this thread that may not be stopped -> cant ptrace_write
363
+ info[:tid] ||= @tid if info[:pid] == @pid and info[:type] == :hwbp
659
364
 
660
365
  b = Breakpoint.new
661
366
  info.each { |k, v|
@@ -740,45 +445,69 @@ class Debugger
740
445
  end
741
446
 
742
447
  # called in the context of the target when a bpx is to be initialized
743
- # will disassemble the code pointed, and try to initialize #emul_instr
448
+ # may (lazily) initialize b.emul_instr for virtual singlestep
744
449
  def init_bpx(b)
745
- @disassembler.disassemble_fast_block(b.address) # XXX configurable dasm method
746
- if di = @disassembler.di_at(b.address) and
747
- fdbd = @disassembler.get_fwdemu_binding(di, register_pc) and
748
- not fdbd[:incomplete_binding] and not fdbd.index(Expression::Unknown) and
749
- fdbd.keys.all? { |k| k.kind_of?(Symbol) or k.kind_of?(Indirection) }
750
-
751
- puts di.instruction, fdbd.inspect
752
- b.emul_instr = lambda { |dbg|
753
- resv = lambda { |e|
754
- r = e
755
- flags = Expression[r].externals.uniq.find_all { |f| f.to_s =~ /flags?_(.+)/ }
756
- if flags.first
757
- bd = {}
758
- flags.each { |f|
759
- f.to_s =~ /flags?_(.+)/
760
- bd[f] = dbg.get_flag_value($1.downcase.to_sym)
761
- }
762
- r = r.bind(bd)
763
- end
764
- dbg.resolve(r)
450
+ # dont bother setting stuff up if it is never to be used
451
+ return if b.oneshot and not b.condition
452
+
453
+ # lazy setup of b.emul_instr: delay building emulating lambda to if/when actually needed
454
+ # we still need to disassemble now and update @disassembler, before we patch the memory for the bpx
455
+ di = init_bpx_disassemble(b.address)
456
+ b.hash_shared.each { |bb| bb.emul_instr = di }
457
+ end
458
+
459
+ # retrieve the di at a given address, disassemble if needed
460
+ # TODO make it so this doesn't interfere with other 'real' disassembler later commands, eg disassemble() or disassemble_fast_deep()
461
+ # (right now, when they see the block already present they stop all processing)
462
+ def init_bpx_disassemble(addr)
463
+ @disassembler.disassemble_fast_block(addr)
464
+ @disassembler.di_at(addr)
465
+ end
466
+
467
+ # checks if bp has an emul_instr
468
+ # do the lazy initialization if needed
469
+ def has_emul_instr(bp)
470
+ if bp.emul_instr.kind_of?(DecodedInstruction)
471
+ if di = bp.emul_instr and fdbd = @disassembler.get_fwdemu_binding(di, register_pc) and
472
+ fdbd.all? { |k, v| (k.kind_of?(Symbol) or k.kind_of?(Indirection)) and
473
+ k != :incomplete_binding and v != Expression::Unknown }
474
+ # setup a lambda that will mimic, using the debugger primitives, the actual execution of the instruction
475
+ bp.emul_instr = lambda {
476
+ fdbd.map { |k, v|
477
+ k = Indirection[emulinstr_resv(k.pointer), k.len] if k.kind_of?(Indirection)
478
+ [k, emulinstr_resv(v)]
479
+ }.each { |k, v|
480
+ if k.to_s =~ /flags?_(.+)/i
481
+ f = $1.downcase.to_sym
482
+ set_flag_value(f, v)
483
+ elsif k.kind_of?(Symbol)
484
+ set_reg_value(k, v)
485
+ elsif k.kind_of?(Indirection)
486
+ memory_write_int(k.pointer, v, k.len)
487
+ end
488
+ }
765
489
  }
490
+ bp.hash_shared.each { |bb| bb.emul_instr = bp.emul_instr }
491
+ else
492
+ bp.hash_shared.each { |bb| bb.emul_instr = nil }
493
+ end
494
+ end
766
495
 
767
- fdbd.map { |k, v|
768
- k = Indirection[resv[k.pointer], k.len] if k.kind_of?(Indirection)
769
- [k, resv[v]]
770
- }.each { |k, v|
771
- if k.to_s =~ /flags?_(.+)/
772
- dbg.set_flag_value($1.downcase.to_sym, v)
773
- elsif k.kind_of?(Symbol)
774
- dbg.set_reg_value(k, v)
775
- elsif k.kind_of?(Indirection)
776
- dbg.memory_write_int(k.pointer, v, k.len)
777
- end
778
- }
496
+ bp.emul_instr
497
+ end
498
+
499
+ def emulinstr_resv(e)
500
+ r = e
501
+ flags = Expression[r].externals.uniq.find_all { |f| f.to_s =~ /flags?_(.+)/i }
502
+ if flags.first
503
+ bd = {}
504
+ flags.each { |f|
505
+ f.to_s =~ /flags?_(.+)/i
506
+ bd[f] = get_flag_value($1.downcase.to_sym)
779
507
  }
780
- b.hash_shared.each { |bb| bb.emul_instr = b.emul_instr }
508
+ r = r.bind(bd)
781
509
  end
510
+ resolve(r)
782
511
  end
783
512
 
784
513
  # sets a breakpoint on execution
@@ -798,6 +527,7 @@ puts di.instruction, fdbd.inspect
798
527
  h = { :type => :hwbp }
799
528
  h[:hash_owner] = @breakpoint_thread
800
529
  addr = resolve_expr(addr) if not addr.kind_of? ::Integer
530
+ mtype = mtype.to_sym
801
531
  h[:hash_key] = [addr, mtype, mlen]
802
532
  h[:internal] = { :type => mtype, :len => mlen }
803
533
  h[:oneshot] = true if oneshot
@@ -813,7 +543,7 @@ puts di.instruction, fdbd.inspect
813
543
  h = { :type => :bpm }
814
544
  addr = resolve_expr(addr) if not addr.kind_of? ::Integer
815
545
  h[:hash_key] = addr & -4096 # XXX actually referenced at addr, addr+4096, ... addr+len
816
- h[:internal] = { :type => type, :len => mlen }
546
+ h[:internal] = { :type => mtype, :len => mlen }
817
547
  h[:oneshot] = true if oneshot
818
548
  h[:condition] = cond if cond
819
549
  h[:action] = action if action
@@ -821,7 +551,7 @@ puts di.instruction, fdbd.inspect
821
551
  end
822
552
 
823
553
 
824
- # define the lambda to use to log stuff (used by #puts)
554
+ # define the lambda to use to log stuff
825
555
  def set_log_proc(l=nil, &b)
826
556
  @log_proc = l || b
827
557
  end
@@ -831,7 +561,7 @@ puts di.instruction, fdbd.inspect
831
561
  if @log_proc
832
562
  a.each { |aa| @log_proc[aa] }
833
563
  else
834
- puts(*a)
564
+ puts(*a) if $VERBOSE
835
565
  end
836
566
  end
837
567
 
@@ -843,7 +573,7 @@ puts di.instruction, fdbd.inspect
843
573
 
844
574
  # invalidates the EncodedData backend for the dasm sections
845
575
  def dasm_invalidate
846
- disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to? :invalidate }
576
+ disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to?(:invalidate) } if disassembler
847
577
  end
848
578
 
849
579
  # return all breakpoints set on a specific address (or all bp)
@@ -862,7 +592,7 @@ puts di.instruction, fdbd.inspect
862
592
  ret |= bb.hash_shared
863
593
  }
864
594
 
865
- @breakpoint_memory.each_value { |m|
595
+ @breakpoint_memory.each_value { |bb|
866
596
  next if addr and (bb.address+bb.internal[:len] <= addr or bb.address > addr)
867
597
  ret |= bb.hash_shared
868
598
  }
@@ -870,9 +600,10 @@ puts di.instruction, fdbd.inspect
870
600
  ret
871
601
  end
872
602
 
873
- def find_breakpoint(addr=nil)
874
- return @breakpoint[addr] if @breakpoint[addr] and (not block_given? or yield(@breakpoint[addr]))
875
- all_breakpoints(addr).find { |b| yield b }
603
+ # return on of the breakpoints at address addr
604
+ def find_breakpoint(addr=nil, &b)
605
+ return @breakpoint[addr] if @breakpoint[addr] and (not b or b.call(@breakpoint[addr]))
606
+ all_breakpoints(addr).find { |bp| b.call bp }
876
607
  end
877
608
 
878
609
 
@@ -894,7 +625,6 @@ puts di.instruction, fdbd.inspect
894
625
  @breakpoint_cause = nil
895
626
  @run_method = run_m
896
627
  @run_args = run_a
897
- @state = :running
898
628
  @info = nil
899
629
  true
900
630
  end
@@ -922,10 +652,12 @@ puts di.instruction, fdbd.inspect
922
652
  return @cpu.dbg_find_singlestep(self) if @cpu.respond_to?(:dbg_find_singlestep)
923
653
  @run_method == :singlestep
924
654
  end
925
-
655
+
926
656
  # called when the target stops due to a soft breakpoint exception
927
657
  def evt_bpx(b=nil)
928
658
  b ||= find_bp_bpx
659
+ # TODO handle race:
660
+ # bpx foo ; thread hits foo ; we bc foo ; os notify us of bp hit but we already cleared everything related to 'bpx foo' -> unhandled bp exception
929
661
  return evt_exception(:type => 'breakpoint') if not b
930
662
 
931
663
  @state = :stopped
@@ -959,7 +691,7 @@ puts di.instruction, fdbd.inspect
959
691
 
960
692
  # return the breakpoint that is responsible for the evt_hwbp
961
693
  def find_bp_hwbp
962
- return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:dbg_find_bpx)
694
+ return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:dbg_find_hwbp)
963
695
  @breakpoint_thread.find { |b| b.address == pc }
964
696
  end
965
697
 
@@ -995,9 +727,9 @@ puts di.instruction, fdbd.inspect
995
727
  return if b.address+b.internal[:len] <= info[:fault_addr]
996
728
  return if b.address >= info[:fault_addr] + info[:fault_len]
997
729
  case b.internal[:type]
998
- when :x; info[:fault_addr] == pc # XXX
999
- when :r; info[:fault_access] == :r
730
+ when :r; info[:fault_access] == :r # or info[:fault_access] == :x
1000
731
  when :w; info[:fault_access] == :w
732
+ when :x; info[:fault_access] == :x # XXX non-NX cpu => check pc is in bpm range ?
1001
733
  when :rw; true
1002
734
  end
1003
735
  end
@@ -1008,8 +740,10 @@ puts di.instruction, fdbd.inspect
1008
740
 
1009
741
  found_valid_active = false
1010
742
 
743
+ pre_callback_pc = pc
744
+
1011
745
  # XXX may have many active bps with callback that continue/singlestep/singlestep{}...
1012
- b.hash_shared.dup.map { |bb|
746
+ b.hash_shared.dup.find_all { |bb|
1013
747
  # ignore inactive bps
1014
748
  next if bb.state != :active
1015
749
 
@@ -1030,9 +764,11 @@ puts di.instruction, fdbd.inspect
1030
764
  # oneshot
1031
765
  del_bp(bb) if bb.oneshot
1032
766
 
1033
- # callback
1034
767
  bb.action
1035
- }.compact.each { |cb| cb.call }
768
+ }.each { |bb| bb.action.call }
769
+
770
+ # discard @breakpoint_cause if a bp callback did modify register_pc
771
+ @breakpoint_cause = nil if pc != pre_callback_pc
1036
772
 
1037
773
  # we did break due to a bp whose condition is not true: resume
1038
774
  # (unless a callback already resumed)
@@ -1118,13 +854,13 @@ puts di.instruction, fdbd.inspect
1118
854
  # resume execution as if we never stopped
1119
855
  # disable offending bp + singlestep if needed
1120
856
  def resume_badbreak(b=nil)
1121
- # ensure we didn't delete b
857
+ # ensure we didn't delete b
1122
858
  if b and b.hash_shared.find { |bb| bb.state == :active }
1123
859
  rm = @run_method
1124
860
  if rm == :singlestep
1125
861
  singlestep_bp(b)
1126
862
  else
1127
- @run_args = ra
863
+ ra = @run_args
1128
864
  singlestep_bp(b) { send rm, *ra }
1129
865
  end
1130
866
  else
@@ -1136,10 +872,10 @@ puts di.instruction, fdbd.inspect
1136
872
  # if the breakpoint provides an emulation stub, run that, otherwise
1137
873
  # disable the breakpoint, singlestep, and re-enable
1138
874
  def singlestep_bp(bp, &b)
1139
- if be = bp.hash_shared.find { |bb| bb.emul_instr }
875
+ if has_emul_instr(bp)
1140
876
  @state = :stopped
1141
- be.emul_instr[self]
1142
- yield if block_given?
877
+ bp.emul_instr.call
878
+ b.call if b
1143
879
  else
1144
880
  bp.hash_shared.each { |bb|
1145
881
  disable_bp(bb, :temp_inactive) if bb.state == :active
@@ -1151,13 +887,23 @@ puts di.instruction, fdbd.inspect
1151
887
  enable_bp(bb) if bb.state == :temp_inactive
1152
888
  }
1153
889
  prev_sscb[] if prev_sscb
1154
- yield if block_given?
890
+ b.call if b
1155
891
  }
1156
892
  end
1157
893
  end
1158
894
 
895
+ # checks if @breakpoint_cause is valid, or was obsoleted by the user changing pc
896
+ def check_breakpoint_cause
897
+ if bp = @breakpoint_cause and
898
+ (bp.type == :bpx or (bp.type == :hwbp and bp.internal[:type] == :x)) and
899
+ pc != bp.address
900
+ bp = @breakpoint_cause = nil
901
+ end
902
+ bp
903
+ end
1159
904
 
1160
905
  # checks if the running target has stopped (nonblocking)
906
+ # returns false if no debug event happened
1161
907
  def check_target
1162
908
  do_check_target
1163
909
  end
@@ -1171,7 +917,7 @@ puts di.instruction, fdbd.inspect
1171
917
  # bypasses a software breakpoint on pc if needed
1172
918
  # thread breakpoints must be manually disabled before calling continue
1173
919
  def continue
1174
- if b = @breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
920
+ if b = check_breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
1175
921
  singlestep_bp(b) {
1176
922
  next if not check_pre_run(:continue)
1177
923
  do_continue
@@ -1192,11 +938,11 @@ puts di.instruction, fdbd.inspect
1192
938
  # resume execution of the target one instruction at a time
1193
939
  def singlestep(&b)
1194
940
  @singlestep_cb = b
1195
- bp = @breakpoint_cause
941
+ bp = check_breakpoint_cause
1196
942
  return if not check_pre_run(:singlestep)
1197
- if bp and bp.hash_shared.find { |bb| bb.state == :active } and be = bp.hash_shared.find { |bb| bb.emul_instr }
943
+ if bp and bp.hash_shared.find { |bb| bb.state == :active } and has_emul_instr(bp)
1198
944
  @state = :stopped
1199
- be.emul_instr[self]
945
+ bp.emul_instr.call
1200
946
  invalidate
1201
947
  evt_singlestep(true)
1202
948
  else
@@ -1248,6 +994,11 @@ puts di.instruction, fdbd.inspect
1248
994
  do_singlestep
1249
995
  end
1250
996
 
997
+ def stepout_wait
998
+ stepout
999
+ wait_target
1000
+ end
1001
+
1251
1002
  # set a singleshot breakpoint, run the process, and wait
1252
1003
  def go(target, cond=nil)
1253
1004
  bpx(target, true, cond)
@@ -1432,9 +1183,9 @@ puts di.instruction, fdbd.inspect
1432
1183
  end
1433
1184
 
1434
1185
  # load symbols from all libraries found by the OS module
1435
- def loadallsyms
1186
+ def loadallsyms(&b)
1436
1187
  modules.each { |m|
1437
- yield m.addr if block_given?
1188
+ b.call(m.addr) if b
1438
1189
  loadsyms(m.addr, m.path)
1439
1190
  }
1440
1191
  end
@@ -1466,7 +1217,7 @@ puts di.instruction, fdbd.inspect
1466
1217
  end
1467
1218
 
1468
1219
  # parses the expression contained in arg, updates arg to point after the expr
1469
- def parse_expr!(arg)
1220
+ def parse_expr!(arg, &b)
1470
1221
  return if not e = IndExpression.parse_string!(arg) { |s|
1471
1222
  # handle 400000 -> 0x400000
1472
1223
  # XXX no way to override and force decimal interpretation..
@@ -1481,7 +1232,7 @@ puts di.instruction, fdbd.inspect
1481
1232
  bd = {}
1482
1233
  e.externals.grep(::String).each { |ex|
1483
1234
  if not v = register_list.find { |r| ex.downcase == r.to_s.downcase } ||
1484
- (block_given? && yield(ex)) || symbols.index(ex)
1235
+ (b && b.call(ex)) || symbols.index(ex)
1485
1236
  lst = symbols.values.find_all { |s| s.downcase.include? ex.downcase }
1486
1237
  case lst.length
1487
1238
  when 0
@@ -1516,7 +1267,7 @@ puts di.instruction, fdbd.inspect
1516
1267
  next if bd[ex]
1517
1268
  case ex
1518
1269
  when ::Symbol; bd[ex] = get_reg_value(ex)
1519
- when ::String; bd[ex] = @symbols.index(ex) || 0
1270
+ when ::String; bd[ex] = @symbols.index(ex) || @disassembler.prog_binding[ex] || 0
1520
1271
  end
1521
1272
  }
1522
1273
  Expression[e].bind(bd).reduce { |i|
@@ -1619,7 +1370,7 @@ puts di.instruction, fdbd.inspect
1619
1370
  plugin_filename = pf + '.rb'
1620
1371
  end
1621
1372
  end
1622
- if not File.exist?(plugin_filename) and File.exist?(plugin_filename + '.rb')
1373
+ if (not File.exist?(plugin_filename) or File.directory?(plugin_filename)) and File.exist?(plugin_filename + '.rb')
1623
1374
  plugin_filename += '.rb'
1624
1375
  end
1625
1376
 
@@ -1669,18 +1420,26 @@ puts di.instruction, fdbd.inspect
1669
1420
 
1670
1421
  # see EData#pattern_scan
1671
1422
  # scans only mapped areas of @memory, using os_process.mappings
1672
- def pattern_scan(pat, start=0, len=@memory.length-start)
1423
+ def pattern_scan(pat, start=0, len=@memory.length-start, &b)
1673
1424
  ret = []
1674
- mappings.each { |a, l, *o_|
1675
- a = start if a < start
1676
- l = start+len-a if a+l > start+len
1677
- next if l <= 0
1678
- EncodedData.new(@memory[a, l]).pattern_scan(pat) { |o|
1679
- o += a
1680
- ret << o if not block_given? or yield(o)
1425
+ mappings.each { |maddr, mlen, perm, *o_|
1426
+ next if perm !~ /r/i
1427
+ mlen -= start-maddr if maddr < start
1428
+ maddr = start if maddr < start
1429
+ mlen = start+len-maddr if maddr+mlen > start+len
1430
+ next if mlen <= 0
1431
+ EncodedData.new(read_mapped_range(maddr, mlen)).pattern_scan(pat) { |off|
1432
+ off += maddr
1433
+ ret << off if not b or b.call(off)
1681
1434
  }
1682
1435
  }
1683
1436
  ret
1684
1437
  end
1438
+
1439
+ def read_mapped_range(addr, len)
1440
+ # try to use a single get_page call
1441
+ s = @memory.get_page(addr, len) || ''
1442
+ s.length == len ? s : (s = @memory[addr, len] ? s.to_str : nil)
1443
+ end
1685
1444
  end
1686
1445
  end