ragweed 0.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +34 -0
  5. data/examples/hittracertux.rb +49 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/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 +427 -0
  14. data/lib/ragweed/debuggertux.rb +346 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/bblock.rb +73 -0
  18. data/lib/ragweed/rasm/isa.rb +1115 -0
  19. data/lib/ragweed/rasm.rb +59 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +156 -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 +59 -0
  34. data/lib/ragweed/wraposx/constants.rb +122 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +254 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +227 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +433 -0
  40. data/lib/ragweed/wraposx.rb +59 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +7 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +59 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/ragweed.gemspec +34 -0
  47. data/spec/ragweed_spec.rb +7 -0
  48. data/spec/spec_helper.rb +16 -0
  49. data/tasks/ann.rake +80 -0
  50. data/tasks/bones.rake +20 -0
  51. data/tasks/gem.rake +201 -0
  52. data/tasks/git.rake +40 -0
  53. data/tasks/notes.rake +27 -0
  54. data/tasks/post_load.rake +34 -0
  55. data/tasks/rdoc.rake +51 -0
  56. data/tasks/rubyforge.rake +55 -0
  57. data/tasks/setup.rb +292 -0
  58. data/tasks/spec.rake +54 -0
  59. data/tasks/svn.rake +47 -0
  60. data/tasks/test.rake +40 -0
  61. data/tasks/zentest.rake +36 -0
  62. data/test/test_ragweed.rb +0 -0
  63. 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