tduehr-ragweed 0.1.5

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 (62) hide show
  1. data/History.txt +15 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +48 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/arena.rb +55 -0
  11. data/lib/ragweed/blocks.rb +128 -0
  12. data/lib/ragweed/debugger32.rb +338 -0
  13. data/lib/ragweed/debuggerosx.rb +419 -0
  14. data/lib/ragweed/debuggertux.rb +347 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/isa.rb +1046 -0
  18. data/lib/ragweed/rasm/util.rb +26 -0
  19. data/lib/ragweed/rasm.rb +53 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +87 -0
  23. data/lib/ragweed/wrap32/debugging.rb +163 -0
  24. data/lib/ragweed/wrap32/device.rb +49 -0
  25. data/lib/ragweed/wrap32/event.rb +50 -0
  26. data/lib/ragweed/wrap32/hooks.rb +23 -0
  27. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  28. data/lib/ragweed/wrap32/process.rb +506 -0
  29. data/lib/ragweed/wrap32/process_token.rb +59 -0
  30. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  31. data/lib/ragweed/wrap32/winx.rb +16 -0
  32. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  33. data/lib/ragweed/wrap32.rb +53 -0
  34. data/lib/ragweed/wraposx/constants.rb +101 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +244 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +213 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  40. data/lib/ragweed/wraposx.rb +53 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +3 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +53 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. metadata +127 -0
@@ -0,0 +1,26 @@
1
+ # Cheating; monkeypatching Object is evil.
2
+ class Object
3
+ # self-evident
4
+ def callable?; respond_to? :call; end
5
+ def number?; kind_of? Numeric; end
6
+
7
+ # while X remains callable, keep calling it to get its value
8
+ def derive
9
+ x = self
10
+ while x.callable?
11
+ x = x()
12
+ end
13
+ return x
14
+ end
15
+ end
16
+
17
+ # class String
18
+ # # this is just horrible
19
+ # def distorm
20
+ # Frasm::DistormDecoder.new.decode(self)
21
+ # end
22
+ #
23
+ # def disasm
24
+ # distorm.each {|i| puts i.mnem}
25
+ # end
26
+ # end
@@ -0,0 +1,53 @@
1
+ # Dir[File.expand_path("#{File.dirname(__FILE__)}/rasm/*.rb")].each do |file|
2
+ # require file
3
+ # end
4
+ module Ragweed; end
5
+ module Ragweed::Rasm
6
+
7
+ # :stopdoc:
8
+ VERSION = '0.1.5'
9
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
10
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
11
+ # :startdoc:
12
+
13
+ # Returns the version string for the library.
14
+ #
15
+ def self.version
16
+ VERSION
17
+ end
18
+
19
+ # Returns the library path for the module. If any arguments are given,
20
+ # they will be joined to the end of the libray path using
21
+ # <tt>File.join</tt>.
22
+ #
23
+ def self.libpath( *args )
24
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
25
+ end
26
+
27
+ # Returns the lpath for the module. If any arguments are given,
28
+ # they will be joined to the end of the path using
29
+ # <tt>File.join</tt>.
30
+ #
31
+ def self.path( *args )
32
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
33
+ end
34
+
35
+ # Utility method used to require all files ending in .rb that lie in the
36
+ # directory below this file that has the same name as the filename passed
37
+ # in. Optionally, a specific _directory_ name can be passed in such that
38
+ # the _filename_ does not have to be equivalent to the directory.
39
+ #
40
+ def self.require_all_libs_relative_to( fname, dir = nil )
41
+ dir ||= ::File.basename(fname, '.*')
42
+ search_me = ::File.expand_path(
43
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
44
+
45
+ Dir.glob(search_me).sort.each {|rb| require rb}
46
+ # require File.dirname(File.basename(__FILE__)) + "/#{x}"
47
+
48
+ end
49
+ end # module Ragweed::Rasm
50
+
51
+ Ragweed::Rasm.require_all_libs_relative_to(__FILE__)
52
+
53
+ # EOF
@@ -0,0 +1,197 @@
1
+ # Stolen (mostly) from Net::SSH
2
+
3
+ # a* string a10 string 10 bytes A* string trimmed
4
+ # C uchar
5
+ # L native u32, l signed
6
+ # N BE u32, n 16
7
+ # Qq quad
8
+ # Ss native u16
9
+ # Vv LE u32
10
+
11
+ # Encapsulate a buffer of data with structured data accessors
12
+ class Ragweed::Sbuf
13
+ attr_reader :content
14
+ attr_accessor :position
15
+
16
+ def self.from(*args)
17
+ raise ArgumentError, "odd argument count" if args.length.odd?
18
+
19
+ b = self.new
20
+ while not args.empty?
21
+ t = args.shift; v = args.shift
22
+ if t == :raw
23
+ b.straw(v)
24
+ elsif v.kind_of? Array
25
+ b.st t, *v
26
+ else
27
+ b.st t, v
28
+ end
29
+ end
30
+
31
+ return b
32
+ end
33
+
34
+ def st(t, *args)
35
+ send "st#{ t }", *args
36
+ end
37
+
38
+ def straw(*args)
39
+ args.each do |raw|
40
+ @content << raw.to_s
41
+ end
42
+ self
43
+ end
44
+
45
+ def initialize(opts={})
46
+ @content = opts[:content] || ""
47
+ @position = 0
48
+ end
49
+
50
+ def consume!(n=position)
51
+ if(n >= length); clear!
52
+ elsif n > 0
53
+ @content = remainder(n)
54
+ @position -= n
55
+ @position = 0 if @position < 0
56
+ end
57
+ self
58
+ end
59
+
60
+ def ldraw(n=length)
61
+ n = ((self.length) - position) if(position + n > length)
62
+ @position += n
63
+ @content[position-n, n]
64
+ end
65
+
66
+ def ld(t, *args)
67
+ return ldraw(t) if t.kind_of? Numeric
68
+
69
+ begin
70
+ send "ld#{ t }", *args
71
+ rescue => e
72
+ case t.to_s
73
+ when /^strz(\d+)?/
74
+ n = $1.to_i if not $1.empty?
75
+ n ||= 0
76
+ ldsz(n)
77
+ when /^strs(\d+)?/
78
+ n = $1.to_i if not $1.empty?
79
+ n ||= 0
80
+ ldss(n)
81
+ else
82
+ raise e
83
+ end
84
+ end
85
+ end
86
+
87
+ def sz(t, *args); self.class.sz(t, *args); end
88
+ def self.sz(t, *args)
89
+ begin
90
+ send "sz#{ t }", *args
91
+ rescue => e
92
+ case t.to_s
93
+ when /^strz(\d+)/
94
+ $1.to_i
95
+ when /^strs(\d+)/
96
+ $1.to_i
97
+ when /^str.*/
98
+ raise Exception, "can't take size of unbounded string"
99
+ else
100
+ raise e
101
+ end
102
+ end
103
+ end
104
+
105
+ def shraw(n=length)
106
+ ret = ldraw(n)
107
+ consume!
108
+ return ret
109
+ end
110
+
111
+ def sh(t, *args)
112
+ return shraw(t) if t.kind_of? Numeric
113
+ ret = send "ld#{ t }", *args
114
+ consume!
115
+ return ret
116
+ end
117
+
118
+ def ldsz(n=0)
119
+ n = @content.size - @position if n == 0
120
+ ld(n).unpack("a#{ n }").first
121
+ end
122
+
123
+ def ldss(n=0)
124
+ n = @content.size - @position if n == 0
125
+ ld(n).unpack("A#{ n }").first
126
+ end
127
+
128
+ def length; @content.length; end
129
+ def size; @content.size; end
130
+ def empty?; @content.empty?; end
131
+
132
+ def available; length - position; end
133
+ alias_method :remaining, :available
134
+
135
+ def to_s; @content.dup; end
136
+ def ==(b); to_s == b.to_s; end
137
+ def reset; @position = 0; end
138
+ def eof?; @position >= length; end
139
+ def clear!; @content = "" and reset; end
140
+ def remainder(n = position); @content[n..-1] || ""; end
141
+ def remainder_as_buffer(t=Sbuf); t.new(:content => remainder); end
142
+
143
+ def self.szl64; 8; end
144
+ def self.szb64; 8; end
145
+ def self.szn64; 8; end
146
+
147
+ def ldl64; ld(8).unpack("Q").first; end
148
+ def ldb64; ld(8).reverse.unpack("Q").first; end
149
+ alias_method :ldn64, :ldb64
150
+
151
+ def stl64(v); straw([v].pack("Q")); end
152
+ def stb64(v); straw([v].pack("Q").reverse); end
153
+ alias_method :stn64, :stb64
154
+
155
+ def self.szl32; 4; end
156
+ def self.szb32; 4; end
157
+ def self.szn32; 4; end
158
+
159
+ def ldl32; ld(4).unpack("L").first; end
160
+ def ldb32; ld(4).unpack("N").first; end
161
+ alias_method :ldn32, :ldb32
162
+
163
+ def stl32(v); straw([v].pack("L")); end
164
+ def stb32(v); straw([v].pack("N")); end
165
+ alias_method :stn32, :stb32
166
+
167
+ def self.szl16; 2; end
168
+ def self.szb16; 2; end
169
+ def self.szn16; 2; end
170
+
171
+ def ldl16; ld(2).unpack("v").first; end
172
+ def ldb16; ld(2).unpack("n").first; end
173
+ alias_method :ldn16, :ldb16
174
+
175
+ def stl16(v); straw([v].pack("v")); end
176
+ def stb16(v); straw([v].pack("n")); end
177
+ alias_method :stn16, :stb16
178
+
179
+ def self.szl8; 1; end;
180
+ def self.szb8; 1; end;
181
+ def self.szn8; 1; end;
182
+
183
+ def ldl8; ld(1)[0]; end
184
+ def ldb8; ldl8; end
185
+ alias_method :ldn8, :ldb8
186
+
187
+ def stl8(v)
188
+ if v.kind_of? String
189
+ straw(v[0].chr)
190
+ else
191
+ straw([v].pack("c"))
192
+ end
193
+ end
194
+
195
+ def stb8(v); stl8(v); end
196
+ alias_method :stn8, :stb8
197
+ end
@@ -0,0 +1,103 @@
1
+ class Ragweed::Trampoline
2
+
3
+ # Normally called through WinProcess#remote_call, but, for
4
+ # what it's worth: needs a WinProcess instance and a location,
5
+ # which can be a string module!function or a pointer.
6
+ def initialize(p, loc, opts={})
7
+ @p = p
8
+ @loc = @p.get_proc loc
9
+ @argc = opts[:argc]
10
+ @a = @p.arena
11
+ @mem = @a.alloc(1024)
12
+ @arg_mem = @mem + 512
13
+ @wait = opts[:wait] || true
14
+ @chicken = opts[:chicken]
15
+ @opts = opts
16
+ end
17
+
18
+ # Call the remote function. Returns the 32 bit EAX return value provided
19
+ # by stdcall.
20
+ def call(*args)
21
+ raise "Insufficient Arguments" if @argc and @argc != args.size
22
+
23
+ @shim = Ragweed::Blocks::remote_trampoline(args.size, @opts)
24
+
25
+ # Won't leak memory.
26
+ @p.arena do |a|
27
+ # 1024 is a SWAG. Divide it in half, one for the trampoline
28
+ # (which is unrolled, because I am lazy and dumb) and the other
29
+ # for the call stack.
30
+ base = @p.ptr(a.alloc(1024))
31
+
32
+ argm = base + 512
33
+ cur = argm
34
+
35
+ # Write the location for the tramp to call
36
+ cur.write(@loc)
37
+ cur += 4
38
+
39
+ # Write the function arguments into the call stack.
40
+ (0...args.size).each_backwards do |i|
41
+ if args[i].kind_of? Integer
42
+ val = args[i].to_l32
43
+ elsif args[i].kind_of? String
44
+ stash = a.copy(args[i])
45
+ val = stash.to_l32
46
+ else
47
+ val = args[i].to_s
48
+ end
49
+ cur.write(val)
50
+ cur += 4
51
+ end if args.size.nonzero?
52
+
53
+ # Write a placeholder for the return value
54
+ cur.write(0xDEADBEEF.to_l32)
55
+
56
+ # Write the tramp
57
+ s = @shim.assemble
58
+ base.write(s)
59
+
60
+ th = Wrap32::create_remote_thread(@p.handle, base, argm)
61
+ Wrap32::wait_for_single_object(th) if @wait
62
+ Wrap32::close_handle(th)
63
+ ret = @p.read32(cur)
64
+ if ret == 0xDEADBEEF
65
+ ret = nil
66
+ end
67
+ ret
68
+ end
69
+ end
70
+ end
71
+
72
+ class Trevil
73
+ def initialize(p)
74
+ @p = p
75
+ @ev1, @ev2 = [WinEvent.new, WinEvent.new]
76
+ @a = @p.arena
77
+ end
78
+
79
+ def clear!
80
+ @a.release
81
+ end
82
+
83
+ def go
84
+ mem = @a.alloc(1024)
85
+ base = @p.ptr(mem)
86
+ data = base + 512
87
+ swch = ["OpenProcess",
88
+ "DuplicateHandle",
89
+ "ResetEvent",
90
+ "SetEvent",
91
+ "WaitForSingleObject"].
92
+ map {|x| @p.get_proc("kernel32!#{x}").to_i}.
93
+ pack("LLLLL")
94
+ state = [Wrap32::get_current_process_id, @ev1.handle, @ev2.handle].
95
+ pack("LLL")
96
+
97
+ data.write(swch + state)
98
+ base.write(event_pair_stub(:debug => false).assemble)
99
+ Wrap32::create_remote_thread(@p.handle, base, data)
100
+ @ev1.wait
101
+ @ev2
102
+ end
103
+ end
@@ -0,0 +1,87 @@
1
+ # These should probably be extensions to Module since that's the location of instance_eval and friends.
2
+ class Object
3
+ module ObjectExtensions
4
+ # Every object has a "singleton" class, which you can think
5
+ # of as the class (ie, 1.metaclass =~ Fixnum) --- but that you
6
+ # can modify and extend without fucking up the actual class.
7
+ def metaclass; class << self; self; end; end
8
+ def meta_eval(&blk) metaclass.instance_eval &blk; end
9
+ def meta_def(name, &blk) meta_eval { define_method name, &blk }; end
10
+ def try(meth, *args); send(meth, *args) if respond_to? meth; end
11
+
12
+ def through(meth, *args)
13
+ if respond_to? meth
14
+ send(meth, *args)
15
+ else
16
+ self
17
+ end
18
+ end
19
+
20
+ def pbpaste
21
+ `pbpaste`
22
+ end if RUBY_PLATFORM =~ /darwin/
23
+
24
+ ## This is from Topher Cyll's Stupd IRB tricks
25
+ def mymethods
26
+ (self.methods - self.class.superclass.methods).sort
27
+ end
28
+ end
29
+ include ObjectExtensions
30
+ end
31
+
32
+ class String
33
+ def to_l32; unpack("L").first; end
34
+ def to_b32; unpack("N").first; end
35
+ def to_l16; unpack("v").first; end
36
+ def to_b16; unpack("n").first; end
37
+ def to_u8; self[0]; end
38
+ def shift_l32; shift(4).to_l32; end
39
+ def shift_b32; shift(4).to_b32; end
40
+ def shift_l16; shift(2).to_l16; end
41
+ def shift_b16; shift(2).to_b16; end
42
+ def shift_u8; shift(1).to_u8; end
43
+ end
44
+
45
+ class Integer
46
+ module IntegerExtensions
47
+ # Convert integers to binary strings
48
+ def to_l32; [self].pack "L"; end
49
+ def to_b32; [self].pack "N"; end
50
+ def to_l16; [self].pack "v"; end
51
+ def to_b16; [self].pack "n"; end
52
+ def to_u8; [self].pack "C"; end
53
+ def ffs
54
+ i = 0
55
+ v = self
56
+ while((v >>= 1) != 0)
57
+ i += 1
58
+ end
59
+ return i
60
+ end
61
+ end
62
+ include IntegerExtensions
63
+ end
64
+
65
+ class Module
66
+ def flag_dump(i)
67
+ @bit_map ||= constants.map do |k|
68
+ [k, const_get(k.intern).ffs]
69
+ end.sort {|x, y| x[1] <=> y[1]}
70
+
71
+ last = 0
72
+ r = ""
73
+ @bit_map.each do |tup|
74
+ if((v = (tup[1] - last)) > 1)
75
+ r << ("." * (v-1))
76
+ end
77
+
78
+ if((i & (1 << tup[1])) != 0)
79
+ r << tup[0][0].chr
80
+ else
81
+ r << tup[0][0].chr.downcase
82
+ end
83
+ last = tup[1]
84
+ end
85
+ return r.reverse
86
+ end
87
+ end
@@ -0,0 +1,163 @@
1
+ module Ragweed::Wrap32
2
+ module DebugCodes
3
+ CREATE_PROCESS = 3
4
+ CREATE_THREAD = 2
5
+ EXCEPTION = 1
6
+ EXIT_PROCESS = 5
7
+ EXIT_THREAD = 4
8
+ LOAD_DLL = 6
9
+ OUTPUT_DEBUG_STRING = 8
10
+ RIP = 9
11
+ UNLOAD_DLL = 7
12
+ end
13
+
14
+ module ExceptionCodes
15
+ ACCESS_VIOLATION = 0xC0000005
16
+ BREAKPOINT = 0x80000003
17
+ ALIGNMENT = 0x80000002
18
+ SINGLE_STEP = 0x80000004
19
+ BOUNDS = 0xC0000008
20
+ DIVIDE_BY_ZERO = 0xC0000094
21
+ INT_OVERFLOW = 0xC0000095
22
+ INVALID_HANDLE = 0xC0000008
23
+ PRIV_INSTRUCTION = 0xC0000096
24
+ STACK_OVERFLOW = 0xC00000FD
25
+ INVALID_DISPOSITION = 0xC0000026
26
+ end
27
+
28
+ module ContinueCodes
29
+ CONTINUE = 0x10002
30
+ BREAK = 0x40010008
31
+ CONTROL_C = 0x40010005
32
+ UNHANDLED = 0x80010001
33
+ TERMINATE_THREAD = 0x40010003
34
+ TERMINATE_PROCESS = 0x40010004
35
+ end
36
+ end
37
+
38
+ class Ragweed::Wrap32::DebugEvent
39
+ (FIELDS = [ :code,
40
+ :pid,
41
+ :tid,
42
+ :file_handle,
43
+ :process_handle,
44
+ :thread_handle,
45
+ :base,
46
+ :offset,
47
+ :info_size,
48
+ :thread_base,
49
+ :start_address,
50
+ :image_name,
51
+ :unicode,
52
+ :exception_code,
53
+ :exception_flags,
54
+ :exception_record,
55
+ :exception_address,
56
+ :parameters,
57
+ :exit_code,
58
+ :dll_base,
59
+ :rip_error,
60
+ :rip_type]).each {|x| attr_accessor x}
61
+
62
+ def initialize(str)
63
+ @code, @pid, @tid = str.unpack("LLL")
64
+ str.shift 12
65
+ case @code
66
+ when Wrap32::DebugCodes::CREATE_PROCESS
67
+ @file_handle, @process_handle, @thread_handle,
68
+ @base, @offset,
69
+ @info_size, @thread_base, @start_address,
70
+ @image_name, @unicode = str.unpack("LLLLLLLLLH")
71
+ when Wrap32::DebugCodes::CREATE_THREAD
72
+ @thread_handle, @thread_base, @start_address = str.unpack("LLL")
73
+ when Wrap32::DebugCodes::EXCEPTION
74
+ @exception_code, @exception_flags,
75
+ @exception_record, @exception_address, @parameter_count = str.unpack("LLLLL")
76
+ str = str[20..-1]
77
+ @parameters = []
78
+ @parameter_count.times do
79
+ begin
80
+ @parameters << (str.unpack("L").first)
81
+ str = str[4..-1]
82
+ rescue;end
83
+ end
84
+ when Wrap32::DebugCodes::EXIT_PROCESS
85
+ @exit_code = str.unpack("L").first
86
+ when Wrap32::DebugCodes::EXIT_THREAD
87
+ @exit_code = str.unpack("L").first
88
+ when Wrap32::DebugCodes::LOAD_DLL
89
+ @file_handle, @dll_base, @offset,
90
+ @info_size, @image_name, @unicode = str.unpack("LLLLLH")
91
+ when Wrap32::DebugCodes::OUTPUT_DEBUG_STRING
92
+ when Wrap32::DebugCodes::RIP
93
+ @rip_error, @rip_type = str.unpack("LL")
94
+ when Wrap32::DebugCodes::UNLOAD_DLL
95
+ @dll_base = str.unpack("L").first
96
+ else
97
+ raise WinX.new(:wait_for_debug_event)
98
+ end
99
+ end
100
+
101
+ def inspect_code(c)
102
+ Wrap32::DebugCodes.to_key_hash[c].to_s || c.to_i
103
+ end
104
+
105
+ def inspect_exception_code(c)
106
+ Wrap32::ExceptionCodes.to_key_hash[c].to_s || c.to_i.to_s(16)
107
+ end
108
+
109
+ def inspect_parameters(p)
110
+ "[ " + p.map {|x| x.to_i.to_s}.join(", ") + " ]"
111
+ end
112
+
113
+ def inspect
114
+ body = lambda do
115
+ FIELDS.map do |f|
116
+ if (v = send(f))
117
+ f.to_s + "=" + (try("inspect_#{f.to_s}".intern, v) || v.to_i.to_s(16))
118
+ end
119
+ end.compact.join(" ")
120
+ end
121
+
122
+ "#<DebugEvent #{body.call}>"
123
+ end
124
+ end
125
+
126
+ module Ragweed::Wrap32
127
+ class << self
128
+ def wait_for_debug_event(ms=1000)
129
+ buf = "\x00" * 1024
130
+ r = CALLS["kernel32!WaitForDebugEvent:PL=L"].call(buf, ms)
131
+ raise WinX.new(:wait_for_debug_event) if r == 0 and get_last_error != 121
132
+ return Wrap32::DebugEvent.new(buf) if r != 0
133
+ return nil
134
+ end
135
+
136
+ def continue_debug_event(pid, tid, code)
137
+ r = CALLS["kernel32!ContinueDebugEvent:LLL=L"].call(pid, tid, code)
138
+ raise WinX.new(:continue_debug_event) if r == 0
139
+ return r
140
+ end
141
+
142
+ def debug_active_process(pid)
143
+ r = CALLS["kernel32!DebugActiveProcess:L=L"].call(pid)
144
+ raise WinX.new(:debug_active_process) if r == 0
145
+ return r
146
+ end
147
+
148
+ def debug_set_process_kill_on_exit(val=0)
149
+ r = CALLS["kernel32!DebugSetProcessKillOnExit:L=L"].call(val)
150
+ raise WinX.new(:debug_set_process_kill_on_exit) if r == 0
151
+ return r
152
+ end
153
+
154
+ def debug_active_process_stop(pid)
155
+ # don't care about failure
156
+ CALLS["kernel32!DebugActiveProcessStop:L=L"].call(pid)
157
+ end
158
+
159
+ def flush_instruction_cache(h, v1=0, v2=0)
160
+ CALLS["kernel32!FlushInstructionCache:LLL=L"].call(h, v1, v2)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,49 @@
1
+ module Ragweed
2
+ class Device
3
+ def initialize(path, options={})
4
+ @path = path
5
+ @options = options
6
+ @h = Wrap32::create_file(@path, :flags => Wrap32::FileAttributes::OVERLAPPED|Wrap32::FileAttributes::NORMAL)
7
+ end
8
+
9
+ def ioctl(code, inbuf, outbuf)
10
+ overlap(lambda do |o|
11
+ Wrap32::device_io_control(@h, code, inbuf, outbuf, o)
12
+ end) do |ret, count|
13
+ outbuf[0..count]
14
+ end
15
+ end
16
+
17
+ def read(sz)
18
+ overlap(lambda do |o|
19
+ Wrap32::read_file(@h, sz, o)
20
+ end) do |ret, count|
21
+ ret[0..count]
22
+ end
23
+ end
24
+
25
+ def write(buf)
26
+ overlap(lambda do |o|
27
+ Wrap32::write_file(@h, buf, o)
28
+ end) do |ret, count|
29
+ count
30
+ end
31
+ end
32
+
33
+ def release
34
+ Wrap32::close_handle(@h)
35
+ @h = nil
36
+ end
37
+
38
+ private
39
+
40
+ def overlap(proc)
41
+ o = Wrap32::Overlapped.get
42
+ ret = proc.call(o)
43
+ count = o.wait(@h)
44
+ r = yield ret, count
45
+ o.release
46
+ ret = r if r
47
+ end
48
+ end
49
+ end