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.
- data/History.txt +15 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +30 -0
- data/examples/hittracertux.rb +48 -0
- data/examples/hittracerx.rb +63 -0
- data/examples/hook_notepad.rb +9 -0
- data/examples/snicker.rb +183 -0
- data/examples/tux-example.rb +23 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +338 -0
- data/lib/ragweed/debuggerosx.rb +419 -0
- data/lib/ragweed/debuggertux.rb +347 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm/isa.rb +1046 -0
- data/lib/ragweed/rasm/util.rb +26 -0
- data/lib/ragweed/rasm.rb +53 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +87 -0
- data/lib/ragweed/wrap32/debugging.rb +163 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +23 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +506 -0
- data/lib/ragweed/wrap32/process_token.rb +59 -0
- data/lib/ragweed/wrap32/thread_context.rb +208 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +526 -0
- data/lib/ragweed/wrap32.rb +53 -0
- data/lib/ragweed/wraposx/constants.rb +101 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +244 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +213 -0
- data/lib/ragweed/wraposx/wraposx.rb +376 -0
- data/lib/ragweed/wraposx.rb +53 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +3 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +53 -0
- data/lib/ragweed.rb +84 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_ragweed.rb +0 -0
- 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
|