2017-06-28 13:00:00

JavaScript代码的逆向读取

uglify
minify

前言

javascript的压缩混淆随处可见,包括uglify、compressor、clouser等。

而uglify带来的好处很多,除了压缩代码体积之外,还可以维持源代码的可读性。

目前社交类网站如FaceBook、微博等,出于保护前端(业务或者技术)逻辑的目的, 生产代码的源码并没有开源。 了解混淆原理可以帮助我们进行代码逻辑的逆推,重塑源代码,学到更多“非开源”的内容。

本篇文章主要是围绕新浪微博前端代码为例子,进行逆向读取的语法分析。

1. if条件句

当条件执行语句只有一行时,转换为条件赋值语句(conditional/ternary Operator)

var s = false;
if(s) {
    console.log(123);
}
 
// after uglification
 
var s=!1;s&&console.log(123);

语句短路

var s = true;
if(!s) {
    console.log(123);
}
 
// after uglification
 
var s=!0;s||console.log(123);
var a = true;
var b = false;
if(!a) {
   if(!b) {
       console.log(123);
   }
}
 
// after uglification
 
var a=!0,b=!1;a||b||console.log(123);

2. 函数

2.1 函数声明

主要是参数名称的简化。

function add(num1, num2) {
  return num1 + num2;
}
 
// after uglification
 
function add(a,t){return a+t}

2.2 Self-invoking functions

通过“!”触发函数

(function(num1, num2) {
   return num1 + num2;
})(2,3);
 
// after uglification
 
!function(a,t){return a+t}(2,3);

2.3 破坏可读性的混淆

这种方式虽然对可读性进行了破坏,但是大多数情况,恢复起来的难度并不大。

var a = document.getElementById('a');
a.innerHTML = 'hello world';
 
// after uglification
 
(function(a, b, c, d, e, f){
    a[d] = a[b][c](d);
    a[d][e]=f;
})(this, 'document', 'getElementById', 'a', 'innerHTML', 'hello world');

3. 变量

3.1 作用域区分

由于压缩需要对现有变量名称进行改写,所以会出现相同的名称出现在不同区域,这时候的结局方案是:“就近原则”

下图例子代码来自weibo.com页面源码。

var a = $CONFIG, // a表示变量$CONFIG
    b = FM.view;
FM.view = function(a) {
    a = a || {}; // a表示方程的第一个argument,而不是外面的$CONFIG
    return a;
}

3.2 赋值

赋值操作的返回值是等号右边的引用,所以多重引用传递写成一行。

下图例子代码来自weibo.com页面源码。

m = k.onerror = k.onload = k.onreadystatechange = function() {
    // ... ...
    // function body
    // ... ...
}

总结

本文主要涉及的是传统JavaScript的逆向读取的几个常见情况,稍后我会更新es2015的加密与混淆。