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.
Files changed (62) hide show
  1. data/History.txt +15 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +48 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/arena.rb +55 -0
  11. data/lib/ragweed/blocks.rb +128 -0
  12. data/lib/ragweed/debugger32.rb +338 -0
  13. data/lib/ragweed/debuggerosx.rb +419 -0
  14. data/lib/ragweed/debuggertux.rb +347 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/isa.rb +1046 -0
  18. data/lib/ragweed/rasm/util.rb +26 -0
  19. data/lib/ragweed/rasm.rb +53 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +87 -0
  23. data/lib/ragweed/wrap32/debugging.rb +163 -0
  24. data/lib/ragweed/wrap32/device.rb +49 -0
  25. data/lib/ragweed/wrap32/event.rb +50 -0
  26. data/lib/ragweed/wrap32/hooks.rb +23 -0
  27. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  28. data/lib/ragweed/wrap32/process.rb +506 -0
  29. data/lib/ragweed/wrap32/process_token.rb +59 -0
  30. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  31. data/lib/ragweed/wrap32/winx.rb +16 -0
  32. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  33. data/lib/ragweed/wrap32.rb +53 -0
  34. data/lib/ragweed/wraposx/constants.rb +101 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +244 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +213 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  40. data/lib/ragweed/wraposx.rb +53 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +3 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +53 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. metadata +127 -0
@@ -0,0 +1,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