ragweed 0.1.7.3 → 0.2.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +33 -8
- data/Rakefile +80 -23
- data/VERSION +1 -0
- data/examples/hittracertux.rb +2 -6
- data/examples/hook_notepad.rb +1 -1
- data/examples/tux-example.rb +3 -2
- data/lib/.DS_Store +0 -0
- data/lib/ragweed/debugger32.rb +188 -145
- data/lib/ragweed/debuggerosx.rb +13 -13
- data/lib/ragweed/debuggertux.rb +267 -140
- data/lib/ragweed/rasm.rb +1 -1
- data/lib/ragweed/wrap32/debugging.rb +184 -64
- data/lib/ragweed/wrap32/hooks.rb +27 -11
- data/lib/ragweed/wrap32/process.rb +114 -7
- data/lib/ragweed/wrap32/process_token.rb +23 -7
- data/lib/ragweed/wrap32/thread_context.rb +100 -166
- data/lib/ragweed/wrap32/wrap32.rb +127 -72
- data/lib/ragweed/wrap32.rb +1 -1
- data/lib/ragweed/wraposx/constants.rb +1 -9
- data/lib/ragweed/wraposx/region_info.rb +209 -188
- data/lib/ragweed/wraposx/structs.rb +102 -0
- data/lib/ragweed/wraposx/thread_context.rb +636 -159
- data/lib/ragweed/wraposx/thread_info.rb +40 -107
- data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
- data/lib/ragweed/wraposx/wraposx.rb +154 -231
- data/lib/ragweed/wraposx.rb +2 -1
- data/lib/ragweed/wraptux/constants.rb +46 -22
- data/lib/ragweed/wraptux/struct_helpers.rb +25 -0
- data/lib/ragweed/wraptux/threads.rb +0 -0
- data/lib/ragweed/wraptux/wraptux.rb +58 -62
- data/lib/ragweed/wraptux.rb +3 -4
- data/lib/ragweed.rb +36 -8
- data/ragweed.gemspec +85 -15
- metadata +50 -18
@@ -1,18 +1,5 @@
|
|
1
1
|
module Ragweed; end
|
2
2
|
module Ragweed::Wraposx::ThreadInfo
|
3
|
-
class << self
|
4
|
-
#factory method to get a ThreadInfo variant
|
5
|
-
def get(flavor,tid)
|
6
|
-
found = false
|
7
|
-
klass = self.constants.detect{|c| con = self.const_get(c); con.kind_of?(Class) && (flavor == con.const_get(:FLAVOR))}
|
8
|
-
if klass.nil?
|
9
|
-
raise Ragwed::Wraposx::KErrno::INVALID_ARGUMENT
|
10
|
-
else
|
11
|
-
klass.get(tid)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
3
|
# info interfaces
|
17
4
|
BASIC_INFO = 3 #basic information
|
18
5
|
|
@@ -22,17 +9,6 @@ module Ragweed::Wraposx::ThreadInfo
|
|
22
9
|
SCHED_RR_INFO = 11
|
23
10
|
# SCHED_FIFO_INFO = 12
|
24
11
|
|
25
|
-
FLAVORS = {
|
26
|
-
# define THREAD_BASIC_INFO_COUNT ((mach_msg_type_number_t)(sizeof(thread_basic_info_data_t) / sizeof(natural_t)))
|
27
|
-
BASIC_INFO => {:size => 30, :count => 10},
|
28
|
-
# define POLICY_TIMESHARE_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_timeshare_info)/sizeof(integer_t)))
|
29
|
-
SCHED_TIMESHARE_INFO => {:size => 20, :count => 5},
|
30
|
-
# define POLICY_RR_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_rr_info)/sizeof(integer_t)))
|
31
|
-
SCHED_RR_INFO => {:size => 20,:count => 5},
|
32
|
-
# define POLICY_FIFO_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_fifo_info)/sizeof(integer_t)))
|
33
|
-
# SCHED_FIFO_INFO => {:size => 16,:count => 4} # immediately returns KERNEL_INVALID_POLICY on osx
|
34
|
-
}
|
35
|
-
|
36
12
|
module State
|
37
13
|
#Thread run states
|
38
14
|
RUNNING = 1 #/* thread is running normally */
|
@@ -42,46 +18,6 @@ module Ragweed::Wraposx::ThreadInfo
|
|
42
18
|
HALTED = 5 #/* thread is halted at a clean point */
|
43
19
|
end
|
44
20
|
|
45
|
-
module ThreadInfoMixins
|
46
|
-
def initialize(str=nil)
|
47
|
-
refresh(str) if (str && !str.empty?)
|
48
|
-
end
|
49
|
-
|
50
|
-
# (re)loads the data from str
|
51
|
-
def refresh(str)
|
52
|
-
fields = self.class.const_get :FIELDS
|
53
|
-
pp self.class
|
54
|
-
if str and not str.empty?
|
55
|
-
str.unpack(fields.map {|x| x[1]}.join("")).each_with_index do |val, i|
|
56
|
-
raise "i is nil" if i.nil?
|
57
|
-
instance_variable_set "@#{ fields[i][0] }".intern, val
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def to_s
|
63
|
-
fields = self.class.const_get :FIELDS
|
64
|
-
fields.map {|f| send(f[0])}.pack(fields.map {|x| x[1]}.join(""))
|
65
|
-
end
|
66
|
-
|
67
|
-
def inspect
|
68
|
-
body = lambda do
|
69
|
-
self.class.const_get(:FIELDS).map do |f|
|
70
|
-
"#{f[0]}=#{send(f[0]).to_s}"
|
71
|
-
end.join(", ")
|
72
|
-
end
|
73
|
-
"#<#{self.class.name.split('::').last(2).join('::')} #{body.call}>"
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.get(t)
|
77
|
-
self.new(Ragweed::Wraposx::thread_info_raw(t, self.class.const_get(:FLAVOR)))
|
78
|
-
end
|
79
|
-
|
80
|
-
def get(t)
|
81
|
-
refresh(Ragweed::Wraposx::vm_region_raw(t, self.class.const_get(:FLAVOR)))
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
21
|
# struct thread_basic_info
|
86
22
|
# {
|
87
23
|
# time_value_t user_time;
|
@@ -93,35 +29,23 @@ module Ragweed::Wraposx::ThreadInfo
|
|
93
29
|
# integer_t suspend_count;
|
94
30
|
# integer_t sleep_time;
|
95
31
|
# };
|
96
|
-
class Basic
|
97
|
-
include Ragweed::
|
32
|
+
class Basic < FFI::Struct
|
33
|
+
include Ragweed::FFIStructInclude
|
98
34
|
module Flags
|
99
35
|
#Thread flags (flags field).
|
100
36
|
SWAPPED = 0x1 #/* thread is swapped out */
|
101
37
|
IDLE = 0x2 #/* thread is an idle thread */
|
102
38
|
end
|
103
|
-
|
104
|
-
attr_accessor :user_time
|
105
|
-
attr_accessor :system_time
|
106
|
-
alias_method :__refresh, :refresh
|
107
|
-
(FIELDS = [ [:user_time_s, "I"],
|
108
|
-
[:user_time_us, "I"],
|
109
|
-
[:system_time_s, "I"],
|
110
|
-
[:system_time_us, "I"],
|
111
|
-
[:cpu_usage, "I"],
|
112
|
-
[:policy, "I"],
|
113
|
-
[:run_state, "I"],
|
114
|
-
[:flags, "I"],
|
115
|
-
[:suspend_count, "I"],
|
116
|
-
[:sleep_time, "I"]]).each {|x| attr_accessor x[0]}
|
117
39
|
|
118
40
|
FLAVOR = Ragweed::Wraposx::ThreadInfo::BASIC_INFO
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
41
|
+
layout :user_time, Ragweed::Wraposx::TimeValue,
|
42
|
+
:system_time, Ragweed::Wraposx::TimeValue,
|
43
|
+
:cpu_usage, :int,
|
44
|
+
:policy, Ragweed::Wraposx::Libc.find_type(:policy_t),
|
45
|
+
:run_state, :int,
|
46
|
+
:flags, :int,
|
47
|
+
:suspend_count, :int,
|
48
|
+
:sleep_time, :int
|
125
49
|
|
126
50
|
def dump(&block)
|
127
51
|
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
@@ -150,13 +74,12 @@ EOM
|
|
150
74
|
# boolean_t depressed;
|
151
75
|
# int depress_priority;
|
152
76
|
# };
|
153
|
-
class SchedTimeshare
|
154
|
-
include Ragweed::
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
[:depress_priority, "I"]]).each {|x| attr_accessor x[0]}
|
77
|
+
class SchedTimeshare < FFI::Struct
|
78
|
+
include Ragweed::FFIStructInclude
|
79
|
+
layout :max_priority, :int,
|
80
|
+
:base_priority, :int,
|
81
|
+
:cur_priority, :int,
|
82
|
+
:depress_priority, :int
|
160
83
|
|
161
84
|
def dump(&block)
|
162
85
|
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
@@ -181,13 +104,13 @@ EOM
|
|
181
104
|
# boolean_t depressed;
|
182
105
|
# int depress_priority;
|
183
106
|
# };
|
184
|
-
class SchedRr
|
185
|
-
include Ragweed::
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
107
|
+
class SchedRr < FFI::Struct
|
108
|
+
include Ragweed::FFIStructInclude
|
109
|
+
layout :max_priority, :int,
|
110
|
+
:base_priority, :int,
|
111
|
+
:quantum, :int,
|
112
|
+
:depressed, Ragweed::Wraposx::Libc.find_type(:boolean_t),
|
113
|
+
:depress_priority, :int
|
191
114
|
|
192
115
|
def dump(&block)
|
193
116
|
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
@@ -201,8 +124,19 @@ EOM
|
|
201
124
|
depressed: #{(!self.depressed.zero?).to_s.rjust(8, " ")} #{maybe_hex.call(self.depressed)}
|
202
125
|
depress_priority: #{self.depress_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.depressed_priority)}
|
203
126
|
EOM
|
127
|
+
end
|
204
128
|
end
|
205
|
-
|
129
|
+
|
130
|
+
FLAVORS = {
|
131
|
+
# define THREAD_BASIC_INFO_COUNT ((mach_msg_type_number_t)(sizeof(thread_basic_info_data_t) / sizeof(natural_t)))
|
132
|
+
BASIC_INFO => {:size => 30, :count => 10, :class => Basic},
|
133
|
+
# define POLICY_TIMESHARE_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_timeshare_info)/sizeof(integer_t)))
|
134
|
+
SCHED_TIMESHARE_INFO => {:size => 20, :count => 5, :class => SchedTimeshare},
|
135
|
+
# define POLICY_RR_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_rr_info)/sizeof(integer_t)))
|
136
|
+
SCHED_RR_INFO => {:size => 20,:count => 5, :class => SchedRr},
|
137
|
+
# define POLICY_FIFO_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_fifo_info)/sizeof(integer_t)))
|
138
|
+
# SCHED_FIFO_INFO => {:size => 16,:count => 4} # immediately returns KERNEL_INVALID_POLICY on osx
|
139
|
+
}
|
206
140
|
end
|
207
141
|
|
208
142
|
module Ragweed::Wraposx
|
@@ -215,13 +149,12 @@ module Ragweed::Wraposx
|
|
215
149
|
# thread_flavor_t flavor,
|
216
150
|
# thread_info_t thread_info,
|
217
151
|
# mach_msg_type_number_t thread_info_count);
|
218
|
-
def
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
r = CALLS["libc!thread_info:IIPP=I"].call(thread,flavor,info,Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:count]).first
|
152
|
+
def thread_info(thread, flavor)
|
153
|
+
info = FFI::MemoryPointer.new(ThreadInfo::FLAVORS[flavor][:class], 1)
|
154
|
+
count = FFI::MemoryPointer.new(:int, 1).write_int(ThreadInfo::FLAVORS[flavor][:count])
|
155
|
+
r = Libc.thread_info(thread, flavor, info, count)
|
223
156
|
raise KernelCallError.new(r) if r != 0
|
224
|
-
|
157
|
+
ThreadInfo::FLAVORS[flavor][:class].new info
|
225
158
|
end
|
226
159
|
end
|
227
160
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Ragweed; end
|
2
|
+
module Ragweed::Wraposx::ThreadState
|
3
|
+
#Thread run states
|
4
|
+
RUNNING = 1 #/* thread is running normally */
|
5
|
+
STOPPED = 2 #/* thread is stopped */
|
6
|
+
WAITING = 3 #/* thread is waiting normally */
|
7
|
+
UNINTERRUPTIBLE = 4 #/* thread is in an uninterruptible wait */
|
8
|
+
HALTED = 5 #/* thread is halted at a clean point */
|
9
|
+
end
|
10
|
+
|
11
|
+
module Ragweed::Wraposx::TFlags
|
12
|
+
#Thread flags (flags field).
|
13
|
+
SWAPPED = 0x1 #/* thread is swapped out */
|
14
|
+
IDLE = 0x2 #/* thread is an idle thread */
|
15
|
+
end
|
16
|
+
|
17
|
+
class Ragweed::Wraposx::ThreadInfo
|
18
|
+
include Ragweed
|
19
|
+
attr_accessor :user_time
|
20
|
+
attr_accessor :system_time
|
21
|
+
(FIELDS = [ [:user_time_s, "I"],
|
22
|
+
[:user_time_us, "I"],
|
23
|
+
[:system_time_s, "I"],
|
24
|
+
[:system_time_us, "I"],
|
25
|
+
[:cpu_usage, "I"],
|
26
|
+
[:policy, "I"],
|
27
|
+
[:run_state, "I"],
|
28
|
+
[:flags, "I"],
|
29
|
+
[:suspend_count, "I"],
|
30
|
+
[:sleep_time, "I"]]).each {|x| attr_accessor x[0]}
|
31
|
+
|
32
|
+
def initialize(str=nil)
|
33
|
+
refresh(str) if str
|
34
|
+
end
|
35
|
+
|
36
|
+
#(re)loads the data from str
|
37
|
+
def refresh(str)
|
38
|
+
if str and not str.empty?
|
39
|
+
str.unpack(FIELDS.map {|x| x[1]}.join("")).each_with_index do |val, i|
|
40
|
+
raise "i is nil" if i.nil?
|
41
|
+
instance_variable_set "@#{ FIELDS[i][0] }".intern, val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@user_time = @user_time_s + (@user_time_us/1000000.0)
|
45
|
+
@system_time = @system_time_s + (@system_time_us/1000000.0)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
FIELDS.map {|f| send(f[0])}.pack(FIELDS.map {|x| x[1]}.join(""))
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.get(t)
|
53
|
+
self.new(Wraposx::thread_info_raw(t))
|
54
|
+
end
|
55
|
+
|
56
|
+
def get(t)
|
57
|
+
refresh(Wraposx::thread_info_raw(t))
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
body = lambda do
|
62
|
+
FIELDS.map do |f|
|
63
|
+
"#{f[0]}=#{send(f[0]).to_s}"
|
64
|
+
end.join(", ")
|
65
|
+
end
|
66
|
+
"#<ThreadInfo #{body.call}>"
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump(&block)
|
70
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
71
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
72
|
+
|
73
|
+
string =<<EOM
|
74
|
+
-----------------------------------------------------------------------
|
75
|
+
INFO:
|
76
|
+
user_time: #{self.user_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.user_time)}
|
77
|
+
system_time: #{self.system_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.system_time)}
|
78
|
+
cpu_usage: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cpu_usage)}
|
79
|
+
policy: #{self.policy.to_s.rjust(8, "0")} #{maybe_hex.call(self.policy)}
|
80
|
+
run_state: #{self.run_state.to_s.rjust(8, "0")} #{maybe_hex.call(self.run_state)}
|
81
|
+
suspend_count: #{self.suspend_count.to_s.rjust(8, "0")} #{maybe_hex.call(self.suspend_count)}
|
82
|
+
sleep_time: #{self.sleep_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.sleep_time)}
|
83
|
+
flags: #{self.flags.to_s(2).rjust(32, "0")} #{Wraposx::TFlags.flag_dump(self.flags)}
|
84
|
+
EOM
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module Ragweed::Wraposx
|
89
|
+
|
90
|
+
# FIXME - constants should be under separate sub-modules
|
91
|
+
# XXX - implement more thread info flavors (if possible)
|
92
|
+
# XXX - move to class based implementation a la region_info
|
93
|
+
# info interfaces
|
94
|
+
THREAD_BASIC_INFO = 3 #basic information
|
95
|
+
|
96
|
+
# following are obsolete interfaces
|
97
|
+
THREAD_SCHED_TIMESHARE_INFO = 10
|
98
|
+
THREAD_SCHED_RR_INFO = 11
|
99
|
+
THREAD_SCHED_FIFO_INFO = 12
|
100
|
+
|
101
|
+
# define THREAD_BASIC_INFO_COUNT ((mach_msg_type_number_t)(sizeof(thread_basic_info_data_t) / sizeof(natural_t)))
|
102
|
+
# the two time fields are each two ints
|
103
|
+
THREAD_BASIC_INFO_COUNT = 10
|
104
|
+
|
105
|
+
class << self
|
106
|
+
|
107
|
+
# Returns the packed string representation of the thread_info_t struct for later parsing.
|
108
|
+
# kern_return_t thread_info
|
109
|
+
# (thread_act_t target_thread,
|
110
|
+
# thread_flavor_t flavor,
|
111
|
+
# thread_info_t thread_info,
|
112
|
+
# mach_msg_type_number_t thread_info_count);
|
113
|
+
def thread_info_raw(thread)
|
114
|
+
info = ("\x00"*1024).to_ptr
|
115
|
+
count = ([THREAD_BASIC_INFO_COUNT].pack("I_")).to_ptr
|
116
|
+
r = CALLS["libc!thread_info:IIPP=I"].call(thread,THREAD_BASIC_INFO,info,count).first
|
117
|
+
raise KernelCallError.new(:thread_info, r) if r != 0
|
118
|
+
return info.to_s(SIZEOFINT*THREAD_BASIC_INFO_COUNT)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|