Come funziona questo JavaScript offuscato?


93

Come funziona il seguente JavaScript?

Capisco che è un codice minimizzato. Ho provato a de-offuscarlo un po ', ma non riesco ad avere un'idea chiara di come ottenga questo effetto. Posso vedere che sta usando stringhe per iterazioni di qualche tipo, uso dell'oggetto Date, strana manipolazione di stringhe, funzioni matematiche, quindi il codice si stampa da solo.

Come si potrebbe riscrivere lo stesso effetto con un esempio minimo?

eval(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)')//

JSFiddle


8
Bella animazione ... potrebbe finire per usarla da qualche parte in realtà!
tymeJV

7
Oh bello. Non ho notato il violino.
ThiefMaster

37
Questo si chiama Quine, e questo è uno dei più fantastici Quine che abbia mai visto. en.wikipedia.org/wiki/Quine_(computing)
David Souther

9
@Roko C.Buljan penso che questa sia la sua pagina: aem1k.com
Alexander

5
Sembra che l'autore abbia ora pubblicato una versione annotata su GitHub.
Der Hochstapler

Risposte:


67

Prefazione : ho abbellito e annotato ampiamente il codice su http://jsfiddle.net/WZXYr/2/

Considera lo strato più esterno:

eval(z = '...');

Una stringa di codice è memorizzata nella variabile z. L'operatore di assegnazione restituisce il valore assegnato, quindi anche la stringa di codice viene passata come argomento in eval.

La stringa di codice zviene eseguita all'interno di eval. Il codice è estremamente ottuso, anche quando viene ripulito, ma sembra:

  1. Analizza una stringa di numeri in base 36, delineati dal carattere 4.
  2. Popolare una mappa di valori, utilizzando le variabili globali e, xe yper tenere mappa di stato. Lo stato della mappa è, in parte, una funzione del secondo corrente sull'orologio da parete ( new Date / 1e3).
  3. Utilizzando i valori della mappa, il codice genera una stringa di output, p
    • il codice utilizza p += " *#"[index]per decidere se utilizzare uno spazio, un asterisco o un cancelletto, dove si indextrova effettivamente e[x++] + e[x++](come detto sopra, ee xsono responsabili dello stato della mappa)
    • se l'indice è maggiore della lunghezza di " *#", è presente un codice di fallback che popola l'output pcon caratteri da z. I personaggi interni sono popolati con personaggi di animazione, mentre i personaggi esterni vengono estratti z.

Alla fine del codice, c'è una chiamata a setTimeout(z), che valuta in modo asincrono la stringa di codice z. Questa chiamata ripetuta di zconsente il ciclo del codice.

Esempio semplice:

Ecco una versione semplicissima ( 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)')
  1. Il forciclo aggiunge ogni carattere alla stringa di output p(la stringa è lunga 172 caratteri):

    for(i=0;i<172;++i)
  2. Il condizionale interno decide se siamo su un personaggio tra la posizione 62 e la 67, che sono i personaggi animati:

    if(i > 62 && i < 67)
  3. Se lo siamo, stampa !---, spostato in base al decimo del secondo valore dell'orologio da parete. Ciò fornisce l'effetto di animazione.

    p+="!---"[~~(new Date/1e2 + i)%4]

    (Tutta la cattiveria intorno new Dateè davvero lì solo per trasformare un valore di data in un numero compreso tra 0 e 3.)

  4. Altrimenti, se non siamo su un personaggio animato, stampa il icarattere indice dalla stringa definita da

    "eval(z='" + z + "')"

    Cioè, la stringa di codice zcircondata da eval('e ').

  5. Infine, visualizza la stringa e usa setTimeoutper mettere in coda un'altra esecuzione di z:

    document.body.innerHTML = p;setTimeout(z)

Nota che il mio risultato finale non è del tutto giusto - non ho rappresentato per le barre inverse verso la fine - ma dovrebbe ancora dare una buona idea di come la tecnica funziona in generale.


8
Nota questo github.com/aemkei/world/blob/master/annotated.js - la versione annotata dell'autore in GitHub.
Benjamin Gruenbaum

36

Ecco la fonte annotata. Ps: sono l'autore;)

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()

5
Nota, è anche spiegato in questo video youtube.com/watch?v=RTxtiLp1C8Y
Benjamin Gruenbaum

21

Ecco un'altra versione deoffuscata manualmente, spostando tutte le inizializzazioni fuori dall'espressione nelle proprie dichiarazioni:

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)

Ecco cosa succede:

  • zè una stringa multilinea contenente tutto il codice. È evaled.
  • Alla fine del codice zviene passato a setTimeout. Funziona come requestAnimationFramee evalinsieme, valutandolo in un intervallo alla massima velocità possibile.
  • Il codice stesso viene inizializzato p, il buffer di stringa su cui verrà aggiunto l'HTML e n, un array di numeri codificati in base 36 (uniti in una stringa da "4", i commenti sono spazzatura irrilevante che non è considerata da parseInt).
  • ogni numero in ncodifica una riga ( n.length == 16). Ora è enumerato .
  • Viene inizializzato un gruppo di variabili, alcune camuffate da earray letterale, ma vengono poi convertite in numbers ( x) o booleans ( r) o stringhe ( t) quando vengono utilizzate.
  • Ogni cifra del numero tviene enumerata, invertendo il valore booleano rogni turno. Per diverse angolazioni x, e in base all'ora corrente new Date / 1000 (in modo che dia un'animazione), l'array eviene riempito utilizzando alcuni operatori bit per bit - con 1quando rè falso e 2s quando rè vero in quel momento.
  • Quindi un ciclo itera le 61 colonne dell'immagine, da x=0a 122 a passi doppi, aggiungendo singoli caratteri a p.
  • Bessendo il backslash, la stringa Sè costruita dalla stringa di codice zfacendo l'escape di backslash e apostrofi, per ottenere una rappresentazione accurata di ciò che appariva nel sorgente.
  • Ogni due numeri consecutivi da evengono aggiunti e utilizzati per accedere a un personaggio da " *#", per costruire l'immagine animata. Se uno degli indici non è definito, l' NaNindice si risolve in un carattere indefinito e invece Sviene preso il rispettivo carattere dalla stringa (controlla la formula x/2+61*y-1). Se quel carattere deve essere un carattere di parola , viene colorato in modo diverso utilizzando il fontcolormetodo String .
  • Dopo ogni riga, vengono aggiunti il ​​backspace finale e un'interruzione di riga pe la stringa HTML viene assegnata al corpo del documento.

Come si potrebbe riscrivere lo stesso effetto per un esempio minimo?

Ecco un altro esempio:

setInterval(z='s=("setInterval(z=\'"+\
z.replace(/[\\\\\']/g,"\\\\$&")+"\')"\
).match(/.{1,37}/g).join("\\\\\\n");d\
ocument.body.innerHTML=\"<\\pre>"+s.s\
lice(0, 175)+String( + new Date()).fo\
ntcolor("red")+s.slice(188)')

( demo su jsfiddle.net )

Ha tutte le cose rilevanti di cui hai bisogno per questo tipo di animazione:

  • setIntervale Dateper l'animazione
  • Una ricostruzione del proprio codice ( simile a quine ), qui:

    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
  • L'output tramite document.body.innerHTMLe un <pre>elemento

  • Sostituzione di alcune parti del codice con la stringa animata

2
devo ammetterlo, ottima risposta!
rafaelcastrocouto

5

Viene valutata una stringa con tutto il codice e un timeout esegue il ciclo; La stringa è memorizzata in una variabile denominata ze al centro del codice, tra i commenti /*e */c'è una "Earth ASCII Art". Il codice analizza i commenti e modifica il contenuto del documento, mantenendo il js e aggiornando l'art. Di seguito è solo il codice tagliato:

  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
  }

6
Ad ogni modo, incredibile come intorno all'Equatore la grafica abbia più spin ... sorprendente. +1 BTW
Roko C. Buljan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.