JSVMP执行流程分析 密级: 【C-1】 | 时间:2024-02-21 | 目录:测试笔记 | 编辑本文 文章距今已发表三个月,请自行判断文中技术方法、代码的有效性:) ### 结构分析 观察vmp后函数的结构,样本代码见文末。(测试地址:https://rce.ink/vmp.html ) ``` function interpreter(parentScope, index, stack, constantPool, bytecode, option={}, args) ``` 参数释义如下: parentScope:作用域,用于保存指令处理后的结果及属性 index:指针,初始为0 stack:存值的栈 constantPool:常量池,获取常量 bytecode:字节码数组,用于提取opcode以及相应的操作数 option:特殊状态下有效 args:特殊状态下有效 提取出字节码如下: ``` [3,3,3,4,4,5,12,4,3,0,1,17,0,0,5,3,1,4,6,54,1,12,4,4,0,1,17,0,0,5,4,1,4,7,53,1,5,3,1,4,8,52,1,51,1,13,4,9,0,0,5,9,1,4,10,50,1,7,76,14,74,5,9,1,4,11,54,1,13,4,9,0,0,16,6,76,4,12,4,13,51,1,5,9,1,51,1,12,4,14,1,1,4,15,2,1,0] ``` 提取出常量池如下: ``` ["@faceless","arguments","__proto__","a","b",22,2,6,4,"c",70,7,"test",100,"console","log"] ``` 我们来跟进观察一下,vmp是如何实现虚拟化保护的: 自运行函数一开始定义了一个localScope空对象,定义@faceless与arguments两个属性,并保存参数中的option 与args, 判断option中的选项开关,从window对象中取值并设置到localScope中。并把localScope原型定义为window对象,这样就可以通过localScope调用到window对象中的方法。 ![](https://p0.meituan.net/xianfu/2d4256e0beb990bc88975b137cb622b847632.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 定义了一个getScope的方法,目的是在给定的作用域链中查找具有指定名称的属性,并返回拥有该属性的对象。如果没有找到,则返回 null。 ![](https://p0.meituan.net/xianfu/d74efdd115dde16f0816eefdd244958372967.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 后面就进入vmp的分发器Dispatcher了。在html页面19行打一个断点,这代表当前获取的指针位置。 ![](https://p0.meituan.net/xianfu/ae5bd41b8ab3f25d6d999b97e38b9ad740440.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 刷新页面 ![](https://p0.meituan.net/xianfu/2c25cf1929265421789e3042bec2d056213178.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 通过指针0,获取到的opcode为3。它将进入某个分支执行,在下面找到这个逻辑判断: ![](https://p1.meituan.net/xianfu/b7837063d4ae2797df70bb532d2c10f772682.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 在这个handler里,他将指针自增1,也就是1。在字节码数组中向后取一位值,也就是3,这个值将作为常量池中的索引。并将t1赋值为常量池中的a,并把localscope定义一个属性a,值为undefined。 总结一下 | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | 去掉这里的断点,继续向下跟进。 ![](https://p1.meituan.net/xianfu/6535f8ad016768b9a46dab09a12e169e83968.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 这里的opcode还是3,前面的流程做的事原始代码中的var a的部分,大胆猜测这里做的事var b的操作。 ![](https://p0.meituan.net/xianfu/02d88ead445303e834cc8ed6fd82900e63956.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 指针继续自增1,取到常量池中的b。也同样赋值给localScope。 ![](https://p0.meituan.net/xianfu/87c80b156880958df571211398af123183438.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 之后opcode就变成4了,向下跟进发现它先指针自增取字节码,然后从常量池中取这个索引的值并压入栈中。 ![](https://p0.meituan.net/xianfu/edca00df5c3922424f217b47e20e5db150835.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| 继续往下,发现opcode是12了 ![](https://p0.meituan.net/xianfu/213e8975e982a00825c4233de973c8fa129769.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 找到对应的handler ![](https://p0.meituan.net/xianfu/f8e994a0a56522280bf067c0d4bcaf8219935.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 发现是把当前的本地作用域放入栈中 | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| |12 |将当前的localScope压入栈| 接着往下跟进,opcode又为4 ![](https://p0.meituan.net/xianfu/56f7b05dadf516e6070d8985373f8bad68605.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 往后取操作数,是用这个值作为常量池数组索引取值 ![](https://p0.meituan.net/xianfu/97d45841bbdbecb61d85ae5735fd2d6f55206.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 可以发现此时栈中存在22、window、a三个值。我们可以整理归纳一下,前面字节码做了哪些事: 3,3 定义变量a 3,4 定义变量b 4,5 取了原始代码中的a的值 放入栈中 12 将localScope放入栈中 4,3, 取了a这个变量名 放入栈中 这里其实有点感觉了,三个值放入栈中,一个值为window对象,一个属性名一个属性值,大概率后面的opcode要做值出栈,赋值的作用。类似弹出a和window,然后做类似window.a = 22 。印证一下接着往下吧: ![](https://p0.meituan.net/xianfu/708ae137a5e35c089a88f6c45116dca874402.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) ![](https://p1.meituan.net/xianfu/96ac09b08580854b0d54843b5fb0a1db54095.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 这里的逻辑就很清晰了,弹出三个元素,分别为属性名a、window对象、值22,然后向下找一个操作数,此时为1。通过getScope函数获取存在属性a的作用域,因为前面localScope定义过a的属性(通过opecode=3的第一次操作),所以会返回作用域localScope,并把这个作用域的属性a赋值22。由于操作数t4为1,需要把这个值压回栈内。 ![](https://p0.meituan.net/xianfu/cb5c5a322a04126a069ef03a4e342d6b122057.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| |12 |将当前的localScope压入栈| |0 |弹出栈顶的三个元素,找到存在栈顶元素名属性的作用域,设置这个属性的值为第三个元素的值,最后把值压回栈内| 3,3 定义变量a 3,4 定义变量b 4,5 取了原始代码中的a的值 放入栈中 12 将localScope放入栈中 4,3, 取了a这个变量名 放入栈中 0,1 localScope 赋值,将赋的值放入栈中 此时通过6条指令,12个字节码,我们实现了原始代码中的var a =22的效果。 接着往下看别的opcode ![](https://p0.meituan.net/xianfu/243ed710beb6b51e8ac3f15a3880ee2350246.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 取出栈顶元素,往下取操作数为0,将此值作为弹出元素的数量,对栈顶进行清理的操作。再往下取操作数t5,判断是否要把前面栈顶的值放回。 ![](https://p0.meituan.net/xianfu/d3397176f8f241d6ff99d21badfadfcb74482.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 可以看到栈此时被清空了。 3,3 定义变量a 3,4 定义变量b 4,5 取了原始代码中的a的值 放入栈中 12 将localScope放入栈中 4,3, 取了a这个变量名 放入栈中 0,1 localScope 赋值,将赋的值放入栈中 17,0,0 弹出栈顶,清空0个栈上元素,不把弹出的值压回栈内 | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| |12 |将当前的localScope压入栈| |0 |弹出栈顶的三个元素,找到存在栈顶元素名属性的作用域,设置这个属性的值为第三个元素的值,最后把值压回栈内| |17 |弹出栈顶元素,根据第一个操作数判断需要清空的栈内元素的个数,第二个操作数判断是否把值压回栈内| opcode为0时,赋值结束不确定后续是否要从栈获取这个值,所以会将这个值重新压入栈中。 为17时,根据操作数是否清空栈。或者仅保留栈顶元素。 向下跟进,opcode为5。 ![](https://p0.meituan.net/xianfu/d577e47d445183c968143d952e1b81e897097.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 第一个操作数用于在常量池获取属性名,第二个操作数用于判断是否压入栈内。此时将作用域中的a的值压入了栈内。 | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| |12 |将当前的localScope压入栈| |0 |弹出栈顶的三个元素,找到存在栈顶元素名属性的作用域,设置这个属性的值为第三个元素的值,最后把值压回栈内| |17 |弹出栈顶元素,根据第一个操作数判断需要清空的栈内元素的个数,第二个操作数判断是否把值压回栈内| |5|根据第一个操作数拿到属性名,取值后根据第二个操作数判断是否压入栈内| 3,3 定义变量a 3,4 定义变量b 4,5 取了原始代码中的a的值 放入栈中 12 将localScope放入栈中 4,3, 取了a这个变量名 放入栈中 0,1 localScope 赋值,将赋的值放入栈中 17,0,0 弹出栈顶,清空0个栈上元素,不把弹出的值压回栈内 5,4,1 取常量池中第四个值作为属性名,获取作用域中该属性的值并压入栈内 后面到了opcode为4的位置,将2的值压入栈内。此时栈分布 ![](https://p1.meituan.net/xianfu/bc9639b1bc16f58ec4373fde4e546a8010433.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 再到opcode为54的操作了,这里稍微麻烦点: 先进入一个指令范围的判断,把栈内两个元素弹出。(可以想到这段范围的opcode实现了对栈内两个元素的各类运算) ![](https://p0.meituan.net/xianfu/a244244d6aad0c36343664a4571277c232175.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 再进入这个分支进行除运算 ![](https://p0.meituan.net/xianfu/110ae171161ff47698dc9bb31e05228814159.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 这不就是原始代码中的 a/2的部分吗。再取操作数判断是否要压回栈内,最终把除后的结果压入栈内: ![](https://p0.meituan.net/xianfu/ce99ad13d9bc643a2955db414b27933537439.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) | opcode | 用途 | | ------------ | ------------ | | 3 | 变量定义,类似于 var xx = undefind | |4 |根据操作数,从常量池取值,压入栈| |12 |将当前的localScope压入栈| |0 |弹出栈顶的三个元素,找到存在栈顶元素名属性的作用域,设置这个属性的值为第三个元素的值,最后把值压回栈内| |17 |弹出栈顶元素,根据第一个操作数判断需要清空的栈内元素的个数,第二个操作数判断是否把值压回栈内| |5|根据第一个操作数拿到属性名,取值后根据第二个操作数判断是否压入栈内| |54|取栈顶两个元素弹出,进行除操作,结果根据操作数判断是否压入栈内| 后面又到了opcode为12,把localscope压入栈,略过。 又到了opcode为4,跟据操作数把b压入栈内。 随后就是0了,此时栈内分布: ![](https://p0.meituan.net/xianfu/456c423a2bd04f5b342023d04c1769bd10367.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 很显然做了同样的步骤,把localscope中的b属性赋值为11。预测下一条指令必定是17以清空栈。 ![](https://p0.meituan.net/xianfu/dea8a98bdc9b8b9e85c439a0aa4e466175394.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 结果真如预想的一样。后面的步骤,进行简化: 5 -> 将b的值压入栈 4 -> 将常量池6压入栈 53 -> 弹出两个元素,进行乘法操作,结果压入栈 5 -> 将a的值 压入栈 4 -> 将常量4压入栈 52 -> 弹出栈顶两个元素 进行减法操作,结果压入栈 (stop,此时栈内存放着b*6的值与a-4的值,不出所料后面必定是进行加操作) ![](https://p0.meituan.net/xianfu/e2ebacdd944572d1701a3f262efc0d1964919.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 此时栈内存放着 (b*6)+(a-4)的结果值,接着是需要赋值给c的。但是和ab有区别,他并不是var定义的值,而是直接赋值。在js的赋值操作中,他是直接成为window的一个属性的: ![](https://p0.meituan.net/xianfu/ea4a028f885a4c89836bfc07d9fa1ce812279.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 我们来预测一下指令吧,先把window作用域放到栈内,再从常量池取c这个属性名放入栈内,最后进行赋值操作,顺序应该是: ![](https://p0.meituan.net/xianfu/d36fb076b5986972d77587058114b20e20625.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 13(通过代码阅读直接找到opcode) 4 0 看下结果吧。 ![](https://p0.meituan.net/xianfu/ba0667d78b4a422788e7f270ea4e32f163735.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) ![](https://p0.meituan.net/xianfu/a1900ad206c57f4c453e5e140672376564164.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) ![](https://p0.meituan.net/xianfu/1a4f5eccfa40bca6c6f619bed62277d376551.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 完全正确。此时已经赋值了c在window属性中,下一条指令的opcode为5,根据操作数拿到了c这个值并压入栈内。(虽然c定义在window中,但是localScope的原型指向了window,所以按照查找机制也可以通过localScope.c取到c的值。这也就是一开始初始化的时候为什么需要那样定义。) 之后的opcode为4,根据表推测,将常量池中的70压入栈内,即将与c的值做比较。 之后的opcode为50,是一个比较运算 ![](https://p0.meituan.net/xianfu/0c6e25ff584658a460f66970da7081ba25264.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 运算结束后,栈内存在一个布尔值 ![](https://p1.meituan.net/xianfu/a7e9be38469a7e407fe5e91c972383548689.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 往后的操作码为7,如果栈内元素为false,则修改索引值为第一个操作数。这里为true,所以没有跳转。有没有有点像,如果是false进入else分支,跳过if逻辑直接进入else的分支起始指针? ![](https://p0.meituan.net/xianfu/85763aed1429c9e1d13ccde45fb51e3031733.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) opcode为14, 这应该是所有指令中最难理解的了。 这条指令分成几部分: | opcode | 操作数1 | body | 操作数2 | | ------------ | ------------ | ------------ | ------------ | | 14 | 跳转位置 | 指令集 | 16 | 进入14时,将指令集用分发器再执行一遍,并把结果存储到作用域。直到遇到opcode16跳出。 在抽象语法树中,按照深度优先的逻辑,遇到ifstatement时,会执行push(14)->对子块继续做字节码转换->执行push(16)结束这个statement。 ![](https://p0.meituan.net/xianfu/0b5b1cfd99b0e94010cd7fb2d75b707976626.png%40watermark=1&&object=L3dkY2Zsb3cvN2RiN2M4NTFjYmVjZDg4MTM1OTZjMTYzOWE2MzQ4MDM0MjY0LnBuZw==&p=8&t=90&x=10&y=10) 至此 对jsvmp有基本的认知即可。后续会继续学习相应的开发以及对抗vmp的一些探索。 ### 原始代码 ```javascript var a = 22 var b = a/2 c = (b*6)+(a-4) if(c>70){ c = c/7 } console.log("test"+100+c) ``` ### 样本代码 ````javascript (function interpreter(parentScope, index, stack, constantPool, bytecode, option = {}, args) { let localScope = {}; localScope[constantPool[0]] = option.t || this; localScope[constantPool[1]] = args; if (option.n) localScope[option.n] = parentScope[option.f]; localScope.__proto__ = parentScope; function getScope(scope, name) { if (!scope || name == constantPool[2]) return null; if (scope.hasOwnProperty(name)) return scope; return getScope(Object.getPrototypeOf(scope), name); } if (option.e) { localScope[constantPool[bytecode[index + 1]]] = option.e; } while (index < bytecode.length) { let t0, t1, t2, t3, t4, t5; let instruction = bytecode[index++]; if (instruction == 16) { return; } if (instruction == 21) { stack.push(`${stack.pop()}`); } if (instruction == 58) { t0 = stack.pop(); t1 = !t0; t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == undefined) { throw new Error("当前指令不存在,请检查指令集!"); } if (instruction == 24) { debugger; } if (instruction == 9) { t0 = stack.pop(); t1 = bytecode[index++]; t2 = bytecode[index++]; t3 = stack[stack.length - 1]; t4 = function () { return interpreter(localScope, t1, stack, constantPool, bytecode, { t: this, n: t0, f: t0 || t3, r: 1 }, arguments); }; if (t2) { stack.push(t4); } else { localScope[t0] = t4; } } if (instruction == 30) { t0 = bytecode[index++]; t1 = []; for (t2 = 0; t2 < t0; t2++) { t3 = stack.pop(); t1.unshift(t3); } t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == 23) { t0 = stack.pop(); t1 = stack.pop(); t3 = new RegExp(t1, t0); stack.push(t3); } if (instruction == 15) { t0 = bytecode[index++]; t1 = interpreter(localScope, index, stack, constantPool, bytecode, { t: localScope[constantPool[0]] }); if (!t1) { index = t0; } else { if (t1 === 1) { return; } else { if (option.r) { return stack.pop(); } return t1; } } } if (instruction == 14) { t0 = bytecode[index++]; t1 = interpreter(localScope, index, stack, constantPool, bytecode, { t: localScope[constantPool[0]] }); if (t1 === undefined) { index = t0; } else { if (option.r) { return stack.pop(); } return t1; } } if (33 < instruction && instruction < 57) { t0 = stack.pop(); t1 = stack.pop(); if (instruction == 40) { t2 = t1 || t0; } if (instruction == 43) { t2 = t1 >= t0; } if (instruction == 53) { t2 = t1 * t0; } if (instruction == 36) { t2 = t1 >>> t0; } if (instruction == 46) { t2 = t1 != t0; } if (instruction == 44) { t2 = t1 <= t0; } if (instruction == 48) { t2 = t1 !== t0; } if (instruction == 55) { t2 = t1 % t0; } if (instruction == 35) { t2 = t1 in t0; } if (instruction == 39) { t2 = t1 | t0; } if (instruction == 42) { t2 = t1 && t0; } if (instruction == 51) { t2 = t1 + t0; } if (instruction == 38) { t2 = t1 << t0; } if (instruction == 52) { t2 = t1 - t0; } if (instruction == 56) { t2 = t1 ^ t0; } if (instruction == 49) { t2 = t1 < t0; } if (instruction == 50) { t2 = t1 > t0; } if (instruction == 34) { t2 = t1 instanceof t0; } if (instruction == 37) { t2 = t1 >> t0; } if (instruction == 54) { t2 = t1 / t0; } if (instruction == 45) { t2 = t1 == t0; } if (instruction == 41) { t2 = t1 & t0; } if (instruction == 47) { t2 = t1 === t0; } t3 = bytecode[index++]; if (t3) stack.push(t2); } if (instruction == 57) { t0 = stack.pop(); t1 = ~t0; t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == 20) { throw stack.pop(); } if (instruction == 2) { t0 = bytecode[index++]; t1 = stack.pop(); t2 = stack.pop(); args = []; for (t3 = 0; t3 < t0; t3++) args.unshift(stack.pop()); if (t2 === 0) { t4 = t1.apply(localScope, args); } else { t4 = t2[t1].apply(t2, args); } t5 = bytecode[index++]; if (t5) stack.push(t4); } if (instruction == 0) { t0 = stack.pop(); t1 = stack.pop(); t2 = stack.pop(); t4 = bytecode[index++]; t3 = getScope(t1, t0) || t1; t3[t0] = t2; if (t4) stack.push(t2); } if (instruction == 29) { t0 = stack.pop(); t1 = stack.pop(); t2 = stack.pop(); t2[t1] = t0; t5 = bytecode[index++]; if (t5) stack.push(t2); } if (instruction == 32) { t0 = stack.pop(); t1 = void t0; t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == 3) { t0 = bytecode[index++]; t1 = constantPool[t0]; localScope[t1] = undefined; } if (instruction == 17) { t0 = bytecode[index++]; t1 = stack.pop(); for (t2 = 0; t2 < t0; t2++) stack.pop(); t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == 33) { t0 = stack.pop(); t1 = stack.pop(); t2 = delete t1[t0]; t5 = bytecode[index++]; if (t5) stack.push(t2); } if (instruction == 8) { t0 = stack.pop(); t1 = stack.pop(); t2 = bytecode[index++]; if (t1 == -1) t1 = t2; t1 = t0[t1]; index = t1; } if (instruction == 18) { t0 = stack.pop(); t0 = Number(t0); if (t0 == NaN) t0 = -1; stack.push(t0); } if (instruction == 6) { t0 = bytecode[index++]; index = t0; } if (instruction == 26) { t0 = bytecode[index++]; t1 = stack.pop(); t2 = stack.pop(); t3 = getScope(t2, t1) || t2; t4 = !t0 ? t3[t1]-- : --t3[t1]; t5 = bytecode[index++]; if (t5) stack.push(t4); } if (instruction == 22) { t0 = bytecode[index++]; t2 = ""; for (t1 = 0; t1 < t0; t1++) { t2 += stack.pop(); } t5 = bytecode[index++]; if (t5) stack.push(t2); } if (instruction == 13) { stack.push(window); } if (instruction == 27) { t0 = stack.pop(); t1 = typeof t0; t5 = bytecode[index++]; if (t5) stack.push(t1); } if (instruction == 12) { stack.push(localScope); } if (instruction == 4) { t0 = bytecode[index++]; stack.push(constantPool[t0]); } if (instruction == 25) { t0 = bytecode[index++]; t1 = stack.pop(); t2 = stack.pop(); t3 = getScope(t2, t1) || t2; t4 = !t0 ? t3[t1]++ : ++t3[t1]; t5 = bytecode[index++]; if (t5) stack.push(t4); } if (instruction == 28) { stack.push({}); } if (instruction == 19) { t2 = bytecode[index++]; t3 = bytecode[index++]; t4 = bytecode[index++]; t5 = bytecode[index++]; try { t1 = interpreter(localScope, t3, stack, constantPool, bytecode, { t: localScope[constantPool[0]] }); if (t1 > 0) { if (option.r) { return stack.pop(); } return t1; } } catch (e) { t1 = interpreter(localScope, t4, stack, constantPool, bytecode, { t: localScope[constantPool[0]], e: e }); if (t1 > 0) { if (option.r) { return stack.pop(); } return t1; } } finally { t1 = interpreter(localScope, t5, stack, constantPool, bytecode, { t: localScope[constantPool[0]] }); if (t1 > 0) { if (option.r) { return stack.pop(); } return t1; } } index = t2; } if (instruction == 5) { t0 = constantPool[bytecode[index++]]; t1 = localScope; t2 = t1[t0]; t5 = bytecode[index++]; if (t5) stack.push(t2); } if (instruction == 31) { t0 = stack.pop(); t1 = stack.pop(); t2 = bytecode[index++]; args = []; for (t3 = 0; t3 < t2; t3++) args.unshift(stack.pop()); t4 = new t1[t0](...args); t5 = bytecode[index++]; if (t5) stack.push(t4); } if (instruction == 1) { t0 = stack.pop(); t1 = stack.pop(); t5 = bytecode[index++]; if (t5) stack.push(t1[t0]); } if (instruction == 7) { t0 = stack.pop(); t1 = bytecode[index++]; if (!t0) index = t1; } if (instruction == 11) { if (option.r) { return stack.pop(); } return bytecode[index++]; } if (instruction == 10) { t0 = bytecode[index++]; if (!args) args = [].concat(stack); for (t1 = t0; t1 >= 0; t1--) { t2 = stack.pop(); localScope[t2] = args[t1]; } } } })(typeof window !== 'undefined' ? window : (window = global, window), 0, [],["@faceless","arguments","__proto__","a","b",22,2,6,4,"c",70,7,"test",100,"console","log"], [3,3,3,4,4,5,12,4,3,0,1,17,0,0,5,3,1,4,6,54,1,12,4,4,0,1,17,0,0,5,4,1,4,7,53,1,5,3,1,4,8,52,1,51,1,13,4,9,0,0,5,9,1,4,10,50,1,7,76,14,74,5,9,1,4,11,54,1,13,4,9,0,0,16,6,76,4,12,4,13,51,1,5,9,1,51,1,12,4,14,1,1,4,15,2,1,0]) ```` 评论列表 写评论 您的IP:3.145.10.45,临时用户名:51fba31d评论已接入DepyWAF审计与流量系统,请勿频繁操作导致IP拉黑 提交评论 © 版权声明:非标注『转载』情况下本文为原创文章,版权归 Depy's docs 所有,转载请联系博主获得授权。