metasm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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