Rather than always focusing on SERIOUS BUSINESS, I’d like share something a little more light hearted today. Whether you celebrate Christmas or not, I think you’ll find this little holiday themed hack a great deal of fun to play with.
Leah Neukirchen’s Christmas Hack
When I first started programming in Ruby, the ruby-talk mailing list was the best place to interact with the community and keep up with other active Ruby hackers. But because there were a lot more hobbyists in 2004 than there were people doing Ruby as a full time job, the posts focused on sharing fun hacks just as often as they did on discussing practical issues.
One of my favorites was Leah Neukirchen’s obfuscated Christmas message to the Ruby community in 2004. I’ve copied the source code below, and I encourage you to run it and see that it is indeed a valid Ruby program!
s="IyBUaGFua3MgZm9yIGxvb2tpbmcgYXQgbXkgY29kZ S4KIwojIENvcHlyaWdodCAoQykgMjAwMiAgQ2hyaXN0a WFuI E 5 l d Wtpc mNoZ W 4 gP G N obmV 1a2l y Y 2hlb k B nbWF pbC5 j b 20+CiM K I yBUa GlzI H Byb2dyYW 0 gaXM gZnJ l Z SBzb2Z0d2F y Z Tsge W91I G NhbiByZWRpc3 R yaWJ 1dGU g aXQgYW5kL29yCi M g bW9k aWZ5 I Gl0IHVuZGVyIHRoZ S B0ZX Jtcy B vZiB0aGUgR05VIEdlb m VyYW wgUH V ibGljIExpY 2 Vuc2 UuCg p T VERPVVQuc3lu Y y A9IH RydW U KZDEsIGQyID0gM C 4xNS wgMC 4 wNgpzID0gIk1lcnJ 5 IGNo cmlz d G1hcywgLi4uIGFuZCB h IGhh cHB5 I G5ldyB5ZWFyIgptID0gJ X d7LC AuID ogISArICogMCBPIEB9CnUg P SAiI CIgK i BzLnNpemUKCnByaW50ICJcci A gI3t 1fVx y IjsKCigwLi4ocy5z a XplL TEpK S 50b19hLnNvcnRfYnkg e yByY W5kI H 0uZWFjaCB7IHxyfAogIH N sZWV wIGQ x CiAgbmV4dCBpZiBzW3JdID 0 9ICI gIls wXQogIG0uZWFjaCB7IHxrfAo gICA gdVt y XSA9IGsKICAgIHByaW50ICIgIC N 7dX1 cciI KICAgIHNsZWVwIGQyCiAgfQogIHV bcl0 gPSB zW3JdCiAgcHJpbnQgIiAgI3t1fVxyI g p9Cg pzbG VlcCBkMgpwcmludCAiICAje3V9IVxyI jsKc 2xlZ X A gMwpwc m l udCA iICA j e3V9IS A g LS1j aHJp c z JcbiI7 C g ojIG ZpbG x lciBzc G F jZSA jIyM j I yMjIyM j I yMjI yMjI y M j I yMjI yMK";eval s.delete!(" \n").unpack("m*")## ### Copyright (C) 2004 Christian Neukirchen
When run, this code prints out “Merry christmas, … and a happy new year! –chris2” by randomly filling in each character in a little animation. After some folks commented on how cool this hack was, someone inevitably asked how it was done, which lead another Ruby hacker Michael Neumann to post his guess to the list. Here is what he said:
Pretty easy (except drawing the tree :). Write the source-code first, then
base64encode it, and insert newlines/whitespace to make the picture.
At the time, I was too much of a beginner with Ruby to fully appreciate the solution discussion, and mostly just chalked it up to magic. But now, the above statement is immediately obvious to me, and since it wasn’t further explained in the mailing list thread, I can give an example for those who are in the same shoes now that I was in a few years ago.
What I didn’t know at the time is that
Base64 is an encoding that allows you to translate any binary data into purely printable characters by converting the contents into a string of characters that uses basic alphanumeric values. I would have known that if I read the documentation for Ruby’s
Base64 standard library, but again, I was a newbie at the time. :)
It turns out that the idea for
Base64 encoding was extracted from how MIME attachments in email are implemented. This is all stuff you can find on wikipedia, so rather than digging into the gory details, let’s see how it relates to the problem at hand.
The following small snippet should clear things up a bit.
>> source = "puts 'hello world'" => "puts 'hello world'" >> encoded_source = Base64.encode64(source) => "cHV0cyAnaGVsbG8gd29ybGQn\n" >> Base64.decode64(encoded_source) => "puts 'hello world'" >> eval Base64.decode64(encoded_source) hello world => nil
Another way of decoding
Base64 encoded strings is via the
String#unpack method, using the template
"m*". You can see this in Leah’s code, which is what tipped Michael off in the first place. With that in mind, we can build a tiny obfuscated “Hello World” program.
s = "c H V0cyA n a G VsbG 8 g d 2 9 y bGQn" eval s.delete(" \n").unpack("m*")
In the end, Michael was right when he said this was pretty easy to do. As long
as you understand some basic string manipulation and how to decode a
encoded string, you could use this technique to render your code as pretty much any arbitrary ASCII art.
Of course, one would expect that the person who eventually would go on to create something as clever and useful as the Rack web server interface would have an extra trick or two up their sleeve. Not to disappoint, Leah confirmed Michael’s explanation was valid, but in the process revealed that they felt it’d be too fragile and tedious to manually format the code themselves into the desired ascii art.
For those curious about how they got around this problem, you can check out their full solution
which implements a code generator that fills in a template with the
base64 encoded source.
While the code should be pretty easy to follow with a little effort, feel free to post questions here if you need help figuring things out. It’s a really neat bit of code and is worth exploring, so I don’t mind giving some hints where needed.
Writing this article reminded me of two lessons that I sometimes forget, even to this day.
The first lesson is that you can’t judge the complexity of something by simply scratching its surface. When I saw this code posted to ruby-talk back in 2004, even though I was a newbie at the time, I could have figured it out if I only took a bit of time to study the topics that were being discussed. But since I saw a bunch of obscure binary data in the shape of a Christmas tree being passed to
eval(), I judged the snippet as being too complicated for me, appreciated it for its magic, and moved on. That sort of lack of self-confidence can really prevent you from stumbling upon interesting new ideas, tools, and techniques.
The second lesson is that hacking doesn’t always have to be SERIOUS BUSINESS. Because I’m working on things I feel are super important most of the time, it’s easy for me to forget to be playful and generally curious. Sometimes I feel like I’m too busy to do something just for the joy of the hack, and that worries me a bit. Writing this article reminded that I should resist this temptation, and make more time and space in my life for playful discovery, because it is a great way to learn and have fun at the same time.
NOTE: This article has also been published on the Ruby Best Practices blog. There may be additional commentary over there worth taking a look at.
Practicing Ruby is proudly independent, open source, and advertising-free.
This is a 100% reader-funded, reader-focused project that needs your support.