yangzj1992

## 对一行混淆 JS 代码的逆向分析过程

``````<pre id=p><script>n=setInterval("for(n+=7,i=k,P='p.\\n';i-=1/k;P+=P[i%2?(i%2*j-j+n/k^j)&1:2])j=k/i;p.innerHTML=P",k=64)</script>
``````

## 第一部分，使代码可读。

``````<script src="code.js"></script>
<pre id="p"></pre>
``````

``````var delay = 64;
var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var n = setInterval(draw, delay);
``````

`var draw` 由于是一个字符串，它会被 setInterval 用 eval() 方法执行（setInterval 可以接受一个 function 或是 string 来执行）。所以这里我们将它重写成一个真实的 function。

``````var delay = 64;
var p = document.getElementById("p"); // < ---------------
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
for (n += 7, i = delay, P = 'p.\n'; i -= 1 / delay; P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2]) {
j = delay / i; p.innerHTML = P;
}
};
var n = setInterval(draw, delay);
``````

``````var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay; // < ---------------
var P ='p.\n';
var j;
for (n += 7; i > 0 ;P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2]) {
j = delay / i; p.innerHTML = P;
i -= 1 / delay;
}
};
var n = setInterval(draw, delay);
``````

``````var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) { // <----------------------
//Update HTML
p.innerHTML = P;

j = delay / i;
i -= 1 / delay;
P += P[i % 2 ? (i % 2 * j - j + n / delay ^ j) & 1 : 2];
}
};
var n = setInterval(draw, delay);
``````

``````var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;

j = delay / i;
i -= 1 / delay;

let index;
let iIsOdd = (i % 2 != 0); // <---------------

if (iIsOdd) { // <---------------
index = (i % 2 * j - j + n / delay ^ j) & 1;
} else {
index = 2;
}

P += P[index];
}
};
var n = setInterval(draw, delay);
``````

`(i % 2 * j - j + n / delay ^ j) & 1`，这段代码可以巧妙地去比较一个数是否为奇偶，它的原理其实是数值转换为二进制进行与运算返回的十进制结果。

``````0 & 1 // 0
1 & 1 // 1
2 & 1 // 0
3 & 1 // 1
3 & 2 // 2
8 & 8 // 8
``````

``````var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;

j = delay / i;
i -= 1 / delay;

let index;
let iIsOdd = (i % 2 != 0);

if (iIsOdd) {
let magic = (i % 2 * j - j + n / delay ^ j);
let magicIsOdd = (magic % 2 != 0); // &1 < --------------------------
if (magicIsOdd) { // &1 <--------------------------
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}

P += P[index];
}
};
var n = setInterval(draw, delay);
``````

``````P[0] = 'p'
P[1] = '.'
P[2] = '\n'
``````

``````var delay = 64;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";
var draw = function() {
var i = delay;
var P ='p.\n';
var j;
n += 7;
while (i > 0) {
//Update HTML
p.innerHTML = P;

j = delay / i;
i -= 1 / delay;

let index;
let iIsOdd = (i % 2 != 0);

if (iIsOdd) {
let magic = (i % 2 * j - j + n / delay ^ j);
let magicIsOdd = (magic % 2 != 0); // &1
if (magicIsOdd) { // &1
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}

switch (index) { // P += P[index]; <-----------------------
case 0:
P += "p"; // aka P[0]
break;
case 1:
P += "."; // aka P[1]
break;
case 2:
P += "\n"; // aka P[2]
}
}
};

var n = setInterval(draw, delay);
``````

``````const DELAY = 64; // approximately 15 frames per second
var n = 1;
var p = document.getElementById("p");
// var draw = "for(n+=7,i=delay,P='p.\\n';i-=1/delay;P+=P[i%2?(i%2*j-j+n/delay^j)&1:2])j=delay/i;p.innerHTML=P";

/**
* Draws a picture
* 128 chars by 32 chars = total 4096 chars
*/
var draw = function() {
var i = DELAY; // 64
var P ='p.\n'; // First line, reference for chars to use
var j;

n += 7;

while (i > 0) {

j = DELAY / i;
i -= 1 / DELAY;

let index;
let iIsOdd = (i % 2 != 0);

if (iIsOdd) {
let magic = ((i % 2 * j - j + n / DELAY) ^ j); // < ------------------
let magicIsOdd = (magic % 2 != 0); // &1
if (magicIsOdd) { // &1
index = 1;
} else {
index = 0;
}
} else {
index = 2;
}

switch (index) { // P += P[index];
case 0:
P += "p";
break;
case 1:
P += ".";
break;
case 2:
P += "\n";
}
}
//Update HTML
p.innerHTML = P;
};

setInterval(draw, 64);
``````

## 第二部分，理解代码。

`draw()` 函数第一次执行时 i 被 `var i = DELAY;` 初始化为 64，并在每次循环中被 `i -= 1 / DELAY;` 进行逐次 1/64 的递减。直到 `i > 0` 时结束（循环 64 * 64 次）

### 这些图表是什么意思

``````First        Second         Result
1              1              1
1              0              1
0              1              1
0              0              0
``````

``````var a=12;
var b=23;
a^=b,b^=a,a^=b;//a=23,b=12
``````

``````-2 ^ 3 =
1111 1111 1111 1110
^ 0000 0000 0000 0011
= 1111 1111 1111 1101
``````

`i` 值的这个范围内 `j` 将从 1 开始慢慢的走向 2（即基本为 1.XXX)。这样在另一端也为 1 时，我们将会得到异或计算结果为 0（偶数），最后获得 `p` 字符输出。