ragweed 0.2.3-java → 0.2.4-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/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 +2 -2
- metadata +2 -2
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|
|