实现原理
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还没有初始化的原因)