ragweed 0.2.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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