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
data/History.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
== 0.1.7.2 / 2009-09-21
|
2
|
+
|
3
|
+
* fixed Sub and Add in Rasm correctly. added Leave instruction
|
4
|
+
|
5
|
+
== 0.1.7.1 / 2009-09-20
|
6
|
+
|
7
|
+
* added Rasm::Subl to avoid conflict in Bblock with Kernel#sub
|
8
|
+
* setup for call trampolines in osx
|
9
|
+
|
10
|
+
== 0.1.7 / 2009-08-03
|
11
|
+
|
12
|
+
* bug fixes (Wraposx#RegionInfo should now fully work)
|
13
|
+
|
14
|
+
== 0.1.6 / 2009-07-13
|
15
|
+
|
16
|
+
* bug fixes and API work
|
17
|
+
|
18
|
+
== 0.1.5 / 2009-06-18
|
19
|
+
|
20
|
+
* more bug fixes
|
21
|
+
|
22
|
+
== 0.1.1 / 2009-06-02
|
23
|
+
|
24
|
+
* bug fixen and namespace changes
|
25
|
+
|
26
|
+
== 0.1.0 / 2009-05-31
|
27
|
+
|
28
|
+
* initial release
|
29
|
+
|
30
|
+
== 0.0.1 / 2009-05-13
|
31
|
+
|
32
|
+
* initial pull from svn
|
data/README.rdoc
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
== Ragweed
|
2
|
+
by tduehr, crohlf, and tqbf
|
3
|
+
http://chargen.matasano.com
|
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
|
+
== Supported Platforms
|
13
|
+
|
14
|
+
Ragweed is supported and has been tested on the following platforms (32bit intel only):
|
15
|
+
|
16
|
+
Windows 7
|
17
|
+
Windows XP
|
18
|
+
Linux Ubuntu 10.4
|
19
|
+
Linux Ubuntu 9.10
|
20
|
+
Mac OS X 10.6
|
21
|
+
Mac OS X 10.5
|
22
|
+
|
23
|
+
At this time only Ruby 1.8.x has been tested. We are actively investigating both 64 bit
|
24
|
+
support for each platform and support for Ruby 1.9.x. Unfortunately, both of these things
|
25
|
+
require significant changes to Ragweed.
|
26
|
+
|
27
|
+
* We are currently moving to FFI from ruby/dl. This will likely result in some incompatibilities if you are using the low level functions calls directly. It will also add ffi as a dependency. This move is to facilitate 1.9 and 64bit support.
|
28
|
+
|
29
|
+
== FEATURES/PROBLEMS:
|
30
|
+
|
31
|
+
* This suite is currently fairly piecemeal. Each OS has it's own set of tools.
|
32
|
+
The most complete set is for Win32.
|
33
|
+
|
34
|
+
* Work is ongoing to complete and unify the OSX and Linux portions.
|
35
|
+
|
36
|
+
* The FFI move is mostly complete. There may be a few changes to some structures to come, but everything should mostly match the C APIs.
|
37
|
+
|
38
|
+
* The move to FFI should give us free support for jRuby. This is, however, untested at this time.
|
39
|
+
|
40
|
+
* Struct's Nerve[http://github.com/struct/Nerve] is an example of the API we are heading toward
|
41
|
+
|
42
|
+
== SYNOPSIS:
|
43
|
+
|
44
|
+
require 'debuggerosx'
|
45
|
+
d = Debuggerosx.new(514) # pid of process to trace
|
46
|
+
|
47
|
+
Please see the examples directory for more. There are hit tracers for each platform.
|
48
|
+
|
49
|
+
== REQUIREMENTS:
|
50
|
+
|
51
|
+
* FFI - This was required to get around the limitations of Ruby/DL. If you're using Ragweed from jRuby, this should be free.
|
52
|
+
|
53
|
+
== INSTALL:
|
54
|
+
|
55
|
+
sudo gem install ragweed
|
56
|
+
# relax with a tasty beverage, you're done
|
57
|
+
|
58
|
+
== LICENSE:
|
59
|
+
|
60
|
+
Copyright 2009/2010 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,86 @@
|
|
1
|
+
|
2
|
+
# begin
|
3
|
+
# require 'bones'
|
4
|
+
# rescue LoadError
|
5
|
+
# abort '### Please install the "bones" gem ###'
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# ensure_in_path 'lib'
|
9
|
+
# require 'ragweed'
|
10
|
+
#
|
11
|
+
# task :default => 'test:run'
|
12
|
+
# task 'gem:release' => 'test:run'
|
13
|
+
#
|
14
|
+
# Bones {
|
15
|
+
# name 'ragweed'
|
16
|
+
# ignore_file '.gitignore'
|
17
|
+
# authors 'tduehr, tqbf, struct'
|
18
|
+
# email 'td@matasano.com'
|
19
|
+
# description 'General debugging tool written in Ruby for OSX/Win32/Linux'
|
20
|
+
# summary 'Scriptable debugger'
|
21
|
+
# exclude << %w(old$)
|
22
|
+
# url 'http://github.com/tduehr/ragweed/tree/master'
|
23
|
+
# version Ragweed::VERSION
|
24
|
+
# rdoc.opts << "--inline-source"
|
25
|
+
# rdoc.opts << "--line-numbers"
|
26
|
+
# spec.opts << '--color'
|
27
|
+
# }
|
28
|
+
# # EOF
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
require 'rake'
|
32
|
+
|
33
|
+
begin
|
34
|
+
require 'jeweler'
|
35
|
+
Jeweler::Tasks.new do |gem|
|
36
|
+
gem.name = "ragweed"
|
37
|
+
gem.summary = %Q{Scriptable debugger}
|
38
|
+
gem.description = %Q{General debugging tool written in Ruby for OSX/Win32/Linux}
|
39
|
+
gem.email = "td@matasano.com"
|
40
|
+
gem.homepage = "http://github.com/tduehr/ragweed"
|
41
|
+
gem.authors = ["tduehr", "struct", "tqbf"]
|
42
|
+
gem.rdoc_options = ["--inline-source", "--line-numbers", "--main", "README.rdoc"]
|
43
|
+
gem.platform = "java" if Gem::Platform.local.os == "java"
|
44
|
+
gem.add_dependency "ffi", "~> 1.0" if Gem::Platform.local.os != "java"
|
45
|
+
# gem.exclude = [%w(old)]
|
46
|
+
# gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
47
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
48
|
+
end
|
49
|
+
Jeweler::GemcutterTasks.new
|
50
|
+
rescue LoadError
|
51
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
52
|
+
end
|
53
|
+
|
54
|
+
require 'rake/testtask'
|
55
|
+
Rake::TestTask.new(:test) do |test|
|
56
|
+
test.libs << 'lib' << 'test'
|
57
|
+
test.pattern = 'test/**/test_*.rb'
|
58
|
+
test.verbose = true
|
59
|
+
end
|
60
|
+
|
61
|
+
begin
|
62
|
+
require 'rcov/rcovtask'
|
63
|
+
Rcov::RcovTask.new do |test|
|
64
|
+
test.libs << 'test'
|
65
|
+
test.pattern = 'test/**/test_*.rb'
|
66
|
+
test.verbose = true
|
67
|
+
end
|
68
|
+
rescue LoadError
|
69
|
+
task :rcov do
|
70
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
task :test => :check_dependencies
|
75
|
+
|
76
|
+
task :default => :test
|
77
|
+
|
78
|
+
require 'rake/rdoctask'
|
79
|
+
Rake::RDocTask.new do |rdoc|
|
80
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
81
|
+
|
82
|
+
rdoc.rdoc_dir = 'rdoc'
|
83
|
+
rdoc.title = "ragweed #{version}"
|
84
|
+
rdoc.rdoc_files.include('README*')
|
85
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
86
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'ragweed'
|
4
|
+
|
5
|
+
filename = ARGV[0]
|
6
|
+
pid = ARGV[1].to_i
|
7
|
+
|
8
|
+
raise "hittracertux.rb FILE PID" if (ARGV.size < 2 or pid <= 0)
|
9
|
+
|
10
|
+
opts = {}
|
11
|
+
d = Ragweed::Debuggertux.new(pid, opts)
|
12
|
+
d.attach
|
13
|
+
|
14
|
+
File.open(filename, "r") do |fd|
|
15
|
+
lines = fd.readlines
|
16
|
+
lines.map {|x| x.chomp}
|
17
|
+
lines.each do |tl|
|
18
|
+
fn, addr = tl.split(",", 2)
|
19
|
+
d.breakpoint_set(addr.to_i(16), fn, (bpl = lambda do puts "hit - #{fn} #{addr}\n"; end))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
d.install_bps
|
24
|
+
d.continue
|
25
|
+
catch(:throw) { d.loop }
|
26
|
+
|
27
|
+
# An IDC script for generating the text file this hit tracer requires
|
28
|
+
=begin
|
29
|
+
#include <idc.idc>
|
30
|
+
|
31
|
+
static main()
|
32
|
+
{
|
33
|
+
auto entry, fname, outf, fd;
|
34
|
+
outf = AskFile(1, "*.txt", "Please select an output file");
|
35
|
+
fd = fopen(outf,"w");
|
36
|
+
|
37
|
+
for(entry=NextFunction(0); entry != BADADDR; entry=NextFunction(entry) )
|
38
|
+
{
|
39
|
+
fname = GetFunctionName(entry);
|
40
|
+
fprintf(fd, "%s,0x%x\n", fname, entry);
|
41
|
+
}
|
42
|
+
|
43
|
+
fclose(fd);
|
44
|
+
}
|
45
|
+
=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 = Debugger32.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
|
data/examples/snicker.rb
ADDED
@@ -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,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
## Simple example of attaching to a process and letting it run
|
4
|
+
|
5
|
+
require 'ragweed'
|
6
|
+
|
7
|
+
pid = Ragweed::Debuggertux.find_by_regex(/gcalctool/)
|
8
|
+
|
9
|
+
begin
|
10
|
+
t = Ragweed::Debuggertux.threads(pid)
|
11
|
+
puts "Available pid/tdpids\n"
|
12
|
+
t.each do |h| puts h end
|
13
|
+
puts "Which thread do you want to attach to?"
|
14
|
+
pid = STDIN.gets.chomp.to_i
|
15
|
+
|
16
|
+
opts = {}
|
17
|
+
opts[:fork] = true ## This flag tells ragweed to trace any forked child processes
|
18
|
+
d = Ragweed::Debuggertux.new(pid, opts)
|
19
|
+
d.attach
|
20
|
+
d.continue
|
21
|
+
catch(:throw) { d.loop }
|
22
|
+
rescue
|
23
|
+
puts "Maybe your PID is wrong?"
|
24
|
+
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 Ragweed::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 ::File.join(::File.dirname(__FILE__),'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 = Ragweed::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, TODO clean up
|
45
|
+
end
|
46
|
+
module_function :remote_trampoline
|
47
|
+
|
48
|
+
def event_pair_stub(opts={})
|
49
|
+
i = Ragweed::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
|