ragweed 0.1.7.2

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.
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