Sunday, March 27, 2011

Finding the Answer (ab)using JavaScript

This is my little tribute to Douglas Adams (and also my first go at obfuscation):


vI=0x2A;(o=eval)((r=unescape)("%5F%5f%"+vI/+(+vI+'')[1]%6+"D%36%3b"))+o(r("%"+__+"1\
%6"+(ww=String["fromCharCode"])(69)+"%73%77%65%72%3D")+((~~[]+011>>!~~~(J=Math)[""]+
(Jan=isNaN)(+/\//[+[]*+-"1"]+""[-1])*~!-+(___=__/2)+2^!+!J['round'](![H=undefined]+1
+o((J+1).substr(!+(B=alert)+-!B+7<<.9-1,!/^/.$-~255>>(2*!o('('+(F='function(')+'ema\
cs){if((dreas=vI)>emacs){always=1}}(vI-5))')-~1*1-(+!(function(){o(arguments[__['co\
nstructor'].prototype*9]+"='"+arguments[0]+"'")}('$'+__))))*2)+"Magnum/* */"[!+''+7]
+".PI")^!H*!Jan(Jan)-+2+1))<<(!0x0-[010][dreas%2]/2+(4&4)+o("'.'+o(((1|__^4*2)+\"\"\
)[1])")<<+!+(c=NaN)<<1^~+!c*-___+(+J['floor'](~2)^-4))|/**/!![,]["le"+($$$=(parseInt
+2))[2]+"g"+$$$[4]+"h"]/**/*(o("o('~1+010+/*~*/~([][0,+$6[1]+2]-always|!{a:1}[0]+24\
<<1)%15<<~!J.ceil.call.apply(J.ceil,[1..toString()])+1')")+0xE^3<<1)>>+!o('('+F+'){\
_:if(!(_=!1)){return}}())')>>!null+_));o(r(["%42",(o+'')[13],answer,(r+'')[18],ww(59
)].                                   join                                    ('')))


If the layout screwed up, here's how it should look like:


Click here to run it.

Why?


tl;dr: it was fun.

Now basically, the code does 'nothing' except alert the number 42. Actually, the majority of the code is doing a calculation to come up with the number 42, and then alert it.

Removing a single character from the above code will either invalidate the output or invalidate the code itself with a syntax error (except from that redundant whitespace on the last line)...and that was actually quite tricky to do (and may not have fully achieved since it wasn't the plan from the beginning), but was part of the fun in creating this.

I was actually writing a script that would run through my code removing a single character each time and then running (eval) the script, but I didn't finish it because as it turns out, complicating shit can be pretty fun so I switched back to this.

But the major part of the fun was finding creative ways of complicating things. Yup, complicating things! It felt refreshingly good trying to, for a change, obscure the code as much as possible given that every character is needed for the ultimate outcome.

How?


For example, let's say I had a calculation which involved the number zero. Now, the way a sane programmer would represent a zero is by using the 0 literal representation. But that's obviously too boring for a project like this, so I would try and find new ways of representing the value of zero. An example would be something like +!!NaN, or maybe -{}>>1.

Of course, there's a reason why both those obscure representations evaluate to 0 and how my above code manages to compute and alert the number 42, and one way of finding out those reasons is by painstakingly analysing the spec.

If you don't have time to carefully go through those 252 pages, what you really need to grasp is JavaScript's type coercion rules due to weak typing...and then, abuse them mercilessly!

I'm not really going to get too much into JavaScript's type system in this post, but I will briefly explain why the above two examples I mentioned evaluate to 0.

Let's take -{}>>1 first. According to JavaScript's operator precedence rules, the unary negation sign is evaluated first, thus coercing {} into the special NaN value. Then the bitwise shift coerces the NaN value into a 0 (since NaN is falsy) and 0 >> 1 is 0.

Now for +!!NaN, starting with !NaN evaluating to the boolean value true because the logical not sign coerces the NaN value to the boolean value false, and the negation (logical-not) of false is true, so !!NaN evaluates to false because double negating false evaluates to true. Finally, since the + here is used as a unary operation and not binary, it coerces false to 0; and that's it!

Splitting it up


Up until the end, I was working on a single line with no whitespace but when I was sort of ready with obfuscating, I needed to split the lines up into some shape (remotely representing something)...a task which I figured to be trivial at best. But as I came to realize, splitting the code into multiple lines without introducing errors is far from trivial.

Reason being of course that you can't just split the line in whichever point you want because naturally keywords cannot be split and other restrictions of the language syntax restrict you from splitting certain constructs directly into multiple lines.

For example, say you have function(){return}, you can't for example split in the middle of the word function:
fun
ction(){return}

That will not compile, and neither will compile if you split the return keyword; you can split at any other part of the line though.

But I actually had the most trouble splitting property calls. Say you had to split [].constructor.prototype; now that's a bitch because of the two relatively long keywords in the expression.

How did I get around this? Simple. I switched from using dot-notation to subscript-notation, and since strings can be successfully split into multiple lines, it was much easier:

[]['constructor']['prototype']

Now if we wanted to split:

[]['construc\
tor']['prototype']

To split strings into multiple lines in JavaScript, just add the backslash symbol to the end of the unterminated string literal, and now, your splitting task is much easier.

Although using the subscript-notation made the lines easier to split, it introduced a new problem.

Say I have ['ab'] and I need to split between the ' and the a characters.

You can't split like this:

['
ab']

because now you have an unterminated string, which results in an error; but you also can't do this:

['\
ab']

because although it runs successfully, you have now added an extra character (\) to the previous line and that would invalidate your box-line shape of the code. And of course, can't also do this:

[
'ab']

because, as mentioned previously, although it runs, you have now removed a character from the previous line which would invalidate the box.

This one was a bit more annoying to deal with because, to my knowledge, there is no way around it...but still, this was all part of the challenge involved and it was fun.