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,526 @@
|
|
1
|
+
%w[ostruct Win32API pp].each {|x| require x}
|
2
|
+
|
3
|
+
module Ragweed;end
|
4
|
+
module Ragweed::Wrap32
|
5
|
+
NULL = 0x0
|
6
|
+
|
7
|
+
module PagePerms
|
8
|
+
EXECUTE = 0x10
|
9
|
+
EXECUTE_READ = 0x20
|
10
|
+
EXECUTE_READWRITE = 0x40
|
11
|
+
EXECUTE_WRITECOPY = 0x80
|
12
|
+
NOACCESS = 0x1
|
13
|
+
READONLY = 0x2
|
14
|
+
READWRITE = 0x4
|
15
|
+
WRITECOPY = 0x8
|
16
|
+
|
17
|
+
WRITEABLE = [EXECUTE_READWRITE,
|
18
|
+
EXECUTE_WRITECOPY,
|
19
|
+
READWRITE,
|
20
|
+
WRITECOPY]
|
21
|
+
end
|
22
|
+
|
23
|
+
module FileSharing
|
24
|
+
NONE = 0
|
25
|
+
DELETE = 4
|
26
|
+
READ = 1
|
27
|
+
WRITE = 2
|
28
|
+
end
|
29
|
+
|
30
|
+
module FileDisposition
|
31
|
+
CREATE_ALWAYS = 2
|
32
|
+
CREATE_NEW = 1
|
33
|
+
OPEN_ALWAYS = 4
|
34
|
+
OPEN_EXISTING = 3
|
35
|
+
TRUNCATE_EXISTING = 5
|
36
|
+
end
|
37
|
+
|
38
|
+
module FileAttributes
|
39
|
+
ARCHIVE = 0x20
|
40
|
+
ENCRYPTED = 0x4000
|
41
|
+
HIDDEN = 0x2
|
42
|
+
NORMAL = 0x80
|
43
|
+
OFFLINE = 0x1000
|
44
|
+
READONLY = 1
|
45
|
+
SYSTEM = 4
|
46
|
+
TEMPORARY = 0x100
|
47
|
+
BACKUP = 0x02000000
|
48
|
+
DELETE_ON_CLOSE = 0x04000000
|
49
|
+
NO_BUFFERING = 0x20000000
|
50
|
+
NO_RECALL = 0x00100000
|
51
|
+
REPARSE_POINT = 0x00200000
|
52
|
+
OVERLAPPED = 0x40000000
|
53
|
+
POSIX = 0x0100000
|
54
|
+
RANDOM_ACCESS = 0x10000000
|
55
|
+
SEQUENTIAL = 0x08000000
|
56
|
+
WRITE_THROUGH = 0x80000000
|
57
|
+
end
|
58
|
+
|
59
|
+
module FileAccess
|
60
|
+
GENERIC_READ = 0x80000000
|
61
|
+
GENERIC_WRITE = 0x40000000
|
62
|
+
GENERIC_EXECUTE = 0x20000000
|
63
|
+
GENERIC_ALL = 0x10000000
|
64
|
+
end
|
65
|
+
|
66
|
+
module FormatArgs
|
67
|
+
FROM_SYSTEM = 4096
|
68
|
+
ALLOCATE_BUFFER = 256
|
69
|
+
end
|
70
|
+
|
71
|
+
# Does 2 things:
|
72
|
+
# 1. Parses a terse notation for Win32 functions: "module!function:args=return",
|
73
|
+
# where "args" and "return" are in String#unpack notation.
|
74
|
+
#
|
75
|
+
# 2. Memoizes the Win32API lookup.
|
76
|
+
#
|
77
|
+
# Returns a callable object implementing the specified call.
|
78
|
+
|
79
|
+
CALLS = Hash.new do |h, str|
|
80
|
+
lib = proc = args = ret = nil
|
81
|
+
lib, rest = str.split "!"
|
82
|
+
proc, rest = rest.split ":"
|
83
|
+
args, ret = rest.split("=") if rest
|
84
|
+
ret ||= ""
|
85
|
+
args ||= []
|
86
|
+
raise "need proc" if not proc
|
87
|
+
h[str] = Win32API.new(lib, proc, args, ret)
|
88
|
+
end
|
89
|
+
|
90
|
+
# --------------------------------------------------------------------------------------
|
91
|
+
|
92
|
+
class << self
|
93
|
+
|
94
|
+
# Get a process handle given a pid
|
95
|
+
def open_process(pid)
|
96
|
+
r = CALLS["kernel32!OpenProcess:LLL=L"].call(0x1F0FFF, 0, pid)
|
97
|
+
raise WinX.new(:open_process) if r == 0
|
98
|
+
return r
|
99
|
+
end
|
100
|
+
|
101
|
+
# Get a thread handle given a tid; if a block is provided, the semantics are
|
102
|
+
# as File#open with a block.
|
103
|
+
def open_thread(tid, &block)
|
104
|
+
h = CALLS["kernel32!OpenThread:LLL=L"].call(0x1F03FF, 0, tid)
|
105
|
+
raise WinX.new(:open_thread) if h == 0
|
106
|
+
if block_given?
|
107
|
+
ret = yield h
|
108
|
+
close_handle(h)
|
109
|
+
return ret
|
110
|
+
end
|
111
|
+
h
|
112
|
+
end
|
113
|
+
|
114
|
+
# Close any Win32 handle. Reminder: Win32 handles are just integers, like file
|
115
|
+
# descriptors in Posix.
|
116
|
+
def close_handle(h)
|
117
|
+
raise WinX.new(:close_handle) if CALLS["kernel32!CloseHandle:L"].call(h) != 0
|
118
|
+
end
|
119
|
+
|
120
|
+
# Get the last error code (errno) (can't fail)
|
121
|
+
def get_last_error
|
122
|
+
CALLS["kernel32!GetLastError:=L"].call
|
123
|
+
end
|
124
|
+
|
125
|
+
# strerror(errno) (can't fail)
|
126
|
+
def format_message(code=nil)
|
127
|
+
code ||= get_last_error
|
128
|
+
buf = "\x00" * 4096
|
129
|
+
CALLS["kernel32!FormatMessageA:LPLLPLP"].
|
130
|
+
call(4096, NULL, code, 0x00000400, buf, 4096, NULL)
|
131
|
+
return buf.split("\x00")[0]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Allocate memory in a remote process (or yourself, with handle -1)
|
135
|
+
def virtual_alloc_ex(h, sz, addr=NULL, prot=0x40)
|
136
|
+
r = CALLS["kernel32!VirtualAllocEx:LLLLL=L"].
|
137
|
+
call(h, addr, sz, 0x1000, prot)
|
138
|
+
raise WinX.new(:virtual_alloc_ex) if r == 0
|
139
|
+
return r
|
140
|
+
end
|
141
|
+
|
142
|
+
# Free memory in a remote process given the pointer returned from virtual_alloc_ex
|
143
|
+
def virtual_free_ex(h, ptr, type=0x8000)
|
144
|
+
r = CALLS["kernel32!VirtualFreeEx:LLLL=L"].call(h, ptr.to_i, 0, type)
|
145
|
+
raise WinX.new(:virtual_free_ex) if r == 0
|
146
|
+
return r
|
147
|
+
end
|
148
|
+
|
149
|
+
# Write a string into the memory of a remote process given its handle and an address
|
150
|
+
def write_process_memory(h, dst, val)
|
151
|
+
val = val.to_s if not val.kind_of? String
|
152
|
+
r = CALLS["kernel32!WriteProcessMemory:LLPLL=L"].call(h, dst.to_i, val, val.size, NULL)
|
153
|
+
raise WinX.new(:write_process_memory) if r == 0
|
154
|
+
return r
|
155
|
+
end
|
156
|
+
|
157
|
+
# Read from a remote process given an address and length, returning a string.
|
158
|
+
def read_process_memory(h, ptr, len)
|
159
|
+
val = "\x00" * len
|
160
|
+
r = CALLS["kernel32!ReadProcessMemory:LLPLL=L"].call(h, ptr.to_i, val, len, NULL)
|
161
|
+
raise WinX.new(:read_process_memory) if r == 0
|
162
|
+
return val ## don't handle short reads XXX
|
163
|
+
end
|
164
|
+
|
165
|
+
def str2memory_basic_info(mbi)
|
166
|
+
s = OpenStruct.new
|
167
|
+
s.BaseAddress,
|
168
|
+
s.AllocationBase,
|
169
|
+
s.AllocationProtect,
|
170
|
+
s.RegionSize,
|
171
|
+
s.State,
|
172
|
+
s.Protect,
|
173
|
+
s.Type = mbi.unpack("LLLLLLL")
|
174
|
+
return s
|
175
|
+
end
|
176
|
+
|
177
|
+
# Return a struct with the MEMORY_BASIC_INFORMATION for a given address in the
|
178
|
+
# memory of a remote process. Gives you addressable memory ranges and protection
|
179
|
+
# flags.
|
180
|
+
def virtual_query_ex(h, ptr)
|
181
|
+
mbi = [0,0,0,0,0,0,0].pack("LLLLLLL")
|
182
|
+
if CALLS["kernel32!VirtualQueryEx:LLPL=L"].call(h, ptr, mbi, mbi.size)
|
183
|
+
str2memory_basic_info(mbi)
|
184
|
+
else
|
185
|
+
nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Change the protection of specific memory regions in a remote process.
|
190
|
+
def virtual_protect_ex(h, addr, prot, size=0)
|
191
|
+
old = [0].pack("L")
|
192
|
+
base = virtual_query_ex(h, addr).BaseAddress if size == 0
|
193
|
+
base ||= addr
|
194
|
+
|
195
|
+
if CALLS["kernel32!VirtualProtectEx:LLLLP=L"].call(h, base, size, prot, old)
|
196
|
+
old.unpack("L").first
|
197
|
+
else
|
198
|
+
raise WinX.new(:virtual_protect_ex)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# getpid
|
203
|
+
def get_current_process_id
|
204
|
+
CALLS["kernel32!GetCurrentProcessId:=L"].call # can't realistically fail
|
205
|
+
end
|
206
|
+
|
207
|
+
# gettid
|
208
|
+
def get_current_thread_id
|
209
|
+
CALLS["kernel32!GetCurrentThreadId:=L"].call # can't realistically fail
|
210
|
+
end
|
211
|
+
|
212
|
+
# Given a DLL name, get a handle to the DLL.
|
213
|
+
def get_module_handle(name)
|
214
|
+
name = name.to_utf16
|
215
|
+
r = CALLS["kernel32!GetModuleHandleW:P=L"].call(name)
|
216
|
+
raise WinX.new(:get_module_handle) if r == 0
|
217
|
+
return r
|
218
|
+
end
|
219
|
+
|
220
|
+
# load a library explicitly from a dll
|
221
|
+
def load_library(name)
|
222
|
+
name = name.to_utf16
|
223
|
+
r = CALLS["kernel32!LoadLibraryW:P=L"].call(name)
|
224
|
+
raise WinX.new(:load_library) if r == 0
|
225
|
+
return r
|
226
|
+
end
|
227
|
+
|
228
|
+
# Using notation x = "foo!bar" or x = handle, y = meth, look up a function's
|
229
|
+
# address in a module. Note that this is local, not remote.
|
230
|
+
def get_proc_address(x, y=nil)
|
231
|
+
if not y
|
232
|
+
mod, meth = x.split "!"
|
233
|
+
h = get_module_handle(mod)
|
234
|
+
else
|
235
|
+
h = x
|
236
|
+
meth = y
|
237
|
+
end
|
238
|
+
|
239
|
+
r = CALLS["kernel32!GetProcAddress:LP=L"].call(h, meth)
|
240
|
+
return r # pass error through
|
241
|
+
end
|
242
|
+
|
243
|
+
# Select(2), for a single object handle.
|
244
|
+
def wait_for_single_object(h)
|
245
|
+
r = CALLS["kernel32!WaitForSingleObject:LL=L"].call(h, -1)
|
246
|
+
raise WinX.new(:wait_for_single_object) if r == -1
|
247
|
+
end
|
248
|
+
|
249
|
+
def str2process_info(str)
|
250
|
+
ret = OpenStruct.new
|
251
|
+
ret.dwSize,
|
252
|
+
ret.cntUsage,
|
253
|
+
ret.th32ProcessID,
|
254
|
+
ret.th32DefaultHeapID,
|
255
|
+
ret.th32ModuleID,
|
256
|
+
ret.cntThreads,
|
257
|
+
ret.th32ParentProcessID,
|
258
|
+
ret.pcPriClassBase,
|
259
|
+
ret.dwFlags,
|
260
|
+
ret.szExeFile = str.unpack("LLLLLLLLLA2048")
|
261
|
+
ret.szExeFile = ret.szExeFile.asciiz
|
262
|
+
return ret
|
263
|
+
end
|
264
|
+
|
265
|
+
# Use Toolhelp32 to enumerate all running processes on the box, returning
|
266
|
+
# a struct with PIDs and executable names.
|
267
|
+
def all_processes
|
268
|
+
h = CALLS["kernel32!CreateToolhelp32Snapshot:LL=L"].call(0x2, 0)
|
269
|
+
if h != -1
|
270
|
+
pi = [(9*4)+2048,0,0,0,0,0,0,0,0,"\x00"*2048].pack("LLLLLLLLLa2048")
|
271
|
+
if CALLS["kernel32!Process32First:LP=L"].call(h, pi) != 0
|
272
|
+
yield str2process_info(pi)
|
273
|
+
while CALLS["kernel32!Process32Next:LP=L"].call(h, pi) != 0
|
274
|
+
yield str2process_info(pi)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
else
|
278
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def str2module_info(str)
|
283
|
+
ret = OpenStruct.new
|
284
|
+
ret.dwSize,
|
285
|
+
ret.th32ModuleID,
|
286
|
+
ret.th32ProcessID,
|
287
|
+
ret.GlblcntUsage,
|
288
|
+
ret.ProccntUsage,
|
289
|
+
ret.modBaseAddr,
|
290
|
+
ret.modBaseSize,
|
291
|
+
ret.hModule,
|
292
|
+
ret.szModule,
|
293
|
+
ret.szExePath = str.unpack("LLLLLLLLA256A260")
|
294
|
+
ret.szModule = ret.szModule.asciiz
|
295
|
+
ret.szExePath = ret.szExePath.asciiz
|
296
|
+
return ret
|
297
|
+
end
|
298
|
+
|
299
|
+
# Given a pid, enumerate the modules loaded into the process, returning base
|
300
|
+
# addresses, memory ranges, and the module name.
|
301
|
+
def list_modules(pid=0)
|
302
|
+
h = CALLS["kernel32!CreateToolhelp32Snapshot:LL=L"].call(0x8, pid)
|
303
|
+
if h != -1
|
304
|
+
mi = [260+256+(8*4),0,0,0,0,0,0,0,"\x00"*256,"\x00"*260].pack("LLLLLLLLa256a260")
|
305
|
+
if w32("kernel32!Module32First:LP=L").call(h, mi) != 0
|
306
|
+
yield str2module_info(mi)
|
307
|
+
while w32("kernel32!Module32Next:LP=L").call(h, mi) != 0
|
308
|
+
yield str2module_info(mi)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
else
|
312
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Use virtual_query_ex to tell whether an address is writable.
|
317
|
+
def writeable?(h, off)
|
318
|
+
if (x = virtual_query_ex(h, off))
|
319
|
+
return PagePerms::WRITEABLE.member?(x.Protect & 0xFF)
|
320
|
+
else
|
321
|
+
return false
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# NQIP does a lot of things, the most useful of which are getting the
|
326
|
+
# image name of a running process, and telling whether a debugger is loaded. This
|
327
|
+
# interface is Ioctl-style; provide an ordinal and a buffer to pass results through.
|
328
|
+
def nt_query_information_process(h, ord, buf)
|
329
|
+
lenp = [0].pack("L")
|
330
|
+
if CALLS["ntdll!NtQueryInformationProcess:LLPLP=L"].call(h, ord, buf, buf.size, lenp) == 0
|
331
|
+
len = lenp.unpack("L").first
|
332
|
+
return buf[0..(len-1)]
|
333
|
+
end
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
|
337
|
+
def str2thread_info(str)
|
338
|
+
ret = OpenStruct.new
|
339
|
+
ret.dwSize,
|
340
|
+
ret.cntUsage,
|
341
|
+
ret.th32ThreadID,
|
342
|
+
ret.th32OwnerProcessID,
|
343
|
+
ret.tpBasePri,
|
344
|
+
ret.tpDeltaPri,
|
345
|
+
ret.thFlags = str.unpack("LLLLLLL")
|
346
|
+
return ret
|
347
|
+
end
|
348
|
+
|
349
|
+
# List all the threads in a process given its pid, returning a struct containing
|
350
|
+
# tids and run state. This is relatively expensive, because it uses Toolhelp32.
|
351
|
+
def threads(pid)
|
352
|
+
h = CALLS["kernel32!CreateToolhelp32Snapshot:LL=L"].call(0x4, pid)
|
353
|
+
if h != -1
|
354
|
+
mi = [(7*4),0,0,0,0,0,0].pack("LLLLLLL")
|
355
|
+
if w32("kernel32!Thread32First:LP=L").call(h, mi) != 0
|
356
|
+
ti = str2thread_info(mi)
|
357
|
+
yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
|
358
|
+
while w32("kernel32!Thread32Next:LP=L").call(h, mi) != 0
|
359
|
+
ti = str2thread_info(mi)
|
360
|
+
yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
|
361
|
+
end
|
362
|
+
end
|
363
|
+
else
|
364
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
# Suspend a thread given its handle.
|
369
|
+
def suspend_thread(h)
|
370
|
+
r = CALLS["kernel32!SuspendThread:L=L"].call(h)
|
371
|
+
raise WinX.new(:suspend_thread) if r == 0
|
372
|
+
return r
|
373
|
+
end
|
374
|
+
|
375
|
+
# Resume a suspended thread, returning nonzero if the thread was suspended,
|
376
|
+
# and 0 if it was running.
|
377
|
+
def resume_thread(h)
|
378
|
+
CALLS["kernel32!ResumeThread:L=L"].call(h)
|
379
|
+
end
|
380
|
+
|
381
|
+
# Create a remote thread in the process, starting at the location
|
382
|
+
# "start", with the threadproc argument "arg"
|
383
|
+
def create_remote_thread(h, start, arg)
|
384
|
+
r = CALLS["kernel32!CreateRemoteThread:LLLLLLL=L"].call(h, NULL, 0, start.to_i, arg.to_i, 0, 0)
|
385
|
+
raise WinX.new(:create_remote_thread) if r == 0
|
386
|
+
return r
|
387
|
+
end
|
388
|
+
|
389
|
+
def sleep(ms=0)
|
390
|
+
CALLS["kernel32!Sleep:L=L"].call(ms)
|
391
|
+
end
|
392
|
+
|
393
|
+
# clone a handle out of another open process (or self, with -1)
|
394
|
+
def duplicate_handle(ph, h)
|
395
|
+
ret = "\x00\x00\x00\x00"
|
396
|
+
r = CALLS["kernel32!DuplicateHandle:LLLPLLL=L"].call(ph, h, -1, ret, 0, 0, 0x2)
|
397
|
+
raise WinX.new(:duplicate_handle) if r == 0
|
398
|
+
ret.to_l32
|
399
|
+
end
|
400
|
+
|
401
|
+
def create_file(name, opts={})
|
402
|
+
opts[:disposition] ||= FileDisposition::OPEN_ALWAYS
|
403
|
+
opts[:sharing] ||= FileSharing::READ | FileSharing::WRITE
|
404
|
+
opts[:access] ||= FileAccess::GENERIC_ALL
|
405
|
+
opts[:flags] ||= 0
|
406
|
+
|
407
|
+
r = CALLS["kernel32!CreateFile:PLLPLLP=L"].
|
408
|
+
call(name, opts[:access], opts[:sharing], NULL, opts[:disposition], opts[:flags], NULL)
|
409
|
+
raise WinX.new(:create_file) if r == -1
|
410
|
+
return r
|
411
|
+
end
|
412
|
+
|
413
|
+
# i haven't made this work, but named handles are kind of silly anyways
|
414
|
+
def open_event(name)
|
415
|
+
r = CALLS["kernel32!OpenEvent:LLP=L"].call(0, 0, name)
|
416
|
+
raise WinX.new(:open_event) if r == 0
|
417
|
+
return r
|
418
|
+
end
|
419
|
+
|
420
|
+
# signal an event
|
421
|
+
def set_event(h)
|
422
|
+
r = CALLS["kernel32!SetEvent:L=L"].call(h)
|
423
|
+
raise WinX.new(:set_event) if r == 0
|
424
|
+
return r
|
425
|
+
end
|
426
|
+
|
427
|
+
# force-unsignal event (waiting on the event handle also does this)
|
428
|
+
def reset_event(h)
|
429
|
+
r = CALLS["kernel32!ResetEvent:L=L"].call(h)
|
430
|
+
raise WinX.new(:reset_event) if r == 0
|
431
|
+
return r
|
432
|
+
end
|
433
|
+
|
434
|
+
# create an event, which you can signal and wait on across processes
|
435
|
+
def create_event(name=nil, auto=false, signalled=false)
|
436
|
+
auto = (1 if auto) || 0
|
437
|
+
signalled = (1 if signalled) || 0
|
438
|
+
name ||= 0
|
439
|
+
|
440
|
+
r = CALLS["kernel32!CreateEvent:LLLP=L"].call(0, auto, signalled, name);
|
441
|
+
raise WinX.new(:create_event) if r == 0
|
442
|
+
return r
|
443
|
+
end
|
444
|
+
|
445
|
+
def write_file(h, buf, overlapped=nil)
|
446
|
+
if overlapped
|
447
|
+
opp = overlapped.to_s
|
448
|
+
else
|
449
|
+
opp = NULL
|
450
|
+
end
|
451
|
+
|
452
|
+
outw = "\x00" * 4
|
453
|
+
r = CALLS["kernel32!WriteFile:LPLPP=L"].call(h, buf, buf.size, outw, opp)
|
454
|
+
raise WinX.new(:write_file) if r == 0 and get_last_error != 997
|
455
|
+
return buf, outw.unpack("L").first
|
456
|
+
end
|
457
|
+
|
458
|
+
def read_file(h, count, overlapped=nil)
|
459
|
+
if overlapped
|
460
|
+
opp = overlapped.to_s
|
461
|
+
else
|
462
|
+
opp = NULL
|
463
|
+
end
|
464
|
+
outw = "\x00" * 4
|
465
|
+
if not (buf = overlapped.try(:target)) or buf.size < count
|
466
|
+
buf = "\x00" * count
|
467
|
+
overlapped.target = buf if overlapped
|
468
|
+
end
|
469
|
+
|
470
|
+
r = CALLS["kernel32!ReadFile:LPLPP=L"].call(h, buf, count, outw, opp)
|
471
|
+
raise WinX.new(:read_file) if r == 0 and get_last_error != 997
|
472
|
+
return buf, outw.unpack("L").first
|
473
|
+
end
|
474
|
+
|
475
|
+
def device_io_control(h, code, inbuf, outbuf, overlapped=NULL)
|
476
|
+
overlapped = overlapped.to_s if overlapped
|
477
|
+
outw = "\x00" * 4
|
478
|
+
r = CALLS["kernel32!DeviceIoControl:LLPLPLPP=L"].
|
479
|
+
call(h, code, inbuf, inbuf.size, outbuf, outbuf.size, outw, overlapped)
|
480
|
+
raise WinX.new(:device_io_control) if r == 0 and get_last_error != 997
|
481
|
+
return outw.unpack("L").first
|
482
|
+
end
|
483
|
+
|
484
|
+
def get_overlapped_result(h, overlapped)
|
485
|
+
overlapped = overlapped.to_s
|
486
|
+
outw = "\x00" * 4
|
487
|
+
r = CALLS["kernel32!GetOverlappedResult:LPPL=L"].call(h, overlapped, outw, 0)
|
488
|
+
raise WinX.new(:get_overlapped_result) if r == 0
|
489
|
+
return outw.unpack("L").first
|
490
|
+
end
|
491
|
+
|
492
|
+
# just grab some local memory
|
493
|
+
def malloc(sz)
|
494
|
+
r = CALLS["msvcrt!malloc:L=L"].call(sz)
|
495
|
+
raise WinX.new(:malloc) if r == 0
|
496
|
+
return r
|
497
|
+
end
|
498
|
+
|
499
|
+
def memcpy(dst, src, size)
|
500
|
+
CALLS["msvcrt!memcpy:PPL=L"].call(dst, src, size)
|
501
|
+
end
|
502
|
+
|
503
|
+
# Block wrapper for thread suspension
|
504
|
+
def with_suspended_thread(tid)
|
505
|
+
open_thread(tid) do |h|
|
506
|
+
begin
|
507
|
+
suspend_thread(h)
|
508
|
+
ret = yield h
|
509
|
+
ensure
|
510
|
+
resume_thread(h)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
def wfmo(handles, ms=100)
|
516
|
+
hp = handles.to_ptr
|
517
|
+
r = CALLS["kernel32!WaitForMultipleObjects:LPLL=L"].call(handles.size, hp, 0, ms)
|
518
|
+
raise WinX(:wait_for_multiple_objects) if r == 0xFFFFFFFF
|
519
|
+
if r < handles.size
|
520
|
+
return handles[r]
|
521
|
+
else
|
522
|
+
return nil
|
523
|
+
end
|
524
|
+
end
|
525
|
+
end
|
526
|
+
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.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::Wrap32
|
50
|
+
|
51
|
+
Ragweed::Wrap32.require_all_libs_relative_to(__FILE__)
|
52
|
+
|
53
|
+
# EOF
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Ragweed; end
|
2
|
+
module Ragweed::Wraposx;end
|
3
|
+
module Ragweed::Wraposx::Ptrace
|
4
|
+
TRACE_ME = 0 # child declares it's being traced
|
5
|
+
#(READ|WRITE)_[IDU] are not valid in OSX but defined in ptrace.h
|
6
|
+
READ_I = 1 # read word in child's I space
|
7
|
+
READ_D = 2 # read word in child's D space
|
8
|
+
READ_U = 3 # read word in child's user structure
|
9
|
+
WRITE_I = 4 # write word in child's I space
|
10
|
+
WRITE_D = 5 # write word in child's D space
|
11
|
+
WRITE_U = 6 # write word in child's user structure
|
12
|
+
CONTINUE = 7 # continue the child
|
13
|
+
KILL = 8 # kill the child process
|
14
|
+
STEP = 9 # single step the child
|
15
|
+
ATTACH = 10 # trace some running process
|
16
|
+
DETACH = 11 # stop tracing a process
|
17
|
+
SIGEXC = 12 # signals as exceptions for current_proc
|
18
|
+
THUPDATE = 13 # signal for thread
|
19
|
+
ATTACHEXC = 14 # attach to running process with signal exception
|
20
|
+
FORCEQUOTA = 30 # Enforce quota for root
|
21
|
+
DENY_ATTACH = 31 #Prevent process from being traced
|
22
|
+
FIRSTMACH = 32 # for machine-specific requests
|
23
|
+
end
|
24
|
+
|
25
|
+
module Ragweed::Wraposx::Signal
|
26
|
+
#the Ruby module Signal also has this information
|
27
|
+
SIGHUP = 1 # hangup
|
28
|
+
SIGINT = 2 # interrupt
|
29
|
+
SIGQUIT = 3 # quit
|
30
|
+
SIGILL = 4 # illegal instruction (not reset when caught)
|
31
|
+
SIGTRAP = 5 # trace trap (not reset when caught)
|
32
|
+
SIGABRT = 6 # abort()
|
33
|
+
#if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE))
|
34
|
+
SIGPOLL = 7 # pollable event ([XSR] generated, not supported)
|
35
|
+
#else /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
|
36
|
+
SIGIOT = SIGABRT # compatibility
|
37
|
+
SIGEMT = 7 # EMT instruction
|
38
|
+
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
|
39
|
+
SIGFPE = 8 # floating point exception
|
40
|
+
SIGKILL = 9 # kill (cannot be caught or ignored)
|
41
|
+
SIGBUS = 10 # bus error
|
42
|
+
SIGSEGV = 11 # segmentation violation
|
43
|
+
SIGSYS = 12 # bad argument to system call
|
44
|
+
SIGPIPE = 13 # write on a pipe with no one to read it
|
45
|
+
SIGALRM = 14 # alarm clock
|
46
|
+
SIGTERM = 15 # software termination signal from kill
|
47
|
+
SIGURG = 16 # urgent condition on IO channel
|
48
|
+
SIGSTOP = 17 # sendable stop signal not from tty
|
49
|
+
SIGTSTP = 18 # stop signal from tty
|
50
|
+
SIGCONT = 19 # continue a stopped process
|
51
|
+
SIGCHLD = 20 # to parent on child stop or exit
|
52
|
+
SIGTTIN = 21 # to readers pgrp upon background tty read
|
53
|
+
SIGTTOU = 22 # like TTIN for output if (tp->t_local<OSTOP)
|
54
|
+
#if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))
|
55
|
+
SIGIO = 23 # input/output possible signal
|
56
|
+
#endif
|
57
|
+
SIGXCPU = 24 # exceeded CPU time limit
|
58
|
+
SIGXFSZ = 25 # exceeded file size limit
|
59
|
+
SIGVTALRM = 26 # virtual time alarm
|
60
|
+
SIGPROF = 27 # profiling time alarm
|
61
|
+
#if (!defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE))
|
62
|
+
SIGWINCH = 28 # window size changes
|
63
|
+
SIGINFO = 29 # information request
|
64
|
+
#endif
|
65
|
+
SIGUSR1 = 30 # user defined signal 1
|
66
|
+
SIGUSR2 = 31 # user defined signal 2
|
67
|
+
end
|
68
|
+
|
69
|
+
module Ragweed::Wraposx::Wait
|
70
|
+
NOHANG = 0x01 # [XSI] no hang in wait/no child to reap
|
71
|
+
UNTRACED = 0x02 # [XSI] notify on stop, untraced child
|
72
|
+
EXITED = 0x04 # [XSI] Processes which have exitted
|
73
|
+
STOPPED = 0x08 # [XSI] Any child stopped by signal
|
74
|
+
CONTINUED = 0x10 # [XSI] Any child stopped then continued
|
75
|
+
NOWWAIT = 0x20 # [XSI] Leave process returned waitable
|
76
|
+
end
|
77
|
+
|
78
|
+
module Ragweed::Wraposx::Vm; end
|
79
|
+
module Ragweed::Wraposx::Vm::Prot
|
80
|
+
#vm_protect permission flags for memory spaces
|
81
|
+
READ = 0x1 #read permission
|
82
|
+
WRITE = 0x2 #write permission
|
83
|
+
EXECUTE = 0x4 #execute permission
|
84
|
+
NONE = 0x0 #no rights
|
85
|
+
ALL = 0x7 #all permissions
|
86
|
+
end
|
87
|
+
|
88
|
+
module Ragweed::Wraposx::Dl
|
89
|
+
RTLD_LAZY = 0x1
|
90
|
+
RTLD_NOW = 0x2
|
91
|
+
RTLD_LOCAL = 0x4
|
92
|
+
RTLD_GLOBAL = 0x8
|
93
|
+
RTLD_NOLOAD = 0x10
|
94
|
+
RTLD_NODELETE = 0x80
|
95
|
+
RTLD_FIRST = 0x100 #/* Mac OS X 10.5 and later */
|
96
|
+
|
97
|
+
# Special handle arguments for dlsym().
|
98
|
+
#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */
|
99
|
+
#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */
|
100
|
+
#define RTLD_SELF ((void *) -3) /* Search this and subsequent objects (Mac OS X 10.5 and later) */
|
101
|
+
end
|