Module: Ragweed::Wraposx

Defined in:
lib/ragweed/wraposx.rb,
lib/ragweed/wraposx/structs.rb,
lib/ragweed/wraposx/wraposx.rb,
lib/ragweed/wraposx/constants.rb,
lib/ragweed/wraposx/region_info.rb,
lib/ragweed/wraposx/region_info.rb,
lib/ragweed/wraposx/thread_info.rb,
lib/ragweed/wraposx/kernelerrorx.rb,
lib/ragweed/wraposx/thread_context.rb

Defined Under Namespace

Modules: Dl, KErrno, KernelReturn, Libc, Ptrace, Signal, ThreadContext, ThreadInfo, Vm, Wait Classes: FpControl, FpStatus, KernelCallError, MmstReg, TimeValue, XmmReg

Constant Summary collapse

VERSION =

:stopdoc:

File.read(File.join(File.dirname(__FILE__),"..","..","VERSION")).strip
LIBPATH =
::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
PATH =
::File.dirname(LIBPATH) + ::File::SEPARATOR

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.execv(path, *args) ⇒ Object

Changes execution to file in path with *args as though called from command line.

int execv(const char *path, char *const argv[]);



348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/ragweed/wraposx/wraposx.rb', line 348

def execv(path, *args)
  FFI.errno = 0
  args.flatten!
  argv = FFI::MemoryPointer.new(:pointer, args.size + 2)
  argv[0].put_pointer(0, FFI::MemoryPointer.from_string(path.to_s))
  args.each_with_index do |arg, i|
    argv[i + 1].put_pointer(0, FFI::MemoryPointer.from_string(arg.to_s))
  end
  argv[args.size + 1].put_pointer(0, nil)

  Libc.execv(path, argv)
  # if this ever returns, there's been an error
  raise SystemCallError(:execv, FFI.errno)
end

.getpidObject

pid_t getpid(void);

see also getpid(2)



69
70
71
# File 'lib/ragweed/wraposx/wraposx.rb', line 69

def getpid
  Libc.getpid
end

.kill(pid, sig) ⇒ Object

Sends a signal to a process

int kill(pid_t pid, int sig);

See kill(2)



228
229
230
231
232
233
# File 'lib/ragweed/wraposx/wraposx.rb', line 228

def kill(pid, sig)
  FFI::errno = 0
  r = Libc.kill(pid, sig)
  raise SystemCallError.new "kill", FFI::errno if r != 0
  r
end

.libpath(*args) ⇒ Object

Returns the library path for the module. If any arguments are given, they will be joined to the end of the libray path using File.join.



23
24
25
# File 'lib/ragweed/wraposx.rb', line 23

def self.libpath( *args )
  args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
end

.mach_task_selfObject

From docs at web.mit.edu/darwin/src/modules/xnu/osfmk/man/mach_task_self.html Returns send rights to the task’s kernel port.

mach_port_t mach_task_self(void)

There is no man page for this call.



167
168
169
# File 'lib/ragweed/wraposx/wraposx.rb', line 167

def mach_task_self
  Libc.mach_task_self
end

.path(*args) ⇒ Object

Returns the lpath for the module. If any arguments are given, they will be joined to the end of the path using File.join.



31
32
33
# File 'lib/ragweed/wraposx.rb', line 31

def self.path( *args )
  args.empty? ? PATH : ::File.join(PATH, args.flatten)
end

.pt_attach(pid) ⇒ Object

ptrace(PT_ATTACH, … )



115
116
117
# File 'lib/ragweed/wraposx/wraposx.rb', line 115

def pt_attach pid
  ptrace(Ragweed::Wraposx::Ptrace::ATTACH, pid, nil, nil).first
end

.pt_continue(pid, addr = 1, sig = 0) ⇒ Object

ptrace(PT_CONTINUE, pid, addr, signal)



100
101
102
# File 'lib/ragweed/wraposx/wraposx.rb', line 100

def pt_continue pid, addr = 1, sig = 0
  ptrace(Ragweed::Wraposx::Ptrace::CONTINUE, pid, addr, sig).first
end

.pt_deny_attach(pid) ⇒ Object

ptrace(PT_DENY_ATTACH, … )



95
96
97
# File 'lib/ragweed/wraposx/wraposx.rb', line 95

def pt_deny_attach pid
  ptrace(Ragweed::Wraposx::Ptrace::DENY_ATTACH, pid, nil, nil).first
end

.pt_detach(pid) ⇒ Object

ptrace(PT_DETACH, … )



120
121
122
# File 'lib/ragweed/wraposx/wraposx.rb', line 120

def pt_detach pid
  ptrace(Ragweed::Wraposx::Ptrace::DETACH, pid, nil, nil).first
end

.pt_kill(pid) ⇒ Object

ptrace(PT_KILL, … )



110
111
112
# File 'lib/ragweed/wraposx/wraposx.rb', line 110

def pt_kill pid
  ptrace(Ragweed::Wraposx::Ptrace::KILL, pid, nil, nil).first
end

.pt_step(pid, addr = 1, sig = 0) ⇒ Object

ptrace(PT_STEP, pid, addr, signal)



105
106
107
# File 'lib/ragweed/wraposx/wraposx.rb', line 105

def pt_step pid, addr = 1, sig = 0
  ptrace(Ragweed::Wraposx::Ptrace::STEP, pid, addr, sig).first
end

.pt_trace_me(pid) ⇒ Object

ptrace(PT_TRACE_ME, …)



90
91
92
# File 'lib/ragweed/wraposx/wraposx.rb', line 90

def pt_trace_me pid
  ptrace(Ragweed::Wraposx::Ptrace::TRACE_ME, pid, nil, nil).first
end

.ptrace(request, pid, addr, data) ⇒ Object

Apple’s ptrace is fairly gimped. The memory read and write functionality has been removed. We will be using mach kernel calls for that. see vm_read and vm_write. for details on ptrace and the process for the Wraposx/debuggerosx port see: www.matasano.com/log/1100/what-ive-been-doing-on-my-summer-vacation-or-it-has-to-work-otherwise-gdb-wouldnt/

int ptrace(int request, pid_t pid, caddr_t addr, int data);

see also ptrace(2)

Raises:

  • (SystemCallError)


82
83
84
85
86
87
# File 'lib/ragweed/wraposx/wraposx.rb', line 82

def ptrace(request, pid, addr, data)
  FFI.errno = 0
  r = Libc.ptrace(request, pid, addr, data)
  raise SystemCallError.new("ptrace", FFI.errno) if r == -1 and FFI.errno != 0
  [r, data]
end

.require_all_libs_relative_to(fname, dir = nil) ⇒ Object

Utility method used to require all files ending in .rb that lie in the directory below this file that has the same name as the filename passed in. Optionally, a specific directory name can be passed in such that the filename does not have to be equivalent to the directory.



45
46
47
48
49
50
51
52
53
54
# File 'lib/ragweed/wraposx.rb', line 45

def self.require_all_libs_relative_to( fname, dir = nil )
  self.require_utils
  dir ||= ::File.basename(fname, '.*')
  search_me = ::File.expand_path(
      ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
  
  Dir.glob(search_me).reject{|rb| rb =~ /#{__FILE__}/}.sort.each {|rb| require rb}
  # require File.dirname(File.basename(__FILE__)) + "/#{x}"

end

.require_utilsObject

Utility function to load utility classes and extensions



36
37
38
# File 'lib/ragweed/wraposx.rb', line 36

def self.require_utils
  %w{utils}.each{|r| require self.libpath(r)+'.rb'}
end

.task_for_pid(pid, target = nil) ⇒ Object

Requires sudo to use as of 10.5 or 10.4.11(ish) Returns the task id for a process.

kern_return_t task_for_pid(

mach_port_name_t target_tport,
int pid,
mach_port_name_t *t);

There is no man page for this call.

Raises:



180
181
182
183
184
185
186
# File 'lib/ragweed/wraposx/wraposx.rb', line 180

def task_for_pid(pid, target=nil)
  target ||= mach_task_self
  port = FFI::MemoryPointer.new :uint, 1
  r = Libc.task_for_pid(target, pid, port)
  raise KernelCallError.new(:task_for_pid, r) if r != 0
  port.read_uint
end

.task_resume(task) ⇒ Object

Decrement the target tasks suspend count kern_return_t task_resume

(task_t          task);

Raises:



207
208
209
210
211
# File 'lib/ragweed/wraposx/wraposx.rb', line 207

def task_resume(task)
  r = Libc.task_resume(task)
  raise KernelCallError.new(r) if r != 0
  r
end

.task_suspend(task) ⇒ Object

Increment the target tasks suspend count kern_return_t task_suspend

(task_t          task);

Raises:



216
217
218
219
220
# File 'lib/ragweed/wraposx/wraposx.rb', line 216

def task_suspend(task)
  r = Libc.task_suspend(task)
  raise KernelCallError.new(r) if r != 0
  r
end

.task_threads(port) ⇒ Object

Returns an Array of thread IDs for the given task

kern_return_t task_threads

(task_t                                    task,
 thread_act_port_array_t            thread_list,
 mach_msg_type_number_t*           thread_count);

There is no man page for this funtion.

Raises:



196
197
198
199
200
201
202
# File 'lib/ragweed/wraposx/wraposx.rb', line 196

def task_threads(port)
  threads = FFI::MemoryPointer.new :pointer, 1
  count = FFI::MemoryPointer.new :int, 1
  r = Libc.task_threads(port, threads, count)
  raise KernelCallError.new(:task_threads, r) if r != 0
  threads.read_pointer.read_array_of_uint(count.read_uint)
end

.thread_get_state(thread, flavor) ⇒ Object

Returns a thread’s registers for given a thread id

kern_return_t thread_get_state

(thread_act_t                     target_thread,
 thread_state_flavor_t                   flavor,
 thread_state_t                       old_state,
 mach_msg_type_number_t         *old_state_count);

Raises:



1000
1001
1002
1003
1004
1005
1006
# File 'lib/ragweed/wraposx/thread_context.rb', line 1000

def thread_get_state(thread,flavor)
  state = FFI::MemoryPointer.new Ragweed::Wraposx::ThreadContext::FLAVORS[flavor][:class]
  count = FFI::MemoryPointer.new(:int, 1).write_uint Ragweed::Wraposx::ThreadContext::FLAVORS[flavor][:count]
  r = Libc.thread_get_state(thread, flavor, state, count)
  raise KernelCallError.new(:thread_get_state, r) if r != 0
  Ragweed::Wraposx::ThreadContext::FLAVORS[flavor][:class].new state
end

.thread_info(thread, flavor) ⇒ Object

Returns the thread_info_t struct.

kern_return_t thread_info

(thread_act_t                     target_thread,
 thread_flavor_t                         flavor,
 thread_info_t                      thread_info,
 mach_msg_type_number_t       thread_info_count);

Raises:



160
161
162
163
164
165
166
# File 'lib/ragweed/wraposx/thread_info.rb', line 160

def thread_info(thread, flavor)
  info = FFI::MemoryPointer.new(ThreadInfo::FLAVORS[flavor][:class], 1)
  count = FFI::MemoryPointer.new(:int, 1).write_int(ThreadInfo::FLAVORS[flavor][:count])
  r = Libc.thread_info(thread, flavor, info, count)
  raise KernelCallError.new(r) if r != 0
  ThreadInfo::FLAVORS[flavor][:class].new info
end

.thread_resume(thread) ⇒ Object

Resumes a suspended thread by id.

kern_return_t thread_resume

(thread_act_t                     target_thread);

There is no man page for this function.

Raises:



326
327
328
329
330
# File 'lib/ragweed/wraposx/wraposx.rb', line 326

def thread_resume(thread)
  r = Libc.thread_resume(thread)
  raise KernelCallError.new(:thread_resume, r) if r != 0
  r
end

.thread_set_state(thread, flavor, state) ⇒ Object

Sets the register state of thread.

kern_return_t thread_set_state

(thread_act_t                     target_thread,
 thread_state_flavor_t                   flavor,
 thread_state_t                       new_state,
 mach_msg_type_number_t         new_state_count);

Raises:



1015
1016
1017
1018
1019
# File 'lib/ragweed/wraposx/thread_context.rb', line 1015

def thread_set_state(thread, flavor, state)
  r = Libc.thread_set_state(thread, flavor, state.to_ptr, ThreadContext::FLAVORS[flavor][:count])
  raise KernelCallError.new(:thread_set_state, r) if r!= 0
  Ragweed::Wraposx::ThreadContext::FLAVORS[flavor][:class].new state
end

.thread_suspend(thread) ⇒ Object

Suspends a thread by id.

kern_return_t thread_suspend

(thread_act_t                     target_thread);

There is no man page for this function.

Raises:



338
339
340
341
342
# File 'lib/ragweed/wraposx/wraposx.rb', line 338

def thread_suspend(thread)
  r = Libc.thread_suspend(thread)
  raise KernelCallError.new(:thread_suspend, r) if r != 0
  r
end

.versionObject

Returns the version string for the library.



15
16
17
# File 'lib/ragweed/wraposx.rb', line 15

def self.version
  VERSION
end

.vm_allocate(task, address, size, anywhere) ⇒ Object

Allocates a page in the memory space of the target task.

kern_return_t vm_allocate

(vm_task_t                          target_task,
 vm_address_t                           address,
 vm_size_t                                 size,
 boolean_t                             anywhere);

Raises:



296
297
298
299
300
301
302
303
# File 'lib/ragweed/wraposx/wraposx.rb', line 296

def vm_allocate(task, address, size, anywhere)
  addr = FFI::MemoryPointer.new :int, 1
  addr.write_int(address)
  anywhere = anywhere ? 1 : 0
  r = Libc.vm_allocate(task, addr, size, anywhere)
  raise KernelCallError.new(r) if r != 0
  addr.address
end

.vm_deallocate(task, address, size) ⇒ Object

deallocates a page in the memoryspace of target task.

kern_return_t vm_deallocate

(vm_task_t                          target_task,
 vm_address_t                           address,
 vm_size_t                                 size);

Raises:



312
313
314
315
316
317
318
# File 'lib/ragweed/wraposx/wraposx.rb', line 312

def vm_deallocate(task, address, size)
  addr = FFI::MemoryPointer.new :int, 1
  addr.write_int(address)
  r = Libc.vm_deallocate(task, addr, size)
  raise KernelCallError.new(r) if r != 0
  r
end

.vm_protect(task, addr, size, setmax, prot) ⇒ Object

Changes the protection state beginning at addr for size bytes to the mask prot. If setmax is true this will set the maximum permissions, otherwise it will set FIXME

kern_return_t vm_protect

(vm_task_t           target_task,
 vm_address_t            address,
 vm_size_t                  size,
 boolean_t           set_maximum,
 vm_prot_t        new_protection);

There is no man page for this function.

Raises:



281
282
283
284
285
286
# File 'lib/ragweed/wraposx/wraposx.rb', line 281

def vm_protect(task, addr, size, setmax, prot)
  setmax = setmax ? 1 : 0
  r = Libc.vm_protect(task, addr, size, setmax, prot)
  raise KernelCallError.new(:vm_protect, r) if r != 0
  r
end

.vm_read(task, addr, sz = 256) ⇒ Object

Reads sz bytes from task’s address space starting at addr.

kern_return_t vm_read_overwrite

(vm_task_t                           target_task,
 vm_address_t                        address,
 vm_size_t                           size,
 vm_address_t                        *data_out,
 mach_msg_type_number_t              *data_size);

There is no man page for this function.

Raises:



245
246
247
248
249
250
251
# File 'lib/ragweed/wraposx/wraposx.rb', line 245

def vm_read(task, addr, sz=256)
  buf = FFI::MemoryPointer.new(sz)
  len = FFI::MemoryPointer.new(:uint).write_uint(sz)
  r = Libc.vm_read_overwrite(task, addr, sz, buf, len)
  raise KernelCallError.new(:vm_read, r) if r != 0
  buf.read_string(len.read_uint)
end

.vm_write(task, addr, val) ⇒ Object

Writes val to task’s memory space at address addr. It is necessary for val.size to report the size of val in bytes

kern_return_t vm_write

(vm_task_t                          target_task,
 vm_address_t                           address,
 pointer_t                                 data,
 mach_msg_type_number_t              data_count);

There is no man page for this function.

Raises:



263
264
265
266
267
268
# File 'lib/ragweed/wraposx/wraposx.rb', line 263

def vm_write(task, addr, val)
  val = FFI::MemoryPointer.new(val)
  r = Libc.vm_write(task, addr, val, val.size)
  raise KernelCallError.new(:vm_write, r) if r != 0
  r
end

.waitObject

Originally coded for use in debuggerosx but I’ve switched to waitpid for usability and debugging purposes.

Returns status of child when child recieves a signal.

pid_t wait(int *stat_loc);

see also wait(2)



133
134
135
136
137
138
139
# File 'lib/ragweed/wraposx/wraposx.rb', line 133

def wait
  stat = FFI::MemoryPointer.new :int, 1
  FFI.errno = 0
  pid = Libc.wait stat
  raise SystemCallError.new "wait", FFI.errno if pid == -1
  [pid, stat.read_int]
end

.waitpid(pid, opts = 0) ⇒ Object

The wait used in debuggerosx. opt is an OR of the options to be used.

Returns an array. The first element is the pid of the child process as returned by the waitpid system call. The second, the status as an integer of that pid.

pid_t waitpid(pid_t pid, int *stat_loc, int options);

see also wait(2)



152
153
154
155
156
157
158
# File 'lib/ragweed/wraposx/wraposx.rb', line 152

def waitpid pid, opts = 0
  stat = FFI::MemoryPointer.new :int, 1
  FFI.errno = 0
  r = Libc.waitpid(pid, stat, opts)
  raise SystemCallError.new "waitpid", FFI.errno if r == -1
  [r, stat.read_int]
end

Instance Method Details

#vm_region(task, addr, flavor) ⇒ Object

Returns the base address, size, and a pointer to the requested information about the memory region at address in the target_task.

Currently, only VM_REGION_BASIC_INFO is supported by Apple. Unless this is being run in 32bits, use vm_region_64 instead.

kern_return_t vm_region

(vm_map_t                   target_task,
 vm_address_t                  *address,
 vm_size_t                        *size,
 vm_region_flavor_t              flavor,
 vm_region_info_t                  info,
 mach_msg_type_number_t     *info_count,
 mach_port_t               *object_name);

Raises:



236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/ragweed/wraposx/region_info.rb', line 236

def vm_region(task, addr, flavor)
  info = FFI::MemoryPointer.new(Vm::FLAVORS[flavor][:class], 1)
  count = FFI::MemoryPointer.new(:int, 1).write_int(Vm::FLAVORS[flavor][:count])
  address = FFI::MemoryPointer.new(Libc.find_type(:vm_address_t), 1).write_ulong(addr)
  sz = FFI::MemoryPointer.new(Libc.find_type(:vm_size_t), 1)
  objn = FFI::MemoryPointer.new(Libc.find_type(:mach_port_t), 1)
  
  r = Libc.vm_region(task, address, sz, flavor, info, count, objn)
  raise KernelCallError.new(:vm_region, r) if r != 0
  ret = Vm::Flavors[flavor][:class].new info
  ret.region_size = size.read_ulong
  ret.base_address = address.read_ulong
  ret
end

#vm_region_64(task, addr, flavor) ⇒ Object

Returns the base address, size, and a pointer to the requested information about the memory region at address in the target_task.

Currently, only VM_REGION_BASIC_INFO is supported by Apple.

kern_return_t vm_region

(vm_map_t                   target_task,
 vm_address_t                  *address,
 vm_size_t                        *size,
 vm_region_flavor_t              flavor,
 vm_region_info_t                  info,
 mach_msg_type_number_t     *info_count,
 mach_port_t               *object_name);

Raises:



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/ragweed/wraposx/region_info.rb', line 264

def vm_region_64(task, addr, flavor)
  # OSX does this as well, so we need to do it ourselves
  flavor = Vm::REGION_BASIC_INFO_64 if flavor == Vm::REGION_BASIC_INFO
  info = FFI::MemoryPointer.new(Vm::FLAVORS[flavor][:class])
  count = FFI::MemoryPointer.new(Libc.find_type(:mach_msg_type_number_t), 1).write_uint(Vm::FLAVORS[flavor][:count])
  address = FFI::MemoryPointer.new(Libc.find_type(:vm_address_t), 1).write_ulong(addr)
  sz = FFI::MemoryPointer.new(Libc.find_type(:vm_size_t), 1)
  objn = FFI::MemoryPointer.new(Libc.find_type(:mach_port_t), 1)
  
  r = Libc.vm_region_64(task, address, sz, flavor, info, count, objn)
  raise KernelCallError.new(:vm_region_64, r) if r != 0
  ret = Vm::FLAVORS[flavor][:class].new info
  ret.region_size = sz.read_ulong
  ret.base_address = address.read_ulong
  ret
end