ragweed 0.2.0-java
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 +60 -0
- data/README.txt +9 -0
- data/Rakefile +86 -0
- data/VERSION +1 -0
- data/examples/hittracertux.rb +45 -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 +24 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +400 -0
- data/lib/ragweed/debuggerosx.rb +456 -0
- data/lib/ragweed/debuggertux.rb +502 -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 +182 -0
- data/lib/ragweed/wrap32/debugging.rb +401 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +39 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +613 -0
- data/lib/ragweed/wrap32/process_token.rb +75 -0
- data/lib/ragweed/wrap32/thread_context.rb +142 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +583 -0
- data/lib/ragweed/wrap32.rb +59 -0
- data/lib/ragweed/wraposx/constants.rb +114 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +275 -0
- data/lib/ragweed/wraposx/structs.rb +102 -0
- data/lib/ragweed/wraposx/thread_context.rb +902 -0
- data/lib/ragweed/wraposx/thread_info.rb +160 -0
- data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
- data/lib/ragweed/wraposx/wraposx.rb +356 -0
- data/lib/ragweed/wraposx.rb +60 -0
- data/lib/ragweed/wraptux/constants.rb +101 -0
- data/lib/ragweed/wraptux/process.rb +35 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +72 -0
- data/lib/ragweed/wraptux.rb +57 -0
- data/lib/ragweed.rb +112 -0
- data/ragweed.gemspec +102 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/test/test_ragweed.rb +0 -0
- metadata +121 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
module Ragweed; end
|
2
|
+
module Ragweed::Wraposx::ThreadInfo
|
3
|
+
# info interfaces
|
4
|
+
BASIC_INFO = 3 #basic information
|
5
|
+
|
6
|
+
# following are obsolete interfaces
|
7
|
+
# according to the source @ fxr they still work except FIFO
|
8
|
+
SCHED_TIMESHARE_INFO = 10
|
9
|
+
SCHED_RR_INFO = 11
|
10
|
+
# SCHED_FIFO_INFO = 12
|
11
|
+
|
12
|
+
module State
|
13
|
+
#Thread run states
|
14
|
+
RUNNING = 1 #/* thread is running normally */
|
15
|
+
STOPPED = 2 #/* thread is stopped */
|
16
|
+
WAITING = 3 #/* thread is waiting normally */
|
17
|
+
UNINTERRUPTIBLE = 4 #/* thread is in an uninterruptible wait */
|
18
|
+
HALTED = 5 #/* thread is halted at a clean point */
|
19
|
+
end
|
20
|
+
|
21
|
+
# struct thread_basic_info
|
22
|
+
# {
|
23
|
+
# time_value_t user_time;
|
24
|
+
# time_value_t system_time;
|
25
|
+
# integer_t cpu_usage;
|
26
|
+
# policy_t policy;
|
27
|
+
# integer_t run_state;
|
28
|
+
# integer_t flags;
|
29
|
+
# integer_t suspend_count;
|
30
|
+
# integer_t sleep_time;
|
31
|
+
# };
|
32
|
+
class Basic < FFI::Struct
|
33
|
+
include Ragweed::FFIStructInclude
|
34
|
+
module Flags
|
35
|
+
#Thread flags (flags field).
|
36
|
+
SWAPPED = 0x1 #/* thread is swapped out */
|
37
|
+
IDLE = 0x2 #/* thread is an idle thread */
|
38
|
+
end
|
39
|
+
|
40
|
+
FLAVOR = Ragweed::Wraposx::ThreadInfo::BASIC_INFO
|
41
|
+
layout :user_time, Ragweed::Wraposx::TimeValue,
|
42
|
+
:system_time, Ragweed::Wraposx::TimeValue,
|
43
|
+
:cpu_usage, :int,
|
44
|
+
:policy, Ragweed::Wraposx::Libc.find_type(:policy_t),
|
45
|
+
:run_state, :int,
|
46
|
+
:flags, :int,
|
47
|
+
:suspend_count, :int,
|
48
|
+
:sleep_time, :int
|
49
|
+
|
50
|
+
def dump(&block)
|
51
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
52
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
53
|
+
|
54
|
+
string =<<EOM
|
55
|
+
-----------------------------------------------------------------------
|
56
|
+
INFO:
|
57
|
+
user_time: #{self.user_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.user_time)}
|
58
|
+
system_time: #{self.system_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.system_time)}
|
59
|
+
cpu_usage: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cpu_usage)}
|
60
|
+
policy: #{self.policy.to_s.rjust(8, "0")} #{maybe_hex.call(self.policy)}
|
61
|
+
run_state: #{self.run_state.to_s.rjust(8, "0")} #{maybe_hex.call(self.run_state)}
|
62
|
+
suspend_count: #{self.suspend_count.to_s.rjust(8, "0")} #{maybe_hex.call(self.suspend_count)}
|
63
|
+
sleep_time: #{self.sleep_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.sleep_time)}
|
64
|
+
flags: #{self.flags.to_s(2).rjust(32, "0")} #{Flags.flag_dump(self.flags)}
|
65
|
+
EOM
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# struct policy_timeshare_info
|
70
|
+
# {
|
71
|
+
# int max_priority;
|
72
|
+
# int base_priority;
|
73
|
+
# int cur_priority;
|
74
|
+
# boolean_t depressed;
|
75
|
+
# int depress_priority;
|
76
|
+
# };
|
77
|
+
class SchedTimeshare < FFI::Struct
|
78
|
+
include Ragweed::FFIStructInclude
|
79
|
+
layout :max_priority, :int,
|
80
|
+
:base_priority, :int,
|
81
|
+
:cur_priority, :int,
|
82
|
+
:depress_priority, :int
|
83
|
+
|
84
|
+
def dump(&block)
|
85
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
86
|
+
|
87
|
+
string =<<EOM
|
88
|
+
-----------------------------------------------------------------------
|
89
|
+
Timeshare Info:
|
90
|
+
max_priority: #{self.max_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.max_priority)}
|
91
|
+
base_priority: #{self.base_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.base_priority)}
|
92
|
+
cur_priority: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cur_priority)}
|
93
|
+
depressed: #{(!self.depressed.zero?).to_s.rjust(8, " ")} #{maybe_hex.call(self.depressed)}
|
94
|
+
depress_priority: #{self.depress_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.depressed_priority)}
|
95
|
+
EOM
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# struct policy_rr_info
|
100
|
+
# {
|
101
|
+
# int max_priority;
|
102
|
+
# int base_priority;
|
103
|
+
# int quantum;
|
104
|
+
# boolean_t depressed;
|
105
|
+
# int depress_priority;
|
106
|
+
# };
|
107
|
+
class SchedRr < FFI::Struct
|
108
|
+
include Ragweed::FFIStructInclude
|
109
|
+
layout :max_priority, :int,
|
110
|
+
:base_priority, :int,
|
111
|
+
:quantum, :int,
|
112
|
+
:depressed, Ragweed::Wraposx::Libc.find_type(:boolean_t),
|
113
|
+
:depress_priority, :int
|
114
|
+
|
115
|
+
def dump(&block)
|
116
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
117
|
+
|
118
|
+
string =<<EOM
|
119
|
+
-----------------------------------------------------------------------
|
120
|
+
Round Robin Info:
|
121
|
+
max_priority: #{self.max_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.max_priority)}
|
122
|
+
base_priority: #{self.base_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.base_priority)}
|
123
|
+
quantum: #{self.quantum.to_s.rjust(8, "0")} #{maybe_hex.call(self.quantum)}
|
124
|
+
depressed: #{(!self.depressed.zero?).to_s.rjust(8, " ")} #{maybe_hex.call(self.depressed)}
|
125
|
+
depress_priority: #{self.depress_priority.to_s.rjust(8, "0")} #{maybe_hex.call(self.depressed_priority)}
|
126
|
+
EOM
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
FLAVORS = {
|
131
|
+
# define THREAD_BASIC_INFO_COUNT ((mach_msg_type_number_t)(sizeof(thread_basic_info_data_t) / sizeof(natural_t)))
|
132
|
+
BASIC_INFO => {:size => 30, :count => 10, :class => Basic},
|
133
|
+
# define POLICY_TIMESHARE_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_timeshare_info)/sizeof(integer_t)))
|
134
|
+
SCHED_TIMESHARE_INFO => {:size => 20, :count => 5, :class => SchedTimeshare},
|
135
|
+
# define POLICY_RR_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_rr_info)/sizeof(integer_t)))
|
136
|
+
SCHED_RR_INFO => {:size => 20,:count => 5, :class => SchedRr},
|
137
|
+
# define POLICY_FIFO_INFO_COUNT ((mach_msg_type_number_t)(sizeof(struct policy_fifo_info)/sizeof(integer_t)))
|
138
|
+
# SCHED_FIFO_INFO => {:size => 16,:count => 4} # immediately returns KERNEL_INVALID_POLICY on osx
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
module Ragweed::Wraposx
|
143
|
+
class << self
|
144
|
+
|
145
|
+
# Returns the packed string representation of the thread_info_t struct for later parsing.
|
146
|
+
#
|
147
|
+
# kern_return_t thread_info
|
148
|
+
# (thread_act_t target_thread,
|
149
|
+
# thread_flavor_t flavor,
|
150
|
+
# thread_info_t thread_info,
|
151
|
+
# mach_msg_type_number_t thread_info_count);
|
152
|
+
def thread_info(thread, flavor)
|
153
|
+
info = FFI::MemoryPointer.new(ThreadInfo::FLAVORS[flavor][:class], 1)
|
154
|
+
count = FFI::MemoryPointer.new(:int, 1).write_int(ThreadInfo::FLAVORS[flavor][:count])
|
155
|
+
r = Libc.thread_info(thread, flavor, info, count)
|
156
|
+
raise KernelCallError.new(r) if r != 0
|
157
|
+
ThreadInfo::FLAVORS[flavor][:class].new info
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Ragweed; end
|
2
|
+
module Ragweed::Wraposx::ThreadState
|
3
|
+
#Thread run states
|
4
|
+
RUNNING = 1 #/* thread is running normally */
|
5
|
+
STOPPED = 2 #/* thread is stopped */
|
6
|
+
WAITING = 3 #/* thread is waiting normally */
|
7
|
+
UNINTERRUPTIBLE = 4 #/* thread is in an uninterruptible wait */
|
8
|
+
HALTED = 5 #/* thread is halted at a clean point */
|
9
|
+
end
|
10
|
+
|
11
|
+
module Ragweed::Wraposx::TFlags
|
12
|
+
#Thread flags (flags field).
|
13
|
+
SWAPPED = 0x1 #/* thread is swapped out */
|
14
|
+
IDLE = 0x2 #/* thread is an idle thread */
|
15
|
+
end
|
16
|
+
|
17
|
+
class Ragweed::Wraposx::ThreadInfo
|
18
|
+
include Ragweed
|
19
|
+
attr_accessor :user_time
|
20
|
+
attr_accessor :system_time
|
21
|
+
(FIELDS = [ [:user_time_s, "I"],
|
22
|
+
[:user_time_us, "I"],
|
23
|
+
[:system_time_s, "I"],
|
24
|
+
[:system_time_us, "I"],
|
25
|
+
[:cpu_usage, "I"],
|
26
|
+
[:policy, "I"],
|
27
|
+
[:run_state, "I"],
|
28
|
+
[:flags, "I"],
|
29
|
+
[:suspend_count, "I"],
|
30
|
+
[:sleep_time, "I"]]).each {|x| attr_accessor x[0]}
|
31
|
+
|
32
|
+
def initialize(str=nil)
|
33
|
+
refresh(str) if str
|
34
|
+
end
|
35
|
+
|
36
|
+
#(re)loads the data from str
|
37
|
+
def refresh(str)
|
38
|
+
if str and not str.empty?
|
39
|
+
str.unpack(FIELDS.map {|x| x[1]}.join("")).each_with_index do |val, i|
|
40
|
+
raise "i is nil" if i.nil?
|
41
|
+
instance_variable_set "@#{ FIELDS[i][0] }".intern, val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@user_time = @user_time_s + (@user_time_us/1000000.0)
|
45
|
+
@system_time = @system_time_s + (@system_time_us/1000000.0)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
FIELDS.map {|f| send(f[0])}.pack(FIELDS.map {|x| x[1]}.join(""))
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.get(t)
|
53
|
+
self.new(Wraposx::thread_info_raw(t))
|
54
|
+
end
|
55
|
+
|
56
|
+
def get(t)
|
57
|
+
refresh(Wraposx::thread_info_raw(t))
|
58
|
+
end
|
59
|
+
|
60
|
+
def inspect
|
61
|
+
body = lambda do
|
62
|
+
FIELDS.map do |f|
|
63
|
+
"#{f[0]}=#{send(f[0]).to_s}"
|
64
|
+
end.join(", ")
|
65
|
+
end
|
66
|
+
"#<ThreadInfo #{body.call}>"
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump(&block)
|
70
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
71
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
72
|
+
|
73
|
+
string =<<EOM
|
74
|
+
-----------------------------------------------------------------------
|
75
|
+
INFO:
|
76
|
+
user_time: #{self.user_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.user_time)}
|
77
|
+
system_time: #{self.system_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.system_time)}
|
78
|
+
cpu_usage: #{self.cpu_usage.to_s.rjust(8, "0")} #{maybe_hex.call(self.cpu_usage)}
|
79
|
+
policy: #{self.policy.to_s.rjust(8, "0")} #{maybe_hex.call(self.policy)}
|
80
|
+
run_state: #{self.run_state.to_s.rjust(8, "0")} #{maybe_hex.call(self.run_state)}
|
81
|
+
suspend_count: #{self.suspend_count.to_s.rjust(8, "0")} #{maybe_hex.call(self.suspend_count)}
|
82
|
+
sleep_time: #{self.sleep_time.to_s.rjust(8, "0")} #{maybe_hex.call(self.sleep_time)}
|
83
|
+
flags: #{self.flags.to_s(2).rjust(32, "0")} #{Wraposx::TFlags.flag_dump(self.flags)}
|
84
|
+
EOM
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module Ragweed::Wraposx
|
89
|
+
|
90
|
+
# FIXME - constants should be under separate sub-modules
|
91
|
+
# XXX - implement more thread info flavors (if possible)
|
92
|
+
# XXX - move to class based implementation a la region_info
|
93
|
+
# info interfaces
|
94
|
+
THREAD_BASIC_INFO = 3 #basic information
|
95
|
+
|
96
|
+
# following are obsolete interfaces
|
97
|
+
THREAD_SCHED_TIMESHARE_INFO = 10
|
98
|
+
THREAD_SCHED_RR_INFO = 11
|
99
|
+
THREAD_SCHED_FIFO_INFO = 12
|
100
|
+
|
101
|
+
# define THREAD_BASIC_INFO_COUNT ((mach_msg_type_number_t)(sizeof(thread_basic_info_data_t) / sizeof(natural_t)))
|
102
|
+
# the two time fields are each two ints
|
103
|
+
THREAD_BASIC_INFO_COUNT = 10
|
104
|
+
|
105
|
+
class << self
|
106
|
+
|
107
|
+
# Returns the packed string representation of the thread_info_t struct for later parsing.
|
108
|
+
# kern_return_t thread_info
|
109
|
+
# (thread_act_t target_thread,
|
110
|
+
# thread_flavor_t flavor,
|
111
|
+
# thread_info_t thread_info,
|
112
|
+
# mach_msg_type_number_t thread_info_count);
|
113
|
+
def thread_info_raw(thread)
|
114
|
+
info = ("\x00"*1024).to_ptr
|
115
|
+
count = ([THREAD_BASIC_INFO_COUNT].pack("I_")).to_ptr
|
116
|
+
r = CALLS["libc!thread_info:IIPP=I"].call(thread,THREAD_BASIC_INFO,info,count).first
|
117
|
+
raise KernelCallError.new(:thread_info, r) if r != 0
|
118
|
+
return info.to_s(SIZEOFINT*THREAD_BASIC_INFO_COUNT)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,356 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Ragweed; end
|
4
|
+
module Ragweed::Wraposx
|
5
|
+
|
6
|
+
module Libc
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib FFI::Library::LIBC
|
9
|
+
typedef :int, :kern_return_t
|
10
|
+
|
11
|
+
typedef :ulong_long, :memory_object_offset_t
|
12
|
+
typedef :uint, :vm_inherit_t
|
13
|
+
typedef :uint, :natural_t
|
14
|
+
typedef :natural_t, :mach_msg_type_number_t
|
15
|
+
typedef :natural_t, :mach_port_name_t
|
16
|
+
typedef :mach_port_name_t, :mach_port_t
|
17
|
+
typedef :mach_port_t, :vm_map_t
|
18
|
+
typedef :mach_port_t, :task_t
|
19
|
+
typedef :mach_port_t, :thread_act_t
|
20
|
+
typedef :int, :vm_region_flavor_t
|
21
|
+
typedef :int, :vm_prot_t
|
22
|
+
typedef :int, :vm_behavior_t
|
23
|
+
typedef :int, :policy_t
|
24
|
+
typedef :int, :boolean_t
|
25
|
+
typedef :int, :thread_state_flavor_t
|
26
|
+
case FFI::Platform::LONG_SIZE
|
27
|
+
when 64
|
28
|
+
# ifdef __LP64__
|
29
|
+
typedef :uintptr_t, :vm_size_t
|
30
|
+
typedef :uintptr_t, :vm_offset_t
|
31
|
+
when 32
|
32
|
+
# else /* __LP64__ */
|
33
|
+
typedef :natural_t, :vm_size_t
|
34
|
+
typedef :natural_t, :vm_offset_t
|
35
|
+
else
|
36
|
+
raise "Unsupported Platform"
|
37
|
+
end
|
38
|
+
|
39
|
+
typedef :vm_offset_t, :vm_address_t
|
40
|
+
|
41
|
+
attach_function :getpid, [], :pid_t
|
42
|
+
attach_function :ptrace, [:int, :pid_t, :ulong, :int], :int
|
43
|
+
attach_function :wait, [:pointer], :pid_t
|
44
|
+
attach_function :waitpid, [:pid_t, :pointer, :int], :pid_t
|
45
|
+
attach_function :mach_task_self, [], :mach_port_t
|
46
|
+
attach_function :task_for_pid, [:mach_port_name_t, :int, :pointer], :kern_return_t
|
47
|
+
attach_function :task_threads, [:task_t, :pointer, :pointer], :kern_return_t
|
48
|
+
attach_function :kill, [:pid_t, :int], :int
|
49
|
+
attach_function :vm_read_overwrite, [:vm_map_t, :vm_address_t, :vm_size_t, :vm_address_t, :pointer], :kern_return_t
|
50
|
+
attach_function :vm_write, [:vm_map_t, :vm_address_t, :vm_offset_t, :mach_msg_type_number_t], :kern_return_t
|
51
|
+
attach_function :vm_protect, [:vm_map_t, :vm_address_t, :vm_size_t, :boolean_t, :vm_prot_t], :kern_return_t
|
52
|
+
attach_function :vm_allocate, [:vm_map_t, :pointer, :vm_size_t, :int], :kern_return_t
|
53
|
+
attach_function :vm_deallocate, [:vm_map_t, :vm_address_t, :vm_size_t], :kern_return_t
|
54
|
+
attach_function :thread_resume, [:thread_act_t], :kern_return_t
|
55
|
+
attach_function :thread_suspend, [:thread_act_t], :kern_return_t
|
56
|
+
attach_function :task_suspend, [:int], :kern_return_t
|
57
|
+
attach_function :task_resume, [:int], :kern_return_t
|
58
|
+
attach_function :sysctl, [:pointer, :int, :pointer, :pointer, :pointer, :int], :int
|
59
|
+
attach_function :execv, [:string, :pointer], :int
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
|
64
|
+
# pid_t
|
65
|
+
# getpid(void);
|
66
|
+
#
|
67
|
+
# see also getpid(2)
|
68
|
+
def getpid
|
69
|
+
Libc.getpid
|
70
|
+
end
|
71
|
+
|
72
|
+
# Apple's ptrace is fairly gimped. The memory read and write functionality has been
|
73
|
+
# removed. We will be using mach kernel calls for that. see vm_read and vm_write.
|
74
|
+
# for details on ptrace and the process for the Wraposx/debuggerosx port see:
|
75
|
+
# http://www.matasano.com/log/1100/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt/
|
76
|
+
#
|
77
|
+
#int
|
78
|
+
#ptrace(int request, pid_t pid, caddr_t addr, int data);
|
79
|
+
#
|
80
|
+
# see also ptrace(2)
|
81
|
+
def ptrace(request, pid, addr, data)
|
82
|
+
FFI.errno = 0
|
83
|
+
r = Libc.ptrace(request, pid, addr, data)
|
84
|
+
raise SystemCallError.new("ptrace", FFI.errno) if r == -1 and FFI.errno != 0
|
85
|
+
[r, data]
|
86
|
+
end
|
87
|
+
|
88
|
+
# ptrace(PT_TRACE_ME, ...)
|
89
|
+
def pt_trace_me pid
|
90
|
+
ptrace(Ragweed::Wraposx::Ptrace::TRACE_ME, pid, nil, nil).first
|
91
|
+
end
|
92
|
+
|
93
|
+
# ptrace(PT_DENY_ATTACH, ... )
|
94
|
+
def pt_deny_attach pid
|
95
|
+
ptrace(Ragweed::Wraposx::Ptrace::DENY_ATTACH, pid, nil, nil).first
|
96
|
+
end
|
97
|
+
|
98
|
+
# ptrace(PT_CONTINUE, pid, addr, signal)
|
99
|
+
def pt_continue pid, addr = 1, sig = 0
|
100
|
+
ptrace(Ragweed::Wraposx::Ptrace::CONTINUE, pid, addr, sig).first
|
101
|
+
end
|
102
|
+
|
103
|
+
# ptrace(PT_STEP, pid, addr, signal)
|
104
|
+
def pt_step pid, addr = 1, sig = 0
|
105
|
+
ptrace(Ragweed::Wraposx::Ptrace::STEP, pid, addr, sig).first
|
106
|
+
end
|
107
|
+
|
108
|
+
# ptrace(PT_KILL, ... )
|
109
|
+
def pt_kill pid
|
110
|
+
ptrace(Ragweed::Wraposx::Ptrace::KILL, pid, nil, nil).first
|
111
|
+
end
|
112
|
+
|
113
|
+
# ptrace(PT_ATTACH, ... )
|
114
|
+
def pt_attach pid
|
115
|
+
ptrace(Ragweed::Wraposx::Ptrace::ATTACH, pid, nil, nil).first
|
116
|
+
end
|
117
|
+
|
118
|
+
# ptrace(PT_DETACH, ... )
|
119
|
+
def pt_detach pid
|
120
|
+
ptrace(Ragweed::Wraposx::Ptrace::DETACH, pid, nil, nil).first
|
121
|
+
end
|
122
|
+
|
123
|
+
# Originally coded for use in debuggerosx but I've switched to waitpid for
|
124
|
+
# usability and debugging purposes.
|
125
|
+
#
|
126
|
+
# Returns status of child when child recieves a signal.
|
127
|
+
#
|
128
|
+
# pid_t
|
129
|
+
# wait(int *stat_loc);
|
130
|
+
#
|
131
|
+
# see also wait(2)
|
132
|
+
def wait
|
133
|
+
stat = FFI::MemoryPointer.new :int, 1
|
134
|
+
FFI.errno = 0
|
135
|
+
pid = Libc.wait stat
|
136
|
+
raise SystemCallError.new "wait", FFI.errno if pid == -1
|
137
|
+
[pid, stat.read_int]
|
138
|
+
end
|
139
|
+
|
140
|
+
# The wait used in debuggerosx.
|
141
|
+
# opt is an OR of the options to be used.
|
142
|
+
#
|
143
|
+
# Returns an array. The first element is the pid of the child process
|
144
|
+
# as returned by the waitpid system call. The second, the status as
|
145
|
+
# an integer of that pid.
|
146
|
+
#
|
147
|
+
# pid_t
|
148
|
+
# waitpid(pid_t pid, int *stat_loc, int options);
|
149
|
+
#
|
150
|
+
# see also wait(2)
|
151
|
+
def waitpid pid, opts = 0
|
152
|
+
stat = FFI::MemoryPointer.new :int, 1
|
153
|
+
FFI.errno = 0
|
154
|
+
r = Libc.waitpid(pid, stat, opts)
|
155
|
+
raise SystemCallError.new "waitpid", FFI.errno if r == -1
|
156
|
+
[r, stat.read_int]
|
157
|
+
end
|
158
|
+
|
159
|
+
# From docs at http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_task_self.html
|
160
|
+
# Returns send rights to the task's kernel port.
|
161
|
+
#
|
162
|
+
# mach_port_t
|
163
|
+
# mach_task_self(void)
|
164
|
+
#
|
165
|
+
# There is no man page for this call.
|
166
|
+
def mach_task_self
|
167
|
+
Libc.mach_task_self
|
168
|
+
end
|
169
|
+
|
170
|
+
# Requires sudo to use as of 10.5 or 10.4.11(ish)
|
171
|
+
# Returns the task id for a process.
|
172
|
+
#
|
173
|
+
# kern_return_t task_for_pid(
|
174
|
+
# mach_port_name_t target_tport,
|
175
|
+
# int pid,
|
176
|
+
# mach_port_name_t *t);
|
177
|
+
#
|
178
|
+
# There is no man page for this call.
|
179
|
+
def task_for_pid(pid, target=nil)
|
180
|
+
target ||= mach_task_self
|
181
|
+
port = FFI::MemoryPointer.new :int, 1
|
182
|
+
r = Libc.task_for_pid(target, pid, port)
|
183
|
+
raise KernelCallError.new(:task_for_pid, r) if r != 0
|
184
|
+
port.read_int
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns an Array of thread IDs for the given task
|
188
|
+
#
|
189
|
+
# kern_return_t task_threads
|
190
|
+
# (task_t task,
|
191
|
+
# thread_act_port_array_t thread_list,
|
192
|
+
# mach_msg_type_number_t* thread_count);
|
193
|
+
#
|
194
|
+
#There is no man page for this funtion.
|
195
|
+
def task_threads(port)
|
196
|
+
threads = FFI::MemoryPointer.new :int, 1
|
197
|
+
count = FFI::MemoryPointer.new :int, 1
|
198
|
+
r = Libc.task_threads(port, threads, count)
|
199
|
+
raise KernelCallError.new(:task_threads, r) if r != 0
|
200
|
+
threads.read_array_of_int(count.read_int)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Decrement the target tasks suspend count
|
204
|
+
# kern_return_t task_resume
|
205
|
+
# (task_t task);
|
206
|
+
def task_resume(task)
|
207
|
+
r = Libc.task_resume(task)
|
208
|
+
raise KernelCallError.new(r) if r != 0
|
209
|
+
r
|
210
|
+
end
|
211
|
+
|
212
|
+
# Increment the target tasks suspend count
|
213
|
+
# kern_return_t task_suspend
|
214
|
+
# (task_t task);
|
215
|
+
def task_suspend(task)
|
216
|
+
r = Libc.task_suspend(task)
|
217
|
+
raise KernelCallError.new(r) if r != 0
|
218
|
+
end
|
219
|
+
|
220
|
+
# Sends a signal to a process
|
221
|
+
#
|
222
|
+
# int
|
223
|
+
# kill(pid_t pid, int sig);
|
224
|
+
#
|
225
|
+
# See kill(2)
|
226
|
+
def kill(pid, sig)
|
227
|
+
FFI::errno = 0
|
228
|
+
r = Libc.kill(pid, sig)
|
229
|
+
raise SystemCallError.new "kill", FFI::errno if r != 0
|
230
|
+
end
|
231
|
+
|
232
|
+
# Reads sz bytes from task's address space starting at addr.
|
233
|
+
#
|
234
|
+
# kern_return_t vm_read_overwrite
|
235
|
+
# (vm_task_t target_task,
|
236
|
+
# vm_address_t address,
|
237
|
+
# vm_size_t size,
|
238
|
+
# vm_address_t *data_out,
|
239
|
+
# mach_msg_type_number_t *data_size);
|
240
|
+
#
|
241
|
+
# There is no man page for this function.
|
242
|
+
def vm_read(task, addr, sz=256)
|
243
|
+
buf = FFI::MemoryPointer.new(sz)
|
244
|
+
len = FFI::MemoryPointer(sz.to_l32)
|
245
|
+
r = Libc.vm_read_overwrite(task, addr, sz, buf, len)
|
246
|
+
raise KernelCallError.new(:vm_read, r) if r != 0
|
247
|
+
return buf.to_str(len.to_str(4).to_l32)
|
248
|
+
end
|
249
|
+
|
250
|
+
# Writes val to task's memory space at address addr.
|
251
|
+
# It is necessary for val.size to report the size of val in bytes
|
252
|
+
#
|
253
|
+
# kern_return_t vm_write
|
254
|
+
# (vm_task_t target_task,
|
255
|
+
# vm_address_t address,
|
256
|
+
# pointer_t data,
|
257
|
+
# mach_msg_type_number_t data_count);
|
258
|
+
#
|
259
|
+
# There is no man page for this function.
|
260
|
+
def vm_write(task, addr, val)
|
261
|
+
val = FFI::MemoryPointer.new
|
262
|
+
r = Libc.vm_write(task, addr, val, val.size)
|
263
|
+
raise KernelCallError.new(:vm_write, r) if r != 0
|
264
|
+
return nil
|
265
|
+
end
|
266
|
+
|
267
|
+
# Changes the protection state beginning at addr for size bytes to the mask prot.
|
268
|
+
# If setmax is true this will set the maximum permissions, otherwise it will set FIXME
|
269
|
+
#
|
270
|
+
# kern_return_t vm_protect
|
271
|
+
# (vm_task_t target_task,
|
272
|
+
# vm_address_t address,
|
273
|
+
# vm_size_t size,
|
274
|
+
# boolean_t set_maximum,
|
275
|
+
# vm_prot_t new_protection);
|
276
|
+
#
|
277
|
+
# There is no man page for this function.
|
278
|
+
def vm_protect(task, addr, size, setmax, prot)
|
279
|
+
setmax = setmax ? 1 : 0
|
280
|
+
r = Libc.vm_protect(task, addr, size, setmax, prot)
|
281
|
+
raise KernelCallError.new(:vm_protect, r) if r != 0
|
282
|
+
return nil
|
283
|
+
end
|
284
|
+
|
285
|
+
# Allocates a page in the memory space of the target task.
|
286
|
+
#
|
287
|
+
# kern_return_t vm_allocate
|
288
|
+
# (vm_task_t target_task,
|
289
|
+
# vm_address_t address,
|
290
|
+
# vm_size_t size,
|
291
|
+
# boolean_t anywhere);
|
292
|
+
#
|
293
|
+
def vm_allocate(task, address, size, anywhere)
|
294
|
+
addr = FFI::MemoryPointer.new :int, 1
|
295
|
+
addr.write_int(address)
|
296
|
+
anywhere = anywhere ? 1 : 0
|
297
|
+
r = Libc.vm_allocate(task, addr, size, anywhere)
|
298
|
+
raise KernelCallError.new(r) if r != 0
|
299
|
+
addr.address
|
300
|
+
end
|
301
|
+
|
302
|
+
# deallocates a page in the memoryspace of target task.
|
303
|
+
#
|
304
|
+
# kern_return_t vm_deallocate
|
305
|
+
# (vm_task_t target_task,
|
306
|
+
# vm_address_t address,
|
307
|
+
# vm_size_t size);
|
308
|
+
#
|
309
|
+
def vm_deallocate(task, address, size)
|
310
|
+
addr = FFI::MemoryPointer.new :int, 1
|
311
|
+
addr.write_int(address)
|
312
|
+
r = Libc.vm_deallocate(task, addr, size)
|
313
|
+
raise KernelCallError.new(r) if r != 0
|
314
|
+
end
|
315
|
+
|
316
|
+
# Resumes a suspended thread by id.
|
317
|
+
#
|
318
|
+
# kern_return_t thread_resume
|
319
|
+
# (thread_act_t target_thread);
|
320
|
+
#
|
321
|
+
# There is no man page for this function.
|
322
|
+
def thread_resume(thread)
|
323
|
+
r = Libc.thread_resume(thread)
|
324
|
+
raise KernelCallError.new(:thread_resume, r) if r != 0
|
325
|
+
end
|
326
|
+
|
327
|
+
# Suspends a thread by id.
|
328
|
+
#
|
329
|
+
# kern_return_t thread_suspend
|
330
|
+
# (thread_act_t target_thread);
|
331
|
+
#
|
332
|
+
# There is no man page for this function.
|
333
|
+
def thread_suspend(thread)
|
334
|
+
r = Libc.thread_suspend(thread)
|
335
|
+
raise KernelCallError.new(:thread_suspend, r) if r != 0
|
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
|
+
FFI.errno = 0
|
344
|
+
args.flatten!
|
345
|
+
argv = FFI::MemoryPointer.new(:pointer, args.size + 1)
|
346
|
+
args.each_with_index do |arg, i|
|
347
|
+
argv[i].put_pointer(0, FFI::MemoryPointer.from_string(arg.to_s))
|
348
|
+
end
|
349
|
+
argv[args.size].put_pointer(0, nil)
|
350
|
+
|
351
|
+
r = Libc.execv(path, argv)
|
352
|
+
# if this ever returns, there's been an error
|
353
|
+
raise SystemCallError(:execv, FFI.errno)
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Dir[File.expand_path("#{File.dirname(__FILE__)}/wraposx/*.rb")].each do |file|
|
2
|
+
# require file
|
3
|
+
# end
|
4
|
+
module Ragweed; end
|
5
|
+
module Ragweed::Wraposx
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
VERSION = File.read(File.join(File.dirname(__FILE__),"..","..","VERSION")).strip
|
9
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
10
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
11
|
+
# :startdoc:
|
12
|
+
|
13
|
+
# Returns the version string for the library.
|
14
|
+
#
|
15
|
+
def self.version
|
16
|
+
VERSION
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the library path for the module. If any arguments are given,
|
20
|
+
# they will be joined to the end of the libray path using
|
21
|
+
# <tt>File.join</tt>.
|
22
|
+
#
|
23
|
+
def self.libpath( *args )
|
24
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the lpath for the module. If any arguments are given,
|
28
|
+
# they will be joined to the end of the path using
|
29
|
+
# <tt>File.join</tt>.
|
30
|
+
#
|
31
|
+
def self.path( *args )
|
32
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Utility function to load utility classes and extensions
|
36
|
+
def self.require_utils
|
37
|
+
%w{utils}.each{|r| require self.libpath(r)+'.rb'}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Utility method used to require all files ending in .rb that lie in the
|
41
|
+
# directory below this file that has the same name as the filename passed
|
42
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
43
|
+
# the _filename_ does not have to be equivalent to the directory.
|
44
|
+
#
|
45
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
46
|
+
self.require_utils
|
47
|
+
dir ||= ::File.basename(fname, '.*')
|
48
|
+
search_me = ::File.expand_path(
|
49
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
50
|
+
|
51
|
+
Dir.glob(search_me).reject{|rb| rb =~ /#{__FILE__}/}.sort.each {|rb| require rb}
|
52
|
+
# require File.dirname(File.basename(__FILE__)) + "/#{x}"
|
53
|
+
|
54
|
+
end
|
55
|
+
end # module Ragweed::Wraposx
|
56
|
+
|
57
|
+
require 'ragweed/wraposx/wraposx'
|
58
|
+
Ragweed::Wraposx.require_all_libs_relative_to(__FILE__)
|
59
|
+
|
60
|
+
# EOF
|