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.
- data/History.txt +15 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +30 -0
- data/examples/hittracertux.rb +48 -0
- data/examples/hittracerx.rb +63 -0
- data/examples/hook_notepad.rb +9 -0
- data/examples/snicker.rb +183 -0
- data/examples/tux-example.rb +23 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +338 -0
- data/lib/ragweed/debuggerosx.rb +419 -0
- data/lib/ragweed/debuggertux.rb +347 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm/isa.rb +1046 -0
- data/lib/ragweed/rasm/util.rb +26 -0
- data/lib/ragweed/rasm.rb +53 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +87 -0
- data/lib/ragweed/wrap32/debugging.rb +163 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +23 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +506 -0
- data/lib/ragweed/wrap32/process_token.rb +59 -0
- data/lib/ragweed/wrap32/thread_context.rb +208 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +526 -0
- data/lib/ragweed/wrap32.rb +53 -0
- data/lib/ragweed/wraposx/constants.rb +101 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +244 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +213 -0
- data/lib/ragweed/wraposx/wraposx.rb +376 -0
- data/lib/ragweed/wraposx.rb +53 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +3 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +53 -0
- data/lib/ragweed.rb +84 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_ragweed.rb +0 -0
- 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
|
data/lib/ragweed/rasm.rb
ADDED
@@ -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
|
data/lib/ragweed/sbuf.rb
ADDED
@@ -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
|