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.
- data/History.txt +32 -0
- data/README.rdoc +60 -0
- data/README.txt +9 -0
- data/Rakefile +86 -0
- data/VERSION +1 -0
- data/examples/hittracertux.rb +45 -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 +24 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +400 -0
- data/lib/ragweed/debuggerosx.rb +456 -0
- data/lib/ragweed/debuggertux.rb +502 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm/bblock.rb +73 -0
- data/lib/ragweed/rasm/isa.rb +1115 -0
- data/lib/ragweed/rasm.rb +59 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +182 -0
- data/lib/ragweed/wrap32/debugging.rb +401 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +39 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +613 -0
- data/lib/ragweed/wrap32/process_token.rb +75 -0
- data/lib/ragweed/wrap32/thread_context.rb +142 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +583 -0
- data/lib/ragweed/wrap32.rb +59 -0
- data/lib/ragweed/wraposx/constants.rb +114 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +275 -0
- data/lib/ragweed/wraposx/structs.rb +102 -0
- data/lib/ragweed/wraposx/thread_context.rb +902 -0
- data/lib/ragweed/wraposx/thread_info.rb +160 -0
- data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
- data/lib/ragweed/wraposx/wraposx.rb +356 -0
- data/lib/ragweed/wraposx.rb +60 -0
- data/lib/ragweed/wraptux/constants.rb +101 -0
- data/lib/ragweed/wraptux/process.rb +35 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +72 -0
- data/lib/ragweed/wraptux.rb +57 -0
- data/lib/ragweed.rb +112 -0
- data/ragweed.gemspec +102 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/test/test_ragweed.rb +0 -0
- 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
|