iZsh-ragweed 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +29 -0
- data/README.rdoc +35 -0
- data/README.txt +9 -0
- data/Rakefile +30 -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.rb +84 -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 +345 -0
- data/lib/ragweed/detour.rb +223 -0
- data/lib/ragweed/ptr.rb +48 -0
- data/lib/ragweed/rasm.rb +53 -0
- data/lib/ragweed/rasm/isa.rb +1046 -0
- data/lib/ragweed/rasm/util.rb +26 -0
- data/lib/ragweed/sbuf.rb +197 -0
- data/lib/ragweed/trampoline.rb +103 -0
- data/lib/ragweed/utils.rb +88 -0
- data/lib/ragweed/wrap32.rb +53 -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/wraposx.rb +53 -0
- data/lib/ragweed/wraposx/constants.rb +112 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +250 -0
- data/lib/ragweed/wraposx/thread_context.rb +203 -0
- data/lib/ragweed/wraposx/thread_info.rb +225 -0
- data/lib/ragweed/wraposx/wraposx.rb +376 -0
- data/lib/ragweed/wraptux.rb +53 -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/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/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,88 @@
|
|
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
|
+
## This is from Topher Cyll's Stupd IRB tricks
|
21
|
+
def mymethods
|
22
|
+
(self.methods - self.class.superclass.methods).sort
|
23
|
+
end
|
24
|
+
end
|
25
|
+
include ObjectExtensions
|
26
|
+
end
|
27
|
+
|
28
|
+
class String
|
29
|
+
def to_l32; unpack("L").first; end
|
30
|
+
def to_b32; unpack("N").first; end
|
31
|
+
def to_l16; unpack("v").first; end
|
32
|
+
def to_b16; unpack("n").first; end
|
33
|
+
def to_u8; self[0]; end
|
34
|
+
def shift_l32; shift(4).to_l32; end
|
35
|
+
def shift_b32; shift(4).to_b32; end
|
36
|
+
def shift_l16; shift(2).to_l16; end
|
37
|
+
def shift_b16; shift(2).to_b16; end
|
38
|
+
def shift_u8; shift(1).to_u8; end
|
39
|
+
|
40
|
+
def shift(count=1)
|
41
|
+
return self if count == 0
|
42
|
+
slice! 0..(count-1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Integer
|
47
|
+
module IntegerExtensions
|
48
|
+
# Convert integers to binary strings
|
49
|
+
def to_l32; [self].pack "L"; end
|
50
|
+
def to_b32; [self].pack "N"; end
|
51
|
+
def to_l16; [self].pack "v"; end
|
52
|
+
def to_b16; [self].pack "n"; end
|
53
|
+
def to_u8; [self].pack "C"; end
|
54
|
+
def ffs
|
55
|
+
i = 0
|
56
|
+
v = self
|
57
|
+
while((v >>= 1) != 0)
|
58
|
+
i += 1
|
59
|
+
end
|
60
|
+
return i
|
61
|
+
end
|
62
|
+
end
|
63
|
+
include IntegerExtensions
|
64
|
+
end
|
65
|
+
|
66
|
+
class Module
|
67
|
+
def flag_dump(i)
|
68
|
+
@bit_map ||= constants.map do |k|
|
69
|
+
[k, const_get(k.intern).ffs]
|
70
|
+
end.sort {|x, y| x[1] <=> y[1]}
|
71
|
+
|
72
|
+
last = 0
|
73
|
+
r = ""
|
74
|
+
@bit_map.each do |tup|
|
75
|
+
if((v = (tup[1] - last)) > 1)
|
76
|
+
r << ("." * (v-1))
|
77
|
+
end
|
78
|
+
|
79
|
+
if((i & (1 << tup[1])) != 0)
|
80
|
+
r << tup[0][0].chr
|
81
|
+
else
|
82
|
+
r << tup[0][0].chr.downcase
|
83
|
+
end
|
84
|
+
last = tup[1]
|
85
|
+
end
|
86
|
+
return r.reverse
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Dir[File.expand_path("#{File.dirname(__FILE__)}/wrap32/*.rb")].each do |file|
|
2
|
+
# require file
|
3
|
+
# end
|
4
|
+
module Ragweed; end
|
5
|
+
module Ragweed::Wrap32
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
VERSION = '0.1.6'
|
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::Wrap32
|
50
|
+
|
51
|
+
Ragweed::Wrap32.require_all_libs_relative_to(__FILE__)
|
52
|
+
|
53
|
+
# EOF
|