ragweed 0.1.7.2
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 +35 -0
- data/README.txt +9 -0
- data/Rakefile +34 -0
- data/examples/hittracertux.rb +49 -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 +427 -0
- data/lib/ragweed/debuggertux.rb +346 -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 +156 -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 +59 -0
- data/lib/ragweed/wraposx/constants.rb +122 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +254 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +227 -0
- data/lib/ragweed/wraposx/wraposx.rb +433 -0
- data/lib/ragweed/wraposx.rb +59 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +59 -0
- data/lib/ragweed.rb +84 -0
- data/ragweed.gemspec +34 -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 +132 -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
|
+
@@list ||= 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,254 @@
|
|
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 => 8},
|
30
|
+
REGION_EXTENDED_INFO => {:size => 32, :count => 8},
|
31
|
+
REGION_TOP_INFO => {:size => 17,:count => 5}
|
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
|
+
#
|
43
|
+
# to change slightly in 0.2.0+
|
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
|
+
if str and not str.empty?
|
54
|
+
str.unpack(fields.map {|x| x[1]}.join("")).each_with_index do |val, i|
|
55
|
+
raise "i is nil" if i.nil?
|
56
|
+
instance_variable_set "@#{ fields[i][0] }".intern, val
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
fields = self.class.const_get :FIELDS
|
63
|
+
fields.map {|f| send(f[0])}.pack(fields.map {|x| x[1]}.join(""))
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.get(t, a, flavor)
|
67
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, flavor))
|
68
|
+
end
|
69
|
+
|
70
|
+
def get(t, a)
|
71
|
+
refresh(Ragweed::Wraposx::vm_region_raw(t, a, self.class.const_get(:FLAVOR)))
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect
|
75
|
+
fields = self.class.const_get(:FIELDS)
|
76
|
+
body = lambda do
|
77
|
+
fields.map do |f|
|
78
|
+
"#{f[0]}=#{send(f[0]).to_s}"
|
79
|
+
end.join(", ")
|
80
|
+
end
|
81
|
+
"#<#{self.class.name.split("::").last} #{body.call}>"
|
82
|
+
end
|
83
|
+
|
84
|
+
def dump(&block)
|
85
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
86
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
87
|
+
|
88
|
+
string =<<EOM
|
89
|
+
-----------------------------------------------------------------------
|
90
|
+
INFO:
|
91
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
92
|
+
max_protection: #{self.max_protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.max_protection)}
|
93
|
+
inheritance: #{self.inheritance.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.inheritance)}
|
94
|
+
shared: #{self.shared.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared)}
|
95
|
+
reserved: #{self.reserved.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.reserved)}
|
96
|
+
offset: #{self.offset.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.offset)}
|
97
|
+
behavior: #{self.behavior.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.behavior)}
|
98
|
+
user_wired_count: #{self.user_wired_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_wired_count)}
|
99
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
100
|
+
EOM
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Ragweed::Wraposx::RegionBasicInfo < Ragweed::Wraposx::RegionInfo
|
105
|
+
|
106
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_BASIC_INFO
|
107
|
+
|
108
|
+
(FIELDS = [ [:protection, "i"], # The current protection for the region.
|
109
|
+
[:max_protection, "i"], # The maximum protection allowed for the region.
|
110
|
+
[:inheritance, "I"], # The inheritance attribute for the region.
|
111
|
+
[:shared, "I"], # Shared indicator. If true, the region is shared by another task. If false, the region is not shared.
|
112
|
+
[:reserved, "I"], # If true the region is protected from random allocation.
|
113
|
+
[:offset, "L"], # The region's offset into the memory object. The region begins at this offset.
|
114
|
+
[:behavior, "i"], # Expected reference pattern for the memory.
|
115
|
+
[:user_wired_count, "S"],
|
116
|
+
[:size, "I"], # size of memory region returned
|
117
|
+
[:base_address, "L"]
|
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
|
+
BASIC INFO:
|
127
|
+
base address: #{self.base_address.to_s(16).rjust(8, "0")}
|
128
|
+
|
129
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
130
|
+
max_protection: #{self.max_protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.max_protection)}
|
131
|
+
inheritance: #{self.inheritance.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.inheritance)}
|
132
|
+
shared: #{self.shared.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared)}
|
133
|
+
reserved: #{self.reserved.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.reserved)}
|
134
|
+
offset: #{self.offset.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.offset)}
|
135
|
+
behavior: #{self.behavior.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.behavior)}
|
136
|
+
user_wired_count: #{self.user_wired_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_wired_count)}
|
137
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
138
|
+
EOM
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.get(t, a)
|
142
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Ragweed::Wraposx::RegionExtendedInfo < Ragweed::Wraposx::RegionInfo
|
147
|
+
|
148
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_EXTENDED_INFO
|
149
|
+
(FIELDS = [ [:protection, "i"],
|
150
|
+
[:user_tag, "I"],
|
151
|
+
[:pages_resident, "I"],
|
152
|
+
[:pages_shared_now_private, "I"],
|
153
|
+
[:pages_swapped_out, "I"],
|
154
|
+
[:pages_dirtied, "I"],
|
155
|
+
[:ref_count, "I"],
|
156
|
+
[:shadow_depth, "S"],
|
157
|
+
[:external_pager, "C"],
|
158
|
+
[:share_mode, "C"],
|
159
|
+
[:size, "I"],
|
160
|
+
[:base_address, "I"] ]).each {|x| attr_accessor x[0]}
|
161
|
+
|
162
|
+
def dump(&block)
|
163
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
164
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
165
|
+
|
166
|
+
string =<<EOM
|
167
|
+
-----------------------------------------------------------------------
|
168
|
+
EXTENDED INFO:
|
169
|
+
base address: #{self.base_address.to_s(16).rjust(8, "0")}
|
170
|
+
|
171
|
+
protection: #{self.protection.to_s(2).rjust(8, "0")} #{Ragweed::Wraposx::Vm::Pflags.flag_dump(self.protection)}
|
172
|
+
user_tag: #{self.user_tag.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.user_tag)}
|
173
|
+
pages_resident: #{self.pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_resident)}
|
174
|
+
pages_shared_now_private: #{self.pages_shared_now_private.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_shared_now_private)}
|
175
|
+
pages_swapped_out: #{self.pages_swapped_out.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_swapped_out)}
|
176
|
+
pages_dirtied: #{self.pages_dirtied.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.pages_dirtied)}
|
177
|
+
ref_count: #{self.ref_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ref_count)}
|
178
|
+
shadow_depth: #{self.shadow_depth.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shadow_depth)}
|
179
|
+
external_pager: #{self.external_pager.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.external_pager)}
|
180
|
+
share_mode: #{self.share_mode.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.share_mode)}
|
181
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
182
|
+
EOM
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.get(t, a)
|
186
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Ragweed::Wraposx::RegionTopInfo < Ragweed::Wraposx::RegionInfo
|
191
|
+
|
192
|
+
FLAVOR = Ragweed::Wraposx::Vm::REGION_TOP_INFO
|
193
|
+
|
194
|
+
(FIELDS = [ [:obj_id, "I"],
|
195
|
+
[:ref_count, "I"],
|
196
|
+
[:private_pages_resident, "I"],
|
197
|
+
[:shared_pages_resident, "I"],
|
198
|
+
[:share_mode, "C"],
|
199
|
+
[:size, "I"],
|
200
|
+
[:base_address,"I"]]).each {|x| attr_accessor x[0]}
|
201
|
+
|
202
|
+
def dump(&block)
|
203
|
+
maybe_hex = lambda {|a| begin; "\n" + (" " * 9) + block.call(a, 16).hexdump(true)[10..-2]; rescue; ""; end }
|
204
|
+
maybe_dis = lambda {|a| begin; "\n" + block.call(a, 16).distorm.map {|i| " " + i.mnem}.join("\n"); rescue; ""; end }
|
205
|
+
|
206
|
+
string =<<EOM
|
207
|
+
-----------------------------------------------------------------------
|
208
|
+
TOP INFO:
|
209
|
+
base address: #{self.base_address.to_s(16).rjust(8, "0")}
|
210
|
+
|
211
|
+
obj_id: #{self.obj_id.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.obj_id)}
|
212
|
+
ref_count: #{self.ref_count.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.ref_count)}
|
213
|
+
private_pages_resident: #{self.private_pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.private_pages_resident)}
|
214
|
+
shared_pages_resident: #{self.shared_pages_resident.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.shared_pages_resident)}
|
215
|
+
share_mode: #{self.share_mode.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.share_mode)}
|
216
|
+
size: #{self.size.to_s(16).rjust(8, "0")} #{maybe_hex.call(self.size)}
|
217
|
+
EOM
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.get(t, a)
|
221
|
+
self.new(Ragweed::Wraposx::vm_region_raw(t, a, FLAVOR))
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
module Ragweed::Wraposx
|
226
|
+
class << self
|
227
|
+
|
228
|
+
# Returns a string containing the memory region information for task
|
229
|
+
# at address.
|
230
|
+
#
|
231
|
+
# The order of the elements in the returned string will change in 0.2.0+
|
232
|
+
# to match the argument order.
|
233
|
+
#
|
234
|
+
# kern_return_t vm_region
|
235
|
+
# (vm_task_t target_task,
|
236
|
+
# vm_address_t address,
|
237
|
+
# vm_size_t size,
|
238
|
+
# vm_region_flavor_t flavor,
|
239
|
+
# vm_region_info_t info,
|
240
|
+
# mach_msg_type_number_t info_count,
|
241
|
+
# memory_object_name_t object_name);
|
242
|
+
def vm_region_raw(task, address, flavor)
|
243
|
+
info = ("\x00"*Vm::FLAVORS[flavor][:size]).to_ptr
|
244
|
+
count = ([Vm::FLAVORS[flavor][:count]].pack("I_")).to_ptr
|
245
|
+
address = ([address].pack("I_")).to_ptr
|
246
|
+
objn = ([0].pack("I_")).to_ptr
|
247
|
+
sz = ("\x00"*SIZEOFINT).to_ptr
|
248
|
+
r = CALLS["libc!vm_region:IPPIPPP=I"].call(task, address, sz, flavor, info, count, objn).first
|
249
|
+
raise KernelCallError.new(:vm_region, r) if r != 0
|
250
|
+
# this
|
251
|
+
return "#{info.to_s(Vm::FLAVORS[flavor][:size])}#{sz.to_s(SIZEOFINT)}#{address.to_s(SIZEOFINT)}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
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
|
+
# TODO - 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
|