Wie funktioniert dieses verschleierte JavaScript?

93

Wie funktioniert das folgende JavaScript?

Ich verstehe, dass es sich um minimierten Code handelt. Ich habe versucht, es ein wenig zu verschleiern, aber ich kann mir kein klares Konzept darüber machen, wie es diesen Effekt erzielt. Ich kann sehen, dass es Strings für irgendeine Art von Iteration, Verwendung des Date-Objekts, seltsame String-Manipulation, Math-Funktionen verwendet, dann druckt sich der Code selbst.

Wie könnte der gleiche Effekt mit einem minimalen Beispiel umgeschrieben werden?

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

Alexander
quelle
8
Coole Animation ... könnte das tatsächlich irgendwo verwenden!
TymeJV
7
Oh schön. Ich habe die Geige nicht bemerkt.
ThiefMaster
37
Dies nennt man Quine, und dies ist eine der fantastischeren Quines, die ich je gesehen habe. en.wikipedia.org/wiki/Quine_(computing)
David Souther
9
@Roko C. Buljan Ich denke, das ist seine Seite: aem1k.com
Alexander
5
Scheint, als hätte der Autor jetzt eine kommentierte Version auf GitHub veröffentlicht.
Der Hochstapler

Antworten:

67

Vorwort : Ich habe den Code unter http://jsfiddle.net/WZXYr/2/ ausführlich verschönert und kommentiert.

Betrachten Sie die äußerste Schicht:

eval(z = '...');

In der Variablen wird eine Codezeichenfolge gespeichert z. Der Zuweisungsoperator gibt den zugewiesenen Wert zurück, sodass die Codezeichenfolge auch als Argument übergeben wird eval.

Die Codezeichenfolge wird zinnerhalb von ausgeführt eval. Der Code ist extrem stumpf, selbst wenn er bereinigt wird, aber es scheint:

  1. Analysieren Sie eine Zeichenfolge von Basis-36-Zahlen, die vom Zeichen abgegrenzt werden 4.
  2. Bestücken Sie eine Karte von Werten, die globalen Variablen verwendet e, xund yzu halten Karte Zustand. Der Kartenstatus ist teilweise eine Funktion der aktuellen Sekunde auf der Wanduhr ( new Date / 1e3).
  3. Unter Verwendung der Kartenwerte generiert der Code eine Ausgabezeichenfolge. p
    • Der Code verwendet p += " *#"[index], um zu entscheiden, ob ein Leerzeichen, ein Sternchen oder eine Raute verwendet werden soll, wo indexsich tatsächlich befindet e[x++] + e[x++](wie oben erwähnt eund xfür den Kartenstatus verantwortlich).
    • Wenn der Index größer als die Länge von ist " *#", gibt es einen Fallback-Code, der die Ausgabe pmit Zeichen von füllt z. Innere Zeichen werden mit Animationszeichen gefüllt, während äußere Zeichen aus gezogen werden z.

Am Ende des Codes gibt es einen Aufruf von setTimeout(z), der die Codezeichenfolge asynchron auswertet z. Dieser wiederholte Aufruf von zermöglicht dem Code eine Schleife.

Einfaches Beispiel:

Hier ist eine supereinfache 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)')
  1. Die forSchleife fügt jedes Zeichen zur Ausgabezeichenfolge hinzu p(die Zeichenfolge ist 172 Zeichen lang):

    for(i=0;i<172;++i)
  2. Die innere Bedingung entscheidet, ob wir uns auf einem Charakter zwischen Position 62 und 67 befinden. Dies sind die animierten Charaktere:

    if(i > 62 && i < 67)
  3. Wenn dies der Fall ist, wird der Ausdruck !---basierend auf dem Zehntel des zweiten Wanduhrwerts verschoben. Dies liefert den Animationseffekt.

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

    (Die ganze Gemeinheit new Dateist wirklich nur da, um einen Datumswert in eine Zahl zwischen 0 und 3 umzuwandeln.)

  4. Wenn Sie kein animiertes Zeichen verwenden, drucken Sie das Indexzeichen iaus der durch definierten Zeichenfolge

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

    Das heißt, die Codezeichenfolge, zdie von eval('und umgeben ist ').

  5. Geben Sie abschließend die Zeichenfolge aus und stellen Sie setTimeouteine weitere Ausführung von z:

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

Beachten Sie, dass meine endgültige Ausgabe nicht ganz richtig ist - ich habe die Backslashes gegen Ende nicht berücksichtigt -, aber es sollte Ihnen trotzdem eine ziemlich gute Vorstellung davon geben, wie die Technik im Allgemeinen funktioniert.

Apsiller
quelle
8
Beachten Sie diese github.com/aemkei/world/blob/master/annotated.js - die kommentierte Version des Autors in GitHub.
Benjamin Gruenbaum
36

Hier ist die kommentierte Quelle. Ps: Ich bin der Autor;)

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()
Aemkei
quelle
5
Beachten Sie, es wird auch in diesem Video youtube.com/watch?v=RTxtiLp1C8Y
Benjamin Gruenbaum
21

Hier ist eine weitere manuell deobfuscierte Version, die alle Initialisierungen aus dem Ausdruck in eigene Anweisungen verschiebt:

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)

Folgendes passiert:

  • zist eine mehrzeilige Zeichenfolge, die den gesamten Code enthält. Es ist evaled.
  • Am Ende des Codes zwird an übergeben setTimeout. Es funktioniert wie requestAnimationFrameund evalzusammen und wertet es in einem Intervall mit der höchstmöglichen Rate aus.
  • Der Code selbst wird initialisiert p, der Zeichenfolgenpuffer, an den der HTML- Code angehängt wird, und nein Array von Base-36-codierten Zahlen (verbunden durch eine Zeichenfolge "4", wobei die Kommentare irrelevanter Müll sind, der von nicht berücksichtigt wird parseInt).
  • Jede Zahl in ncodiert eine Zeile ( n.length == 16). Es ist jetzt aufgezählt .
  • Eine Reihe von Variablen wird initialisiert, einige als eArray-Literal getarnt, aber sie werden dann bei Verwendung in Zahlen ( x) oder Boolesche Werte ( r) oder Zeichenfolgen ( t) umgewandelt.
  • Jede Ziffer in der Zahl twird aufgezählt, wobei der Boolesche Wert in rjeder Runde invertiert wird . Für verschiedene Winkel xund abhängig von der aktuellen Zeit new Date / 1000 (so dass eine Animation angezeigt ewird ) wird das Array mit einigen bitweisen Operatoren gefüllt - mit 1wann rist falsch und 2s wann rist zu diesem Zeitpunkt wahr.
  • Dann iteriert eine Schleife die 61 Spalten des Bildes x=0in doppelten Schritten von bis 122, wobei einzelne Zeichen an angehängt werden p.
  • BAls Backslash wird die Zeichenfolge Saus der Code-Zeichenfolge erstellt, zindem Backslashes und Apostrophe ausgeblendet werden, um eine genaue Darstellung des Erscheinungsbilds in der Quelle zu erhalten.
  • Alle zwei aufeinander folgenden Nummern von ewerden hinzugefügt und verwendet, um auf ein Zeichen von zuzugreifen " *#"und das animierte Bild aufzubauen. Wenn einer der Indizes nicht definiert ist, wird der NaNIndex in ein undefiniertes Zeichen aufgelöst und stattdessen wird das entsprechende Zeichen aus der SZeichenfolge übernommen (siehe Formel x/2+61*y-1). Wenn dieses Zeichen ein Wortzeichen sein soll , wird es mit der fontcolorString-Methode unterschiedlich gefärbt .
  • Nach jeder Zeile werden die nachfolgende Rücktaste und ein Zeilenumbruch hinzugefügt p, und die HTML-Zeichenfolge wird dem Dokumentkörper zugewiesen.

Wie könnte der gleiche Effekt für ein minimales Beispiel umgeschrieben werden?

Hier ist ein anderes Beispiel:

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 bei jsfiddle.net )

Es hat alle relevanten Dinge, die Sie für diese Art von Animation benötigen:

  • setIntervalund Datefür die Animation
  • Eine Rekonstruktion des eigenen Codes ( quine- like) hier:

    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
    
  • Die Ausgabe über document.body.innerHTMLund ein <pre>Element

  • Ersetzen einiger Teile des Codes durch die animierte Zeichenfolge
Bergi
quelle
2
Ich muss zugeben, tolle Antwort!
Rafaelcastrocouto
5

Eine Zeichenfolge mit dem gesamten Code wird ausgewertet, und eine Zeitüberschreitung führt zu einer Schleife. Die Zeichenfolge wird in einer Variablen mit dem Namen zund in der Mitte des Codes zwischen Kommentaren gespeichert , /*und */es befindet sich eine "Earth ASCII Art". Der Code analysiert die Kommentare und ändert den Dokumentinhalt, behält das js bei und aktualisiert die Grafik. Unten ist nur der Code in Scheiben geschnitten:

  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
  }
rafaelcastrocouto
quelle
6
Wie auch immer, unglaublich, wie rund um den Äquator die Grafik mehr Spin hat ... erstaunlich. +1 BTW
Roko C. Buljan