实现原理
JSVMP 的核心是在 JavaScript 代码保护过程中引入代码虚拟化思想,实现源代码的虚拟化过程,将目标代码转换成自定义的字节码,这些字节码只有特殊的解释器才能识别,隐藏目标代码的关键逻辑。服务器端读取 JavaScript 代码 —> 词法分析 —> 语法分析 —> 生成AST语法树 —> 生成私有指令 —> 生成对应私有解释器,将私有指令加密与私有解释器发送给浏览器,然后一边解释,一边执行。
模拟JSVMP执行流程
准备数据
1  | var a = '丽丽'  | 
第一次数据转换
1  | var a ;  | 
第二次转换
1  | // 我们假设赋值指令为 1, 加和指令为 2,声明指令为 3  | 
将数据压缩到数组
1  | _stack = [  | 
通过自执行函数,对数据进行处理
1  | !function(_stack) {  | 
实际jsvmp会更加的复杂,这个是基本的逻辑,就行自己写一个解释器来解释自己的代码
关于jsvmp的解法一般有3种,补环境,和插桩扣逻辑,jsrpc,当然还有自动化等方式可自行研究试试
实战1
https://www.toutiao.com/?wid=1742104764742
尝试逆向_signature参数
直接全局找一下参数名称
找到疑似加密点,接着在这里做一些断点分析,可以看出来是P函数进行加密的
梳理一下逻辑,就是将完整的url(https://www.toutiao.com/toutiao/api/pc/info)传递给`window.byted_acrawler.sign`函数进行签名

跟进到这个方法,可以往文件头部去看一下
很明显的jsvmp的结构,上面的方法是他用来翻译代码的解释器,调用的时候就把对应的参数传递,他会进行解析,后面还有一些环境判断
这种情况直接把整个JS文件复制到本地,执行一下
referrer对应的键没有定义,尝试去断点,但是这里有很多地方都会调用到这里,不太好找,采用日志断点
刷新一下,观察日志中有referrer的地方
可以看到是document对象,Node环境中并没有,所以定义一个,顺便也把window全局对象也定义了
1  | window = global;  | 
再次运行
没有相应的方法,而这种一般是通过exports导入的,我们看一下最后的环境判断
浏览器中exports是undefined
所以我们直接将"undefined" != typeof exports ? exports : void 0修改为void 0
再次运行
这个就没法通过日志去看了,因为日志中有很多length的定义,我们观察一下代码S[R] = S[R][A],出现这个报错肯定是S[R]为undefined,而S[R]是上一次循环被赋值的,所以我们只需要找到length上一次的定义逻辑即可
通过调试可以发现,当通过protocol赋值之后,S[R]就变为了undefined
而第一张图里面的Object正是我们的document对象(在出现找不到href属性时通过日志找到的),那么我们再加上一个protocol属性即可
再次运行
可以发现长度有点过于短了,我们可以在这里加一个输出,看还有哪些参数是需要的
可以看到最后还有个cookie的值,我们尝试把浏览器的cookie值复制进去(cookie一般是在document对象中,其实在之前的日志调试也可以看到)
document.cookie这个前面和后面都放一下吧,这里是放在后面才成功的….(可能是document还没有初始化的原因)