• 详解升讯威在线客服系统前端 JavaScript 脚本加密技术(1)


    我在业余时间开发维护了一款免费开源的升讯威在线客服系统,也收获了许多用户。对我来说,只要能获得用户的认可,就是我最大的动力。

    这段时间有几个技术小伙伴问了我一个有意思的问题:“你的前端脚本是怎么加密的?”

    我决定写帖子来分享这个问题的答案。

    在线客服系统访客端:

    在线客服系统客服端:


    免费在线使用 & 免费私有化部署:https://kf.shengxunwei.com


    视频实拍:演示升讯威在线客服系统在网络中断,直接禁用网卡,拔掉网线的情况下,也不丢消息,不出异常。
    https://blog.shengxunwei.com/Home/Post/fe432a51-337c-4558-b9e8-347b58cbcd53


    首先打开客服系统官方网站:https://kf.shengxunwei.com 然后查看嵌入的客服系统 JavaScript 文件,接着,用浏览器访问嵌入站点的 JavaScript 文件。可以看到,文件是加密和压缩过的:

    然后我们通过格式化工具,格式化看看:

    所有的变量名都经过了混淆:

    所有的函数名都经过了混淆:

    那么这是怎么做到的呢?我打算分成几篇不同的博文,循序渐进的解答这个问题,如何混淆前端 JavaScript 代码。

    JavaScript 语法解析

    JavaScript 是如何执行的

    对于常见编译型语言(例如:Java)来说,编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节码生成。

    对于解释型语言(例如 JavaScript)来说,通过词法分析 -> 语法分析 -> 语法树,就可以开始解释执行了。

    具体过程是这样的:

    1.词法分析是将字符流(char stream)转换为记号流(token stream)

    NAME "AST"  
    EQUALS  
    NAME "is Tree"  
    SEMICOLON
    

    2.语法分析成 AST (Abstract Syntax Tree)。

    3.预编译,当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理!并且是先预声明变量,再预定义函数!

    4.解释执行,在执行过程中,JavaScript 引擎是严格按着作用域机制(scope)来执行的,并且 JavaScript 的变量和函数作用域是在定义时决定的,而不是执行时决定的。JavaScript 中的变量作用域在函数体内有效,无块作用域;

    function func(){
        for(var i = 0; i < array.length; i++){  
          //do something here.  
        }
        //此时 i 仍然有值,及 i == array.length  
        console.log(i); // 但在 java 语言中,则无效
    }
    

    JavaScript 引擎通过作用域链(scope chain)把多个嵌套的作用域串连在一起,并借助这个链条帮助 JavaScript 解释器检索变量的值。这个作用域链相当于一个索引表,并通过编号来存储它们的嵌套关系。当 JavaScript 解释器检索变量的值,会按着这个索引编号进行快速查找,直到找到全局对象(global object)为止,如果没有找到值,则传递一个特殊的 undefined 值。

    var scope = "global";
    scopeTest();
    function scopeTest(){  
        console.log(scope);  
        var scope = "local";  
        console.log(scope); 
    }
    打印结果:undefined,local;
    

    我们常说的 V8 是 Google 发布的开源 JavaScript 引擎,采用 C++ 编写。SpiderMonkey(Mozilla,基于 C)、Rhino(Mozilla,基于 Java),而 Nodejs 依赖于 V8 引擎开发,接下来的内容是 JavaScript 在 V8 引擎中的运行状态,而类似的 JavaScript 现代引擎对于这些实现大同小异。

    在本文的开头提到了编译型语言,解释型语言。JavaScript 是解释型语言且弱类型,在生成 AST 之后,就开始一边解释,一边执行,但是有个弊端,当某段代码被多次执行时,它就有了可优化的空间(比如类型判断优化),而不用一次次的去重复之前的解释执行。 编译型语言如 JAVA,可以在执行前就进行优化编译,但是这会耗费大量的时间,显然不适用于 Web 交互。

    于是就有了,JIT(Just-in-time),JIT 是两种模式的混合。

    它是如何工作的呢:

    1.在 JavaScript 引擎中增加一个监视器(也叫分析器)。监视器监控着代码的运行情况,记录代码一共运行了多少次、如何运行的等信息,如果同一行代码运行了几次,这个代码段就被标记成了 “warm”,如果运行了很多次,则被标记成 “hot”。

    2.(基线编译器)如果一段代码变成了 “warm”,那么 JIT 就把它送到基线编译器去编译,并且把编译结果存储起来。比如,监视器监视到了,某行、某个变量执行同样的代码、使用了同样的变量类型,那么就会把编译后的版本,替换这一行代码的执行,并且存储。

    3.(优化编译器)如果一个代码段变得 “hot”,监视器会把它发送到优化编译器中。生成一个更快速和高效的代码版本出来,并且存储。例如:循环加一个对象属性时,假设它是 INT 类型,优先做 INT 类型的判断

    4.(去优化)可是对于 JavaScript 从来就没有确定这么一说,前 99 个对象属性保持着 INT 类型,可能第 100 个就没有这个属性了,那么这时候 JIT 会认为做了一个错误的假设,并且把优化代码丢掉,执行过程将会回到解释器或者基线编译器,这一过程叫做去优化。

    总结

    明白了基本原理之后,接下来就是如何执行混淆的过程了,

    未完待续。


    免费在线使用 & 免费私有化部署:https://kf.shengxunwei.com


    视频实拍:演示升讯威在线客服系统在网络中断,直接禁用网卡,拔掉网线的情况下,也不丢消息,不出异常。
    https://blog.shengxunwei.com/Home/Post/fe432a51-337c-4558-b9e8-347b58cbcd53


  • 相关阅读:
    Arnoldi Iteration 思考
    安卓手机获取root权限---修补面具root步骤解析
    求助帖:React Native failed installing Ruby Gems(rn 下载 Runby Gems 失败)
    区块链实训教程(6)--开发、编译、部署、调用HelloWorld合约
    计算机毕业设计(附源码)python疫情期间的校园防控管理系统
    $nextTick 原理及作用
    用好 DIV 和 API,在前端系统中轻松嵌入数据分析模块
    wireshark数据结构
    使用 NVIDIA CUDA-Pointpillars 检测点云中的对象
    PowerBI中导出数据方法汇总
  • 原文地址:https://www.cnblogs.com/sheng_chao/p/16445609.html