Comment fonctionne ce JavaScript obscurci?

Comment fonctionne le JavaScript suivant?

Je comprends que c’est un code minifié, mais j’ai essayé de le dégraisser un peu, mais je n’arrive pas à comprendre clairement comment il réalise cet effet. Je peux voir qu’il utilise des chaînes pour une itération quelconque, l’utilisation de l’object Date, une manipulation de chaîne étrange, des fonctions mathématiques, puis le code s’imprime.

Comment le même effet pourrait-il être réécrit avec un exemple minimal?

eval(z='p=""/* ,.oq#+ ,._, */;for(y in n="zw24l6k\ 4e3t4jnt4qj24xh2 x/* =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

Avantpropos : j’ai embelli et annoté le code de manière approfondie sur http://jsfiddle.net/WZXYr/2/

Considérons la couche la plus extérieure:

 eval(z = '...'); 

Une chaîne de code est stockée dans la variable z . L’opérateur d’affectation renvoie la valeur affectée, de sorte que la chaîne de code est également transmise en tant qu’argument dans eval .

La chaîne de code z s’exécute à l’intérieur d’ eval . Le code est extrêmement obtus, même lorsqu’il est nettoyé, mais il semble que:

  1. Analyser une chaîne de nombres de base 36, délimitée par le caractère 4 .
  2. Remplissez une carte de valeurs en utilisant les variables globales e , x et y pour conserver l’état de la carte. L’état de la carte est en partie fonction de la seconde en cours sur l’horloge murale ( new Date / 1e3 ).
  3. En utilisant les valeurs de la carte, le code génère une chaîne de sortie, p
    • le code utilise p += " *#"[index] pour décider d’utiliser un espace, un astérisque ou une marque de hachage, où index est en fait e[x++] + e[x++] (comme indiqué ci-dessus, e et x sont responsables) pour l’état de la carte)
    • Si l’index est supérieur à la longueur de " *#" , il existe un code de secours qui remplit la sortie p avec des caractères de z . Les caractères internes sont remplis de caractères d’animation, tandis que les caractères externes sont extraits de z .

À la fin du code, il y a un appel à setTimeout(z) , qui évalue de manière asynchrone la chaîne de code z . Cette invocation répétée de z permet au code de boucler.

Exemple simple:

Voici une version super simple ( 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. La boucle for ajoute chaque caractère à la chaîne de sortie p (la chaîne comporte 172 caractères):

     for(i=0;i<172;++i) 
  2. Le conditionnel interne décide si nous sums sur un personnage entre la position 62 à 67, qui sont les personnages animés:

     if(i > 62 && i < 67) 
  3. Si c'est le cas, imprimez !--- , décalé en fonction du dixième de la seconde valeur de l'horloge murale. Ceci fournit l'effet d'animation.

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

    (Toute la méchanceté autour de la new Date est vraiment là pour transformer une valeur de date en un nombre entre 0 et 3.)

  4. Sinon, si nous ne sums pas sur un personnage animé, imprimez alors le caractère d'index- i de la chaîne définie par

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

    C'est-à-dire la chaîne de code z entourée par eval(' et ') .

  5. Enfin, affichez la chaîne et utilisez setTimeout pour mettre en queue une autre exécution de z :

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

Notez que mon résultat final n'est pas tout à fait correct - je n'ai pas tenu compte des antislash vers la fin - mais cela devrait vous donner une assez bonne idée de la façon dont la technique fonctionne généralement.

Voici la source annotée. Ps: je suis l’auteur;)

 function z(){ // will be replaced with eval p = "<" + "pre>"; // use 
 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" + // ssortingng 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 ssortingng n[y], // … with a base of 36 36 ) + ( // large number will be converted to ssortingng 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 masortingx ~~( // 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 masortingx ) } 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()

Voici une autre version désobfusquée manuellement, déplaçant toutes les initialisations d’expression dans leurs propres instructions:

 z='p="<"+"pre>"/* ,.oq#+ ,._, */;for(y in n="zw24l6k\ 4e3t4jnt4qj24xh2 x/* =<,m#F^ AW###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^ AW###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) 

Voici ce qui se passe:

  • z est une chaîne multiligne contenant tout le code. Il est eval
  • À la fin du code, z est passé à setTimeout . Cela fonctionne comme requestAnimationFrame et eval ensemble, l'évaluant dans un intervalle au taux le plus élevé possible.
  • Le code lui-même initialise p , le tampon de chaîne sur lequel le code HTML sera ajouté, et n , un tableau de nombres codés en base 36 (joints dans une chaîne par "4" , les commentaires n'étant pas pris en compte par parseInt ).
  • chaque nombre dans n code une ligne ( n.length == 16 ). Il est maintenant énuméré .
  • Un groupe de variables est initialisé, certaines étant déguisées en littéral e array, mais elles sont ensuite converties en nombres ( x ) ou en booléens ( r ) ou en chaînes ( t ) lorsqu'elles sont utilisées.
  • Chaque chiffre du nombre t est énuméré, inversant le booléen r chaque tour. Pour différents angles x , et en fonction de l' heure actuelle new Date / 1000 (pour donner une animation), le tableau e est rempli à l'aide d' opérateurs binarys - avec 1 lorsque r est faux et 2 s lorsque r est vrai à ce moment .
  • Ensuite, une boucle itère les 61 colonnes de l'image, de x=0 à 122 en deux étapes, en ajoutant des caractères uniques à p .
  • B étant la barre oblique inverse, la chaîne S est construite à partir de la chaîne de code z en échappant à des barres obliques inverses et à des apostrophes, pour obtenir une représentation précise de ce qu’elle était dans la source.
  • Tous les deux numéros consécutifs de e sont ajoutés et utilisés pour accéder à un caractère de " *#" , pour construire l'image animée. Si l'un des index n'est pas défini, l'index NaN résout en un caractère indéfini et à la place, le caractère respectif de la chaîne S est pris (consultez la formule x/2+61*y-1 ). Si ce caractère doit être un caractère de mot , il est coloré différemment en utilisant la méthode de chaîne de caractères fontcolor .
  • Après chaque ligne, le retour arrière et un retour à la ligne sont ajoutés à p , et la chaîne HTML est affectée au corps du document.

Comment le même effet pourrait-il être réécrit pour un exemple minimal?

Voici un autre exemple:

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

( démo sur jsfiddle.net )

Il a toutes les choses pertinentes dont vous avez besoin pour ce type d'animation:

  • setInterval et Date pour l'animation
  • Une reconstitution de son propre code ( quine- like), ici:

     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 
  • La sortie via document.body.innerHTML et un élément

    
    

  • Remplacement de certaines parties du code par la chaîne animée

Une chaîne avec le tout le code est évaluée et un délai d’attente fait la boucle; La chaîne est stockée dans une variable nommée z et au milieu du code, entre les commentaires /* et */ il y a un “Earth ASCII Art”. Le code parsing les commentaires et modifie le contenu du document, en conservant le js et en mettant à jour l’art. Ci-dessous, le code est tranché:

  p="
"; 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 }