iZsh-ragweed 0.1.8

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 (62) hide show
  1. data/History.txt +29 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -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.rb +84 -0
  11. data/lib/ragweed/arena.rb +55 -0
  12. data/lib/ragweed/blocks.rb +128 -0
  13. data/lib/ragweed/debugger32.rb +338 -0
  14. data/lib/ragweed/debuggerosx.rb +419 -0
  15. data/lib/ragweed/debuggertux.rb +345 -0
  16. data/lib/ragweed/detour.rb +223 -0
  17. data/lib/ragweed/ptr.rb +48 -0
  18. data/lib/ragweed/rasm.rb +53 -0
  19. data/lib/ragweed/rasm/isa.rb +1046 -0
  20. data/lib/ragweed/rasm/util.rb +26 -0
  21. data/lib/ragweed/sbuf.rb +197 -0
  22. data/lib/ragweed/trampoline.rb +103 -0
  23. data/lib/ragweed/utils.rb +88 -0
  24. data/lib/ragweed/wrap32.rb +53 -0
  25. data/lib/ragweed/wrap32/debugging.rb +163 -0
  26. data/lib/ragweed/wrap32/device.rb +49 -0
  27. data/lib/ragweed/wrap32/event.rb +50 -0
  28. data/lib/ragweed/wrap32/hooks.rb +23 -0
  29. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  30. data/lib/ragweed/wrap32/process.rb +506 -0
  31. data/lib/ragweed/wrap32/process_token.rb +59 -0
  32. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  33. data/lib/ragweed/wrap32/winx.rb +16 -0
  34. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  35. data/lib/ragweed/wraposx.rb +53 -0
  36. data/lib/ragweed/wraposx/constants.rb +112 -0
  37. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  38. data/lib/ragweed/wraposx/region_info.rb +250 -0
  39. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  40. data/lib/ragweed/wraposx/thread_info.rb +225 -0
  41. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  42. data/lib/ragweed/wraptux.rb +53 -0
  43. data/lib/ragweed/wraptux/constants.rb +68 -0
  44. data/lib/ragweed/wraptux/threads.rb +7 -0
  45. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. metadata +127 -0
@@ -0,0 +1,203 @@
1
+ module Ragweed; end
2
+ module Ragweed::Wraposx
3
+ module EFlags
4
+ CARRY = 0x1
5
+ X0 = 0x2
6
+ PARITY = 0x4
7
+ X1 = 0x8
8
+ ADJUST = 0x10
9
+ X2 = 0x20
10
+ ZERO = 0x40
11
+ SIGN = 0x80
12
+ TRAP = 0x100
13
+ INTERRUPT = 0x200
14
+ DIRECTION = 0x400
15
+ OVERFLOW = 0x800
16
+ IOPL1 = 0x1000
17
+ IOPL2 = 0x2000
18
+ NESTEDTASK = 0x4000
19
+ X3 = 0x8000
20
+ RESUME = 0x10000
21
+ V86MODE = 0x20000
22
+ ALIGNCHECK = 0x40000
23
+ VINT = 0x80000
24
+ VINTPENDING = 0x100000
25
+ CPUID = 0x200000
26
+ end
27
+ end
28
+
29
+ class Ragweed::Wraposx::ThreadContext
30
+ include Ragweed
31
+ (FIELDS = [ [:eax, "I"],
32
+ [:ebx, "I"],
33
+ [:ecx, "I"],
34
+ [:edx, "I"],
35
+ [:edi, "I"],
36
+ [:esi, "I"],
37
+ [:ebp, "I"],
38
+ [:esp, "I"],
39
+ [:ss, "I"],
40
+ [:eflags, "I"],
41
+ [:eip, "I"],
42
+ [:cs, "I"],
43
+ [:ds, "I"],
44
+ [:es, "I"],
45
+ [:fs, "I"],
46
+ [:gs, "I"]]).each {|x| attr_accessor x[0]}
47
+ FIELDTYPES = FIELDS.map {|x| x[1]}.join("")
48
+
49
+ def initialize(str=nil)
50
+ refresh(str) if str
51
+ end
52
+
53
+ #(re)loads the data fields from str
54
+ def refresh(str)
55
+ if str
56
+ str.unpack(FIELDTYPES).each_with_index do |val, i|
57
+ instance_variable_set "@#{ FIELDS[i][0] }".intern, val
58
+ end
59
+ end
60
+ end
61
+
62
+ def to_s
63
+ FIELDS.map {|f| send(f[0])}.pack(FIELDTYPES)
64
+ end
65
+
66
+ def self.get(h)
67
+ Wraposx::thread_suspend(h)
68
+ r = self.new(Wraposx::thread_get_state_raw(h))
69
+ Wraposx::thread_resume(h)
70
+ return r
71
+ end
72
+
73
+ def get(h)
74
+ Wraposx::thread_suspend(h)
75
+ r = refresh(Wraposx::thread_get_state_raw(h))
76
+ Wraposx::thread_resume(h)
77
+ return r
78
+ end
79
+
80
+ def set(h)
81
+ Wraposx::thread_suspend(h)
82
+ r = Wraposx::thread_set_state_raw(h, self.to_s)
83
+ Wraposx::thread_resume(h)
84
+ return
85
+ end
86
+
87
+ def inspect
88
+ body = lambda do
89
+ FIELDS.map do |f|
90
+ "#{f[0]}=#{send(f[0]).to_s(16)}"
91
+ end.join(", ")
92
+ end
93
+ "#<ThreadContext #{body.call}>"
94
+ end
95
+
96
+ def dump(&block)
97
+ maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
98
+ maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
99
+
100
+ string =<<EOM
101
+ -----------------------------------------------------------------------
102
+ CONTEXT:
103
+ EIP: #{self.eip.to_s(16).rjust(8, "0")} #{maybe_dis.call(self.eip)}
104
+
105
+ EAX: #{self.eax.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.eax)}
106
+ EBX: #{self.ebx.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ebx)}
107
+ ECX: #{self.ecx.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ecx)}
108
+ EDX: #{self.edx.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.edx)}
109
+ EDI: #{self.edi.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.edi)}
110
+ ESI: #{self.esi.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.esi)}
111
+ EBP: #{self.ebp.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ebp)}
112
+ ESP: #{self.esp.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.esp)}
113
+ EFL: #{self.eflags.to_s(2).rjust(32, "0")} #{Wraposx::EFlags.flag_dump(self.eflags)}
114
+ EOM
115
+ end
116
+
117
+ # sets/clears the TRAP flag
118
+ def single_step(v=true)
119
+ if v
120
+ @eflags |= Wraposx::EFlags::TRAP
121
+ else
122
+ @eflags &= ~(Wraposx::EFlags::TRAP)
123
+ end
124
+ end
125
+ end
126
+
127
+ module Ragweed::Wraposx
128
+
129
+ # FIXME - constants need to be in separate sub modules
130
+ # XXX - move to class based implementation a la region_info
131
+ # define i386_THREAD_STATE_COUNT ((mach_msg_type_number_t)( sizeof (i386_thread_state_t) / sizeof (int) ))
132
+ # i386_thread_state_t is a struct w/ 16 uint
133
+ I386_THREAD_STATE_COUNT = 16
134
+ I386_THREAD_STATE = 1
135
+ REGISTER_SYMS = [:eax,:ebx,:ecx,:edx,:edi,:esi,:ebp,:esp,:ss,:eflags,:eip,:cs,:ds,:es,:fs,:gs]
136
+
137
+ class << self
138
+
139
+ # Returns a Hash of the thread's registers given a thread id.
140
+ #
141
+ # kern_return_t thread_get_state
142
+ # (thread_act_t target_thread,
143
+ # thread_state_flavor_t flavor,
144
+ # thread_state_t old_state,
145
+ # mach_msg_type_number_t old_state_count);
146
+ def thread_get_state(thread)
147
+ state_arr = ("\x00"*SIZEOFINT*I386_THREAD_STATE_COUNT).to_ptr
148
+ count = ([I386_THREAD_STATE_COUNT].pack("I_")).to_ptr
149
+ r = CALLS["libc!thread_get_state:IIPP=I"].call(thread, I386_THREAD_STATE, state_arr, count).first
150
+ raise KernelCallError.new(:thread_get_state, r) if r != 0
151
+ r = state_arr.to_s(I386_THREAD_STATE_COUNT*SIZEOFINT).unpack("I_"*I386_THREAD_STATE_COUNT)
152
+ regs = Hash.new
153
+ I386_THREAD_STATE_COUNT.times do |i|
154
+ regs[REGISTER_SYMS[i]] = r[i]
155
+ end
156
+ return regs
157
+ end
158
+
159
+ # Returns string representation of a thread's registers for unpacking given a thread id
160
+ #
161
+ # kern_return_t thread_get_state
162
+ # (thread_act_t target_thread,
163
+ # thread_state_flavor_t flavor,
164
+ # thread_state_t old_state,
165
+ # mach_msg_type_number_t old_state_count);
166
+ def thread_get_state_raw(thread)
167
+ state_arr = ("\x00"*SIZEOFINT*I386_THREAD_STATE_COUNT).to_ptr
168
+ count = ([I386_THREAD_STATE_COUNT].pack("I_")).to_ptr
169
+ r = CALLS["libc!thread_get_state:IIPP=I"].call(thread, I386_THREAD_STATE, state_arr, count).first
170
+ raise KernelCallError.new(:thread_get_state, r) if r != 0
171
+ return state_arr.to_s(I386_THREAD_STATE_COUNT*SIZEOFINT)
172
+ end
173
+
174
+ # Sets the register state of thread from a Hash containing it's values.
175
+ #
176
+ # kern_return_t thread_set_state
177
+ # (thread_act_t target_thread,
178
+ # thread_state_flavor_t flavor,
179
+ # thread_state_t new_state,
180
+ # target_thread new_state_count);
181
+ def thread_set_state(thread, state)
182
+ s = Array.new
183
+ I386_THREAD_STATE_COUNT.times do |i|
184
+ s << state[REGISTER_SYMS[i]]
185
+ end
186
+ s = s.pack("I_"*I386_THREAD_STATE_COUNT).to_ptr
187
+ r = CALLS["libc!thread_set_state:IIPI=I"].call(thread, I386_THREAD_STATE, s, I386_THREAD_STATE_COUNT).first
188
+ raise KernelCallError.new(:thread_set_state, r) if r!= 0
189
+ end
190
+
191
+ # Sets the register state of thread from a packed string containing it's values.
192
+ #
193
+ # kern_return_t thread_set_state
194
+ # (thread_act_t target_thread,
195
+ # thread_state_flavor_t flavor,
196
+ # thread_state_t new_state,
197
+ # target_thread new_state_count);
198
+ def thread_set_state_raw(thread, state)
199
+ r = CALLS["libc!thread_set_state:IIPI=I"].call(thread, I386_THREAD_STATE, state.to_ptr, I386_THREAD_STATE_COUNT).first
200
+ raise KernelCallError.new(:thread_set_state, r) if r!= 0
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,225 @@
1
+ module Ragweed; end
2
+ module Ragweed::Wraposx::ThreadInfo
3
+ class << self
4
+ #factory method to get a ThreadInfo variant
5
+ def self.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
+ # kern_return_t thread_info
213
+ # (thread_act_t target_thread,
214
+ # thread_flavor_t flavor,
215
+ # thread_info_t thread_info,
216
+ # mach_msg_type_number_t thread_info_count);
217
+ def thread_info_raw(thread, flavor)
218
+ info = ("\x00"*1024).to_ptr
219
+ count = ([Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:count]].pack("I_")).to_ptr
220
+ r = CALLS["libc!thread_info:IIPP=I"].call(thread,flavor,info,Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:count]).first
221
+ raise KernelCallError.new(r) if r != 0
222
+ return info.to_s(Ragweed::Wraposx::ThreadInfo::FLAVORS[flavor][:size])
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,376 @@
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
+ # Sends a signal to a process
154
+ #
155
+ # int
156
+ # kill(pid_t pid, int sig);
157
+ #
158
+ # See kill(2)
159
+ def kill(pid, sig)
160
+ DL.last_error = 0
161
+ r = CALLS["libc!kill:II=I"].call(pid,sig).first
162
+ raise SystemCallError.new("kill",DL.last_error) if r != 0
163
+ end
164
+
165
+ # function to marshal 32bit integers into DL::PtrData objects
166
+ # necessary due to Ruby/DL not properly dealing with 31 and 32 bit integers
167
+ def dl_bignum_to_ulong(x)
168
+ if x.class == Fixnum
169
+ return DL::PtrData.new(x)
170
+ else
171
+ # shut up
172
+ c = x / 4
173
+ e = x - (c * 4)
174
+ v = DL::PtrData.new 0
175
+ v += c
176
+ v += c
177
+ v += c
178
+ v += c
179
+ v += e
180
+ return v
181
+ end
182
+ end
183
+
184
+ # Reads sz bytes from task's address space starting at addr.
185
+ #
186
+ # kern_return_t vm_read_overwrite
187
+ # (vm_task_t target_task,
188
+ # vm_address_t address,
189
+ # vm_size_t size,
190
+ # vm_address_t *data_out,
191
+ # mach_msg_type_number_t *data_size);
192
+ #
193
+ # There is no man page for this function.
194
+ def vm_read(task, addr, sz=256)
195
+ addr = dl_bignum_to_ulong(addr)
196
+ buf = ("\x00" * sz).to_ptr
197
+ len = (sz.to_l32).to_ptr
198
+ r = CALLS["libc!vm_read_overwrite:IPIPP=I"].call(task, addr, sz, buf, len).first
199
+ raise KernelCallError.new(:vm_read, r) if r != 0
200
+ return buf.to_str(len.to_str(4).to_l32)
201
+ end
202
+
203
+ # Writes val to task's memory space at address addr.
204
+ # It is necessary for val.size to report the size of val in bytes
205
+ #
206
+ # kern_return_t vm_write
207
+ # (vm_task_t target_task,
208
+ # vm_address_t address,
209
+ # pointer_t data,
210
+ # mach_msg_type_number_t data_count);
211
+ #
212
+ # There is no man page for this function.
213
+ def vm_write(task, addr, val)
214
+ addr = dl_bignum_to_ulong(addr)
215
+ val = val.to_ptr
216
+ r = CALLS["libc!vm_write:IPPI=I"].call(task, addr, val, val.size).first
217
+ raise KernelCallError.new(:vm_write, r) if r != 0
218
+ return nil
219
+ end
220
+
221
+ # Changes the protection state beginning at addr for size bytes to the mask prot.
222
+ # If setmax is true this will set the maximum permissions, otherwise it will set FIXME
223
+ #
224
+ # kern_return_t vm_protect
225
+ # (vm_task_t target_task,
226
+ # vm_address_t address,
227
+ # vm_size_t size,
228
+ # boolean_t set_maximum,
229
+ # vm_prot_t new_protection);
230
+ #
231
+ # There is no man page for this function.
232
+ def vm_protect(task, addr, size, setmax, prot)
233
+ addr = dl_bignum_to_ulong(addr)
234
+ setmax = setmax ? 1 : 0
235
+ r = CALLS["libc!vm_protect:IPIII=I"].call(task,addr,size,setmax,prot).first
236
+ raise KernelCallError.new(:vm_protect, r) if r != 0
237
+ return nil
238
+ end
239
+
240
+ # Resumes a suspended thread by id.
241
+ #
242
+ # kern_return_t thread_resume
243
+ # (thread_act_t target_thread);
244
+ #
245
+ # There is no man page for this function.
246
+ def thread_resume(thread)
247
+ r = CALLS["libc!thread_resume:I=I"].call(thread).first
248
+ raise KernelCallError.new(:thread_resume, r) if r != 0
249
+ end
250
+
251
+ # Suspends a thread by id.
252
+ #
253
+ # kern_return_t thread_suspend
254
+ # (thread_act_t target_thread);
255
+ #
256
+ # There is no man page for this function.
257
+ def thread_suspend(thread)
258
+ r = CALLS["libc!thread_suspend:I=I"].call(thread).first
259
+ raise KernelCallError.new(:thread_suspend, r) if r != 0
260
+ end
261
+
262
+ # Suspends a task by id.
263
+ #
264
+ # kern_return_t task_suspend
265
+ # (task_t task);
266
+ #
267
+ # There is no man page for this function.
268
+ def task_suspend(task)
269
+ r = CALLS["libc!task_suspend:I=I"].call(task).first
270
+ raise KernelCallError.new(:task_suspend, r) if r != 0
271
+ end
272
+
273
+ # Resumes a suspended task by id.
274
+ #
275
+ # kern_return_t task_resume
276
+ # (task_t task);
277
+ #
278
+ # There is no man page for this function.
279
+ def task_resume(task)
280
+ r = CALLS["libc!task_resume:I=I"].call(task).first
281
+ raise KernelCallError.new(:task_resume, r) if r != 0
282
+ end
283
+
284
+ # Used to query kernel state.
285
+ # Returns output buffer on successful call or required buffer size on ENOMEM.
286
+ #
287
+ # mib: and array of integers decribing the MIB
288
+ # newb: the buffer to replace the old information (only used on some commands so it defaults to empty)
289
+ # oldlenp: output buffer size
290
+ #
291
+ # int
292
+ # sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
293
+ #
294
+ # this function doesn't really match the Ruby Way(tm)
295
+ #
296
+ # see sysctl(8)
297
+ def sysctl(mib,oldlen=0,newb="")
298
+ DL.last_error = 0
299
+ mibp = mib.pack("I_"*mib.size).to_ptr
300
+ oldlenp = [oldlen].pack("I_").to_ptr
301
+ namelen = mib.size
302
+ oldp = (oldlen > 0 ? "\x00"*oldlen : NULL)
303
+ newp = (newb.empty? ? NULL : newb.to_ptr)
304
+ newlen = newb.size
305
+ r = CALLS["libc!sysctl:PIPPPI=I"].call(mibp, namelen, oldp, oldlenp, newp, newlen).first
306
+ return oldlenp.to_str(SIZEOFINT).unpack("I_").first if (r == -1 and DL.last_error == Errno::ENOMEM::Errno)
307
+ raise SystemCallError.new("sysctl", DL.last_error) if r != 0
308
+ return oldp.to_str(oldlenp.to_str(SIZEOFINT).unpack("I_").first)
309
+ end
310
+
311
+ # Used to query kernel state.
312
+ # Returns output buffer on successful call and required buffer size as an Array.
313
+ #
314
+ # mib: and array of integers decribing the MIB
315
+ # newb: the buffer to replace the old information (only used on some commands so it defaults to empty)
316
+ # oldlenp: output buffer size
317
+ #
318
+ # int
319
+ # sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
320
+ #
321
+ # this function doesn't really match the Ruby Way(tm)
322
+ #
323
+ # see sysctl(8)
324
+ def sysctl_raw(mib,oldlen=0,newb="")
325
+ DL.last_error = 0
326
+ mibp = mib.pack('I_'*mib.size).to_ptr
327
+ oldlenp = [oldlen].pack("I_").to_ptr
328
+ namelen = mib.size
329
+ oldp = (oldlen > 0 ? ("\x00"*oldlen).to_ptr : NULL)
330
+ newp = (newb.empty? ? NULL : newb.to_ptr)
331
+ newlen = newb.size
332
+ r = CALLS["libc!sysctl:PIPPPI=I"].call(mibp, namelen, oldp, oldlenp, newp, newlen).first
333
+ ret = (DL.last_error == Errno::ENOMEM::Errno ? NULL : oldp)
334
+ raise SystemCallError.new("sysctl", DL.last_error) if (r != 0 and DL.last_error != Errno::ENOMEM::Errno)
335
+ return [ret,oldlenp.to_str(SIZEOFINT).unpack("I_").first]
336
+ end
337
+
338
+ # Changes execution to file in path with *args as though called from command line.
339
+ #
340
+ # int
341
+ # execv(const char *path, char *const argv[]);
342
+ def execv(path,*args)
343
+ DL.last_error = 0
344
+ argv = ""
345
+ args.flatten.each { |arg| argv = "#{ argv }#{arg.to_ptr.ref.to_s(SIZEOFINT)}" }
346
+ argv += ("\x00"*SIZEOFINT)
347
+ r = CALLS["libc!execv:SP"].call(path,argv.to_ptr).first
348
+ raise SystemCallError.new("execv", DL.last_error) if r == -1
349
+ return r
350
+ end
351
+ end
352
+ end
353
+
354
+ # if __FILE__ == $0
355
+ # include Ragweed
356
+ # require 'pp'
357
+ # require 'constants'
358
+ # addr = data = 0
359
+ # pid = 1319
360
+ # int = "\x00" * 4
361
+ # port = 0
362
+ # Wraposx::ptrace(Wraposx::Ptrace::ATTACH,pid,0,0)
363
+ # # status = Wraposx::waitpid(pid,0)
364
+ # # Wraposx::ptrace(Wraposx::Ptrace::CONTINUE,pid,1,0)
365
+ # mts = Wraposx::mach_task_self
366
+ # port = Wraposx::task_for_pid(mts,pid)
367
+ # port2 = Wraposx::task_for_pid(mts,pid)
368
+ # threads = Wraposx::task_threads(port)
369
+ # state = Wraposx::thread_get_state(threads.first)
370
+ # pp port
371
+ # pp port2
372
+ # pp threads
373
+ # pp state
374
+ # # Wraposx::thread_set_state(threads.first,state)
375
+ # Wraposx::ptrace(Wraposx::Ptrace::DETACH,pid,0,0)
376
+ # end