iZsh-ragweed 0.1.8

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 +29 -0
  2. data/README.rdoc +35 -0
  3. data/README.txt +9 -0
  4. data/Rakefile +30 -0
  5. data/examples/hittracertux.rb +49 -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.rb +84 -0
  11. data/lib/ragweed/arena.rb +55 -0
  12. data/lib/ragweed/blocks.rb +128 -0
  13. data/lib/ragweed/debugger32.rb +338 -0
  14. data/lib/ragweed/debuggerosx.rb +419 -0
  15. data/lib/ragweed/debuggertux.rb +345 -0
  16. data/lib/ragweed/detour.rb +223 -0
  17. data/lib/ragweed/ptr.rb +48 -0
  18. data/lib/ragweed/rasm.rb +53 -0
  19. data/lib/ragweed/rasm/isa.rb +1046 -0
  20. data/lib/ragweed/rasm/util.rb +26 -0
  21. data/lib/ragweed/sbuf.rb +197 -0
  22. data/lib/ragweed/trampoline.rb +103 -0
  23. data/lib/ragweed/utils.rb +88 -0
  24. data/lib/ragweed/wrap32.rb +53 -0
  25. data/lib/ragweed/wrap32/debugging.rb +163 -0
  26. data/lib/ragweed/wrap32/device.rb +49 -0
  27. data/lib/ragweed/wrap32/event.rb +50 -0
  28. data/lib/ragweed/wrap32/hooks.rb +23 -0
  29. data/lib/ragweed/wrap32/overlapped.rb +46 -0
  30. data/lib/ragweed/wrap32/process.rb +506 -0
  31. data/lib/ragweed/wrap32/process_token.rb +59 -0
  32. data/lib/ragweed/wrap32/thread_context.rb +208 -0
  33. data/lib/ragweed/wrap32/winx.rb +16 -0
  34. data/lib/ragweed/wrap32/wrap32.rb +526 -0
  35. data/lib/ragweed/wraposx.rb +53 -0
  36. data/lib/ragweed/wraposx/constants.rb +112 -0
  37. data/lib/ragweed/wraposx/kernelerrorx.rb +147 -0
  38. data/lib/ragweed/wraposx/region_info.rb +250 -0
  39. data/lib/ragweed/wraposx/thread_context.rb +203 -0
  40. data/lib/ragweed/wraposx/thread_info.rb +225 -0
  41. data/lib/ragweed/wraposx/wraposx.rb +376 -0
  42. data/lib/ragweed/wraptux.rb +53 -0
  43. data/lib/ragweed/wraptux/constants.rb +68 -0
  44. data/lib/ragweed/wraptux/threads.rb +7 -0
  45. data/lib/ragweed/wraptux/wraptux.rb +76 -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
@@ -0,0 +1,29 @@
1
+ == 0.1.8 / 2009-07-24
2
+
3
+ * lib/ragweed/wraposx/constants.rb (Vm::Sm): Add shared_mode constants.
4
+ * lib/ragweed/wraposx/region_info.rb: Add real support for
5
+ REGION_EXTENDED_INFO and REGION_TOP_INFO.
6
+
7
+ == 0.1.7 / 2009-07-24
8
+
9
+ * lib/ragweed/wraposx/region_info.rb: Add the returned address of the region.
10
+
11
+ == 0.1.6 / 2009-07-13
12
+
13
+ * bug fixes and API work
14
+
15
+ == 0.1.5 / 2009-06-18
16
+
17
+ * more bug fixes
18
+
19
+ == 0.1.1 / 2009-06-02
20
+
21
+ * bug fixen and namespace changes
22
+
23
+ == 0.1.0 / 2009-05-31
24
+
25
+ * initial release
26
+
27
+ == 0.0.1 / 2009-05-13
28
+
29
+ * initial pull from svn
@@ -0,0 +1,35 @@
1
+ Ragweed
2
+ by tduehr, struct, tqbf and iZsh
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 izsh-ragweed
32
+
33
+ == LICENSE:
34
+
35
+ Copyright 2009 Matasano Security, LLC All Rights Reserved
@@ -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.
@@ -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, iZsh'
23
+ PROJ.email = 'izsh@iphone-dev.com'
24
+ PROJ.url = 'github.com/iZsh/ragweed'
25
+ PROJ.version = Ragweed::VERSION
26
+ PROJ.rubyforge.name = 'ragweed'
27
+
28
+ PROJ.spec.opts << '--color'
29
+
30
+ # EOF
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ragweed'
4
+ require 'debuggertux'
5
+ require 'pp'
6
+ require 'irb'
7
+ #include Ragweed
8
+
9
+ filename = ARGV[0]
10
+ pid = ARGV[1].to_i
11
+
12
+ raise "hittracertux.rb FILE PID" if (ARGV.size < 2 or pid <= 0)
13
+
14
+ d = Debuggertux.new(pid)
15
+ d.attach
16
+
17
+ File.open(filename, "r") do |fd|
18
+ lines = fd.readlines
19
+ lines.map {|x| x.chomp}
20
+ lines.each do |tl|
21
+ fn, addr = tl.split(",", 2)
22
+ d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do puts "hit - #{fn} #{addr}\n"; end))
23
+ end
24
+ end
25
+
26
+ d.install_bps
27
+ d.continue
28
+ catch(:throw) { d.loop }
29
+
30
+
31
+ # An IDC script for generating the text file this hit tracer requires
32
+ =begin
33
+ #include <idc.idc>
34
+
35
+ static main()
36
+ {
37
+ auto entry, fname, outf, fd;
38
+ outf = AskFile(1, "*.txt", "Please select an output file");
39
+ fd = fopen(outf,"w");
40
+
41
+ for(entry=NextFunction(0); entry != BADADDR; entry=NextFunction(entry) )
42
+ {
43
+ fname = GetFunctionName(entry);
44
+ fprintf(fd, "%s,0x%x\n", fname, entry);
45
+ }
46
+
47
+ fclose(fd);
48
+ }
49
+ =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 'ragweed'
7
+
8
+ pid = Ragweed::Debuggertux.find_by_regex(/gcalctool/)
9
+
10
+ begin
11
+ t = Ragweed::Debuggertux.threads(pid)
12
+ puts "Available pid/tdpids\n"
13
+ t.each do |h| puts h end
14
+ puts "Which thread do you want to attach to?"
15
+ pid = STDIN.gets.chomp.to_i
16
+
17
+ d = Ragweed::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,84 @@
1
+
2
+ module Ragweed
3
+
4
+ # :stopdoc:
5
+ VERSION = '0.1.6'
6
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
+ # :startdoc:
9
+
10
+ # Returns the version string for the library.
11
+ #
12
+ def self.version
13
+ VERSION
14
+ end
15
+
16
+ # Returns the library path for the module. If any arguments are given,
17
+ # they will be joined to the end of the libray path using
18
+ # <tt>File.join</tt>.
19
+ #
20
+ def self.libpath( *args )
21
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
22
+ end
23
+
24
+ # Returns the lpath for the module. If any arguments are given,
25
+ # they will be joined to the end of the path using
26
+ # <tt>File.join</tt>.
27
+ #
28
+ def self.path( *args )
29
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
30
+ end
31
+
32
+ # Utility method used to require all files ending in .rb that lie in the
33
+ # directory below this file that has the same name as the filename passed
34
+ # in. Optionally, a specific _directory_ name can be passed in such that
35
+ # the _filename_ does not have to be equivalent to the directory.
36
+ #
37
+ def self.require_all_libs_relative_to( fname, dir = nil )
38
+ dir ||= ::File.basename(fname, '.*')
39
+ search_me = ::File.expand_path(
40
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
41
+
42
+ # Don't want to load wrapper or debugger here.
43
+ Dir.glob(search_me).sort.reject{|rb| rb =~ /(wrap|debugger|rasm[^.])/}.each {|rb| require rb}
44
+ # require File.dirname(File.basename(__FILE__)) + "/#{x}"d
45
+ end
46
+
47
+ def self.require_os_libs_relative_to( fname, dir= nil )
48
+ dir ||= ::File.basename(fname, '.*')
49
+ pkgs = ""
50
+ dbg = ""
51
+ case
52
+ when RUBY_PLATFORM =~ /win(dows|32)/i
53
+ pkgs = '32'
54
+ when RUBY_PLATFORM =~ /darwin/i
55
+ pkgs = 'osx'
56
+ when RUBY_PLATFORM =~ /linux/i
57
+ pkgs = 'tux'
58
+ # when RUBY_PLATFORM =~ /java/i
59
+ # XXX -TODO
60
+ else
61
+ warn "Platform not supported no wrapper libraries loaded."
62
+ end
63
+
64
+ if not pkgs.empty?
65
+ search_me = File.expand_path(File.join(File.dirname(fname), dir,"**", "*#{pkgs}.rb"))
66
+ Dir.glob(search_me).sort.each {|rb| require rb}
67
+ end
68
+ end
69
+ end # module Ragweed
70
+
71
+
72
+ # pkgs = %w[arena sbuf ptr process event rasm blocks detour trampoline device debugger hooks]
73
+ # pkgs << 'wrap32' if RUBY_PLATFORM =~ /win(dows|32)/i
74
+ # pkgs << 'wraposx' if RUBY_PLATFORM =~ /darwin/i
75
+ # pkgs << 'wraptux' if RUBY_PLATFORM =~ /linux/i
76
+ # pkgs.each do |x|
77
+ # require File.dirname(__FILE__) + "/#{x}"
78
+ # end
79
+
80
+
81
+ Ragweed.require_os_libs_relative_to(__FILE__)
82
+ Ragweed.require_all_libs_relative_to(__FILE__)
83
+
84
+ # EOF