ragweed 0.2.0-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|