Module: Ragweed::Wrap32

Defined in:
lib/ragweed/wrap32/debugging.rb,
lib/ragweed/wrap32.rb,
lib/ragweed/wrap32/winx.rb,
lib/ragweed/wrap32/wrap32.rb,
lib/ragweed/wrap32/debugging.rb,
lib/ragweed/wrap32/process_token.rb,
lib/ragweed/wrap32/thread_context.rb,
lib/ragweed/wrap32/thread_context.rb

Overview

Wrap the Win32 debugging specific APIs

Defined Under Namespace

Modules: ContextFlags, ContinueCodes, DebugCodes, EFlags, ExceptionCodes, ExceptionSubTypes, FileAccess, FileAttributes, FileDisposition, FileSharing, FormatArgs, PagePermissions, PagePerms, PrivilegeAttribute, TokenAccess, Win Classes: CreateProcessDebugInfo, CreateThreadDebugInfo, DebugEvent, DebugEventU, ExceptionDebugInfo, ExceptionRecord, ExitProcessDebugInfo, ExitThreadDebugInfo, LoadDLLDebugInfo, OutputDebugStringInfo, Overlapped, ProcessInfo, ProcessToken, RipInfo, StartupInfo, ThreadContext, UnloadDLLDebugInfo, WinX

Constant Summary collapse

VERSION =

:stopdoc:

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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.adjust_token_privileges(t, disable, *args) ⇒ Object

Raises:



47
48
49
50
51
52
53
# File 'lib/ragweed/wrap32/process_token.rb', line 47

def adjust_token_privileges(t, disable, *args)
  buf = FFI::MemoryPointer.from_string( [args.size].pack("L") + (args.map {|tup| tup.pack("QL") }.join("")) )

  r = Win.AdjustTokenPrivileges(t, disable, buf, buf.size, nil, nil)

  raise WinX.new(:adjust_token_privileges) if r == 0
end

.all_processesObject

Use Toolhelp32 to enumerate all running processes on the box, returning a struct with PIDs and executable names.



327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/ragweed/wrap32/wrap32.rb', line 327

def all_processes
  h = Win.CreateToolhelp32Snapshot(0x2, 0)
  if h != -1
    pi = [(9*4)+2048,0,0,0,0,0,0,0,0,"\x00"*2048].pack("LLLLLLLLLa2048")
    if Win.Process32First(h, pi) != 0
      yield str2process_info(pi)
      while Win.Process32Next(h, pi) != 0
        yield str2process_info(pi)
      end
    end
  else
    raise WinX.new(:create_toolhelp32_snapshot)
  end
end

.close_handle(h) ⇒ Object

Close any Win32 handle. Reminder: Win32 handles are just integers, like file descriptors in Posix.

Raises:



163
164
165
# File 'lib/ragweed/wrap32/wrap32.rb', line 163

def close_handle(h)
raise WinX.new(:close_handle) if Win.CloseHandle(h) == 0
end

.continue_debug_event(pid, tid, code) ⇒ Object

Raises:



374
375
376
377
378
# File 'lib/ragweed/wrap32/debugging.rb', line 374

def continue_debug_event(pid, tid, code)
  r = Win.ContinueDebugEvent(pid, tid, code)
  raise WinX.new(:continue_debug_event) if r == 0
  return r
end

.create_event(name = nil, auto = false, signalled = false) ⇒ Object

create an event, which you can signal and wait on across processes

Raises:



496
497
498
499
500
501
502
503
504
# File 'lib/ragweed/wrap32/wrap32.rb', line 496

def create_event(name=nil, auto=false, signalled=false)
  auto = (1 if auto) || 0
  signalled = (1 if signalled) || 0
  name ||= 0

  r = Win.CreateEventA(0, auto, signalled, name);
  raise WinX.new(:create_event) if r == 0
  return r
end

.create_file(name, opts = {}) ⇒ Object

Raises:



463
464
465
466
467
468
469
470
471
472
# File 'lib/ragweed/wrap32/wrap32.rb', line 463

def create_file(name, opts={})
  opts[:disposition] ||= FileDisposition::OPEN_ALWAYS
  opts[:sharing] ||= FileSharing::READ | FileSharing::WRITE
  opts[:access] ||= FileAccess::GENERIC_ALL
  opts[:flags] ||= 0

  r = Win.CreateFileA(name, opts[:access], opts[:sharing], NULL, opts[:disposition], opts[:flags], NULL)
  raise WinX.new(:create_file) if r == -1
  return r
end

.create_remote_thread(h, start, arg) ⇒ Object

Create a remote thread in the process, starting at the location “start”, with the threadproc argument “arg”

Raises:



445
446
447
448
449
# File 'lib/ragweed/wrap32/wrap32.rb', line 445

def create_remote_thread(h, start, arg)
  r = Win.CreateRemoteThread(h, NULL, 0, start.to_i, arg.to_i, 0, 0)
  raise WinX.new(:create_remote_thread) if r == 0
  return r
end

.debug_active_process(pid) ⇒ Object

Raises:



380
381
382
383
384
# File 'lib/ragweed/wrap32/debugging.rb', line 380

def debug_active_process(pid)
  r = Win.DebugActiveProcess(pid)
  raise WinX.new(:debug_active_process) if r == 0
  return r
end

.debug_active_process_stop(pid) ⇒ Object



392
393
394
395
# File 'lib/ragweed/wrap32/debugging.rb', line 392

def debug_active_process_stop(pid)
  # don't care about failure
  Win.DebugActiveProcessStop(pid)
end

.debug_set_process_kill_on_exit(val = 0) ⇒ Object

Raises:



386
387
388
389
390
# File 'lib/ragweed/wrap32/debugging.rb', line 386

def debug_set_process_kill_on_exit(val=0)
  r = Win.DebugSetProcessKillOnExit(val)
  raise WinX.new(:debug_set_process_kill_on_exit) if r == 0
  return r
end

.device_io_control(h, code, inbuf, outbuf, overlapped = NULL) ⇒ Object

Raises:



536
537
538
539
540
541
542
# File 'lib/ragweed/wrap32/wrap32.rb', line 536

def device_io_control(h, code, inbuf, outbuf, overlapped=NULL)
  overlapped = overlapped.to_s if overlapped
  outw = "\x00" * 4
  r = Win.DeviceIoControl(h, code, inbuf, inbuf.size, outbuf, outbuf.size, outw, overlapped)
  raise WinX.new(:device_io_control) if r == 0 and get_last_error != 997
  return outw.unpack("L").first
end

.duplicate_handle(ph, h) ⇒ Object

clone a handle out of another open process (or self, with -1)

Raises:



456
457
458
459
460
461
# File 'lib/ragweed/wrap32/wrap32.rb', line 456

def duplicate_handle(ph, h)
  ret = "\x00\x00\x00\x00"
  r = Win.DuplicateHandle(ph, h, -1, ret, 0, 0, 0x2)
  raise WinX.new(:duplicate_handle) if r == 0
  ret.to_l32
end

.flush_instruction_cache(h, v1 = 0, v2 = 0) ⇒ Object



397
398
399
# File 'lib/ragweed/wrap32/debugging.rb', line 397

def flush_instruction_cache(h, v1=0, v2=0)
  Win.FlushInstructionCache(h, v1, v2)
end

.format_message(code = nil) ⇒ Object

strerror(errno) (can’t fail)



173
174
175
176
177
178
# File 'lib/ragweed/wrap32/wrap32.rb', line 173

def format_message(code=nil)
  code ||= get_last_error
  buf = FFI::MemoryPointer.from_string("\x00" * 4096)
  Win.FormatMessageA(4096, nil, code, 0x00000400, buf, 4096, nil)
  return buf.to_s.split("\x00")[0]
end

.get_current_process_idObject

getpid



257
258
259
# File 'lib/ragweed/wrap32/wrap32.rb', line 257

def get_current_process_id
  Win.GetCurrentProcessId() # can't realistically fail
end

.get_current_thread_idObject

gettid



267
268
269
# File 'lib/ragweed/wrap32/wrap32.rb', line 267

def get_current_thread_id
  Win.GetCurrentThreadId() # can't realistically fail
end

.get_last_errorObject

Get the last error code (errno) (can’t fail)



168
169
170
# File 'lib/ragweed/wrap32/wrap32.rb', line 168

def get_last_error
  Win.GetLastError()
end

.get_mapped_filename(h, lpv, size) ⇒ Object

Raises:



211
212
213
214
215
216
# File 'lib/ragweed/wrap32/wrap32.rb', line 211

def get_mapped_filename(h, lpv, size)
    val = "\x00" * size
    r = Win.GetMappedFileNameA(h, lpv.to_i, val, size)
    raise WinX.new(:get_mapped_filename) if r == 0
    return val
end

.get_module_handle(name) ⇒ Object

Given a DLL name, get a handle to the DLL.

Raises:



272
273
274
275
276
277
# File 'lib/ragweed/wrap32/wrap32.rb', line 272

def get_module_handle(name)
  name = name
  r = Win.GetModuleHandleA(name)
  raise WinX.new(:get_module_handle) if r == 0
  return r
end

.get_overlapped_result(h, overlapped) ⇒ Object

Raises:



544
545
546
547
548
549
550
# File 'lib/ragweed/wrap32/wrap32.rb', line 544

def get_overlapped_result(h, overlapped)
  overlapped = overlapped.to_s
  outw = "\x00" * 4
  r = Win.GetOverlappedResult(h, overlapped, outw, 0)
  raise WinX.new(:get_overlapped_result) if r == 0
  return outw.unpack("L").first
end

.get_proc_address(x, y = nil) ⇒ Object

Using notation x = “foo!bar” or x = handle, y = meth, look up a function’s address in a module. Note that this is local, not remote.



289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/ragweed/wrap32/wrap32.rb', line 289

def get_proc_address(x, y=nil)
  if not y
    mod, meth = x.split "!"
    h = get_module_handle(mod)
  else
    h = x
    meth = y
  end

  r = Win.GetProcAddress(h, meth)
  return r # pass error through
end

.get_process_id(h) ⇒ Object

get_processid



262
263
264
# File 'lib/ragweed/wrap32/wrap32.rb', line 262

def get_process_id(h)
  Win.GetProcessId(h)
end

.get_thread_context(h) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ragweed/wrap32/thread_context.rb', line 122

def get_thread_context(h)
  c = FFI::MemoryPointer.new(Ragweed::Wrap32::ThreadContext, 1)
  ctx = Ragweed::Wrap32::ThreadContext.new c
  ctx.context_flags = Ragweed::Wrap32::ContextFlags::DEBUG
  #suspend_thread(h)
  ret = Win.GetThreadContext(h, ctx)
  #resume_thread(h)
  if ret != 0
    return ctx
  else
    raise WinX.new(:get_thread_context)
  end
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.



20
21
22
# File 'lib/ragweed/wrap32.rb', line 20

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

.list_modules(pid = 0) ⇒ Object

Given a pid, enumerate the modules loaded into the process, returning base addresses, memory ranges, and the module name.



362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/ragweed/wrap32/wrap32.rb', line 362

def list_modules(pid=0)
  h = Win.CreateToolhelp32Snapshot(0x8, pid)
  if h != -1
    mi = [260+256+(8*4),0,0,0,0,0,0,0,"\x00"*256,"\x00"*260].pack("LLLLLLLLa256a260")
    if Win.Module32First(h, mi) != 0
      yield str2module_info(mi)
      while Win.Module32Next(h, mi) != 0
        yield str2module_info(mi)
      end
    end
  else
    raise WinX.new(:create_toolhelp32_snapshot)
  end
end

.load_library(name) ⇒ Object

load a library explicitly from a dll

Raises:



280
281
282
283
284
285
# File 'lib/ragweed/wrap32/wrap32.rb', line 280

def load_library(name)
  name = name
  r = Win.LoadLibraryA(name)
  raise WinX.new(:load_library) if r == 0
  return r
end

.lookup_privilege_value(name) ⇒ Object

Raises:



55
56
57
58
59
60
61
62
# File 'lib/ragweed/wrap32/process_token.rb', line 55

def lookup_privilege_value(name)
  namep = FFI::MemoryPointer.from_string(name)
  outw = FFI::MemoryPointer.new(:int64, 1)
  r = Win.LookupPrivilegeValueA(nil, namep, outw)
  r = Win.LookupPrivilegeValueA(nil, name, outw)
  raise WinX.new(:lookup_privilege_value) if r == 0
  outw.read_long_long
end

.malloc(sz) ⇒ Object

just grab some local memory XXX same as FFI name ?

Raises:



554
555
556
557
558
# File 'lib/ragweed/wrap32/wrap32.rb', line 554

def malloc(sz)
  r = Win.malloc(sz)
  raise WinX.new(:malloc) if r == 0
  return r
end

.memcpy(dst, src, size) ⇒ Object



560
561
562
# File 'lib/ragweed/wrap32/wrap32.rb', line 560

def memcpy(dst, src, size)
  Win.memcpy(dst, src, size)
end

.nt_query_information_process(h, ord, buf) ⇒ Object

NQIP does a lot of things, the most useful of which are getting the image name of a running process, and telling whether a debugger is loaded. This interface is Ioctl-style; provide an ordinal and a buffer to pass results through.



389
390
391
392
393
394
395
396
# File 'lib/ragweed/wrap32/wrap32.rb', line 389

def nt_query_information_process(h, ord, buf)
  lenp = [0].pack("L")
  if Win.NtQueryInformationProcess(h, ord, buf, buf.size, lenp) == 0
    len = lenp.unpack("L").first
    return buf[0..(len-1)]
  end
  nil
end

.open_event(name) ⇒ Object

i haven’t made this work, but named handles are kind of silly anyways

Raises:



475
476
477
478
479
# File 'lib/ragweed/wrap32/wrap32.rb', line 475

def open_event(name)
  r = Win.OpenEventA(0, 0, name)
  raise WinX.new(:open_event) if r == 0
  return r
end

.open_process(pid) ⇒ Object

Get a process handle given a pid

Raises:



142
143
144
145
146
# File 'lib/ragweed/wrap32/wrap32.rb', line 142

def open_process(pid)
  r = Win.OpenProcess(0x1F0FFF, 0, pid)
  raise WinX.new(:open_process) if r == 0
  return r
end

.open_process_token(h, access = Ragweed::Wrap32::TokenAccess::ADJUST_PRIVILEGES) ⇒ Object

Raises:



40
41
42
43
44
45
# File 'lib/ragweed/wrap32/process_token.rb', line 40

def open_process_token(h, access=Ragweed::Wrap32::TokenAccess::ADJUST_PRIVILEGES)
  outw = "\x00" * 4
  r = Win.OpenProcessToken(h, access, outw)
  raise WinX.new(:open_process_token) if r == 0
  return outw.unpack("L").first
end

.open_thread(tid, &block) ⇒ Object

Get a thread handle given a tid; if a block is provided, the semantics are as File#open with a block.

Raises:



150
151
152
153
154
155
156
157
158
159
# File 'lib/ragweed/wrap32/wrap32.rb', line 150

def open_thread(tid, &block)
  h = Win.OpenThread(0x1F03FF, 0, tid)
  raise WinX.new(:open_thread) if h == 0
  if block_given?
    ret = yield h
    close_handle(h)
    return ret
  end
  h
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.



28
29
30
# File 'lib/ragweed/wrap32.rb', line 28

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

.read_file(h, count, overlapped = nil) ⇒ Object

Raises:



519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/ragweed/wrap32/wrap32.rb', line 519

def read_file(h, count, overlapped=nil)
  if overlapped
    opp = overlapped.to_s
  else
    opp = NULL
  end
  outw = "\x00" * 4
  if not (buf = overlapped.try(:target)) or buf.size < count
    buf = "\x00" * count
    overlapped.target = buf if overlapped
  end

  r = Win.ReadFile(h, buf, count, outw, opp)
  raise WinX.new(:read_file) if r == 0 and get_last_error != 997
  return buf, outw.unpack("L").first
end

.read_process_memory(h, ptr, len) ⇒ Object

Read from a remote process given an address and length, returning a string.

Raises:



203
204
205
206
207
208
209
# File 'lib/ragweed/wrap32/wrap32.rb', line 203

def read_process_memory(h, ptr, len)
  # val = FFI::MemoryPointer.from_string("\x00" * len)
  val = "\x00" * len
  r = Win.ReadProcessMemory(h, ptr.to_i, val, len, NULL)
  raise WinX.new(:read_process_memory) if r == 0
  return val ## don't handle short reads XXX
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.



42
43
44
45
46
47
48
49
50
51
# File 'lib/ragweed/wrap32.rb', line 42

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).sort.each {|rb| require rb}
  # require File.dirname(File.basename(__FILE__)) + "/#{x}"

end

.require_utilsObject

Utility function to load utility classes and extensions



33
34
35
# File 'lib/ragweed/wrap32.rb', line 33

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

.reset_event(h) ⇒ Object

force-unsignal event (waiting on the event handle also does this)

Raises:



489
490
491
492
493
# File 'lib/ragweed/wrap32/wrap32.rb', line 489

def reset_event(h)
  r = Win.ResetEvent(h)
  raise WinX.new(:reset_event) if r == 0
  return r
end

.resume_thread(h) ⇒ Object

Resume a suspended thread, returning nonzero if the thread was suspended, and 0 if it was running.



439
440
441
# File 'lib/ragweed/wrap32/wrap32.rb', line 439

def resume_thread(h)
  ResumeThread(h)
end

.set_event(h) ⇒ Object

signal an event

Raises:



482
483
484
485
486
# File 'lib/ragweed/wrap32/wrap32.rb', line 482

def set_event(h)
  r = Win.SetEvent(h)
  raise WinX.new(:set_event) if r == 0
  return r
end

.set_thread_context(h, ctx) ⇒ Object

Raises:



136
137
138
139
140
# File 'lib/ragweed/wrap32/thread_context.rb', line 136

def set_thread_context(h, ctx)
  ret = Win.SetThreadContext(h, ctx)
  raise WinX.new(:set_thread_context) if ret == 0
  return ret
end

.sleep(ms = 0) ⇒ Object



451
452
453
# File 'lib/ragweed/wrap32/wrap32.rb', line 451

def sleep(ms=0)
  Win.Sleep(ms)
end

.str2memory_basic_info(mbi) ⇒ Object

Deprecated.
  • will be replaced with a proper object



219
220
221
222
223
224
225
226
227
228
229
# File 'lib/ragweed/wrap32/wrap32.rb', line 219

def str2memory_basic_info(mbi)
  s = OpenStruct.new
  s.BaseAddress,
  s.AllocationBase,
  s.AllocationProtect,
  s.RegionSize,
  s.State,
  s.Protect,
  s.Type = mbi.unpack("LLLLLLL")
  return s
end

.str2module_info(str) ⇒ Object

Deprecated.
  • will be replaced with a proper object



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/ragweed/wrap32/wrap32.rb', line 343

def str2module_info(str)
  ret = OpenStruct.new
  ret.dwSize,
  ret.th32ModuleID,
  ret.th32ProcessID,
  ret.GlblcntUsage,
  ret.ProccntUsage,
  ret.modBaseAddr,
  ret.modBaseSize,
  ret.hModule,
  ret.szModule,
  ret.szExePath = str.unpack("LLLLLLLLA256A260")
  ret.szModule = ret.szModule.asciiz
  ret.szExePath = ret.szExePath.asciiz
  return ret
end

.str2process_info(str) ⇒ Object

Deprecated.
  • will be replaced with a proper object



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/ragweed/wrap32/wrap32.rb', line 309

def str2process_info(str)
  ret = OpenStruct.new
  ret.dwSize,
  ret.cntUsage,
  ret.th32ProcessID,
  ret.th32DefaultHeapID,
  ret.th32ModuleID,
  ret.cntThreads,
  ret.th32ParentProcessID,
  ret.pcPriClassBase,
  ret.dwFlags,
  ret.szExeFile = str.unpack("LLLLLLLLLA2048")
  ret.szExeFile = ret.szExeFile.asciiz
  return ret
end

.str2thread_info(str) ⇒ Object

Deprecated.
  • will be replaced with a proper object



399
400
401
402
403
404
405
406
407
408
409
# File 'lib/ragweed/wrap32/wrap32.rb', line 399

def str2thread_info(str)
  ret = OpenStruct.new
  ret.dwSize,
  ret.cntUsage,
  ret.th32ThreadID,
  ret.th32OwnerProcessID,
  ret.tpBasePri,
  ret.tpDeltaPri,
  ret.thFlags = str.unpack("LLLLLLL")
  return ret
end

.suspend_thread(h) ⇒ Object

Suspend a thread given its handle.

Raises:



431
432
433
434
435
# File 'lib/ragweed/wrap32/wrap32.rb', line 431

def suspend_thread(h)
  r = Win.SuspendThread(h)
  raise WinX.new(:suspend_thread) if r == 0
  return r
end

.threads(pid) ⇒ Object

List all the threads in a process given its pid, returning a struct containing tids and run state. This is relatively expensive, because it uses Toolhelp32.



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/ragweed/wrap32/wrap32.rb', line 413

def threads(pid)
  h = Win.CreateToolhelp32Snapshot(0x4, pid)
  if h != -1
    mi = [(7*4),0,0,0,0,0,0].pack("LLLLLLL")
    if Win.Thread32First(h, mi) != 0
      ti = str2thread_info(mi)
      yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
      while Win.Thread32Next(h, mi) != 0
        ti = str2thread_info(mi)
        yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
      end
    end
  else
    raise WinX.new(:create_toolhelp32_snapshot)
  end
end

.versionObject

Returns the version string for the library.



12
13
14
# File 'lib/ragweed/wrap32.rb', line 12

def self.version
  VERSION
end

.virtual_alloc_ex(h, sz, addr = NULL, prot = 0x40) ⇒ Object

Allocate memory in a remote process (or yourself, with handle -1)

Raises:



181
182
183
184
185
# File 'lib/ragweed/wrap32/wrap32.rb', line 181

def virtual_alloc_ex(h, sz, addr=NULL, prot=0x40)
  r = Win.VirtualAllocEx(h, addr, sz, 0x1000, prot)
  raise WinX.new(:virtual_alloc_ex) if r == 0
  return r
end

.virtual_free_ex(h, ptr, type = 0x8000) ⇒ Object

Free memory in a remote process given the pointer returned from virtual_alloc_ex

Raises:



188
189
190
191
192
# File 'lib/ragweed/wrap32/wrap32.rb', line 188

def virtual_free_ex(h, ptr, type=0x8000)
  r = Win.VirtualFreeEx(h, ptr.to_i, 0, type)
  raise WinX.new(:virtual_free_ex) if r == 0
  return r
end

.virtual_protect_ex(h, addr, prot, size = 0) ⇒ Object

Change the protection of specific memory regions in a remote process.



244
245
246
247
248
249
250
251
252
253
254
# File 'lib/ragweed/wrap32/wrap32.rb', line 244

def virtual_protect_ex(h, addr, prot, size=0)
  old = [0].pack("L")
  base = virtual_query_ex(h, addr).BaseAddress if size == 0
  base ||= addr

  if Win.VirtualProtectEx(h, base, size, prot, old)
    old.unpack("L").first
  else
    raise WinX.new(:virtual_protect_ex)
  end
end

.virtual_query_ex(h, ptr) ⇒ Object

Return a struct with the MEMORY_BASIC_INFORMATION for a given address in the memory of a remote process. Gives you addressable memory ranges and protection flags.



234
235
236
237
238
239
240
241
# File 'lib/ragweed/wrap32/wrap32.rb', line 234

def virtual_query_ex(h, ptr)
  mbi = [0,0,0,0,0,0,0].pack("LLLLLLL")
  if Win.VirtualQueryEx(h, ptr, mbi, mbi.size)
    str2memory_basic_info(mbi)
  else
    nil
  end
end

.wait_for_debug_event(ms = 1000) ⇒ Object

Raises:



365
366
367
368
369
370
371
372
# File 'lib/ragweed/wrap32/debugging.rb', line 365

def wait_for_debug_event(ms=1000)
#      buf = FFI::MemoryPointer.new(Ragweed::Wrap32::DebugEvent, 1)
  buf = FFI::MemoryPointer.from_string("\x00" * 1024)
  r = Win.WaitForDebugEvent(buf, ms)
  raise WinX.new(:wait_for_debug_event) if r == 0 and get_last_error != 121
  return Ragweed::Wrap32::DebugEvent.new(buf) if r != 0
  return nil
end

.wait_for_single_object(h) ⇒ Object

Select(2), for a single object handle.

Raises:



303
304
305
306
# File 'lib/ragweed/wrap32/wrap32.rb', line 303

def wait_for_single_object(h)
  r = Win.WaitForSingleObject(h, -1)
  raise WinX.new(:wait_for_single_object) if r == -1
end

.wfmo(handles, ms = 100) ⇒ Object



576
577
578
579
580
581
582
583
584
585
# File 'lib/ragweed/wrap32/wrap32.rb', line 576

def wfmo(handles, ms=100)
  hp = handles.to_ptr
  r = Win.WaitForMultipleObjects(handles.size, hp, 0, ms)
  raise WinX(:wait_for_multiple_objects) if r == 0xFFFFFFFF
  if r < handles.size
    return handles[r]
  else
    return nil
  end
end

.with_suspended_thread(tid) ⇒ Object

Block wrapper for thread suspension



565
566
567
568
569
570
571
572
573
574
# File 'lib/ragweed/wrap32/wrap32.rb', line 565

def with_suspended_thread(tid)
  open_thread(tid) do |h|
    begin
      suspend_thread(h)
      ret = yield h
    ensure
      resume_thread(h)
    end
  end
end

.write_file(h, buf, overlapped = nil) ⇒ Object

Raises:



506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/ragweed/wrap32/wrap32.rb', line 506

def write_file(h, buf, overlapped=nil)
  if overlapped
    opp = overlapped.to_s
  else
    opp = NULL
  end

  outw = "\x00" * 4
  r = Win.WriteFile(h, buf, buf.size, outw, opp)
  raise WinX.new(:write_file) if r == 0 and get_last_error != 997
  return buf, outw.unpack("L").first
end

.write_process_memory(h, dst, val) ⇒ Object

Write a string into the memory of a remote process given its handle and an address

Raises:



195
196
197
198
199
200
# File 'lib/ragweed/wrap32/wrap32.rb', line 195

def write_process_memory(h, dst, val)
  val = val.to_s if not val.kind_of? String
  r = Win.WriteProcessMemory(h, dst.to_i, val, val.size, NULL)
  raise WinX.new(:write_process_memory) if r == 0
  return r
end

.writeable?(h, off) ⇒ Boolean

Use virtual_query_ex to tell whether an address is writable.

Returns:

  • (Boolean)


378
379
380
381
382
383
384
# File 'lib/ragweed/wrap32/wrap32.rb', line 378

def writeable?(h, off)
  if (x = virtual_query_ex(h, off))
    return PagePerms::WRITEABLE.member?(x.Protect & 0xFF)
  else
    return false
  end
end

Instance Method Details

#terminate_process(handle, exit_code) ⇒ Object

Raises:



65
66
67
68
# File 'lib/ragweed/wrap32/process_token.rb', line 65

def terminate_process(handle, exit_code)
  r = Win.TerminateProcess(handle, exit_code)
  raise WinX.new(:terminate_process) if r != 0
end