JS混淆与反混淆

js混淆的利与弊

站在网站开发者的角度

1、是为了保护我们的前端代码逻辑
2、精简代码、加快传输

站在爬虫者的角度

1、增加了获取数据的难度

常见混淆与反混淆方法

JS压缩

特征:

原理:
削减是一个从源代码中删除不必要的字符的技术使它看起来简单而整洁。这种技术也 被称为代码压缩和最小化

eval加密

js中的 eval() 方法就是一个 js 语言的执行器,它能把其中的参数按照 JavaScript 语法进行解析并执行,简单来说就是把原本的 js 代码变成了 eval 的参数,变成参数后代码就成了字符串,其中的一些字符就会被按照特定格式“编码”

特征:

最明显的特征是生成的代码以 eval(function(p,a,c,k,e,r)) 开头

eval(function(){console.log(100);return 200})()

原理:

这类混淆的关键思想在于将需要执行的代码进行一次编码,在执行的时候还原出浏览器可执行的合法 的脚本

破解方法-浏览器
打开 谷歌 或者 火狐 浏览器
按 F12 打开控制台
把代码复制进去
删除开头 eval 这4个字母
按回车键

破解方法-node.js

将eval 的内容转变为普通的函数

变量名混淆

原理:

字符串字面量混淆: 首先提取全部的字符串,在全局作用域创建一个字符串数组,同时转义字符增大 阅读难度,然后将字符串出现的地方替换成为数组元素的引用

var _0x3012 = ['substring', 'atob', 'charCodeAt', 'push', 'test'];
// 打乱数组的内容
(function(_0x3ed35c, _0x48b8fe) {
    var _0x1ad9d9 = function(_0x8eeda7) {
        while (--_0x8eeda7) {
            _0x3ed35c['push'](_0x3ed35c['shift']());
        }
    };
    _0x1ad9d9(++_0x48b8fe);
}(_0x3012, 0x153));
// 用函数实现字符串的调用
var _0x3a8e = function(_0xc40c11, _0x32bbb2) {
    _0xc40c11 = _0xc40c11 - 0x0;
    var _0x4e269a = _0x3012[_0xc40c11];
    return _0x4e269a;
};

console.log(_0x3a8e('0x4'));

**变量名混淆:**不同于压缩器的缩短命名,此处使用了下划线加数字的格式,变量之间区分度很低,相 比单个字母更难以阅读

/*变量名混淆*/
var _0x3a8e = function(_0xc40c11, _0x32bbb2) {
    _0xc40c11 = _0xc40c11 - 0x0;
    var _0x4e269a = _0x3012[_0xc40c11];
    return _0x4e269a;
};

**成员运算符混淆:**将点运算符替换为字符串下标形式,然后对字符串进行混淆删除多余的空白字符:减小文件体积,这是所有压缩器都会做的事

/*成员运算符混淆*/
console.log(('')['\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72']['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](65));

console.log(('')['constructor']['fromCharCode'](65));


console.log(''.constructor.fromCharCode(65))
console.log(''.constructor)
console.log(String.fromCharCode(65));

破解方法-IDE、解密工具、浏览器:特殊字符变成普通内容

特征:

  1. 把变量名、函数名、参数名等,替换成没有语义,看着又很像的名字。

    _0x21dd83、_0x21dd84、_0x21dd85
  2. 用十六进制文本去表示一个字符串

    \x56\x49\x12\x23
  3. 利用JS能识别的编码来做混淆。JS是Unicode编码,本身就能识别这种编码。类似的一些变量名,函数名都可以用这个表示,并且调用。

类似:

\u6210\u529f 表示中文字符(成功)。

类似:

\u0053\u0074\u0072\u0069\u006e\u0067.\u0066\u0072\u006f\u006d\u0043\u0068\u0061\u0072\u0043\u006f\u0064\u0065 代表 String.fromCharCode

类似:

('')['\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72']['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65']; 效果等同于String.fromCharCode

  1. 把一大堆方法名、字符串等存到数组中,这个数组可以是上千个成员。然后调用的时候,取数组成员去用

    var arr = ["Date","getTime"];
    var time = new window[arr[0]]()[arr[1]]();
    console.log(time);
  2. 字符串加密后发送到前端,然后前端调用对应的函数去解密,得到明文

    var arr = ['xxxx']
    
    // 定义的解密函数
    function dec(str){
      return 'push'
    }
    test[dec(arr[0])](200);

控制流平坦化

将顺序执行的代码混淆成乱序执行,并加以混淆

以下两段代码的执行结果是相同的:

// 正常形态
function test(a){
	var b = a;
    b += 1;
    b += 2;
    b += 3;
    b += 4;
    return a + b
}

// 乱序形态
//(这里比较简单,在很多加密网站上case 后面往往不是数字或字符串,而是类似 YFp[15][45][4]这样的对象,相当恶心)
function test1(a){
  var arr = [1,2,3,4,5,6]
  for(var i = 0, i < arr.lenght, i++){
    switch (arr[i]) {
      case 4:
        b += 3;
        break;
      case 2:
        b += 1;
      break;
      case 1:
        var b = a;
      break;
      case 3:
        b += 2;
      break;
      case 6:
        return a + b
      case 5:
        b += 4;
      break;
    }
  }
}
// 结果都是30 但是test1看着费劲
console.log(test1(10));
console.log(test(10));

解决办法: 使用JavaScript的抽象语法书(AST) 进行还原

压缩代码

把多行代码压缩成一行

function test(a){
  var b = a;
  var c = b + 1;
  var d = b + 2;
  var e = b + 3;
  var f = b + 4;
  return e + f
}

// 压缩一下
function test1(a){
  var b,c,d,e,f
  return f = (e = (d = ( c = (b = a,b + 1),b + 2),b + 3),b + 4),e + f
}

解决办法: 使用JavaScript的抽象语法书(AST) 进行还原

使用特定符号编写 js 脚本

特征:

image-20191225190724159

原理:
jsfuck 源于一门编程语言 brainfuck ,其主要的思想就是只使用8种特定的符号来编写代码。而 jsfuck
也是沿用了这个思想,它仅仅使用6种符号来编写代码。它们分别是(、)、+、[、]、!。
常用混淆工具:http://www.jsfuck.com/

特殊转化规则

利用一些只能在浏览器中运行的特殊语句进行反扒,这种只能讲语法重新进行改写

在浏览器中 base64 编码转换使用的是

_0x1c0cdf = _0xcbc80b['atob'](_0x1c0cdf),

但是在nodejs调试的时候使用的是

Buffer.from(_0x1c0cdf,"base64").toString()