【问题标题】:How does this obfuscated JavaScript work?这个混淆的 JavaScript 是如何工作的?
【发布时间】:2013-09-20 23:23:04
【问题描述】:

以下 JavaScript 是如何工作的?

我知道这是缩小的代码。我已经尝试对它进行一点去混淆,但我无法清楚地了解它是如何实现这种效果的。我可以看到它正在使用字符串进行某种迭代、使用 Date 对象、奇怪的字符串操作、数学函数,然后代码会自行打印。

如何用一个最小的例子重写相同的效果?

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

【问题讨论】:

  • 很酷的动画...可能最终会在某个地方使用它!
  • 哦,很好。没注意到小提琴。
  • 这被称为Quine,这是我见过的最神奇的Quine之一。 en.wikipedia.org/wiki/Quine_(computing)
  • @Roko C. Buljan 我认为这是他的页面:aem1k.com
  • 好像作者现在在 GitHub 上放了一个annotated version

标签: javascript


【解决方案1】:

前言:我在http://jsfiddle.net/WZXYr/2/对代码进行了广泛的美化和注释

考虑最外层:

eval(z = '...');

代码字符串存储在变量z 中。赋值运算符返回分配的值,因此代码字符串也作为参数传递给eval

代码字符串zeval 内部运行。该代码非常迟钝,即使在清理后也是如此,但它似乎:

  1. 解析由字符4 描述的base-36 数字字符串。
  2. 使用全局变量exy 填充值映射以保持映射状态。地图状态在一定程度上是挂钟上当前秒数的函数 (new Date / 1e3)。
  3. 使用映射值,代码生成一个输出字符串,p
    • 代码使用p += " *#"[index]来决定是否使用空格、星号或井号,其中index实际上是e[x++] + e[x++](如上所述,ex负责地图状态)
    • 如果索引大于" *#" 的长度,则有备用代码使用来自z 的字符填充输出p。内部字符填充动画字符,而外部字符从z 中提取。

在代码的最后,有一个对setTimeout(z)的调用,它异步评估代码字符串z。这种对z 的重复调用允许代码循环。

简单示例:

这是一个超级简单的版本 (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. for 循环将每个字符添加到输出字符串p(字符串长度为 172 个字符):

    for(i=0;i<172;++i)
    
  2. 内部条件决定我们是否在位置 62 到 67 之间的角色上,即动画角色:

    if(i > 62 && i < 67)
    
  3. 如果是,则打印出!---,根据第二个挂钟值的十分之一移动。这提供了动画效果。

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

    new Date 周围的所有麻烦实际上只是为了将日期值转换为 0 到 3 之间的数字。)

  4. 否则,如果我们不在动画角色上,则从定义的字符串中打印 index-i 字符

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

    即由eval('')包围的代码字符串z

  5. 最后,输出字符串并使用setTimeout排队另一个执行z

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

请注意,我的最终输出并不完全正确 - 我没有考虑到最后的反斜杠 - 但它仍然应该让您很好地了解该技术的工作原理一般。

【讨论】:

【解决方案2】:

这里是带注释的来源。 ps:我是作者;)

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

【讨论】:

【解决方案3】:

这是另一个手动反混淆版本,将所有初始化从表达式移到自己的语句中:

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)

会发生什么:

  • z 是一个包含所有代码的多行字符串。我是evaled。
  • 在代码的最后,z 被传递给setTimeout。它就像 requestAnimationFrameeval 一起工作,在一个区间内以尽可能高的速率对其进行评估。
  • 代码本身初始化p,将附加HTML 的字符串缓冲区,和n,一个base-36 编码的数字数组(由"4" 连接成一个字符串,cmets 无关紧要parseInt 不考虑的垃圾)。
  • n 中的每个数字都会编码一行 (n.length == 16)。现在是enumerated
  • 初始化了一堆变量,其中一些伪装成 e 数组字面量,但在使用时它们被转换为数字 (x) 或布尔值 (r) 或字符串 (t)。李>
  • 枚举t 中的每个数字,每转一圈反转布尔值r。对于不同的角度x,并根据current time new Date / 1000(以便它给出动画),数组e 使用一些bitwise operators 填充 - 当r 为假时使用12s 当r 为真时。
  • 然后一个循环确实迭代了图像的 61 列,从 x=0 到 122 双步,将单个字符附加到 p
  • B 是反斜杠,字符串 S 是从代码字符串 z 通过转义反斜杠和撇号构建的,以获得它在源代码中的准确表示。
  • 每两个来自e 的连续数字被添加并用于访问来自" *#" 的字符,以构建动画图像。如果未定义其中一个索引,则NaN 索引解析为未定义的字符,取而代之的是S 字符串中的相应字符(查看公式x/2+61*y-1)。如果该字符应为word character,则使用fontcolor String method 以不同的颜色显示。
  • 在每一行之后,在p 中添加尾随退格和换行符,并将 HTML 字符串分配给文档正文。

如何用一个最小的例子重写相同的效果?

这是另一个例子:

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

它拥有制作此类动画所需的所有相关内容:

  • setIntervalDate 用于动画

  • 自己的代码重构(quine-like),在这里:

      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
    
  • 通过document.body.innerHTML&lt;pre&gt; 元素输出

  • 用动画字符串替换部分代码

【讨论】:

  • 必须承认,很好的答案!
【解决方案4】:

一个包含所有代码的字符串被求值,超时使循环; 该字符串存储在一个名为z 的变量中,并且在代码的中间,在cmets /**/之间有一个“Earth ASCII Art”。 代码解析cmets并更改文档内容,保留js并更新art。贝娄只是切片的代码:

  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
  }

【讨论】:

  • 无论如何,令人难以置信的是,赤道周围的图形有更多的旋转......令人惊叹。 +1 顺便说一句
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
相关资源
最近更新 更多