操作符混淆工具

1 序

1.1 从一段神奇的JS代码说起

前段时间在公众号看到一段神奇的代码,它长这个样子:

(!(~+[])+{})[--[~[]][+[]]*[~+[]]+~~!+[]]+({}+[])[[~!+[]]*~+[]]

如果把这行代码扔到浏览器里面执行以下,就会输出sb字符串。

这是什么鬼,还有这种操作?

1.2 走进科学时间

上面的代码由!()*+-[]{}~这11种符号组成,其实这些符号都是JS的操作符,而上面的代码在执行后转换成字符串则是因为:

  • 当操作符作用的操作数类型不一致或者不是基本类型时,JS将自动完成类型转化;
  • 不同的操作符具有不同的优先级

将上面的代码按照 操作符优先级 进行区域划分,大致可以分为以下的几个部分。

可以看到实际上就是应用JS的类型隐式转换生成字符串,然后从字符串里提取想要的字符。 至于为什么上图的叶节点为什么是这样生成的值,请参照 es5.github.io/ 9 Type Conversion and Testing

2 操作符代码混淆器

收到前文的启发,本人萌发了一种“操作符代码混淆器”的想法。也就是利用上文提及的原理,将JS代码混淆成全部由操作符组成的“让人看着头疼的代码”。

这意味着一些简单的字符层面上的代码注入防范工作完全无法对我们的代码生效,因为我们的代码完全由“操作符”构成,根本就不包含敏感关键字。

"操作符代码混淆器"需要解决几个关键性的问题:

  • 操作符生成其他字符
  • 字符串组装成可执行代码

2.1 数字

生成数字实际上只要有一个数字0,我们完成可以通过自增操作符++生成数字1-9,所以我们只需要

// '数字集合'
$ = +[]; // 0
$ = {
    _: $++, // 0
    __: $++, // 1
    ___: $++, // 2
    ____: $++, // 3
    _____: $++, // 4
    $_: $++, // 5
    $__: $++, // 6
    $___: $++, // 7
    $____: $++, // 8
    $_____: $++, // 9
}

2.2 英文字符

从前文我们可以知道其逻辑是根据不同的类型转换成字符串时可以生成不同的描述性字符串

{} + [] // '[object Object]'
![] +[] // 'false'
!![] + [] // 'true'
[][$._] + [] //'undefined'

从上面我们可以得到 a b c d e f i j l n o r s t u这些字符。但是这很明显没办法包含所有英文字符,同时也没办法表示换行符等特殊字符。

2.3 通用字符

所以我们需要一个更加通用的方案来通过操作符生成其他字符。 基于我们现在已经得到的数字字符,我们可以使用八进制的表示方式来生成其他ASCII字符。

'\\' + $.__ + $._____ + $.$__ + '\\' // '\147' 即 g
'\\' + $.__ + $.$ + $._              // '\150' 即 h , 以此类推

但是这也随之带来一个问题,那就是我们为了使用八进制来表达ASCII字符,引入了'\\'这种常量字符串,这使得整个计划优点不完美,但是作者目前没有想到更好的实现方式。

// '字符集合'
$$ = '\\';

$$ = {
    _: $$ + $.__ + $._____ + $.__, // 141 a
    __: $$ + $.__ + $._____ + $.___, // 142 b
    ___: $$ + $.__ + $._____ + $.____, // 143 c
    ____: $$ + $.__ + $._____ + $._____, // 144 d
    _____: $$ + $.__ + $._____ + $.$_, // 145 e
    $_: $$ + $.__ + $._____ + $.$__, // 146 f
    $__: $$ + $.__ + $._____ + $.$___, // 147 g
    $___: $$ + $.__ + $.$_ + $._, // 150 h
    $____: $$ + $.__ + $.$_ + $.__, // 151 i
    $_____: $$ + $.__ + $.$_ + $.___, // 152 j
    $$_: $$ + $.__ + $.$_ + $.____, // 153 k
    $$__: $$ + $.__ + $.$_ + $._____, // 154 l
    $$___: $$ + $.__ + $.$_ + $.$_, // 155 m
    $$____: $$ + $.__ + $.$_ + $.$__, // 156 n
    $$_____: $$ + $.__ + $.$_ + $.$___, // 157 o
    $$$_: $$ + $.__ + $.$__ + $._, // 160 p
    $$$__: $$ + $.__ + $.$__ + $.__, // 161 q
    $$$___: $$ + $.__ + $.$__ + $.___, // 162 r
    $$$____: $$ + $.__ + $.$__ + $.____, // 163 s
    $$$_____: $$ + $.__ + $.$__ + $._____, // 164 t
    $$$$_: $$ + $.__ + $.$__ + $.$_, // 165 u
    $$$$__: $$ + $.__ + $.$__ + $.$__, // 166 v
    $$$$___: $$ + $.__ + $.$__ + $.$___, // 167 w
    $$$$____: $$ + $.__ + $.$___ + $._, // 170 x
    $$$$_____: $$ + $.__ + $.$___ + $.__, // 171 y
    $$$$$_: $$ + $.__ + $.$___ + $.___ // 172 z
}

2.4 执行容器

前文我们只是将代码内容转换成了字符串的形式,这么一个字符串还需要能够跑起来。 好,我们的第一想法可能是eval方法

eval() 函数可计算某个字符串,并执行其中的的 JavaScript代码。

假设我们已经将代码转换成了字符串,但是下面的用户调用方式未免显得太过没有逼格。

var codeStr = '混淆过的代码';
// 用户调用
eval(codeStr)

所以我们的目标是生成的代码用户可以直接扔到浏览器里面开始执行,即我们需要一个可以执行的函数容器:

// 字符串集合
$$$ = {
    _: {} + [], // '[object Object]'
    __: ![] + [], // 'false'
    ___: !![] + [], // 'true'
    ____: [][$._] + [] //'undefined'
}

// 替换字符集, 这里不替换的话无法根据constructor找到Function
$$.___ = $$$._[$.$_]; // c
$$._____ = $$$.___[$.____] // e
$$.$$____ = $$$.____[$.__]; //n 
$$.$$_____ = $$$._[$.__]; // o
$$.$$$___ = $$$.___[$.__]; // r
$$.$$$____ = $$$.__[$.____]; // s
$$.$$$_____ = $$$.___[$._]; // t
$$.$$$$_ = $$$.____[$._]; // u


$$$._____ = $$.___ + $$.$$_____ + $$.$$____ + $$.$$$____ + $$.$$$_____ + $$.$$$___ + $$.$$$$_ + $$.___ + $$.$$$_____ + $$.$$_____ + $$.$$$___; // 'constructor'
$$$.$_ = $$.$$$___ + $$._____ + $$.$$$_____ + $$.$$$$_ + $$.$$$___ + $$.$$____; // 'return'

$$$.$__ = ($._)[$$$$._][$$$$._] // Function

// 执行容器调用方式
$$$.$__($$$.$__($$$.$_ + '\"' + '这里是经过混淆后的代码' + '\"'())();
// 实际上就是
// Function(Function()('return \"' + '这里是经过混淆后的代码' + '\"')())()

3 结论

通过以上实现,基本实现了一个简单代码混淆工具的逻辑,可以只使用操作符对代码进行混淆,但依旧遗留了一些问题

  • 代码依赖字符串,生成的代码也会包含字符串常量,并不是完全的“操作符化”;
  • 工具的代码本身很难阅读,使得维护和开发非常困难,这个工作可以依赖构建工具进行优化;
  • 目前只包含了对ASCII字符的处理,对字符集以外字符的处理是有问题的;
  • 本工具的应用场景具有局限性

4 相关资料

一段神奇的javascript代码 运算符优先级 Annotated ECMAScript 5.1 - 9 Type Conversion and Testing jjencode JS代码加密混淆工具 jjencode

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【答疑解惑第三十一讲】如何使用全局变量?

疑惑一 全局变量如何用? ? 看图中的箭头的地方,声明全局变量的时候使用了static,这个问题就涉及到了static的使用,如果要使用全局变量,并且在多文件中...

3419
来自专栏吴伟祥

Java 8 新特性 转

Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支...

935
来自专栏V站

PHP反序列化深入理解

在PHP中右serialize()和unserialize()两个函数,php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。u...

1545
来自专栏ml

C与C++在const用法上的区别

       首先,C和C++在大体结构上不同,却在语法上相同。  所以在使用的时候,我们会时常遇到一些莫名其妙的问题,觉得语法上是正确的,但是编译的时候却出现...

3134
来自专栏xingoo, 一个梦想做发明家的程序员

const指南

基本词义  意思就就是说利用const进行修饰的变量的值在程序的任意位置将不能再被修改,就如同常数一样使用!  使用方法 const int a=1;//这里定...

18710
来自专栏C/C++基础

C++ new的三种面貌

C++中使用new运算符产生一个存在于Heap(堆)上对象时,实际上调用了operator new()函数和placement new()函数。在使用new创建...

911
来自专栏微信公众号:Java团长

触摸Java常量池

java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,这次好好总结一下。

991
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-day01-基础题

1. 简述java语言,具有哪些特性? (1).java语言是简单的 java语言是和c++语言类似的,其次java中丢弃了c++中一些难理解的特性,比如运算符...

2684
来自专栏SHERlocked93的前端小站

JS 利用高阶函数实现函数缓存(备忘模式)

高阶函数就是那种输入参数里面有一个或者多个函数,输出也是函数的函数,这个在js里面主要是利用闭包实现的,最简单的就是经常看到的在一个函数内部输出另一个函数,比如

3233
来自专栏思考的代码世界

Python编程从入门到实践之字典|第5天

在Python中,字典是一系列键—值对。每个键都与一个值相关联,你可以使用键来访问与之 相关联的值。与键相关联的值可以是数字、字符串、列表乃至字典。事实上,可将...

3509

扫码关注云+社区