metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. metadata +255 -0
@@ -0,0 +1,1686 @@
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/main'
7
+
8
+ 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
+ # this class implements a high-level debugging API (abstract superclass)
313
+ class Debugger
314
+ class Breakpoint
315
+ attr_accessor :address,
316
+ # context where the bp was defined
317
+ :pid, :tid,
318
+ # bool: oneshot ?
319
+ :oneshot,
320
+ # current bp state: :active, :inactive (internal use), :disabled (user-specified)
321
+ :state,
322
+ # type: type of breakpoint (:bpx = soft, :hw = hard)
323
+ :type,
324
+ # Expression if this is a conditionnal bp
325
+ # may be a Proc, String or Expression, evaluated every time the breakpoint hits
326
+ # if it returns 0 or false, the breakpoint is ignored
327
+ :condition,
328
+ # Proc to run if this bp has a callback
329
+ :action,
330
+ # Proc to run to emulate the overwritten instr behavior
331
+ # used to avoid unset/singlestep/re-set, more multithread friendly
332
+ :emul_instr,
333
+ # internal data, cpu-specific (overwritten byte for a softbp, memory type/size for hwbp..)
334
+ :internal,
335
+ # reference breakpoints sharing a target implementation (same hw debug register, soft bp addr...)
336
+ # shared is an array of Breakpoints, the same Array object in all shared breakpoints
337
+ # owner is a hash key => shared (dbg.breakpoint)
338
+ # key is an identifier for the Bp class in owner (bp.address)
339
+ :hash_shared, :hash_owner, :hash_key,
340
+ # user-defined breakpoint-specific stuff
341
+ :userdata
342
+
343
+ # append the breakpoint to hash_owner + hash_shared
344
+ def add(owner=@hash_owner)
345
+ @hash_owner = owner
346
+ @hash_key ||= @address
347
+ return add_bpm if @type == :bpm
348
+ if pv = owner[@hash_key]
349
+ @hash_shared = pv.hash_shared
350
+ @internal ||= pv.internal
351
+ @emul_instr ||= pv.emul_instr
352
+ else
353
+ owner[@hash_key] = self
354
+ @hash_shared = []
355
+ end
356
+ @hash_shared << self
357
+ end
358
+
359
+ # register a bpm: add references to all page start covered in @hash_owner
360
+ def add_bpm
361
+ m = @address + @internal[:len]
362
+ a = @address & -0x1000
363
+ @hash_shared = [self]
364
+
365
+ @internal ||= {}
366
+ @internal[:orig_prot] ||= {}
367
+ while a < m
368
+ if pv = @hash_owner[a]
369
+ if not pv.hash_shared.include?(self)
370
+ pv.hash_shared.concat @hash_shared-pv.hash_shared
371
+ @hash_shared.each { |bpm| bpm.hash_shared = pv.hash_shared }
372
+ end
373
+ @internal[:orig_prot][a] = pv.internal[:orig_prot][a]
374
+ else
375
+ @hash_owner[a] = self
376
+ end
377
+ a += 0x1000
378
+ end
379
+ end
380
+
381
+ # delete the breakpoint from hash_shared, and hash_owner if empty
382
+ def del
383
+ return del_bpm if @type == :bpm
384
+ @hash_shared.delete self
385
+ if @hash_shared.empty?
386
+ @hash_owner.delete @hash_key
387
+ elsif @hash_owner[@hash_key] == self
388
+ @hash_owner[@hash_key] = @hash_shared.first
389
+ end
390
+ end
391
+
392
+ # unregister a bpm
393
+ def del_bpm
394
+ m = @address + @internal[:len]
395
+ a = @address & -0x1000
396
+ @hash_shared.delete self
397
+ while a < m
398
+ pv = @hash_owner[a]
399
+ if pv == self
400
+ if opv = @hash_shared.find { |bpm|
401
+ bpm.address < a + 0x1000 and bpm.address + bpm.internal[:len] > a
402
+ }
403
+ @hash_owner[a] = opv
404
+ else
405
+ @hash_owner.delete a
406
+
407
+ # split hash_shared on disjoint ranges
408
+ prev_shared = @hash_shared.find_all { |bpm|
409
+ bpm.address < a + 0x1000 and bpm.address + bpm.internal[:len] <= a
410
+ }
411
+
412
+ prev_shared.each { |bpm|
413
+ bpm.hash_shared = prev_shared
414
+ @hash_shared.delete bpm
415
+ }
416
+ end
417
+ end
418
+ a += 0x1000
419
+ end
420
+ end
421
+ end
422
+
423
+ # per-process data
424
+ attr_accessor :memory, :cpu, :disassembler, :breakpoint, :breakpoint_memory,
425
+ :modulemap, :symbols, :symbols_len
426
+ # per-thread data
427
+ attr_accessor :state, :info, :breakpoint_thread, :singlestep_cb, :run_method,
428
+ :run_args, :breakpoint_cause
429
+
430
+ # which/where per-process/thread stuff is stored
431
+ attr_accessor :pid_stuff, :tid_stuff, :pid_stuff_list, :tid_stuff_list
432
+
433
+ # global debugger callbacks, called whenever such event occurs
434
+ attr_accessor :callback_singlestep, :callback_bpx, :callback_hwbp, :callback_bpm,
435
+ :callback_exception, :callback_newthread, :callback_endthread,
436
+ :callback_newprocess, :callback_endprocess, :callback_loadlibrary
437
+
438
+ # global switches, specify wether to break on exception/thread event
439
+ # can be a Proc that is evaluated (arg = info parameter of the evt_func)
440
+ # trace_children is a bool to tell if we should debug subprocesses spawned
441
+ # by the target
442
+ attr_accessor :pass_all_exceptions, :ignore_newthread, :ignore_endthread,
443
+ :trace_children
444
+
445
+ # link to the user-interface object if available
446
+ attr_accessor :gui
447
+
448
+ # initializes the disassembler internal data - subclasses should call super()
449
+ def initialize
450
+ @pid_stuff = {}
451
+ @tid_stuff = {}
452
+ @log_proc = nil
453
+ @state = :dead
454
+ @info = ''
455
+ # stuff saved when we switch pids
456
+ @pid_stuff_list = [:memory, :cpu, :disassembler, :symbols, :symbols_len,
457
+ :modulemap, :breakpoint, :breakpoint_memory, :tid, :tid_stuff,
458
+ :dead_process]
459
+ @tid_stuff_list = [:state, :info, :breakpoint_thread, :singlestep_cb,
460
+ :run_method, :run_args, :breakpoint_cause, :dead_thread]
461
+ @callback_loadlibrary = lambda { |h| loadsyms(h[:address]) ; continue }
462
+ @callback_newprocess = lambda { |h| log "process #{@pid} created" }
463
+ @callback_endprocess = lambda { |h| log "process #{@pid} died" }
464
+ initialize_newpid
465
+ initialize_newtid
466
+ end
467
+
468
+ def shortname; self.class.name.split('::').last.downcase; end
469
+
470
+ attr_reader :pid
471
+ # change pid and associated cached data
472
+ # this will also re-load the previously selected tid for this process
473
+ def pid=(npid)
474
+ return if npid == pid
475
+ raise "invalid pid" if not check_pid(npid)
476
+ swapout_pid
477
+ @pid = npid
478
+ swapin_pid
479
+ end
480
+ alias set_pid pid=
481
+
482
+ attr_reader :tid
483
+ def tid=(ntid)
484
+ return if ntid == tid
485
+ raise "invalid tid" if not check_tid(ntid)
486
+ swapout_tid
487
+ @tid = ntid
488
+ swapin_tid
489
+ end
490
+ alias set_tid tid=
491
+
492
+ # creates stuff related to a new process being debugged
493
+ # includes disassembler, modulemap, symbols, breakpoints
494
+ # subclasses should check that @pid maps to a real process and raise() otherwise
495
+ # to be called with @pid/@tid set, calls initialize_memory+initialize_cpu
496
+ def initialize_newpid
497
+ return if not pid
498
+ @pid_stuff_list.each { |s| instance_variable_set("@#{s}", nil) }
499
+
500
+ @symbols = {}
501
+ @symbols_len = {}
502
+ @modulemap = {}
503
+ @breakpoint = {}
504
+ @breakpoint_memory = {}
505
+ @tid_stuff = {}
506
+ initialize_cpu
507
+ initialize_memory
508
+ initialize_disassembler
509
+ end
510
+
511
+ # subclasses should check that @tid maps to a real thread and raise() otherwise
512
+ def initialize_newtid
513
+ return if not tid
514
+ @tid_stuff_list.each { |s| instance_variable_set("@#{s}", nil) }
515
+
516
+ @state = :stopped
517
+ @info = 'new'
518
+ @breakpoint_thread = {}
519
+ gui.swapin_tid if @disassembler and gui.respond_to?(:swapin_tid)
520
+ end
521
+
522
+ # initialize the disassembler from @cpu/@memory
523
+ def initialize_disassembler
524
+ return if not @memory or not @cpu
525
+ @disassembler = Shellcode.decode(@memory, @cpu).disassembler
526
+ gui.swapin_pid if gui.respond_to?(:swapin_pid)
527
+ end
528
+
529
+ # we're switching focus from one pid to another, save current pid data
530
+ def swapout_pid
531
+ return if not pid
532
+ swapout_tid
533
+ gui.swapout_pid if gui.respond_to?(:swapout_pid)
534
+ @pid_stuff[@pid] ||= {}
535
+ @pid_stuff_list.each { |fld|
536
+ @pid_stuff[@pid][fld] = instance_variable_get("@#{fld}")
537
+ }
538
+ end
539
+
540
+ # we're switching focus from one tid to another, save current tid data
541
+ def swapout_tid
542
+ return if not tid
543
+ gui.swapout_tid if gui.respond_to?(:swapout_tid)
544
+ @tid_stuff[@tid] ||= {}
545
+ @tid_stuff_list.each { |fld|
546
+ @tid_stuff[@tid][fld] = instance_variable_get("@#{fld}")
547
+ }
548
+ end
549
+
550
+ # we're switching focus from one pid to another, load current pid data
551
+ def swapin_pid
552
+ return initialize_newpid if not @pid_stuff[@pid]
553
+
554
+ @pid_stuff_list.each { |fld|
555
+ instance_variable_set("@#{fld}", @pid_stuff[@pid][fld])
556
+ }
557
+ swapin_tid
558
+ gui.swapin_pid if gui.respond_to?(:swapin_pid)
559
+ end
560
+
561
+ # we're switching focus from one tid to another, load current tid data
562
+ def swapin_tid
563
+ return initialize_newtid if not @tid_stuff[@tid]
564
+
565
+ @tid_stuff_list.each { |fld|
566
+ instance_variable_set("@#{fld}", @tid_stuff[@tid][fld])
567
+ }
568
+ gui.swapin_tid if gui.respond_to?(:swapin_tid)
569
+ end
570
+
571
+ # delete references to the current pid
572
+ # switch to another pid, set @state = :dead if none available
573
+ def del_pid
574
+ @pid_stuff.delete @pid
575
+ if @pid = @pid_stuff.keys.first
576
+ swapin_pid
577
+ else
578
+ @state = :dead
579
+ @info = ''
580
+ @tid = nil
581
+ end
582
+ end
583
+
584
+ # delete references to the current thread
585
+ # calls del_pid if no tid left
586
+ def del_tid
587
+ @tid_stuff.delete @tid
588
+ if @tid = @tid_stuff.keys.first
589
+ swapin_tid
590
+ else
591
+ del_pid
592
+ end
593
+ end
594
+
595
+ # change the debugger to a specific pid/tid
596
+ # if given a block, run the block and then restore the original pid/tid
597
+ # pid may be an object that respond to #pid/#tid
598
+ def switch_context(npid, ntid=nil)
599
+ if npid.respond_to? :pid
600
+ ntid ||= npid.tid
601
+ npid = npid.pid
602
+ end
603
+ oldpid = pid
604
+ oldtid = tid
605
+ set_pid npid
606
+ set_tid ntid if ntid
607
+ if block_given?
608
+ # shortcut begin..ensure overhead
609
+ return yield if oldpid == pid and oldtid == tid
610
+
611
+ begin
612
+ yield
613
+ ensure
614
+ set_pid oldpid
615
+ set_tid oldtid
616
+ end
617
+ end
618
+ end
619
+ alias set_context switch_context
620
+
621
+ # iterate over all pids, yield in the context of this pid
622
+ def each_pid
623
+ # ensure @pid is last, so that we finish in the current context
624
+ lst = @pid_stuff.keys - [@pid]
625
+ lst << @pid
626
+ return lst if not block_given?
627
+ lst.each { |p|
628
+ set_pid p
629
+ yield
630
+ }
631
+ end
632
+
633
+ # iterate over all tids of the current process, yield in its context
634
+ def each_tid
635
+ lst = @tid_stuff.keys - [@tid]
636
+ lst << @tid
637
+ return lst if not block_given?
638
+ lst.each { |t|
639
+ set_tid t
640
+ yield
641
+ }
642
+ end
643
+
644
+ # iterate over all tids of all pids, yield in their context
645
+ def each_pid_tid
646
+ each_pid { each_tid { yield } }
647
+ end
648
+
649
+
650
+ # create a thread/process breakpoint
651
+ # addr can be a numeric address, an Expression that is resolved, or
652
+ # a String that is parsed+resolved
653
+ # info's keys are set to the breakpoint
654
+ # standard keys are :type, :oneshot, :condition, :action
655
+ # returns the Breakpoint object
656
+ def add_bp(addr, info={})
657
+ info[:pid] ||= @pid
658
+ info[:tid] ||= @tid if info[:pid] == @pid
659
+
660
+ b = Breakpoint.new
661
+ info.each { |k, v|
662
+ b.send("#{k}=", v)
663
+ }
664
+
665
+ switch_context(b) {
666
+ addr = resolve_expr(addr) if not addr.kind_of? ::Integer
667
+ b.address = addr
668
+
669
+ b.hash_owner ||= case b.type
670
+ when :bpm; @breakpoint_memory
671
+ when :hwbp; @breakpoint_thread
672
+ when :bpx; @breakpoint
673
+ end
674
+ # XXX bpm may hash_share with an :active, but be larger and still need enable()
675
+ b.add
676
+
677
+ enable_bp(b) if not info[:state]
678
+ }
679
+
680
+ b
681
+ end
682
+
683
+ # remove a breakpoint
684
+ def del_bp(b)
685
+ disable_bp(b)
686
+ b.del
687
+ end
688
+
689
+ # activate an inactive breakpoint
690
+ def enable_bp(b)
691
+ return if b.state == :active
692
+ if not b.hash_shared.find { |bb| bb.state == :active }
693
+ switch_context(b) {
694
+ if not b.internal
695
+ init_bpx(b) if b.type == :bpx
696
+ b.internal ||= {}
697
+ b.hash_shared.each { |bb| bb.internal ||= b.internal }
698
+ end
699
+ do_enable_bp(b)
700
+ }
701
+ end
702
+ b.state = :active
703
+ end
704
+
705
+ # deactivate an active breakpoint
706
+ def disable_bp(b, newstate = :inactive)
707
+ return if b.state != :active
708
+ b.state = newstate
709
+ return if b.hash_shared.find { |bb| bb.state == :active }
710
+ switch_context(b) {
711
+ do_disable_bp(b)
712
+ }
713
+ end
714
+
715
+
716
+ # delete all breakpoints defined in the current thread
717
+ def del_all_breakpoints_thread
718
+ @breakpoint_thread.values.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
719
+ end
720
+
721
+ # delete all breakpoints for the current process and all its threads
722
+ def del_all_breakpoints
723
+ each_tid { del_all_breakpoints_thread }
724
+ @breakpoint.values.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
725
+ @breakpoint_memory.values.uniq.map { |b| b.hash_shared }.flatten.uniq.each { |b| del_bp(b) }
726
+ end
727
+
728
+ # calls do_enable_bpm for bpms, or @cpu.dbg_enable_bp
729
+ def do_enable_bp(b)
730
+ if b.type == :bpm; do_enable_bpm(b)
731
+ else @cpu.dbg_enable_bp(self, b)
732
+ end
733
+ end
734
+
735
+ # calls do_disable_bpm for bpms, or @cpu.dbg_disable_bp
736
+ def do_disable_bp(b)
737
+ if b.type == :bpm; do_disable_bpm(b)
738
+ else @cpu.dbg_disable_bp(self, b)
739
+ end
740
+ end
741
+
742
+ # 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
744
+ 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)
765
+ }
766
+
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
+ }
779
+ }
780
+ b.hash_shared.each { |bb| bb.emul_instr = b.emul_instr }
781
+ end
782
+ end
783
+
784
+ # sets a breakpoint on execution
785
+ def bpx(addr, oneshot=false, cond=nil, &action)
786
+ h = { :type => :bpx }
787
+ h[:oneshot] = true if oneshot
788
+ h[:condition] = cond if cond
789
+ h[:action] = action if action
790
+ add_bp(addr, h)
791
+ end
792
+
793
+ # sets a hardware breakpoint
794
+ # mtype in :r :w :x
795
+ # mlen is the size of the memory zone to cover
796
+ # mlen may be constrained by the architecture
797
+ def hwbp(addr, mtype=:x, mlen=1, oneshot=false, cond=nil, &action)
798
+ h = { :type => :hwbp }
799
+ h[:hash_owner] = @breakpoint_thread
800
+ addr = resolve_expr(addr) if not addr.kind_of? ::Integer
801
+ h[:hash_key] = [addr, mtype, mlen]
802
+ h[:internal] = { :type => mtype, :len => mlen }
803
+ h[:oneshot] = true if oneshot
804
+ h[:condition] = cond if cond
805
+ h[:action] = action if action
806
+ add_bp(addr, h)
807
+ end
808
+
809
+ # sets a memory breakpoint
810
+ # mtype is :r :w :rw or :x
811
+ # mlen is the size of the memory zone to cover
812
+ def bpm(addr, mtype=:r, mlen=4096, oneshot=false, cond=nil, &action)
813
+ h = { :type => :bpm }
814
+ addr = resolve_expr(addr) if not addr.kind_of? ::Integer
815
+ h[:hash_key] = addr & -4096 # XXX actually referenced at addr, addr+4096, ... addr+len
816
+ h[:internal] = { :type => type, :len => mlen }
817
+ h[:oneshot] = true if oneshot
818
+ h[:condition] = cond if cond
819
+ h[:action] = action if action
820
+ add_bp(addr, h)
821
+ end
822
+
823
+
824
+ # define the lambda to use to log stuff (used by #puts)
825
+ def set_log_proc(l=nil, &b)
826
+ @log_proc = l || b
827
+ end
828
+
829
+ # show information to the user, uses log_proc if defined
830
+ def log(*a)
831
+ if @log_proc
832
+ a.each { |aa| @log_proc[aa] }
833
+ else
834
+ puts(*a)
835
+ end
836
+ end
837
+
838
+
839
+ # marks the current cache of memory/regs invalid
840
+ def invalidate
841
+ @memory.invalidate if @memory
842
+ end
843
+
844
+ # invalidates the EncodedData backend for the dasm sections
845
+ def dasm_invalidate
846
+ disassembler.sections.each_value { |s| s.data.invalidate if s.data.respond_to? :invalidate }
847
+ end
848
+
849
+ # return all breakpoints set on a specific address (or all bp)
850
+ def all_breakpoints(addr=nil)
851
+ ret = []
852
+ if addr
853
+ if b = @breakpoint[addr]
854
+ ret |= b.hash_shared
855
+ end
856
+ else
857
+ @breakpoint.each_value { |bb| ret |= bb.hash_shared }
858
+ end
859
+
860
+ @breakpoint_thread.each_value { |bb|
861
+ next if addr and bb.address != addr
862
+ ret |= bb.hash_shared
863
+ }
864
+
865
+ @breakpoint_memory.each_value { |m|
866
+ next if addr and (bb.address+bb.internal[:len] <= addr or bb.address > addr)
867
+ ret |= bb.hash_shared
868
+ }
869
+
870
+ ret
871
+ end
872
+
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 }
876
+ end
877
+
878
+
879
+ # to be called right before resuming execution of the target
880
+ # run_m is the method that should be called if the execution is stopped
881
+ # due to a side-effect of the debugger (bpx with wrong condition etc)
882
+ # returns nil if the execution should be avoided (just deleted the dead thread/process)
883
+ def check_pre_run(run_m, *run_a)
884
+ if @dead_process
885
+ del_pid
886
+ return
887
+ elsif @dead_thread
888
+ del_tid
889
+ return
890
+ elsif @state == :running
891
+ return
892
+ end
893
+ @cpu.dbg_check_pre_run(self) if @cpu.respond_to?(:dbg_check_pre_run)
894
+ @breakpoint_cause = nil
895
+ @run_method = run_m
896
+ @run_args = run_a
897
+ @state = :running
898
+ @info = nil
899
+ true
900
+ end
901
+
902
+
903
+ # called when the target stops due to a singlestep exception
904
+ def evt_singlestep(b=nil)
905
+ b ||= find_singlestep
906
+ return evt_exception(:type => 'singlestep') if not b
907
+
908
+ @state = :stopped
909
+ @info = 'singlestep'
910
+ @cpu.dbg_evt_singlestep(self) if @cpu.respond_to?(:dbg_evt_singlestep)
911
+
912
+ callback_singlestep[] if callback_singlestep
913
+
914
+ if cb = @singlestep_cb
915
+ @singlestep_cb = nil
916
+ cb.call # call last, as the cb may change singlestep_cb/state/etc
917
+ end
918
+ end
919
+
920
+ # returns true if the singlestep is due to us
921
+ def find_singlestep
922
+ return @cpu.dbg_find_singlestep(self) if @cpu.respond_to?(:dbg_find_singlestep)
923
+ @run_method == :singlestep
924
+ end
925
+
926
+ # called when the target stops due to a soft breakpoint exception
927
+ def evt_bpx(b=nil)
928
+ b ||= find_bp_bpx
929
+ return evt_exception(:type => 'breakpoint') if not b
930
+
931
+ @state = :stopped
932
+ @info = 'breakpoint'
933
+ @cpu.dbg_evt_bpx(self, b) if @cpu.respond_to?(:dbg_evt_bpx)
934
+
935
+ callback_bpx[b] if callback_bpx
936
+
937
+ post_evt_bp(b)
938
+ end
939
+
940
+ # return the breakpoint that is responsible for the evt_bpx
941
+ def find_bp_bpx
942
+ return @cpu.dbg_find_bpx(self) if @cpu.respond_to?(:dbg_find_bpx)
943
+ @breakpoint[pc]
944
+ end
945
+
946
+ # called when the target stops due to a hwbp exception
947
+ def evt_hwbp(b=nil)
948
+ b ||= find_bp_hwbp
949
+ return evt_exception(:type => 'hwbp') if not b
950
+
951
+ @state = :stopped
952
+ @info = 'hwbp'
953
+ @cpu.dbg_evt_hwbp(self, b) if @cpu.respond_to?(:dbg_evt_hwbp)
954
+
955
+ callback_hwbp[b] if callback_hwbp
956
+
957
+ post_evt_bp(b)
958
+ end
959
+
960
+ # return the breakpoint that is responsible for the evt_hwbp
961
+ def find_bp_hwbp
962
+ return @cpu.dbg_find_hwbp(self) if @cpu.respond_to?(:dbg_find_bpx)
963
+ @breakpoint_thread.find { |b| b.address == pc }
964
+ end
965
+
966
+ # called for archs where the same interrupt is generated for hwbp and singlestep
967
+ # checks if a hwbp matches, then call evt_hwbp, else call evt_singlestep (which
968
+ # will forward to evt_exception if singlestep does not match either)
969
+ def evt_hwbp_singlestep
970
+ if b = find_bp_hwbp
971
+ evt_hwbp(b)
972
+ else
973
+ evt_singlestep
974
+ end
975
+ end
976
+
977
+ # called when the target stops due to a memory exception caused by a memory bp
978
+ # called by evt_exception
979
+ def evt_bpm(b)
980
+ @state = :stopped
981
+ @info = 'bpm'
982
+
983
+ callback_bpm[b] if callback_bpm
984
+
985
+ post_evt_bp(b)
986
+ end
987
+
988
+ # return a bpm whose page coverage includes the fault described in info
989
+ def find_bp_bpm(info)
990
+ @breakpoint_memory[info[:fault_addr] & -0x1000]
991
+ end
992
+
993
+ # returns true if the fault described in info is valid to trigger b
994
+ def check_bpm_range(b, info)
995
+ return if b.address+b.internal[:len] <= info[:fault_addr]
996
+ return if b.address >= info[:fault_addr] + info[:fault_len]
997
+ case b.internal[:type]
998
+ when :x; info[:fault_addr] == pc # XXX
999
+ when :r; info[:fault_access] == :r
1000
+ when :w; info[:fault_access] == :w
1001
+ when :rw; true
1002
+ end
1003
+ end
1004
+
1005
+ # handles breakpoint conditions/callbacks etc
1006
+ def post_evt_bp(b)
1007
+ @breakpoint_cause = b
1008
+
1009
+ found_valid_active = false
1010
+
1011
+ # XXX may have many active bps with callback that continue/singlestep/singlestep{}...
1012
+ b.hash_shared.dup.map { |bb|
1013
+ # ignore inactive bps
1014
+ next if bb.state != :active
1015
+
1016
+ # ignore out-of-range bpms
1017
+ next if bb.type == :bpm and not check_bpm_range(bb, b.internal)
1018
+
1019
+ # check condition
1020
+ case bb.condition
1021
+ when nil; cd = 1
1022
+ when Proc; cd = bb.condition.call
1023
+ when String, Expression; cd = resolve_expr(bb.condition)
1024
+ else raise "unknown bp condition #{bb.condition.inspect}"
1025
+ end
1026
+ next if not cd or cd == 0
1027
+
1028
+ found_valid_active = true
1029
+
1030
+ # oneshot
1031
+ del_bp(bb) if bb.oneshot
1032
+
1033
+ # callback
1034
+ bb.action
1035
+ }.compact.each { |cb| cb.call }
1036
+
1037
+ # we did break due to a bp whose condition is not true: resume
1038
+ # (unless a callback already resumed)
1039
+ resume_badbreak(b) if not found_valid_active and @state == :stopped
1040
+ end
1041
+
1042
+ # called whenever the target stops due to an exception
1043
+ # type may be:
1044
+ # * 'access violation', :fault_addr, :fault_len, :fault_access (:r/:w/:x)
1045
+ # anything else for other exceptions (access violation is special to handle bpm)
1046
+ # ...
1047
+ def evt_exception(info={})
1048
+ if info[:type] == 'access violation' and b = find_bp_bpm(info)
1049
+ info[:fault_len] ||= 1
1050
+ b.internal.update info
1051
+ return evt_bpm(b)
1052
+ end
1053
+
1054
+ @state = :stopped
1055
+ @info = "exception #{info[:type]}"
1056
+
1057
+ callback_exception[info] if callback_exception
1058
+
1059
+ pass = pass_all_exceptions
1060
+ pass = pass[info] if pass.kind_of? Proc
1061
+ if pass
1062
+ pass_current_exception
1063
+ resume_badbreak
1064
+ end
1065
+ end
1066
+
1067
+ def evt_newthread(info={})
1068
+ @state = :stopped
1069
+ @info = 'new thread'
1070
+
1071
+ callback_newthread[info] if callback_newthread
1072
+
1073
+ ign = ignore_newthread
1074
+ ign = ign[info] if ign.kind_of? Proc
1075
+ if ign
1076
+ continue
1077
+ end
1078
+ end
1079
+
1080
+ def evt_endthread(info={})
1081
+ @state = :stopped
1082
+ @info = 'end thread'
1083
+ # mark the thread as to be deleted on next check_pre_run
1084
+ @dead_thread = true
1085
+
1086
+ callback_endthread[info] if callback_endthread
1087
+
1088
+ ign = ignore_endthread
1089
+ ign = ign[info] if ign.kind_of? Proc
1090
+ if ign
1091
+ continue
1092
+ end
1093
+ end
1094
+
1095
+ def evt_newprocess(info={})
1096
+ @state = :stopped
1097
+ @info = 'new process'
1098
+
1099
+ callback_newprocess[info] if callback_newprocess
1100
+ end
1101
+
1102
+ def evt_endprocess(info={})
1103
+ @state = :stopped
1104
+ @info = 'end process'
1105
+ @dead_process = true
1106
+
1107
+ callback_endprocess[info] if callback_endprocess
1108
+ end
1109
+
1110
+ def evt_loadlibrary(info={})
1111
+ @state = :stopped
1112
+ @info = 'loadlibrary'
1113
+
1114
+ callback_loadlibrary[info] if callback_loadlibrary
1115
+ end
1116
+
1117
+ # called when we did break due to a breakpoint whose condition is invalid
1118
+ # resume execution as if we never stopped
1119
+ # disable offending bp + singlestep if needed
1120
+ def resume_badbreak(b=nil)
1121
+ # ensure we didn't delete b
1122
+ if b and b.hash_shared.find { |bb| bb.state == :active }
1123
+ rm = @run_method
1124
+ if rm == :singlestep
1125
+ singlestep_bp(b)
1126
+ else
1127
+ @run_args = ra
1128
+ singlestep_bp(b) { send rm, *ra }
1129
+ end
1130
+ else
1131
+ send @run_method, *@run_args
1132
+ end
1133
+ end
1134
+
1135
+ # singlesteps over an active breakpoint and run its block
1136
+ # if the breakpoint provides an emulation stub, run that, otherwise
1137
+ # disable the breakpoint, singlestep, and re-enable
1138
+ def singlestep_bp(bp, &b)
1139
+ if be = bp.hash_shared.find { |bb| bb.emul_instr }
1140
+ @state = :stopped
1141
+ be.emul_instr[self]
1142
+ yield if block_given?
1143
+ else
1144
+ bp.hash_shared.each { |bb|
1145
+ disable_bp(bb, :temp_inactive) if bb.state == :active
1146
+ }
1147
+ # this *should* work with different bps stopping the current instr
1148
+ prev_sscb = @singlestep_cb
1149
+ singlestep {
1150
+ bp.hash_shared.each { |bb|
1151
+ enable_bp(bb) if bb.state == :temp_inactive
1152
+ }
1153
+ prev_sscb[] if prev_sscb
1154
+ yield if block_given?
1155
+ }
1156
+ end
1157
+ end
1158
+
1159
+
1160
+ # checks if the running target has stopped (nonblocking)
1161
+ def check_target
1162
+ do_check_target
1163
+ end
1164
+
1165
+ # waits until the running target stops (due to a breakpoint, fault, etc)
1166
+ def wait_target
1167
+ do_wait_target while @state == :running
1168
+ end
1169
+
1170
+ # resume execution of the target
1171
+ # bypasses a software breakpoint on pc if needed
1172
+ # thread breakpoints must be manually disabled before calling continue
1173
+ def continue
1174
+ if b = @breakpoint_cause and b.hash_shared.find { |bb| bb.state == :active }
1175
+ singlestep_bp(b) {
1176
+ next if not check_pre_run(:continue)
1177
+ do_continue
1178
+ }
1179
+ else
1180
+ return if not check_pre_run(:continue)
1181
+ do_continue
1182
+ end
1183
+ end
1184
+ alias run continue
1185
+
1186
+ # continue ; wait_target
1187
+ def continue_wait
1188
+ continue
1189
+ wait_target
1190
+ end
1191
+
1192
+ # resume execution of the target one instruction at a time
1193
+ def singlestep(&b)
1194
+ @singlestep_cb = b
1195
+ bp = @breakpoint_cause
1196
+ 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 }
1198
+ @state = :stopped
1199
+ be.emul_instr[self]
1200
+ invalidate
1201
+ evt_singlestep(true)
1202
+ else
1203
+ do_singlestep
1204
+ end
1205
+ end
1206
+
1207
+ # singlestep ; wait_target
1208
+ def singlestep_wait(&b)
1209
+ singlestep(&b)
1210
+ wait_target
1211
+ end
1212
+
1213
+ # tests if the specified instructions should be stepover() using singlestep or
1214
+ # by putting a breakpoint at next_addr
1215
+ def need_stepover(di = di_at(pc))
1216
+ di and @cpu.dbg_need_stepover(self, di.address, di)
1217
+ end
1218
+
1219
+ # stepover: singlesteps, but do not enter in subfunctions
1220
+ def stepover
1221
+ di = di_at(pc)
1222
+ if need_stepover(di)
1223
+ bpx di.next_addr, true, Expression[:tid, :==, @tid]
1224
+ continue
1225
+ else
1226
+ singlestep
1227
+ end
1228
+ end
1229
+
1230
+ # stepover ; wait_target
1231
+ def stepover_wait
1232
+ stepover
1233
+ wait_target
1234
+ end
1235
+
1236
+ # checks if an instruction should stop the stepout() (eg it is a return instruction)
1237
+ def end_stepout(di = di_at(pc))
1238
+ di and @cpu.dbg_end_stepout(self, di.address, di)
1239
+ end
1240
+
1241
+ # stepover until finding the last instruction of the function
1242
+ def stepout
1243
+ # TODO thread-local bps
1244
+ while not end_stepout
1245
+ stepover
1246
+ wait_target
1247
+ end
1248
+ do_singlestep
1249
+ end
1250
+
1251
+ # set a singleshot breakpoint, run the process, and wait
1252
+ def go(target, cond=nil)
1253
+ bpx(target, true, cond)
1254
+ continue_wait
1255
+ end
1256
+
1257
+ # continue_wait until @state == :dead
1258
+ def run_forever
1259
+ continue_wait until @state == :dead
1260
+ end
1261
+
1262
+ # decode the Instruction at the address, use the @disassembler cache if available
1263
+ def di_at(addr)
1264
+ @disassembler.di_at(addr) || @disassembler.disassemble_instruction(addr)
1265
+ end
1266
+
1267
+ # list the general purpose register names available for the target
1268
+ def register_list
1269
+ @cpu.dbg_register_list
1270
+ end
1271
+
1272
+ # hash { register_name => register_size_in_bits }
1273
+ def register_size
1274
+ @cpu.dbg_register_size
1275
+ end
1276
+
1277
+ # retrieves the name of the register holding the program counter (address of the next instruction)
1278
+ def register_pc
1279
+ @cpu.dbg_register_pc
1280
+ end
1281
+
1282
+ # retrieve the name of the register holding the stack pointer
1283
+ def register_sp
1284
+ @cpu.dbg_register_sp
1285
+ end
1286
+
1287
+ # then name of the register holding the cpu flags
1288
+ def register_flags
1289
+ @cpu.dbg_register_flags
1290
+ end
1291
+
1292
+ # list of flags available in the flag register
1293
+ def flag_list
1294
+ @cpu.dbg_flag_list
1295
+ end
1296
+
1297
+ # retreive the value of the program counter register (eip)
1298
+ def pc
1299
+ get_reg_value(register_pc)
1300
+ end
1301
+ alias ip pc
1302
+
1303
+ # change the value of pc
1304
+ def pc=(v)
1305
+ set_reg_value(register_pc, v)
1306
+ end
1307
+ alias ip= pc=
1308
+
1309
+ # retrieve the value of the stack pointer register
1310
+ def sp
1311
+ get_reg_value(register_sp)
1312
+ end
1313
+
1314
+ # update the stack pointer
1315
+ def sp=(v)
1316
+ set_reg_value(register_sp, v)
1317
+ end
1318
+
1319
+ # retrieve the value of a flag (0/1)
1320
+ def get_flag_value(f)
1321
+ @cpu.dbg_get_flag(self, f)
1322
+ end
1323
+
1324
+ # retrieve the value of a flag (true/false)
1325
+ def get_flag(f)
1326
+ get_flag_value(f) != 0
1327
+ end
1328
+
1329
+ # change the value of a flag
1330
+ def set_flag_value(f, v)
1331
+ (v && v != 0) ? set_flag(f) : unset_flag(f)
1332
+ end
1333
+
1334
+ # switch the value of a flag (true->false, false->true)
1335
+ def toggle_flag(f)
1336
+ set_flag_value(f, 1-get_flag_value(f))
1337
+ end
1338
+
1339
+ # set the value of the flag to true
1340
+ def set_flag(f)
1341
+ @cpu.dbg_set_flag(self, f)
1342
+ end
1343
+
1344
+ # set the value of the flag to false
1345
+ def unset_flag(f)
1346
+ @cpu.dbg_unset_flag(self, f)
1347
+ end
1348
+
1349
+ # returns the name of the module containing addr or nil
1350
+ def addr2module(addr)
1351
+ @modulemap.keys.find { |k| @modulemap[k][0] <= addr and @modulemap[k][1] > addr }
1352
+ end
1353
+
1354
+ # returns a string describing addr in term of symbol (eg 'libc.so.6!printf+2f')
1355
+ def addrname(addr)
1356
+ (addr2module(addr) || '???') + '!' +
1357
+ if s = @symbols[addr] ? addr : @symbols_len.keys.find { |s_| s_ < addr and s_ + @symbols_len[s_] > addr }
1358
+ @symbols[s] + (addr == s ? '' : ('+%x' % (addr-s)))
1359
+ else '%08x' % addr
1360
+ end
1361
+ end
1362
+
1363
+ # same as addrname, but scan preceding addresses if no symbol matches
1364
+ def addrname!(addr)
1365
+ (addr2module(addr) || '???') + '!' +
1366
+ if s = @symbols[addr] ? addr :
1367
+ @symbols_len.keys.find { |s_| s_ < addr and s_ + @symbols_len[s_] > addr } ||
1368
+ @symbols.keys.sort.find_all { |s_| s_ < addr and s_ + 0x10000 > addr }.max
1369
+ @symbols[s] + (addr == s ? '' : ('+%x' % (addr-s)))
1370
+ else '%08x' % addr
1371
+ end
1372
+ end
1373
+
1374
+ # loads the symbols from a mapped module
1375
+ def loadsyms(addr, name='%08x'%addr.to_i)
1376
+ if addr.kind_of? String
1377
+ modules.each { |m|
1378
+ if m.path =~ /#{addr}/i
1379
+ addr = m.addr
1380
+ name = File.basename m.path
1381
+ break
1382
+ end
1383
+ }
1384
+ return if not addr.kind_of? Integer
1385
+ end
1386
+ return if not peek = @memory.get_page(addr, 4)
1387
+ if peek == "\x7fELF"
1388
+ cls = LoadedELF
1389
+ elsif peek[0, 2] == "MZ" and @memory[addr+@memory[addr+0x3c,4].unpack('V').first, 4] == "PE\0\0"
1390
+ cls = LoadedPE
1391
+ else return
1392
+ end
1393
+
1394
+ begin
1395
+ e = cls.load @memory[addr, 0x1000_0000]
1396
+ e.load_address = addr
1397
+ e.decode_header
1398
+ e.decode_exports
1399
+ rescue
1400
+ # cache the error so that we dont hit it every time
1401
+ @modulemap[addr.to_s(16)] ||= [addr, addr+0x1000]
1402
+ return
1403
+ end
1404
+
1405
+ if n = e.module_name and n != name
1406
+ name = n
1407
+ end
1408
+
1409
+ @modulemap[name] ||= [addr, addr+e.module_size]
1410
+
1411
+ cnt = 0
1412
+ e.module_symbols.each { |n_, a, l|
1413
+ cnt += 1
1414
+ a += addr
1415
+ @disassembler.set_label_at(a, n_, false)
1416
+ @symbols[a] = n_ # XXX store "lib!sym" ?
1417
+ if l and l > 1; @symbols_len[a] = l
1418
+ else @symbols_len.delete a # we may overwrite an existing symbol, keep len in sync
1419
+ end
1420
+ }
1421
+ log "loaded #{cnt} symbols from #{name}"
1422
+
1423
+ true
1424
+ end
1425
+
1426
+ # scan the target memory for loaded libraries, load their symbols
1427
+ def scansyms(addr=0, max=@memory.length-0x1000-addr)
1428
+ while addr <= max
1429
+ loadsyms(addr)
1430
+ addr += 0x1000
1431
+ end
1432
+ end
1433
+
1434
+ # load symbols from all libraries found by the OS module
1435
+ def loadallsyms
1436
+ modules.each { |m|
1437
+ yield m.addr if block_given?
1438
+ loadsyms(m.addr, m.path)
1439
+ }
1440
+ end
1441
+
1442
+ # see Disassembler#load_map
1443
+ def load_map(str, off=0)
1444
+ str = File.read(str) if File.exist?(str)
1445
+ sks = @disassembler.sections.keys.sort
1446
+ str.each_line { |l|
1447
+ case l.strip
1448
+ when /^([0-9A-F]+)\s+(\w+)\s+(\w+)/i # kernel.map style
1449
+ a = $1.to_i(16) + off
1450
+ n = $3
1451
+ when /^([0-9A-F]+):([0-9A-F]+)\s+([a-z_]\w+)/i # IDA style
1452
+ # see Disassembler for comments
1453
+ a = sks[$1.to_i(16)] + $2.to_i(16) + off
1454
+ n = $3
1455
+ else next
1456
+ end
1457
+ @disassembler.set_label_at(a, n, false)
1458
+ @symbols[a] = n
1459
+ }
1460
+
1461
+ end
1462
+
1463
+ # parses the expression contained in arg
1464
+ def parse_expr(arg)
1465
+ parse_expr!(arg.dup)
1466
+ end
1467
+
1468
+ # parses the expression contained in arg, updates arg to point after the expr
1469
+ def parse_expr!(arg)
1470
+ return if not e = IndExpression.parse_string!(arg) { |s|
1471
+ # handle 400000 -> 0x400000
1472
+ # XXX no way to override and force decimal interpretation..
1473
+ if s.length > 4 and not @disassembler.get_section_at(s.to_i) and @disassembler.get_section_at(s.to_i(16))
1474
+ s.to_i(16)
1475
+ else
1476
+ s.to_i
1477
+ end
1478
+ }
1479
+
1480
+ # resolve ambiguous symbol names/hex values
1481
+ bd = {}
1482
+ e.externals.grep(::String).each { |ex|
1483
+ if not v = register_list.find { |r| ex.downcase == r.to_s.downcase } ||
1484
+ (block_given? && yield(ex)) || symbols.index(ex)
1485
+ lst = symbols.values.find_all { |s| s.downcase.include? ex.downcase }
1486
+ case lst.length
1487
+ when 0
1488
+ if ex =~ /^[0-9a-f]+$/i and @disassembler.get_section_at(ex.to_i(16))
1489
+ v = ex.to_i(16)
1490
+ else
1491
+ raise "unknown symbol name #{ex}"
1492
+ end
1493
+ when 1
1494
+ v = symbols.index(lst.first)
1495
+ log "using #{lst.first} for #{ex}"
1496
+ else
1497
+ suggest = lst[0, 50].join(', ')
1498
+ suggest = suggest[0, 125] + '...' if suggest.length > 128
1499
+ raise "ambiguous symbol name #{ex}: #{suggest} ?"
1500
+ end
1501
+ end
1502
+ bd[ex] = v
1503
+ }
1504
+ e = e.bind(bd)
1505
+
1506
+ e
1507
+ end
1508
+
1509
+ # resolves an expression involving register values and/or memory indirection using the current context
1510
+ # uses #register_list, #get_reg_value, @mem, @cpu
1511
+ # :tid/:pid resolve to current thread
1512
+ def resolve_expr(e)
1513
+ e = parse_expr(e) if e.kind_of? ::String
1514
+ bd = { :tid => @tid, :pid => @pid }
1515
+ Expression[e].externals.each { |ex|
1516
+ next if bd[ex]
1517
+ case ex
1518
+ when ::Symbol; bd[ex] = get_reg_value(ex)
1519
+ when ::String; bd[ex] = @symbols.index(ex) || 0
1520
+ end
1521
+ }
1522
+ Expression[e].bind(bd).reduce { |i|
1523
+ if i.kind_of? Indirection and p = i.pointer.reduce and p.kind_of? ::Integer
1524
+ i.len ||= @cpu.size/8
1525
+ p &= (1 << @cpu.size) - 1 if p < 0
1526
+ Expression.decode_imm(@memory, i.len, @cpu, p)
1527
+ end
1528
+ }
1529
+ end
1530
+ alias resolve resolve_expr
1531
+
1532
+ # return/yield an array of [addr, addr symbolic name] corresponding to the current stack trace
1533
+ def stacktrace(maxdepth=500, &b)
1534
+ @cpu.dbg_stacktrace(self, maxdepth, &b)
1535
+ end
1536
+
1537
+ # accepts a range or begin/end address to read memory, or a register name
1538
+ def [](arg0, arg1=nil)
1539
+ if arg1
1540
+ arg0 = resolve_expr(arg0) if not arg0.kind_of? ::Integer
1541
+ arg1 = resolve_expr(arg1) if not arg1.kind_of? ::Integer
1542
+ @memory[arg0, arg1].to_str
1543
+ elsif arg0.kind_of? ::Range
1544
+ arg0.begin = resolve_expr(arg0.begin) if not arg0.begin.kind_of? ::Integer # cannot happen, invalid ruby Range
1545
+ arg0.end = resolve_expr(arg0.end) if not arg0.end.kind_of? ::Integer
1546
+ @memory[arg0].to_str
1547
+ else
1548
+ get_reg_value(arg0)
1549
+ end
1550
+ end
1551
+
1552
+ # accepts a range or begin/end address to write memory, or a register name
1553
+ def []=(arg0, arg1, val=nil)
1554
+ arg1, val = val, arg1 if not val
1555
+ if arg1
1556
+ arg0 = resolve_expr(arg0) if not arg0.kind_of? ::Integer
1557
+ arg1 = resolve_expr(arg1) if not arg1.kind_of? ::Integer
1558
+ @memory[arg0, arg1] = val
1559
+ elsif arg0.kind_of? ::Range
1560
+ arg0.begin = resolve_expr(arg0.begin) if not arg0.begin.kind_of? ::Integer # cannot happen, invalid ruby Range
1561
+ arg0.end = resolve_expr(arg0.end) if not arg0.end.kind_of? ::Integer
1562
+ @memory[arg0] = val
1563
+ else
1564
+ set_reg_value(arg0, val)
1565
+ end
1566
+ end
1567
+
1568
+
1569
+ # read an int from the target memory, int of sz bytes (defaults to cpu.size)
1570
+ def memory_read_int(addr, sz=@cpu.size/8)
1571
+ addr = resolve_expr(addr) if not addr.kind_of? ::Integer
1572
+ Expression.decode_imm(@memory, sz, @cpu, addr)
1573
+ end
1574
+
1575
+ # write an int in the target memory
1576
+ def memory_write_int(addr, val, sz=@cpu.size/8)
1577
+ addr = resolve_expr(addr) if not addr.kind_of? ::Integer
1578
+ val = resolve_expr(val) if not val.kind_of? ::Integer
1579
+ @memory[addr, sz] = Expression.encode_imm(val, sz, @cpu)
1580
+ end
1581
+
1582
+ # retrieve an argument (call at a function entrypoint)
1583
+ def func_arg(nr)
1584
+ @cpu.dbg_func_arg(self, nr)
1585
+ end
1586
+ def func_arg_set(nr, val)
1587
+ @cpu.dbg_func_arg_set(self, nr, val)
1588
+ end
1589
+
1590
+ # retrieve a function returned value (call at func exitpoint)
1591
+ def func_retval
1592
+ @cpu.dbg_func_retval(self)
1593
+ end
1594
+ def func_retval_set(val)
1595
+ @cpu.dbg_func_retval_set(self, val)
1596
+ end
1597
+ def func_retval=(val)
1598
+ @cpu.dbg_func_retval_set(self, val)
1599
+ end
1600
+
1601
+ # retrieve a function return address (call at func entry/exit)
1602
+ def func_retaddr
1603
+ @cpu.dbg_func_retaddr(self)
1604
+ end
1605
+ def func_retaddr_set(addr)
1606
+ @cpu.dbg_func_retaddr_set(self, addr)
1607
+ end
1608
+ def func_retaddr=(addr)
1609
+ @cpu.dbg_func_retaddr_set(self, addr)
1610
+ end
1611
+
1612
+ def load_plugin(plugin_filename)
1613
+ if not File.exist?(plugin_filename) and defined? Metasmdir
1614
+ # try autocomplete
1615
+ pf = File.join(Metasmdir, 'samples', 'dbg-plugins', plugin_filename)
1616
+ if File.exist?(pf)
1617
+ plugin_filename = pf
1618
+ elsif File.exist?(pf + '.rb')
1619
+ plugin_filename = pf + '.rb'
1620
+ end
1621
+ end
1622
+ if not File.exist?(plugin_filename) and File.exist?(plugin_filename + '.rb')
1623
+ plugin_filename += '.rb'
1624
+ end
1625
+
1626
+ instance_eval File.read(plugin_filename)
1627
+ end
1628
+
1629
+ # return the list of memory mappings of the current process
1630
+ # array of [start, len, perms, infos]
1631
+ def mappings
1632
+ [[0, @memory.length]]
1633
+ end
1634
+
1635
+ # return a list of Process::Modules (with a #path, #addr) for the current process
1636
+ def modules
1637
+ []
1638
+ end
1639
+
1640
+ # list debugged pids
1641
+ def list_debug_pids
1642
+ @pid_stuff.keys | [@pid].compact
1643
+ end
1644
+
1645
+ # return a list of OS::Process listing all alive processes (incl not debugged)
1646
+ # default version only includes current debugged pids
1647
+ def list_processes
1648
+ list_debug_pids.map { |p| OS::Process.new(p) }
1649
+ end
1650
+
1651
+ # check if pid is valid
1652
+ def check_pid(pid)
1653
+ list_processes.find { |p| p.pid == pid }
1654
+ end
1655
+
1656
+ # list debugged tids
1657
+ def list_debug_tids
1658
+ @tid_stuff.keys | [@tid].compact
1659
+ end
1660
+
1661
+ # list of thread ids existing in the current process (incl not debugged)
1662
+ # default version only lists debugged tids
1663
+ alias list_threads list_debug_tids
1664
+
1665
+ # check if tid is valid for the current process
1666
+ def check_tid(tid)
1667
+ list_threads.include?(tid)
1668
+ end
1669
+
1670
+ # see EData#pattern_scan
1671
+ # scans only mapped areas of @memory, using os_process.mappings
1672
+ def pattern_scan(pat, start=0, len=@memory.length-start)
1673
+ 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)
1681
+ }
1682
+ }
1683
+ ret
1684
+ end
1685
+ end
1686
+ end