ragweed 0.1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +34 -0
  5. data/examples/hittracertux.rb +49 -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 +427 -0
  14. data/lib/ragweed/debuggertux.rb +346 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/bblock.rb +73 -0
  18. data/lib/ragweed/rasm/isa.rb +1115 -0
  19. data/lib/ragweed/rasm.rb +59 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +156 -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 +59 -0
  34. data/lib/ragweed/wraposx/constants.rb +122 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +254 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +227 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +433 -0
  40. data/lib/ragweed/wraposx.rb +59 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +7 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +59 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/ragweed.gemspec +34 -0
  47. data/spec/ragweed_spec.rb +7 -0
  48. data/spec/spec_helper.rb +16 -0
  49. data/tasks/ann.rake +80 -0
  50. data/tasks/bones.rake +20 -0
  51. data/tasks/gem.rake +201 -0
  52. data/tasks/git.rake +40 -0
  53. data/tasks/notes.rake +27 -0
  54. data/tasks/post_load.rake +34 -0
  55. data/tasks/rdoc.rake +51 -0
  56. data/tasks/rubyforge.rake +55 -0
  57. data/tasks/setup.rb +292 -0
  58. data/tasks/spec.rake +54 -0
  59. data/tasks/svn.rake +47 -0
  60. data/tasks/test.rake +40 -0
  61. data/tasks/zentest.rake +36 -0
  62. data/test/test_ragweed.rb +0 -0
  63. metadata +132 -0
@@ -0,0 +1,59 @@
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.7.2'
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 function to load utility classes and extensions
36
+ def self.require_utils
37
+ %w{utils sbuf}.each{|r| require self.libpath(r)+'.rb'}
38
+ end
39
+
40
+ # Utility method used to require all files ending in .rb that lie in the
41
+ # directory below this file that has the same name as the filename passed
42
+ # in. Optionally, a specific _directory_ name can be passed in such that
43
+ # the _filename_ does not have to be equivalent to the directory.
44
+ #
45
+ def self.require_all_libs_relative_to( fname, dir = nil )
46
+ self.require_utils
47
+ dir ||= ::File.basename(fname, '.*')
48
+ search_me = ::File.expand_path(
49
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
50
+
51
+ Dir.glob(search_me).sort.each {|rb| require rb}
52
+ # require File.dirname(File.basename(__FILE__)) + "/#{x}"
53
+
54
+ end
55
+ end # module Ragweed::Rasm
56
+
57
+ Ragweed::Rasm.require_all_libs_relative_to(__FILE__)
58
+
59
+ # 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=Ragweed::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 = Ragweed::Wrap32::create_remote_thread(@p.handle, base, argm)
61
+ Ragweed::Wrap32::wait_for_single_object(th) if @wait
62
+ Ragweed::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 = [Ragweed::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
+ Ragweed::Wrap32::create_remote_thread(@p.handle, base, data)
100
+ @ev1.wait
101
+ @ev2
102
+ end
103
+ end
@@ -0,0 +1,156 @@
1
+ class Array
2
+ module ArrayExtensions
3
+ # Convert to hash
4
+ ##
5
+ def to_hash
6
+ # too clever.
7
+ # Hash[*self.flatten]
8
+
9
+ h = Hash.new
10
+ each do |k,v|
11
+ h[k] = v
12
+ end
13
+ h
14
+ end
15
+ end
16
+ include ArrayExtensions
17
+ end
18
+
19
+ # These should probably be extensions to Module since that's the location of instance_eval and friends.
20
+ class Object
21
+ module ObjectExtensions
22
+ # Every object has a "singleton" class, which you can think
23
+ # of as the class (ie, 1.metaclass =~ Fixnum) --- but that you
24
+ # can modify and extend without fucking up the actual class.
25
+ def metaclass; class << self; self; end; end
26
+ def meta_eval(&blk) metaclass.instance_eval &blk; end
27
+ def meta_def(name, &blk) meta_eval { define_method name, &blk }; end
28
+ def try(meth, *args); send(meth, *args) if respond_to? meth; end
29
+
30
+ def through(meth, *args)
31
+ if respond_to? meth
32
+ send(meth, *args)
33
+ else
34
+ self
35
+ end
36
+ end
37
+
38
+ ## This is from Topher Cyll's Stupd IRB tricks
39
+ def mymethods
40
+ (self.methods - self.class.superclass.methods).sort
41
+ end
42
+ # self-evident
43
+ def callable?; respond_to? :call; end
44
+ def number?; kind_of? Numeric; end
45
+
46
+ # while X remains callable, keep calling it to get its value
47
+ def derive
48
+ # also, don't drink and derive
49
+ x = self
50
+ while x.callable?
51
+ x = x()
52
+ end
53
+ return x
54
+ end
55
+ end
56
+ include ObjectExtensions
57
+ end
58
+
59
+ class String
60
+ def to_l32; unpack("L").first; end
61
+ def to_b32; unpack("N").first; end
62
+ def to_l16; unpack("v").first; end
63
+ def to_b16; unpack("n").first; end
64
+ def to_u8; self[0]; end
65
+ def shift_l32; shift(4).to_l32; end
66
+ def shift_b32; shift(4).to_b32; end
67
+ def shift_l16; shift(2).to_l16; end
68
+ def shift_b16; shift(2).to_b16; end
69
+ def shift_u8; shift(1).to_u8; end
70
+
71
+ def shift(count=1)
72
+ return self if count == 0
73
+ slice! 0..(count-1)
74
+ end
75
+
76
+ # Sometimes string buffers passed through Win32 interfaces come with
77
+ # garbage after the trailing NUL; this method gets rid of that, like
78
+ # String#trim
79
+ def asciiz
80
+ begin
81
+ self[0..self.index("\x00")-1]
82
+ rescue
83
+ self
84
+ end
85
+ end
86
+
87
+ def asciiz!; replace asciiz; end
88
+
89
+ # Convert a string into hex characters
90
+ def hexify
91
+ self.unpack("H*").first
92
+ end
93
+
94
+ # Convert a string of raw hex characters (no %'s or anything) into binary
95
+ def dehexify
96
+ [self].pack("H*")
97
+ end
98
+ end
99
+
100
+ class Integer
101
+ module IntegerExtensions
102
+ # Convert integers to binary strings
103
+ def to_l32; [self].pack "L"; end
104
+ def to_b32; [self].pack "N"; end
105
+ def to_l16; [self].pack "v"; end
106
+ def to_b16; [self].pack "n"; end
107
+ def to_u8; [self].pack "C"; end
108
+
109
+ # sign extend
110
+ def sx8; ([self].pack "c").unpack("C").first; end
111
+ def sx16; ([self].pack "s").unpack("S").first; end
112
+ def sx32; ([self].pack "l").unpack("L").first; end
113
+
114
+ def ffs
115
+ i = 0
116
+ v = self
117
+ while((v >>= 1) != 0)
118
+ i += 1
119
+ end
120
+ return i
121
+ end
122
+ end
123
+ include IntegerExtensions
124
+ end
125
+
126
+ class Module
127
+ def to_name_hash
128
+ @name_hash ||= constants.map {|k| [k.intern, const_get(k.intern)]}.to_hash
129
+ end
130
+
131
+ def to_key_hash
132
+ @key_hash ||= constants.map {|k| [const_get(k.intern), k.intern]}.to_hash
133
+ end
134
+
135
+ def flag_dump(i)
136
+ @bit_map ||= constants.map do |k|
137
+ [k, const_get(k.intern).ffs]
138
+ end.sort {|x, y| x[1] <=> y[1]}
139
+
140
+ last = 0
141
+ r = ""
142
+ @bit_map.each do |tup|
143
+ if((v = (tup[1] - last)) > 1)
144
+ r << ("." * (v-1))
145
+ end
146
+
147
+ if((i & (1 << tup[1])) != 0)
148
+ r << tup[0][0].chr
149
+ else
150
+ r << tup[0][0].chr.downcase
151
+ end
152
+ last = tup[1]
153
+ end
154
+ return r.reverse
155
+ end
156
+ 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 Ragweed::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 Ragweed::Wrap32::DebugCodes::CREATE_THREAD
72
+ @thread_handle, @thread_base, @start_address = str.unpack("LLL")
73
+ when Ragweed::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 Ragweed::Wrap32::DebugCodes::EXIT_PROCESS
85
+ @exit_code = str.unpack("L").first
86
+ when Ragweed::Wrap32::DebugCodes::EXIT_THREAD
87
+ @exit_code = str.unpack("L").first
88
+ when Ragweed::Wrap32::DebugCodes::LOAD_DLL
89
+ @file_handle, @dll_base, @offset,
90
+ @info_size, @image_name, @unicode = str.unpack("LLLLLH")
91
+ when Ragweed::Wrap32::DebugCodes::OUTPUT_DEBUG_STRING
92
+ when Ragweed::Wrap32::DebugCodes::RIP
93
+ @rip_error, @rip_type = str.unpack("LL")
94
+ when Ragweed::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
+ Ragweed::Wrap32::DebugCodes.to_key_hash[c].to_s || c.to_i
103
+ end
104
+
105
+ def inspect_exception_code(c)
106
+ Ragweed::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 Ragweed::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