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.
Files changed (62) hide show
  1. data/History.txt +15 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +48 -0
  6. data/examples/hittracerx.rb +63 -0
  7. data/examples/hook_notepad.rb +9 -0
  8. data/examples/snicker.rb +183 -0
  9. data/examples/tux-example.rb +23 -0
  10. data/lib/ragweed/arena.rb +55 -0
  11. data/lib/ragweed/blocks.rb +128 -0
  12. data/lib/ragweed/debugger32.rb +338 -0
  13. data/lib/ragweed/debuggerosx.rb +419 -0
  14. data/lib/ragweed/debuggertux.rb +347 -0
  15. data/lib/ragweed/detour.rb +223 -0
  16. data/lib/ragweed/ptr.rb +48 -0
  17. data/lib/ragweed/rasm/isa.rb +1046 -0
  18. data/lib/ragweed/rasm/util.rb +26 -0
  19. data/lib/ragweed/rasm.rb +53 -0
  20. data/lib/ragweed/sbuf.rb +197 -0
  21. data/lib/ragweed/trampoline.rb +103 -0
  22. data/lib/ragweed/utils.rb +87 -0
  23. data/lib/ragweed/wrap32/debugging.rb +163 -0
  24. data/lib/ragweed/wrap32/device.rb +49 -0
  25. data/lib/ragweed/wrap32/event.rb +50 -0
  26. data/lib/ragweed/wrap32/hooks.rb +23 -0
  27. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  28. data/lib/ragweed/wrap32/process.rb +506 -0
  29. data/lib/ragweed/wrap32/process_token.rb +59 -0
  30. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  31. data/lib/ragweed/wrap32/winx.rb +16 -0
  32. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  33. data/lib/ragweed/wrap32.rb +53 -0
  34. data/lib/ragweed/wraposx/constants.rb +101 -0
  35. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  36. data/lib/ragweed/wraposx/region_info.rb +244 -0
  37. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  38. data/lib/ragweed/wraposx/thread_info.rb +213 -0
  39. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  40. data/lib/ragweed/wraposx.rb +53 -0
  41. data/lib/ragweed/wraptux/constants.rb +68 -0
  42. data/lib/ragweed/wraptux/threads.rb +3 -0
  43. data/lib/ragweed/wraptux/wraptux.rb +76 -0
  44. data/lib/ragweed/wraptux.rb +53 -0
  45. data/lib/ragweed.rb +84 -0
  46. data/spec/ragweed_spec.rb +7 -0
  47. data/spec/spec_helper.rb +16 -0
  48. data/tasks/ann.rake +80 -0
  49. data/tasks/bones.rake +20 -0
  50. data/tasks/gem.rake +201 -0
  51. data/tasks/git.rake +40 -0
  52. data/tasks/notes.rake +27 -0
  53. data/tasks/post_load.rake +34 -0
  54. data/tasks/rdoc.rake +51 -0
  55. data/tasks/rubyforge.rake +55 -0
  56. data/tasks/setup.rb +292 -0
  57. data/tasks/spec.rake +54 -0
  58. data/tasks/svn.rake +47 -0
  59. data/tasks/test.rake +40 -0
  60. data/tasks/zentest.rake +36 -0
  61. data/test/test_ragweed.rb +0 -0
  62. metadata +127 -0
data/History.txt ADDED
@@ -0,0 +1,15 @@
1
+ == 0.1.5 / 2009-06-18
2
+
3
+ * more bug fixes
4
+
5
+ == 0.1.1 / 2009-06-02
6
+
7
+ * bug fixen and namespace changes
8
+
9
+ == 0.1.0 / 2009-05-31
10
+
11
+ * initial release
12
+
13
+ == 0.0.1 / 2009-05-13
14
+
15
+ * initial pull from svn
data/README.rdoc ADDED
@@ -0,0 +1,35 @@
1
+ Ragweed
2
+ by tduehr, struct, and tqbf
3
+ http://matasano.com/log
4
+
5
+ == DESCRIPTION:
6
+
7
+ * Ragweed is a set of scriptable debugging tools written mostly in native ruby.
8
+
9
+ * Where required the Ruby/DL and Win32API libraries are used to interface the machine
10
+ and OS native system calls.
11
+
12
+ == FEATURES/PROBLEMS:
13
+
14
+ * This suite is currently fairly piecemeal. Each OS has it's own set of tools.
15
+ The most complete set is for Win32.
16
+
17
+ * Work is ongoing to complete and unify the OSX and Linux portions.
18
+
19
+ == SYNOPSIS:
20
+
21
+ require 'debuggerosx'
22
+ d = Debuggerosx.new(514) # pid of process to trace
23
+
24
+ == REQUIREMENTS:
25
+
26
+ * NONE - no really, this is pure native ruby hooking system libraries. There are no other dependencies, none.
27
+
28
+ == INSTALL:
29
+
30
+ gem sources -a http://gems.github.com
31
+ sudo gem install tduehr-ragweed
32
+
33
+ == LICENSE:
34
+
35
+ Copyright 2009 Matasano Security, LLC All Rights Reserved
data/README.txt ADDED
@@ -0,0 +1,9 @@
1
+ Ragweed is a set of scriptable debugging tools written mostly in native ruby.
2
+
3
+ Where required the Ruby/DL and Win32API libraries are used to interface the machine
4
+ and OS native system calls.
5
+
6
+ This suite is currently fairly piecemeal. Each OS has it's own set of tools.
7
+ The most complete set is for Win32.
8
+
9
+ Work is ongoing to complete and unify the OSX and Linux portions.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'ragweed'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'ragweed'
22
+ PROJ.authors = 'tduehr, tqbf, struct'
23
+ PROJ.email = 'td@matasano.com'
24
+ PROJ.url = 'github.com/tduehr/ragweed'
25
+ PROJ.version = Ragweed::VERSION
26
+ PROJ.rubyforge.name = 'ragweed'
27
+
28
+ PROJ.spec.opts << '--color'
29
+
30
+ # EOF
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'debuggertux'
4
+ require 'pp'
5
+ require 'irb'
6
+ include Ragweed
7
+
8
+ filename = ARGV[0]
9
+ pid = ARGV[1].to_i
10
+
11
+ raise "hittracertux.rb FILE PID" if (ARGV.size < 2 or pid <= 0)
12
+
13
+ d = Debuggertux.new(pid)
14
+ d.attach
15
+
16
+ File.open(filename, "r") do |fd|
17
+ lines = fd.readlines
18
+ lines.map {|x| x.chomp}
19
+ lines.each do |tl|
20
+ fn, addr = tl.split(",", 2)
21
+ d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do puts "hit - #{fn} #{addr}\n"; end))
22
+ end
23
+ end
24
+
25
+ d.install_bps
26
+ d.continue
27
+ catch(:throw) { d.loop }
28
+
29
+
30
+ # An IDC script for generating the text file this hit tracer requires
31
+ =begin
32
+ #include <idc.idc>
33
+
34
+ static main()
35
+ {
36
+ auto entry, fname, outf, fd;
37
+ outf = AskFile(1, "*.txt", "Please select an output file");
38
+ fd = fopen(outf,"w");
39
+
40
+ for(entry=NextFunction(0); entry != BADADDR; entry=NextFunction(entry) )
41
+ {
42
+ fname = GetFunctionName(entry);
43
+ fprintf(fd, "%s,0x%x\n", fname, entry);
44
+ }
45
+
46
+ fclose(fd);
47
+ }
48
+ =end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # A simple hittracer to debug and test Debuggerosx.
4
+ # FILE is CSV file,address
5
+ # PID is the proces id to attach to.
6
+
7
+ require 'debuggerosx'
8
+ require 'pp'
9
+ require 'irb'
10
+ include Ragweed
11
+
12
+ filename = ARGV[0]
13
+ pid = ARGV[1].to_i
14
+
15
+ raise "hittracerosx.rb FILE PID" if (ARGV.size < 2 or pid <= 0)
16
+
17
+ class Debuggerosx
18
+ def on_exit
19
+ exit(1)
20
+ end
21
+
22
+ def on_single_step
23
+ end
24
+
25
+ def on_segv(thread)
26
+ pp self.get_registers(thread)
27
+ pp self.threads
28
+ self.threads.each {|thread| puts Wraposx::ThreadContext.get(thread).dump}
29
+ self.threads.each {|thread| puts Wraposx::ThreadInfo.get(thread).dump}
30
+ throw(:break)
31
+ end
32
+
33
+ def on_bus(thread)
34
+ throw(:break)
35
+ end
36
+ end
37
+
38
+ d = Debuggerosx.new(pid)
39
+ d.attach
40
+
41
+ File.open(filename, "r") do |fd|
42
+ lines = fd.readlines
43
+ lines.map {|x| x.chomp}
44
+ lines.each do |tl|
45
+ fn, addr = tl.split(",", 2)
46
+ d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do | t, r, s | puts "#{ s.breakpoints[r.eip].first.function } hit in thread #{ t }\n"; end))
47
+ end
48
+ end
49
+
50
+ d.install_bps
51
+ d.continue
52
+ catch(:throw) { d.loop }
53
+ pp d.wait 1
54
+ pp d.threads
55
+
56
+ d.threads.each do |t|
57
+ r = Wraposx::ThreadContext.get(t)
58
+ i = Wraposx::ThreadInfo.get(t)
59
+ pp r
60
+ puts r.dump
61
+ pp i
62
+ puts i.dump
63
+ end
@@ -0,0 +1,9 @@
1
+ require "ragweed"
2
+ include Ragweed
3
+
4
+ dbg = Debugger.find_by_regex /notepad/i
5
+ raise "notepad not running" if dbg.nil?
6
+
7
+ dbg.hook('kernel32!CreateFileW', 7) {|e,c,d,a| puts "#{d} CreateFileW for #{dbg.process.read(a[0],512).from_utf16_buffer}"}
8
+ dbg.loop
9
+ dbg.release
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This is a slightly more complex hit tracer implementation of
4
+ # Debuggerosx. It does fork/exec and attempts, in a manner resembling
5
+ # rocket surgery with a steamroller, to skip any call to ptrace.
6
+
7
+ # This was last setup to debug the race condition in Debuggerosx#on_breakpoint
8
+ # using ftp as the child.
9
+
10
+ require 'rubygems'
11
+ require 'ragweed'
12
+ require 'pp'
13
+ require 'ruby-debug'
14
+ Debugger.start
15
+
16
+ filename = ARGV[0]
17
+ pid = ARGV[1].to_i
18
+ ptraceloc = 0
19
+ rd, wr = nil, nil
20
+
21
+ class Snicker < Ragweed::Debuggerosx
22
+ attr_accessor :attached
23
+
24
+ def on_exit(status)
25
+ pp "Exited with status #{ status }"
26
+ @hooked = false
27
+ @attached = false
28
+ @exited = true
29
+ throw(:break)
30
+ end
31
+
32
+ def on_single_step
33
+ end
34
+
35
+ def on_sigsegv
36
+ pp "SEGV"
37
+ pp self.threads
38
+ self.threads.each do |t|
39
+ pp self.get_registers(t)
40
+ pp Ragweed::Wraposx::ThreadInfo.get(t)
41
+ end
42
+ debugger
43
+ @exited = true
44
+ throw(:break)
45
+ end
46
+
47
+ def on_sigbus
48
+ pp "sigbus"
49
+ # pp Ragweed::Wraposx::vm_read(@task,0x420f,169)
50
+ # # Kernel.debugger
51
+ # # Debugger.breakpoint
52
+ # # Debugger.catchpoint
53
+ # debugger
54
+ throw(:break)
55
+ end
56
+ end
57
+
58
+ if pid == 0
59
+ rd, wr = IO.pipe
60
+ pid = fork
61
+ end
62
+
63
+ if pid.nil?
64
+ ptraceloc = Ragweed::Wraposx::LIBS['/usr/lib/libc.dylib'].sym("ptrace", "IIIII").to_ptr.ref.to_s(Ragweed::Wraposx::SIZEOFINT).unpack("I_").first
65
+
66
+ pp ptraceloc.to_s(16)
67
+ rd.close
68
+ wr.puts ptraceloc
69
+
70
+ Ragweed::Wraposx::ptrace(Ragweed::Wraposx::Ptrace::TRACE_ME, 0, 0, 0)
71
+ puts "Traced!"
72
+ # sleep(1)
73
+
74
+ puts "Execing #{ARGV[1]}"
75
+ exec(ARGV[1])
76
+ puts "it left"
77
+ else
78
+ d = Snicker.new(pid)
79
+
80
+ if rd
81
+ wr.close
82
+ # d.attached = true
83
+ ptraceloc = rd.gets.chomp.to_i(0)
84
+
85
+ pp ptraceloc.to_s(16)
86
+ pp d
87
+ pp d.threads
88
+
89
+ raise "Fail!" if ptraceloc == 0
90
+
91
+ d.breakpoint_set(ptraceloc,"Ptrace",(bpl = lambda do |t, r, s|
92
+ puts "#{ s.breakpoints[r.eip].first.function } hit in thread #{ t }\n"
93
+ pp r
94
+ if Ragweed::Wraposx::vm_read(s.task,r.esp + 4,4).unpack("I").first == Ragweed::Wraposx::Ptrace::DENY_ATTACH
95
+ pp Ragweed::Wraposx::vm_read(s.task,r.esp-28,32).unpack("I_*").map{|x| x.to_s(16)}
96
+ pp Ragweed::Wraposx::vm_read(s.task,r.esp,32).unpack("I_*").map{|x| x.to_s(16)}
97
+ r.eax = 0
98
+ # r.esp = r.ebp
99
+ # r.ebp = Ragweed::Wraposx::vm_read(s.task,r.esp,4).unpack("I_").first
100
+ r.eip = Ragweed::Wraposx::vm_read(s.task,r.esp,4).unpack("V").first
101
+ # r.esp = Ragweed::Wraposx::vm_read(s.task,r.ebp,4).unpack("I_").first
102
+ # r.ebp +=4
103
+ # r.eip = Ragweed::Wraposx::vm_read(s.task,r.esp,4).unpack("I_").first
104
+ # r.esp+=4
105
+ pp "bounced"
106
+ return false
107
+ else
108
+ pp Ragweed::Wraposx::vm_read(s.task,r.esp-28,32).unpack("I_*").map{|x| x.to_s(16)}
109
+ pp Ragweed::Wraposx::vm_read(s.task,r.esp,32).unpack("I_*").map{|x| x.to_s(16)}
110
+ # pp Ragweed::Wraposx::dl_bignum_to_ulong(r.esp).ptr.to_s(4).unpack("I").first.to_s(16)
111
+ # pp Ragweed::Wraposx::dl_bignum_to_ulong(r.esp - 15*4).to_s(4*16).unpack("I*").map{|x| x.to_s(16)}
112
+ # pp Ragweed::Wraposx::dl_bignum_to_ulong(r.esp).to_s(15*4).unpack("I*").map{|x| x.to_s(16)}
113
+ return true
114
+ end
115
+ end))
116
+
117
+ d.install_bps
118
+
119
+ class Snicker < Ragweed::Debuggerosx
120
+ def on_sigtrap
121
+ if not @first
122
+ @first = true
123
+ self.install_bps
124
+ end
125
+ end
126
+
127
+ def on_stop(signal)
128
+ pp "#Stopped with signal #{ signal } (on_stop)"
129
+ end
130
+ end
131
+
132
+ else
133
+ d.attach
134
+
135
+ class Snicker < Ragweed::Debuggerosx
136
+ def on_sigstop
137
+ if not @first
138
+ @first = true
139
+ self.install_bps
140
+ end
141
+ end
142
+
143
+ def on_stop(signal)
144
+ pp "#Stopped with signal #{ signal } (on_stop)"
145
+ end
146
+ end
147
+ end
148
+
149
+ # File.open(filename, "r") do |fd|
150
+ # lines = fd.readlines
151
+ # lines.map {|x| x.chomp!}
152
+ # lines.each do |tl|
153
+ # pp tl
154
+ # fn, addr = tl.split(",", 2)
155
+ # pp [fn, addr.to_i(16)]
156
+ # if (not addr.nil? and addr.to_i(16) > 0)
157
+ # d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do | t, r, s |
158
+ # puts "#{ s.breakpoints[r.eip].first.function } hit in thread #{ t }\n"
159
+ # # pp r
160
+ # # debugger
161
+ # end))
162
+ # end
163
+ # end
164
+ # end
165
+ #
166
+ # blpwd = Wraposx::vm_read(d.task,0x420f,16)
167
+ # bbus = Wraposx::vm_read(d.task,0x4220,32)
168
+
169
+ catch(:break) { d.loop() }
170
+
171
+ if not d.exited
172
+ pp d.threads
173
+
174
+ d.threads.each do |t|
175
+ r = Ragweed::Wraposx::ThreadContext.get(t)
176
+ i = Ragweed::Wraposx::ThreadInfo.get(t)
177
+ pp r
178
+ puts r.dump
179
+ pp i
180
+ puts i.dump
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ## Simple example of attaching to a process and letting it run
4
+
5
+ require 'pp'
6
+ require 'debuggertux'
7
+ include Ragweed
8
+
9
+ pid = Debuggertux.find_by_regex(/gcalctool/)
10
+
11
+ begin
12
+ t = Debuggertux.get_thread_pids(pid)
13
+ puts "Which thread do you want to attach to?"
14
+ t.each do |h| puts h end
15
+ pid = STDIN.gets.chomp.to_i
16
+
17
+ d = Debuggertux.new(pid)
18
+ d.attach
19
+ d.continue
20
+ catch(:throw) { d.loop }
21
+ rescue
22
+ puts "Maybe your PID is wrong?"
23
+ end
@@ -0,0 +1,55 @@
1
+ class Ragweed::Arena
2
+ # I want 3 lambdas:
3
+ # * "get" should take no arguments and result in the address of a fresh
4
+ # 4k page.
5
+ # * "free" should free any 4k page returned by "get"
6
+ # * "copy" should implement memcpy, copying a string into a 4k page.
7
+ def initialize(get, free, copy)
8
+ @get = get
9
+ @free = free
10
+ @copy = copy
11
+ @pages = []
12
+ @avail = 0
13
+ @off = 0
14
+ end
15
+
16
+ private
17
+
18
+ def get
19
+ p = @get.call
20
+ @pages << p
21
+ @cur = p
22
+ @avail = 4096
23
+ @off = 0
24
+ end
25
+
26
+ public
27
+
28
+ # Allocate any size less than 4090 from the arena.
29
+ def alloc(sz)
30
+ raise "can't handle > page size now" if sz > 4090
31
+ get if sz > @avail
32
+ ret = @off
33
+ @off += sz
34
+ round = 4 - (@off % 4)
35
+ if (@off + round) > 4096
36
+ @avail = 0
37
+ @off = 4096
38
+ else
39
+ @off += round
40
+ @avail -= (sz + round)
41
+ end
42
+
43
+ return Ptr.new(@cur + ret)
44
+ end
45
+
46
+ # Copy a buffer into the arena and return its new address.
47
+ def copy(buf)
48
+ ret = alloc(buf.size)
49
+ @copy.call(ret, buf)
50
+ return ret
51
+ end
52
+
53
+ # Release the whole arena all at once.
54
+ def release; @pages.each {|p| @free.call(p)}; end
55
+ end
@@ -0,0 +1,128 @@
1
+ require 'ragweed/rasm'
2
+
3
+ pushv = $VERBOSE
4
+ $VERBOSE = nil
5
+
6
+ module Ragweed::Blocks
7
+ include Ragweed::Rasm
8
+ extend Ragweed::Rasm
9
+
10
+ def remote_trampoline(argc, opts={})
11
+ i = Rasm::Subprogram.new
12
+
13
+ # drop directly to debugger
14
+ i << Int(3) if opts[:debug]
15
+
16
+ # psuedo-frame-pointer
17
+ i.<< Push(esi)
18
+ i.<< Mov(esi, esp)
19
+
20
+ # get the thread arg
21
+ i.<< Add(esi, 8)
22
+
23
+ # load it
24
+ i.<< Mov(esi, [esi])
25
+ i.<< Push(ebx)
26
+ i.<< Mov(ebx, [esi])
27
+ i.<< Push(ecx)
28
+
29
+ # harvest arguments out of the argument buffer
30
+ (0...argc).each do |n|
31
+ i.<< Mov(ecx, [esi+(4+(n*4))])
32
+ i.<< Push(ecx)
33
+ end
34
+
35
+ i.<< Call(ebx)
36
+
37
+ # stuff return value after args
38
+ i.<< Mov([esi + (4+(argc*4))], eax)
39
+
40
+ # epilogue
41
+ i.<< Pop(ecx)
42
+ i.<< Pop(ebx)
43
+ i.<< Pop(esi)
44
+ i.<< Ret() # i think this is an artifact of my IRB, XXX clean up
45
+ end
46
+ module_function :remote_trampoline
47
+
48
+ def event_pair_stub(opts={})
49
+ i = Rasm::Subprogram.new
50
+
51
+ i << Int(3) if opts[:debug]
52
+
53
+ i.<< Push(ebp)
54
+ i.<< Mov(ebp, esp)
55
+ i.<< Sub(esp, 12)
56
+
57
+ i.<< Push(esi)
58
+ i.<< Mov(esi, [ebp+8])
59
+
60
+ i.<< Push(eax)
61
+ i.<< Push(ebx)
62
+ i.<< Push(edx)
63
+
64
+ # OpenProcess
65
+ i.<< Mov(ebx, [esi]) # function ptr
66
+ i.<< Mov(eax, [esi+24])
67
+ i.<< Push(eax)
68
+ i.<< Xor(eax, eax)
69
+ i.<< Push(eax)
70
+ i.<< Or(eax, 0x1F0FFF)
71
+ i.<< Push(eax)
72
+ i.<< Call(ebx)
73
+ i.<< Mov([ebp-4], eax)
74
+
75
+ # DuplicateHandle
76
+ i.<< Mov(ebx, [esi+4]) # function ptr
77
+ (1..2).each do |which|
78
+ i.<< Push(2) # flags
79
+ i.<< Push(0) # dunno
80
+ i.<< Push(0) # dunno
81
+ i.<< Mov(edx, ebp) # my LEA encoding is broken
82
+ i.<< Sub(edx, 8+(4*(which-1)))
83
+ i.<< Lea(eax, [edx])
84
+ i.<< Push(eax) # handle out-arg
85
+ i.<< Xor(eax, eax)
86
+ i.<< Not(eax)
87
+ i.<< Push(eax) # target process
88
+ i.<< Mov(ecx, esi)
89
+ i.<< Add(ecx, (20 + (4 * which)))
90
+ i.<< Push([ecx])
91
+ i.<< Push([ebp-4]) # target process handle
92
+ i.<< Call(ebx)
93
+ end
94
+
95
+ # ResetHandle
96
+ i.<< Mov(ebx, [esi+8]) # function ptr
97
+ (0..1).each do |which|
98
+ i.<< Push([ebp-(8+(4*which))])
99
+ i.<< Call(ebx)
100
+ end
101
+
102
+ # SignalHandle
103
+ i.<< Mov(ebx, [esi+12]) # function ptr
104
+ i.<< Push([ebp-8])
105
+ i.<< Call(ebx)
106
+
107
+ # WaitForSingleObject
108
+ i.<< Mov(ebx, [esi+16])
109
+ i.<< Xor(eax, eax)
110
+ i.<< Not(eax)
111
+ i.<< Push(eax)
112
+ i.<< Push([ebp-12])
113
+ i.<< Call(ebx)
114
+
115
+ # All done!
116
+
117
+ i.<< Pop(edx)
118
+ i.<< Pop(ebx)
119
+ i.<< Pop(eax)
120
+ i.<< Pop(ecx)
121
+ i.<< Pop(esi)
122
+ i.<< Add(esp, 12)
123
+ i.<< Pop(ebp)
124
+ i.<< Ret()
125
+ end
126
+ end
127
+
128
+ $VERBOSE = pushv