How does this obfuscated JavaScript work?
Foreword: I beautified and annotated the code extensively at http://jsfiddle.net/WZXYr/2/
Consider the outermost layer:
eval(z = '...');
A code string is stored in the variable z
. The assignment operator returns the value assigned, so the code string also is passed as an argument into eval
.
The code string z
runs inside of eval
. The code is extremely obtuse, even when cleaned up, but it seems to:
- Parse a string of base-36 numbers, delineated by the character
4
. - Populate a map of values, using the global variables
e
,x
, andy
to hold map state. Map state is, in part, a function of the current second on the wall clock (new Date / 1e3
). - Using the map values, the code generates an output string,
p
- the code uses
p += " *#"[index]
to decide whether to use a space, asterisk, or hash mark, whereindex
is actuallye[x++] + e[x++]
(as said above,e
andx
are responsible for map state) - if the index is larger than the length of
" *#"
, there is fallback code that populates the outputp
with characters fromz
. Inner characters are populated with animation characters, while outer characters are pulled fromz
.
- the code uses
At the end of the code, there is a call to setTimeout(z)
, which asynchronously evaluates the code string z
. This repeat invocation of z
allows the code to loop.
Simple example:
Here's a super-simple version (http://jsfiddle.net/5QXn8/):
eval(z='p="<"+"pre>";for(i=0;i<172;++i)if(i > 62 && i < 67)p+="!---"[~~(new Date/1e2 + i)%4];else p += ("eval(z=\'" + z + "\')")[i];document.body.innerHTML = p;setTimeout(z)')
-
The
for
loop adds each character to the output stringp
(the string is 172 characters long):for(i=0;i<172;++i)
-
The inner conditional decides if we're on a character between position 62 to 67, which are the animated characters:
if(i > 62 && i < 67)
-
If we are, then print out
!---
, shifted based on the tenth of the second wall-clock value. This provides the animation effect.p+="!---"[~~(new Date/1e2 + i)%4]
(All the nastiness around
new Date
is really just there to transform a date value into a number between 0 and 3.) -
Otherwise, if we're not on an animated character, then print the index-
i
character from the string defined by"eval(z='" + z + "')"
That is, the code string
z
surrounded byeval('
and')
. -
Finally, output the string and use
setTimeout
to queue up another execution ofz
:document.body.innerHTML = p;setTimeout(z)
Note that my final output isn't quite right -- I haven't accounted for the backslashes toward the end -- but it should still give you a pretty good idea of how the technique works generally.
Here is the annotated source. Ps: I'm the author ;)
function z(){ // will be replaced with eval
p = "<" + "pre>"; // use <pre> tag for formatted output
for ( // loop though lines
y in n = ( // y - the line number
"zw24" + // n - the encoded data
"l6k4" + // every line holds encoded data
"e3t4" +
"jnt4" + // string will be concated in build process
"qj24" +
"xh2 4" + // data after spaces will be ignored but
"2kty24" + // … is used to not break block comments
"wrt4" + // … which will save some chars
"13n24" +
"3n9h24" +
"3pdxt4" +
"1csb 4" +
"3iyb6k4" +
"3pk724" +
"3nmr24"
).split(4) // data will be split by (unused) 4
){
for ( // loop throug every char in line
a in t = parseInt( // numbers are encoded as string
n[y], // … with a base of 36
36
) + ( // large number will be converted to string
e = // e - holds the rendered globe
x = // x - horizonal position
r = [] // r - bitmap flag if pixel is set
)
){
r = !r; // toggle binary flag
for ( // look though bitmap states
i = 0;
t[a] > i; // draw pixel t[a]-times
i += .05
)
with (Math) // refer to Math later
x -= .05,
0 > cos( // prevent backface visibility
o =
new Date / 1e3 + // get rotation based on current time
x / PI
) && (
e[ // access matrix
~~( // convert float to integer
sin(o) * // rotate around y axis
sin(.5 + y/7) *
32 // scale up the globe
) + 60 // move to center
] = -~r // store bitmap state in render matrix
)
}
for ( // loop through columns
x = 0;
122 > x; // break after char 122
) p += " *#"[ // add space, asterisk or hash
e[x++] + // … based pixel opacity
e[x++]
] || (S = ( // otherwise use the original code
"eval(z='" + // inception of missing "eval" statement
z
.split(B = "\\") // escape \ with \\
.join(B + B)
.split(Q = "'") // escape ' with \'
.join(B + Q) +
Q + // add missing ')
")////////" // add extra chars to fill mapping
)[
x / 2 + // get character at current position
61 * y-1
]
).fontcolor( // colorize outpu
/\w/.test(S) && // test for [0-9A-Z]
"#03B" // render blue
// otherwise pink (default)
);
document.body.innerHTML = // render output
p += // append new line
B + // add backspace
"\n"; // add new line
}
setTimeout(z) // render animation on next frame
}
z()
Here is another manually deobfuscated version, moving all initialisation out of expression into own statements:
z='p="<"+"pre>"/* ,.oq#+ ,._, */;for(y in n="zw24l6k\
4e3t4jnt4qj24xh2 x/* =<,m#F^ A W###q. */42kty24wrt413n243n\
9h243pdxt41csb yz/* #K q##H######Am */43iyb6k43pk7243nm\
r24".split(4)){/* dP cpq#q##########b, */for(a in t=pars\
eInt(n[y],36)+/* p##@###YG=[#######y */(e=x=r=[]))for\
(r=!r,i=0;t[a/* d#qg `*PWo##q#######D */]>i;i+=.05)wi\
th(Math)x-= /* aem1k.com Q###KWR#### W[ */.05,0>cos(o=\
new Date/1e3/* .Q#########Md#.###OP A@ , */+x/PI)&&(e[~\
~(32*sin(o)*/* , (W#####Xx######.P^ T % */sin(.5+y/7))\
+60] =-~ r);/* #y `^TqW####P###BP */for(x=0;122>\
x;)p+=" *#"/* b. OQ####x#K */[e[x++]+e[x++\
]]||(S=("eval"/* l `X#####D , */+"(z=\'"+z.spl\
it(B = "\\\\")./* G####B" # */join(B+B).split\
(Q="\'").join(B+Q/* VQBP` */)+Q+")//m1k")[x/2\
+61*y-1]).fontcolor/* TP */(/\\w/.test(S)&&"#\
03B");document.body.innerHTML=p+=B+"\\n"}setTimeout(z)';
p = "<" + "pre>";
n = ["zw2", "l6k", "e3t", "jnt", "qj2", "xh2 x/* =<,m#F^ A W###q. */", "2kty2", "wrt", "13n2", "3n9h2", "3pdxt", "1csb yz/* #K q##H######Am */", "3iyb6k", "3pk72", "3nmr2", ""]
for (y in n) {
e = [];
x = 0;
r = true;
t = parseInt(n[y], 36) + "";
for (a in t) {
r = !r
for (i = 0; i < t[a]; i += 0.05) {
x -= 0.05;
o = new Date / 1e3 + x / Math.PI
if (Math.cos(o) < 0)
e[~~(32 * Math.sin(o) * Math.sin(0.5 + y / 7)) + 60] = -~r;
}
for (x = 0; x < 122;) {
S = "eval" + "(z='" + z.split(B = "\\").join(B + B).split(Q = "'").join(B + Q) + Q + ")//m1k"
p += " *#"[e[x++] + e[x++]] || S[x/2+61*y-1]).fontcolor(/\w/.test(S[x/2+61*y-1]) && "#03B");
}
p += B + "\n";
document.body.innerHTML = p;
}
setTimeout(z)
Here is what happens:
-
z
is a multiline string containing all of the code. It iseval
ed. - At the end of the code,
z
is passed tosetTimeout
. It works likerequestAnimationFrame
andeval
together, evaluating it in an interval at the highest possible rate. - The code itself initialises
p
, the string buffer onto which the HTML will be appended, andn
, an array of base-36-encoded numbers (joined into a string by"4"
, the comments being irrelevant garbage that is not considered byparseInt
). - each number in
n
does encode one line (n.length == 16
). It is now enumerated. - A bunch of variables is initialised, some disguised as the
e
array literal but they are then cast to numbers (x
) or booleans (r
) or strings (t
) when used. - Each digit in the number
t
is enumerated, inverting the booleanr
each turn. For different anglesx
, and depending on the current timenew Date / 1000
(so that it gives an animation), the arraye
is filled using some bitwise operators - with1
whenr
is false and2
s whenr
is true at that time. - Then a loop does iterate the 61 columns of the image, from
x=0
to 122 in double steps, appending single characters top
. -
B
being the backslash, the stringS
is built from the code stringz
by escaping backslashes and apostrophes, to get an accurate representation of what it looked in the source. - Every two consecutive numbers from
e
are added and used to access a character from" *#"
, to build up the animated image. If one of the indices is not defined, theNaN
index resolves to an undefined character and instead the respective character from theS
string is taken (check out the formulax/2+61*y-1
). If that character should be a word character, it is colored differently using thefontcolor
String method. - After each line, the trailing backspace and a linebreak are added to
p
, and the HTML string gets assigned to the document body.
How the same effect could be rewritten for a minimal example?
Here is an other example:
setInterval(z='s=("setInterval(z=\'"+\
z.replace(/[\\\\\']/g,"\\\\$&")+"\')"\
).match(/.{1,37}/g).join("\\\\\\n");d\
ocument.body.innerHTML="<\\pre>"+s.sl\
ice(0, 175)+String( + new Date()).fon\
tcolor("red")+s.slice(188)')
It has all the releveant things you need for this kind of animation:
-
setInterval
andDate
for the animation -
A reconstruction of its own code (quine-like), in here:
s = ( "setInterval(z='" // the outer invokation + z.replace(/[\\\']/g,"\\$&") // the escaped version + "\')" ) // the end of the assignment .match(/.{1,37}/g).join("\\\n"); // chunked into lines
-
The output via
document.body.innerHTML
and a<pre>
element -
Replacing some parts of the code with the animated string
A string with the all the code is evaluated, and a timeout makes the loop;
The string is stored in a variable named z
and in the middle of the code, between comments /*
and */
there is an "Earth ASCII Art".
The code parses the comments and changes the document content, keeping the js and updating the art. Bellow is just the code sliced:
p="<pre>";
for(y in n="zw24l6k4e3t4jnt4qj24xh2 x42kty24wrt413n243n9h243pdxt41csb yz43iyb6k43pk7243nmr24".split(4)){
for(a in t = parseInt(n[y],36)+(e=x=r=[]))
for(r=!r,i=0;t[a]>i;i+=.05)
with(Math) x-= .05,0>cos(o=new Date/1e3+x/PI)&&(e[~~(32*sin(o)*sin(.5+y/7))+60] =-~ r);
for(x=0;122>x;) p += " *#"[e[x++]+e[x++\]] ||
(S=("eval"+"(z=\'"+z.split(B = "\\\\").join(B+B).split(Q="\'").join(B+Q)+Q+")//m1k")[x/2+61*y-1]).fontcolor(/\\w/.test(S)&&"#\03B");
p += B+"\\n"
document.body.innerHTML= p
}