JavaScript殽杂平安加固

媒介

在平安攻防疆场中,前端代码都是公然的,那末对前端举行加密有意义吗?能够大部份人的回复是,毫无意义,不要自创加密算法,直接用HTTPS吧。但事实上,纵然不相识密码学,也应晓得是有意义的,因为加密前解密后的环节,是不受庇护的。HTTPS只能庇护传输层,另外别无用途。

而加密环节又分:

  • 传输加密(匹敌链路破解)
  • 数据加密(匹敌协定破解)
  • 代码加密(隐藏算法、反调试…)

本文重要枚举一些我见到的,我想到的一些加密体式格局,实在确切的说,应当叫殽杂,不应当叫加密。

那末,代码殽杂的细致道理是什么?实在很简单,就是去除代码中尽量多的有意义的信息,比方诠释、换行、空格、代码负号、变量重命名、属性重命名(许可的状况下)、无用代码的移除等等。因为代码是公然的,我们必需认可没有任何一种算法能够完整不被破解,所以,我们只能尽量增添攻击者浏览代码的本钱。

原文地点

语法树AST殽杂

在保证代码底本的功用性的状况下,我们能够对代码的AST按需举行变动,然后将变动后的AST在天生一份代码举行输出,抵达殽杂的目标,我们最经常运用的uglify-js就是如许对代码举行殽杂的,固然uglify-js的殽杂只是重要举行代码紧缩,即我们下面讲到的变量名殽杂。

变量名殽杂

将变量名殽杂成浏览比较难浏览的字符,增添代码浏览难度,上面说的uglify-js举行的殽杂,就是把变量殽杂成了短名(重要是为了举行代码紧缩),而如今大部份平安方向的殽杂,都邑将其殽杂成类16进制变量名,效果以下:

var test = 'hello';

殽杂后:

var _0x7deb = 'hello';

注重事项:

  1. eval语法,eval函数中能够运用了本来的变量名,假如不对其举行处置惩罚,能够会运转报错,以下:

    var test = 'hello';
    eval('console.log(test)');

    假如不对eval中的console.log(test)举行关联的殽杂,则会报错。不过,假如eval语法超出了静态剖析的领域,比方:

    var test = 'hello';
    var variableName = 'test';
    eval('console.log(' + variableName + ')');

    这类咋办呢,能够要举行遍历AST找到其运转效果,然后在举行殽杂,不过貌似本钱比较高。

  2. 全局变量的编码,假如代码是作为SDK举行输出的,我们需要保留全局变量名的稳定,比方:

    <script>
    var $ = function(id) {
        return document.getElementById(id);
    };
    </script>

    $变量是放在全局下的,殽杂事后以下:

    <script>
    var _0x6482fa = function(id) {
        return document.getElementById(id);
    };
    </script>

    那末假如依靠这一段代码的模块,运用$('id')挪用自然会报错,因为这个全局变量已被殽杂了。

常量提取

将JS中的常量提取到数组中,挪用的时刻用数组下标的体式格局挪用,如许的话直接读懂基础不能够了,要么反AST处置惩罚下,要么一步一步调试,事情量大增。

以上面的代码为例:

var test = 'hello';

殽杂事后:

var _0x9d2b = ['hello'];

var _0xb7de = function (_0x4c7513) {
    var _0x96ade5 = _0x9d2b[_0x4c7513];
    return _0x96ade5;
};

var test = _0xb7de(0);

固然,我们能够依据需求,将数组转化为二位数组、三维数组等,只需要在需要用到的处所猎取就能够。

常量殽杂

将常量举行加密处置惩罚,上面的代码中,虽然已是殽杂事后的代码了,然则hello字符串照样以明文的情势出如今代码中,能够应用JS中16进制编码会直接解码的特性将关键字的Unicode举行了16进制编码。以下:

var test = 'hello';

连系常量提取取得殽杂效果:

var _0x9d2b = ['\x68\x65\x6c\x6c\x6f'];

var _0xb7de = function (_0x4c7513) {
    _0x4c7513 = _0x4c7513 - 0x0;
    var _0x96ade5 = _0x9d2b[_0x4c7513];
    return _0x96ade5;
};

var test = _0xb7de('0x0');

固然,除了JS特性自带的Unicode自动剖析之外,也能够自定义一些加解密算法,比方对常量举行base64编码,或许其他的什么rc4等等,只需要运用的时刻解密就OK,比方上面的代码用base64编码后:

var _0x9d2b = ['aGVsbG8=']; // base64编码后的字符串

var _0xaf421 = function (_0xab132) {
    // base64解码函数
    var _0x75aed = function(_0x2cf82) {
        // TODO: 解码
    };
    return _0x75aed(_0xab132);
}

var _0xb7de = function (_0x4c7513) {
    _0x4c7513 = _0x4c7513 - 0x0;
    var _0x96ade5 = _0xaf421(_0x9d2b[_0x4c7513]);
    return _0x96ade5;
};

var test = _0xb7de('0x0');

运算殽杂

将一切的逻辑运算符、二元运算符都变成函数,目标也是增添代码浏览难度,让其没法直接经由过程静态剖析取得效果。以下:

var i = 1 + 2;
var j = i * 2;
var k = j || i;

殽杂后:

var _0x62fae = {
    _0xeca4f: function(_0x3c412, _0xae362) {
        return _0x3c412 + _0xae362;
    },
    _0xe82ae: function(_0x63aec, _0x678ec) {
        return _0x63aec * _0x678ec;
    },
    _0x2374a: function(_0x32487, _0x3a461) {
        return _0x32487 || _0x3a461;
    }
};

var i = _0x62fae._0e8ca4f(1, 2);
var j = _0x62fae._0xe82ae(i, 2);
var k = _0x62fae._0x2374a(i, j);

固然除了逻辑运算符和二元运算符之外,还能够将函数挪用、静态字符串举行相似的殽杂,以下:

var fun1 = function(name) {
    console.log('hello, ' + name);
};
var fun2 = function(name, age) {
    console.log(name + ' is ' + age + ' years old');
}

var name = 'xiao.ming';
fun1(name);
fun2(name, 8);
var _0x62fae = {
    _0xe82ae: function(_0x63aec, _0x678ec) {
        return _0x63aec(_0x678ec);
    },
    _0xeca4f: function(_0x92352, _0x3c412, _0xae362) {
        return _0x92352(_0x3c412, _0xae362)
    },
    _0x2374a: 'xiao.ming',
    _0x5482a: 'hello, ',
    _0x837ce: ' is ',
    _0x3226e: ' years old'
};

var fun1 = function(name) {
    console.log(_0x62fae._0x5482a + name);
};
var fun2 = function(name, age) {
    console.log(name + _0x62fae._0x837ce + age + _0x62fae._0x3226e);
}

var name = _0x62fae._0x2374a;
_0x62fae._0xe82ae(fun1, name);
_0x62fae._0xeca4f(fun2, name, 0x8);

上面的例子中,fun1和fun2内的字符串相加也会被殽杂走,静态字符串也会被前面提到的字符串提取抽取到数组中(我就是懒,这部份代码就不写了)。

需要注重的是,我们每次碰到雷同的运算符,需不需要从新天生函数举行替代,这就按个人需求了。

语法丑化

将我们经常运用的语法殽杂成我们不经常运用的语法,前提是不转变代码的功用。比方for换成do/while,以下:

for (i = 0; i < n; i++) { 
    // TODO: do something
}

var i = 0;
do {
    if (i >= n) break;
    
    // TODO: do something
    i++;
} while (true)

动态实行

将静态实行代码增加动态推断,运转时动态决议运算符,滋扰静态剖析。

以下:

var c = 1 + 2;

殽杂事后:

function _0x513fa(_0x534f6, _0x85766) { return _0x534f6 + _0x85766; }
function _0x3f632(_0x534f6, _0x534f6) { return _0x534f6 - _0x534f6; }

// 动态剖断函数
function _0x3fa24() {
    return true;
}

var c = _0x3fa24() ? _0x513fa(1, 2) : _0x3f632(1, 2);

流程殽杂

对实行流程举行殽杂,又称掌握流扁平化,为何要做殽杂实行流程呢?因为在代码开辟的过程当中,为了使代码逻辑清楚,便于保护和扩大,会把代码编写的逻辑异常清楚。一段代码从输入,经由种种if/else分支,递次实行以后取得差别的效果,而我们需要将这些实行流程和剖断流程举行殽杂,让攻击者没那末随意马虎摸清楚我们的实行逻辑。

掌握流扁平化又分递次扁平化、前提扁平化,

递次扁平化

望文生义,将按递次、自上而下实行的代码,分解成数个分支举行实行,以下代码:

(function () {
    console.log(1);
    console.log(2);
    console.log(3);
    console.log(4);
    console.log(5);
})();

流程图以下:

《JavaScript殽杂平安加固》

殽杂事后代码以下:

(function () {
    var flow = '3|4|0|1|2'.split('|'), index = 0;
    while (!![]) {
        switch (flow[index++]) {
        case '0':
            console.log(3);
            continue;
        case '1':
            console.log(4);
            continue;
        case '2':
            console.log(5);
            continue;
        case '3':
            console.log(1);
            continue;
        case '4':
            console.log(2);
            continue;
        }
        break;
    }
}());

殽杂事后的流程图以下:

《JavaScript殽杂平安加固》

流程看起来了。

前提扁平化

前提扁平化的作用是把一切if/else分支的流程,悉数扁平到一个流程中,在流程图中具有雷同的进口和出口。

以下面的代码:

function modexp(y, x, w, n) {
    var R, L;
    var k = 0;
    var s = 1;
    while(k < w) {
        if (x[k] == 1) {
            R = (s * y) % n;
        }
        else {
            R = s;
        }
        s = R * R % n;
        L = R;
        k++;
    }
    return L;
}

如上代码,流程图是如许的

《JavaScript殽杂平安加固》

掌握流扁平化后代码以下:

function modexp(y, x, w, n) {
    var R, L, s, k;
    var next = 0;
    for(;;) {
        switch(next) {
        case 0: k = 0; s = 1; next = 1; break;
        case 1: if (k < w) next = 2; else next = 6; break;
        case 2: if (x[k] == 1) next = 3; else next = 4; break;
        case 3: R = (s * y) % n; next = 5; break;
        case 4: R = s; next = 5; break;
        case 5: s = R * R % n; L = R; k++; next = 1; break;
        case 6: return L;
        }
    }
}

殽杂后的流程图以下:

《JavaScript殽杂平安加固》

直观的觉得就是代码变了,一切的代码都挤到了一层当中,如许做的优点在于在让攻击者没法直观,或经由过程静态剖析的要领推断哪些代码先实行哪些后实行,必需要经由过程动态运转才纪录实行递次,从而加重了剖析的累赘。

需要注重的是,在我们的流程中,无论是递次流程照样前提流程,假如涌现了块作用域的变量声明(const/let),那末上面的流程扁平化将会涌现毛病,因为switch/case内部为块作用域,表达式被分到case内部以后,其他case没法取到const/let的变量声明,自然会报错。

不通明谓词

上面的switch/case的推断是经由过程数字(也就是谓词)的情势推断的,而且是通明的,能够看到的,为了越发的殽杂黑白,能够将case推断设定为表达式,让其没法直接推断,比方应用上面代码,改成不通明谓词:

function modexp(y, x, w, n) {
    var a = 0, b = 1, c = 2 * b + a;
    var R, L, s, k;
    var next = 0;
    for(;;) {
        switch(next) {
        case (a * b): k = 0; s = 1; next = 1; break;
        case (2 * a + b): if (k < w) next = 2; else next = 6; break;
        case (2 * b - a): if (x[k] == 1) next = 3; else next = 4; break;
        case (3 * a + b + c): R = (s * y) % n; next = 5; break;
        case (2 * b + c): R = s; next = 5; break;
        case (2 * c + b): s = R * R % n; L = R; k++; next = 1; break;
        case (4 * c - 2 * b): return L;
        }
    }
}

谓词用a、b、c三个变量构成,以至能够把这三个变量隐藏到全局中定义,或许隐藏在某个数组中,让攻击者不能那末随意马虎找到。

剧本加壳

将剧本举行编码,运转时 解码 再 eval 实行如:

eval (…………………………..……………. ……………. !@#$%^&* ……………. .…………………………..……………. )

然则实际上如许意义并不大,因为攻击者只需要把alert或许console.log就暴露无遗了

革新计划:应用Function / (function(){}).constructor将代码当作字符串传入,然后实行,以下:

var code = 'console.log("hellow")';
(new Function(code))();

如上代码,能够对code举行加密殽杂,比方aaencode,道理也是云云,我们举个例子

alert("Hello, JavaScript");

应用aaencode殽杂事后,代码以下:

゚ω゚ノ= /`m´)ノ ~┻━┻   //*´∇`*/ ['_']; o=(゚ー゚)  =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_');

这段代码看起来很新鲜,不像是JavaScript代码,然则实际上这段代码是用一些看似脸色的标记,声清楚明了一个16位的数组(用来示意16进制位置),然后将code当作字符串遍历,把每一个代码标记经由过程string.charCodeAt取这个16位的数组下标,拼接成代码。也许的意义就是把代码当作字符串,然后运用这些标记的拼接替代这一段代码(能够看到代码里有许多加号),末了,经由过程(new Function(code))('_')实行。

仔细观察上面这一段代码,把代码末了的('_')去掉,在运转,你会直接看到源代码,然后Function.constructor存在(゚Д゚)变量中,感兴趣的同砚能够自行检察。

除了aaencode,jjencode道理也是差不多,就不做诠释了,其他更霸气的jsfuck,这些都是对代码举行加密的,这里就不细致引见了。

反调试

因为JavaScript自带debugger语法,我们能够应用死循环性的debugger,当页面翻开调试面板的时刻,无穷进入调试状况。

定时实行

在代码最先实行的时刻,运用setInterval定时触发我们的反调试函数。

随机实行

在代码天生阶段,随机在部份函数体中注入我们的反调试函数,当代码实行到特定逻辑的时刻,假如调试面板在翻开状况,则无穷进入调试状况。

内容监测

因为我们的代码能够已反调试了,攻击者能够会将代码拷贝到本身当地,然后修正,调试,实行,这个时刻就需要增加一些检测举行剖断,假如不是平常的环境实行,那让代码自行失利。

代码自检

在代码天生的时刻,为函数天生一份Hash,在代码实行之前,经由过程函数 toString 要领,检测代码是不是被改动

function module() {
    // 改动校验
    if (Hash(module.toString()) != 'JkYxnHlxHbqKowiuy') {
        // 代码被改动!
    }
}

环境自检

搜检当前剧本的实行环境,比方当前的URL是不是在许可的白名单内、当前环境是不是平常的浏览器。

假如为Nodejs环境,假如涌现异常环境,以至我们能够启动木马,历久跟踪。

废代码注入

插进去一些永久不会发作的代码,让攻击者在剖析代码的时刻被这些无用的废代码殽杂黑白,增添浏览难度。

废逻辑注入

与废代码相对峙的就是有效的代码,这些有效的代码代表着被实行代码的逻辑,这个时刻我们能够网络这些逻辑,增添一段剖断来决议实行真逻辑照样假逻辑,以下:

(function(){
    if (true) {
        var foo = function () {
            console.log('abc');
        };
        var bar = function () {
            console.log('def');
        };
        var baz = function () {
            console.log('ghi');
        };
        var bark = function () {
            console.log('jkl');
        };
        var hawk = function () {
            console.log('mno');
        };
 
        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

能够看到,一切的console.log都是我们的实行逻辑,这个时刻能够网络一切的console.log,然后制造假剖断来实行真逻辑代码,网络逻辑注入后以下:

(function(){
    if (true) {
        var foo = function () {
            if ('aDas' === 'aDas') {
                console.log('abc');
            } else {
                console.log('def');
            }
        };
        var bar = function () {
            if ('Mfoi' !== 'daGs') {
                console.log('ghi');
            } else {
                console.log('def');
            }
        };
        var baz = function () {
            if ('yuHo' === 'yuHo') {
                console.log('ghi');
            } else {
                console.log('abc');
            }
        };
        var bark = function () {
            if ('qu2o' === 'qu2o') {
                console.log('jkl');
            } else {
                console.log('mno');
            }
        };
        var hawk = function () {
            if ('qCuo' !== 'qcuo') {
                console.log('jkl');
            } else {
                console.log('mno');
            }
        };
 
        foo();
        bar();
        baz();
        bark();
        hawk();
    }
})();

剖断逻辑中天生了一些字符串,在没有运用字符串提取的状况下,这是能够经由过程代码静态剖析来取得实在的实行逻辑的,或许我们能够运用上文讲到的动态实行来决议实行真逻辑,能够看一下运用字符串提取和变量名编码后的效果,以下:

var _0x6f5a = [
    'abc',
    'def',
    'caela',
    'hmexe',
    'ghi',
    'aaeem',
    'maxex',
    'mno',
    'jkl',
    'ladel',
    'xchem',
    'axdci',
    'acaeh',
    'log'
];
(function (_0x22c909, _0x4b3429) {
    var _0x1d4bab = function (_0x2e4228) {
        while (--_0x2e4228) {
            _0x22c909['push'](_0x22c909['shift']());
        }
    };
    _0x1d4bab(++_0x4b3429);
}(_0x6f5a, 0x13f));
var _0x2386 = function (_0x5db522, _0x143eaa) {
    _0x5db522 = _0x5db522 - 0x0;
    var _0x50b579 = _0x6f5a[_0x5db522];
    return _0x50b579;
};
(function () {
    if (!![]) {
        var _0x38d12d = function () {
            if (_0x2386('0x0') !== _0x2386('0x1')) {
                console[_0x2386('0x2')](_0x2386('0x3'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x4'));
            }
        };
        var _0x128337 = function () {
            if (_0x2386('0x5') !== _0x2386('0x6')) {
                console[_0x2386('0x2')](_0x2386('0x4'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x7'));
            }
        };
        var _0x55d92e = function () {
            if (_0x2386('0x8') !== _0x2386('0x8')) {
                console[_0x2386('0x2')](_0x2386('0x3'));
            } else {
                console[_0x2386('0x2')](_0x2386('0x7'));
            }
        };
        var _0x3402dc = function () {
            if (_0x2386('0x9') !== _0x2386('0x9')) {
                console[_0x2386('0x2')](_0x2386('0xa'));
            } else {
                console[_0x2386('0x2')](_0x2386('0xb'));
            }
        };
        var _0x28cfaa = function () {
            if (_0x2386('0xc') === _0x2386('0xd')) {
                console[_0x2386('0x2')](_0x2386('0xb'));
            } else {
                console[_0x2386('0x2')](_0x2386('0xa'));
            }
        };
        _0x38d12d();
        _0x128337();
        _0x55d92e();
        _0x3402dc();
        _0x28cfaa();
    }
}());

求值圈套

除了注入实行逻辑之外,还能够埋入一个隐藏的圈套,在一个永不抵达没法静态剖析的分支里,援用该函数,正经常运用户不会实行,而 AST 遍历求值时,则会触发圈套!圈套能干啥呢?

  • 日记上报,实时相识状况
  • 在当地存储隐写特性,历久跟踪
  • 开释CSRF破绽,取得破解者的细致信息
  • 开启自尽顺序(页面崩溃、死循环、耗尽内存等)

加壳滋扰

在代码用eval包裹,然后对eval参数举行加密,并埋下圈套,在解码时插进去无用代码,滋扰显现,大批换行、诠释、字符串等大批特别字符,致使显现卡顿。

完毕

也许我想到的殽杂就包含这些,单个特性运用的话,殽杂效果平常,各个特性组合起来用的话,终究效果很明显,固然这个看个人需求,毕竟殽杂是个双刃剑,在增添了浏览难度的同时,也增大了剧本的体积,降低了代码的运转效力。

参考文献

代码殽杂之道——掌握流扁平与不通明谓词理论篇

    原文作者:_杨溜溜
    原文地址: https://segmentfault.com/a/1190000019423501
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞