25 янв. 2011 г.

Обфускация JavaScript


В статье собраны всем известные методы и предельно извращенные. Эту статью я решил написать после недавнего прочтения поста в блоге Badass JavaScript и решил её дополнить своими находками.




Первый способ


Он всем известен — обфускация минимизаторами такими как JS Packer, JSmin, YUI Compressor, Closure compiler или можно просто пугуглить «JavaScript Obfuscator» и найдется ещё сто штук разных обфускаторов.

Они превращают существующий код

function MyClass(){
    this.foo = function(argument1, argument2){

        var addedArgs = parseInt(argument1)+parseInt(argument2);
        return addedArgs;
    }
    var anonymousInnerFunction = function(){
        // do stuff here!

    }
}


В какой-то такой вид:

function MyClass(){this.foo=function(c,b){var d=parseInt©+parseInt(b);return d};var a=function(){}};



Или такой:

var _0xd799=["\x66\x6F\x6F"];function MyClass(){this[_0xd799[0]]=function (_0xefcax2,_0xefcax3){var _0xefcax4=parseInt(_0xefcax2)+parseInt(_0xefcax3);return _0xefcax4;} ;var _0xefcax5=function (){} ;} ;



Или вот такой:

eval(function(p,a,c,k,e,d){e=function©{return c};if(!''.replace(/^/,String)){while(c--){d[c]=k[c]||c}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e©+'\\b','g'),k[c])}}return p}('4 0="3 5!";9 2(1){6(1+"\\7"+0)}2("8");',10,10,'a|msg|MsgBox|Hello|var|World|alert|n|OK|function'.split('|'),0,{}))



Но ничего не стоит его восстановить с помощью jsbeautifier.org либо просто убрать eval и получить исходный код, многое потеряем, но смысл кода восстановим. Ну и с первого взгляда мы видим, что перед нами JavaScript.



Все это были цветочки под катом жесткие методы обфускации.



Способ второй



Изменение кода до неузнаваемости, который превратит наш мизерный скрипт:

alert(0)



В такой Braifuck-подобный вид:


([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]()[(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]])(+[])



Или вот в такой (код может не работать ибо хабрапарсер):

゚ω゚ノ= /`m´)ノ ~┻━┻ //*´∇`*/ ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');



Тут с первого раза и не скажешь, что перед нами работающий JavaScript.

Тулза делающая код вида один, код вида два.



Объяснение некоторых моментов способа два



Пример:

($=[$=[]][(__=!$+$)[_=-~-~-~$]+({}+$)[_/_]+
($$=($_=!''+$)[_/_]+$_[+$])])()[__[_/_]+__
[_+~$]+$_[_]+$$](_/_)


Имеем какое-то месево символов, но на самом деле в нем скрывается alert(1), но ведь там нет ни одной строки и ни одного символа, откуда это?!



Будем разбираться:

$=[] это пустой массив


$=[$=[]] это массив с ссылкой на массив

В переменной $ будет 0.

Теперь у нас есть 0 и мы можем ссылаться. Делаем __ = "false" через (__ = !$ + $ )

Далее _ = -~-~-~$

Оператор ~ в JavaScript означает -(N+1) поэтому -~ = +1


Если $ = 0 тогда -~-~-~$ = 3

Получаем: _ = 3



Таким образом _/_ = 3/3 = 1



(__ = !$ + $ )[ _ = -~-~-~$]

("false")[_]


("false")[3]

"false"[3] = s




({} + $)[_/_]

(" object")[_/_]

(" object")[1]

" object"[1] = o




$$ = ( $_ = !'' + $)[_/_]

$$ = ( "true")[1]


"true"[1] = r


$_[+$] = "true"[0] = t



$_ = "true"null

$$ = rt




($$ = ( $_ = !'' + $)[_/_] + $_[+$] ))



!'' = "true"

$_ = (true)


$_[1] = r

$_[0] = t

$$ = rt




Таким образом первая строка станет ($ = [] ["s" + "o"+ "r"+ "t" ] )()

($=[]["sort"])()



Идем дельше

[__[_/_]+__[_+~$]+$_[_]+$$](_/_)




$ = 0

_ = 3

__ = "false"

$_ = "true"

$$ = "rt"




Строка превращается…

[__[_/_]+__[_+~$]+$_[_]+$$](_/_)




Превращается…

[__[1] + __[3 + -1] + $_[3] + $$)(1);



Превращается…

["false"[1] + "false"[3 + -1 ] + "true"[3] + "rt"] (1)



Превращается в

[ "a" + "l" + "e" + "r" + "t" ](1)



В конце мы получаем ($=[]["sort"])()["alert"](1)




Разобьём по частям

a = [] // Создает массив

b = a["sort"] // Получает ссылку на метод sort

c = b() // Выполяет метод вне контекста массива для получения ссылки на window

d = c["alert"] // Получает ссылку на window.alert

d(1) // Выполняет window.alert с аргументом 1




Итог: window["alert"](1)




Это, конечно, искусственный пример и никакой из обфускаторов так не сможет.



Способ третий



Первый способ делал код похожим на JavaScript, второй совершенно не похожим, а третий сделает код вообще невидимым.



Готовых решений не видел, концепт, подсмотренный с одного из видео JS конференций.

Код будет состоять из двух частей: видимая честь — можно использовать что-нибудь описанное выше для её обфускации и невидимая часть.

Если с видимой все ясно, то секрет невидимой вот в чем: Имеющийся «плохой код» (иначе зачем его прятать) мы пропускаем через наш обфускатор-изчезатор, который превращает видимый скрипт в невидимый т.е. в строку состоящую из знаков табуляции (бит 1) и пробелов (бит 0).

В итоге мы получим в 8 раз больше кода чем имели. Видимая часть будет декодировать невидимую часть и её выполнять: декодирует биты в число, а число переделает в символ String.fromCharCode() ну а дальше eval




В конце получается что-то такое (невидимую часть не обязательно скрывать в элементе)

decodeAndEval(document.getElementById("evilCode").innerHTML);

<div id="evilCode">



</div>





По мотивам: badassjs.com/post/2929065287/obfuscation adamcecc.blogspot.com/2011/01/javascript.html




Буду рад ответить на ваши вопросы и увидеть способы обфускации известные вам.



PS Представленный код может не работать, ибо хабрапарсер©, ссылки на тулзы имеются — в случае чего сделайте свой код.