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.
162 163 164 165 166 |
# File 'lib/ragweed/wrap32/process.rb', line 162 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.
150 151 152 153 154 155 156 157 158 159 |
# File 'lib/ragweed/wrap32/process.rb', line 150 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.
532 533 534 |
# File 'lib/ragweed/wrap32/process.rb', line 532 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.
480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
# File 'lib/ragweed/wrap32/process.rb', line 480 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.
463 464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/ragweed/wrap32/process.rb', line 463 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.
245 246 247 248 249 250 251 |
# File 'lib/ragweed/wrap32/process.rb', line 245 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.
277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/ragweed/wrap32/process.rb', line 277 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.
539 540 541 542 543 |
# File 'lib/ragweed/wrap32/process.rb', line 539 def changes(i, sleeptime=0.5) q = adler_map(i) sleep(sleeptime) adler_compare(i, q) end |
#detour(loc, o = {}) ⇒ Object
596 597 598 599 600 601 602 |
# File 'lib/ragweed/wrap32/process.rb', line 596 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.
333 |
# File 'lib/ragweed/wrap32/process.rb', line 333 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.
321 322 323 324 |
# File 'lib/ragweed/wrap32/process.rb', line 321 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.
589 590 591 592 593 |
# File 'lib/ragweed/wrap32/process.rb', line 589 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.
365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/ragweed/wrap32/process.rb', line 365 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.
255 256 257 |
# File 'lib/ragweed/wrap32/process.rb', line 255 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 73 74 75 76 77 78 79 80 |
# File 'lib/ragweed/wrap32/process.rb', line 51 def get_deferred_proc_remote(name, handle, base_of_dll) 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 = handle # Location is an offset if is_hex(meth) baseaddr = 0 modules.each do |m| if m.szModule == mod break end end 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.
327 328 329 330 |
# File 'lib/ragweed/wrap32/process.rb', line 327 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
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 115 116 117 118 119 120 121 122 |
# File 'lib/ragweed/wrap32/process.rb', line 84 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 == 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.
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/ragweed/wrap32/process.rb', line 424 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.
169 170 171 172 173 174 175 176 177 |
# File 'lib/ragweed/wrap32/process.rb', line 169 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.
292 |
# File 'lib/ragweed/wrap32/process.rb', line 292 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
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/ragweed/wrap32/process.rb', line 128 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.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
# File 'lib/ragweed/wrap32/process.rb', line 302 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.
204 205 206 207 208 209 210 211 212 |
# File 'lib/ragweed/wrap32/process.rb', line 204 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.
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 |
# File 'lib/ragweed/wrap32/process.rb', line 382 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.
215 |
# File 'lib/ragweed/wrap32/process.rb', line 215 def read(off, sz=4096); Ragweed::Wrap32::read_process_memory(@h, off, sz); end |
#read16(off) ⇒ Object
218 |
# File 'lib/ragweed/wrap32/process.rb', line 218 def read16(off); read(off, 2).unpack("v").first; end |
#read32(off) ⇒ Object
217 |
# File 'lib/ragweed/wrap32/process.rb', line 217 def read32(off); read(off, 4).unpack("L").first; end |
#read8(off) ⇒ Object
219 |
# File 'lib/ragweed/wrap32/process.rb', line 219 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
546 547 548 549 |
# File 'lib/ragweed/wrap32/process.rb', line 546 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
226 227 228 229 230 231 232 233 |
# File 'lib/ragweed/wrap32/process.rb', line 226 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.
200 |
# File 'lib/ragweed/wrap32/process.rb', line 200 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.
193 |
# File 'lib/ragweed/wrap32/process.rb', line 193 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.
340 341 342 343 344 345 346 347 348 349 350 351 |
# File 'lib/ragweed/wrap32/process.rb', line 340 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.
404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/ragweed/wrap32/process.rb', line 404 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.
197 |
# File 'lib/ragweed/wrap32/process.rb', line 197 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.
189 |
# File 'lib/ragweed/wrap32/process.rb', line 189 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.
240 |
# File 'lib/ragweed/wrap32/process.rb', line 240 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.
355 356 357 358 359 |
# File 'lib/ragweed/wrap32/process.rb', line 355 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.
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 |
# File 'lib/ragweed/wrap32/process.rb', line 570 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.
181 182 183 184 185 186 |
# File 'lib/ragweed/wrap32/process.rb', line 181 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.
260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/ragweed/wrap32/process.rb', line 260 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
552 553 554 555 556 557 558 |
# File 'lib/ragweed/wrap32/process.rb', line 552 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
561 562 563 564 565 |
# File 'lib/ragweed/wrap32/process.rb', line 561 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
221 |
# File 'lib/ragweed/wrap32/process.rb', line 221 def write16(off, v); write(off, [v].pack("v")); end |
#write32(off, v) ⇒ Object
220 |
# File 'lib/ragweed/wrap32/process.rb', line 220 def write32(off, v); write(off, [v].pack("L")); end |
#write8(off, v) ⇒ Object
222 |
# File 'lib/ragweed/wrap32/process.rb', line 222 def write8(off, v); write(off, v.chr); end |
#writeable?(off) ⇒ Boolean
Can I write to this address in the process?
236 |
# File 'lib/ragweed/wrap32/process.rb', line 236 def writeable?(off); Ragweed::Wrap32::writeable? @h, off; end |