ragweed 0.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|