Code Monkey home page Code Monkey logo

hijack's Introduction

Hijack: Provides an irb session to an existing ruby process.

Intro

Hijack allows you to connect to any ruby process and execute code as if it were a normal irb session. Hijack does not require your target process to require any hijacking code, hijack is able to connect to any ruby process. It achieves this by using gdb to inject a payload into the process which starts up a DRb server, hijack then detaches gdb and reconnects via DRb. Please note that gdb will halt your target process while it is attached, though the injection process is very quick and your process should only be halted for a few milliseconds.

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.

Using Hijack

$ hijack 16451
=> Hijacked 16451 (my_script.rb) (ruby 1.8.7 [i686-darwin9])
>>

Using ruby-debug

Hijack can be used to start ruby-debug in your target process, for example:

$ hijack 61378
=> Hijacked 61378 (/opt/local/bin/thin) (ruby 1.8.7 [i686-darwin9])
>> hijack_debug_mode
=> true

We’ve enabled debug mode, but we still need to insert a breakpoint:

>> ActionController::Dispatcher.class_eval do
>>   class << self
>>     def dispatch_with_debugger(cgi, session_options, output)
>>       debugger
>>       dispatch_without_debugger(cgi, session_options, output)
>>     end
>>     alias_method :dispatch_without_debugger, :dispatch
>>     alias_method :dispatch, :dispatch_with_debugger
>>   end
>> end

Now tell hijack that we can start debugging:

>> hijack_debug_start
Connected.

Point your browser at 0.0.0.0:3000 to trigger the breakpoint and ruby-debug’s console will appear:

>> hijack_debug_start
Connected.
(eval):5
(rdb:4) step
/Users/ian/Projects/zioko/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:28 new(output).dispatch_cgi(cgi, session_options)
(rdb:4) backtrace
--> #0 /Users/ian/Projects/zioko/vendor/rails/actionpack/lib/action_controller/dispatcher.rb:28 in 'dispatch_without_debugger'
    #1 (eval):5 in 'dispatch'
    #2 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/rack/adapter/rails.rb:81 in 'call'
    #3 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/rack/adapter/rails.rb:69 in 'call'
    #4 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/thin/connection.rb:76 in 'pre_process'
    #5 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/thin/connection.rb:74 in 'pre_process'
    #6 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/thin/connection.rb:57 in 'process'
    #7 /opt/local/lib/ruby/gems/1.8/gems/thin-1.2.2/lib/thin/connection.rb:42 in 'receive_data'
(rdb:4)

One caveat is that ruby-debug’s ‘irb’ command will not work because the debug connection is remote, you also can’t use hijack’s irb console whilst debugging. A future version of hijack will hopefully allow you to switch between the two.

Monkey Patching

Just as in a normal irb session, you can redefine the code running in your target process.

This example redefines ActionController’s dispatcher to print basic request activity (the example can be found in examples/rails_dispatcher.rb).

Start up a Thin web server:

$ thin start
>> Using rails adapter
>> Thin web server (v1.2.2 codename I Find Your Lack of Sauce Disturbing)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:3000, CTRL+C to stop

In another console hijack the Thin process:

$ ps | grep thin
61160 ttys001    0:01.43 /opt/local/bin/ruby /opt/local/bin/thin start

$ hijack 61160
=> Hijacked 61160 (/opt/local/bin/thin) (ruby 1.8.7 [i686-darwin9])
>> ActionController::Dispatcher.class_eval do
?>   class << self
>>     def dispatch_with_spying(cgi, session_options, output)
>>       env = cgi.__send__(:env_table)
>>       puts "#{Time.now.strftime('%Y/%m/%d %H:%M:%S')} - #{env['REMOTE_ADDR']} - #{env['REQUEST_URI']}"
>>       dispatch_without_spying(cgi, session_options, output)
>>     end
>>     alias_method :dispatch_without_spying, :dispatch
>>     alias_method :dispatch, :dispatch_with_spying
>>   end
>> end

Point your browser to 0.0.0.0:3000.

Back in hijack you’ll see your browsing activity:

2009/08/22 14:24:48 - 127.0.0.1 - /
2009/08/22 14:24:53 - 127.0.0.1 - /login
2009/08/22 14:24:54 - 127.0.0.1 - /signup

Instead of pasting your code into hijack, you can pass hijack the -e option execute a local file on the target process.

Process Output

By default hijack will forward your process output to the hijack client. This can get a little messy if your trying to write code at the same time as the target process writes to STDOUT/STDERR. You can mute and unmute the process with:

>> hijack_mute
=> true
>> hijack_unmute
=> true

For ease of use, hijack helper methods are discoverable with tab completion. hi<tab><tab> will give you a list of available helpers.

Process Mirroring

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 isn’t loaded in the hijack client.

To work around this, when hijack connects to a remote process it will inspect all the files required by the process and also attempt to require them itself. This may not work for all object types however so you may still get a warning when an object cannot be dumped.

Contributors

hijack's People

Contributors

ileitch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hijack's Issues

Hijacking Passenger

I'm not sure whether this is an issue or a feature, but I thought it would be handy to hijack a passenger rails process to debug it :) Is this theoretically possible? Anyway, I tried the following without success (I tried with and without sudo):

$ passenger-memory-stats
#75.6 MB 1.0 MB /Library/Ruby/Gems/1.8/gems/passenger-2.1.3/ext/apache2/ApplicationPoolServerExecutable
#2828 ? 177.7 MB 102.5 MB Rails:

Gives pid 2828

So I try to hijack it :)

$ hijack 2828 --gdb-debug

Ends with:

. done
0x903e16fa in select$DARWIN_EXTSN ()
(gdb) call (void)rb_eval_string("require 'stringio'\n require 'drb'\n\n unless defined?(Hijack)\n module Hijack\n class OutputCopier\n def self.remote\n @@Remote\n end\n\n def self.start(pid)\n @@Remote = DRbObject.new(nil, 'drbunix://tmp/hijack.' + pid + '.sock')\n\n class << $stdout\n def write_with_copying(str)\n write_without_copying(str)\n Hijack::OutputCopier.remote.write('stdout', str) rescue nil\n end\n alias_method :write_without_copying, :write\n alias_method :write, :write_with_copying\n end\n\n class << $stderr\n def write_with_copying(str)\n write_without_copying(str)\n Hijack::OutputCopier.remote.write('stderr', str) rescue nil\n end\n alias_method :write_without_copying, :write\n alias_method :write, :write_with_copying\n end\n end\n end\n\n class Evaluator\n def initialize(context)\n @context = context\n @file = FILE\n end\n\n def evaluate(rb)\n if rb =~ /_hijack_output_receiver_ready([\d]+)/\n OutputCopier.start($1)\n elsif rb =~ /__hijack_get_remote_file_name/\n @file\n else\n @context.instance_eval(rb)\n end\n end\n end\n\n def self.start(context)\n return if @service && @service.alive?\n evaluator = Hijack::Evaluator.new(context)\n @service = DRb.start_service('drbunix://tmp/hijack.2998.sock', evaluator)\n File.chmod(0600, '/tmp/hijack.2998.sock')\n end\n end\n end\n __hijack_context = self\n Signal.trap('USR2') { Hijack.start(__hijack_context) }")
Detaching from program: `/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby', process 2998 thread 0x203.

Race condition in gdb

It looks like if the gdb command executes before we select() hijacking will just hang.

hangs in ubuntu during the start

I just installed the latest 0.1.9 and when i tried to hijack a rails app, running on mongrel, it would just hang after the first screen output, i.e. "=> Hijacking ... ".
I am running ruby 1.8.6 patch 287. Is there any workaround on this?

Can't hijack Rails 3 process running under Ruby 1.9.2

Here is the output I get running hijack --gdb-debug command on Rails 3/Ruby 1.9.2 process:


...
> #50 0x08167afc in vm_exec (th=0x8430740) at vm.c:1132
> #51 0x08167e44 in rb_iseq_eval_main (iseqval=138667220) at vm.c:1373
> #52 0x0805dfa2 in ruby_exec_internal (n=0x843e4d4) at eval.c:204
> #53 0x0805f8e2 in ruby_run_node (n=0x843e4d4) at eval.c:251
> #54 0x0805d370 in main (argc=3, argv=0xbffc7c44) at main.c:35
(gdb) set variable (int)rb_trap_pending=1
> No symbol "rb_trap_pending" in current context.
(gdb) break rb_trap_exec
> Function "rb_trap_exec" not defined.
> Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal]
(gdb) continue

Obviously nothing happens afterwards.

PS. It looks like rb_trap_exec was removed in this commit
http://redmine.ruby-lang.org/repositories/diff/ruby-19/signal.c?rev=19080

able to hijack 1.9?

this hijack attempt fails, either because the process is always stuck in GC, or because it's 1.9.1?

rdp       1880 32308 93 16:41 pts/0    01:46:04 ruby multiple_runs_same_setting_grapher.rb yanc_30mb_3_url_use_bittorrent_atfalse
rdp       7126 23734  0 13:26 pts/6    00:00:07 ruby /home/rdp/go/src/mongrel_dir_handler_current_dir.rb
rdp       9177  9053  0 18:35 pts/5    00:00:00 grep ruby
[27:1835][rdp@ilab1:~]$ hijack 1880
=> Hijacking...
=> 1880 doesn't appear to be a Ruby process!

hijack gives a strange issue at times.

Hi,
I get an error about mirror process every time I start hjak. I have to either reinstall the gem or to run the update to make the gem work.

details of the error:

/Users/xxxxxxxx/.rvm/gems/ruby-1.9.2-p290/gems/hijack-0.2.0/lib/hijack/console.rb:52:in mirror_process': undefined method-' for nil:NilClass (NoMethodError)
from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/gems/hijack-0.2.0/lib/hijack/console.rb:15:in initialize' from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/gems/hijack-0.2.0/lib/hijack.rb:18:innew'
from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/gems/hijack-0.2.0/lib/hijack.rb:18:in start' from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/gems/hijack-0.2.0/bin/hijack:27:in<top (required)>'
from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/bin/hijack:19:in load' from /Users/shaikhtabrez/.rvm/gems/ruby-1.9.2-p290/bin/hijack:19:in

'
~/xxxx>bundle update hijack
Fetching gem metadata from http://rubygems.org/.^C

Actually, today it has stopped working with the update too. Las time I did a full uninstall and an update. Any suggestions?

cryptic error if gdb is not installed

$ hijack 20554
=> Hijacking.../var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:8: Use RbConfig instead of obsolete and deprecated Config.
/var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:113:in `write': Broken pipe (Errno::EPIPE)
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:113:in `puts'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:113:in `exec'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:100:in `backtrace'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:92:in `ensure_attached_to_ruby_process'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:34:in `attach_outside_gc'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/gdb.rb:9:in `initialize'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/payload.rb:9:in `new'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/payload.rb:9:in `inject'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack/console.rb:10:in `initialize'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack.rb:18:in `new'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/lib/hijack.rb:18:in `start'
    from /var/lib/gems/1.9.1/gems/hijack-0.2.1/bin/hijack:27:in `<top (required)>'
    from /usr/local/bin/hijack:19:in `load'
    from /usr/local/bin/hijack:19:in `<main>'

The issue was that gdb is not installed. After installing it, hijacking appears to be working as intended (I get a "20554 doesn't appear to be a Ruby process!" error, but that's obviously a different issue).

I suggest to preflight a check for gdb and if not found, print "GDB could not be found. Please ensure it is installed." or similar message.

Hijack doesn't work with Emacs inferior ruby process?

Hello!

I'm trying to use hijack as an inferior Ruby process in Emacs, but sending regions and definitions seems not to work. Code gets sent from my editing buffer to the repl, but the code doesn't actually seem to get eval'd.

Maybe an issue with how the hijack prompt is started?

hijack fails missing constant Config

# hijack --debug 932
=> Hijacking.../opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack/gdb.rb:8:in `initialize': uninitialized constant Hijack::GDB::Config (NameError)
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack/payload.rb:9:in `new'
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack/payload.rb:9:in `inject'
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack/console.rb:10:in `initialize'
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack.rb:18:in `new'
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/lib/hijack.rb:18:in `start'
    from /opt/rubies/ruby-2.2.2/lib/ruby/gems/2.2.0/gems/hijack-0.2.1/bin/hijack:27:in `<top (required)>'
    from /opt/rubies/ruby-2.2.2/bin/hijack:23:in `load'
    from /opt/rubies/ruby-2.2.2/bin/hijack:23:in `<main>'

hangs in OSX Leopard, ruby 1.9.2 at start

I tried hijacking irb and get the "hijacking ..." status.

As mentioned elsewhere, i did connect to the irb process using gdb and then run bt.

Here is part of the output:
#35 0x00000001001610d5 in rb_catch_obj (tag=4050190, func=0x10017d7a0 <catch_i>, data=0) at vm_eval.c:1523
#36 0x0000000100161b00 in rb_f_catch (argc=<value temporarily unavailable, due to optimizations>, argv=<value temporarily unavailable, due to optimizations>) at vm_eval.c:1499
#37 0x0000000100165487 in vm_call_cfunc inlined at /Users/rahul/.rvm/src/ruby-1.9.2-head/vm_insnhelper.c:401
#38 0x0000000100165487 in vm_call_method (th=0x1003016a0, cfp=0x1004ffce8, num=1, blockptr=0x1004ffd11, flag=8, id=<value temporarily unavailable, due to optimizations>, me=0x10031d590, recv=4321140880) at vm_insnhelper.c:523
#39 0x0000000100168215 in vm_exec_core (th=0x1003016a0, initial=<value temporarily unavailable, due to optimizations>) at insns.def:1006
#40 0x000000010016fe53 in vm_exec (th=0x1003016a0) at vm.c:1145
#41 0x000000010017d9e1 in catch_i (tag=3214606, data=<value temporarily unavailable, due to optimizations>) at vm.c:557
#42 0x00000001001610d5 in rb_catch_obj (tag=3214606, func=0x10017d7a0 <catch_i>, data=0) at vm_eval.c:1523
#43 0x0000000100161b00 in rb_f_catch (argc=<value temporarily unavailable, due to optimizations>, argv=<value temporarily unavailable, due to optimizations>) at vm_eval.c:1499
#44 0x0000000100165487 in vm_call_cfunc inlined at /Users/rahul/.rvm/src/ruby-1.9.2-head/vm_insnhelper.c:401
#45 0x0000000100165487 in vm_call_method (th=0x1003016a0, cfp=0x1004ffea0, num=1, blockptr=0x1004ffec9, flag=8, id=<value temporarily unavailable, due to optimizations>, me=0x10031d590, recv=4303825320) at vm_insnhelper.c:523
#46 0x0000000100168215 in vm_exec_core (th=0x1003016a0, initial=<value temporarily unavailable, due to optimizations>) at insns.def:1006
#47 0x000000010016fe53 in vm_exec (th=0x1003016a0) at vm.c:1145
#48 0x0000000100170158 in rb_iseq_eval_main (iseqval=4303807640) at vm.c:1386
#49 0x000000010003f0d2 in ruby_exec_internal (n=0x10086e498) at eval.c:214
#50 0x0000000100041a5c in ruby_exec_node inlined at /Users/rahul/.rvm/src/ruby-1.9.2-head/eval.c:261
#51 0x0000000100041a5c in ruby_run_node (n=<value temporarily unavailable, due to optimizations>) at eval.c:254
#52 0x0000000100000ecf in main (argc=7, argv=0x7fff5fbfcfe0) at main.c:35

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.