tduehr-ragweed 0.1.5

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 (62) hide show
  1. data/History.txt +15 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +48 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/arena.rb +55 -0
  11. data/lib/ragweed/blocks.rb +128 -0
  12. data/lib/ragweed/debugger32.rb +338 -0
  13. data/lib/ragweed/debuggerosx.rb +419 -0
  14. data/lib/ragweed/debuggertux.rb +347 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/isa.rb +1046 -0
  18. data/lib/ragweed/rasm/util.rb +26 -0
  19. data/lib/ragweed/rasm.rb +53 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +87 -0
  23. data/lib/ragweed/wrap32/debugging.rb +163 -0
  24. data/lib/ragweed/wrap32/device.rb +49 -0
  25. data/lib/ragweed/wrap32/event.rb +50 -0
  26. data/lib/ragweed/wrap32/hooks.rb +23 -0
  27. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  28. data/lib/ragweed/wrap32/process.rb +506 -0
  29. data/lib/ragweed/wrap32/process_token.rb +59 -0
  30. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  31. data/lib/ragweed/wrap32/winx.rb +16 -0
  32. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  33. data/lib/ragweed/wrap32.rb +53 -0
  34. data/lib/ragweed/wraposx/constants.rb +101 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +244 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +213 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  40. data/lib/ragweed/wraposx.rb +53 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +3 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +53 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. metadata +127 -0
@@ -0,0 +1,338 @@
1
+ require 'ragweed/wrap32'
2
+
3
+ # I am not particularly proud of this code, which I basically debugged
4
+ # into existence, but it does work.
5
+
6
+ # Debugger class for win32
7
+ # You can use this class in 2 ways:
8
+ #
9
+ # (1) You can create instances of Debugger and use them to set and handle
10
+ # breakpoints.
11
+ #
12
+ # (2) If you want to do more advanced event handling, you can subclass from
13
+ # debugger and define your own on_whatever events. If you handle an event
14
+ # that Debugger already handles, call "super", too.
15
+ class Ragweed::Debugger32
16
+ include Ragweed
17
+
18
+ # Breakpoint class. Handles the actual setting, removal and triggers for
19
+ # breakpoints.
20
+ # no user servicable parts.
21
+ class Breakpoint
22
+ INT3 = 0xCC
23
+ attr_accessor :orig
24
+ attr_accessor :bpid
25
+
26
+ def initialize(bp, ip, callable)
27
+ @@bpid ||= 0
28
+ @bp = bp
29
+ @addr = ip
30
+ @callable = callable
31
+ @bpid = (@@bpid += 1)
32
+ end
33
+
34
+ def install
35
+ @orig = process.read8(@addr)
36
+ if(@orig != INT3)
37
+ process.write8(@addr, INT3)
38
+ Wrap32::flush_instruction_cache(@bp.process.handle)
39
+ end
40
+ end
41
+
42
+ def uninstall
43
+ if(@orig != INT3)
44
+ process.write8(@addr, @orig)
45
+ Wrap32::flush_instruction_cache(@bp.process.handle)
46
+ end
47
+ end
48
+
49
+ def call(*args); @callable.call(*args); end
50
+ def method_missing(meth, *args); @bp.send(meth, *args); end
51
+ end
52
+
53
+ # Get a handle to the process so you can mess with it.
54
+ def process; @p; end
55
+
56
+ # This is how you normally create a debug instance: with a regex
57
+ # on the image name of the process.
58
+ # d = Debugger.find_by_regex /notepad/i
59
+ def self.find_by_regex(rx)
60
+ Wrap32::all_processes do |p|
61
+ if p.szExeFile =~ rx
62
+ return self.new(p.th32ProcessID)
63
+ end
64
+ end
65
+ nil
66
+ end
67
+
68
+ # If you want to get one by hand, and not by Debugger#find_by_regex,
69
+ # pass this either a PID or a Process object.
70
+ def initialize(p)
71
+ # grab debug privilege at least once.
72
+ @@token ||= Wrap32::ProcessToken.new.grant("seDebugPrivilege")
73
+
74
+ p = Process.new(p) if p.kind_of? Numeric
75
+ @p = p
76
+ @steppers = []
77
+ @handled = Wrap32::ContinueCodes::UNHANDLED
78
+ @first = true
79
+ @attached = false
80
+
81
+ # for setting breakpoints: inject a thread to do GetProcAddress,
82
+ # cache the result.
83
+ @resolve = Hash.new do |h, k|
84
+ if k.kind_of? String
85
+ ip = @p.get_proc_remote(k)
86
+ else
87
+ ip = k
88
+ end
89
+ raise "no such location" if ip == 0 or ip == 0xFFFFFFFF
90
+ h[k] = ip
91
+ end
92
+
93
+ # the magic initializer here just makes sure the Hash always
94
+ # contains arrays with convenience methods.
95
+ @breakpoints = Hash.new do |h, k|
96
+ bps = Array.new
97
+ def bps.call(*args); each {|bp| bp.call(*args)}; end
98
+ def bps.install; each {|bp| bp.install}; end
99
+ def bps.uninstall; each {|bp| bp.uninstall}; end
100
+ h[k] = bps
101
+ end
102
+ end
103
+
104
+ # single-step the thread (by TID). "callable" is something that honors
105
+ # .call, like a Proc. In a dubious design decision: the "handle" to the
106
+ # single stepper is the Proc object itself. See Debugger#on_breakpoint
107
+ # for an example of how to use this.
108
+ def step(tid, callable)
109
+ if @steppers.empty?
110
+ Wrap32::open_thread(tid) do |h|
111
+ ctx = Wrap32::ThreadContext.get(h)
112
+ ctx.single_step(true)
113
+ ctx.set(h)
114
+ end
115
+ end
116
+ @steppers << callable
117
+ end
118
+
119
+ # turn off single-stepping for one callable (you can have more than one
120
+ # at a time). In other words, when you pass a Proc to Debugger#step, save
121
+ # it somewhere, and later pass it to "unstep" to turn it off.
122
+ def unstep(tid, callable)
123
+ @steppers = @steppers.reject {|x| x == callable}
124
+ if @steppers.empty?
125
+ Wrap32::open_thread(tid) do |h|
126
+ ctx = Wrap32::ThreadContext.get(h)
127
+ ctx.single_step(false)
128
+ ctx.set(h)
129
+ end
130
+ end
131
+ end
132
+
133
+ # convenience: either from a TID or a BreakpointEvent, get the thread context.
134
+ def context(tid_or_event)
135
+ if not tid_or_event.kind_of? Numeric
136
+ tid = tid_or_event.tid
137
+ else
138
+ tid = tid_or_event
139
+ end
140
+ Wrap32::open_thread(tid) {|h| Wrap32::ThreadContext.get(h)}
141
+ end
142
+
143
+ # set a breakpoint given an address, which can also be a string in the form
144
+ # "module!function", as in, "user32!SendMessageW". Be aware that the symbol
145
+ # lookup takes place in an injected thread; it's safer to use literal addresses.
146
+ #
147
+ # to handle the breakpoint, pass a block to this method, which will be called
148
+ # when the breakpoint hits.
149
+ #
150
+ # breakpoints are always re-set after firing. If you don't want them to be
151
+ # re-set, unset them manually.
152
+ #
153
+ # returns a numeric id that can be used to clear the breakpoint.
154
+ def breakpoint_set(ip, callable=nil, &block)
155
+ if not callable and block_given?
156
+ callable = block
157
+ end
158
+ ip = @resolve[ip]
159
+ @breakpoints[ip] << Breakpoint.new(self, ip, callable)
160
+ end
161
+
162
+ # clear a breakpoint given an id, or clear all breakpoints associated with
163
+ # an address.
164
+ def breakpoint_clear(ip, bpid=nil)
165
+ ip = @resolve[ip]
166
+ if not bpid
167
+ @breakpoints[ip].uninstall
168
+ @breakpoints.delete ip
169
+ else
170
+ found = nil
171
+ @breakpoints[ip].each_with_index do |bp, i|
172
+ if bp.bpid == bpid
173
+ found = i
174
+ if bp.orig != Breakpoint::INT3
175
+ if @breakpoints[ip][i+1]
176
+ @breakpoints[ip][i + 1].orig = bp.orig
177
+ else
178
+ bp.uninstall
179
+ end
180
+ end
181
+ end
182
+ end
183
+ raise "couldn't find #{ ip }" if not found
184
+ @breakpoints[ip].delete_at(found) if found
185
+ end
186
+ end
187
+
188
+ # handle a breakpoint event:
189
+ # call handlers for the breakpoint, step past and reset it.
190
+ def on_breakpoint(ev)
191
+ if @first
192
+
193
+ # DbgUiRemoteInjectWhatever actually injects a thread into the
194
+ # target process, which explicitly issues an INT3. We never care
195
+ # about this breakpoint right now.
196
+ @first = false
197
+ else
198
+ ctx = context(ev)
199
+ eip = ev.exception_address
200
+
201
+ # call handlers, then clear the breakpoint so we can execute the
202
+ # real instruction.
203
+ @breakpoints[eip].first.uninstall
204
+ @breakpoints[eip].call(ev, ctx)
205
+
206
+ # single step past the instruction...
207
+ step(ev.tid, (onestep = lambda do |ev, ctx|
208
+ if ev.exception_address != eip
209
+
210
+ # ... then re-install the breakpoint ...
211
+ if not @breakpoints[eip].empty?
212
+ @breakpoints[eip].first.install
213
+ end
214
+
215
+ # ... and stop single-stepping.
216
+ unstep(ev.tid, onestep)
217
+ end
218
+ end))
219
+
220
+ # put execution back where it's supposed to be...
221
+ Wrap32::open_thread(ev.tid) do |h|
222
+ ctx = context(ev)
223
+ ctx.eip = eip # eip was ev.exception_address
224
+ ctx.set(h)
225
+ end
226
+ end
227
+
228
+ # tell the target to stop handling this event
229
+ @handled = Wrap32::ContinueCodes::CONTINUE
230
+ end
231
+
232
+ # handle a single-step event
233
+ def on_single_step(ev)
234
+ ctx = context(ev)
235
+ Wrap32::open_thread(ev.tid) do |h|
236
+ # re-enable the trap flag before our handler, which may
237
+ # choose to disable it.
238
+ ctx.single_step(true)
239
+ ctx.set(h)
240
+ end
241
+
242
+ @steppers.each {|s| s.call(ev, ctx)}
243
+
244
+ @handled = Wrap32::ContinueCodes::CONTINUE
245
+ end
246
+
247
+ # this is sort of insane but most of my programs are just
248
+ # debug loops, so if you don't do this, they just hang when
249
+ # the target closes.
250
+ def on_exit_process(ev)
251
+ exit(1)
252
+ end
253
+
254
+ # Read through me to see all the random events you can hook in
255
+ # a subclass.
256
+ #
257
+ # call me directly if you want to handle multiple debugger instances,
258
+ # i guess.
259
+ def wait
260
+ self.attach() if not @attached
261
+
262
+ ev = Wrap32::wait_for_debug_event
263
+ return if not ev
264
+ case ev.code
265
+ when Wrap32::DebugCodes::CREATE_PROCESS
266
+ try(:on_create_process, ev)
267
+ when Wrap32::DebugCodes::CREATE_THREAD
268
+ try(:on_create_thread, ev)
269
+ when Wrap32::DebugCodes::EXIT_PROCESS
270
+ try(:on_exit_process, ev)
271
+ when Wrap32::DebugCodes::EXIT_THREAD
272
+ try(:on_exit_thread, ev)
273
+ when Wrap32::DebugCodes::LOAD_DLL
274
+ try(:on_load_dll, ev)
275
+ when Wrap32::DebugCodes::OUTPUT_DEBUG_STRING
276
+ try(:on_output_debug_string, ev)
277
+ when Wrap32::DebugCodes::RIP
278
+ try(:on_rip, ev)
279
+ when Wrap32::DebugCodes::UNLOAD_DLL
280
+ try(:on_unload_dll, ev)
281
+ when Wrap32::DebugCodes::EXCEPTION
282
+ case ev.exception_code
283
+ when Wrap32::ExceptionCodes::ACCESS_VIOLATION
284
+ try(:on_access_violation, ev)
285
+ when Wrap32::ExceptionCodes::BREAKPOINT
286
+ try(:on_breakpoint, ev)
287
+ when Wrap32::ExceptionCodes::ALIGNMENT
288
+ try(:on_alignment, ev)
289
+ when Wrap32::ExceptionCodes::SINGLE_STEP
290
+ try(:on_single_step, ev)
291
+ when Wrap32::ExceptionCodes::BOUNDS
292
+ try(:on_bounds, ev)
293
+ when Wrap32::ExceptionCodes::DIVIDE_BY_ZERO
294
+ try(:on_divide_by_zero, ev)
295
+ when Wrap32::ExceptionCodes::INT_OVERFLOW
296
+ try(:on_int_overflow, ev)
297
+ when Wrap32::ExceptionCodes::INVALID_HANDLE
298
+ try(:on_invalid_handle, ev)
299
+ when Wrap32::ExceptionCodes::PRIV_INSTRUCTION
300
+ try(:on_priv_instruction, ev)
301
+ when Wrap32::ExceptionCodes::STACK_OVERFLOW
302
+ try(:on_stack_overflow, ev)
303
+ when Wrap32::ExceptionCodes::INVALID_DISPOSITION
304
+ try(:on_invalid_disposition, ev)
305
+ end
306
+ end
307
+
308
+ Wrap32::continue_debug_event(ev.pid, ev.tid, @handled)
309
+ @handled = Wrap32::ContinueCodes::UNHANDLED
310
+ end
311
+
312
+ # debug loop.
313
+ def loop
314
+ while true
315
+ wait
316
+ end
317
+ end
318
+
319
+ # this is called implicitly by Debugger#wait.
320
+ # Attaches to the child process for debugging
321
+ def attach
322
+ Wrap32::debug_active_process(@p.pid)
323
+ Wrap32::debug_set_process_kill_on_exit
324
+ @attached = true
325
+ @breakpoints.each do |k, v|
326
+ v.install
327
+ end
328
+ end
329
+
330
+ # let go of the target.
331
+ def release
332
+ Wrap32::debug_active_process_stop(@p.pid)
333
+ @attached = false
334
+ @breakpoints.each do |k, v|
335
+ v.uninstall
336
+ end
337
+ end
338
+ end