ragweed 0.2.0-java

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 (54) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +60 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +86 -0
  5. data/VERSION +1 -0
  6. data/examples/hittracertux.rb +45 -0
  7. data/examples/hittracerx.rb +63 -0
  8. data/examples/hook_notepad.rb +9 -0
  9. data/examples/snicker.rb +183 -0
  10. data/examples/tux-example.rb +24 -0
  11. data/lib/ragweed/arena.rb +55 -0
  12. data/lib/ragweed/blocks.rb +128 -0
  13. data/lib/ragweed/debugger32.rb +400 -0
  14. data/lib/ragweed/debuggerosx.rb +456 -0
  15. data/lib/ragweed/debuggertux.rb +502 -0
  16. data/lib/ragweed/detour.rb +223 -0
  17. data/lib/ragweed/ptr.rb +48 -0
  18. data/lib/ragweed/rasm/bblock.rb +73 -0
  19. data/lib/ragweed/rasm/isa.rb +1115 -0
  20. data/lib/ragweed/rasm.rb +59 -0
  21. data/lib/ragweed/sbuf.rb +197 -0
  22. data/lib/ragweed/trampoline.rb +103 -0
  23. data/lib/ragweed/utils.rb +182 -0
  24. data/lib/ragweed/wrap32/debugging.rb +401 -0
  25. data/lib/ragweed/wrap32/device.rb +49 -0
  26. data/lib/ragweed/wrap32/event.rb +50 -0
  27. data/lib/ragweed/wrap32/hooks.rb +39 -0
  28. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  29. data/lib/ragweed/wrap32/process.rb +613 -0
  30. data/lib/ragweed/wrap32/process_token.rb +75 -0
  31. data/lib/ragweed/wrap32/thread_context.rb +142 -0
  32. data/lib/ragweed/wrap32/winx.rb +16 -0
  33. data/lib/ragweed/wrap32/wrap32.rb +583 -0
  34. data/lib/ragweed/wrap32.rb +59 -0
  35. data/lib/ragweed/wraposx/constants.rb +114 -0
  36. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  37. data/lib/ragweed/wraposx/region_info.rb +275 -0
  38. data/lib/ragweed/wraposx/structs.rb +102 -0
  39. data/lib/ragweed/wraposx/thread_context.rb +902 -0
  40. data/lib/ragweed/wraposx/thread_info.rb +160 -0
  41. data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
  42. data/lib/ragweed/wraposx/wraposx.rb +356 -0
  43. data/lib/ragweed/wraposx.rb +60 -0
  44. data/lib/ragweed/wraptux/constants.rb +101 -0
  45. data/lib/ragweed/wraptux/process.rb +35 -0
  46. data/lib/ragweed/wraptux/threads.rb +7 -0
  47. data/lib/ragweed/wraptux/wraptux.rb +72 -0
  48. data/lib/ragweed/wraptux.rb +57 -0
  49. data/lib/ragweed.rb +112 -0
  50. data/ragweed.gemspec +102 -0
  51. data/spec/ragweed_spec.rb +7 -0
  52. data/spec/spec_helper.rb +16 -0
  53. data/test/test_ragweed.rb +0 -0
  54. metadata +121 -0
@@ -0,0 +1,400 @@
1
+ require ::File.join(::File.dirname(__FILE__),'wrap32')
2
+
3
+ # Debugger class for win32
4
+ # You can use this class in 2 ways:
5
+ #
6
+ # (1) You can create instances of Debugger and use them to set and handle
7
+ # breakpoints.
8
+ #
9
+ # (2) If you want to do more advanced event handling, you can subclass from
10
+ # debugger and define your own on_whatever events. If you handle an event
11
+ # that Debugger already handles, call "super", too.
12
+ class Ragweed::Debugger32
13
+ include Ragweed
14
+
15
+ ## This will preserve the last event seen, but as read only
16
+ ## useful if you want to pass around the ragweed object after
17
+ ## an event has occured (post-mortem crash analysis)
18
+ attr_reader :event
19
+
20
+ ## Breakpoint class. Handles the actual setting,
21
+ ## removal and triggers for breakpoints.
22
+ ## no user servicable parts.
23
+ class Breakpoint
24
+
25
+ INT3 = 0xCC
26
+
27
+ attr_accessor :orig, :deferred, :addr
28
+
29
+ def initialize(process, ip, def_status, callable)
30
+ @process = process
31
+ @addr = ip
32
+ @callable = callable
33
+ @deferred = def_status
34
+ @orig = 0
35
+ end
36
+
37
+ def addr; @addr; end
38
+
39
+ def install
40
+ if @addr == 0 or @deferred == true
41
+ return
42
+ end
43
+
44
+ o = @process.read8(@addr)
45
+
46
+ if(orig != INT3)
47
+ @orig = o
48
+ @process.write8(@addr, INT3)
49
+ Ragweed::Wrap32::flush_instruction_cache(@process.handle)
50
+ end
51
+ end
52
+
53
+ def deferred_install(h, base)
54
+ @addr = @process.get_deferred_proc_remote(@addr, h, base)
55
+ self.install
56
+ return @addr
57
+ end
58
+
59
+ def uninstall
60
+ if(@orig != INT3)
61
+ @process.write8(@addr, @orig)
62
+ Ragweed::Wrap32::flush_instruction_cache(@process.handle)
63
+ end
64
+ end
65
+
66
+ def call(*args); @callable.call(*args); end
67
+ end ## End Breakpoint class
68
+
69
+ ## Get a handle to the process so you can mess with it.
70
+ def process; @p; end
71
+
72
+ def self.find_by_regex(rx)
73
+ Ragweed::Wrap32::all_processes do |p|
74
+ if p.szExeFile =~ rx
75
+ return self.new(p.th32ProcessID)
76
+ end
77
+ end
78
+ nil
79
+ end
80
+
81
+ def initialize(p)
82
+ ## grab debug privilege at least once
83
+ @@token ||= Ragweed::Wrap32::ProcessToken.new.grant('seDebugPrivilege')
84
+
85
+ p = Process.new(p) if p.kind_of? Numeric
86
+ @p = p
87
+ @steppers = []
88
+ @handled = Ragweed::Wrap32::ContinueCodes::UNHANDLED
89
+ @attached = false
90
+
91
+ ## breakpoints is a hash with a key being the breakpoint
92
+ ## addr and the value being a Breakpoint class
93
+ @breakpoints = Hash.new
94
+
95
+ ## We want to ignore ntdll!DbgBreakPoint
96
+ @ntdll_dbg_break_point = @p.get_proc_remote('ntdll!DbgBreakPoint')
97
+ end
98
+
99
+ ## single-step the thread (by TID). "callable" is something that honors
100
+ ## .call, like a Proc. In a dubious design decision: the "handle" to the
101
+ ## single stepper is the Proc object itself. See Debugger#on_breakpoint
102
+ ## for an example of how to use this.
103
+ def step(tid, callable)
104
+ if @steppers.empty?
105
+ Ragweed::Wrap32::open_thread(tid) do |h|
106
+ ctx = Ragweed::Wrap32::get_thread_context(h)
107
+ ctx.single_step(true)
108
+ Ragweed::Wrap32::set_thread_context(h, ctx)
109
+ end
110
+ end
111
+ @steppers << callable
112
+ end
113
+
114
+ ## turn off single-stepping for one callable (you can have more than one
115
+ ## at a time). In other words, when you pass a Proc to Debugger#step, save
116
+ ## it somewhere, and later pass it to "unstep" to turn it off.
117
+ def unstep(tid, callable)
118
+ @steppers = @steppers.reject {|x| x == callable}
119
+ if @steppers.empty?
120
+ Ragweed::Wrap32::open_thread(tid) do |h|
121
+ ctx = Ragweed::Wrap32::get_thread_context(h)
122
+ ctx.single_step(false)
123
+ Ragweed::Wrap32::set_thread_context(h, ctx)
124
+ end
125
+ end
126
+ end
127
+
128
+ ## convenience: either from a TID or a BreakpointEvent, get the thread context.
129
+ def context(tid_or_event)
130
+ if not tid_or_event.kind_of? Numeric
131
+ tid = tid_or_event.tid
132
+ else
133
+ tid = tid_or_event
134
+ end
135
+ Ragweed::Wrap32::open_thread(tid) { |h| Ragweed::Wrap32::get_thread_context(h) }
136
+ end
137
+
138
+ ## set a breakpoint given an address, which can also be a string in the form
139
+ ## "module!function", as in, "user32!SendMessageW". Be aware that the symbol
140
+ ## lookup takes place in an injected thread; it's safer to use literal addresses
141
+ ## when possible.
142
+ #
143
+ ## to handle the breakpoint, pass a block to this method, which will be called
144
+ ## when the breakpoint hits.
145
+ #
146
+ ## breakpoints are always re-set after firing. If you don't want them to be
147
+ ## re-set, unset them manually.
148
+ def breakpoint_set(ip, callable=nil, &block)
149
+ if not callable and block_given?
150
+ callable = block
151
+ end
152
+
153
+ def_status = false
154
+
155
+ ## This is usually 'Module!Function' or 'Module!0x1234'
156
+ if @p.is_breakpoint_deferred(ip) == true
157
+ def_status = true
158
+ else
159
+ def_status = false
160
+ ip = @p.get_proc_remote(ip)
161
+ end
162
+
163
+ ## If we cant immediately set the breakpoint
164
+ ## mark it as deferred and wait till later
165
+ ## Sometimes *_proc_remote() will return the
166
+ ## name indicating failure (just in case)
167
+ if ip == 0 or ip == 0xFFFFFFFF or ip.kind_of? String
168
+ def_status = true
169
+ else
170
+ def_status = false
171
+ end
172
+
173
+ ## Dont want duplicate breakpoint objects
174
+ @breakpoints.each_key { |k| if k == ip then return end }
175
+ bp = Breakpoint.new(@p, ip, def_status, callable)
176
+ @breakpoints[ip] = bp
177
+ end
178
+
179
+ ## Clear a breakpoint by ip
180
+ def breakpoint_clear(ip)
181
+ bp = @breakpoints[ip]
182
+
183
+ if bp.nil?
184
+ return nil
185
+ end
186
+
187
+ bp.uninstall
188
+ @breakpoints.delete(ip)
189
+ end
190
+
191
+ ## handle a breakpoint event:
192
+ ## call handlers for the breakpoint, step past and reset it.
193
+ def on_breakpoint(ev)
194
+ ctx = context(ev)
195
+ eip = ev.exception_address
196
+
197
+ if eip == @ntdll_dbg_break_point
198
+ return
199
+ end
200
+
201
+ @breakpoints[eip].uninstall
202
+
203
+ ## Call the block passed to breakpoint_set
204
+ ## which may have been passed through hook()
205
+ @breakpoints[eip].call(ev, ctx)
206
+
207
+ ## single step past the instruction...
208
+ step(ev.tid, (onestep = lambda do |ev, ctx|
209
+ if ev.exception_address != eip
210
+ ## ... then re-install the breakpoint ...
211
+ if not @breakpoints[eip].nil?
212
+ @breakpoints[eip].install
213
+ end
214
+ ## ... and stop single-stepping.
215
+ unstep(ev.tid, onestep)
216
+ end
217
+ end))
218
+
219
+ ## Put execution back where it's supposed to be...
220
+ Ragweed::Wrap32::open_thread(ev.tid) do |h|
221
+ ctx = context(ev)
222
+ ctx.eip = eip ## eip was ev.exception_address
223
+ Ragweed::Wrap32::set_thread_context(h, ctx)
224
+ end
225
+
226
+ ## Tell the target to stop handling this event
227
+ @handled = Ragweed::Wrap32::ContinueCodes::CONTINUE
228
+ end
229
+
230
+ ## FIX: this method should be a bit more descriptive in its naming
231
+ def get_dll_name(ev)
232
+ name = Ragweed::Wrap32::get_mapped_filename(@p.handle, ev.base_of_dll, 256)
233
+ name.gsub!(/[\n]+/,'')
234
+ name.gsub!(/[^\x21-\x7e]/,'')
235
+ i = name.index('0')
236
+ i ||= name.size
237
+ return name[0, i]
238
+ end
239
+
240
+ def on_load_dll(ev)
241
+ dll_name = get_dll_name(ev)
242
+
243
+ @breakpoints.each_pair do |k,bp|
244
+ if !bp.addr.kind_of?String
245
+ next
246
+ end
247
+
248
+ m,f = bp.addr.split('!')
249
+
250
+ if dll_name =~ /#{m}/i
251
+ deferred = bp.deferred
252
+
253
+ if deferred == true
254
+ bp.deferred = false
255
+ end
256
+
257
+ new_addr = bp.deferred_install(ev.file_handle, ev.base_of_dll)
258
+
259
+ if !new_addr.nil?
260
+ @breakpoints[new_addr] = bp.dup
261
+ @breakpoints.delete(k)
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ ## handle a single-step event
268
+ def on_single_step(ev)
269
+ ctx = context(ev)
270
+ Ragweed::Wrap32::open_thread(ev.tid) do |h|
271
+ ## re-enable the trap flag before our handler,
272
+ ## which may choose to disable it.
273
+ ctx.single_step(true)
274
+ Ragweed::Wrap32.set_thread_context(h, ctx)
275
+ end
276
+
277
+ @steppers.each {|s| s.call(ev, ctx)}
278
+
279
+ @handled = Ragweed::Wrap32::ContinueCodes::CONTINUE
280
+ end
281
+
282
+ ## This is sort of insane but most of my programs are just
283
+ ## debug loops, so if you don't do this, they just hang when
284
+ ## the target closes.
285
+ def on_exit_process(ev)
286
+ exit(1)
287
+ end
288
+
289
+ ## TODO: Implement each of these
290
+ def on_create_process(ev) end
291
+ def on_create_thread(ev) end
292
+ def on_exit_thread(ev) end
293
+ def on_output_debug_string(ev) end
294
+ def on_rip(ev) end
295
+ def on_unload_dll(ev) end
296
+ def on_guard_page(ev) end
297
+ def on_alignment(ev) end
298
+ def on_bounds(ev) end
299
+ def on_divide_by_zero(ev) end
300
+ def on_int_overflow(ev) end
301
+ def on_invalid_handle(ev) end
302
+ def on_illegal_instruction(ev) end
303
+ def on_priv_instruction(ev) end
304
+ def on_stack_overflow(ev) end
305
+ def on_heap_corruption(ev) end
306
+ def on_buffer_overrun(ev) end
307
+ def on_invalid_disposition(ev) end
308
+ def on_attach() end
309
+
310
+ ## Read through me to see all the random events
311
+ ## you can hook in a subclass.
312
+ def wait
313
+ self.attach() if not @attached
314
+
315
+ @event = ev = Ragweed::Wrap32::wait_for_debug_event
316
+ return if not ev
317
+ case ev.code
318
+ when Ragweed::Wrap32::DebugCodes::CREATE_PROCESS
319
+ try(:on_create_process, ev)
320
+ when Ragweed::Wrap32::DebugCodes::CREATE_THREAD
321
+ try(:on_create_thread, ev)
322
+ when Ragweed::Wrap32::DebugCodes::EXIT_PROCESS
323
+ try(:on_exit_process, ev)
324
+ when Ragweed::Wrap32::DebugCodes::EXIT_THREAD
325
+ try(:on_exit_thread, ev)
326
+ when Ragweed::Wrap32::DebugCodes::LOAD_DLL
327
+ try(:on_load_dll, ev)
328
+ when Ragweed::Wrap32::DebugCodes::OUTPUT_DEBUG_STRING
329
+ try(:on_output_debug_string, ev)
330
+ when Ragweed::Wrap32::DebugCodes::RIP
331
+ try(:on_rip, ev)
332
+ when Ragweed::Wrap32::DebugCodes::UNLOAD_DLL
333
+ try(:on_unload_dll, ev)
334
+ when Ragweed::Wrap32::DebugCodes::EXCEPTION
335
+ case ev.exception_code
336
+ when Ragweed::Wrap32::ExceptionCodes::ACCESS_VIOLATION
337
+ try(:on_access_violation, ev)
338
+ when Ragweed::Wrap32::ExceptionCodes::GUARD_PAGE
339
+ try(:on_guard_page, ev)
340
+ when Ragweed::Wrap32::ExceptionCodes::BREAKPOINT
341
+ try(:on_breakpoint, ev)
342
+ when Ragweed::Wrap32::ExceptionCodes::ALIGNMENT
343
+ try(:on_alignment, ev)
344
+ when Ragweed::Wrap32::ExceptionCodes::SINGLE_STEP
345
+ try(:on_single_step, ev)
346
+ when Ragweed::Wrap32::ExceptionCodes::BOUNDS
347
+ try(:on_bounds, ev)
348
+ when Ragweed::Wrap32::ExceptionCodes::DIVIDE_BY_ZERO
349
+ try(:on_divide_by_zero, ev)
350
+ when Ragweed::Wrap32::ExceptionCodes::INT_OVERFLOW
351
+ try(:on_int_overflow, ev)
352
+ when Ragweed::Wrap32::ExceptionCodes::INVALID_HANDLE
353
+ try(:on_invalid_handle, ev)
354
+ when Ragweed::Wrap32::ExceptionCodes::ILLEGAL_INSTRUCTION
355
+ try(:on_illegal_instruction, ev)
356
+ when Ragweed::Wrap32::ExceptionCodes::PRIV_INSTRUCTION
357
+ try(:on_priv_instruction, ev)
358
+ when Ragweed::Wrap32::ExceptionCodes::STACK_OVERFLOW
359
+ try(:on_stack_overflow, ev)
360
+ when Ragweed::Wrap32::ExceptionCodes::HEAP_CORRUPTION
361
+ try(:on_heap_corruption, ev)
362
+ when Ragweed::Wrap32::ExceptionCodes::BUFFER_OVERRUN
363
+ try(:on_buffer_overrun, ev)
364
+ when Ragweed::Wrap32::ExceptionCodes::INVALID_DISPOSITION
365
+ try(:on_invalid_disposition, ev)
366
+ end
367
+ end
368
+
369
+ Ragweed::Wrap32::continue_debug_event(ev.pid, ev.tid, @handled)
370
+ @handled = Ragweed::Wrap32::ContinueCodes::UNHANDLED
371
+ end
372
+
373
+ ## Debug loop
374
+ def loop
375
+ while true
376
+ wait
377
+ end
378
+ end
379
+
380
+ ## This is called implicitly by Debugger#wait.
381
+ ## Attaches to the child process for debugging
382
+ def attach
383
+ Ragweed::Wrap32::debug_active_process(@p.pid)
384
+ Ragweed::Wrap32::debug_set_process_kill_on_exit
385
+ @attached = true
386
+ try(:on_attach)
387
+ @breakpoints.each_pair do |k, bp|
388
+ bp.install
389
+ end
390
+ end
391
+
392
+ ## Let go of the target.
393
+ def release
394
+ Ragweed::Wrap32::debug_active_process_stop(@p.pid)
395
+ @attached = false
396
+ @breakpoints.each_pair do |k, bp|
397
+ bp.uninstall
398
+ end
399
+ end
400
+ end