您的位置:首页 > Web前端 > JavaScript

酷壳上的一段js代码分析

2011-02-01 17:02 295 查看
From Cool Shell I saw"一段javascript代码", a confused code snipped, originates from here blackhat .
($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_)

the author of Cool Shell had analyzed it. but the analysis is fairly rough, besides, it's incorrect at the last part of it, as what @Atry pointed out at the comment part of the article. here i'm gonna analyze it as well.
firstly, I substitute variable names in order to make them easy to read, as this:
(a=[a=[]][(d=!a+a)[e=-~-~-~a]+({}+a)[e/e]+
(b=(c=!''+a)[e/e]+c[+a])])()[d[e/e]+d
[e+~a]+c[e]+b](e/e)


outline its structure as this
(a=[①][②])()[③]()
, i found there are roughly 3 exp that are fairly complex.
but first thing first , let's look at the sequence of how the exp get evaluated. thanks for my colleage 陈超 to remind me this problem.

basically exp were evaluated from left to right by using a stack. so the sequence of evaluate this exp is approximately ① -> ② -> a=[①][②] ->()() -> [③]. etc(some are omitted). so be clear that ③ exp is evaluated only after the exp precede it evaluated, in this particular example, notice the variable a 's value.

①:
a=[]

a is assigned as empty array; at this point, pay highly attention to the outermost a in exp
(a=[a=[]][...])
,this outermost
a
value can only be judged after evaluation of exp [a=[]][②], as far as the assignment of exp ①, a is [], notice the priority of exp

② : ② is really a complex exp

(d=!a + a)
: a is [], ! op needs a boolean object as operand, so it's boolean context, from !a we get false boolean object, notice any object implicitly convert to true in boolean context. for the 2nd a, the toString() method of a get called, what we get is a empty string, combined with + op make the preceded boolean object false convert to string , so the whole exp is a string "false".

e=-~-~-~a
, ~a, ~'s operand need to be a number, so operand
a
should convert to a number value implicitly. An object's converting to number depends on valueOf() or toString(), what we get is a number or NaN. see example below

console.log(Number([])); //0
console.log(Number([1]);//1
console.log(Number([1,2]); //NaN
console.log(parseInt([1,2])) //1
console.log(~[]); //-1

js integer is a 32bit int, tilde 0 convert all bit 0 to 1, the sign bit is 1, so js compiler would regard it as negative number and it value, as complementary code, we can know it's -1. so
~a ->-1, -~a -> 1, ~1 -> -2, ~2 -> -3, -(-3) -> 3;
, so
(d=!a+a)[e=-~-~-~a] -> "false"[3] -> "s";


Next piece of code in ② :
({}+a)[e/e]
:{} and a both in + exp both convert to string then do concatenation. so it's string "[object Object]". e is 3, so ("[object Object]")[1] is "o";

According to analysis above , we have:
b=(c=!''+ a)[e/e]+c[+a]
: !'' -> boolean object true, ! operand needs to be boolean, boolean context; c=!''+a => "true", a is empty array, we get empty string, c is string "true"; +[] -> 0, Number context, we get 0, similar to +"",so that,
("true")[1] + "true"[0] -> "rt"


so ② :
[(d=!a+a)[e=-~-~-~a]+({}+a)[e/e]+ b=(c=!''+a)[e/e]+c[+a])]
->
["s" + "o" + "rt"] ->["sort"]


being at this phase. js compiler start to try to evaluate
(a=[a=[]["sort"])
, and then
(a=[a=[]["sort"])()
. the sequence contains 2 steps here. first we assign the Array.prototype.sort method to
a
as property of window. then call that method using
a
rather than a array object.
in js,through what reference to call a method is very important to the result. the object that own the reference(as its property) become the this value in that function.so that js function invocation is really sending message between 2 objects(the object that own the reference and the function object). the function, as a method who provides the behavior, and the caller provides the identity meaning who perform the action.the result could vary from one to another calling objects. see below example
var a = Object.prototype.toString;
console.log(Object.prototype.toString()); // [object Object]
console.log(a()); //[object Window]; a is a property of window. this in method refer to window

we call toString() binded to Object.prototype object by using 2 different ref, one is the original ref, the other is as the window's property, we get different output. it's all due to the
this
value!
sort method return
this
, so when call it as a property of window, what get returned is window. notice that at this moment, variable
a
become the sort method rather than a empty array;

part ③:
d[e/e]+d[e+~a]+c[e]+b
. according to analysis before ③ become:
["false"[1] + "false"[3-1] + "true"[3] + "rt" ](1)
, notice that,
~a
, converting a function object to number, we get NaN, ~NaN happen to be same as ~0, we get -1;

so the whole exp:
(a=[a=[]]["sort"])()["alert"](1)
->
window["alert"](1);


when i took a first glance at this code snipped and get to know it 's a alet(1), i really feel astonished. how can it done without the characters alert's emerging? but after this analysis, i found it's nothing special, but of course we shouldn't coding like this unless we purposely do some confusing work.
thought it's nothing weired. still i think there are several points that i should take as notes, which really broaden my horizon. before seeing this code, at least i never thought code can be written like this.

so finally summery the skills used in this example:
one skill used in this code snipped is to get string through implicit cast, in order to do this, we need to be very familiar to some characteristic of some certain operator and its operand, as well as some objects. we abstract some certain char in a string, then piece them up.
the other point with a generic meaning is how to get the window object in this sort of confused code.as long as we get the window, we can do whatever we want with the DOM api. unlike the way we get properties of some object, (we can piece them up using the associated-array-alike form, in which way we can do arithmetic on the string representing the property) we cant piece up characters to get the reference to window. in code above we get the window reference by invocation to a function, at this partically case, sort().
finally , we should pay highly attention to th sequence of evaluation to a exp.to know its correct order of evaluating is first thing first.
list points as below:

~a , +a
: arithmetic op, number context , convert it to number:

!a , !''
: logical op, boolean context; convert to boolean object

a = ![] + [];
: object 's toString(), boolean object + "" get a string;

(a=[].sort)()
: get window. to get window has a generic meaning in confuse code work.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: