ragweed 0.1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +32 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +34 -0
- data/examples/hittracertux.rb +49 -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 +427 -0
- data/lib/ragweed/debuggertux.rb +346 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm/bblock.rb +73 -0
- data/lib/ragweed/rasm/isa.rb +1115 -0
- data/lib/ragweed/rasm.rb +59 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +156 -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 +59 -0
- data/lib/ragweed/wraposx/constants.rb +122 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +254 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +227 -0
- data/lib/ragweed/wraposx/wraposx.rb +433 -0
- data/lib/ragweed/wraposx.rb +59 -0
- data/lib/ragweed/wraptux/constants.rb +68 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +76 -0
- data/lib/ragweed/wraptux.rb +59 -0
- data/lib/ragweed.rb +84 -0
- data/ragweed.gemspec +34 -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 +132 -0
data/lib/ragweed/rasm.rb
ADDED
@@ -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
|
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=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
|