2

I have a set of files that seem to be obfuscated or compiled by Ruby. If I do a file [sic] to one of the files:

a /usr/bin/env ruby script text executable

all of them start with this:

#!/usr/bin/env ruby
require 'iseq';RubyVM::InstructionSequence.load(Marshal.load(File.read(__FILE__,nil,113))).eval

What is this file? How can I see the code or debug it?

NOTE: Ruby version ruby 2.1.3p242 (2014-09-19 revision 47630) [x86_64-linux]

user1618465
  • 1,813
  • 2
  • 32
  • 58
  • What's happening here is that it's reading a file containing ruby-marshalled bytes, unmarshalling them with `Marshal.load`, then turning the result into an `InstructionSequence` object or something. The 2.2.3 docs for `RubyVM::InstructionSequence` don't have a `.load` method; what version of Ruby are you running this with? Also, try change from eval'ing to printing and see what comes out. i.e.: `require 'iseq';puts RubyVM::InstructionSequence.load(Marshal.load(File.read(__FILE__,nil,113)))` – user513951 Oct 15 '15 at 23:46
  • `RubyVM::InstructionSequence::load` is part of the `iseq` gem, which is `require`d at the beginning of this snippet. It is not (yet) in YARV proper, because YARV bytecode is unsafe, it can crash the VM (or worse). The YARV compiler never generates unsafe bytecode, but obviously, that cannot be guaranteed for bytecodes loaded from elsewhere, hence bytecode loading is disallowed, until someone takes the time to write a bytecode verifier. – Jörg W Mittag Oct 16 '15 at 00:30

1 Answers1

3

This is code compiled to a Ruby Virtual Machine. It is using the iseq gem which exposes the private method RubyVM::InstructionSequence::load.

You can't extract the original source code from it, but the debugger should work. You can read the compiled code in something like a human readable form with RubyVM::InstructionSequence#disassemble. Assuming Marshal.load returns a RubyVM::InstructionSequence object, this should do it.

require 'iseq';puts RubyVM::InstructionSequence.load(Marshal.load(File.read(__FILE__,nil,161))).disass‌​emble
user1618465
  • 1,813
  • 2
  • 32
  • 58
Schwern
  • 153,029
  • 25
  • 195
  • 336
  • 1
    Note: it's not "the Ruby Virtual Machine". There is no such thing. It's the YARV Virtual Machine, and it will *only* work with YARV, not with MRI (which is a pure AST-walking interpreter, and doesn't have a bytecode format), Rubinius, MRuby, MagLev, MacRuby, and RubyGoLightly (which have different bytecode formats), JRuby and XRuby (which use JVM bytecode), IronRuby and Ruby.NET (which use CLI CIL bytecode), Topaz (which uses PyPy bytecode), Opal (which compiles to ECMAScript), and many others. (Granted, many of those no longer exist.) HotRuby and RedSun OTOH can execute YARV bytecode. – Jörg W Mittag Oct 16 '15 at 00:41
  • … but also no longer exist. – Jörg W Mittag Oct 16 '15 at 00:41
  • Hi @Schwern ! I did what you said and this is what I am getting: `/tmp/foo:2:in \`
    ': undefined method \`disassemble' for # (NoMethodError)`. So I get that `Marshal.load` returns an `array` instead of a `RubyVM::InstructionSequence` object. What should I do here? thank you
    – user1618465 Oct 16 '15 at 22:26
  • 1
    I got it. I had to `RubyVM::InstructionSequence.load` `Marshal.load(File.read(__FILE__,nil,113))` to get a `RubyVM::InstructionSequence` ;) Edit your answer with this line and I'll flag it as correct. `require 'iseq';puts RubyVM::InstructionSequence.load(Marshal.load(File.read(__FILE__,nil,161))).disassemble` Thank you!! – user1618465 Oct 16 '15 at 22:49
  • 1
    @user1618465 Would you mind [editing it](http://stackoverflow.com/posts/33160720/edit)? You have the code, so you're more likely to get it right. – Schwern Oct 16 '15 at 22:57
  • @Schwern Sorry for coming back on this but is there any tool I can use to debug the bytecode I get from `.disassemble`? – user1618465 Oct 20 '15 at 07:58
  • @user1618465 Sorry, I don't know. Sounds like another question. – Schwern Oct 20 '15 at 18:18