Class: Ragweed::Process
- Includes:
- Ragweed
- Defined in:
- lib/ragweed/wrap32/process.rb,
lib/ragweed/wraptux/process.rb
Defined Under Namespace
Classes: AdlerChart
Constant Summary
Constants included from Ragweed
Instance Attribute Summary collapse
-
#pid ⇒ Object
readonly
Returns the value of attribute pid.
Class Method Summary collapse
-
.by_name(n) ⇒ Object
Look up a process by name or regex, returning an array of all matching processes, as objects.
- .find_by_regex(name) ⇒ Object
Instance Method Summary collapse
-
#adler_chart(i) ⇒ Object
See WinProcess::AdlerChart.
-
#adler_compare(i, orig, opts = {}) ⇒ Object
If you store the adler map, you’ve compressed the memory region down to a small series of fixnums, and you can use it with this function to re-check the memory region and see if anything’s changing.
-
#adler_map(i, opts = {}) ⇒ Object
Like entropy_map, scan a process and compute adler16 checksums for 1k (or :window) blocks.
-
#alloc(sz, syscall = false) ⇒ Object
Use arenas, when possible, to quickly allocate memory.
-
#arena(&block) ⇒ Object
Get another allocation arena for this process.
-
#changes(i, sleeptime = 0.5) ⇒ Object
Given a memory region, use the adler routines to create a checksum map, wait a short period of time, and scan for changes, to find churning memory.
- #detour(loc, o = {}) ⇒ Object
-
#dump_memory(i, opts = {}) ⇒ Object
Print a canonical hexdump of an entire memory region by region number.
-
#dump_memory_list ⇒ Object
Human-readable standard output of list_memory.
-
#dump_stack_trace(tid) ⇒ Object
(also: #bt)
Human-readable version of thread_stack_trace, with module offsets.
-
#dup_handle(h) ⇒ Object
clone a handle from the remote process to here (to here? tf?).
-
#entropy_map(i, opts = {}) ⇒ Object
Take a region of memory and walk over it in 255-byte samples (less than 255 bytes and you lose accuracy, but you can increase it with the “:window” option), computing entropy for each sample, returning a list of [offset,entropy] tuples.
-
#free(off) ⇒ Object
Free the return value of syscall_alloc.
-
#get_deferred_proc_remote(name, handle, base_of_dll) ⇒ Object
This only gets called for breakpoints in modules that have just been loaded and detected by a LOAD_DLL event.
-
#get_memory(i, opts = {}) ⇒ Object
Read an entire memory region into a string by region number.
-
#get_proc(name) ⇒ Object
look up a process by its name — but this is in the local process, which is broken — a heuristic that sometimes works for w32 functions, but probably never otherwise.
-
#get_proc_remote(name) ⇒ Object
This only gets called for breakpoints in modules that are already loaded.
- #handle ⇒ Object
-
#hunt(key, opts = {}) ⇒ Object
Given a string key, find it in memory.
-
#image ⇒ Object
Return the EXE name of the process.
-
#initialize(pid) ⇒ Process
constructor
Just need a PID to get started.
-
#insert(buf) ⇒ Object
Insert a string anywhere into the memory of the remote process, returning its address, using an arena.
-
#is_breakpoint_deferred(ip) ⇒ Object
Check if breakpoint location is deferred This method expects a string ‘module!function’ true is the module is not yet loaded false is the module is loaded.
- #is_hex(s) ⇒ Object
-
#list_memory(&block) ⇒ Object
List all memory regions in the remote process by iterating over VirtualQueryEx.
-
#modules(&block) ⇒ Object
List the modules for the process, either yielding a struct for each to a block, or returning a list.
-
#pointers_to(src, dst, opts = {}) ⇒ Object
Given a source and destination memory region, scan through “source” looking for properly-aligned U32LE values that would be valid pointers into “destination”.
-
#ptr(x) ⇒ Object
Get a pointer into the remote process; pointers are just fixnums with a read/write method and a to_s.
-
#read(off, sz = 4096) ⇒ Object
Read/write ranges of data or fixnums to/from the process by address.
- #read16(off) ⇒ Object
- #read32(off) ⇒ Object
- #read8(off) ⇒ Object
-
#region_range(i, opts = {}) ⇒ Object
Get the memory range, as a Ruby Range, for a region by index.
-
#remote_call(meth, *args) ⇒ Object
call a function, by name or address, in the process, using CreateRemoteThread.
-
#resume(tid) ⇒ Object
Resume a thread by tid.
-
#resume_all ⇒ Object
Resume all the threads in the process.
-
#scan(i, opts = {}) ⇒ Object
In Python, and maybe Ruby, it was much faster to work on large memory regions a 4k page at a time, rather than reading the whole thing into one big string.
-
#strings_mem(i, opts = {}) ⇒ Object
Given a memory region number, do a Unix strings(1) on it.
-
#suspend(tid) ⇒ Object
Suspend a thread by tid.
-
#suspend_all ⇒ Object
Suspend all the threads in the process.
-
#syscall_alloc(sz) ⇒ Object
Use VirtualAllocEx to grab a block of memory in the process.
-
#thread_context(tid) ⇒ Object
Dump thread context, returning a struct that contains things like .Eip and .Eax.
-
#thread_stack_trace(tid) ⇒ Object
For libraries compiled with frame pointers: walk EBP back until it stops giving intelligible addresses, and, at each step, grab the saved EIP from just before it.
-
#threads(full = false, &block) ⇒ Object
Return a list of all the threads in the process; relatively expensive, so cache the result.
-
#to_modoff(off, force = false) ⇒ Object
Convert an address to “module+10h” notation, when possible.
-
#which_region_has?(addr, opts = {}) ⇒ Boolean
Figure out what region (by region index) has an address.
-
#with_suspended_thread(tid) ⇒ Object
Do something with a thread while its suspended.
-
#write(off, data) ⇒ Object
ptrace sucks, writing 8 or 16 bytes will probably result in failure unless you PTRACE_POKE first and get the rest of the original value at the address.
- #write16(off, v) ⇒ Object
- #write32(off, v) ⇒ Object
- #write8(off, v) ⇒ Object
-
#writeable?(off) ⇒ Boolean
Can I write to this address in the process?.
Methods included from Ragweed
libpath, path, require_all_libs_relative_to, require_os_libs_relative_to, version
Constructor Details
#initialize(pid) ⇒ Process
Just need a PID to get started.
154 155 156 157 158 |
# File 'lib/ragweed/wrap32/process.rb', line 154 def initialize(pid) @pid = pid @h = Ragweed::Wrap32::open_process(pid) @a = arena() end |
Instance Attribute Details
#pid ⇒ Object (readonly)
Returns the value of attribute pid.
3 4 5 |
# File 'lib/ragweed/wrap32/process.rb', line 3 def pid @pid end |
Class Method Details
.by_name(n) ⇒ Object
Look up a process by name or regex, returning an array of all matching processes, as objects.
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/ragweed/wrap32/process.rb', line 142 def self.by_name(n) n = Regexp.new(n) if not n.kind_of? Regexp p = [] all_processes do |px| if px.szExeFile =~ n p << self.new(px.th32ProcessID) end end p end |
.find_by_regex(name) ⇒ Object
6 7 8 9 10 11 12 |
# File 'lib/ragweed/wrap32/process.rb', line 6 def self.find_by_regex(name) Ragweed::Wrap32::all_processes do |p| if p.szExeFile =~ name return self.new(p.th32ProcessID) end end end |
Instance Method Details
#adler_chart(i) ⇒ Object
See WinProcess::AdlerChart. Get one.
524 525 526 |
# File 'lib/ragweed/wrap32/process.rb', line 524 def adler_chart(i) AdlerChart.new self, i end |
#adler_compare(i, orig, opts = {}) ⇒ Object
If you store the adler map, you’ve compressed the memory region down to a small series of fixnums, and you can use it with this function to re-check the memory region and see if anything’s changing.
472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/ragweed/wrap32/process.rb', line 472 def adler_compare(i, orig, opts={}) refresh opts window = windowize(i, opts) ret = [] c = -1 scan(i, opts) do |block,soff| 0.stepwith(block.size-1, window) do |off, len| if block[off,len].adler != orig[c += 1] ret << soff+off end end end ret end |
#adler_map(i, opts = {}) ⇒ Object
Like entropy_map, scan a process and compute adler16 checksums for 1k (or :window) blocks.
455 456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/ragweed/wrap32/process.rb', line 455 def adler_map(i, opts={}) refresh opts window = windowize(i, opts) ret = [] scan(i, opts) do |block,soff| 0.stepwith(block.size-1, window) do |off, len| if (b = block[off,len]) ret << b.adler end end end ret end |
#alloc(sz, syscall = false) ⇒ Object
Use arenas, when possible, to quickly allocate memory. The upside is this is very fast. The downside is you can’t free the memory without invalidating every allocation you’ve made prior.
237 238 239 240 241 242 243 |
# File 'lib/ragweed/wrap32/process.rb', line 237 def alloc(sz, syscall=false) if syscall or sz > 4090 ret = syscall_alloc(sz) else ptr(@a.alloc(sz)) end end |
#arena(&block) ⇒ Object
Get another allocation arena for this process. Pretty cheap. Given a block, behaves like File#open, disposing of the arena when you’re done.
269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/ragweed/wrap32/process.rb', line 269 def arena(&block) me = self a = Arena.new(lambda {me.syscall_alloc(4096)}, lambda {|p| me.free(p)}, lambda {|dst, src| me.write(dst, src)}) if block_given? ret = yield a a.release return ret end a end |
#changes(i, sleeptime = 0.5) ⇒ Object
Given a memory region, use the adler routines to create a checksum map, wait a short period of time, and scan for changes, to find churning memory.
531 532 533 534 535 |
# File 'lib/ragweed/wrap32/process.rb', line 531 def changes(i, sleeptime=0.5) q = adler_map(i) sleep(sleeptime) adler_compare(i, q) end |
#detour(loc, o = {}) ⇒ Object
588 589 590 591 592 593 594 |
# File 'lib/ragweed/wrap32/process.rb', line 588 def detour(loc, o={}) klass = o[:class] || Detour loc = get_proc(loc) r = klass.new(loc, o) r.call if not o[:chicken] return r end |
#dump_memory(i, opts = {}) ⇒ Object
Print a canonical hexdump of an entire memory region by region number.
325 |
# File 'lib/ragweed/wrap32/process.rb', line 325 def dump_memory(i, opts={}); get_memory(i, opts).hexdump; end |
#dump_memory_list ⇒ Object
Human-readable standard output of list_memory. Remember that the index number is important.
313 314 315 316 |
# File 'lib/ragweed/wrap32/process.rb', line 313 def dump_memory_list list_memory.each_with_index {|x,i| puts "#{ i }. #{ x[0].to_s(16) }(#{ x[1] })"} true end |
#dump_stack_trace(tid) ⇒ Object Also known as: bt
Human-readable version of thread_stack_trace, with module offsets.
581 582 583 584 585 |
# File 'lib/ragweed/wrap32/process.rb', line 581 def dump_stack_trace(tid) thread_stack_trace(tid).each do |frame, code| puts "#{ frame.to_x } @ #{ to_modoff(code) }" end end |
#dup_handle(h) ⇒ Object
clone a handle from the remote process to here (to here? tf?)
23 24 25 |
# File 'lib/ragweed/wrap32/process.rb', line 23 def dup_handle(h) Ragweed::Wrap32::duplicate_handle(@h, h) end |
#entropy_map(i, opts = {}) ⇒ Object
Take a region of memory and walk over it in 255-byte samples (less than 255 bytes and you lose accuracy, but you can increase it with the “:window” option), computing entropy for each sample, returning a list of [offset,entropy] tuples.
357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/ragweed/wrap32/process.rb', line 357 def entropy_map(i, opts={}) ret = [] startoff = opts[:starting_offset] startoff ||= 0 window = opts[:window] || 255 scan(i, opts) do |block, soff| startoff.stepwith(block.size, window) do |off, len| ret << [off, block[off,len].entropy] end end return ret end |
#free(off) ⇒ Object
Free the return value of syscall_alloc. Do NOT use for the return value of alloc.
247 248 249 |
# File 'lib/ragweed/wrap32/process.rb', line 247 def free(off) Ragweed::Wrap32::virtual_free_ex(@h, off) end |
#get_deferred_proc_remote(name, handle, base_of_dll) ⇒ Object
This only gets called for breakpoints in modules that have just been loaded and detected by a LOAD_DLL event. It is called from on_load_dll() -> deferred_install()
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/ragweed/wrap32/process.rb', line 51 def get_deferred_proc_remote(name, handle, base_of_dll) return name if !name.kind_of?String mod, meth = name.split "!" raise "can not set this breakpoint: #{name}" if mod.nil? or meth.nil? modh = handle # Location is an offset if is_hex(meth) baseaddr = 0 modules.each { |m| break if m.szModule == mod } ret = base_of_dll + meth.hex else # Location is a symbolic name # Win32 should have successfully loaded the DLL ret = remote_call "kernel32!GetProcAddress", modh, meth end ret end |
#get_memory(i, opts = {}) ⇒ Object
Read an entire memory region into a string by region number.
319 320 321 322 |
# File 'lib/ragweed/wrap32/process.rb', line 319 def get_memory(i, opts={}) refresh opts read(@memlist[i][0], @memlist[i][1]) end |
#get_proc(name) ⇒ Object
look up a process by its name — but this is in the local process, which is broken — a heuristic that sometimes works for w32 functions, but probably never otherwise.
30 31 32 33 |
# File 'lib/ragweed/wrap32/process.rb', line 30 def get_proc(name) return Ragweed::Ptr.new(name) if name.kind_of? Numeric or name.kind_of? Ptr ptr(Ragweed::Wrap32::get_proc_address(name)) end |
#get_proc_remote(name) ⇒ Object
This only gets called for breakpoints in modules that are already loaded
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/ragweed/wrap32/process.rb', line 76 def get_proc_remote(name) if !name.kind_of?String return name end mod, meth = name.split "!" if mod.nil? or meth.nil? raise "can not set this breakpoint: #{name}" end modh = remote_call "kernel32!GetModuleHandleA", mod raise "no such module #{ mod }" if not modh # Location is an offset if is_hex(meth) baseaddr = 0 modules.each do |m| if m.szModule.to_s.match(/#{mod}/) baseaddr = m.modBaseAddr break end end # Somehow the module does not appear to be # loaded. This should have been caught by # Process::is_breakpoint_deferred either way # Process::initialize should catch this return if baseaddr == 0 or baseaddr == -1 return name end ret = baseaddr + meth.hex else # Location is a symbolic name ret = remote_call "kernel32!GetProcAddress", modh, meth end ret end |
#handle ⇒ Object
2 |
# File 'lib/ragweed/wrap32/process.rb', line 2 def handle; @h; end |
#hunt(key, opts = {}) ⇒ Object
Given a string key, find it in memory. Very slow. Will read all memory regions, but you can provide “:index_range”, which must be a Range object, to constrain which ranges to search through. Returns a list of structs containing absolute memory locations, the index of the region, and some surrounding context for the hit.
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
# File 'lib/ragweed/wrap32/process.rb', line 416 def hunt(key, opts={}) ret = [] refresh opts range = opts[:index_range] || (0..@memlist.size) @memlist.each_with_index do |t, i| if range.member? i if opts[:noisy] puts "#{ i }. #{ t[0].to_s(16) } -> #{ (t[0]+t[1]).to_s(16) }" end scan(i, opts) do |block, soff| if (needle = block.index(key)) r = OpenStruct.new r.location = (t[0] + soff + needle) r.index = i r.context = block ret << r return ret if opts[:first] end end end end ret end |
#image ⇒ Object
Return the EXE name of the process.
161 162 163 164 165 166 167 168 169 |
# File 'lib/ragweed/wrap32/process.rb', line 161 def image buf = "\x00" * 256 if Ragweed::Wrap32::nt_query_information_process(@h, 27, buf) buf = buf.from_utf16 buf = buf[(buf.index("\\"))..-1] return buf.asciiz end nil end |
#insert(buf) ⇒ Object
Insert a string anywhere into the memory of the remote process, returning its address, using an arena.
284 |
# File 'lib/ragweed/wrap32/process.rb', line 284 def insert(buf); @a.copy(buf); end |
#is_breakpoint_deferred(ip) ⇒ Object
Check if breakpoint location is deferred This method expects a string ‘module!function’ true is the module is not yet loaded false is the module is loaded
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/ragweed/wrap32/process.rb', line 120 def is_breakpoint_deferred(ip) if !ip.kind_of? String return false end m,f = ip.split('!') if f.nil? or m.nil? return true end modules.each do |d| if d.szModule.to_s.match(/#{m}/) return false end end return true end |
#is_hex(s) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/ragweed/wrap32/process.rb', line 35 def is_hex(s) s = s.strip # Strip leading 0s and 0x prefix while s[0..1] == '0x' or s[0..1] == '00' s = s[2..-1] end o = s s.hex.to_s(16) == o end |
#list_memory(&block) ⇒ Object
List all memory regions in the remote process by iterating over VirtualQueryEx. With a block, yields MEMORY_BASIC_INFORMATION structs. Without it, returns [baseaddr,size] tuples.
We “index” this list, so that we can refer to memory locations by “region number”, which is a Rubycorn-ism and not a Win32-ism. You’ll see lots of functions asking for memory indices, and this is what they’re referring to.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/ragweed/wrap32/process.rb', line 294 def list_memory(&block) ret = [] i = 0 while (mbi = Ragweed::Wrap32::virtual_query_ex(@h, i)) break if (not ret.empty? and mbi.BaseAddress == 0) if block_given? yield mbi else base = mbi.BaseAddress || 0 size = mbi.RegionSize || 0 ret << [base,size] if mbi.State & 0x1000 # MEM_COMMIT i = base + size end end ret end |
#modules(&block) ⇒ Object
List the modules for the process, either yielding a struct for each to a block, or returning a list.
196 197 198 199 200 201 202 203 204 |
# File 'lib/ragweed/wrap32/process.rb', line 196 def modules(&block) if block_given? Ragweed::Wrap32::list_modules(@pid, &block) else ret = [] Ragweed::Wrap32::list_modules(@pid) {|x| ret << x} return ret end end |
#pointers_to(src, dst, opts = {}) ⇒ Object
Given a source and destination memory region, scan through “source” looking for properly-aligned U32LE values that would be valid pointers into “destination”. The “:range_start” and “:range_end” options constrain what a “valid pointer” into “destination” is.
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
# File 'lib/ragweed/wrap32/process.rb', line 374 def pointers_to(src, dst, opts={}) refresh opts ret = {} range = ((opts[:range_start] || @memlist[dst][0])..(opts[:range_stop] || @memlist[dst][0]+@memlist[dst][1])) scan(src, opts) do |block, soff| 0.stepwith(block.size, 4) do |off, len| if len == 4 if range.member? block[off,4].to_l32 ret[soff + off] = block[off,4].to_l32 end end end end return ret end |
#ptr(x) ⇒ Object
Get a pointer into the remote process; pointers are just fixnums with a read/write method and a to_s.
16 17 18 19 20 |
# File 'lib/ragweed/wrap32/process.rb', line 16 def ptr(x) ret = Ragweed::Ptr.new(x) ret.p = self return ret end |
#read(off, sz = 4096) ⇒ Object
Read/write ranges of data or fixnums to/from the process by address.
207 |
# File 'lib/ragweed/wrap32/process.rb', line 207 def read(off, sz=4096); Ragweed::Wrap32::read_process_memory(@h, off, sz); end |
#read16(off) ⇒ Object
210 |
# File 'lib/ragweed/wrap32/process.rb', line 210 def read16(off); read(off, 2).unpack("v").first; end |
#read32(off) ⇒ Object
209 |
# File 'lib/ragweed/wrap32/process.rb', line 209 def read32(off); read(off, 4).unpack("L").first; end |
#read8(off) ⇒ Object
211 |
# File 'lib/ragweed/wrap32/process.rb', line 211 def read8(off); read(off, 1)[0]; end |
#region_range(i, opts = {}) ⇒ Object
Get the memory range, as a Ruby Range, for a region by index
538 539 540 541 |
# File 'lib/ragweed/wrap32/process.rb', line 538 def region_range(i, opts={}) refresh opts (@memlist[i][0]..(@memlist[i][1]+@memlist[i][0])) end |
#remote_call(meth, *args) ⇒ Object
call a function, by name or address, in the process, using CreateRemoteThread
218 219 220 221 222 223 224 225 |
# File 'lib/ragweed/wrap32/process.rb', line 218 def remote_call(meth, *args) loc = meth loc = get_proc(loc) if loc.kind_of? String loc = Ragweed::Ptr.new loc raise "bad proc name" if loc.null? t = Trampoline.new(self, loc) t.call *args end |
#resume(tid) ⇒ Object
Resume a thread by tid.
192 |
# File 'lib/ragweed/wrap32/process.rb', line 192 def resume(tid); Ragweed::Wrap32::open_thread(tid) {|x| Ragweed::Wrap32::resume_thread(x)}; end |
#resume_all ⇒ Object
Resume all the threads in the process. XXX this will not resume threads with suspend counts greater than 1.
185 |
# File 'lib/ragweed/wrap32/process.rb', line 185 def resume_all; threads.each {|x| resume(x)}; end |
#scan(i, opts = {}) ⇒ Object
In Python, and maybe Ruby, it was much faster to work on large memory regions a 4k page at a time, rather than reading the whole thing into one big string. Scan takes a memory region and yields 4k chunks of it to a block, along with the length of each chunk.
332 333 334 335 336 337 338 339 340 341 342 343 |
# File 'lib/ragweed/wrap32/process.rb', line 332 def scan(i, opts={}) refresh opts memt = @memlist[i] if memt[1] > 4096 0.step(memt[1], 4096) do |i| block = (memt[1] - i).cap(4096) yield read(memt[0] + i, block), memt[0]+i end else yield read(memt[0], memt[1]), memt[0] end end |
#strings_mem(i, opts = {}) ⇒ Object
Given a memory region number, do a Unix strings(1) on it. Valid options: :unicode: you probably always want to set this to “true” :minimum: how small strings to accept.
Fairly slow.
396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/ragweed/wrap32/process.rb', line 396 def strings_mem(i, opts={}) ret = [] opts[:offset] ||= 0 scan(i) do |block, soff| while 1 off, size = block.nextstring(opts) break if not off opts[:offset] += (off + size) ret << [soff+off, size, block[off,size]] end end ret end |
#suspend(tid) ⇒ Object
Suspend a thread by tid. Technically, this doesn’t need to be a method; you can suspend a thread anywhere without a process handle.
189 |
# File 'lib/ragweed/wrap32/process.rb', line 189 def suspend(tid); Ragweed::Wrap32::open_thread(tid) {|x| Ragweed::Wrap32::suspend_thread(x)}; end |
#suspend_all ⇒ Object
Suspend all the threads in the process.
181 |
# File 'lib/ragweed/wrap32/process.rb', line 181 def suspend_all; threads.each {|x| suspend(x)}; end |
#syscall_alloc(sz) ⇒ Object
Use VirtualAllocEx to grab a block of memory in the process. This is expensive, the equivalent of mmap()‘ing for each allocation.
232 |
# File 'lib/ragweed/wrap32/process.rb', line 232 def syscall_alloc(sz); ptr(Ragweed::Wrap32::virtual_alloc_ex(@h, sz)); end |
#thread_context(tid) ⇒ Object
Dump thread context, returning a struct that contains things like .Eip and .Eax.
347 348 349 350 351 |
# File 'lib/ragweed/wrap32/process.rb', line 347 def thread_context(tid) Ragweed::Wrap32::open_thread(tid) do |h| Ragweed::Wrap32::get_thread_context(h) end end |
#thread_stack_trace(tid) ⇒ Object
For libraries compiled with frame pointers: walk EBP back until it stops giving intelligible addresses, and, at each step, grab the saved EIP from just before it.
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
# File 'lib/ragweed/wrap32/process.rb', line 562 def thread_stack_trace(tid) with_suspended_thread(tid) do ctx = thread_context(tid) if((start = read32(ctx.Ebp)) != 0) a = start stack = [[start, read32(start+4)]] while((a = read32(a)) and a != 0 and not stack.member?(a)) begin stack << [a, read32(a+4)] rescue; break; end end return stack end end [] end |
#threads(full = false, &block) ⇒ Object
Return a list of all the threads in the process; relatively expensive, so cache the result.
173 174 175 176 177 178 |
# File 'lib/ragweed/wrap32/process.rb', line 173 def threads(full=false, &block) return Ragweed::Wrap32::threads(@pid, &block) if block_given? ret = [] Ragweed::Wrap32::threads(@pid) {|x| ((full) ? ret << x : ret << x.th32ThreadID) } return ret end |
#to_modoff(off, force = false) ⇒ Object
Convert an address to “module+10h” notation, when possible.
252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/ragweed/wrap32/process.rb', line 252 def to_modoff(off, force=false) if not @modules or force @modules = modules.sort {|x,y| x.modBaseAddr <=> y.modBaseAddr} end @modules.each do |m| if off >= m.modBaseAddr and off < (m.modBaseAddr + m.modBaseSize) return "#{ m.szModule }+#{ (off - m.modBaseAddr).to_s(16) }h" end end return "#{ off.to_x }h" end |
#which_region_has?(addr, opts = {}) ⇒ Boolean
Figure out what region (by region index) has an address
544 545 546 547 548 549 550 |
# File 'lib/ragweed/wrap32/process.rb', line 544 def which_region_has?(addr, opts={}) refresh opts @memlist.each_with_index do |r, i| return i if (r[0]..r[0]+r[1]).member? addr end return nil end |
#with_suspended_thread(tid) ⇒ Object
Do something with a thread while its suspended
553 554 555 556 557 |
# File 'lib/ragweed/wrap32/process.rb', line 553 def with_suspended_thread(tid) ret = nil Ragweed::Wrap32::with_suspended_thread(tid) {|x| ret = yield} return ret end |
#write(off, data) ⇒ Object
ptrace sucks, writing 8 or 16 bytes will probably result in failure unless you PTRACE_POKE first and get the rest of the original value at the address
23 |
# File 'lib/ragweed/wraptux/process.rb', line 23 def write(off, data); Ragweed::Wrap32::write_process_memory(@h, off, data); end |
#write16(off, v) ⇒ Object
213 |
# File 'lib/ragweed/wrap32/process.rb', line 213 def write16(off, v); write(off, [v].pack("v")); end |
#write32(off, v) ⇒ Object
212 |
# File 'lib/ragweed/wrap32/process.rb', line 212 def write32(off, v); write(off, [v].pack("L")); end |
#write8(off, v) ⇒ Object
214 |
# File 'lib/ragweed/wrap32/process.rb', line 214 def write8(off, v); write(off, v.chr); end |
#writeable?(off) ⇒ Boolean
Can I write to this address in the process?
228 |
# File 'lib/ragweed/wrap32/process.rb', line 228 def writeable?(off); Ragweed::Wrap32::writeable? @h, off; end |