Pasek boczny
Konto
Konto
Szukaj
Szukaj
Ustawienia
Ustawienia
Szukaj

Szukaj w:



Zaawansowane wyszukiwanie
Ustawienia
Przełącz na wersję mobilną
Motyw
Język
Portale internetowe Kissdigital.pl - rozwiązania mobilne Kofeina sklep
Projekt, budowa i promocja - budujemy kompleksowo portale internetowe w oparciu o Drupal. Zaawansowane aplikacje mobilne - bogate portfolio. Dostarczamy czystą kofeinę, z której możesz sam przygotować "energetyka" takiego jak lubisz.




[JavaScript] Ukrywanie ciała funkcji
  • 0 Głosów - 0 Średnio
  • 1
  • 2
  • 3
  • 4
  • 5
Subskrybuj ten temat
Comandeer
Przyjaciel
Offline

Reputacja: 0

 0     0    
1   04-09-2012   20:45   
Na wstępie uprzedzę, że technika ta nie powstała w mym płodnym umyśle 2 Została znaleziona przez

Goście nie widzą linków. Zarejestruj się na forum klikając tutaj.


w czasie debugowania pewnego upierdliwego błędu w działaniu Google Maps w tejże przeglądarce. Jak nie trudno zgadnąć, zostało to w bólach znalezione w źródle tego zaawansowanego webappa. Ja jedynie postanowiłem to wykorzystać na szerszą skalę 3
Trzeba też zaznaczyć, że technika ta jest średnio przydatna jeśli nie zaciemnimy równocześnie źródła skryptu (np. poprzez uglify.js czy też GCC).
O co wgl chodzi? A no chodzi o pewną magiczną właściwość poniższego fragmentu kodu:
Kod:
Function.prototype
Niby co w tym takiego nadzwyczajnego? Już mówię!
Kod:
console.log(Function.prototype.call.apply(Function.prototype.bind,[function(){}]);
Zapewne w większości powyższy kod wzbudza podświadomy lęk 2 Przyznać trzeba, że naprawdę wygląda dość dziwacznie. Ale postaram się troszkę przybliżyć jego sens.
Zacznijmy od Function.prototype. Jak już kiedyś wspomniałem, JS jest takim mocno zboczonym językiem, gdzie wszystko jest obiektem. Nic zatem dziwnego, że funkcje są tak naprawdę... obiektami "klasy" Function ("klasy", bo jak wiadomo ECMAScript to jedyna grupa języków prototypowanych). Zatem każda zmiana w prototypie tego globalnego obiektu będzie dziedziczona przez wszystkie funkcje. Jednak tutaj skrypterzy Google poszli o krok dalej i wywołują poszczególne metody prosto z prototypu! (czasami, ale to bardzo rzadko, okazuje się to przydatne).
Zatem jakie metody się tu wywołuje? call, apply i bind. Wszystkie działają dość podobnie i ich głównym zadaniem jest zmienienie kontekstu wywołania funkcji (this). Już wyjaśniam na przykładzie:
Kod:
var d={n:1}
,g=function() {console.log(this);}
g.apply(d);
To wywołanie funkcji wyrzuci do konsoli obiekt d, zamiast standardowego window.Dość użyteczne, pod warunkiem, że pisze się obiektowy JS i trochę kojarzy pojęcie kontekstu wywołania 2 Dla nas jednak nie jest to aż tak ważne. Wystarczy wiedzieć, że apply przyjmuje jako pierwszy parametr obiekt, który ma zastąpić nam this, a drugim parametrem jest tablica parametrów wywoływanej funkcji. Przykład, bo brzmi to dziwnie:
Kod:
var g=function(r) {console.log(r);};
g.apply(window,['whatever']);
W konsoli ujrzymy wspaniały napis whatever. call w zasadzie działa tak samo, ale zamiast tablicy parametrów, przyjmuje ich listę:
Kod:
var g=function(r) {console.log(r);};
g.call(window,'whatever');
Efekt identyczny jak w poprzednim przykładzie 2 Trochę inaczej działa bind, ponieważ nie wywołuje funkcji w podanym kontekście, a zwraca jej "klona", działającego w danym scope. Dla kompletności przykład, z wiadomym wynikiem:
Kod:
var g=function(r) {console.log(r);};
(g.bind(window,['whatever']))();
Zatem gdy trochę się wysili szare komórki, można dojść do pewnego uogólnienia, że w bardzo zaowalowany sposób po prostu wywołujemy Function.prototype.bind 2
Ok, a teraz gwóźdź programu: co nam daje połączenie tych trzech metod? A no, przy dobrze zaciemnionym kodzie bardzo utrudnia podejrzenie działania naszych funkcji i ukrywa to przed debuggerami. Kod
Kod:
console.log(Function.prototype.call.apply(Function.prototype.bind,[function(){}]);
zwróci nam bowiem nic nie mówiące
Kod:
function() { [native code] }
Chyba nie muszę mówić jaką konsternację na twarzy młodocianego hakiera zrobi informacja, że wasza super-hiper-tajna funkcja, w której zaimplementowaliście czekający na opatentowanie algorytm, uparcie twierdzi, że jest funkcją wbudowaną i to na dodatek bez nazwy! 2
I zanim polecisz opakowywać wszystkie swoje funkcje w ten sposób, wiadro zimnej wody dla ochłody: KOMPATYBILNOŚĆ. Tak, wiem, że wiesz, że ja wiem, że wiesz, że nie działa to w IE < 9 - przecież to logiczne! Ale niestety rynek tych przeglądarek wciąż jest dośc spory. Toteż - dla własnej wygody i kompatybilności - napiszmy sobie prostą funckję pomocniczą. Nazwijmy ją (a jakże!) obfuscate. Użyjemy tu dwóch bardziej zaawansowanych elementów JS: funkcji natychmiastowego wywołania oraz closures (i w tym miejscu większość mniej zdeterminowanych postanowiła zamknąć tą kartę przeglądarki 3). Niech was nie zwiodą pozory - wcale to (aż tak) trudne nie będzie! Na początku zadeklarujmy sobie swoją funkcję:
Kod:
var obfuscate=(function(){
}())
To jest właśnie tzw. funkcja natychmiastowego wywołania. Jak nie trudno zgadnąć, nazywa się tak, bo od razu po zadeklarowaniu jest wywoływana (to tak naprawdę jest wywołanie funkcji anonimowej 2). Na razie nasz kod jest bezużyteczny, bo zmienna obfuscate będzie miała wartość undefined. Toteż trzeba dodać do naszej funkcji return
Kod:
var obfuscate=(function(){
    return function(f)
    {
        return Function.prototype.call.apply(Function.prototype.bind,[f]);
    }
}())
Tak, to jest właśnie closure - funkcja zwracana przez inną funkcję (no ej, w końcu to technika od samego Google 3 nikt nie mówił, że łatwo będzie). Tak naprawdę, gdy JS się wczyta, przeglądarka otrzyma coś takiego:
Kod:
var obfuscate=function(f)
{
    return Function.prototype.call.apply(Function.prototype.bind,[f]);
}
Czemu zatem nie zapisałem tego w taki sposób? Otóż przez IE 8 tak nie zapisałem! Trzeba przecież sprawdzić czy ta metoda ukrywania kodu jest wspierana i w zależności od tego funkcja obfuscate przyjąć może dwie postaci:
- zaciemni nam kod
- zwróci niezaciemniony kod
A jak sprawdzić czy technika jest obsługiwana? Można sprawdzić czy przeglądarka wysypie się na wywołaniu zaciemnionej w taki sposób funkcji. Ok, ale to nam zatrzyma wykonywanie skryptu... chyba, że wrzucimy do tego obsługę wyjątków 2
Kod:
try
{
    Function.prototype.call.apply(Function.prototype.bind,[function() {}])();
}
catch (e)
{}
Dobra, zwracanie niezaciemnionego kodu jest łatwe - wystarczy zwrócić parametr f. Tyle. Zatem po połączeniu wszystkiego razem, otrzymamy coś takiego:
Kod:
var obfuscate=(function()
{
    var a=Function.prototype;
    try
    {
        a.call.apply(a.bind,[function() {}])();
    }
    catch (e)
    {
        return function(f)
        {
            return f;
        };
    }
    return function(f)
    {
        return a.call.apply(a.bind,[f]);
    };
}());
- najpierw przypisujemy sobie Function.prototype do zmiennej a (żeby się później nie napisać i oszczędzić te parę bajtów 2)
- sprawdzamy czy obfuskacja wgl działa w bloku try/catch. jeśli nie, jak gdyby nigdy nic, zwracamy funkcję zwracającą parametr f (nieźle to brzmi 2)
- w innych przypadkach po prostu zwracamy funkcję zwracającą zaciemnioną funkcję
Przykład:
Kod:
var f=obfuscate(function(n,r)
{
    return n+':'+r;
});
console.log(f('r','t')); //"r:t"
console.log(f); //function () { [native code] }
Aaaaa, i taka uwaga na koniec: moja funkcja obfuscate bardzo lubi strict mode 3




Zaloguj się lub zarejestruj, aby odpowiedzieć w temacie.
Dołącz do naszej społeczności!


Zarejestruj się
Posiadasz konto? Kliknij poniżej.


Zaloguj się


Użytkownicy przeglądający ten temat: 1 gości

Centrum napraw komputerów Warszawa ▪ Apartamenty w Kościelisku ▪ Szkolenia dla farmaceutów ▪ Youboost Łukasz Wudyka

Portal  Kontakt  Pomoc  Facebook 
© CentrumWindows
Tłumaczenie © 2007-2014 Polski support MyBB
Skrypt forum MyBB, © 2002-2016 MyBB Group
Biznes-Host