ragweed 0.1.7.2
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 +35 -0
- data/README.txt +9 -0
- data/Rakefile +34 -0
- data/examples/hittracertux.rb +49 -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 +23 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +338 -0
- data/lib/ragweed/debuggerosx.rb +427 -0
- data/lib/ragweed/debuggertux.rb +346 -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 +156 -0
- data/lib/ragweed/wrap32/debugging.rb +163 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +23 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +506 -0
- data/lib/ragweed/wrap32/process_token.rb +59 -0
- data/lib/ragweed/wrap32/thread_context.rb +208 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +526 -0
- data/lib/ragweed/wrap32.rb +59 -0
- data/lib/ragweed/wraposx/constants.rb +122 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +254 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +227 -0
- data/lib/ragweed/wraposx/wraposx.rb +433 -0
- data/lib/ragweed/wraposx.rb +59 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +59 -0
- data/lib/ragweed.rb +84 -0
- data/ragweed.gemspec +34 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_ragweed.rb +0 -0
- metadata +132 -0
@@ -0,0 +1,227 @@
|
|
1
|
+
module Ragweed; end
|
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
|
+
# info interfaces
|
17
|
+
BASIC_INFO = 3 #basic information
|
18
|
+
|
19
|
+
# following are obsolete interfaces
|
20
|
+
# according to the source @ fxr they still work except FIFO
|
21
|
+
SCHED_TIMESHARE_INFO = 10
|
22
|
+
SCHED_RR_INFO = 11
|
23
|
+
# SCHED_FIFO_INFO = 12
|
24
|
+
|
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
|
+
module State
|
37
|
+
#Thread run states
|
38
|
+
RUNNING = 1 #/* thread is running normally */
|
39
|
+
STOPPED = 2 #/* thread is stopped */
|
40
|
+
WAITING = 3 #/* thread is waiting normally */
|
41
|
+
UNINTERRUPTIBLE = 4 #/* thread is in an uninterruptible wait */
|
42
|
+
HALTED = 5 #/* thread is halted at a clean point */
|
43
|
+
end
|
44
|
+
|
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
|
+
# struct thread_basic_info
|
86
|
+
# {
|
87
|
+
# time_value_t user_time;
|
88
|
+
# time_value_t system_time;
|
89
|
+
# integer_t cpu_usage;
|
90
|
+
# policy_t policy;
|
91
|
+
# integer_t run_state;
|
92
|
+
# integer_t flags;
|
93
|
+
# integer_t suspend_count;
|
94
|
+
# integer_t sleep_time;
|
95
|
+
# };
|
96
|
+
class Basic
|
97
|
+
include Ragweed::Wraposx::ThreadInfo::ThreadInfoMixins
|
98
|
+
module Flags
|
99
|
+
#Thread flags (flags field).
|
100
|
+
SWAPPED = 0x1 #/* thread is swapped out */
|
101
|
+
IDLE = 0x2 #/* thread is an idle thread */
|
102
|
+
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
|
+
|
118
|
+
FLAVOR = Ragweed::Wraposx::ThreadInfo::BASIC_INFO
|
119
|
+
#(re)loads the data from str
|
120
|
+
def refresh(str)
|
121
|
+
__refresh str
|
122
|
+
@user_time = @user_time_s + (@user_time_us/1000000.0)
|
123
|
+
@system_time = @system_time_s + (@system_time_us/1000000.0)
|
124
|
+
end
|
125
|
+
|
126
|
+
def dump(&block)
|
127
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
128
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
129
|
+
|
130
|
+
string =<<EOM
|
131
|
+
-----------------------------------------------------------------------
|
132
|
+
INFO:
|
133
|
+
user_time: #{self.user_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.user_time)}
|
134
|
+
system_time: #{self.system_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.system_time)}
|
135
|
+
cpu_usage: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cpu_usage)}
|
136
|
+
policy: #{self.policy.to_s.rjust(8, "0")} #{maybe_hex.call(self.policy)}
|
137
|
+
run_state: #{self.run_state.to_s.rjust(8, "0")} #{maybe_hex.call(self.run_state)}
|
138
|
+
suspend_count: #{self.suspend_count.to_s.rjust(8, "0")} #{maybe_hex.call(self.suspend_count)}
|
139
|
+
sleep_time: #{self.sleep_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.sleep_time)}
|
140
|
+
flags: #{self.flags.to_s(2).rjust(32, "0")} #{Flags.flag_dump(self.flags)}
|
141
|
+
EOM
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# struct policy_timeshare_info
|
146
|
+
# {
|
147
|
+
# int max_priority;
|
148
|
+
# int base_priority;
|
149
|
+
# int cur_priority;
|
150
|
+
# boolean_t depressed;
|
151
|
+
# int depress_priority;
|
152
|
+
# };
|
153
|
+
class SchedTimeshare
|
154
|
+
include Ragweed::Wraposx::ThreadInfo::ThreadInfoMixins
|
155
|
+
(FIELDS = [ [:max_priority, "I"],
|
156
|
+
[:base_priority, "I"],
|
157
|
+
[:cur_priority, "I"],
|
158
|
+
[:depressed, "I"],
|
159
|
+
[:depress_priority, "I"]]).each {|x| attr_accessor x[0]}
|
160
|
+
|
161
|
+
def dump(&block)
|
162
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
163
|
+
|
164
|
+
string =<<EOM
|
165
|
+
-----------------------------------------------------------------------
|
166
|
+
Timeshare Info:
|
167
|
+
max_priority: #{self.max_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.max_priority)}
|
168
|
+
base_priority: #{self.base_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.base_priority)}
|
169
|
+
cur_priority: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cur_priority)}
|
170
|
+
depressed: #{(!self.depressed.zero?).to_s.rjust(8, " ")} #{maybe_hex.call(self.depressed)}
|
171
|
+
depress_priority: #{self.depress_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.depressed_priority)}
|
172
|
+
EOM
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# struct policy_rr_info
|
177
|
+
# {
|
178
|
+
# int max_priority;
|
179
|
+
# int base_priority;
|
180
|
+
# int quantum;
|
181
|
+
# boolean_t depressed;
|
182
|
+
# int depress_priority;
|
183
|
+
# };
|
184
|
+
class SchedRr
|
185
|
+
include Ragweed::Wraposx::ThreadInfo::ThreadInfoMixins
|
186
|
+
(FIELDS = [ [:max_priority, "I"],
|
187
|
+
[:base_priority, "I"],
|
188
|
+
[:quantum, "I"],
|
189
|
+
[:depressed, "I"],
|
190
|
+
[:depress_priority, "I"]]).each {|x| attr_accessor x[0]}
|
191
|
+
|
192
|
+
def dump(&block)
|
193
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
194
|
+
|
195
|
+
string =<<EOM
|
196
|
+
-----------------------------------------------------------------------
|
197
|
+
Round Robin Info:
|
198
|
+
max_priority: #{self.max_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.max_priority)}
|
199
|
+
base_priority: #{self.base_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.base_priority)}
|
200
|
+
quantum: #{self.quantum.to_s.rjust(8, "0")} #{maybe_hex.call(self.quantum)}
|
201
|
+
depressed: #{(!self.depressed.zero?).to_s.rjust(8, " ")} #{maybe_hex.call(self.depressed)}
|
202
|
+
depress_priority: #{self.depress_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.depressed_priority)}
|
203
|
+
EOM
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
module Ragweed::Wraposx
|
209
|
+
class << self
|
210
|
+
|
211
|
+
# Returns the packed string representation of the thread_info_t struct for later parsing.
|
212
|
+
#
|
213
|
+
# kern_return_t thread_info
|
214
|
+
# (thread_act_t target_thread,
|
215
|
+
# thread_flavor_t flavor,
|
216
|
+
# thread_info_t thread_info,
|
217
|
+
# mach_msg_type_number_t thread_info_count);
|
218
|
+
def thread_info_raw(thread, flavor)
|
219
|
+
raise KErrno::INVALID_ARGUMENT if ThreadInfo::FLAVORS[flavor].nil?
|
220
|
+
info = ("\x00"*ThreadInfo::FLAVORS[flavor][:size]).to_ptr
|
221
|
+
count = ([Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:count]].pack("I_")).to_ptr
|
222
|
+
r = CALLS["libc!thread_info:IIPP=I"].call(thread,flavor,info,Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:count]).first
|
223
|
+
raise KernelCallError.new(r) if r != 0
|
224
|
+
return "#{info.to_s(ThreadInfo::FLAVORS[flavor][:size])}#{count.to_s(SizeOf::INT)}"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,433 @@
|
|
1
|
+
require 'dl'
|
2
|
+
|
3
|
+
module Ragweed; end
|
4
|
+
module Ragweed::Wraposx
|
5
|
+
|
6
|
+
# These hashes are the magic glue of the ragweed system calls.
|
7
|
+
# This one holds the library references from Ruby/DL.
|
8
|
+
LIBS = Hash.new do |h, str|
|
9
|
+
if not str =~ /^[\.\/].*/
|
10
|
+
str = "/usr/lib/" + str
|
11
|
+
end
|
12
|
+
if not str =~ /.*\.dylib$/
|
13
|
+
str = str + ".dylib"
|
14
|
+
end
|
15
|
+
h[str] = DL.dlopen(str)
|
16
|
+
end
|
17
|
+
|
18
|
+
# This hash holds the function references from Ruby/DL.
|
19
|
+
# It also auto populates LIBS.
|
20
|
+
# CALLS["<library>!<function>:<argument types>=<return type>"]
|
21
|
+
# Hash.new is a beautiful thing.
|
22
|
+
CALLS = Hash.new do |h, str|
|
23
|
+
lib = proc = args = ret = nil
|
24
|
+
lib, rest = str.split "!"
|
25
|
+
proc, rest = rest.split ":"
|
26
|
+
args, ret = rest.split("=") if rest
|
27
|
+
ret ||= "0"
|
28
|
+
raise "need proc" if not proc
|
29
|
+
h[str] = LIBS[lib][proc, ret + args]
|
30
|
+
end
|
31
|
+
|
32
|
+
NULL = DL::PtrData.new(0)
|
33
|
+
|
34
|
+
SIZEOFINT = DL.sizeof('I')
|
35
|
+
SIZEOFLONG = DL.sizeof('L')
|
36
|
+
|
37
|
+
class << self
|
38
|
+
|
39
|
+
# time_t
|
40
|
+
# time(time_t *tloc);
|
41
|
+
#
|
42
|
+
# see also time(3)
|
43
|
+
def time
|
44
|
+
CALLS["libc!time:=I"].call.first
|
45
|
+
end
|
46
|
+
|
47
|
+
# pid_t
|
48
|
+
# getpid(void);
|
49
|
+
#
|
50
|
+
# see also getpid(2)
|
51
|
+
def getpid
|
52
|
+
CALLS["libc!getpid:=I"].call.first
|
53
|
+
end
|
54
|
+
|
55
|
+
# Apple's ptrace is fairly gimped. The memory read and write functionality has been
|
56
|
+
# removed. We will be using mach kernel calls for that. see vm_read and vm_write.
|
57
|
+
# for details on ptrace and the process for the Wraposx/debuggerosx port see:
|
58
|
+
# http://www.matasano.com/log/1100/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt/
|
59
|
+
#
|
60
|
+
#int
|
61
|
+
#ptrace(int request, pid_t pid, caddr_t addr, int data);
|
62
|
+
#
|
63
|
+
# see also ptrace(2)
|
64
|
+
def ptrace(request, pid, addr, data)
|
65
|
+
DL.last_error = 0
|
66
|
+
r = CALLS["libc!ptrace:IIII=I"].call(request, pid, addr, data).first
|
67
|
+
raise SystemCallError.new("ptrace", DL.last_error) if r == -1 and DL.last_error != 0
|
68
|
+
return r
|
69
|
+
end
|
70
|
+
|
71
|
+
# Oringially coded for use in debuggerosx but I've switched to waitpid for
|
72
|
+
# usability and debugging purposes.
|
73
|
+
#
|
74
|
+
# Returns status of child when child recieves a signal.
|
75
|
+
#
|
76
|
+
# pid_t
|
77
|
+
# wait(int *stat_loc);
|
78
|
+
#
|
79
|
+
# see also wait(2)
|
80
|
+
def wait
|
81
|
+
status = ("\x00"*SIZEOFINT).to_ptr
|
82
|
+
r = CALLS["libc!wait:=I"].call(status).first
|
83
|
+
raise SystemCallError.new("wait", DL.last_error) if r== -1
|
84
|
+
return status.to_s(SIZEOFINT).unpack('i_').first
|
85
|
+
end
|
86
|
+
|
87
|
+
# The wait used in debuggerosx.
|
88
|
+
# opt is an OR of the options to be used.
|
89
|
+
#
|
90
|
+
# Returns an array. The first element is the pid of the child process
|
91
|
+
# as returned by the waitpid system call. The second, the status as
|
92
|
+
# an integer of that pid.
|
93
|
+
#
|
94
|
+
# pid_t
|
95
|
+
# waitpid(pid_t pid, int *stat_loc, int options);
|
96
|
+
#
|
97
|
+
# see also wait(2)
|
98
|
+
def waitpid(pid, opt=1)
|
99
|
+
pstatus = ("\x00"*SIZEOFINT).to_ptr
|
100
|
+
r = CALLS["libc!waitpid:IPI=I"].call(pid, pstatus, opt).first
|
101
|
+
raise SystemCallError.new("waitpid", DL.last_error) if r== -1
|
102
|
+
|
103
|
+
# maybe I should return a Hash?
|
104
|
+
return [r, pstatus.to_s(SIZEOFINT).unpack('i_').first]
|
105
|
+
end
|
106
|
+
|
107
|
+
# From docs at http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_task_self.html
|
108
|
+
# Returns send rights to the task's kernel port.
|
109
|
+
#
|
110
|
+
# mach_port_t
|
111
|
+
# mach_task_self(void)
|
112
|
+
#
|
113
|
+
# There is no man page for this call.
|
114
|
+
def mach_task_self
|
115
|
+
CALLS["libc!mach_task_self:=I"].call().first
|
116
|
+
end
|
117
|
+
|
118
|
+
# Requires sudo to use as of 10.5 or 10.4.11(ish)
|
119
|
+
# Returns the task id for a process.
|
120
|
+
#
|
121
|
+
# kern_return_t task_for_pid(
|
122
|
+
# mach_port_name_t target_tport,
|
123
|
+
# int pid,
|
124
|
+
# mach_port_name_t *t);
|
125
|
+
#
|
126
|
+
# There is no man page for this call.
|
127
|
+
def task_for_pid(pid, target=nil)
|
128
|
+
target ||= mach_task_self
|
129
|
+
port = ("\x00"*SIZEOFINT).to_ptr
|
130
|
+
r = CALLS["libc!task_for_pid:IIP=I"].call(target, pid, port).first
|
131
|
+
raise KernelCallError.new(:task_for_pid, r) if r != 0
|
132
|
+
return port.to_s(SIZEOFINT).unpack('i_').first
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns an Array of thread IDs for the given task
|
136
|
+
#
|
137
|
+
# kern_return_t task_threads
|
138
|
+
# (task_t task,
|
139
|
+
# thread_act_port_array_t thread_list,
|
140
|
+
# mach_msg_type_number_t* thread_count);
|
141
|
+
#
|
142
|
+
#There is no man page for this funtion.
|
143
|
+
def task_threads(port)
|
144
|
+
threads = ("\x00"*SIZEOFINT).to_ptr
|
145
|
+
#threads = 0
|
146
|
+
count = ("\x00"*SIZEOFINT).to_ptr
|
147
|
+
r = CALLS["libc!task_threads:IPP=I"].call(port, threads, count).first
|
148
|
+
t = DL::PtrData.new(threads.to_s(SIZEOFINT).unpack('i_').first)
|
149
|
+
raise KernelCallError.new(:task_threads, r) if r != 0
|
150
|
+
return t.to_a("I", count.to_s(SIZEOFINT).unpack('I_').first)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Decrement the target tasks suspend count
|
154
|
+
# kern_return_t task_resume
|
155
|
+
# (task_t task);
|
156
|
+
def task_resume(task)
|
157
|
+
r = CALLC["libc!task_resume:I=I"].call(task).first
|
158
|
+
raise KernelCallError.new(r) if r != 0
|
159
|
+
end
|
160
|
+
|
161
|
+
# Increment the target tasks suspend count
|
162
|
+
# kern_return_t task_suspend
|
163
|
+
# (task_t task);
|
164
|
+
def task_suspend(task)
|
165
|
+
r = CALLC["libc!task_suspend:I=I"].call(task).first
|
166
|
+
raise KernelCallError.new(r) if r != 0
|
167
|
+
end
|
168
|
+
|
169
|
+
# Sends a signal to a process
|
170
|
+
#
|
171
|
+
# int
|
172
|
+
# kill(pid_t pid, int sig);
|
173
|
+
#
|
174
|
+
# See kill(2)
|
175
|
+
def kill(pid, sig)
|
176
|
+
DL.last_error = 0
|
177
|
+
r = CALLS["libc!kill:II=I"].call(pid,sig).first
|
178
|
+
raise SystemCallError.new("kill",DL.last_error) if r != 0
|
179
|
+
end
|
180
|
+
|
181
|
+
# function to marshal 32bit integers into DL::PtrData objects
|
182
|
+
# necessary due to Ruby/DL not properly dealing with 31 and 32 bit integers
|
183
|
+
def dl_bignum_to_ulong(x)
|
184
|
+
if x.class == Fixnum
|
185
|
+
return DL::PtrData.new(x)
|
186
|
+
else
|
187
|
+
# shut up
|
188
|
+
c = x / 4
|
189
|
+
e = x - (c * 4)
|
190
|
+
v = DL::PtrData.new 0
|
191
|
+
v += c
|
192
|
+
v += c
|
193
|
+
v += c
|
194
|
+
v += c
|
195
|
+
v += e
|
196
|
+
return v
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# Reads sz bytes from task's address space starting at addr.
|
201
|
+
#
|
202
|
+
# kern_return_t vm_read_overwrite
|
203
|
+
# (vm_task_t target_task,
|
204
|
+
# vm_address_t address,
|
205
|
+
# vm_size_t size,
|
206
|
+
# vm_address_t *data_out,
|
207
|
+
# mach_msg_type_number_t *data_size);
|
208
|
+
#
|
209
|
+
# There is no man page for this function.
|
210
|
+
def vm_read(task, addr, sz=256)
|
211
|
+
addr = dl_bignum_to_ulong(addr)
|
212
|
+
buf = ("\x00" * sz).to_ptr
|
213
|
+
len = (sz.to_l32).to_ptr
|
214
|
+
r = CALLS["libc!vm_read_overwrite:IPIPP=I"].call(task, addr, sz, buf, len).first
|
215
|
+
raise KernelCallError.new(:vm_read, r) if r != 0
|
216
|
+
return buf.to_str(len.to_str(4).to_l32)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Writes val to task's memory space at address addr.
|
220
|
+
# It is necessary for val.size to report the size of val in bytes
|
221
|
+
#
|
222
|
+
# kern_return_t vm_write
|
223
|
+
# (vm_task_t target_task,
|
224
|
+
# vm_address_t address,
|
225
|
+
# pointer_t data,
|
226
|
+
# mach_msg_type_number_t data_count);
|
227
|
+
#
|
228
|
+
# There is no man page for this function.
|
229
|
+
def vm_write(task, addr, val)
|
230
|
+
addr = dl_bignum_to_ulong(addr)
|
231
|
+
val = val.to_ptr
|
232
|
+
r = CALLS["libc!vm_write:IPPI=I"].call(task, addr, val, val.size).first
|
233
|
+
raise KernelCallError.new(:vm_write, r) if r != 0
|
234
|
+
return nil
|
235
|
+
end
|
236
|
+
|
237
|
+
# Changes the protection state beginning at addr for size bytes to the mask prot.
|
238
|
+
# If setmax is true this will set the maximum permissions, otherwise it will set FIXME
|
239
|
+
#
|
240
|
+
# kern_return_t vm_protect
|
241
|
+
# (vm_task_t target_task,
|
242
|
+
# vm_address_t address,
|
243
|
+
# vm_size_t size,
|
244
|
+
# boolean_t set_maximum,
|
245
|
+
# vm_prot_t new_protection);
|
246
|
+
#
|
247
|
+
# There is no man page for this function.
|
248
|
+
def vm_protect(task, addr, size, setmax, prot)
|
249
|
+
addr = dl_bignum_to_ulong(addr)
|
250
|
+
setmax = setmax ? 1 : 0
|
251
|
+
r = CALLS["libc!vm_protect:IPIII=I"].call(task,addr,size,setmax,prot).first
|
252
|
+
raise KernelCallError.new(:vm_protect, r) if r != 0
|
253
|
+
return nil
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
# Allocates a page in the memory space of the target task.
|
258
|
+
#
|
259
|
+
# kern_return_t vm_allocate
|
260
|
+
# (vm_task_t target_task,
|
261
|
+
# vm_address_t address,
|
262
|
+
# vm_size_t size,
|
263
|
+
# boolean_t anywhere);
|
264
|
+
#
|
265
|
+
def vm_allocate(task, address, size, anywhere)
|
266
|
+
addr = int_to_intptr(address)
|
267
|
+
anywhere = anywhere ? 1 : 0
|
268
|
+
r = CALLS["libc!vm_allocate:IPII=I"].call(task,addr,size,anywhere).first
|
269
|
+
raise KernelCallError.new(r) if r != 0
|
270
|
+
addr.ptr
|
271
|
+
end
|
272
|
+
|
273
|
+
# deallocates a page in the memoryspace of target task.
|
274
|
+
#
|
275
|
+
# kern_return_t vm_deallocate
|
276
|
+
# (vm_task_t target_task,
|
277
|
+
# vm_address_t address,
|
278
|
+
# vm_size_t size);
|
279
|
+
#
|
280
|
+
def vm_deallocate(task,address,size)
|
281
|
+
addr = int_to_intptr(address)
|
282
|
+
r = CALLS["libc!vm_deallocate:IPI=I"].call(task, addr, size).first
|
283
|
+
raise KernelCallError.new(r) if r != 0
|
284
|
+
end
|
285
|
+
|
286
|
+
# Resumes a suspended thread by id.
|
287
|
+
#
|
288
|
+
# kern_return_t thread_resume
|
289
|
+
# (thread_act_t target_thread);
|
290
|
+
#
|
291
|
+
# There is no man page for this function.
|
292
|
+
def thread_resume(thread)
|
293
|
+
r = CALLS["libc!thread_resume:I=I"].call(thread).first
|
294
|
+
raise KernelCallError.new(:thread_resume, r) if r != 0
|
295
|
+
end
|
296
|
+
|
297
|
+
# Suspends a thread by id.
|
298
|
+
#
|
299
|
+
# kern_return_t thread_suspend
|
300
|
+
# (thread_act_t target_thread);
|
301
|
+
#
|
302
|
+
# There is no man page for this function.
|
303
|
+
def thread_suspend(thread)
|
304
|
+
r = CALLS["libc!thread_suspend:I=I"].call(thread).first
|
305
|
+
raise KernelCallError.new(:thread_suspend, r) if r != 0
|
306
|
+
end
|
307
|
+
|
308
|
+
# Suspends a task by id.
|
309
|
+
#
|
310
|
+
# kern_return_t task_suspend
|
311
|
+
# (task_t task);
|
312
|
+
#
|
313
|
+
# There is no man page for this function.
|
314
|
+
def task_suspend(task)
|
315
|
+
r = CALLS["libc!task_suspend:I=I"].call(task).first
|
316
|
+
raise KernelCallError.new(:task_suspend, r) if r != 0
|
317
|
+
end
|
318
|
+
|
319
|
+
# Resumes a suspended task by id.
|
320
|
+
#
|
321
|
+
# kern_return_t task_resume
|
322
|
+
# (task_t task);
|
323
|
+
#
|
324
|
+
# There is no man page for this function.
|
325
|
+
def task_resume(task)
|
326
|
+
r = CALLS["libc!task_resume:I=I"].call(task).first
|
327
|
+
raise KernelCallError.new(:task_resume, r) if r != 0
|
328
|
+
end
|
329
|
+
|
330
|
+
# Used to query kernel state.
|
331
|
+
# Returns output buffer on successful call or required buffer size on ENOMEM.
|
332
|
+
#
|
333
|
+
# mib: and array of integers decribing the MIB
|
334
|
+
# newb: the buffer to replace the old information (only used on some commands so it defaults to empty)
|
335
|
+
# oldlenp: output buffer size
|
336
|
+
#
|
337
|
+
# int
|
338
|
+
# sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
|
339
|
+
#
|
340
|
+
# this function doesn't really match the Ruby Way(tm)
|
341
|
+
#
|
342
|
+
# see sysctl(8)
|
343
|
+
def sysctl(mib,oldlen=0,newb="")
|
344
|
+
DL.last_error = 0
|
345
|
+
mibp = mib.pack("I_"*mib.size).to_ptr
|
346
|
+
oldlenp = [oldlen].pack("I_").to_ptr
|
347
|
+
namelen = mib.size
|
348
|
+
oldp = (oldlen > 0 ? "\x00"*oldlen : NULL)
|
349
|
+
newp = (newb.empty? ? NULL : newb.to_ptr)
|
350
|
+
newlen = newb.size
|
351
|
+
r = CALLS["libc!sysctl:PIPPPI=I"].call(mibp, namelen, oldp, oldlenp, newp, newlen).first
|
352
|
+
return oldlenp.to_str(SIZEOFINT).unpack("I_").first if (r == -1 and DL.last_error == Errno::ENOMEM::Errno)
|
353
|
+
raise SystemCallError.new("sysctl", DL.last_error) if r != 0
|
354
|
+
return oldp.to_str(oldlenp.to_str(SIZEOFINT).unpack("I_").first)
|
355
|
+
end
|
356
|
+
|
357
|
+
# Used to query kernel state.
|
358
|
+
# Returns output buffer on successful call and required buffer size as an Array.
|
359
|
+
#
|
360
|
+
# mib: and array of integers decribing the MIB
|
361
|
+
# newb: the buffer to replace the old information (only used on some commands so it defaults to empty)
|
362
|
+
# oldlenp: output buffer size
|
363
|
+
#
|
364
|
+
# int
|
365
|
+
# sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
|
366
|
+
#
|
367
|
+
# this function doesn't really match the Ruby Way(tm)
|
368
|
+
#
|
369
|
+
# see sysctl(8)
|
370
|
+
def sysctl_raw(mib,oldlen=0,newb="")
|
371
|
+
DL.last_error = 0
|
372
|
+
mibp = mib.pack('I_'*mib.size).to_ptr
|
373
|
+
oldlenp = [oldlen].pack("I_").to_ptr
|
374
|
+
namelen = mib.size
|
375
|
+
oldp = (oldlen > 0 ? ("\x00"*oldlen).to_ptr : NULL)
|
376
|
+
newp = (newb.empty? ? NULL : newb.to_ptr)
|
377
|
+
newlen = newb.size
|
378
|
+
r = CALLS["libc!sysctl:PIPPPI=I"].call(mibp, namelen, oldp, oldlenp, newp, newlen).first
|
379
|
+
ret = (DL.last_error == Errno::ENOMEM::Errno ? NULL : oldp)
|
380
|
+
raise SystemCallError.new("sysctl", DL.last_error) if (r != 0 and DL.last_error != Errno::ENOMEM::Errno)
|
381
|
+
return [ret,oldlenp.to_str(SIZEOFINT).unpack("I_").first]
|
382
|
+
end
|
383
|
+
|
384
|
+
# Changes execution to file in path with *args as though called from command line.
|
385
|
+
#
|
386
|
+
# int
|
387
|
+
# execv(const char *path, char *const argv[]);
|
388
|
+
def execv(path,*args)
|
389
|
+
DL.last_error = 0
|
390
|
+
argv = ""
|
391
|
+
args.flatten.each { |arg| argv = "#{ argv }#{arg.to_ptr.ref.to_s(SIZEOFINT)}" }
|
392
|
+
argv += ("\x00"*SIZEOFINT)
|
393
|
+
r = CALLS["libc!execv:SP"].call(path,argv.to_ptr).first
|
394
|
+
raise SystemCallError.new("execv", DL.last_error) if r == -1
|
395
|
+
return r
|
396
|
+
end
|
397
|
+
|
398
|
+
def int_to_intptr(i)
|
399
|
+
case i
|
400
|
+
when Integer
|
401
|
+
return [i].pack("I").to_ptr
|
402
|
+
when DL::PtrData
|
403
|
+
return i
|
404
|
+
else
|
405
|
+
raise ArgumentError, "Not an Integer"
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# if __FILE__ == $0
|
412
|
+
# include Ragweed
|
413
|
+
# require 'pp'
|
414
|
+
# require 'constants'
|
415
|
+
# addr = data = 0
|
416
|
+
# pid = 1319
|
417
|
+
# int = "\x00" * 4
|
418
|
+
# port = 0
|
419
|
+
# Wraposx::ptrace(Wraposx::Ptrace::ATTACH,pid,0,0)
|
420
|
+
# # status = Wraposx::waitpid(pid,0)
|
421
|
+
# # Wraposx::ptrace(Wraposx::Ptrace::CONTINUE,pid,1,0)
|
422
|
+
# mts = Wraposx::mach_task_self
|
423
|
+
# port = Wraposx::task_for_pid(mts,pid)
|
424
|
+
# port2 = Wraposx::task_for_pid(mts,pid)
|
425
|
+
# threads = Wraposx::task_threads(port)
|
426
|
+
# state = Wraposx::thread_get_state(threads.first)
|
427
|
+
# pp port
|
428
|
+
# pp port2
|
429
|
+
# pp threads
|
430
|
+
# pp state
|
431
|
+
# # Wraposx::thread_set_state(threads.first,state)
|
432
|
+
# Wraposx::ptrace(Wraposx::Ptrace::DETACH,pid,0,0)
|
433
|
+
# end
|