一种基于前端字节码技术的JavaScript虚拟化保护方法与流程

文档序号:15588913发布日期:2018-10-02 18:43阅读:1890来源:国知局

本发明属于计算机软件代码保护领域。如何在源码传输的前端环境中保护javascript应用代码的安全,阻止其被恶意的分析和利用,对保护网络服务提供者的权益至关重要。针对这一严峻问题,本发明通过对javascript代码保护技术的攻防机制研究,进而设计出更有效的代码保护方案,加大攻击难度。



背景技术:

在web发展早期,javascript在web系统中承担的职责不多,只是简单的提交表单,文件也非常简单,因此也不需要任何的保护。而在web应用越来越丰富的今天,伴随着浏览器性能和网速的提高,javascript所承载的工作变得越来多,也越来越重要。不少后端逻辑都在逐渐向前端转移,与此同时也让更多的不法分子有机可乘。在web模型中,javascript往往是不法分子的第一个突破口。知晓了前端逻辑,不法分子可以模拟成一个正常的用户来实施自己的恶意行为。所以,在很多登录、注册、支付、交易等等页面中,关键业务所依赖的javascript都不希望被人轻易的破解,javascript保护应运而生。

由于javascript传输的就是源码,在前端对抗中,源代码的暴露使其安全性十分脆弱,因此一直以来,javascript代码的保护广受学术界和工业界的关注。而目前主流的javascript代码保护措施主要为精简、加密和混淆,这些方法的核心思想主要借鉴自传统的软件代码保护技术。javascript属于脚本语言,在传输过程中就是带有语法属性的文本源码,逆向分析要比传统的经过编译的二进制应用程序更加容易,再加上浏览器的发展,调试器的功能越来越完善,这些保护方法很难起到很好的保护效果。



技术实现要素:

针对目前javascript代码保护方法存在的缺陷,本发明的目的是提出一种基于前端字节码技术webassembly的javascript代码虚拟化保护方法jsvmp(virtualmachinebasedcodeprotectionforjavascript),在牺牲可接受的性能开销的情况下,本方法可以有效的防止当前的分析工具,可以起到很好的保护效果。

为了实现上述任务,本发明采用以下技术方案:

一种基于前端字节码技术的javascript代码虚拟化保护方法,包括以下步骤:

步骤1,将需要保护的javascript代码划分成不同的代码段,根据代码段中是否存在dom属性将代码段分为属性及方法型、计算密集型;

步骤2,分别根据属性及方法型代码段、计算密集型代码段实现的功能,生成其对应的解释程序handler;

步骤3,对需要保护的javascript代码的抽象语法树进行拆分,得到中间代码;对所述的解释程序handler生成其对应的虚拟指令并进行编码,利用所述的虚拟指令表示所述的中间代码,从而得到虚拟化的代码;

步骤4,分别建立计算密集型代码段的虚拟解释器以及属性及方法型代码段的虚拟解释器并进行编译。

进一步地,步骤2中计算密集型代码段按照其实现的功能分为数据转移指令、控制转移指令和运算指令,其中:

数据转移类指令的handler用于处理数据在各存储结构之间的转移;

控制转移指令的handler用于完成跳转相关的功能,通过跳转偏移值修改虚拟程序计数器获得跳转后的执行指令,从而实现控制转移;

运算指令的handler用于针对不同的运算符对数据元进行计算。

进一步地,步骤2中属性及方法代码段按照其实现的功能分为数据转移指令、控制转移指令、属性调用指令、方法调用指令、函数调用指令以及字符串操作指令,其中:

数据转移类指令的handler用于处理数据在各存储结构之间的转移,并添加了字符串保留数组以及webassembly官方定义的宏em_asm;

控制转移指令的handler用于完成跳转相关的功能,通过跳转偏移值修改虚拟程序计数器获得跳转后的执行指令,从而实现控制转移;

属性调用指令的handler利用webassembly内联javascript的宏em_asm,实现通用的属性调用;

方法调用指令的handler利用webassembly内联javascript的宏em_asm_args,实现通用的方法调用;

函数调用指令的handler利用webassembly内联javascript的宏em_asm_args,实现通用的函数调用;

字符串操作指令的handler利用webassembly内联javascript的宏em_asm,实现通用的运算操作。

进一步地,步骤3所述的对需要保护的javascript代码的抽象语法树进行拆分,得到中间代码,包括:

步骤3.3.1,在javascript代码拆分时,如遇到循环和分支这样的控制结构,则执行步骤3.1.2,若未遇到则进行语句拆分:

首先进行代码分析,提取代码的抽象语法树,以每个语句块为单位将其划分为多个子树,并通过后序遍历的方式对每个语句块进行指令拆分,以获取整个代码的中间代码;

步骤3.3.2,当存在循环和分支的控制结构时,将循环和分支结构进行平铺拆分,过程如下:

拆分时先获取控制结构节点,再获取控制节点的子节点,将所有子节点依次插入到控制节点左侧,并且插入标记节点标记结构范围,成为兄弟节点的关系,最后删除原控制节点,从而通过拆分得到中间代码;

对循环和分支结构拆分完之后,返回步骤3.1.1继续对代码进行拆分。

进一步地,步骤3中对所述的解释程序handler生成其对应的虚拟指令并进行编码,包括:

对计算密集型handler和属性及方法型handler分别进行编码和虚拟化,得到每一个handler对应的虚拟指令和编码,具体方法为:

针对每一个handler,生成一个随机数表示该handler对应的虚拟指令,并分配一个编码,并使得随机数、虚拟指令一一对应,将虚拟指令用随机数表示完成编码的部分。

进一步地,步骤3还包括:

将代码经过拆分后得到的指令中的字符串常量全部提取出来,单独存储到一个字符串数组vma[]中,原字符串常量的位置用所对应的数组元素索引vma[i]来置换,i为自然数。

进一步地,步骤4所述的计算密集型代码段的虚拟解释器的结构包括:

调度器、字节码程序、解释程序集以及虚拟执行环境,其中:

所述的调度器用于从字节码程序中读取字节码并解码出语义,然后返回到调度器继续读取字节码、解码过程,直到所有的字节码解码完成后结束循环;

所述的字节码程序由虚拟指令通过编码得到,在运行时,通过调度器读取解码来还原代码段的功能;

所述的解释程序集即步骤2生成的计算密集型代码段的解释程序handler的集合;

所述的虚拟执行环境用于申请内存用以模拟映射本地的真实寄存器。

进一步地,步骤4所述的属性及方法型代码段的虚拟解释器的结构与计算密集型代码段的虚拟解释器的结构基本相同,不同之处在于:

将解释程序集替换为步骤2生成的属性及方法型代码段的解释程序handler的集合,并添加保存字符串的数组vma[]。

一种基于前端字节码技术的javascript代码虚拟化保护系统,包括依次连接的代码划分模块、解释程序生成模块、代码虚拟化模块以及解释器生成编译模块,其中:

所述的代码划分模块用于将需要保护的javascript代码划分成不同的代码段,根据代码段中是否存在dom属性将代码段分为属性及方法型、计算密集型;

所述的解释程序生成模块用于分别根据属性及方法型代码段、计算密集型代码段实现的功能,生成其对应的解释程序handler;

所述的代码虚拟化模块用于对需要保护的javascript代码的抽象语法树进行拆分,得到中间代码;对所述的解释程序handler生成其对应的虚拟指令并进行编码,利用所述的虚拟指令表示所述的中间代码,从而得到虚拟化的代码;

所述的解释器生成编译模块用于分别建立计算密集型代码段的虚拟解释器以及属性及方法型代码段的虚拟解释器并进行编译。

本发明与现有技术相比具有以下技术特点:

1.本发明一方面引入代码虚拟化的思想保护javascript代码的执行逻辑;另一方面基于前端字节码技术字节码技术实现虚拟解释器,隐藏虚拟机本身的核心逻辑;通过两方面技术的结合,既能利用虚拟化增加javascript代码保护的强度又能利用webassembly的效率减少虚拟化过程带来的额外开销,可以有效提高前端关键代码的安全性。

2.本发明实现了javascript代码虚拟化的思想,并针对属性及方法型和计算密集型两种类型的javascript代码设计两套对应的虚拟化方案;提出了基于前端字节码技术的虚拟解释器各个组件的设计和关联关系,实现了针对上述两种不同类型目标代码虚拟解释器的编译和调用。

附图说明

图1是本发明方法的保护流程图;

图2是对象属性操作指令拆分的示例;

图3是计算密集型代码段虚拟化和编码过程的示例;

图4的(a)是计算密集型代码段虚拟解释器的各个组件的结构示意图,图4的(b)是属性及方法型代码段虚拟解释器各个组件的结构示意图;

图5是编译后的两种虚拟解释器的结构对比图;

图6是if语句块平展示例;

图7是利用本发明方法保护前后代码形式对比。

具体实施方式

遵从上述技术方案,如图1所示,一种基于前端字节码技术webassembly的javascript代码虚拟化保护方法,包括以下步骤:

步骤1,将需要保护的javascript代码划分成不同的代码段,根据代码段中是否存在dom属性将代码段分为属性及方法型、计算密集型;

本步骤中,首先对需要保护的javascript代码进行分段,例如可将整个代码中用以实现特定功能(例如计算、调用等)的代码分别划分为一个代码段,然后对每一个代码段进行扫描,查看代码段中是否存在dom属性(“.”属性),若存在则将代码段整体判定为属性及方法型,否则将代码段判定为计算密集型。

例如,对于以下两个划分后的代码段:

functiontest(){document.getelementbyid();vara=3+4;}

functioncal(){vara=3+4;varb=6/3;returna+b;}

其中test代码段中document.getelementbyid即为“.”属性的形式,则test代码段即为属性及方法型;而只要代码段中出现一次“.”属性立刻将其判定为属性及方法型。

在cal代码段中只存在计算式,无document等dom属性,因此将这种完全不含dom属性(“.”属性)的仅含计算操作的代码段定义为计算密集型。

步骤2,分别根据属性及方法型代码段、计算密集型代码段实现的功能,设计其对应的解释程序handler;

步骤2.1,生成计算密集型代码段的解释程序handler

参考现有汇编语言的分类方式,对计算密集型代码段进行分类,将计算密集型代码段按照其实现的功能分为数据转移指令、控制转移指令和运算指令,并建立每一类指令的解释程序handler,称为计算密集型handler。根据指令设计其对应的handler属于本领域技术人员的常规技术手段,下表1给出了一种具体的示例:

表1解释程序分类和具体设计实例

(1)数据转移类指令的handler用于处理数据在各存储结构之间的转移。例如计算密集型的数据存储空间为字节码数组vmdata,参数函数及局部变量数组varlist和栈sta三部分。

(2)控制转移指令的handler用于完成跳转相关的功能,通过字节码数组vmdata获得跳转偏移值,利用偏移值修改虚拟程序计数器vpc获得跳转后的执行指令,从而实现控制转移。

(3)运算指令的handler用于是针对不同的运算符,例如加、减、异或等运算对数据元进行计算。将寄存器中的数值通过运算符进行计算,将结果保存。

步骤2.2,设计属性及方法型代码段的解释程序(handler)

对于属性及方法型代码段,同样按照代码段的功能进行分类,分为数据转移指令、控制转移指令、属性调用指令、方法调用指令、函数调用指令以及字符串操作指令,并生成每一类指令的解释程序handler,称为属性及方法型handler。在本步骤中,利用前端字节码技术webassembly官方定义的宏实现内联javascript的handler,这些handler的设计针对javascript特有的操作类型,根据javascript标准将这些操作分为调用属性,调用方法和字符串操作。具体示例如表2所示:

表2属性及方法型解释程序具体设计实例

(1)数据转移指令的handler,用于处理数据在各存储结构之间的转移,其设计方式可以在计算密集型代码段中的数据转移指令handler的基础上,添加保存字符串数据的字符串保留数组vma[],并添加webassembly官方定义的宏em_asm。

(2)控制转移指令的handler用于完成跳转相关的功能,可知通过字节码数组vmdata获得跳转偏移值,利用偏移值修改虚拟程序计数器vpc获得跳转后的执行指令,从而实现控制转移。该指令的handler设计与计算密集型代码段的控制转移指令的handler相同。

(3)属性调用指令的handler,javascript标准提供两种调用方式,属性调用方式,例如target.property;和元素调用方式,例如target[‘element’]。在这里我们使用元素调用方式。对这种类型的handler的设计同样利用em_asm内联javascript的方式实现属性调用。针对部分javascript标准提供的对象,在handler中对数据值进行比较,然后使用对象代替,例如表2中handlerget(),判断寄存器2中字符串值是否等于官方提供的对象值,如果是则用对象代替字符串进行调用。

(4)方法调用指令的handler,javascript标准提供两种调用方式,属性调用方式,例如target.property(argument),和元素调用方式,例如target[‘element’](argument)。我们这里使用元素调用方式。对这种类型的handler我们使用webassmebly官方提供的em_asm_args宏定义实现方法调用,并且传入参数控制方法调用时参数个数,即其中handler的参数控制方法的参数个数;例如表中handlercal()将参数byte传入em_asm_args控制属性调用中参数的个数,同样对和对象值进行比对,如果相等则用对象代替字符串。

(5)函数调用指令的handler,对这种类型的handler我们使用webassmebly官方提供的em_asm_args宏定义实现方法调用,并且传入控制方法调用时参数的个数,即其中handler的参数控制函数的参数个数;例如表2中handlercal_n(),利用参数byte控制函数参数个数。

(6)字符串操作指令的handler,利用webassembly提供的em_asm宏定义实现运算操作,获取寄存器中值再根据不同运算符计算获得运算结果。

步骤3,对需要保护的javascript代码的抽象语法树进行拆分,得到中间代码;对所述的解释程序handler生成其对应的虚拟指令并进行编码,利用所述的虚拟指令表示所述的中间代码,从而得到虚拟化的代码;

步骤3.1,对需要保护的javascript代码进行指令拆分,通过模拟本地指令的调度方式,选择一种基于栈的指令架构设计具体的拆分过程。指令拆分具体过程如下:

步骤3.1.1,在javascript代码拆分时,如遇到循环和分支这样的控制结构,则执行步骤3.1.2,若未遇到则进行语句拆分:

进行语句拆分时首先进行javascript代码分析,使用rhino来提取javascript代码的抽象语法树,以每个语句块为单位将其划分为多个子树,并通过后序遍历的方式对每个语句块进行指令拆分,以获取整个代码的中间代码(拆分后的多条原子操作的集合)。在分析过程中判断是否存在属性调用,方法调用,函数调用和字符串操作的功能,以此来判断使用计算密集型handler还是属性及方法型handler;即,如果存在属性调用,方法调用,函数调用和字符串操作的功能,则使用属性及方法型handler,否则使用计算密集型handler。

在判断出所使用的handler之后,遇到计算操作节点,根据相应指令的handler对代码进行拆分。对于属性及方法型代码段中的属性调用指令和方法调用指令,先将属性调用方式转化成元素调用方式,再将嵌套的属性调用进行拆分,拆分成handler中描述的形式。

例如:target[‘property1’][‘property2’]();转化成target[‘property1’]和r1[‘property2’]()的形式,其中r1保存target[‘property1’]的结果。

如图2示例中的“document.write(str)”对于这类语句块,经过转换和拆分,将其变为一个“elementget”和一个“functioncall”。注:拆分后的“document”,“write”,“str”均为字符串常量;

步骤3.1.2,当存在循环和分支的控制结构时,将循环和分支结构进行平铺拆分,过程如下:

拆分时先获取控制结构节点,再获取控制节点的子节点,将所有子节点依次插入到控制节点左侧,并且插入标记节点标记结构范围,成为兄弟节点的关系,最后删除原控制节点;拆分后每一条语句都是一个独立的原子操作,且它们之间是线性关系,从而得到了拆分后的原子操作的集合,即中间代码。

对当前遇到的循环和分支的控制结构按照步骤3.1.2拆分完毕后,再按照步骤3.1.1继续进行代码的拆分。

例如针对if语句,如图6所示,if中键body对应的元素都是if语句的判断节点和thenpart等语句,这些节点和if语句为父子节点关系,现在对树结构进行平展,通过添加thenpart,thenpartend和ifend标记节点描述if语句范围,将所有的子节点都变成if语句的兄弟节点,完成对if语句的节点平展。

步骤3.2,实现字符转移过程

经过步骤3.1.1的指令拆分,已经将包括dom对象在内的所有变量和属性都转换成字符串常量(即“property1”,“property2”,“arguments”),这类常量中本身就存在字符串常量一同作为加载指令的参数出现在当前的中间代码中。

将javascript代码经过步骤3.1拆分后得到的指令中的字符串常量全部提取出来,单独存储到一个字符串数组“vma[]”中,原字符串常量的位置用所对应的数组元素索引“vma[i]”来置换,i为自然数;最终可以得到一个用vma[i]表示字符串常量的中间代码和一个字符串数组,这个字符串数组将作为重要的关键数据和最终编码得到字节码程序一起保存在虚拟解释器中。

步骤3.3,实现指令虚拟化和编码

通过之前的操作,抽象语法树根节点的每个子节点功能和之前设计的handler的功能对应,这些子节点语句称为中间代码。在本步骤中,利用自定义虚拟指令集表示每一个handler。

对计算密集型handler和属性及方法型handler分别进行编码和虚拟化,得到每一个handler对应的虚拟指令和编码,具体方法为:

针对每一个handler,生成一个随机数表示该handler对应的虚拟指令,并分配一个编码(不同虚拟指令对应的编码不同),并使得随机数、虚拟指令一一对应,将虚拟指令用随机数表示完成编码的部分,最后将编码的结果保存到vmdata中。按照相同的方法,可生成所有计算密集型handler和属性及方法型handler的虚拟指令和编码,从而构成虚拟指令集和数字指令集。

获得虚拟指令集后,利用虚拟指令表示步骤3.1拆分代码生成的中间代码,从而得到虚拟化的代码。

具体示例如表3所示:

表3虚拟指令到随机数的映射

步骤4,分别建立计算密集型代码段的虚拟解释器以及属性及方法型代码段的虚拟解释器并进行编译。

步骤4.1,建立计算密集型代码段的虚拟解释器

对于计算密集型代码段的虚拟解释器,设计只包含wasm模块的虚拟解释器结构,其组成部分有:调度器(dispatcher)、字节码程序(vmdata)、解释程序集(handlers)以及虚拟执行环境(vmcontext),各组件间的联系如图4的(a)所示。各组件功能如下:

(1)虚拟执行环境(vmcontext)用于申请一段内存用以模拟映射本地的真实寄存器。

(2)字节码程序(vmdata)是经由虚拟指令编码而来的,在运行时,通过调度器读取解码来还原代码段的功能,其中蕴含了目标代码的语义逻辑。

(3)调度器(dispatcher)用于完成如下工作:

首先,从vmdata中读取字节码并解码,字节码的读取顺序主要依靠一个变量vpc(程序计数器)来控制。然后,解码出语义,按照索引调度相应的解释程序handler来解释执行。最后,返回到dispatcher继续上述读取字节码、解码过程,直到解码完成所有的字节码后结束循环。设计使用循环加上选择结构来实现这一调度过程。

(4)解释程序集(handlers)是还原字节码语义的重要组件,调度器在解码字节码之后,会调用相应的解释程序handler。这里的解释程序集即步骤2.1生成的解释程序handler构成的集合。

虚拟机执行时,通过dispatcher中的vpc读取vmdata中的编码数据,获得编码数据后dispatcher会去调用编码所对应的handler来完成相应的功能。

虚拟执行环境vmcontext和解释程序handler完全采用c语言实现所有的数据结构和执行逻辑,如指令“lod_i”的handler,其实现的操作是加载一个立即数到栈顶。因此,计算密集型目标代码的虚拟解释器最终仅编译得到一个“.wasm”文件,其中包含所有的虚拟解释器组件。各个组件的关联关系如图4中所示,dispatch负责调度,完成读取字节码程序vmdata以及解码调度相应的handler执行的循环,计算的中间结果在vmcontext中保存。

编译和调用:

计算密集型代码段的虚拟解释器的编译通过emscripten编译器将包含了虚拟解释器的核心逻辑和相应组件的“.c”文件编译成wasm模块,最终得到一个“.wasm”文件,如图5所示,其中包含的虚拟解释器组件有:调度器dispatcher、虚拟执行环境vmcontext、字节码程序vmdata及解释程序集handlers,在编译的同时自动生成胶接代码gluecode。

在调用时,使用胶接代码来加载“.wasm”文件,将胶接直接添加到目标程序中,完成对wasm模块的加载、到arraybuffer的转化、实例化以及最终生成模块提取等功能。

步骤4.2,建立属性及方法型代码段的虚拟解释器

这部分的实现和计算密集型代码段的解释器基本相同,只是将解释程序集(handlers)替换为属性集方法型代码段的解释程序集(步骤2.2生成的解释程序handler的集合),另外为了配合字符串操作指令的handler,添加保存字符串的数组vma[],如图4的(b)所示;对于属性及方法代码段的数据转移指令的handler,当被调用时,需要从vma[]中取值;除了这些区别之外,其余各个部分的执行过程和计算密集型代码段的解释器完全相同。

因为对于属性及方法型代码段存在着webassembly无法处理的dom对象,因此这些对象的保存需要依赖胶接代码。最终编译后的属性及方法型目标代码的虚拟解释器与计算密集型目标代码的虚拟解释器结构上的差异如图5所示,可以看到比起属性及方法型的目标代码的虚拟解释器,计算密集型代码段的虚拟解释器仅包含wasm模块和独立于虚拟解释器之外的胶接代码。这里对于属性及方法型代码段的虚拟解释器将胶接代码作为为其中的一部分(包含在图5中的js模块),而计算密集型代码段的虚拟解释器却不包括胶接代码部分,它的胶接代码是独立于虚拟解释器之外的,这种结构是因为属性及方法型代码段的虚拟解释器的胶接代码部分保存着代码段的所有dom属性,因此在后面编译中需要整体进行编译,而计算密集型代码段的虚拟解释器的胶接代码却无需随虚拟解释器一起进行编译。

编译和调用:

属性及方法型代码段的虚拟解释器的编译通过emscripten编译器将包含了虚拟解释器的核心逻辑:vma[]、vmdata、dispatcher和相应组件的“.c”文件编译成wasm模块,并将vmcontest、handlers以及编译时产生的胶接代码gluecode编译生成“.js”文件。

在调用时,向保护后的javascript程序中添加胶接代码所在的“.js”文件加载代码,通过该文件将包含虚拟解释器的“.wasm”文件加载到浏览器中。同时将虚拟解释器入口函数实例化成原目标代码的函数名加载,替换原关键代码,当执行到关键代码(属性及方法型代码段)的时候可以直接进入虚拟解释器中执行。示例如下:保护前后代码结构如图7所示,在保护后的函数draw中,虚拟解释器入口函数实例化成__z5interi(1),在执行代码时,当调用保护后的函数draw时,则通过入口函数__z5interi(1)进入虚拟解释器中开始解释执行虚拟指令。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1