ragweed 0.2.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +60 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +86 -0
  5. data/VERSION +1 -0
  6. data/examples/hittracertux.rb +45 -0
  7. data/examples/hittracerx.rb +63 -0
  8. data/examples/hook_notepad.rb +9 -0
  9. data/examples/snicker.rb +183 -0
  10. data/examples/tux-example.rb +24 -0
  11. data/lib/ragweed/arena.rb +55 -0
  12. data/lib/ragweed/blocks.rb +128 -0
  13. data/lib/ragweed/debugger32.rb +400 -0
  14. data/lib/ragweed/debuggerosx.rb +456 -0
  15. data/lib/ragweed/debuggertux.rb +502 -0
  16. data/lib/ragweed/detour.rb +223 -0
  17. data/lib/ragweed/ptr.rb +48 -0
  18. data/lib/ragweed/rasm/bblock.rb +73 -0
  19. data/lib/ragweed/rasm/isa.rb +1115 -0
  20. data/lib/ragweed/rasm.rb +59 -0
  21. data/lib/ragweed/sbuf.rb +197 -0
  22. data/lib/ragweed/trampoline.rb +103 -0
  23. data/lib/ragweed/utils.rb +182 -0
  24. data/lib/ragweed/wrap32/debugging.rb +401 -0
  25. data/lib/ragweed/wrap32/device.rb +49 -0
  26. data/lib/ragweed/wrap32/event.rb +50 -0
  27. data/lib/ragweed/wrap32/hooks.rb +39 -0
  28. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  29. data/lib/ragweed/wrap32/process.rb +613 -0
  30. data/lib/ragweed/wrap32/process_token.rb +75 -0
  31. data/lib/ragweed/wrap32/thread_context.rb +142 -0
  32. data/lib/ragweed/wrap32/winx.rb +16 -0
  33. data/lib/ragweed/wrap32/wrap32.rb +583 -0
  34. data/lib/ragweed/wrap32.rb +59 -0
  35. data/lib/ragweed/wraposx/constants.rb +114 -0
  36. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  37. data/lib/ragweed/wraposx/region_info.rb +275 -0
  38. data/lib/ragweed/wraposx/structs.rb +102 -0
  39. data/lib/ragweed/wraposx/thread_context.rb +902 -0
  40. data/lib/ragweed/wraposx/thread_info.rb +160 -0
  41. data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
  42. data/lib/ragweed/wraposx/wraposx.rb +356 -0
  43. data/lib/ragweed/wraposx.rb +60 -0
  44. data/lib/ragweed/wraptux/constants.rb +101 -0
  45. data/lib/ragweed/wraptux/process.rb +35 -0
  46. data/lib/ragweed/wraptux/threads.rb +7 -0
  47. data/lib/ragweed/wraptux/wraptux.rb +72 -0
  48. data/lib/ragweed/wraptux.rb +57 -0
  49. data/lib/ragweed.rb +112 -0
  50. data/ragweed.gemspec +102 -0
  51. data/spec/ragweed_spec.rb +7 -0
  52. data/spec/spec_helper.rb +16 -0
  53. data/test/test_ragweed.rb +0 -0
  54. metadata +121 -0
@@ -0,0 +1,613 @@
1
+ class Ragweed::Process
2
+ def handle; @h; end
3
+ attr_reader :pid
4
+ include Ragweed
5
+
6
+ def self.find_by_regex(name)
7
+ Ragweed::Wrap32::all_processes do |p|
8
+ if p.szExeFile =~ name
9
+ return self.new(p.th32ProcessID)
10
+ end
11
+ end
12
+ end
13
+
14
+ # Get a pointer into the remote process; pointers are just fixnums
15
+ # with a read/write method and a to_s.
16
+ def ptr(x)
17
+ ret = Ragweed::Ptr.new(x)
18
+ ret.p = self
19
+ return ret
20
+ end
21
+
22
+ # clone a handle from the remote process to here (to here? tf?)
23
+ def dup_handle(h)
24
+ Ragweed::Wrap32::duplicate_handle(@h, h)
25
+ end
26
+
27
+ # look up a process by its name --- but this is in the local process,
28
+ # which is broken --- a heuristic that sometimes works for w32 functions,
29
+ # but probably never otherwise.
30
+ def get_proc(name)
31
+ return Ragweed::Ptr.new(name) if name.kind_of? Numeric or name.kind_of? Ptr
32
+ ptr(Ragweed::Wrap32::get_proc_address(name))
33
+ end
34
+
35
+ def is_hex(s)
36
+ s = s.strip
37
+
38
+ ## Strip leading 0s and 0x prefix
39
+ while s[0..1] == '0x' or s[0..1] == '00'
40
+ s = s[2..-1]
41
+ end
42
+
43
+ o = s
44
+
45
+ if s.hex.to_s(16) == o
46
+ return true
47
+ end
48
+ return false
49
+ end
50
+
51
+ ## This only gets called for breakpoints in modules
52
+ ## that have just been loaded and detected by a LOAD_DLL
53
+ ## event. It is called from on_load_dll() -> deferred_install()
54
+ def get_deferred_proc_remote(name, handle, base_of_dll)
55
+ if !name.kind_of?String
56
+ return name
57
+ end
58
+
59
+ mod, meth = name.split "!"
60
+
61
+ if mod.nil? or meth.nil?
62
+ raise "can not set this breakpoint: #{name}"
63
+ end
64
+
65
+ modh = handle
66
+
67
+ ## Location is an offset
68
+ if is_hex(meth)
69
+ baseaddr = 0
70
+ modules.each do |m|
71
+ if m.szModule == mod
72
+ break
73
+ end
74
+ end
75
+
76
+ ret = base_of_dll + meth.hex
77
+ else
78
+ ## Location is a symbolic name
79
+ ## Win32 should have successfully loaded the DLL
80
+ ret = remote_call "kernel32!GetProcAddress", modh, meth
81
+ end
82
+ ret
83
+ end
84
+
85
+ ## This only gets called for breakpoints
86
+ ## in modules that are already loaded
87
+ def get_proc_remote(name)
88
+ if !name.kind_of?String
89
+ return name
90
+ end
91
+
92
+ mod, meth = name.split "!"
93
+
94
+ if mod.nil? or meth.nil?
95
+ raise "can not set this breakpoint: #{name}"
96
+ end
97
+
98
+ # modh = remote_call "kernel32!GetModuleHandleW", mod.to_utf16
99
+ modh = remote_call "kernel32!GetModuleHandleA", mod
100
+ raise "no such module #{ mod }" if not modh
101
+
102
+ ## Location is an offset
103
+ if is_hex(meth)
104
+ baseaddr = 0
105
+ modules.each do |m|
106
+ if m.szModule == mod
107
+ baseaddr = m.modBaseAddr
108
+ break
109
+ end
110
+ end
111
+
112
+ ## Somehow the module does not appear to be
113
+ ## loaded. This should have been caught by
114
+ ## Process::is_breakpoint_deferred either way
115
+ ## Process::initialize should catch this return
116
+ if baseaddr == 0 or baseaddr == -1
117
+ return name
118
+ end
119
+
120
+ ret = baseaddr + meth.hex
121
+ else
122
+ ## Location is a symbolic name
123
+ ret = remote_call "kernel32!GetProcAddress", modh, meth
124
+ end
125
+ ret
126
+ end
127
+
128
+ ## Check if breakpoint location is deferred
129
+ ## This method expects a string 'module!function'
130
+ ## true is the module is not yet loaded
131
+ ## false is the module is loaded
132
+ def is_breakpoint_deferred(ip)
133
+ if !ip.kind_of? String
134
+ return false
135
+ end
136
+
137
+ m,f = ip.split('!')
138
+
139
+ if f.nil? or m.nil?
140
+ return true
141
+ end
142
+
143
+ modules.each do |d|
144
+ if d.szModule.to_s.match(/#{m}/)
145
+ return false
146
+ end
147
+ end
148
+
149
+ return true
150
+ end
151
+
152
+ ## Look up a process by name or regex, returning an array of all
153
+ ## matching processes, as objects.
154
+ def self.by_name(n)
155
+ n = Regexp.new(n) if not n.kind_of? Regexp
156
+ p = []
157
+ all_processes do |px|
158
+ if px.szExeFile =~ n
159
+ p << self.new(px.th32ProcessID)
160
+ end
161
+ end
162
+ p
163
+ end
164
+
165
+ # Just need a PID to get started.
166
+ def initialize(pid)
167
+ @pid = pid
168
+ @h = Ragweed::Wrap32::open_process(pid)
169
+ @a = arena()
170
+ end
171
+
172
+ # Return the EXE name of the process.
173
+ def image
174
+ buf = "\x00" * 256
175
+ if Ragweed::Wrap32::nt_query_information_process(@h, 27, buf)
176
+ buf = buf.from_utf16
177
+ buf = buf[(buf.index("\\"))..-1]
178
+ return buf.asciiz
179
+ end
180
+ nil
181
+ end
182
+
183
+ # Return a list of all the threads in the process; relatively
184
+ # expensive, so cache the result.
185
+ def threads(full=false, &block)
186
+ return Ragweed::Wrap32::threads(@pid, &block) if block_given?
187
+ ret = []
188
+ Ragweed::Wrap32::threads(@pid) {|x| ((full) ? ret << x : ret << x.th32ThreadID) }
189
+ return ret
190
+ end
191
+
192
+ # Suspend all the threads in the process.
193
+ def suspend_all; threads.each {|x| suspend(x)}; end
194
+
195
+ # Resume all the threads in the process. XXX this will not
196
+ # resume threads with suspend counts greater than 1.
197
+ def resume_all; threads.each {|x| resume(x)}; end
198
+
199
+ # Suspend a thread by tid. Technically, this doesn't need to be
200
+ # a method; you can suspend a thread anywhere without a process handle.
201
+ def suspend(tid); Ragweed::Wrap32::open_thread(tid) {|x| Ragweed::Wrap32::suspend_thread(x)}; end
202
+
203
+ # Resume a thread by tid.
204
+ def resume(tid); Ragweed::Wrap32::open_thread(tid) {|x| Ragweed::Wrap32::resume_thread(x)}; end
205
+
206
+ # List the modules for the process, either yielding a struct for
207
+ # each to a block, or returning a list.
208
+ def modules(&block)
209
+ if block_given?
210
+ Ragweed::Wrap32::list_modules(@pid, &block)
211
+ else
212
+ ret = []
213
+ Ragweed::Wrap32::list_modules(@pid) {|x| ret << x}
214
+ return ret
215
+ end
216
+ end
217
+
218
+ # Read/write ranges of data or fixnums to/from the process by address.
219
+ def read(off, sz=4096); Ragweed::Wrap32::read_process_memory(@h, off, sz); end
220
+ def write(off, data); Ragweed::Wrap32::write_process_memory(@h, off, data); end
221
+ def read32(off); read(off, 4).unpack("L").first; end
222
+ def read16(off); read(off, 2).unpack("v").first; end
223
+ def read8(off); read(off, 1)[0]; end
224
+ def write32(off, v); write(off, [v].pack("L")); end
225
+ def write16(off, v); write(off, [v].pack("v")); end
226
+ def write8(off, v); write(off, v.chr); end
227
+
228
+ # call a function, by name or address, in the process, using
229
+ # CreateRemoteThread
230
+ def remote_call(meth, *args)
231
+ loc = meth
232
+ loc = get_proc(loc) if loc.kind_of? String
233
+ loc = Ragweed::Ptr.new loc
234
+ raise "bad proc name" if loc.null?
235
+ t = Trampoline.new(self, loc)
236
+ t.call *args
237
+ end
238
+
239
+ # Can I write to this address in the process?
240
+ def writeable?(off); Ragweed::Wrap32::writeable? @h, off; end
241
+
242
+ # Use VirtualAllocEx to grab a block of memory in the process. This
243
+ # is expensive, the equivalent of mmap()'ing for each allocation.
244
+ def syscall_alloc(sz); ptr(Ragweed::Wrap32::virtual_alloc_ex(@h, sz)); end
245
+
246
+ # Use arenas, when possible, to quickly allocate memory. The upside
247
+ # is this is very fast. The downside is you can't free the memory
248
+ # without invalidating every allocation you've made prior.
249
+ def alloc(sz, syscall=false)
250
+ if syscall or sz > 4090
251
+ ret = syscall_alloc(sz)
252
+ else
253
+ ptr(@a.alloc(sz))
254
+ end
255
+ end
256
+
257
+ # Free the return value of syscall_alloc. Do NOT use for the return
258
+ # value of alloc.
259
+ def free(off)
260
+ Ragweed::Wrap32::virtual_free_ex(@h, off)
261
+ end
262
+
263
+ # Convert an address to "module+10h" notation, when possible.
264
+ def to_modoff(off, force=false)
265
+ if not @modules or force
266
+ @modules = modules.sort {|x,y| x.modBaseAddr <=> y.modBaseAddr}
267
+ end
268
+
269
+ @modules.each do |m|
270
+ if off >= m.modBaseAddr and off < (m.modBaseAddr + m.modBaseSize)
271
+ return "#{ m.szModule }+#{ (off - m.modBaseAddr).to_s(16) }h"
272
+ end
273
+ end
274
+
275
+ return "#{ off.to_x }h"
276
+ end
277
+
278
+ # Get another allocation arena for this process. Pretty cheap. Given
279
+ # a block, behaves like File#open, disposing of the arena when you're
280
+ # done.
281
+ def arena(&block)
282
+ me = self
283
+ a = Arena.new(lambda {me.syscall_alloc(4096)},
284
+ lambda {|p| me.free(p)},
285
+ lambda {|dst, src| me.write(dst, src)})
286
+ if block_given?
287
+ ret = yield a
288
+ a.release
289
+ return ret
290
+ end
291
+ a
292
+ end
293
+
294
+ # Insert a string anywhere into the memory of the remote process,
295
+ # returning its address, using an arena.
296
+ def insert(buf); @a.copy(buf); end
297
+
298
+ # List all memory regions in the remote process by iterating over
299
+ # VirtualQueryEx. With a block, yields MEMORY_BASIC_INFORMATION
300
+ # structs. Without it, returns [baseaddr,size] tuples.
301
+ #
302
+ # We "index" this list, so that we can refer to memory locations
303
+ # by "region number", which is a Rubycorn-ism and not a Win32-ism.
304
+ # You'll see lots of functions asking for memory indices, and this
305
+ # is what they're referring to.
306
+ def list_memory(&block)
307
+ ret = []
308
+ i = 0
309
+ while (mbi = Ragweed::Wrap32::virtual_query_ex(@h, i))
310
+ break if (not ret.empty? and mbi.BaseAddress == 0)
311
+ if block_given?
312
+ yield mbi
313
+ else
314
+ base = mbi.BaseAddress || 0
315
+ size = mbi.RegionSize || 0
316
+ ret << [base,size] if mbi.State & 0x1000 # MEM_COMMIT
317
+ i = base + size
318
+ end
319
+ end
320
+ ret
321
+ end
322
+
323
+ # Human-readable standard output of list_memory. Remember that the
324
+ # index number is important.
325
+ def dump_memory_list
326
+ list_memory.each_with_index {|x,i| puts "#{ i }. #{ x[0].to_s(16) }(#{ x[1] })"}
327
+ true
328
+ end
329
+
330
+ # Read an entire memory region into a string by region number.
331
+ def get_memory(i, opts={})
332
+ refresh opts
333
+ read(@memlist[i][0], @memlist[i][1])
334
+ end
335
+
336
+ # Print a canonical hexdump of an entire memory region by region number.
337
+ def dump_memory(i, opts={}); get_memory(i, opts).hexdump; end
338
+
339
+ # In Python, and maybe Ruby, it was much faster to work on large
340
+ # memory regions a 4k page at a time, rather than reading the
341
+ # whole thing into one big string. Scan takes a memory region
342
+ # and yields 4k chunks of it to a block, along with the length
343
+ # of each chunk.
344
+ def scan(i, opts={})
345
+ refresh opts
346
+ memt = @memlist[i]
347
+ if memt[1] > 4096
348
+ 0.step(memt[1], 4096) do |i|
349
+ block = (memt[1] - i).cap(4096)
350
+ yield read(memt[0] + i, block), memt[0]+i
351
+ end
352
+ else
353
+ yield read(memt[0], memt[1]), memt[0]
354
+ end
355
+ end
356
+
357
+ # Dump thread context, returning a struct that contains things like
358
+ # .Eip and .Eax.
359
+ def thread_context(tid)
360
+ Ragweed::Wrap32::open_thread(tid) do |h|
361
+ Ragweed::Wrap32::get_thread_context(h)
362
+ end
363
+ end
364
+
365
+ # Take a region of memory and walk over it in 255-byte samples (less
366
+ # than 255 bytes and you lose accuracy, but you can increase it with
367
+ # the ":window" option), computing entropy for each sample, returning
368
+ # a list of [offset,entropy] tuples.
369
+ def entropy_map(i, opts={})
370
+ ret = []
371
+ startoff = opts[:starting_offset]
372
+ startoff ||= 0
373
+ window = opts[:window] || 255
374
+ scan(i, opts) do |block, soff|
375
+ startoff.stepwith(block.size, window) do |off, len|
376
+ ret << [off, block[off,len].entropy]
377
+ end
378
+ end
379
+ return ret
380
+ end
381
+
382
+ # Given a source and destination memory region, scan through "source"
383
+ # looking for properly-aligned U32LE values that would be valid pointers
384
+ # into "destination". The ":range_start" and ":range_end" options
385
+ # constrain what a "valid pointer" into "destination" is.
386
+ def pointers_to(src, dst, opts={})
387
+ refresh opts
388
+ ret = {}
389
+ range = ((opts[:range_start] || @memlist[dst][0])..(opts[:range_stop] || @memlist[dst][0]+@memlist[dst][1]))
390
+ scan(src, opts) do |block, soff|
391
+ 0.stepwith(block.size, 4) do |off, len|
392
+ if len == 4
393
+ if range.member? block[off,4].to_l32
394
+ ret[soff + off] = block[off,4].to_l32
395
+ end
396
+ end
397
+ end
398
+ end
399
+ return ret
400
+ end
401
+
402
+ # Given a memory region number, do a Unix strings(1) on it. Valid
403
+ # options:
404
+ # :unicode: you probably always want to set this to "true"
405
+ # :minimum: how small strings to accept.
406
+ #
407
+ # Fairly slow.
408
+ def strings_mem(i, opts={})
409
+ ret = []
410
+ opts[:offset] ||= 0
411
+ scan(i) do |block, soff|
412
+ while 1
413
+ off, size = block.nextstring(opts)
414
+ break if not off
415
+ opts[:offset] += (off + size)
416
+ ret << [soff+off, size, block[off,size]]
417
+ end
418
+ end
419
+ ret
420
+ end
421
+
422
+ # Given a string key, find it in memory. Very slow. Will read all
423
+ # memory regions, but you can provide ":index_range", which must be
424
+ # a Range object, to constrain which ranges to search through.
425
+ # Returns a list of structs containing absolute memory locations,
426
+ # the index of the region, and some surrounding context for the
427
+ # hit.
428
+ def hunt(key, opts={})
429
+ ret = []
430
+ refresh opts
431
+ range = opts[:index_range] || (0..@memlist.size)
432
+ @memlist.each_with_index do |t, i|
433
+ if range.member? i
434
+ if opts[:noisy]
435
+ puts "#{ i }. #{ t[0].to_s(16) } -> #{ (t[0]+t[1]).to_s(16) }"
436
+ end
437
+ scan(i, opts) do |block, soff|
438
+ if (needle = block.index(key))
439
+ r = OpenStruct.new
440
+ r.location = (t[0] + soff + needle)
441
+ r.index = i
442
+ r.context = block
443
+ ret << r
444
+ return ret if opts[:first]
445
+ end
446
+ end
447
+ end
448
+ end
449
+ ret
450
+ end
451
+
452
+ private
453
+
454
+ def windowize(i, opts={})
455
+ window = opts[:window] || 1024
456
+ if window == :auto
457
+ r = region_range(i)
458
+ window = (r.last - r.first) / 60
459
+ end
460
+ return window
461
+ end
462
+
463
+ public
464
+
465
+ # Like entropy_map, scan a process and compute adler16 checksums for
466
+ # 1k (or :window) blocks.
467
+ def adler_map(i, opts={})
468
+ refresh opts
469
+ window = windowize(i, opts)
470
+ ret = []
471
+ scan(i, opts) do |block,soff|
472
+ 0.stepwith(block.size-1, window) do |off, len|
473
+ if (b = block[off,len])
474
+ ret << b.adler
475
+ end
476
+ end
477
+ end
478
+ ret
479
+ end
480
+
481
+ # If you store the adler map, you've compressed the memory region
482
+ # down to a small series of fixnums, and you can use it with this
483
+ # function to re-check the memory region and see if anything's changing.
484
+ def adler_compare(i, orig, opts={})
485
+ refresh opts
486
+ window = windowize(i, opts)
487
+ ret = []
488
+ c = -1
489
+ scan(i, opts) do |block,soff|
490
+ 0.stepwith(block.size-1, window) do |off, len|
491
+ if block[off,len].adler != orig[c += 1]
492
+ ret << soff+off
493
+ end
494
+ end
495
+ end
496
+ ret
497
+ end
498
+
499
+ # Quick and dirty visualization of a memory region by checksum
500
+ # changes.
501
+ class AdlerChart
502
+
503
+ # Create with a WinProcess and region index
504
+ def initialize(p, i)
505
+ @p = p
506
+ @i = i
507
+ @initstate = map
508
+ end
509
+
510
+ private
511
+ def map; @p.adler_map(@i, :window => :auto); end
512
+
513
+ public
514
+
515
+ # Just puts the chart repeatedly, to get:
516
+ # ........................................................*.*..
517
+ # ..........................................................*..
518
+ # ..........................................................*..
519
+ # Where * represents a chunk of memory that has changed.
520
+ def to_s
521
+ s = StringIO.new
522
+ newstate = map
523
+ @initstate.each_with_index do |sum, i|
524
+ if sum != newstate[i]
525
+ s.write "*"
526
+ @initstate[i] = newstate[i]
527
+ else
528
+ s.write "."
529
+ end
530
+ end
531
+ s.rewind;s.read()
532
+ end
533
+ end
534
+
535
+ # See WinProcess::AdlerChart. Get one.
536
+ def adler_chart(i)
537
+ AdlerChart.new self, i
538
+ end
539
+
540
+ # Given a memory region, use the adler routines to create a checksum
541
+ # map, wait a short period of time, and scan for changes, to find
542
+ # churning memory.
543
+ def changes(i, sleeptime=0.5)
544
+ q = adler_map(i)
545
+ sleep(sleeptime)
546
+ adler_compare(i, q)
547
+ end
548
+
549
+ # Get the memory range, as a Ruby Range, for a region by index
550
+ def region_range(i, opts={})
551
+ refresh opts
552
+ (@memlist[i][0]..(@memlist[i][1]+@memlist[i][0]))
553
+ end
554
+
555
+ # Figure out what region (by region index) has an address
556
+ def which_region_has?(addr, opts={})
557
+ refresh opts
558
+ @memlist.each_with_index do |r, i|
559
+ return i if (r[0]..r[0]+r[1]).member? addr
560
+ end
561
+ return nil
562
+ end
563
+
564
+ # Do something with a thread while its suspended
565
+ def with_suspended_thread(tid)
566
+ ret = nil
567
+ Ragweed::Wrap32::with_suspended_thread(tid) {|x| ret = yield}
568
+ return ret
569
+ end
570
+
571
+ # For libraries compiled with frame pointers: walk EBP back
572
+ # until it stops giving intelligible addresses, and, at each
573
+ # step, grab the saved EIP from just before it.
574
+ def thread_stack_trace(tid)
575
+ with_suspended_thread(tid) do
576
+ ctx = thread_context(tid)
577
+ if((start = read32(ctx.Ebp)) != 0)
578
+ a = start
579
+ stack = [[start, read32(start+4)]]
580
+ while((a = read32(a)) and a != 0 and not stack.member?(a))
581
+ begin
582
+ stack << [a, read32(a+4)]
583
+ rescue; break; end
584
+ end
585
+ return stack
586
+ end
587
+ end
588
+ []
589
+ end
590
+
591
+ # Human-readable version of thread_stack_trace, with module
592
+ # offsets.
593
+ def dump_stack_trace(tid)
594
+ thread_stack_trace(tid).each do |frame, code|
595
+ puts "#{ frame.to_x } @ #{ to_modoff(code) }"
596
+ end
597
+ end
598
+ alias_method :bt, :dump_stack_trace
599
+
600
+ def detour(loc, o={})
601
+ klass = o[:class] || Detour
602
+ loc = get_proc(loc)
603
+ r = klass.new(loc, o)
604
+ r.call if not o[:chicken]
605
+ return r
606
+ end
607
+
608
+ private
609
+
610
+ def refresh(opts={})
611
+ @memlist = list_memory if not @memlist or opts.delete(:refresh)
612
+ end
613
+ end
@@ -0,0 +1,75 @@
1
+ module Ragweed::Wrap32
2
+ module TokenAccess
3
+ ADJUST_DEFAULT = 128
4
+ ADJUST_GROUPS = 64
5
+ ADJUST_PRIVILEGES = 32
6
+ ALL_ACCESS = 0xf00ff
7
+ ASSIGN_PRIMARY = 1
8
+ DUPLICATE = 2
9
+ EXECUTE = 0x20000
10
+ IMPERSONATE = 4
11
+ QUERY = 8
12
+ QUERY_SOURCE = 16
13
+ READ = 0x20008
14
+ WRITE = 0x200e0
15
+ end
16
+
17
+ module PrivilegeAttribute
18
+ ENABLED = 0x2
19
+ ENABLED_BY_DEFAULT = 0x1
20
+ USED_FOR_ACCESS = 0x80000000
21
+ end
22
+
23
+ module Win
24
+ extend FFI::Library
25
+
26
+ ffi_lib 'kernel32','Advapi32'
27
+ ffi_convention :stdcall
28
+ attach_function 'OpenProcess', [ :long, :long, :long ], :long
29
+ attach_function 'OpenProcessToken', [:long, :long, :pointer ], :long
30
+
31
+ # ffi_lib 'advapi32'
32
+ # ffi_convention :stdcall
33
+ attach_function 'AdjustTokenPrivileges', [ :long, :long, :pointer, :long, :pointer, :pointer ], :long
34
+ attach_function 'LookupPrivilegeValueA', [ :pointer, :pointer, :pointer ] ,:long
35
+ end
36
+
37
+ class << self
38
+
39
+ def open_process_token(h, access=Ragweed::Wrap32::TokenAccess::ADJUST_PRIVILEGES)
40
+ outw = "\x00" * 4
41
+ r = Win.OpenProcessToken(h, access, outw)
42
+ raise WinX.new(:open_process_token) if r == 0
43
+ return outw.unpack("L").first
44
+ end
45
+
46
+ def adjust_token_privileges(t, disable, *args)
47
+ buf = FFI::MemoryPointer.from_string( [args.size].pack("L") + (args.map {|tup| tup.pack("QL") }.join("")) )
48
+
49
+ r = Win.AdjustTokenPrivileges(t, disable, buf, buf.size, nil, nil)
50
+
51
+ raise WinX.new(:adjust_token_privileges) if r == 0
52
+ end
53
+
54
+ def lookup_privilege_value(name)
55
+ namep = FFI::MemoryPointer.from_string(name)
56
+ outw = FFI::MemoryPointer.new(:int64, 1)
57
+ r = Win.LookupPrivilegeValueA(nil, namep, outw)
58
+ r = Win.LookupPrivilegeValueA(nil, name, outw)
59
+ raise WinX.new(:lookup_privilege_value) if r == 0
60
+ outw.read_long_long
61
+ end
62
+ end
63
+ end
64
+
65
+ class Ragweed::Wrap32::ProcessToken
66
+ def initialize(p=nil)
67
+ p ||= Ragweed::Wrap32::open_process(Ragweed::Wrap32::get_current_process_id)
68
+ @h = Ragweed::Wrap32::open_process_token(p)
69
+ end
70
+
71
+ def grant(name)
72
+ luid = Ragweed::Wrap32::lookup_privilege_value(name)
73
+ Ragweed::Wrap32::adjust_token_privileges(@h, 0, [luid, Ragweed::Wrap32::PrivilegeAttribute::ENABLED])
74
+ end
75
+ end