tduehr-ragweed 0.1.5
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.
- data/History.txt +15 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +30 -0
- data/examples/hittracertux.rb +48 -0
- data/examples/hittracerx.rb +63 -0
- data/examples/hook_notepad.rb +9 -0
- data/examples/snicker.rb +183 -0
- data/examples/tux-example.rb +23 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +338 -0
- data/lib/ragweed/debuggerosx.rb +419 -0
- data/lib/ragweed/debuggertux.rb +347 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm/isa.rb +1046 -0
- data/lib/ragweed/rasm/util.rb +26 -0
- data/lib/ragweed/rasm.rb +53 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +87 -0
- data/lib/ragweed/wrap32/debugging.rb +163 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +23 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +506 -0
- data/lib/ragweed/wrap32/process_token.rb +59 -0
- data/lib/ragweed/wrap32/thread_context.rb +208 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +526 -0
- data/lib/ragweed/wrap32.rb +53 -0
- data/lib/ragweed/wraposx/constants.rb +101 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +244 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +213 -0
- data/lib/ragweed/wraposx/wraposx.rb +376 -0
- data/lib/ragweed/wraposx.rb +53 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +3 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +53 -0
- data/lib/ragweed.rb +84 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_ragweed.rb +0 -0
- metadata +127 -0
@@ -0,0 +1,147 @@
|
|
1
|
+
# Exception objects for kernel errors likely in Wraposx
|
2
|
+
# If this were a C extension I'd use #ifdef on each to only create the required ones.
|
3
|
+
|
4
|
+
module Ragweed; end
|
5
|
+
module Ragweed::Wraposx; end
|
6
|
+
module Ragweed::Wraposx::KernelReturn
|
7
|
+
SUCCESS = { :value => 0, :message => 'Not an error'}
|
8
|
+
INVALID_ADDRESS = { :value => 1, :message => 'Specified address is not currently valid.'}
|
9
|
+
PROTECTION_FAILURE = { :value => 2, :message => 'Specified memory is valid, but does not permit the required forms of access.'}
|
10
|
+
NO_SPACE = { :value => 3, :message => 'The address range specified is already in use, or no address range of the size specified could be found.'}
|
11
|
+
INVALID_ARGUMENT = { :value => 4, :message => 'The function requested was not applicable to this type of argument, or an argument is invalid'}
|
12
|
+
FAILURE = { :value => 5, :message => 'The function could not be performed. A catch-all.'}
|
13
|
+
RESOURCE_SHORTAGE = { :value => 6, :message => 'A system resource could not be allocated to fulfill this request. This failure may not be permanent.'}
|
14
|
+
NOT_RECEIVER = { :value => 7, :message => 'The task in question does not hold receive rights for the port argument.'}
|
15
|
+
NO_ACCESS = { :value => 8, :message => 'Bogus access restriction.'}
|
16
|
+
MEMORY_FAILURE = { :value => 9, :message => 'During a page fault, the target address refers to a memory object that has been destroyed. This failure is permanent.'}
|
17
|
+
MEMORY_ERROR = { :value => 10, :message => 'During a page fault, the memory object indicated that the data could not be returned. This failure may be temporary; future attempts to access this same data may succeed, as defined by the memory object.'}
|
18
|
+
ALREADY_IN_SET = { :value => 11, :message => 'The receive right is already a member of the portset.'}
|
19
|
+
NOT_IN_SET = { :value => 12, :message => 'The receive right is not a member of a port set.'}
|
20
|
+
NAME_EXISTS = { :value => 13, :message => 'The name already denotes a right in the task.'}
|
21
|
+
ABORTED = { :value => 14, :message => 'The operation was aborted. Ipc code will catch this and reflect it as a message error.'}
|
22
|
+
INVALID_NAME = { :value => 15, :message => 'The name doesn\'t denote a right in the task.'}
|
23
|
+
INVALID_TASK = { :value => 16, :message => 'Target task isn\'t an active task.'}
|
24
|
+
INVALID_RIGHT = { :value => 17, :message => 'The name denotes a right, but not an appropriate right.'}
|
25
|
+
INVALID_VALUE = { :value => 18, :message => 'A blatant range error.'}
|
26
|
+
UREFS_OVERFLOW = { :value => 19, :message => 'Operation would overflow limit on user-references.'}
|
27
|
+
INVALID_CAPABILITY = { :value => 20, :message => 'The supplied (port) capability is improper.'}
|
28
|
+
RIGHT_EXISTS = { :value => 21, :message => 'The task already has send or receive rights for the port under another name.'}
|
29
|
+
INVALID_HOST = { :value => 22, :message => 'Target host isn\'t actually a host.'}
|
30
|
+
MEMORY_PRESENT = { :value => 23, :message => 'An attempt was made to supply "precious" data for memory that is already present in a memory object.'}
|
31
|
+
MEMORY_DATA_MOVED = { :value => 24, :message => 'A page was requested of a memory manager via memory_object_data_request for an object using a MEMORY_OBJECT_COPY_CALL strategy, with the VM_PROT_WANTS_COPY flag being used to specify that the page desired is for a copy of the object, and the memory manager has detected the page was pushed into a copy of the object while the kernel was walking the shadow chain from the copy to the object. This error code is delivered via memory_object_data_error and is handled by the kernel (it forces the kernel to restart the fault). It will not be seen by users.'}
|
32
|
+
MEMORY_RESTART_COPY = { :value => 25, :message => 'A strategic copy was attempted of an object upon which a quicker copy is now possible. The caller should retry the copy using vm_object_copy_quickly. This error code is seen only by the kernel.'}
|
33
|
+
INVALID_PROCESSOR_SET = { :value => 26, :message => 'An argument applied to assert processor set privilege was not a processor set control port.'}
|
34
|
+
POLICY_LIMIT = { :value => 27, :message => 'The specified scheduling attributes exceed the thread\'s limits.'}
|
35
|
+
INVALID_POLICY = { :value => 28, :message => 'The specified scheduling policy is not currently enabled for the processor set.'}
|
36
|
+
INVALID_OBJECT = { :value => 29, :message => 'The external memory manager failed to initialize the memory object.'}
|
37
|
+
ALREADY_WAITING = { :value => 30, :message => 'A thread is attempting to wait for an event for which there is already a waiting thread.'}
|
38
|
+
DEFAULT_SET = { :value => 31, :message => 'An attempt was made to destroy the default processor set.'}
|
39
|
+
EXCEPTION_PROTECTED = { :value => 32, :message => 'An attempt was made to fetch an exception port that is protected, or to abort a thread while processing a protected exception.'}
|
40
|
+
INVALID_LEDGER = { :value => 33, :message => 'A ledger was required but not supplied.'}
|
41
|
+
INVALID_MEMORY_CONTROL= { :value => 34, :message => 'The port was not a memory cache control port.'}
|
42
|
+
INVALID_SECURITY = { :value => 35, :message => 'An argument supplied to assert security privilege was not a host security port.'}
|
43
|
+
NOT_DEPRESSED = { :value => 36, :message => 'thread_depress_abort was called on a thread which was not currently depressed.'}
|
44
|
+
TERMINATED = { :value => 37, :message => 'Object has been terminated and is no longer available'}
|
45
|
+
LOCK_SET_DESTROYED = { :value => 38, :message => 'Lock set has been destroyed and is no longer available.'}
|
46
|
+
LOCK_UNSTABLE = { :value => 39, :message => 'The thread holding the lock terminated before releasing the lock'}
|
47
|
+
LOCK_OWNED = { :value => 40, :message => 'The lock is already owned by another thread'}
|
48
|
+
LOCK_OWNED_SELF = { :value => 41, :message => 'The lock is already owned by the calling thread'}
|
49
|
+
SEMAPHORE_DESTROYED = { :value => 42, :message => 'Semaphore has been destroyed and is no longer available.'}
|
50
|
+
RPC_SERVER_TERMINATED = { :value => 43, :message => 'Return from RPC indicating the target server was terminated before it successfully replied '}
|
51
|
+
RPC_TERMINATE_ORPHAN = { :value => 44, :message => 'Terminate an orphaned activation.'}
|
52
|
+
RPC_CONTINUE_ORPHAN = { :value => 45, :message => 'Allow an orphaned activation to continue executing.'}
|
53
|
+
NOT_SUPPORTED = { :value => 46, :message => 'Empty thread activation (No thread linked to it)'}
|
54
|
+
NODE_DOWN = { :value => 47, :message => 'Remote node down or inaccessible.'}
|
55
|
+
NOT_WAITING = { :value => 48, :message => 'A signalled thread was not actually waiting.'}
|
56
|
+
OPERATION_TIMED_OUT = { :value => 49, :message => 'Some thread-oriented operation (semaphore_wait) timed out'}
|
57
|
+
RETURN_MAX = { :value => 0x100, :message => 'Maximum return value allowable'}
|
58
|
+
|
59
|
+
module_function
|
60
|
+
# Much like Signals.list returns a hash of the possible kernel call return values.
|
61
|
+
def list
|
62
|
+
constants.inject({}){|a, c| a.merge! c => const_get(c)}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
module Ragweed::Wraposx::KErrno; end
|
67
|
+
|
68
|
+
# Exception class for mach kernel calls. Works mostly like SystemCallError.
|
69
|
+
# Subclasses are individual error conditions and case equality (===) is done by class then error number (KErrno)
|
70
|
+
class Ragweed::Wraposx::KernelCallError < StandardError
|
71
|
+
DEFAULT_MESSAGE = "Unknown Error"
|
72
|
+
attr_reader :kerrno
|
73
|
+
|
74
|
+
# Returns a subclass of KernelCallError based on the KernelReturn value err
|
75
|
+
def self.new(msg = "", err = nil)
|
76
|
+
if msg.kind_of? Fixnum
|
77
|
+
err = msg
|
78
|
+
msg = ""
|
79
|
+
end
|
80
|
+
mesg = ""
|
81
|
+
|
82
|
+
klass = Ragweed::Wraposx::KErrno.constants.detect{|x| Ragweed::Wraposx::KErrno.const_get(x).const_get("KErrno") == err}
|
83
|
+
if (klass.nil? or klass.empty?)
|
84
|
+
o = self.allocate
|
85
|
+
o.instance_variable_set("@kerrno", err)
|
86
|
+
mesg = "Unknown kernel error"
|
87
|
+
else
|
88
|
+
o = Ragweed::Wraposx::KErrno.const_get(klass).allocate
|
89
|
+
mesg = Ragweed::Wraposx::KernelReturn.const_get(klass)[:message]
|
90
|
+
end
|
91
|
+
|
92
|
+
if o.class.const_defined?("KErrno")
|
93
|
+
o.instance_variable_set("@kerrno", o.class.const_get("KErrno"))
|
94
|
+
else
|
95
|
+
o.instance_variable_set("@kerrno", err)
|
96
|
+
mesg = "#{mesg}: #{err}" if err
|
97
|
+
end
|
98
|
+
|
99
|
+
mesg = "#{mesg} - #{msg}" if !(msg.nil? or msg.to_s.empty?)
|
100
|
+
o.send(:initialize, mesg)
|
101
|
+
return o
|
102
|
+
end
|
103
|
+
|
104
|
+
# Case equality. Returns true if self and other are KernelCallError or when error numbers match.
|
105
|
+
def self.===(other)
|
106
|
+
return false if not other.kind_of?(Ragweed::Wraposx::KernelCallError)
|
107
|
+
return true if self == Ragweed::Wraposx::KernelCallError
|
108
|
+
|
109
|
+
begin
|
110
|
+
return self.const_get("KErrno") == other.const_get("KErrno")
|
111
|
+
rescue
|
112
|
+
return false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def initialize(msg)
|
117
|
+
super msg
|
118
|
+
end
|
119
|
+
|
120
|
+
# This block builds the subclasses for KernelCallError
|
121
|
+
Ragweed::Wraposx::KernelReturn.list.each do |k, v|
|
122
|
+
case k.intern
|
123
|
+
when :SUCCESS
|
124
|
+
when :RETURN_MAX
|
125
|
+
else
|
126
|
+
klass = Ragweed::Wraposx::KErrno.const_set(k, Class.new(Ragweed::Wraposx::KernelCallError){
|
127
|
+
def self.new(msg = "")
|
128
|
+
o = self.allocate
|
129
|
+
o.instance_variable_set "@kerrno", self.const_get("KErrno")
|
130
|
+
mesg = ""
|
131
|
+
klass = self.name.split("::").last
|
132
|
+
if Ragweed::Wraposx::KernelReturn.const_defined?(klass)
|
133
|
+
mesg = Ragweed::Wraposx::KernelReturn.const_get(klass)[:message]
|
134
|
+
else
|
135
|
+
mesg = "Unknown kernel error"
|
136
|
+
end
|
137
|
+
mesg = "#{mesg} - #{msg}" if not (msg.nil? or msg.empty?)
|
138
|
+
o.send(:initialize, mesg)
|
139
|
+
puts self
|
140
|
+
return o
|
141
|
+
end
|
142
|
+
})
|
143
|
+
klass.const_set "KErrno", v[:value]
|
144
|
+
klass.const_set "DEFAULT_MESSAGE", v[:message]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
module Ragweed; end
|
2
|
+
module Ragweed::Wraposx; end
|
3
|
+
module Ragweed::Wraposx::Vm
|
4
|
+
# these are flavor arguments for vm_region
|
5
|
+
# more to be added as support for 64bit processes gets added
|
6
|
+
REGION_BASIC_INFO = 10
|
7
|
+
REGION_EXTENDED_INFO = 11
|
8
|
+
REGION_TOP_INFO = 12
|
9
|
+
|
10
|
+
# behavior identifiers
|
11
|
+
BEHAVIOR_DEFAULT = 0 # /* default */
|
12
|
+
BEHAVIOR_RANDOM = 1 # /* random */
|
13
|
+
BEHAVIOR_SEQUENTIAL = 2 # /* forward sequential */
|
14
|
+
BEHAVIOR_RSEQNTL = 3 # /* reverse sequential */
|
15
|
+
BEHAVIOR_WILLNEED = 4 # /* will need in near future */
|
16
|
+
BEHAVIOR_DONTNEED = 5 # /* dont need in near future */
|
17
|
+
|
18
|
+
#Virtual memory map inheritance values for vm_inherit_t
|
19
|
+
INHERIT_SHARE = 0 # /* share with child */
|
20
|
+
INHERIT_COPY = 1 # /* copy into child */
|
21
|
+
INHERIT_NONE = 2 # /* absent from child */
|
22
|
+
INHERIT_DONATE_COPY = 3 # /* copy and delete */
|
23
|
+
INHERIT_DEFAULT = 1 # VM_INHERIT_COPY
|
24
|
+
INHERIT_LAST_VALID = 2 # VM_INHERIT_NONE
|
25
|
+
|
26
|
+
#define VM_REGION_BASIC_INFO_COUNT ((mach_msg_type_number_t) (sizeof(vm_region_basic_info_data_t)/sizeof(int)))
|
27
|
+
#define VM_REGION_EXTENDED_INFO_COUNT ((mach_msg_type_number_t) (sizeof(vm_region_extended_info_data_t)/sizeof(int)))
|
28
|
+
#define VM_REGION_TOP_INFO_COUNT ((mach_msg_type_number_t) (sizeof(vm_region_top_info_data_t)/sizeof(int)))
|
29
|
+
FLAVORS = { REGION_BASIC_INFO => {:size => 30, :count => 9},
|
30
|
+
REGION_EXTENDED_INFO => {:size => 32, :count => 9},
|
31
|
+
REGION_TOP_INFO => {:size => 17,:count => 9}
|
32
|
+
}
|
33
|
+
|
34
|
+
module Pflags
|
35
|
+
READ = 0x1 #read permission
|
36
|
+
WRITE = 0x2 #write permission
|
37
|
+
EXECUTE = 0x4 #execute permission
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Memory region info base class.
|
42
|
+
# Currently Apple only supports the basic flavor. The other two flavors
|
43
|
+
# are included for completeness.
|
44
|
+
#
|
45
|
+
class Ragweed::Wraposx::RegionInfo
|
46
|
+
def initialize(str=nil)
|
47
|
+
refresh(str) if str
|
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 self.get(t, a, flavor)
|
68
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, flavor))
|
69
|
+
end
|
70
|
+
|
71
|
+
def get(t, a)
|
72
|
+
refresh(Ragweed::Wraposx::vm_region_raw(t, a, self.class.const_get(:FLAVOR)))
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
fields = self.class.const_get(:FIELDS)
|
77
|
+
body = lambda do
|
78
|
+
fields.map do |f|
|
79
|
+
"#{f[0]}=#{send(f[0]).to_s}"
|
80
|
+
end.join(", ")
|
81
|
+
end
|
82
|
+
"#<#{self.class.name.split("::").last} #{body.call}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
def dump(&block)
|
86
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
87
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
88
|
+
|
89
|
+
string =<<EOM
|
90
|
+
-----------------------------------------------------------------------
|
91
|
+
INFO:
|
92
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
93
|
+
max_protection: #{self.max_protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.max_protection)}
|
94
|
+
inheritance: #{self.inheritance.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.inheritance)}
|
95
|
+
shared: #{self.shared.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared)}
|
96
|
+
reserved: #{self.reserved.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.reserved)}
|
97
|
+
offset: #{self.offset.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.offset)}
|
98
|
+
behavior: #{self.behavior.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.behavior)}
|
99
|
+
user_wired_count: #{self.user_wired_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_wired_count)}
|
100
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
101
|
+
EOM
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Ragweed::Wraposx::RegionBasicInfo < Ragweed::Wraposx::RegionInfo
|
106
|
+
|
107
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_BASIC_INFO
|
108
|
+
|
109
|
+
(FIELDS = [ [:protection, "i"], # The current protection for the region.
|
110
|
+
[:max_protection, "i"], # The maximum protection allowed for the region.
|
111
|
+
[:inheritance, "I"], # The inheritance attribute for the region.
|
112
|
+
[:shared, "I"], # Shared indicator. If true, the region is shared by another task. If false, the region is not shared.
|
113
|
+
[:reserved, "I"], # If true the region is protected from random allocation.
|
114
|
+
[:offset, "L"], # The region's offset into the memory object. The region begins at this offset.
|
115
|
+
[:behavior, "i"], # Expected reference pattern for the memory.
|
116
|
+
[:user_wired_count, "S"],
|
117
|
+
[:size, "I"] # size of memory region returned
|
118
|
+
]).each {|x| attr_accessor x[0]}
|
119
|
+
|
120
|
+
def dump(&block)
|
121
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
122
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
123
|
+
|
124
|
+
string =<<EOM
|
125
|
+
-----------------------------------------------------------------------
|
126
|
+
INFO:
|
127
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
128
|
+
max_protection: #{self.max_protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.max_protection)}
|
129
|
+
inheritance: #{self.inheritance.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.inheritance)}
|
130
|
+
shared: #{self.shared.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared)}
|
131
|
+
reserved: #{self.reserved.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.reserved)}
|
132
|
+
offset: #{self.offset.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.offset)}
|
133
|
+
behavior: #{self.behavior.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.behavior)}
|
134
|
+
user_wired_count: #{self.user_wired_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_wired_count)}
|
135
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
136
|
+
EOM
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.get(t, a)
|
140
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Ragweed::Wraposx::RegionExtendedInfo < Ragweed::Wraposx::RegionInfo
|
145
|
+
|
146
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_EXTENDED_INFO
|
147
|
+
(FIELDS = [ [:protection, "i"],
|
148
|
+
[:user_tag, "I"],
|
149
|
+
[:pages_resident, "I"],
|
150
|
+
[:pages_shared_now_private, "I"],
|
151
|
+
[:pages_swapped_out, "I"],
|
152
|
+
[:pages_dirtied, "I"],
|
153
|
+
[:ref_count, "I"],
|
154
|
+
[:shadow_depth, "S"],
|
155
|
+
[:external_pager, "C"],
|
156
|
+
[:share_mode, "C"],
|
157
|
+
[:size, "I"] ]).each {|x| attr_accessor x[0]}
|
158
|
+
|
159
|
+
def dump(&block)
|
160
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
161
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
162
|
+
|
163
|
+
string =<<EOM
|
164
|
+
-----------------------------------------------------------------------
|
165
|
+
INFO:
|
166
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
167
|
+
user_tag: #{self.user_tag.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_tag)}
|
168
|
+
pages_resident: #{self.pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_resident)}
|
169
|
+
pages_shared_now_private: #{self.pages_shared_now_private.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_shared_now_private)}
|
170
|
+
pages_swapped_out: #{self.pages_swapped_out.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_swapped_out)}
|
171
|
+
pages_dirtied: #{self.pages_dirtied.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_dirtied)}
|
172
|
+
ref_count: #{self.ref_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ref_count)}
|
173
|
+
shadow_depth: #{self.shadow_depth.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shadow_depth)}
|
174
|
+
external_pager: #{self.external_pager.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.external_pager)}
|
175
|
+
share_mode: #{self.share_mode.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.share_mode)}
|
176
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
177
|
+
EOM
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.get(t, a)
|
181
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class Ragweed::Wraposx::RegionTopInfo < Ragweed::Wraposx::RegionInfo
|
186
|
+
|
187
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_TOP_INFO
|
188
|
+
|
189
|
+
(FIELDS = [ [:obj_id, "I"],
|
190
|
+
[:ref_count, "I"],
|
191
|
+
[:private_pages_resident, "I"],
|
192
|
+
[:shared_pages_resident, "I"],
|
193
|
+
[:share_mode, "C"],
|
194
|
+
[:size, "I"]]).each {|x| attr_accessor x[0]}
|
195
|
+
|
196
|
+
def dump(&block)
|
197
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
198
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
199
|
+
|
200
|
+
string =<<EOM
|
201
|
+
-----------------------------------------------------------------------
|
202
|
+
INFO:
|
203
|
+
obj_id: #{self.obj_id.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.obj_id)}
|
204
|
+
ref_count: #{self.ref_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ref_count)}
|
205
|
+
private_pages_resident: #{self.private_pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.private_pages_resident)}
|
206
|
+
shared_pages_resident: #{self.shared_pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared_pages_resident)}
|
207
|
+
share_mode: #{self.share_mode.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.share_mode)}
|
208
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
209
|
+
EOM
|
210
|
+
end
|
211
|
+
|
212
|
+
def self.get(t, a)
|
213
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
module Ragweed::Wraposx
|
218
|
+
class << self
|
219
|
+
|
220
|
+
# Returns a string containing the memory region information for task
|
221
|
+
# at address.
|
222
|
+
# Currently Apple only supports the basic flavor. The other two flavors
|
223
|
+
# are included for completeness.
|
224
|
+
#
|
225
|
+
# kern_return_t vm_region
|
226
|
+
# (vm_task_t target_task,
|
227
|
+
# vm_address_t address,
|
228
|
+
# vm_size_t size,
|
229
|
+
# vm_region_flavor_t flavor,
|
230
|
+
# vm_region_info_t info,
|
231
|
+
# mach_msg_type_number_t info_count,
|
232
|
+
# memory_object_name_t object_name);
|
233
|
+
def vm_region_raw(task, address, flavor)
|
234
|
+
info = ("\x00"*64).to_ptr
|
235
|
+
count = ([Vm::FLAVORS[flavor][:count]].pack("I_")).to_ptr
|
236
|
+
address = ([address].pack("I_")).to_ptr
|
237
|
+
objn = ([0].pack("I_")).to_ptr
|
238
|
+
sz = ("\x00"*SIZEOFINT).to_ptr
|
239
|
+
r = CALLS["libc!vm_region:IPPIPPP=I"].call(task, address, sz, Vm::FLAVORS[flavor][:count], info, count, objn).first
|
240
|
+
raise KernelCallError.new(:vm_region, r) if r != 0
|
241
|
+
return "#{info.to_s(Vm::FLAVORS[flavor][:size])}#{sz.to_s(SIZEOFINT)}"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -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
|