D
Dmitry Severin
The last time you advertised this product here, we had proven the claims
on your website to be false.
http://www.ruby-forum.com/topic/166458#731051
You have not made any changes or corrections to the website since.
Found link to RubyEncoder on InfoQ (
http://www.infoq.com/news/2008/10/rubyencoder ), and just for fun,
decided to look how difficult would it be to crack it
It turns out, that RubyEncoder uses following scheme: modified
Ruby-1.8.7 interpreter,
that stores encoded AST nodes along with encoding/restriction options,
while rgloader simply decodes it back to AST and executes.
So, using just a few quick and dirty hacks it is possible to get source back:
1) one-byte change in library to call external ruby_exic instead of ruby_exec:
----------------
$ cmp -l rgloader.linux.so.original rgloader.linux.so
4616 145 151
----------------
2) A bit patched ruby, ruby-1.8.6/eval.c to keep injected AST:
----------------
NODE *ruby_eval_hack;
int
ruby_exic(){
volatile NODE *tmp;
int state;
Init_stack((void*)&tmp);
ruby_eval_hack = ruby_eval_tree;
state = ruby_exec_internal();
return state;
}
----------------
3) Patch for RawParseTree in ParseTree-3.0.1/lib to retrieve sexp from
intercepted tree:
----------------
builder.prefix " #{extern_mode} NODE *ruby_eval_hack; "
builder.c %Q{
static VALUE parse_tree_full() {
VALUE result = rb_ary_new();
add_to_parse_tree(self, result, ruby_eval_hack, NULL);
return result;
}
----------------
4) And, finally, simple environment to get source code back from RubyEncoder:
----------------
require 'rubygems'
require 'parse_tree'
require 'ruby2ruby'
require 'encoded_script' # protected code, you say?
RawParseTree.new.parse_tree_full().each do |sexp|
puts Ruby2Ruby.new.process(Unifier.new.process(sexp))
end
----------------
Example:
Original:
----------------
class EncodedHelloWorld
ENCODER_VERSION = "1.0"
def initialize
puts "Hello, world!"
end
end
----------------
Encoded:
----------------
# RubyEncoder v1.0 evaluation
_d = _d0 = File.expand_path(File.dirname(__FILE__)); while 1 do _f =
_d + '/rgloader/loader.rb'; break if File.exist?(_f); _d1 =
File.dirname(_d); if _d1 == _d then raise "Ruby script '"+__FILE__+"'
is protected by RubyEncoder and requires the RubyEncoder loader.
Please visit the http://www.rubyencoder.com/loaders/ RubyEncoder site
to download the required loader and unpack it into '"+_d0+"/rgloader/'
directory to run this protected script."; break; else _d = _d1; end;
end; require _f;
RGLoader::load('AAEAAAAEaAAAAIAAAAAA/1nu5hlzvK93ynRezwoJSWaAXO0XWYMyqYojzdIsXeg/n3sTUToqkcdtx9wMbCcidZy4WpqIq2fj9tHsyREq8dCcvPsiWYISiwZ2jFHadIF3FhHZ9eLhZWJTZuRZDYG3Zk0nttbBzuP6EgAAAPgAAACl6rEqW0Dbrjuf0Nl2ehDd4mtpWkb9bP504YjdDfJj1ZM0tqmLXWMXpXnXL1kFNqoEfnws38xmo1J0E/Ziw4typ+51d572ijDg17Xz7NWj9xEykyN4uXEKn/Dt1mKExla1mnX4eAKxbnOJrNqZPDmpIJdOEqOO+/CLfQIGvvKYt11MIyTZK9I2R4J+/oNK2RGwbmzynpFKV32zxdILn4thrQx3gDLbD5ZPbfR6qWsmtJT6pyxccj7RtwGSat4BetCUKmcHR6b/qvp6rvPtaA1m/1JuuGNLUzg3tHHLkA/U14GfF4af9VyqtLQy5ww+jHB6wz4BkFe06gAAAAA=');
----------------
Output:
----------------
class EncodedHelloWorld
ENCODER_VERSION = "1.0"
def initialize
puts("Hello, world!")
end
end
----------------