Obfuscating Ruby Code.

L

Lothar Scholz

Hello Gregory,

GM> Received: Wed, 2 Jun 2004 21:42:14 +0900

GM> Why even bother modifying the ruby source?

GM> Create a C extension that defines a "myrequire", or redefines
GM> require so that it loads the provided ruby script after passing it
GM> through your proprietary decrypting code. Then call ruby with ruby
GM> -r mydecrypter.so -e "myrequire 'main.crb'" (crb = encrypted
GM> ruby?). The C extension doesn't need to be GPL, as it's not a part
GM> of the ruby interpreter, just a compiled .so that's loaded at run
GM> time. No need to provide the code for that.

At some time you must call into the ruby.dll/ruby.so and pass the
source code. So i could add a dump routine into the "rb_require" or the lower
level "rb_compile_string" (which converts the source code into the syntax
tree) in the ruby.dll and get all the code. I must only replace the
given ruby.dll with my patched one and get the same result.

I provide my own patched ruby distribution with my IDE, so i know how
easy it is to do so - even under windows. Linux may be much more easy.
I estimate 30 min to crack your code.
 
G

Gregory Millam

Received: Wed, 2 Jun 2004 15:23:08 +0200
I provide my own patched ruby distribution with my IDE, so i know how
easy it is to do so - even under windows. Linux may be much more easy.
I estimate 30 min to crack your code.

30? Padding your estimate a bit, aren't you?

And - as if any of the other solutions take longer? Obfuscated code is nothing - try being a labbie reading college student code submissions.

You can't stop a knowledgable geek. What I imagine we're trying to stop is script kiddies.

- Greg
 
D

Daniel Berger

Ken Hilton said:
Does anyone know of a Ruby source code obfuscator that's reliable and
readily available? If so, I'd appreciate a pointer to it (apparently, both
ruby-lang and rubygarden are down at the time of this posting so I can't
check for myself.)

Thanks in advance,

Ken.

This obfuscated enough?

# foo.rb
class Foo
attr_accessor :arg1, :arg2
def initialize(arg1,arg2)
@arg1, @arg2 = arg1, arg2
end
def test
puts "Arg1 is #{@arg1}"
puts "Arg2 is #{@arg2}"
end
end

f = Foo.new("hello","world")
f.test

# cfoo.rb
#(foo.rb after being encrypted with crypt-fog and a salt of 44)
\217\230\215\237\237Lr\233\2336LLL\215\240\240\236\213\215\217\217\221\237\237\233\236Lf\215\236\223]XLf\215\236\223^
6LLL\220\221\222L\225\232\225\240\225\215\230\225\246\221T\215\236\223]X\215\236\223^U6LLLLLLl\215\236\223]XLl\215\23
6\223^LiL\215\236\223]XL\215\236\223^6LLL\221\232\2206LLL\220\221\222L\240\221\237\2406LLLLLL\234\241\240\237LNm\236\
223]L\225\237LO\247l\215\236\223]\251N6LLLLLL\234\241\240\237LNm\236\223^L\225\237LO\247l\215\236\223^\251N6LLL\221\2
32\2206\221\232\22066\222LiLr\233\233Z\232\221\243TN\224\221\230\230\233NXN\243\233\236\230\220NU6\222Z\240\221\237\2
40

# Running the code
ruby -e 'require "crypt/fog";eval(Crypt::Fog.decrypt(IO.readlines("cfoo.rb").to_s.chomp,44))'

# Result
Arg1 is hello
Arg2 is world

Regards,

Dan
 
D

David A. Black

Hi --

Ken Hilton said:
Does anyone know of a Ruby source code obfuscator that's reliable and
readily available? If so, I'd appreciate a pointer to it (apparently, both
ruby-lang and rubygarden are down at the time of this posting so I can't
check for myself.)

Thanks in advance,

Ken.

This obfuscated enough?

# foo.rb
class Foo
attr_accessor :arg1, :arg2
def initialize(arg1,arg2)
@arg1, @arg2 = arg1, arg2
end
def test
puts "Arg1 is #{@arg1}"
puts "Arg2 is #{@arg2}"
end
end

f = Foo.new("hello","world")
f.test

# cfoo.rb
#(foo.rb after being encrypted with crypt-fog and a salt of 44)
\217\230\215\237\237Lr\233\2336LLL\215\240\240\236\213\215\217\217\221\237\237\233\236Lf\215\236\223]XLf\215\236\223^
6LLL\220\221\222L\225\232\225\240\225\215\230\225\246\221T\215\236\223]X\215\236\223^U6LLLLLLl\215\236\223]XLl\215\23
6\223^LiL\215\236\223]XL\215\236\223^6LLL\221\232\2206LLL\220\221\222L\240\221\237\2406LLLLLL\234\241\240\237LNm\236\
223]L\225\237LO\247l\215\236\223]\251N6LLLLLL\234\241\240\237LNm\236\223^L\225\237LO\247l\215\236\223^\251N6LLL\221\2
32\2206\221\232\22066\222LiLr\233\233Z\232\221\243TN\224\221\230\230\233NXN\243\233\236\230\220NU6\222Z\240\221\237\2
40

# Running the code
ruby -e 'require "crypt/fog";eval(Crypt::Fog.decrypt(IO.readlines("cfoo.rb").to_s.chomp,44))'

# Result
Arg1 is hello
Arg2 is world

This is probably a stupid question, but... what if someone did:

ruby -e 'require "crypt/fog"; puts eval(Crypt::Fog.decrypt(......)'

?


David
 
D

David A. Black

This is probably a stupid question, but... what if someone did:

ruby -e 'require "crypt/fog"; puts eval(Crypt::Fog.decrypt(......)'

Sorry, I meant:

puts Crypt::Fog.decrypt....


David
 
D

Daniel Berger

eval(Crypt::Fog.decrypt(......)'

Well, you happen to know the method and the salt with which to decrypt
the example I gave you. Besides, the OP only hasked for obfuscation,
not encryption.

Let's have a little fun. I'll give you some obfuscated code and you
tell me how long it takes you to decipher it.

\255\257\244\265\254\245`\215\257\266\251\245J```\243\254\241\263\263`\203\250\262\251\263\264\255\241\263\223\264\257
\262\271J``````\244\245\246`\263\245\243\262\245\264\237\255\245\263\263\241\247\245J`````````\260\265\264\263`b\202\2
45`\263\265\262\245`\264\257`\244\262\251\256\253`\271\257\265\262`\217\266\241\254\264\251\256\245bJ``````\245\256\24
4J```\245\256\244J\245\256\244JJ\243\263`}`\215\257\266\251\245zz\203\250\262\251\263\264\255\241\263\223\264\257\262\
271n\256\245\267J\243\263n\263\245\243\262\245\264\237\255\245\263\263\241\247\245

Regards,

Dan

PS - Attempts to print the results to a terminal using a brute force
approach may cause your terminal to freeze (though not because of my
code). The code itself is harmless. For example:

str = "foo"

1.upto(100){ |x|
puts str.unpack("C*").map{ |e| e -= x }.pack("C*")
}

This causes my terminal to go wonky, though I'm using CDE on Solaris
9. YMMV.
 
B

Bill Kelly

Hi,

From: "Daniel Berger said:
Let's have a little fun. I'll give you some obfuscated code and you
tell me how long it takes you to decipher it.

\255\257\244\265\254\245`\215\257\266\251\245J```\243\254\241\263
\263`\203\250\262\251\263\264\255\241\263\223\264\257
[...]

Hehe... See, that's cheating. <grin> Contest rules should
require your program to actually run! We can all distribute
un-runnable encrypted binaries that no-one can reasonably
decipher. What I mean by "actually run" is the program needs
to be in a form where we can type its name from the command
line, and the program has to be responsible for decrypting
itself, and running. Because that is the problem we're dealing
with. If you were to present your program in that manner, I'd
be happy to spend a couple minutes extracting the unencrypted
source.

Unless I'm missing your point...? If so, apologies!


Regards,

Bill
 
D

David A. Black

Hi --

eval(Crypt::Fog.decrypt(......)'

Well, you happen to know the method and the salt with which to decrypt
the example I gave you. Besides, the OP only hasked for obfuscation,
not encryption.

I'm assuming that if people running the program know that they have to
use crypt/fog, then so will the rogue people who want to decipher it.
I'm also (perhaps wrongly) assuming that salts are all two characters.

Anyway, didn't I say it was a stupid question? :)
Let's have a little fun. I'll give you some obfuscated code and you
tell me how long it takes you to decipher it.

[...]

No fair -- RAA is down and I don't have Crypt::Fog :)


David
 
P

Philipp Kern

ruby -e 'require "crypt/fog";eval(Crypt::Fog.decrypt(IO.readlines("cfoo.rb").to_s.chomp,44))'

And how do you obfuscate the salt? Because that's the problem we try to deal
with. As soon as you call the decrypter you need the password available.

Bye,
phil
 
M

Michael Campbell

Well, you happen to know the method and the salt with which to decrypt
the example I gave you. Besides, the OP only hasked for obfuscation,
not encryption.'

Where does obfuscation stop and encryption begin?
 
R

rolo

David said:
I think (for this purpose anyway) encryption is one form of
obfuscation.
I would refer to code that is 'difficult to read' by humans but is 'normal'
for the machine as obfuscation while it is impossible to read by both human
and machine without a key is encryption.

David may be right technically.

rolo
 
D

Daniel Berger

Philipp Kern said:
And how do you obfuscate the salt? Because that's the problem we try to deal
with. As soon as you call the decrypter you need the password available.

Bye,
phil

1) Put above code in file
2) chown root file
3) chmod 700 file

Regards,

Dan
 
C

Carl Youngblood

Daniel said:
1) Put above code in file
2) chown root file
3) chmod 700 file
So does this mean that he should ask his client, to whom he is selling
the closed-source ruby software, for root access to their servers?
 
K

Ken Hilton

Wow - what a great thread - so many ideas and points of view. This is what
I appreciate most about the Ruby community: everyone comes to the e-table
with an open mind and an opinion. So, now that the dust has settled, allow
me to weigh in. Clearly, a hacker-proof solution is problematic, not just
specifically WRT Ruby but in general (despite Macrovision's claims to the
contrary.) In my case, the objective is obfuscation, not piracy protection
(via encryption ) as the work I'm doing is generally of little use to
parties other than my clients. That said, I'd like to keep my clients
honest with respect to their contracts and to protect certain technologies
from being unreasonably pilfered and obfuscation more or less satisfies the
80/20 rule for this purpose: it poses a reasonable barrier to misuse and
appears to meet a minimal standard for trade secret/IP protection (i.e.,
should someone exploit such code their intent would be undeniable; intent
generally being very hard to prove.) If those who support the idea of a
Ruby obfuscator continue to think such a utility would be of some value, I'd
be interested in collaborating to develop a solution that would be of wide
appeal.

Sincere Regards,

Ken.
 
T

Tyler Zesiger

Lothar said:
Hello Bill,


BK> I imagine the obfuscation would include translation of once-
BK> meaningful identifiers (variable names, method names, ...)
BK> into meaningless gibberish. A lexer would not help much there.

BK> Such an obfuscator sounds really tough to write for Ruby
BK> though... I presume the string form of eval() would have
BK> to be disallowed from referring to any code subject to
BK> obfuscation...

BK> I would be very interested in a working Ruby code obfuscator,
BK> even given certain restrictions like that. I'm writing an
BK> application in Ruby that will likely be heavily pirated, like
BK> a video game, as soon as a cracked copy of it becomes
BK> available. As with video games, which i used to do for about
BK> 9 years professionally, we're just trying to delay the time it
BK> takes for someone to produce a cracked version. At least,
BK> I'm presuming anyone needing a Ruby code obfuscator is probably
BK> coming from the same situation. That's why I'd like one anyway.
BK> I'm developing in Ruby because I want to add features faster
BK> than the competition. (Not to mention how much fun programming
BK> in Ruby is. :) But I've gone into writing this app in Ruby
BK> knowing I'm going to need *some* solution to delaying the
BK> (inevitable) appearance of a cracked version of my app. So I'm
BK> very glad to hear Ken is working on such a technology, and
BK> grateful he'd be willing to share it.


I would also like to see something like this.
But to be true, it wouldn't help very much. The only real protection
system is to put the ruby source code under a BSD license so that you
can change the code and add some private decryption code inside the
"rb_compile_cstr" function. Of couse this can't be open source.
Remember that even a bytecode wouldn't help that much, as you can see
in decompiled Python/Java code. It's very readable.

So my request is up going to matz directly if he want's to change
the license for the ruby interpreter, allowing ruby to be more useable in
some kind of commerical applications.


Actually, here's a license that just might fit the bill - http://www.zesiger.com/license/ - It doesn't require the source to be opened until 2 years after the first commercial sale. It's better than just closing the source completely and forgetting about it.
 
D

David Garamond

Daniel said:
1) Put above code in file
2) chown root file
3) chmod 700 file

Sorry, but this is getting sillier and siller :)

The original problem is to obfuscate Ruby code against end users, which
will have full control to the application, their machine, and the whole
filesystem.

For the above to work, you'll have to distribute your software as in
ASP/rented fashion, not shrink-wrapped. In this case, you don't need
obfuscation anyway.
 
K

Ken Hilton

Great minds think alike. Coincidentally, my wife came to the same
conclusion last night, and she's not even in the industry.
 
J

Jim Moy

...In my case, the objective is obfuscation, not piracy protection
(via encryption ) as the work I'm doing is generally of little use to
parties other than my clients. That said, I'd like to keep my clients
honest with respect to their contracts and to protect certain technologies
from being unreasonably pilfered and obfuscation more or less satisfies the
80/20 rule for this purpose: it poses a reasonable barrier to misuse and

If that's the goal, why not use exerb with the ZLib option turned on?
The resulting binaries can't be grepped for source.

Sure, all someone would need to do is a little reverse engineering on
exerb to figure out how to extract the source, or simply have memorized
what a ZLib header looks like, but seems to me it's a "reasonable
barrier" for the purpose you're describing.

That said, it'd still be a great thing to have a general, more secure
way of securing ruby source. I'd like to be able to take advantage of
it as well.

Jim Moy
 
W

Will Drewry

Just for a little bit of fun, I wrote a dumb obfuscator. It takes a ruby script
and converts it to a C integer array with the ASCII values increased by
array size and the letter's index in the array. This is then included in a C
source file. This then converts the code back to a char array in dynamically
allocated memory and evaluates it with rb_eval_string_protect.

Here's what it looks like:

$ ls
code.rb foo.c to_cary

# We want to obfuscate code.rb.
$ cat code.rb
require 'openssl'
puts "obfuscated ruby - woohoo!"
puts "Here's a hash:"
foo = OpenSSL::Digest::SHA1.new
foo.update("obfuscate me baby")
puts foo


$ cat to_cary
#!/usr/bin/ruby -w

def file2cary(path)
header = "static int code[] = { "
footer = " };\n"
body = ''
me = 0

body << header
file = File.read(path)
file.each do |line|
line.split('').each do |chr|
body << "#{chr[0]-(file.size+me)}, "
me += 1
end
end
body << footer
body << "#define RUBY_CODE_SIZE #{file.size}\n"
return body
end

puts file2cary(ARGV[0])

$ ./to_cary code.rb > code.h
$ cat code.h

static int code[] = { -32, -46, -35, -32, -45, -37, -51, -121, -115,
-44, -44, -56, -48, -44, -45, -53, -123, -153, -52, -48, -50, -52,
-136, -135, -59, -73, -70, -56, -59, -76, -79, -61, -77, -79, -148,
-67, -65, -85, -63, -153, -141, -155, -69, -78, -79, -87, -81, -82,
-161, -161, -186, -85, -81, -83, -85, -169, -168, -131, -103, -91,
-105, -168, -93, -177, -113, -179, -108, -116, -99, -111, -158, -183,
-208, -117, -109, -110, -190, -162, -192, -146, -114, -126, -118,
-146, -147, -155, -174, -175, -166, -130, -133, -136, -123, -123,
-182, -183, -159, -171, -179, -196, -200, -137, -147, -130, -240,
-149, -141, -142, -208, -138, -144, -157, -161, -143, -159, -221,
-228, -152, -166, -163, -149, -152, -169, -172, -154, -170, -240,
-164, -173, -243, -178, -180, -180, -158, -246, -240, -272, -171,
-167, -169, -171, -255, -186, -178, -179, -281, };
#define RUBY_CODE_SIZE 146

$ cat foo.c

/*
* debian-build: gcc -I/usr/lib/ruby/1.8/i386-linux
-L/usr/lib/ruby/1.8/i386-linux -O2 foo.c -o foo -lruby1.8
* redhat-build: gcc -I/usr/lib/ruby/1.8/i686-linux-gnu
-L/usr/lib/ruby/1.8/i686-linux-gnu -O2 foo.c -o foo -lruby
*/

#include "ruby.h"
RUBY_EXTERN VALUE ruby_errinfo;

#include "code.h"

int main() {
int state=0, i;
char *real_code;

real_code = (char *)malloc(sizeof(char[RUBY_CODE_SIZE]));
for(i=0;i<RUBY_CODE_SIZE;i++) {
real_code = (char)(code+(RUBY_CODE_SIZE+i));
}
ruby_init();
ruby_init_loadpath();
ruby_script("my_cool_code");
rb_eval_string_protect(real_code, &state);
if (state) {
rb_p(ruby_errinfo);
}
free(real_code);
return state;
}


$ gcc -I/usr/lib/ruby/1.8/i686-linux-gnu
-L/usr/lib/ruby/1.8/i686-linux-gnu -O2 foo.c -o foo -lruby

$ strip -s foo

$ ./foo
obfuscated ruby - woohoo!
Here's a hash:
4fd04f3b648e92d2356c2ee577c2c2ff523bbee4

# end of fake shell experience

Now if you 'hexedit' the executable, any visible ascii will look like
gibberish. What's cool is that you can fiddle with the to_cary script
to use a custom "obfuscation algorithm" for your program. This should
deter the average code prodder. Anyone who pokes around with the
runtime heap memory will get the script they were after though. This
also doesn't help much if your code is in a lot of separate files. It
all really depends on how much code is getting obfuscated, and if you
can write a build process to stick it all in one ruby file. There are
no doubt much better ideas, but this was a fun experiment.

I'm pretty sure you can write the above code, compile it and sell it
without having to release it GPL since it isn't compiled into Ruby.
You should ask a lawyer. If it's true, however, then I hereby release
this code into the public domain ;-)

Good luck!
/wad
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top