欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]一种基于编译器的的JS混淆及反混淆方案

[原创]一种基于编译器的的JS混淆及反混淆方案


阿特伍德定律指出,“任何可以用JavaScript来写的应用,最终都将用JavaScript来写”,5G的到来将导致大量的应用将不再native化,而从云端直接下载,JS作为与用户交互的入口大有一统所有编程语言的势头,很多大公司如华为正在为5G时代的大前端甚至物联网迫切需要的更高效的JS编译器进行超前的研发 https://www.zhipin.com/job_detail/a57f9c4f08e877531XJz29-8GFI~.html 。但JS表现出强大的统治力的同时,也面临着十分严峻的安全问题:代码直接暴露在浏览器中,因此对JS代码实施保护迫在眉睫。本文将从JS代码的混淆和反混淆两个方面,探讨JS代码的保护机制。


(一)JS代码保护若干技术难点

1.前端环境的复杂。以安卓端为例,不同版本的安卓系统,对webview的对JS代码调用逻辑也不同,给加密功能的开发带来很大挑战。

2.JS的标准在不停迭代。类似于python2,3的区别,JS也分成不同版本,保护方案如何能兼容不同的标准?

3.单线程与异步机制。尽管可以勉强实现前端的多线程,但是绝大部分情况下,浏览器内核单线程执行JS代码,异步编程给安全开发人员提出了新的难题


(二)调研和评估几种技术路线

我曾调研评估过以下几种可能的选择

1对安卓系统源码中对关键函数进行Hook,在JS文件加载前将其解密。虽然需要适配不同版本,但是有hook技术和源码研究的技术储备。

2类似之前分层加密引擎的加密方案

3走编译器路线,缺点是没有编译器经验,风险大

4找一个基于LLVM的JS的编译器parser,先将JS转成IR;接着用ollvm混淆后,再转回JS代码。但事实是,但调研后发现能把IR转JS, JS貌似没法转IR。也找到一个,但是好像没有经过时间检验,不敢用。

5直接再开源项目JavascriptObfuscator基础上做二次开发。但是该工程入口代码被混淆,逻辑框架大量使用很吃经验的写法,还是typrscript。在当时没有足够JS代码经验的情况下,暂时降低该方案的优先级。


(三)如何确定方案

之前我曾和老领导一起实现过一个循环递归加密引擎,本质上是自己实现一个简化的编译器前端。后来老领导跳槽出去赚大钱了,我只能独自维护这个项目,越往后越发觉这个引擎适用的范围比较小。如果继续下去,必须写更详细的词法分析,这一之前被简化的部分现在是不得不正面硬刚。

新方案调研过程中,我自己看UglifyJS的AST遍历框架源码。当我看到当前字母是p时接下来要判断29种不同情况时,我突然意识这条路大反向可能是错的。如果仅仅是词法生成器阶段就有这么多种case要处理,后面如果再遇到更特殊的语法组织形式呢?和未知的问题呢?

我是第一次学JS,我的视野一定是不全面的,一定会遇到很多未知的问题。一旦出现问题我的方案有后路吗?大方向上能继续兼容其他方案吗?这个技术路线能覆盖多大的攻击面?我深感这方案没有任何退路可走。向前几乎无路可走,向后无路可退,总之在没深入了解掌握JS这门语言前,不能低估JS语法的复杂性和特殊性,想搞JS保护,首先要会写JS,不能用C来搞。

无论是继续之前的方案,还是未将来新方案做打算,我都需要引入成熟的编译器,这是目前我能选择的既能打好基础,又能兼顾未来新方案的选择。要扎实的稳健的推进,厚积薄发

为了解决这些问题,一方面,由于从未学过编译原理,我得在网上学习《编译原理》相关课程,但是学术界的课程真的适合实际工程吗?为了快速做出判断,我以快进方式学习2倍速快放,经过三天学习网课《编译原理》后,我得出了然并卵的结论。继续学下去只能纠结在无关的细节上。

另一方面,我开始自学JS,推荐学习网易云课堂《JavaScript前端开发》

同时我开始开始尽可能收集网上的UglifyJS的例子,看别人的代码能否有帮助。但是把全网找遍了发现居然没有多少能利用的样本。

半个月过去,3条路有2条是废了,除了继续学习JS外我无从做起,研发一筹莫展。这时,我的老领导突然发来一个链接,说她最近在做JS反混淆,这个链接也是关于反混淆的,也许对我有帮助。老领导出去赚大钱也不忘小弟我呀!我点进去一看,立即心花怒放,这是一篇从反混淆的角度运用编译器的案例,里面包含了一些语法树操作。即便不多,但我就像抓住救命稻草一样扑了上去,如饥似渴的吸收着。

老领导真是我的福星。看完这篇文章,深感编译器存在目的在于,将所有JS代码按照统一的形式:抽象语法树AST进行组织和操作。此时我无需纠结编译器如何将其组织起来,因为词法分析器已经写好,我现在要尽可能的归纳和总结,编译器的组织规则,试图“看到”内在的规则。前方的路仿佛种豁然开朗,必须马不停蹄的向前推进。

经过不断努力,我总算搞清楚了语法树的组织规则,总结就是,“自上而下,层层包裹”


(四)如何研究AST

什么意思?请看一个具体例子console.log(“hello world”);


上图是这行代码的AST。我们看到,Program,ExpressionStatment,CallExpression是对这单行代码更上层的封装,而这句话这句话本身是一个CallExpression,他里面包含了MemberExpression以及arguments,分别对应了console.log和(“hello world”)

我们把所有的节点都详细打印出来,这里我使用的编译框架是esprima,大家可以选择uglifyjs等其他遍历工具。


再看一下输出结果

***this is new node***

{ type: 'Identifier', name: 'console' }

***this is new node***

{ type: 'Identifier', name: 'log' }

***this is new node***

{ type: 'MemberExpression',

  computed: false,

  object: { type: 'Identifier', name: 'console' },

  property: { type: 'Identifier', name: 'log' } }

***this is new node***

{ type: 'Literal', value: 'hello world' }

***this is new node***

{ type: 'CallExpression',

  callee:

   { type: 'MemberExpression',

     computed: false,

     object: { type: 'Identifier', name: 'console' },

     property: { type: 'Identifier', name: 'log' } },

  arguments:

   [ { type: 'MemberExpression',

       computed: false,

       object: [Object],

       property: [Object] } ] }

***this is new node***

{ type: 'ExpressionStatement',

  expression:

   { type: 'CallExpression',

     callee:

      { type: 'MemberExpression',

        computed: false,

        object: [Object],

        property: [Object] },

     arguments: [ [Object] ] } }

***this is new node***

{ type: 'Program',

  body: [ { type: 'ExpressionStatement', expression: [Object] } ] }

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功