ileitch-hijack 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -10
- data/TODO +1 -1
- data/bin/hijack +3 -2
- data/lib/hijack/console.rb +22 -19
- data/lib/hijack/gdb.rb +11 -3
- data/lib/hijack/helper.rb +1 -1
- data/lib/hijack/payload.rb +7 -0
- data/tasks/gem.rake +1 -1
- data/test/test.rb +3 -0
- metadata +5 -3
data/README.rdoc
CHANGED
@@ -6,10 +6,6 @@ Hijack allows you to connect to any ruby process and execute code as if it were
|
|
6
6
|
|
7
7
|
Hijack uses DRb over a unix socket file, so you need to be on the same machine as the process you want to hijack. This is by design for security reasons. You also need to run the hijack client as the same user as the remote process.
|
8
8
|
|
9
|
-
== WARNING
|
10
|
-
|
11
|
-
Hijack is new code, I'd think twice about trying it out on your critical production systems. I'd love to get some feedback though so if you have any development or staging systems to try it out on then please do!
|
12
|
-
|
13
9
|
== Using Hijack
|
14
10
|
|
15
11
|
$ hijack 16451
|
@@ -104,12 +100,7 @@ Back in hijack you'll see your browsing activity:
|
|
104
100
|
2009/08/22 14:24:53 - 127.0.0.1 - /login
|
105
101
|
2009/08/22 14:24:54 - 127.0.0.1 - /signup
|
106
102
|
|
107
|
-
Instead of pasting your code into hijack, you can pass hijack the -e option
|
108
|
-
|
109
|
-
$ hijack -e examples/rails_dispatcher.rb 61565
|
110
|
-
=> Hijacked 61565 (/opt/local/bin/thin) (ruby 1.8.7 [i686-darwin9])
|
111
|
-
=> Executing examples/rails_dispatcher.rb... done!
|
112
|
-
>> 2009/08/22 14:46:36 - 127.0.0.1 - /
|
103
|
+
Instead of pasting your code into hijack, you can pass hijack the -e option execute a local file on the target process.
|
113
104
|
|
114
105
|
== Process Output
|
115
106
|
|
@@ -120,6 +111,8 @@ By default hijack will forward your process output to the hijack client. This ca
|
|
120
111
|
>> hijack_unmute
|
121
112
|
=> true
|
122
113
|
|
114
|
+
For ease of use, hijack helper methods are discoverable with tab completion. hi<tab><tab> will give you a list of available helpers.
|
115
|
+
|
123
116
|
== Process Mirroring
|
124
117
|
|
125
118
|
DRb cannot dump objects to the hijack client for types that are not loaded in the client process. E.g if the remote process had required ActiveRecord and you tried to dump ActiveRecord::Base back to the client, DRb would instead return a DRb::Unknown object as ActiveRecord
|
data/TODO
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
* Use thread local stdout & stderr capture.
|
2
2
|
* assigning a variable doesn't work
|
3
3
|
* Improve startup experience
|
4
|
-
* Check if attached process is in fact a ruby process
|
5
4
|
* Require actual remote script if possible so that if it defines any classes we can dump those too.
|
5
|
+
* Add a hijack_exec helper to execute a local .rb on the target
|
data/bin/hijack
CHANGED
@@ -11,8 +11,9 @@ new_argv.delete(pid)
|
|
11
11
|
|
12
12
|
new_argv.options do |opts|
|
13
13
|
opts.banner = usage
|
14
|
-
opts.on("--gdb-debug", "Print gdb activity to the console.") { |v| options[:gdb_debug] =
|
15
|
-
opts.on("
|
14
|
+
opts.on("--gdb-debug", "Print gdb activity to the console.") { |v| options[:gdb_debug] = true }
|
15
|
+
opts.on("--mute", "Ignore stdout/stderr writes from the remote process.") { |v| options[:mute] = true }
|
16
|
+
opts.on("-e", "--execute=FILE", String, "Execute the specified file in the remote process and then disconnect.") { |v| options[:execute] = v }
|
16
17
|
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
|
17
18
|
opts.parse!
|
18
19
|
end
|
data/lib/hijack/console.rb
CHANGED
@@ -15,7 +15,7 @@ module Hijack
|
|
15
15
|
mirror_process
|
16
16
|
banner
|
17
17
|
execute_file
|
18
|
-
|
18
|
+
OutputReceiver.start(@remote) unless Hijack.options[:mute]
|
19
19
|
start_irb
|
20
20
|
end
|
21
21
|
|
@@ -36,7 +36,7 @@ module Hijack
|
|
36
36
|
Process.kill('USR2', @pid.to_i)
|
37
37
|
loop do
|
38
38
|
break if File.exists?(Hijack.socket_path_for(@pid))
|
39
|
-
sleep 0.
|
39
|
+
sleep 0.01
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -45,30 +45,32 @@ module Hijack
|
|
45
45
|
end
|
46
46
|
|
47
47
|
module OutputReceiver
|
48
|
-
|
48
|
+
class << self
|
49
|
+
def mute
|
50
|
+
@mute = true
|
51
|
+
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
def unmute(remote)
|
54
|
+
start(remote) unless @started
|
55
|
+
@mute = false
|
56
|
+
end
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
def write(where, str)
|
59
|
+
Object.const_get(where.upcase).write(str) unless @mute
|
60
|
+
end
|
57
61
|
|
58
|
-
|
59
|
-
|
60
|
-
|
62
|
+
def puts(where, str)
|
63
|
+
Object.const_get(where.upcase).puts(str) unless @mute
|
64
|
+
end
|
61
65
|
|
62
|
-
|
63
|
-
|
66
|
+
def start(remote)
|
67
|
+
DRb.start_service(Hijack.socket_for(Process.pid), self)
|
68
|
+
remote.evaluate("__hijack_output_receiver_ready_#{Process.pid}")
|
69
|
+
@started = true
|
70
|
+
end
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
67
|
-
def start_output_receiver
|
68
|
-
DRb.start_service(Hijack.socket_for(Process.pid), OutputReceiver)
|
69
|
-
@remote.evaluate("__hijack_output_receiver_ready_#{Process.pid}")
|
70
|
-
end
|
71
|
-
|
72
74
|
def mirror_process
|
73
75
|
# Attempt to require all files currently loaded by the remote process so DRb can dump as many objects as possible.
|
74
76
|
#
|
@@ -130,6 +132,7 @@ module Hijack
|
|
130
132
|
$stdout.flush
|
131
133
|
@remote.evaluate(File.read(Hijack.options[:execute]))
|
132
134
|
puts "done!"
|
135
|
+
exit
|
133
136
|
else
|
134
137
|
puts "=> Can't find #{Hijack.options[:execute]} to execute!"
|
135
138
|
end
|
data/lib/hijack/gdb.rb
CHANGED
@@ -10,8 +10,11 @@ module Hijack
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def attached_to_ruby_process?
|
13
|
-
|
14
|
-
|
13
|
+
backtrace.any? {|line| line =~ /ruby_run/}
|
14
|
+
end
|
15
|
+
|
16
|
+
def main_thread_blocked_by_join?
|
17
|
+
backtrace.any? {|line| line =~ /rb_thread_join/}
|
15
18
|
end
|
16
19
|
|
17
20
|
def eval(cmd)
|
@@ -29,6 +32,10 @@ module Hijack
|
|
29
32
|
end
|
30
33
|
|
31
34
|
protected
|
35
|
+
def backtrace
|
36
|
+
@backtrace ||= exec('bt').reverse
|
37
|
+
end
|
38
|
+
|
32
39
|
def set_trap_pending
|
33
40
|
exec("set variable (int)rb_trap_pending=1")
|
34
41
|
end
|
@@ -50,7 +57,7 @@ module Hijack
|
|
50
57
|
end
|
51
58
|
|
52
59
|
def exec(str)
|
53
|
-
puts("(gdb) #{str}")
|
60
|
+
puts("(gdb) #{str}") if @verbose
|
54
61
|
@gdb.puts(str)
|
55
62
|
wait
|
56
63
|
end
|
@@ -70,6 +77,7 @@ module Hijack
|
|
70
77
|
end
|
71
78
|
end
|
72
79
|
puts lines.map { |l| "> #{l}" } if @verbose
|
80
|
+
lines
|
73
81
|
end
|
74
82
|
end
|
75
83
|
end
|
data/lib/hijack/helper.rb
CHANGED
data/lib/hijack/payload.rb
CHANGED
@@ -4,6 +4,13 @@ module Hijack
|
|
4
4
|
gdb = GDB.new(pid)
|
5
5
|
unless gdb.attached_to_ruby_process?
|
6
6
|
puts "\n=> #{pid} doesn't appear to be a Ruby process!"
|
7
|
+
gdb.detach
|
8
|
+
exit 1
|
9
|
+
end
|
10
|
+
if gdb.main_thread_blocked_by_join?
|
11
|
+
puts "\n=> Unable to hijack #{pid} because the main thread is blocked waiting for another thread to join."
|
12
|
+
puts "=> Check that you are using the most recent version of hijack, a newer version may have solved this shortcoming."
|
13
|
+
gdb.detach
|
7
14
|
exit 1
|
8
15
|
end
|
9
16
|
gdb.eval(payload(pid))
|
data/tasks/gem.rake
CHANGED
data/test/test.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ileitch-hijack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Leitch
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-16 21:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -41,6 +41,8 @@ files:
|
|
41
41
|
- examples/rails_dispatcher.rb
|
42
42
|
has_rdoc: true
|
43
43
|
homepage: http://github.com/ileitch/hijack
|
44
|
+
licenses: []
|
45
|
+
|
44
46
|
post_install_message:
|
45
47
|
rdoc_options: []
|
46
48
|
|
@@ -61,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
63
|
requirements: []
|
62
64
|
|
63
65
|
rubyforge_project:
|
64
|
-
rubygems_version: 1.
|
66
|
+
rubygems_version: 1.3.5
|
65
67
|
signing_key:
|
66
68
|
specification_version: 3
|
67
69
|
summary: Provides an irb session to an existing ruby process.
|