ragweed 0.2.0-java
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 +32 -0
- data/README.rdoc +60 -0
- data/README.txt +9 -0
- data/Rakefile +86 -0
- data/VERSION +1 -0
- data/examples/hittracertux.rb +45 -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 +24 -0
- data/lib/ragweed/arena.rb +55 -0
- data/lib/ragweed/blocks.rb +128 -0
- data/lib/ragweed/debugger32.rb +400 -0
- data/lib/ragweed/debuggerosx.rb +456 -0
- data/lib/ragweed/debuggertux.rb +502 -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 +182 -0
- data/lib/ragweed/wrap32/debugging.rb +401 -0
- data/lib/ragweed/wrap32/device.rb +49 -0
- data/lib/ragweed/wrap32/event.rb +50 -0
- data/lib/ragweed/wrap32/hooks.rb +39 -0
- data/lib/ragweed/wrap32/overlapped.rb +46 -0
- data/lib/ragweed/wrap32/process.rb +613 -0
- data/lib/ragweed/wrap32/process_token.rb +75 -0
- data/lib/ragweed/wrap32/thread_context.rb +142 -0
- data/lib/ragweed/wrap32/winx.rb +16 -0
- data/lib/ragweed/wrap32/wrap32.rb +583 -0
- data/lib/ragweed/wrap32.rb +59 -0
- data/lib/ragweed/wraposx/constants.rb +114 -0
- data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
- data/lib/ragweed/wraposx/region_info.rb +275 -0
- data/lib/ragweed/wraposx/structs.rb +102 -0
- data/lib/ragweed/wraposx/thread_context.rb +902 -0
- data/lib/ragweed/wraposx/thread_info.rb +160 -0
- data/lib/ragweed/wraposx/thread_info.rb.old +121 -0
- data/lib/ragweed/wraposx/wraposx.rb +356 -0
- data/lib/ragweed/wraposx.rb +60 -0
- data/lib/ragweed/wraptux/constants.rb +101 -0
- data/lib/ragweed/wraptux/process.rb +35 -0
- data/lib/ragweed/wraptux/threads.rb +7 -0
- data/lib/ragweed/wraptux/wraptux.rb +72 -0
- data/lib/ragweed/wraptux.rb +57 -0
- data/lib/ragweed.rb +112 -0
- data/ragweed.gemspec +102 -0
- data/spec/ragweed_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/test/test_ragweed.rb +0 -0
- metadata +121 -0
@@ -0,0 +1,583 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'Win32API'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module Ragweed;end
|
7
|
+
module Ragweed::Wrap32
|
8
|
+
|
9
|
+
NULL = 0x0
|
10
|
+
|
11
|
+
module PagePerms
|
12
|
+
EXECUTE = 0x10
|
13
|
+
EXECUTE_READ = 0x20
|
14
|
+
EXECUTE_READWRITE = 0x40
|
15
|
+
EXECUTE_WRITECOPY = 0x80
|
16
|
+
NOACCESS = 0x1
|
17
|
+
READONLY = 0x2
|
18
|
+
READWRITE = 0x4
|
19
|
+
WRITECOPY = 0x8
|
20
|
+
|
21
|
+
WRITEABLE = [EXECUTE_READWRITE,
|
22
|
+
EXECUTE_WRITECOPY,
|
23
|
+
READWRITE,
|
24
|
+
WRITECOPY]
|
25
|
+
end
|
26
|
+
|
27
|
+
module FileSharing
|
28
|
+
NONE = 0
|
29
|
+
DELETE = 4
|
30
|
+
READ = 1
|
31
|
+
WRITE = 2
|
32
|
+
end
|
33
|
+
|
34
|
+
module FileDisposition
|
35
|
+
CREATE_ALWAYS = 2
|
36
|
+
CREATE_NEW = 1
|
37
|
+
OPEN_ALWAYS = 4
|
38
|
+
OPEN_EXISTING = 3
|
39
|
+
TRUNCATE_EXISTING = 5
|
40
|
+
end
|
41
|
+
|
42
|
+
module FileAttributes
|
43
|
+
ARCHIVE = 0x20
|
44
|
+
ENCRYPTED = 0x4000
|
45
|
+
HIDDEN = 0x2
|
46
|
+
NORMAL = 0x80
|
47
|
+
OFFLINE = 0x1000
|
48
|
+
READONLY = 1
|
49
|
+
SYSTEM = 4
|
50
|
+
TEMPORARY = 0x100
|
51
|
+
BACKUP = 0x02000000
|
52
|
+
DELETE_ON_CLOSE = 0x04000000
|
53
|
+
NO_BUFFERING = 0x20000000
|
54
|
+
NO_RECALL = 0x00100000
|
55
|
+
REPARSE_POINT = 0x00200000
|
56
|
+
OVERLAPPED = 0x40000000
|
57
|
+
POSIX = 0x0100000
|
58
|
+
RANDOM_ACCESS = 0x10000000
|
59
|
+
SEQUENTIAL = 0x08000000
|
60
|
+
WRITE_THROUGH = 0x80000000
|
61
|
+
end
|
62
|
+
|
63
|
+
module FileAccess
|
64
|
+
GENERIC_READ = 0x80000000
|
65
|
+
GENERIC_WRITE = 0x40000000
|
66
|
+
GENERIC_EXECUTE = 0x20000000
|
67
|
+
GENERIC_ALL = 0x10000000
|
68
|
+
end
|
69
|
+
|
70
|
+
module FormatArgs
|
71
|
+
FROM_SYSTEM = 4096
|
72
|
+
ALLOCATE_BUFFER = 256
|
73
|
+
end
|
74
|
+
|
75
|
+
module Win
|
76
|
+
extend FFI::Library
|
77
|
+
|
78
|
+
ffi_lib 'kernel32', 'Advapi32'
|
79
|
+
ffi_convention :stdcall
|
80
|
+
attach_function 'OpenProcess', [ :long, :long, :long ], :long
|
81
|
+
attach_function 'OpenThread', [ :long, :long, :long ], :long
|
82
|
+
attach_function 'CloseHandle', [ :long ], :long
|
83
|
+
attach_function 'GetLastError', [ ], :long
|
84
|
+
attach_function 'FormatMessageA', [ :long, :pointer, :long, :long, :pointer, :long, :pointer ], :void
|
85
|
+
attach_function 'VirtualAllocEx', [ :long, :long, :long, :long, :long ], :long
|
86
|
+
attach_function 'VirtualFreeEx', [ :long, :long, :long, :long, ], :long
|
87
|
+
attach_function 'WriteProcessMemory', [ :long, :long, :pointer, :long, :long ], :long
|
88
|
+
attach_function 'ReadProcessMemory', [ :long, :long, :pointer, :long, :long ], :long
|
89
|
+
attach_function 'VirtualQueryEx', [ :long, :long, :pointer, :long ], :long
|
90
|
+
attach_function 'VirtualProtectEx', [ :long, :long, :long, :long, :pointer ], :void
|
91
|
+
attach_function 'GetCurrentProcessId', [], :long
|
92
|
+
attach_function 'GetProcessId', [ :long ], :long
|
93
|
+
attach_function 'GetCurrentThreadId', [], :long
|
94
|
+
attach_function 'GetModuleHandleA', [ :pointer ], :long
|
95
|
+
attach_function 'LoadLibraryA', [ :pointer ], :long
|
96
|
+
attach_function 'GetProcAddress', [ :long, :pointer], :long
|
97
|
+
attach_function 'WaitForSingleObject', [ :long, :long ], :long
|
98
|
+
attach_function 'Process32First', [ :long, :pointer ], :long
|
99
|
+
attach_function 'Process32Next', [ :long, :pointer ], :long
|
100
|
+
attach_function 'Module32First', [ :long, :pointer ], :long
|
101
|
+
attach_function 'Module32Next', [ :long, :pointer ], :long
|
102
|
+
attach_function 'CreateToolhelp32Snapshot', [ :long, :long ], :long
|
103
|
+
attach_function 'Thread32First', [ :long, :pointer ], :long
|
104
|
+
attach_function 'Thread32Next', [ :long, :pointer ], :long
|
105
|
+
attach_function 'SuspendThread', [ :long ], :long
|
106
|
+
attach_function 'ResumeThread', [ :long ], :long
|
107
|
+
attach_function 'CreateRemoteThread', [ :long, :long, :long, :long, :long, :long, :long ], :long
|
108
|
+
attach_function 'Sleep', [ :long ], :long
|
109
|
+
attach_function 'DuplicateHandle', [ :long, :long, :long, :pointer, :long, :long, :long ], :long
|
110
|
+
attach_function 'CreateFileA', [ :pointer, :long, :long, :pointer, :long, :long, :pointer ], :long
|
111
|
+
attach_function 'OpenEventA', [ :long, :long, :pointer ], :long
|
112
|
+
attach_function 'CreateEventA', [ :long, :long, :long, :pointer ], :long
|
113
|
+
attach_function 'SetEvent', [ :long ], :long
|
114
|
+
attach_function 'ResetEvent', [ :long ], :long
|
115
|
+
attach_function 'WriteFile', [ :long, :pointer, :long, :pointer, :pointer ], :long
|
116
|
+
attach_function 'ReadFile', [ :long, :pointer, :long, :pointer, :pointer ], :long
|
117
|
+
attach_function 'DeviceIoControl', [ :long, :long, :pointer, :long, :pointer, :long, :pointer, :pointer ], :long
|
118
|
+
attach_function 'GetOverlappedResult', [ :long, :pointer, :pointer, :long ], :long
|
119
|
+
attach_function 'WaitForMultipleObjects', [ :long, :pointer, :long, :long ], :long
|
120
|
+
attach_function 'CreateProcessA', [ :pointer, :pointer, :pointer, :pointer, :bool, :long, :pointer, :pointer, :pointer, :pointer ], :long
|
121
|
+
attach_function 'CreateProcessW', [ :pointer, :pointer, :pointer, :pointer, :bool, :long, :pointer, :pointer, :pointer, :pointer ], :long
|
122
|
+
|
123
|
+
ffi_lib 'ntdll'
|
124
|
+
ffi_convention :stdcall
|
125
|
+
attach_function 'NtQueryInformationProcess', [ :long, :long, :pointer, :long, :pointer ], :long
|
126
|
+
|
127
|
+
ffi_lib 'msvcrt'
|
128
|
+
ffi_convention :stdcall
|
129
|
+
attach_function 'malloc', [ :long ], :long
|
130
|
+
attach_function 'memcpy', [ :pointer, :pointer, :long ], :long
|
131
|
+
|
132
|
+
## XXX This shouldnt be in psapi in win7, need to clean this up
|
133
|
+
## XXX Also the unicode version should be supported, this is NOT complete
|
134
|
+
ffi_lib 'psapi'
|
135
|
+
ffi_convention :stdcall
|
136
|
+
attach_function 'GetMappedFileNameA', [ :long, :long, :pointer, :long ], :long
|
137
|
+
end
|
138
|
+
|
139
|
+
class << self
|
140
|
+
|
141
|
+
# Get a process handle given a pid
|
142
|
+
def open_process(pid)
|
143
|
+
r = Win.OpenProcess(0x1F0FFF, 0, pid)
|
144
|
+
raise WinX.new(:open_process) if r == 0
|
145
|
+
return r
|
146
|
+
end
|
147
|
+
|
148
|
+
# Get a thread handle given a tid; if a block is provided, the semantics are
|
149
|
+
# as File#open with a block.
|
150
|
+
def open_thread(tid, &block)
|
151
|
+
h = Win.OpenThread(0x1F03FF, 0, tid)
|
152
|
+
raise WinX.new(:open_thread) if h == 0
|
153
|
+
if block_given?
|
154
|
+
ret = yield h
|
155
|
+
close_handle(h)
|
156
|
+
return ret
|
157
|
+
end
|
158
|
+
h
|
159
|
+
end
|
160
|
+
|
161
|
+
# Close any Win32 handle. Reminder: Win32 handles are just integers, like file
|
162
|
+
# descriptors in Posix.
|
163
|
+
def close_handle(h)
|
164
|
+
raise WinX.new(:close_handle) if Win.CloseHandle(h) == 0
|
165
|
+
end
|
166
|
+
|
167
|
+
# Get the last error code (errno) (can't fail)
|
168
|
+
def get_last_error
|
169
|
+
Win.GetLastError()
|
170
|
+
end
|
171
|
+
|
172
|
+
# strerror(errno) (can't fail)
|
173
|
+
def format_message(code=nil)
|
174
|
+
code ||= get_last_error
|
175
|
+
buf = FFI::MemoryPointer.from_string("\x00" * 4096)
|
176
|
+
Win.FormatMessageA(4096, nil, code, 0x00000400, buf, 4096, nil)
|
177
|
+
return buf.to_s.split("\x00")[0]
|
178
|
+
end
|
179
|
+
|
180
|
+
# Allocate memory in a remote process (or yourself, with handle -1)
|
181
|
+
def virtual_alloc_ex(h, sz, addr=NULL, prot=0x40)
|
182
|
+
r = Win.VirtualAllocEx(h, addr, sz, 0x1000, prot)
|
183
|
+
raise WinX.new(:virtual_alloc_ex) if r == 0
|
184
|
+
return r
|
185
|
+
end
|
186
|
+
|
187
|
+
# Free memory in a remote process given the pointer returned from virtual_alloc_ex
|
188
|
+
def virtual_free_ex(h, ptr, type=0x8000)
|
189
|
+
r = Win.VirtualFreeEx(h, ptr.to_i, 0, type)
|
190
|
+
raise WinX.new(:virtual_free_ex) if r == 0
|
191
|
+
return r
|
192
|
+
end
|
193
|
+
|
194
|
+
# Write a string into the memory of a remote process given its handle and an address
|
195
|
+
def write_process_memory(h, dst, val)
|
196
|
+
val = val.to_s if not val.kind_of? String
|
197
|
+
r = Win.WriteProcessMemory(h, dst.to_i, val, val.size, NULL)
|
198
|
+
raise WinX.new(:write_process_memory) if r == 0
|
199
|
+
return r
|
200
|
+
end
|
201
|
+
|
202
|
+
# Read from a remote process given an address and length, returning a string.
|
203
|
+
def read_process_memory(h, ptr, len)
|
204
|
+
# val = FFI::MemoryPointer.from_string("\x00" * len)
|
205
|
+
val = "\x00" * len
|
206
|
+
r = Win.ReadProcessMemory(h, ptr.to_i, val, len, NULL)
|
207
|
+
raise WinX.new(:read_process_memory) if r == 0
|
208
|
+
return val ## don't handle short reads XXX
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_mapped_filename(h, lpv, size)
|
212
|
+
val = "\x00" * size
|
213
|
+
r = Win.GetMappedFileNameA(h, lpv.to_i, val, size)
|
214
|
+
raise WinX.new(:get_mapped_filename) if r == 0
|
215
|
+
return val
|
216
|
+
end
|
217
|
+
|
218
|
+
def str2memory_basic_info(mbi)
|
219
|
+
s = OpenStruct.new
|
220
|
+
s.BaseAddress,
|
221
|
+
s.AllocationBase,
|
222
|
+
s.AllocationProtect,
|
223
|
+
s.RegionSize,
|
224
|
+
s.State,
|
225
|
+
s.Protect,
|
226
|
+
s.Type = mbi.unpack("LLLLLLL")
|
227
|
+
return s
|
228
|
+
end
|
229
|
+
|
230
|
+
# Return a struct with the MEMORY_BASIC_INFORMATION for a given address in the
|
231
|
+
# memory of a remote process. Gives you addressable memory ranges and protection
|
232
|
+
# flags.
|
233
|
+
def virtual_query_ex(h, ptr)
|
234
|
+
mbi = [0,0,0,0,0,0,0].pack("LLLLLLL")
|
235
|
+
if Win.VirtualQueryEx(h, ptr, mbi, mbi.size)
|
236
|
+
str2memory_basic_info(mbi)
|
237
|
+
else
|
238
|
+
nil
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# Change the protection of specific memory regions in a remote process.
|
243
|
+
def virtual_protect_ex(h, addr, prot, size=0)
|
244
|
+
old = [0].pack("L")
|
245
|
+
base = virtual_query_ex(h, addr).BaseAddress if size == 0
|
246
|
+
base ||= addr
|
247
|
+
|
248
|
+
if Win.VirtualProtectEx(h, base, size, prot, old)
|
249
|
+
old.unpack("L").first
|
250
|
+
else
|
251
|
+
raise WinX.new(:virtual_protect_ex)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# getpid
|
256
|
+
def get_current_process_id
|
257
|
+
Win.GetCurrentProcessId() # can't realistically fail
|
258
|
+
end
|
259
|
+
|
260
|
+
# get_processid
|
261
|
+
def get_process_id(h)
|
262
|
+
Win.GetProcessId(h)
|
263
|
+
end
|
264
|
+
|
265
|
+
# gettid
|
266
|
+
def get_current_thread_id
|
267
|
+
Win.GetCurrentThreadId() # can't realistically fail
|
268
|
+
end
|
269
|
+
|
270
|
+
# Given a DLL name, get a handle to the DLL.
|
271
|
+
def get_module_handle(name)
|
272
|
+
name = name
|
273
|
+
r = Win.GetModuleHandleA(name)
|
274
|
+
raise WinX.new(:get_module_handle) if r == 0
|
275
|
+
return r
|
276
|
+
end
|
277
|
+
|
278
|
+
# load a library explicitly from a dll
|
279
|
+
def load_library(name)
|
280
|
+
name = name
|
281
|
+
r = Win.LoadLibraryA(name)
|
282
|
+
raise WinX.new(:load_library) if r == 0
|
283
|
+
return r
|
284
|
+
end
|
285
|
+
|
286
|
+
# Using notation x = "foo!bar" or x = handle, y = meth, look up a function's
|
287
|
+
# address in a module. Note that this is local, not remote.
|
288
|
+
def get_proc_address(x, y=nil)
|
289
|
+
if not y
|
290
|
+
mod, meth = x.split "!"
|
291
|
+
h = get_module_handle(mod)
|
292
|
+
else
|
293
|
+
h = x
|
294
|
+
meth = y
|
295
|
+
end
|
296
|
+
|
297
|
+
r = Win.GetProcAddress(h, meth)
|
298
|
+
return r # pass error through
|
299
|
+
end
|
300
|
+
|
301
|
+
# Select(2), for a single object handle.
|
302
|
+
def wait_for_single_object(h)
|
303
|
+
r = Win.WaitForSingleObject(h, -1)
|
304
|
+
raise WinX.new(:wait_for_single_object) if r == -1
|
305
|
+
end
|
306
|
+
|
307
|
+
def str2process_info(str)
|
308
|
+
ret = OpenStruct.new
|
309
|
+
ret.dwSize,
|
310
|
+
ret.cntUsage,
|
311
|
+
ret.th32ProcessID,
|
312
|
+
ret.th32DefaultHeapID,
|
313
|
+
ret.th32ModuleID,
|
314
|
+
ret.cntThreads,
|
315
|
+
ret.th32ParentProcessID,
|
316
|
+
ret.pcPriClassBase,
|
317
|
+
ret.dwFlags,
|
318
|
+
ret.szExeFile = str.unpack("LLLLLLLLLA2048")
|
319
|
+
ret.szExeFile = ret.szExeFile.asciiz
|
320
|
+
return ret
|
321
|
+
end
|
322
|
+
|
323
|
+
# Use Toolhelp32 to enumerate all running processes on the box, returning
|
324
|
+
# a struct with PIDs and executable names.
|
325
|
+
def all_processes
|
326
|
+
h = Win.CreateToolhelp32Snapshot(0x2, 0)
|
327
|
+
if h != -1
|
328
|
+
pi = [(9*4)+2048,0,0,0,0,0,0,0,0,"\x00"*2048].pack("LLLLLLLLLa2048")
|
329
|
+
if Win.Process32First(h, pi) != 0
|
330
|
+
yield str2process_info(pi)
|
331
|
+
while Win.Process32Next(h, pi) != 0
|
332
|
+
yield str2process_info(pi)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
else
|
336
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def str2module_info(str)
|
341
|
+
ret = OpenStruct.new
|
342
|
+
ret.dwSize,
|
343
|
+
ret.th32ModuleID,
|
344
|
+
ret.th32ProcessID,
|
345
|
+
ret.GlblcntUsage,
|
346
|
+
ret.ProccntUsage,
|
347
|
+
ret.modBaseAddr,
|
348
|
+
ret.modBaseSize,
|
349
|
+
ret.hModule,
|
350
|
+
ret.szModule,
|
351
|
+
ret.szExePath = str.unpack("LLLLLLLLA256A260")
|
352
|
+
ret.szModule = ret.szModule.asciiz
|
353
|
+
ret.szExePath = ret.szExePath.asciiz
|
354
|
+
return ret
|
355
|
+
end
|
356
|
+
|
357
|
+
# Given a pid, enumerate the modules loaded into the process, returning base
|
358
|
+
# addresses, memory ranges, and the module name.
|
359
|
+
def list_modules(pid=0)
|
360
|
+
h = Win.CreateToolhelp32Snapshot(0x8, pid)
|
361
|
+
if h != -1
|
362
|
+
mi = [260+256+(8*4),0,0,0,0,0,0,0,"\x00"*256,"\x00"*260].pack("LLLLLLLLa256a260")
|
363
|
+
if Win.Module32First(h, mi) != 0
|
364
|
+
yield str2module_info(mi)
|
365
|
+
while Win.Module32Next(h, mi) != 0
|
366
|
+
yield str2module_info(mi)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
else
|
370
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
# Use virtual_query_ex to tell whether an address is writable.
|
375
|
+
def writeable?(h, off)
|
376
|
+
if (x = virtual_query_ex(h, off))
|
377
|
+
return PagePerms::WRITEABLE.member?(x.Protect & 0xFF)
|
378
|
+
else
|
379
|
+
return false
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
# NQIP does a lot of things, the most useful of which are getting the
|
384
|
+
# image name of a running process, and telling whether a debugger is loaded. This
|
385
|
+
# interface is Ioctl-style; provide an ordinal and a buffer to pass results through.
|
386
|
+
def nt_query_information_process(h, ord, buf)
|
387
|
+
lenp = [0].pack("L")
|
388
|
+
if Win.NtQueryInformationProcess(h, ord, buf, buf.size, lenp) == 0
|
389
|
+
len = lenp.unpack("L").first
|
390
|
+
return buf[0..(len-1)]
|
391
|
+
end
|
392
|
+
nil
|
393
|
+
end
|
394
|
+
|
395
|
+
def str2thread_info(str)
|
396
|
+
ret = OpenStruct.new
|
397
|
+
ret.dwSize,
|
398
|
+
ret.cntUsage,
|
399
|
+
ret.th32ThreadID,
|
400
|
+
ret.th32OwnerProcessID,
|
401
|
+
ret.tpBasePri,
|
402
|
+
ret.tpDeltaPri,
|
403
|
+
ret.thFlags = str.unpack("LLLLLLL")
|
404
|
+
return ret
|
405
|
+
end
|
406
|
+
|
407
|
+
# List all the threads in a process given its pid, returning a struct containing
|
408
|
+
# tids and run state. This is relatively expensive, because it uses Toolhelp32.
|
409
|
+
def threads(pid)
|
410
|
+
h = Win.CreateToolhelp32Snapshot(0x4, pid)
|
411
|
+
if h != -1
|
412
|
+
mi = [(7*4),0,0,0,0,0,0].pack("LLLLLLL")
|
413
|
+
if Win.Thread32First(h, mi) != 0
|
414
|
+
ti = str2thread_info(mi)
|
415
|
+
yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
|
416
|
+
while Win.Thread32Next(h, mi) != 0
|
417
|
+
ti = str2thread_info(mi)
|
418
|
+
yield str2thread_info(mi) if ti.th32OwnerProcessID == pid
|
419
|
+
end
|
420
|
+
end
|
421
|
+
else
|
422
|
+
raise WinX.new(:create_toolhelp32_snapshot)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
# Suspend a thread given its handle.
|
427
|
+
def suspend_thread(h)
|
428
|
+
r = Win.SuspendThread(h)
|
429
|
+
raise WinX.new(:suspend_thread) if r == 0
|
430
|
+
return r
|
431
|
+
end
|
432
|
+
|
433
|
+
# Resume a suspended thread, returning nonzero if the thread was suspended,
|
434
|
+
# and 0 if it was running.
|
435
|
+
def resume_thread(h)
|
436
|
+
ResumeThread(h)
|
437
|
+
end
|
438
|
+
|
439
|
+
# Create a remote thread in the process, starting at the location
|
440
|
+
# "start", with the threadproc argument "arg"
|
441
|
+
def create_remote_thread(h, start, arg)
|
442
|
+
r = Win.CreateRemoteThread(h, NULL, 0, start.to_i, arg.to_i, 0, 0)
|
443
|
+
raise WinX.new(:create_remote_thread) if r == 0
|
444
|
+
return r
|
445
|
+
end
|
446
|
+
|
447
|
+
def sleep(ms=0)
|
448
|
+
Win.Sleep(ms)
|
449
|
+
end
|
450
|
+
|
451
|
+
# clone a handle out of another open process (or self, with -1)
|
452
|
+
def duplicate_handle(ph, h)
|
453
|
+
ret = "\x00\x00\x00\x00"
|
454
|
+
r = Win.DuplicateHandle(ph, h, -1, ret, 0, 0, 0x2)
|
455
|
+
raise WinX.new(:duplicate_handle) if r == 0
|
456
|
+
ret.to_l32
|
457
|
+
end
|
458
|
+
|
459
|
+
def create_file(name, opts={})
|
460
|
+
opts[:disposition] ||= FileDisposition::OPEN_ALWAYS
|
461
|
+
opts[:sharing] ||= FileSharing::READ | FileSharing::WRITE
|
462
|
+
opts[:access] ||= FileAccess::GENERIC_ALL
|
463
|
+
opts[:flags] ||= 0
|
464
|
+
|
465
|
+
r = Win.CreateFileA(name, opts[:access], opts[:sharing], NULL, opts[:disposition], opts[:flags], NULL)
|
466
|
+
raise WinX.new(:create_file) if r == -1
|
467
|
+
return r
|
468
|
+
end
|
469
|
+
|
470
|
+
# i haven't made this work, but named handles are kind of silly anyways
|
471
|
+
def open_event(name)
|
472
|
+
r = Win.OpenEventA(0, 0, name)
|
473
|
+
raise WinX.new(:open_event) if r == 0
|
474
|
+
return r
|
475
|
+
end
|
476
|
+
|
477
|
+
# signal an event
|
478
|
+
def set_event(h)
|
479
|
+
r = Win.SetEvent(h)
|
480
|
+
raise WinX.new(:set_event) if r == 0
|
481
|
+
return r
|
482
|
+
end
|
483
|
+
|
484
|
+
# force-unsignal event (waiting on the event handle also does this)
|
485
|
+
def reset_event(h)
|
486
|
+
r = Win.ResetEvent(h)
|
487
|
+
raise WinX.new(:reset_event) if r == 0
|
488
|
+
return r
|
489
|
+
end
|
490
|
+
|
491
|
+
# create an event, which you can signal and wait on across processes
|
492
|
+
def create_event(name=nil, auto=false, signalled=false)
|
493
|
+
auto = (1 if auto) || 0
|
494
|
+
signalled = (1 if signalled) || 0
|
495
|
+
name ||= 0
|
496
|
+
|
497
|
+
r = Win.CreateEventA(0, auto, signalled, name);
|
498
|
+
raise WinX.new(:create_event) if r == 0
|
499
|
+
return r
|
500
|
+
end
|
501
|
+
|
502
|
+
def write_file(h, buf, overlapped=nil)
|
503
|
+
if overlapped
|
504
|
+
opp = overlapped.to_s
|
505
|
+
else
|
506
|
+
opp = NULL
|
507
|
+
end
|
508
|
+
|
509
|
+
outw = "\x00" * 4
|
510
|
+
r = Win.WriteFile(h, buf, buf.size, outw, opp)
|
511
|
+
raise WinX.new(:write_file) if r == 0 and get_last_error != 997
|
512
|
+
return buf, outw.unpack("L").first
|
513
|
+
end
|
514
|
+
|
515
|
+
def read_file(h, count, overlapped=nil)
|
516
|
+
if overlapped
|
517
|
+
opp = overlapped.to_s
|
518
|
+
else
|
519
|
+
opp = NULL
|
520
|
+
end
|
521
|
+
outw = "\x00" * 4
|
522
|
+
if not (buf = overlapped.try(:target)) or buf.size < count
|
523
|
+
buf = "\x00" * count
|
524
|
+
overlapped.target = buf if overlapped
|
525
|
+
end
|
526
|
+
|
527
|
+
r = Win.ReadFile(h, buf, count, outw, opp)
|
528
|
+
raise WinX.new(:read_file) if r == 0 and get_last_error != 997
|
529
|
+
return buf, outw.unpack("L").first
|
530
|
+
end
|
531
|
+
|
532
|
+
def device_io_control(h, code, inbuf, outbuf, overlapped=NULL)
|
533
|
+
overlapped = overlapped.to_s if overlapped
|
534
|
+
outw = "\x00" * 4
|
535
|
+
r = Win.DeviceIoControl(h, code, inbuf, inbuf.size, outbuf, outbuf.size, outw, overlapped)
|
536
|
+
raise WinX.new(:device_io_control) if r == 0 and get_last_error != 997
|
537
|
+
return outw.unpack("L").first
|
538
|
+
end
|
539
|
+
|
540
|
+
def get_overlapped_result(h, overlapped)
|
541
|
+
overlapped = overlapped.to_s
|
542
|
+
outw = "\x00" * 4
|
543
|
+
r = Win.GetOverlappedResult(h, overlapped, outw, 0)
|
544
|
+
raise WinX.new(:get_overlapped_result) if r == 0
|
545
|
+
return outw.unpack("L").first
|
546
|
+
end
|
547
|
+
|
548
|
+
# just grab some local memory
|
549
|
+
# XXX same as FFI name ?
|
550
|
+
def malloc(sz)
|
551
|
+
r = Win.malloc(sz)
|
552
|
+
raise WinX.new(:malloc) if r == 0
|
553
|
+
return r
|
554
|
+
end
|
555
|
+
|
556
|
+
def memcpy(dst, src, size)
|
557
|
+
Win.memcpy(dst, src, size)
|
558
|
+
end
|
559
|
+
|
560
|
+
# Block wrapper for thread suspension
|
561
|
+
def with_suspended_thread(tid)
|
562
|
+
open_thread(tid) do |h|
|
563
|
+
begin
|
564
|
+
suspend_thread(h)
|
565
|
+
ret = yield h
|
566
|
+
ensure
|
567
|
+
resume_thread(h)
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def wfmo(handles, ms=100)
|
573
|
+
hp = handles.to_ptr
|
574
|
+
r = Win.WaitForMultipleObjects(handles.size, hp, 0, ms)
|
575
|
+
raise WinX(:wait_for_multiple_objects) if r == 0xFFFFFFFF
|
576
|
+
if r < handles.size
|
577
|
+
return handles[r]
|
578
|
+
else
|
579
|
+
return nil
|
580
|
+
end
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
@@ -0,0 +1,59 @@
|
|
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 = File.read(File.join(File.dirname(__FILE__),"..","..","VERSION"))
|
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}.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::Wrap32
|
56
|
+
|
57
|
+
Ragweed::Wrap32.require_all_libs_relative_to(__FILE__)
|
58
|
+
|
59
|
+
# EOF
|