metasm 1.0.1 → 1.0.2

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