metasm 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (276) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +3 -0
  4. data/.gitignore +3 -0
  5. data/.hgtags +3 -0
  6. data/Gemfile +3 -0
  7. data/INSTALL +61 -0
  8. data/LICENCE +458 -0
  9. data/README +29 -21
  10. data/Rakefile +10 -0
  11. data/TODO +10 -12
  12. data/doc/code_organisation.txt +3 -1
  13. data/doc/core/DynLdr.txt +247 -0
  14. data/doc/core/ExeFormat.txt +43 -0
  15. data/doc/core/Expression.txt +220 -0
  16. data/doc/core/GNUExports.txt +27 -0
  17. data/doc/core/Ia32.txt +236 -0
  18. data/doc/core/SerialStruct.txt +108 -0
  19. data/doc/core/VirtualString.txt +145 -0
  20. data/doc/core/WindowsExports.txt +61 -0
  21. data/doc/core/index.txt +1 -0
  22. data/doc/style.css +6 -3
  23. data/doc/usage/debugger.txt +327 -0
  24. data/doc/usage/index.txt +1 -0
  25. data/doc/use_cases.txt +2 -2
  26. data/metasm.gemspec +23 -0
  27. data/{lib/metasm.rb → metasm.rb} +15 -3
  28. data/{lib/metasm → metasm}/compile_c.rb +15 -9
  29. data/metasm/cpu/arc.rb +8 -0
  30. data/metasm/cpu/arc/decode.rb +404 -0
  31. data/metasm/cpu/arc/main.rb +191 -0
  32. data/metasm/cpu/arc/opcodes.rb +588 -0
  33. data/metasm/cpu/arm.rb +14 -0
  34. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  35. data/{lib/metasm → metasm/cpu}/arm/decode.rb +15 -18
  36. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  37. data/{lib/metasm → metasm/cpu}/arm/main.rb +3 -6
  38. data/metasm/cpu/arm/opcodes.rb +324 -0
  39. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  40. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  41. data/metasm/cpu/arm64.rb +15 -0
  42. data/metasm/cpu/arm64/debug.rb +38 -0
  43. data/metasm/cpu/arm64/decode.rb +285 -0
  44. data/metasm/cpu/arm64/encode.rb +41 -0
  45. data/metasm/cpu/arm64/main.rb +105 -0
  46. data/metasm/cpu/arm64/opcodes.rb +232 -0
  47. data/metasm/cpu/arm64/parse.rb +20 -0
  48. data/metasm/cpu/arm64/render.rb +95 -0
  49. data/{lib/metasm/mips/compile_c.rb → metasm/cpu/bpf.rb} +4 -2
  50. data/metasm/cpu/bpf/decode.rb +110 -0
  51. data/metasm/cpu/bpf/main.rb +60 -0
  52. data/metasm/cpu/bpf/opcodes.rb +81 -0
  53. data/metasm/cpu/bpf/render.rb +30 -0
  54. data/{lib/metasm/ppc.rb → metasm/cpu/cy16.rb} +2 -4
  55. data/metasm/cpu/cy16/decode.rb +247 -0
  56. data/metasm/cpu/cy16/main.rb +63 -0
  57. data/metasm/cpu/cy16/opcodes.rb +78 -0
  58. data/metasm/cpu/cy16/render.rb +30 -0
  59. data/metasm/cpu/dalvik.rb +11 -0
  60. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +34 -34
  61. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +71 -4
  62. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +21 -12
  63. data/{lib/metasm/mips.rb → metasm/cpu/ebpf.rb} +3 -4
  64. data/metasm/cpu/ebpf/debug.rb +61 -0
  65. data/metasm/cpu/ebpf/decode.rb +142 -0
  66. data/metasm/cpu/ebpf/main.rb +58 -0
  67. data/metasm/cpu/ebpf/opcodes.rb +97 -0
  68. data/metasm/cpu/ebpf/render.rb +36 -0
  69. data/metasm/cpu/ia32.rb +17 -0
  70. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +23 -9
  71. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +44 -6
  72. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +342 -128
  73. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +75 -53
  74. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  75. data/{lib/metasm → metasm/cpu}/ia32/main.rb +66 -8
  76. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  77. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +55 -17
  78. data/{lib/metasm → metasm/cpu}/ia32/render.rb +32 -5
  79. data/metasm/cpu/mcs51.rb +8 -0
  80. data/metasm/cpu/mcs51/decode.rb +99 -0
  81. data/metasm/cpu/mcs51/main.rb +87 -0
  82. data/metasm/cpu/mcs51/opcodes.rb +120 -0
  83. data/metasm/cpu/mips.rb +14 -0
  84. data/metasm/cpu/mips/debug.rb +42 -0
  85. data/{lib/metasm → metasm/cpu}/mips/decode.rb +59 -38
  86. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  87. data/{lib/metasm → metasm/cpu}/mips/main.rb +13 -6
  88. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +87 -18
  89. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  90. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  91. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  92. data/metasm/cpu/msp430/decode.rb +243 -0
  93. data/metasm/cpu/msp430/main.rb +62 -0
  94. data/metasm/cpu/msp430/opcodes.rb +101 -0
  95. data/metasm/cpu/openrisc.rb +11 -0
  96. data/metasm/cpu/openrisc/debug.rb +106 -0
  97. data/metasm/cpu/openrisc/decode.rb +182 -0
  98. data/metasm/cpu/openrisc/decompile.rb +350 -0
  99. data/metasm/cpu/openrisc/main.rb +70 -0
  100. data/metasm/cpu/openrisc/opcodes.rb +109 -0
  101. data/metasm/cpu/openrisc/render.rb +37 -0
  102. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  103. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  104. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  105. data/metasm/cpu/ppc.rb +11 -0
  106. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -37
  107. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  108. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  109. data/{lib/metasm → metasm/cpu}/ppc/main.rb +23 -18
  110. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -6
  111. data/metasm/cpu/ppc/parse.rb +55 -0
  112. data/metasm/cpu/python.rb +8 -0
  113. data/metasm/cpu/python/decode.rb +116 -0
  114. data/metasm/cpu/python/main.rb +36 -0
  115. data/metasm/cpu/python/opcodes.rb +180 -0
  116. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  117. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +50 -23
  118. data/{lib/metasm → metasm/cpu}/sh4/main.rb +38 -27
  119. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  120. data/metasm/cpu/st20.rb +9 -0
  121. data/metasm/cpu/st20/decode.rb +173 -0
  122. data/metasm/cpu/st20/decompile.rb +283 -0
  123. data/metasm/cpu/st20/main.rb +37 -0
  124. data/metasm/cpu/st20/opcodes.rb +140 -0
  125. data/{lib/metasm/arm.rb → metasm/cpu/webasm.rb} +4 -5
  126. data/metasm/cpu/webasm/debug.rb +31 -0
  127. data/metasm/cpu/webasm/decode.rb +321 -0
  128. data/metasm/cpu/webasm/decompile.rb +386 -0
  129. data/metasm/cpu/webasm/encode.rb +104 -0
  130. data/metasm/cpu/webasm/main.rb +81 -0
  131. data/metasm/cpu/webasm/opcodes.rb +214 -0
  132. data/metasm/cpu/x86_64.rb +15 -0
  133. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +40 -25
  134. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  135. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +58 -15
  136. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +59 -28
  137. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +18 -6
  138. data/metasm/cpu/x86_64/opcodes.rb +138 -0
  139. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +12 -4
  140. data/metasm/cpu/x86_64/render.rb +35 -0
  141. data/metasm/cpu/z80.rb +9 -0
  142. data/metasm/cpu/z80/decode.rb +286 -0
  143. data/metasm/cpu/z80/main.rb +67 -0
  144. data/metasm/cpu/z80/opcodes.rb +224 -0
  145. data/metasm/cpu/z80/render.rb +48 -0
  146. data/{lib/metasm/os/main.rb → metasm/debug.rb} +201 -407
  147. data/{lib/metasm → metasm}/decode.rb +104 -24
  148. data/{lib/metasm → metasm}/decompile.rb +804 -478
  149. data/{lib/metasm → metasm}/disassemble.rb +385 -170
  150. data/{lib/metasm → metasm}/disassemble_api.rb +684 -105
  151. data/{lib/metasm → metasm}/dynldr.rb +231 -138
  152. data/{lib/metasm → metasm}/encode.rb +20 -5
  153. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  154. data/{lib/metasm → metasm}/exe_format/autoexe.rb +3 -0
  155. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  156. data/{lib/metasm → metasm}/exe_format/coff.rb +35 -7
  157. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +70 -23
  158. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +24 -22
  159. data/{lib/metasm → metasm}/exe_format/dex.rb +26 -8
  160. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  161. data/{lib/metasm → metasm}/exe_format/elf.rb +108 -58
  162. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +202 -36
  163. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +126 -32
  164. data/metasm/exe_format/gb.rb +65 -0
  165. data/metasm/exe_format/javaclass.rb +424 -0
  166. data/{lib/metasm → metasm}/exe_format/macho.rb +218 -16
  167. data/{lib/metasm → metasm}/exe_format/main.rb +28 -3
  168. data/{lib/metasm → metasm}/exe_format/mz.rb +2 -0
  169. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  170. data/{lib/metasm → metasm}/exe_format/pe.rb +96 -11
  171. data/metasm/exe_format/pyc.rb +167 -0
  172. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  173. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  174. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  175. data/metasm/exe_format/swf.rb +205 -0
  176. data/metasm/exe_format/wasm.rb +402 -0
  177. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  178. data/metasm/exe_format/zip.rb +335 -0
  179. data/metasm/gui.rb +13 -0
  180. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  181. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  182. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +177 -114
  183. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  184. data/metasm/gui/dasm_graph.rb +1754 -0
  185. data/{lib/metasm → metasm}/gui/dasm_hex.rb +16 -12
  186. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  187. data/{lib/metasm → metasm}/gui/dasm_main.rb +360 -77
  188. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  189. data/{lib/metasm → metasm}/gui/debug.rb +109 -34
  190. data/{lib/metasm → metasm}/gui/gtk.rb +174 -44
  191. data/{lib/metasm → metasm}/gui/qt.rb +14 -4
  192. data/{lib/metasm → metasm}/gui/win32.rb +180 -43
  193. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  194. data/{lib/metasm → metasm}/main.rb +421 -286
  195. data/metasm/os/emulator.rb +175 -0
  196. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  197. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  198. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  199. data/metasm/os/main.rb +335 -0
  200. data/{lib/metasm → metasm}/os/windows.rb +151 -58
  201. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  202. data/{lib/metasm → metasm}/parse.rb +49 -36
  203. data/{lib/metasm → metasm}/parse_c.rb +405 -246
  204. data/{lib/metasm → metasm}/preprocessor.rb +71 -41
  205. data/{lib/metasm → metasm}/render.rb +14 -38
  206. data/misc/hexdump.rb +4 -3
  207. data/misc/lint.rb +58 -0
  208. data/misc/objdiff.rb +4 -1
  209. data/misc/objscan.rb +1 -1
  210. data/misc/openrisc-parser.rb +79 -0
  211. data/misc/txt2html.rb +9 -7
  212. data/samples/bindiff.rb +3 -4
  213. data/samples/dasm-plugins/bindiff.rb +15 -0
  214. data/samples/dasm-plugins/bookmark.rb +133 -0
  215. data/samples/dasm-plugins/c_constants.rb +57 -0
  216. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  217. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  218. data/samples/dasm-plugins/dasm_all.rb +70 -0
  219. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  220. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  221. data/samples/dasm-plugins/dump_text.rb +35 -0
  222. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  223. data/samples/dasm-plugins/findgadget.rb +75 -0
  224. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  225. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  226. data/samples/dasm-plugins/imm2off.rb +34 -0
  227. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  228. data/samples/dasm-plugins/patch_file.rb +95 -0
  229. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  230. data/samples/dasm-plugins/scanxrefs.rb +29 -0
  231. data/samples/dasm-plugins/selfmodify.rb +197 -0
  232. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  233. data/samples/dasmnavig.rb +1 -1
  234. data/samples/dbg-apihook.rb +24 -9
  235. data/samples/dbg-plugins/heapscan.rb +283 -0
  236. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  237. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  238. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  239. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  240. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  241. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  242. data/samples/dbg-plugins/trace_func.rb +214 -0
  243. data/samples/disassemble-gui.rb +48 -7
  244. data/samples/disassemble.rb +31 -6
  245. data/samples/dump_upx.rb +24 -12
  246. data/samples/dynamic_ruby.rb +35 -27
  247. data/samples/elfencode.rb +15 -0
  248. data/samples/emubios.rb +251 -0
  249. data/samples/emudbg.rb +127 -0
  250. data/samples/exeencode.rb +6 -5
  251. data/samples/factorize-headers-peimports.rb +1 -1
  252. data/samples/lindebug.rb +186 -391
  253. data/samples/metasm-shell.rb +68 -57
  254. data/samples/peldr.rb +2 -2
  255. data/tests/all.rb +1 -1
  256. data/tests/arc.rb +26 -0
  257. data/tests/dynldr.rb +22 -4
  258. data/tests/expression.rb +57 -0
  259. data/tests/graph_layout.rb +285 -0
  260. data/tests/ia32.rb +80 -26
  261. data/tests/mcs51.rb +27 -0
  262. data/tests/mips.rb +10 -3
  263. data/tests/preprocessor.rb +18 -0
  264. data/tests/x86_64.rb +66 -18
  265. metadata +465 -219
  266. metadata.gz.sig +2 -0
  267. data/lib/metasm/arm/opcodes.rb +0 -177
  268. data/lib/metasm/gui.rb +0 -23
  269. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  270. data/lib/metasm/ia32.rb +0 -14
  271. data/lib/metasm/ia32/opcodes.rb +0 -872
  272. data/lib/metasm/ppc/parse.rb +0 -52
  273. data/lib/metasm/x86_64.rb +0 -12
  274. data/lib/metasm/x86_64/opcodes.rb +0 -118
  275. data/samples/gdbclient.rb +0 -583
  276. data/samples/rubstop.rb +0 -399
@@ -0,0 +1,48 @@
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 gui_hilight_word_regexp_init
28
+ ret = {}
29
+
30
+ # { 'B' => 'B|BC', 'BC' => 'B|C|BC' }
31
+
32
+ %w[BC DE HL].each { |w|
33
+ l0, l1 = w.split(//)
34
+ ret[l0] = "#{l0}#{l1}?"
35
+ ret[l1] = "#{l0}?#{l1}"
36
+ ret[w] = "#{l0}|#{l0}?#{l1}"
37
+ }
38
+
39
+ ret
40
+ end
41
+
42
+ def gui_hilight_word_regexp(word)
43
+ @gui_hilight_word_hash ||= gui_hilight_word_regexp_init
44
+ @gui_hilight_word_hash[word] or super(word)
45
+ end
46
+
47
+ end
48
+ 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,9 +14,9 @@ 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
- # Expression if this is a conditionnal bp
19
+ # Expression if this is a conditional bp
325
20
  # may be a Proc, String or Expression, evaluated every time the breakpoint hits
326
21
  # if it returns 0 or false, the breakpoint is ignored
327
22
  :condition,
@@ -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(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
 
@@ -881,10 +612,10 @@ puts di.instruction, fdbd.inspect
881
612
  # due to a side-effect of the debugger (bpx with wrong condition etc)
882
613
  # returns nil if the execution should be avoided (just deleted the dead thread/process)
883
614
  def check_pre_run(run_m, *run_a)
884
- if @dead_process
615
+ if @dead_process ||= nil
885
616
  del_pid
886
617
  return
887
- elsif @dead_thread
618
+ elsif @dead_thread ||= nil
888
619
  del_tid
889
620
  return
890
621
  elsif @state == :running
@@ -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,14 +1267,14 @@ 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|
1523
1274
  if i.kind_of? Indirection and p = i.pointer.reduce and p.kind_of? ::Integer
1524
1275
  i.len ||= @cpu.size/8
1525
1276
  p &= (1 << @cpu.size) - 1 if p < 0
1526
- Expression.decode_imm(@memory, i.len, @cpu, p)
1277
+ @memory.decode_imm(p, i.len, @cpu)
1527
1278
  end
1528
1279
  }
1529
1280
  end
@@ -1539,11 +1290,11 @@ puts di.instruction, fdbd.inspect
1539
1290
  if arg1
1540
1291
  arg0 = resolve_expr(arg0) if not arg0.kind_of? ::Integer
1541
1292
  arg1 = resolve_expr(arg1) if not arg1.kind_of? ::Integer
1542
- @memory[arg0, arg1].to_str
1293
+ (@memory[arg0, arg1] || '').to_str
1543
1294
  elsif arg0.kind_of? ::Range
1544
1295
  arg0.begin = resolve_expr(arg0.begin) if not arg0.begin.kind_of? ::Integer # cannot happen, invalid ruby Range
1545
1296
  arg0.end = resolve_expr(arg0.end) if not arg0.end.kind_of? ::Integer
1546
- @memory[arg0].to_str
1297
+ (@memory[arg0] || '').to_str
1547
1298
  else
1548
1299
  get_reg_value(arg0)
1549
1300
  end
@@ -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,61 @@ 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
1444
+ end
1445
+
1446
+ class CPU
1447
+ # return the CPU register used to store the current instruction pointer
1448
+ def dbg_register_pc
1449
+ @dbg_register_pc ||= :pc
1450
+ end
1451
+
1452
+ # return the list of CPU registers
1453
+ def dbg_register_list
1454
+ @dbg_register_list ||= [dbg_register_pc]
1455
+ end
1456
+
1457
+ # return the list of flags for the CPU
1458
+ def dbg_flag_list
1459
+ @dbg_flag_list ||= []
1460
+ end
1461
+
1462
+ # return a hash with register name => register size in bits
1463
+ def dbg_register_size
1464
+ @dbg_register_size ||= Hash.new(@size)
1465
+ end
1466
+
1467
+ # returns true if stepover is different from stepinto for this instruction
1468
+ def dbg_need_stepover(dbg, addr, di)
1469
+ di and di.opcode.props[:saveip]
1470
+ end
1471
+
1472
+ # activate a software breakpoint
1473
+ def dbg_enable_bp(dbg, bp)
1474
+ end
1475
+
1476
+ # deactivate a software breakpoint
1477
+ def dbg_disable_bp(dbg, bp)
1478
+ end
1685
1479
  end
1686
1480
  end