ragweed 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +9 -9
- data/VERSION +1 -1
- data/examples/hittracerx.rb +36 -15
- data/lib/ragweed/debugger32.rb +71 -53
- data/lib/ragweed/debuggerosx.rb +48 -25
- data/lib/ragweed/debuggertux.rb +71 -57
- data/lib/ragweed/rasm/isa.rb +1 -1
- data/lib/ragweed/rasm.rb +0 -5
- data/lib/ragweed/utils.rb +7 -3
- data/lib/ragweed/wrap32/hooks.rb +9 -9
- data/lib/ragweed/wrap32/process.rb +24 -28
- data/lib/ragweed/wrap32/wrap32.rb +7 -3
- data/lib/ragweed/wrap32.rb +0 -3
- data/lib/ragweed/wraposx/constants.rb +11 -5
- data/lib/ragweed/wraposx/kernelerrorx.rb +2 -3
- data/lib/ragweed/wraposx/region_info.rb +4 -4
- data/lib/ragweed/wraposx/thread_context.rb +8 -0
- data/lib/ragweed/wraposx/thread_info.rb +11 -3
- data/lib/ragweed/wraposx/wraposx.rb +3 -2
- data/lib/ragweed/wraptux/constants.rb +81 -80
- data/lib/ragweed/wraptux/process.rb +4 -4
- data/lib/ragweed/wraptux/threads.rb +3 -2
- data/lib/ragweed/wraptux.rb +0 -4
- data/ragweed.gemspec +6 -6
- metadata +5 -7
data/README.rdoc
CHANGED
@@ -4,21 +4,20 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
* Ragweed is a set of scriptable debugging tools written
|
7
|
+
* Ragweed is a set of scriptable debugging tools written in native ruby.
|
8
8
|
|
9
|
-
* Where required the
|
9
|
+
* Where required the FFI and Win32API libraries are used to interface the machine
|
10
10
|
and OS native system calls.
|
11
11
|
|
12
|
+
* There are no third party dependencies
|
13
|
+
|
12
14
|
== Supported Platforms
|
13
15
|
|
14
16
|
Ragweed is supported and has been tested on the following platforms (32bit intel only):
|
15
17
|
|
16
|
-
Windows 7
|
17
|
-
|
18
|
-
|
19
|
-
Linux Ubuntu 9.10
|
20
|
-
Mac OS X 10.6
|
21
|
-
Mac OS X 10.5
|
18
|
+
Windows XP, 7
|
19
|
+
Linux Ubuntu 9.04 -> 11.04
|
20
|
+
Mac OS X 10.5, 10.6
|
22
21
|
|
23
22
|
Ragweed works with Ruby 1.8.7, 1.9.1 and 1.9.2 and should work with any Ruby that includes FFI support on 32bit WinXP, Win7, Linux 2.6 and OSX 10.6.
|
24
23
|
We are actively investigating 64 bit support for each platform. Unfortunately, this will require significant changes to Ragweed.
|
@@ -48,7 +47,8 @@ Please see the examples directory for more. There are hit tracers for each platf
|
|
48
47
|
== INSTALL:
|
49
48
|
|
50
49
|
sudo gem install ragweed
|
51
|
-
|
50
|
+
|
51
|
+
# yep, thats it. you're done.
|
52
52
|
|
53
53
|
== LICENSE:
|
54
54
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/examples/hittracerx.rb
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
# FILE is CSV file,address
|
5
5
|
# PID is the proces id to attach to.
|
6
6
|
|
7
|
-
require '
|
7
|
+
require 'rubygems'
|
8
|
+
require 'ragweed'
|
8
9
|
require 'pp'
|
9
10
|
require 'irb'
|
10
11
|
include Ragweed
|
@@ -12,10 +13,18 @@ include Ragweed
|
|
12
13
|
filename = ARGV[0]
|
13
14
|
pid = ARGV[1].to_i
|
14
15
|
|
15
|
-
raise "
|
16
|
+
raise "hittracerx.rb FILE PID" if (ARGV.size < 2 or pid <= 0)
|
17
|
+
|
18
|
+
class HitTracer < Ragweed::Debuggerosx
|
19
|
+
attr_accessor :counts
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
@counts = Hash.new(0)
|
23
|
+
super
|
24
|
+
end
|
16
25
|
|
17
|
-
class Debuggerosx
|
18
26
|
def on_exit
|
27
|
+
pp @counts
|
19
28
|
exit(1)
|
20
29
|
end
|
21
30
|
|
@@ -25,39 +34,51 @@ class Debuggerosx
|
|
25
34
|
def on_segv(thread)
|
26
35
|
pp self.get_registers(thread)
|
27
36
|
pp self.threads
|
28
|
-
self.threads.each {|thread| puts
|
29
|
-
self.threads.each {|thread| puts Wraposx::
|
37
|
+
self.threads.each {|thread| puts self.get_registers(thread).dump}
|
38
|
+
self.threads.each {|thread| puts Ragweed::Wraposx::thread_info(thread, Ragweed::Wraposx::ThreadInfo::BASIC_INFO).dump}
|
30
39
|
throw(:break)
|
31
40
|
end
|
32
41
|
|
33
42
|
def on_bus(thread)
|
43
|
+
puts "BUS!"
|
34
44
|
throw(:break)
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
38
|
-
d =
|
48
|
+
d = HitTracer.new(pid)
|
39
49
|
d.attach
|
40
50
|
|
51
|
+
puts "attached"
|
52
|
+
|
41
53
|
File.open(filename, "r") do |fd|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
fn,
|
46
|
-
|
54
|
+
fd.each_line do |tl|
|
55
|
+
fn, addr = tl.split(",", 2).map{|x| x.strip}
|
56
|
+
pp [fn, addr.to_i(16).to_s(16)]
|
57
|
+
d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do | tid, regs, slf |
|
58
|
+
puts "#{ slf.breakpoints[regs.eip].first.function } hit in thread #{ tid }\n"
|
59
|
+
d.counts[slf.breakpoints[regs.eip].first.function] += 1
|
60
|
+
end))
|
47
61
|
end
|
48
62
|
end
|
49
63
|
|
64
|
+
puts "breakpoints loaded"
|
65
|
+
|
50
66
|
d.install_bps
|
67
|
+
puts "breakpoints installed"
|
51
68
|
d.continue
|
52
|
-
|
69
|
+
puts "continued"
|
70
|
+
catch(:throw) { d.loop(nil) }
|
71
|
+
puts 'thrown'
|
53
72
|
pp d.wait 1
|
54
73
|
pp d.threads
|
55
74
|
|
56
|
-
d.threads.each do |
|
57
|
-
r =
|
58
|
-
i = Wraposx::ThreadInfo
|
75
|
+
d.threads.each do |tid|
|
76
|
+
r = d.get_registers(tid)
|
77
|
+
i = Wraposx::thread_info(tid, Wraposx::ThreadInfo::BASIC_INFO)
|
59
78
|
pp r
|
60
79
|
puts r.dump
|
61
80
|
pp i
|
62
81
|
puts i.dump
|
63
82
|
end
|
83
|
+
|
84
|
+
|
data/lib/ragweed/debugger32.rb
CHANGED
@@ -12,14 +12,14 @@ require ::File.join(::File.dirname(__FILE__),'wrap32')
|
|
12
12
|
class Ragweed::Debugger32
|
13
13
|
include Ragweed
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
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
18
|
attr_reader :event
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
# Breakpoint class. Handles the actual setting,
|
21
|
+
# removal and triggers for breakpoints.
|
22
|
+
# no user servicable parts.
|
23
23
|
class Breakpoint
|
24
24
|
|
25
25
|
INT3 = 0xCC
|
@@ -64,9 +64,9 @@ class Ragweed::Debugger32
|
|
64
64
|
end
|
65
65
|
|
66
66
|
def call(*args); @callable.call(*args); end
|
67
|
-
end
|
67
|
+
end # End Breakpoint class
|
68
68
|
|
69
|
-
|
69
|
+
# Get a handle to the process so you can mess with it.
|
70
70
|
def process; @p; end
|
71
71
|
|
72
72
|
def self.find_by_regex(rx)
|
@@ -79,7 +79,7 @@ class Ragweed::Debugger32
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def initialize(p)
|
82
|
-
|
82
|
+
# grab debug privilege at least once
|
83
83
|
@@token ||= Ragweed::Wrap32::ProcessToken.new.grant('seDebugPrivilege')
|
84
84
|
|
85
85
|
p = Process.new(p) if p.kind_of? Numeric
|
@@ -96,10 +96,10 @@ class Ragweed::Debugger32
|
|
96
96
|
@ntdll_dbg_break_point = @p.get_proc_remote('ntdll!DbgBreakPoint')
|
97
97
|
end
|
98
98
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
103
|
def step(tid, callable)
|
104
104
|
if @steppers.empty?
|
105
105
|
Ragweed::Wrap32::open_thread(tid) do |h|
|
@@ -111,9 +111,9 @@ class Ragweed::Debugger32
|
|
111
111
|
@steppers << callable
|
112
112
|
end
|
113
113
|
|
114
|
-
|
115
|
-
|
116
|
-
|
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
117
|
def unstep(tid, callable)
|
118
118
|
@steppers = @steppers.reject {|x| x == callable}
|
119
119
|
if @steppers.empty?
|
@@ -125,7 +125,7 @@ class Ragweed::Debugger32
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
-
|
128
|
+
# convenience: either from a TID or a BreakpointEvent, get the thread context.
|
129
129
|
def context(tid_or_event)
|
130
130
|
if not tid_or_event.kind_of? Numeric
|
131
131
|
tid = tid_or_event.tid
|
@@ -135,16 +135,16 @@ class Ragweed::Debugger32
|
|
135
135
|
Ragweed::Wrap32::open_thread(tid) { |h| Ragweed::Wrap32::get_thread_context(h) }
|
136
136
|
end
|
137
137
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
142
|
#
|
143
|
-
|
144
|
-
|
143
|
+
# to handle the breakpoint, pass a block to this method, which will be called
|
144
|
+
# when the breakpoint hits.
|
145
145
|
#
|
146
|
-
|
147
|
-
|
146
|
+
# breakpoints are always re-set after firing. If you don't want them to be
|
147
|
+
# re-set, unset them manually.
|
148
148
|
def breakpoint_set(ip, callable=nil, &block)
|
149
149
|
if not callable and block_given?
|
150
150
|
callable = block
|
@@ -152,7 +152,7 @@ class Ragweed::Debugger32
|
|
152
152
|
|
153
153
|
def_status = false
|
154
154
|
|
155
|
-
|
155
|
+
# This is usually 'Module!Function' or 'Module!0x1234'
|
156
156
|
if @p.is_breakpoint_deferred(ip) == true
|
157
157
|
def_status = true
|
158
158
|
else
|
@@ -160,23 +160,23 @@ class Ragweed::Debugger32
|
|
160
160
|
ip = @p.get_proc_remote(ip)
|
161
161
|
end
|
162
162
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
167
|
if ip == 0 or ip == 0xFFFFFFFF or ip.kind_of? String
|
168
168
|
def_status = true
|
169
169
|
else
|
170
170
|
def_status = false
|
171
171
|
end
|
172
172
|
|
173
|
-
|
173
|
+
# Dont want duplicate breakpoint objects
|
174
174
|
@breakpoints.each_key { |k| if k == ip then return end }
|
175
175
|
bp = Breakpoint.new(@p, ip, def_status, callable)
|
176
176
|
@breakpoints[ip] = bp
|
177
177
|
end
|
178
178
|
|
179
|
-
|
179
|
+
# Clear a breakpoint by ip
|
180
180
|
def breakpoint_clear(ip)
|
181
181
|
bp = @breakpoints[ip]
|
182
182
|
|
@@ -188,8 +188,8 @@ class Ragweed::Debugger32
|
|
188
188
|
@breakpoints.delete(ip)
|
189
189
|
end
|
190
190
|
|
191
|
-
|
192
|
-
|
191
|
+
# handle a breakpoint event:
|
192
|
+
# call handlers for the breakpoint, step past and reset it.
|
193
193
|
def on_breakpoint(ev)
|
194
194
|
ctx = context(ev)
|
195
195
|
eip = ev.exception_address
|
@@ -200,34 +200,34 @@ class Ragweed::Debugger32
|
|
200
200
|
|
201
201
|
@breakpoints[eip].uninstall
|
202
202
|
|
203
|
-
|
204
|
-
|
203
|
+
# Call the block passed to breakpoint_set
|
204
|
+
# which may have been passed through hook()
|
205
205
|
@breakpoints[eip].call(ev, ctx)
|
206
206
|
|
207
|
-
|
207
|
+
# single step past the instruction...
|
208
208
|
step(ev.tid, (onestep = lambda do |ev, ctx|
|
209
209
|
if ev.exception_address != eip
|
210
|
-
|
210
|
+
# ... then re-install the breakpoint ...
|
211
211
|
if not @breakpoints[eip].nil?
|
212
212
|
@breakpoints[eip].install
|
213
213
|
end
|
214
|
-
|
214
|
+
# ... and stop single-stepping.
|
215
215
|
unstep(ev.tid, onestep)
|
216
216
|
end
|
217
217
|
end))
|
218
218
|
|
219
|
-
|
219
|
+
# Put execution back where it's supposed to be...
|
220
220
|
Ragweed::Wrap32::open_thread(ev.tid) do |h|
|
221
221
|
ctx = context(ev)
|
222
222
|
ctx.eip = eip ## eip was ev.exception_address
|
223
223
|
Ragweed::Wrap32::set_thread_context(h, ctx)
|
224
224
|
end
|
225
225
|
|
226
|
-
|
226
|
+
# Tell the target to stop handling this event
|
227
227
|
@handled = Ragweed::Wrap32::ContinueCodes::CONTINUE
|
228
228
|
end
|
229
229
|
|
230
|
-
|
230
|
+
# FIX: this method should be a bit more descriptive in its naming
|
231
231
|
def get_dll_name(ev)
|
232
232
|
name = Ragweed::Wrap32::get_mapped_filename(@p.handle, ev.base_of_dll, 256)
|
233
233
|
name.gsub!(/[\n]+/,'')
|
@@ -264,7 +264,7 @@ class Ragweed::Debugger32
|
|
264
264
|
end
|
265
265
|
end
|
266
266
|
|
267
|
-
|
267
|
+
# handle a single-step event
|
268
268
|
def on_single_step(ev)
|
269
269
|
ctx = context(ev)
|
270
270
|
Ragweed::Wrap32::open_thread(ev.tid) do |h|
|
@@ -279,36 +279,54 @@ class Ragweed::Debugger32
|
|
279
279
|
@handled = Ragweed::Wrap32::ContinueCodes::CONTINUE
|
280
280
|
end
|
281
281
|
|
282
|
-
|
283
|
-
|
284
|
-
|
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
285
|
def on_exit_process(ev)
|
286
286
|
exit(1)
|
287
287
|
end
|
288
288
|
|
289
|
-
|
289
|
+
# @abstract
|
290
290
|
def on_create_process(ev) end
|
291
|
+
# @abstract
|
291
292
|
def on_create_thread(ev) end
|
293
|
+
# @abstract
|
292
294
|
def on_exit_thread(ev) end
|
295
|
+
# @abstract
|
293
296
|
def on_output_debug_string(ev) end
|
297
|
+
# @abstract
|
294
298
|
def on_rip(ev) end
|
299
|
+
# @abstract
|
295
300
|
def on_unload_dll(ev) end
|
301
|
+
# @abstract
|
296
302
|
def on_guard_page(ev) end
|
303
|
+
# @abstract
|
297
304
|
def on_alignment(ev) end
|
305
|
+
# @abstract
|
298
306
|
def on_bounds(ev) end
|
307
|
+
# @abstract
|
299
308
|
def on_divide_by_zero(ev) end
|
309
|
+
# @abstract
|
300
310
|
def on_int_overflow(ev) end
|
311
|
+
# @abstract
|
301
312
|
def on_invalid_handle(ev) end
|
313
|
+
# @abstract
|
302
314
|
def on_illegal_instruction(ev) end
|
315
|
+
# @abstract
|
303
316
|
def on_priv_instruction(ev) end
|
317
|
+
# @abstract
|
304
318
|
def on_stack_overflow(ev) end
|
319
|
+
# @abstract
|
305
320
|
def on_heap_corruption(ev) end
|
321
|
+
# @abstract
|
306
322
|
def on_buffer_overrun(ev) end
|
323
|
+
# @abstract
|
307
324
|
def on_invalid_disposition(ev) end
|
325
|
+
# @abstract
|
308
326
|
def on_attach() end
|
309
327
|
|
310
|
-
|
311
|
-
|
328
|
+
# Read through me to see all the random events
|
329
|
+
# you can hook in a subclass.
|
312
330
|
def wait
|
313
331
|
self.attach() if not @attached
|
314
332
|
|
@@ -370,15 +388,15 @@ class Ragweed::Debugger32
|
|
370
388
|
@handled = Ragweed::Wrap32::ContinueCodes::UNHANDLED
|
371
389
|
end
|
372
390
|
|
373
|
-
|
391
|
+
# Debug loop
|
374
392
|
def loop
|
375
393
|
while true
|
376
394
|
wait
|
377
395
|
end
|
378
396
|
end
|
379
397
|
|
380
|
-
|
381
|
-
|
398
|
+
# This is called implicitly by Debugger#wait.
|
399
|
+
# Attaches to the child process for debugging
|
382
400
|
def attach
|
383
401
|
Ragweed::Wrap32::debug_active_process(@p.pid)
|
384
402
|
Ragweed::Wrap32::debug_set_process_kill_on_exit
|
@@ -389,7 +407,7 @@ class Ragweed::Debugger32
|
|
389
407
|
end
|
390
408
|
end
|
391
409
|
|
392
|
-
|
410
|
+
# Let go of the target.
|
393
411
|
def release
|
394
412
|
Ragweed::Wrap32::debug_active_process_stop(@p.pid)
|
395
413
|
@attached = false
|
data/lib/ragweed/debuggerosx.rb
CHANGED
@@ -21,7 +21,6 @@ class Ragweed::Debuggerosx
|
|
21
21
|
attr_accessor :breakpoints
|
22
22
|
|
23
23
|
class Breakpoint
|
24
|
-
# include Ragweed::Wraposx
|
25
24
|
INT3 = 0xCC
|
26
25
|
attr_accessor :orig
|
27
26
|
attr_accessor :bpid
|
@@ -70,14 +69,14 @@ class Ragweed::Debuggerosx
|
|
70
69
|
def method_missing(meth, *args); @bp.send(meth, *args); end
|
71
70
|
end
|
72
71
|
|
73
|
-
#init object
|
74
|
-
#p: pid of process to be debugged
|
75
|
-
#opts: default options for automatically doing things (attach, install, and hook)
|
72
|
+
# init object
|
73
|
+
# p: pid of process to be debugged
|
74
|
+
# opts: default options for automatically doing things (attach, install, and hook)
|
76
75
|
def initialize(p,opts={})
|
77
76
|
if p.kind_of? Numeric
|
78
77
|
@pid = p
|
79
78
|
else
|
80
|
-
#coming soon: find process by name
|
79
|
+
# coming soon: find process by name
|
81
80
|
raise "Provide a PID"
|
82
81
|
end
|
83
82
|
@opts = opts
|
@@ -97,8 +96,11 @@ class Ragweed::Debuggerosx
|
|
97
96
|
@opts.each {|k, v| try(k) if v}
|
98
97
|
end
|
99
98
|
|
100
|
-
#loop calls to wait
|
101
|
-
#
|
99
|
+
# loop calls to wait.
|
100
|
+
# This is the main mode this class will be used at runtime.
|
101
|
+
# First, install the desired breakpoints. Then, run loop().
|
102
|
+
#
|
103
|
+
# times: number of times to loop
|
102
104
|
# if nil this will loop until @exited is set
|
103
105
|
def loop(times=nil)
|
104
106
|
if times.kind_of? Numeric
|
@@ -111,6 +113,7 @@ class Ragweed::Debuggerosx
|
|
111
113
|
end
|
112
114
|
|
113
115
|
# wait for process and run callback on return then continue child
|
116
|
+
# This is usually called by loop()
|
114
117
|
# FIXME - need to do signal handling better (loop through threads only for breakpoints and stepping)
|
115
118
|
# opts: option flags to waitpid(2)
|
116
119
|
#
|
@@ -165,27 +168,38 @@ class Ragweed::Debuggerosx
|
|
165
168
|
end
|
166
169
|
|
167
170
|
# these event functions are stubs. Implementations should override these
|
171
|
+
|
172
|
+
# Fired when attaching to the child process succeeds.
|
168
173
|
def on_attach
|
169
174
|
end
|
170
175
|
|
176
|
+
# Fired when detaching from the child process succeeds.
|
171
177
|
def on_detach
|
172
178
|
end
|
173
179
|
|
180
|
+
# Fired when single stepping at every step
|
181
|
+
# Not currently used in OSX
|
174
182
|
def on_single_step
|
175
|
-
#puts Ragweed::Wraposx::ThreadInfo.get(thread).inspect
|
176
183
|
end
|
177
184
|
|
185
|
+
# Called with the child process's status on exit
|
186
|
+
# Implementations overriding this function should either set @exited to true or call super.
|
178
187
|
def on_exit(status)
|
179
188
|
@exited = true
|
180
189
|
end
|
181
190
|
|
191
|
+
# Called with the signal used to exit kill the child process
|
192
|
+
# Implementations overriding this function should either set @exited to true or call super.
|
182
193
|
def on_signal(signal)
|
183
194
|
@exited = true
|
184
195
|
end
|
185
196
|
|
197
|
+
# Called when the child process is stopped with the signal used.
|
186
198
|
def on_stop(signal)
|
187
199
|
end
|
188
200
|
|
201
|
+
# Called when the child process is continued.
|
202
|
+
# If used by an implementation, this will be a very noisy function.
|
189
203
|
def on_continue
|
190
204
|
end
|
191
205
|
|
@@ -235,16 +249,19 @@ class Ragweed::Debuggerosx
|
|
235
249
|
# get task port for @pid and store in @task so mach calls can be made
|
236
250
|
# opts is a hash for automatically firing other functions as an overide for @opts
|
237
251
|
# returns the task port for @pid
|
252
|
+
# @deprecated - This function will change to attach_mach and instead become similar to the Debugger32#hook function used for tracing the entry and exit of functions in the child.
|
238
253
|
def hook(opts=@opts)
|
239
254
|
@task = Ragweed::Wraposx::task_for_pid(@pid)
|
240
255
|
@hooked = true
|
241
256
|
self.attach(opts) if opts[:attach] and not @attached
|
242
257
|
return @task
|
243
258
|
end
|
259
|
+
alias attach_mach hook
|
244
260
|
|
245
261
|
# theoretically to close the task port but,
|
246
262
|
# no way to close the port has yet been found.
|
247
263
|
# This function currently does little/nothing.
|
264
|
+
# @deprecated - This will be removed at some point.
|
248
265
|
def unhook(opts=@opts)
|
249
266
|
self.detach(opts) if opts[:attach] and @attached
|
250
267
|
self.unintsall_bps if opts[:install] and @installed
|
@@ -253,14 +270,14 @@ class Ragweed::Debuggerosx
|
|
253
270
|
# resumes thread that has been suspended via thread_suspend
|
254
271
|
# thread: thread id of thread to be resumed
|
255
272
|
def resume(thread = nil)
|
256
|
-
thread ||= self.threads.first
|
273
|
+
thread ||= self.threads.first
|
257
274
|
Ragweed::Wraposx::thread_resume(thread)
|
258
275
|
end
|
259
276
|
|
260
|
-
# suspends thread
|
261
|
-
# thread: thread id of thread to be suspended
|
277
|
+
# suspends thread (increments the suspend count)
|
278
|
+
# thread: thread id of thread to be suspended defaults to first thread
|
262
279
|
def suspend(thread = nil)
|
263
|
-
thread ||= self.threads.first
|
280
|
+
thread ||= self.threads.first
|
264
281
|
Ragweed::Wraposx::thread_suspend(thread)
|
265
282
|
end
|
266
283
|
|
@@ -310,24 +327,24 @@ class Ragweed::Debuggerosx
|
|
310
327
|
# thread: id of the thread stopped at a breakpoint
|
311
328
|
def on_breakpoint(thread)
|
312
329
|
r = self.get_registers(thread)
|
313
|
-
#rewind eip to correct position
|
330
|
+
# rewind eip to correct position
|
314
331
|
r.eip -= 1
|
315
|
-
#don't use r.eip since it may be changed by breakpoint callback
|
332
|
+
# don't use r.eip since it may be changed by breakpoint callback
|
316
333
|
eip = r.eip
|
317
|
-
#clear stuff set by INT3
|
318
|
-
#r.esp -=4
|
319
|
-
#r.ebp = r.esp
|
320
|
-
#fire callback
|
334
|
+
# clear stuff set by INT3
|
335
|
+
# r.esp -=4
|
336
|
+
# r.ebp = r.esp
|
337
|
+
# fire callback
|
321
338
|
@breakpoints[eip].call(thread, r, self)
|
322
339
|
if @breakpoints[eip].first.installed?
|
323
|
-
#uninstall breakpoint to continue past it
|
340
|
+
# uninstall breakpoint to continue past it
|
324
341
|
@breakpoints[eip].first.uninstall
|
325
|
-
#set trap flag so we don't go too far before reinserting breakpoint
|
342
|
+
# set trap flag so we don't go too far before reinserting breakpoint
|
326
343
|
r.eflags |= Ragweed::Wraposx::EFlags::TRAP
|
327
|
-
#set registers to commit eip and eflags changes
|
344
|
+
# set registers to commit eip and eflags changes
|
328
345
|
self.set_registers(thread, r)
|
329
346
|
|
330
|
-
#step once
|
347
|
+
# step once
|
331
348
|
self.stepp
|
332
349
|
|
333
350
|
# now we wait() to prevent a race condition that'll SIGBUS us
|
@@ -335,7 +352,7 @@ class Ragweed::Debuggerosx
|
|
335
352
|
# instruction before the parent completes many
|
336
353
|
Ragweed::Wraposx::waitpid(@pid,0)
|
337
354
|
|
338
|
-
#reset breakpoint
|
355
|
+
# reset the breakpoint
|
339
356
|
@breakpoints[eip].first.install
|
340
357
|
end
|
341
358
|
end
|
@@ -359,7 +376,7 @@ class Ragweed::Debuggerosx
|
|
359
376
|
# returns a Ragweed::Wraposx::ThreadContext object containing the register states
|
360
377
|
# thread: thread to get the register state of
|
361
378
|
def get_registers(thread=nil)
|
362
|
-
thread ||= self.threads.first
|
379
|
+
thread ||= self.threads.first
|
363
380
|
Ragweed::Wraposx.thread_get_state(thread, Ragweed::Wraposx::ThreadContext::X86_THREAD_STATE)
|
364
381
|
end
|
365
382
|
|
@@ -400,6 +417,10 @@ class Ragweed::Debuggerosx
|
|
400
417
|
def attached?; @attached; end
|
401
418
|
def installed?; @installed; end
|
402
419
|
|
420
|
+
# returns information about a memory region
|
421
|
+
# addr: address contained in the memory region - usually the start address
|
422
|
+
# flavor: type of information to retrieve. May be specified as either symbol [:basic, :extended, :top] or by integer flavor id.
|
423
|
+
# Currently, only the basic flavor is supported by Apple.
|
403
424
|
def region_info(addr, flavor = :basic)
|
404
425
|
flav = case flavor
|
405
426
|
when :basic
|
@@ -425,7 +446,9 @@ class Ragweed::Debuggerosx
|
|
425
446
|
end
|
426
447
|
|
427
448
|
# XXX watch this space for an object to hold this information
|
428
|
-
#
|
449
|
+
# Get memory ranges by mapping name
|
450
|
+
# name: name of memory range to search for
|
451
|
+
# exact: if true require an exact match, otherwise use regex
|
429
452
|
def get_mapping_by_name name, exact = true
|
430
453
|
ret = []
|
431
454
|
IO.popen("vmmap -interleaved #{@pid}") do |pipe|
|