ragweed 0.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +34 -0
  5. data/examples/hittracertux.rb +49 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/arena.rb +55 -0
  11. data/lib/ragweed/blocks.rb +128 -0
  12. data/lib/ragweed/debugger32.rb +338 -0
  13. data/lib/ragweed/debuggerosx.rb +427 -0
  14. data/lib/ragweed/debuggertux.rb +346 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/bblock.rb +73 -0
  18. data/lib/ragweed/rasm/isa.rb +1115 -0
  19. data/lib/ragweed/rasm.rb +59 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +156 -0
  23. data/lib/ragweed/wrap32/debugging.rb +163 -0
  24. data/lib/ragweed/wrap32/device.rb +49 -0
  25. data/lib/ragweed/wrap32/event.rb +50 -0
  26. data/lib/ragweed/wrap32/hooks.rb +23 -0
  27. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  28. data/lib/ragweed/wrap32/process.rb +506 -0
  29. data/lib/ragweed/wrap32/process_token.rb +59 -0
  30. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  31. data/lib/ragweed/wrap32/winx.rb +16 -0
  32. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  33. data/lib/ragweed/wrap32.rb +59 -0
  34. data/lib/ragweed/wraposx/constants.rb +122 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +254 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +227 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +433 -0
  40. data/lib/ragweed/wraposx.rb +59 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +7 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +59 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/ragweed.gemspec +34 -0
  47. data/spec/ragweed_spec.rb +7 -0
  48. data/spec/spec_helper.rb +16 -0
  49. data/tasks/ann.rake +80 -0
  50. data/tasks/bones.rake +20 -0
  51. data/tasks/gem.rake +201 -0
  52. data/tasks/git.rake +40 -0
  53. data/tasks/notes.rake +27 -0
  54. data/tasks/post_load.rake +34 -0
  55. data/tasks/rdoc.rake +51 -0
  56. data/tasks/rubyforge.rake +55 -0
  57. data/tasks/setup.rb +292 -0
  58. data/tasks/spec.rake +54 -0
  59. data/tasks/svn.rake +47 -0
  60. data/tasks/test.rake +40 -0
  61. data/tasks/zentest.rake +36 -0
  62. data/test/test_ragweed.rb +0 -0
  63. 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