含笑九泉网

【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶

【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶

【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶

    • 前言
    • 初看js代码
    • 补环境方案
    • 修改字节码方案(反混淆与反汇编)
    • 算法还原
    • 后记

前言

距离上一次的系列某乎jsvmp也过了好一段时间,现在也从2.0的某乎版本升级到了3.0的版本

在这里插入图片描述
在这里插入图片描述
自然的,算法也就发生了一些改变。数版最明显最直接可见的进阶变化就是长度边长了,并且相同的系列入参,输出结果并不相同。某乎那么这篇文章就在原来2.0的数版基础上【JS逆向系列】某乎x96参数与jsvmp初体验,来分析一下3.0版本变难了多少,进阶算法又要怎么还原出来。系列

初看js代码

至于参数如何查找这篇文章就跳过了,某乎相关内容可以查看前一篇,数版这篇从【__g._encrypt】开始。进阶两个版本的系列入口是相同的,都是某乎从【__g._encrypt】进入到jsvmp内部代码,入参也都是数版一个md5结果的16进制字符串。

某乎的jsvmp与其他的略有不同,一般的jsvmp是堆栈式的,而某乎的这个是寄存器式的。也是也之前一样,是有vm的初始化,这次3.0的对象是【l】对象

在这里插入图片描述

结构上和之前还是很想的,不过多了不少参数,有几个关键的参数需要注意

参数映射含义
this.c通用寄存器
this.spc寄存器
this.S栈帧
this.i数组缓存
this.Q跳转标志位
this.G操作码数组
this.D字符串数组
this.w控制流出口
this.g异常跳转
this.a时间检测参数
this.e3字节操作码
this.T控制流入口
this.U时间检测参数
this.M常量虚假指令

以上的仅仅是我个人的理解,不一定正确,仅供参考。

补环境方案

还是和之前一样,首先试试能不能通过补环境得出相同的结果,首先在网页上拿一组样本。
在这里插入图片描述
这里入参是【a63da42088bd8d635961ede065daeb51】结果是【RiO+y9AqW9KuaS+8vShliRMUs8LvryJRSxJinhVvmy+JvR5Xel5Uv5psmxAcilNl】,按照之前的办法,就是补环境使得到相同的结果,但是对于3.0版本就会出现问题。这里发现,相同的入参,多次执行,结果是不一样的。

在这里插入图片描述
这就不好办了,那么即使补环境出来的结果,也不知道是不是对的。一般这种情况下,就是计算涉及到的随机数或者时间。而这里就是包含的随机数,所以需要hook随机数的返回

Math.random = function(){     return 0.50};

输入这段代码后再执行加密函数,此时就发现结果都是一样的了

在这里插入图片描述
那么此时就得到了一组样本,当随机数恒定返回0.5时。入参【a63da42088bd8d635961ede065daeb51】的正确结果为【t=V/NpKQqHpejG8nmTuCzIrXW+JszxwLVVyuy+8S0ak=pe1N4BRA6Qxz+LDn+Xyj】,那么接下在就真正可以开始补环境了。

首先安装依赖库

npm install jsdomnpm install canvas

然后在头部加上jsdom的代码

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

");window=dom.window;Math.random = function(){ return 0.50};

结尾加上测试代码

console.log(D('a63da42088bd8d635961ede065daeb51'));console.log('t=V/NpKQqHpejG8nmTuCzIrXW+JszxwLVVyuy+8S0ak=pe1N4BRA6Qxz+LDn+Xyj');

开始测试运行

在这里插入图片描述

提示缺少【document】,那么就加上这个定义

document=window.document;

继续运行,后面还有类似的报错,继续补全。

最后头部为

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

");window=dom.window;document=window.document;navigator=window.navigator;location=window.location;history=window.history;screen=window.screen;Math.random = function(){ return 0.50};

测试可以运行出结果

在这里插入图片描述
这个结果和样本明显不一样,说明还缺少了其他环境没有补到。

那么接下来就得对前面的环境变量上代理,看看还用到了什么属性和方法

window = new Proxy(window, {     set(target, property, value, receiver) {         console.log("设置属性set window", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get window", property, typeof target[property]);        return target[property]    }});document = new Proxy(document, {     set(target, property, value, receiver) {         console.log("设置属性set document", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get document", property, typeof target[property]);        return target[property]    }});navigator = new Proxy(navigator, {     set(target, property, value, receiver) {         console.log("设置属性set navigator", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get navigator", property, typeof target[property]);        return target[property]    }});location = new Proxy(location, {     set(target, property, value, receiver) {         console.log("设置属性set location", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get location", property, typeof target[property]);        return target[property]    }});history = new Proxy(history, {     set(target, property, value, receiver) {         console.log("设置属性set history", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get history", property, typeof target[property]);        return target[property]    }});screen = new Proxy(screen, {     set(target, property, value, receiver) {         console.log("设置属性set screen", property, typeof value);        return Reflect.set(...arguments);    },    get(target, property, receiver) {         console.log("获取属性get screen", property, typeof target[property]);        return target[property]    }});

同时,整个大逻辑被一个try代码块包裹着

在这里插入图片描述
那么如果报错的话,我们也看不到,不方便补环境,所以去掉try代码块,只保留try里面的内容。

在这里插入图片描述
可以看到读取了不少属性,最后运行到【获取属性get document Symbol(Symbol.toStringTag) string】这一步就退出了,那么看看这一步的结果是不是和网页不一样

在这里插入图片描述
在这里插入图片描述
确实是不一样的结果,所以这里就需要hook掉toString方法

var Object_toString = Object.prototype.toString;Object.prototype.toString = function () {     let _temp = Object_toString.call(this, arguments);    console.log(this);    console.log("Object.prototype.toString: " + _temp);    if(this.constructor.name === 'Document'){         return '[object HTMLDocument]';    }    return _temp;};

再次运行后,日志内容比之前更加长了,说明补的内容有效了,同时得到的加密结果也不一样了

在这里插入图片描述
这里最后是location对象出现问题,那么在jsdom上面,就需要补上url链接,那么就会自动补全location对象,开头部分的代码就修改为

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

",{ url:'https://www.zhihu.com/search'});window=dom.window;document=window.document;navigator=window.navigator;location=window.location;history=window.history;screen=window.screen;Math.random = function(){ return 0.50};

在这里插入图片描述

在这里插入图片描述
这里canvas和网页返回的不一样,继续补上

var Object_toString = Object.prototype.toString;Object.prototype.toString = function () {     let _temp = Object_toString.call(this, arguments);    console.log(this);    console.log("Object.prototype.toString: " + _temp);    if(this.constructor.name === 'Document'){         return '[object HTMLDocument]';    }else if(this.constructor.name === 'CanvasRenderingContext2D'){         return '[object CanvasRenderingContext2D]'    }    return _temp;};

在这里插入图片描述
又继续往下跑了,这次是检测了window下的_resourceLoader,浏览器上是undefined,但是node上返回对象。还有后面的_sessionHistory,一起补上。

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

",{ url:'https://www.zhihu.com/search'});window=dom.window;document=window.document;navigator=window.navigator;location=window.location;history=window.history;screen=window.screen;window._resourceLoader = undefined;window._sessionHistory = undefined;Math.random = function(){ return 0.50};

在这里插入图片描述
出现alert未定义,和之前一样处理

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

",{ url:'https://www.zhihu.com/search'});window=dom.window;document=window.document;navigator=window.navigator;location=window.location;history=window.history;screen=window.screen;alert=window.alert;window._resourceLoader = undefined;window._sessionHistory = undefined;Math.random = function(){ return 0.50};

在这里插入图片描述
结果还是不一样,并且获取了window的原型后就没有了,那么这种情况很有可能检测了原型链和函数或者tostring,那么hook一下看看

var Function_toString = Function.prototype.toString;Function.prototype.toString = function () {     let _temp = Function_toString.call(this, arguments);    console.log(this);    console.log("Function.prototype.toString: " + _temp);    return _temp;};

在这里插入图片描述
果然是,那么继续补上

var Function_toString = Function.prototype.toString;Function.prototype.toString = function () {     let _temp = Function_toString.call(this, arguments);    console.log(this);    console.log("Function.prototype.toString: " + _temp);    if(this.name === 'Window'){         return 'function Window() {  [native code] }'    }    return _temp;};

在这里插入图片描述
漂亮,终于得到一样的结果,那么这里补环境就完成了,总结一下我们补了什么

const{ JSDOM}=require("jsdom");const dom=new JSDOM("

Hello world

",{ url:'https://www.zhihu.com/search'});window=dom.window;document=window.document;navigator=window.navigator;location=window.location;history=window.history;screen=window.screen;alert=window.alert;window._resourceLoader = undefined;window._sessionHistory = undefined;Math.random = function(){ return 0.50};window = new Proxy(window, { set(target, property, value, receiver) { console.log("设置属性set window", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get window", property, typeof target[property]); return target[property] }});document = new Proxy(document, { set(target, property, value, receiver) { console.log("设置属性set document", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get document", property, typeof target[property]); return target[property] }});navigator = new Proxy(navigator, { set(target, property, value, receiver) { console.log("设置属性set navigator", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get navigator", property, typeof target[property]); return target[property] }});location = new Proxy(location, { set(target, property, value, receiver) { console.log("设置属性set location", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get location", property, typeof target[property]); return target[property] }});history = new Proxy(history, { set(target, property, value, receiver) { console.log("设置属性set history", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get history", property, typeof target[property]); return target[property] }});screen = new Proxy(screen, { set(target, property, value, receiver) { console.log("设置属性set screen", property, typeof value); return Reflect.set(...arguments); }, get(target, property, receiver) { console.log("获取属性get screen", property, typeof target[property]); return target[property] }});var Object_toString = Object.prototype.toString;Object.prototype.toString = function () { let _temp = Object_toString.call(this, arguments); console.log(this); console.log("Object.prototype.toString: " + _temp); if(this.constructor.name === 'Document'){ return '[object HTMLDocument]'; }else if(this.constructor.name === 'CanvasRenderingContext2D'){ return '[object CanvasRenderingContext2D]' } return _temp;};var Function_toString = Function.prototype.toString;Function.prototype.toString = function () { let _temp = Function_toString.call(this, arguments); console.log(this); console.log("Function.prototype.toString: " + _temp); if(this.name === 'Window'){ return 'function Window() { [native code] }' } return _temp;};

当需要运行的时候,可以把代码部分的代码注释掉,因为这部分只是方便我们查看以及补环境,不影响最终的结果

修改字节码方案(反混淆与反汇编)

在修改字节码之前,要么需要详细分析字节码的逻辑,又或者反汇编字节码到类似js代码的方式。再来看能不能通过修改字节码的方案来绕过环境检测。

例如之前2.0部分的代码,是先进行环境检测,检测完成后才进行真正的加密,所以才可以修改字节码,使得它跳过了环境检测的部分,直接开始核心的加密函数。如果3.0也是沿用之前的逻辑,先进行了检测再加密,那么这种方案就是可行的。

但是3.0没有办法直接进行反汇编,因为相对于2.0的代码来说,增加了控制流的代码,那么最好是先尝试还原了控制流,再做后续处理。

首先是按照前面说的去掉try代码块)
首先肯定是处理反调试,3.0也是有时间检测,但是时间检测被放到了jsvmp内部了,不好直接干掉,那么就把初始化里面关于时间的都干掉

// 删除时间参数traverse(ast, {     SwitchCase(path){         if(path.node.test){             if(path.node.test.value === 300){                 path.node.consequent.splice(0, 1)            }else if(path.node.test.value === 360){                 path.node.consequent.splice(0, 1)            }else if(path.node.test.value === 368){                 path.node.consequent[0].expression.right.test = t.booleanLiteral(false)            }        }    },    FunctionDeclaration(path){         if(path.node.id && path.node.id.name === 'l'){             for (let i = path.node.body.body.length - 1; i >= 0; i--) {                 let item = path.node.body.body[i];                if(item.expression.left.property.name === 'a' || item.expression.left.property.name === 'U'){                     path.node.body.body.splice(i, 1)                }            }        }    }});

在这里插入图片描述
此时再运行,依然可以得到相同的结果,那么就说明这里的时间和2.0是一样,只是用来反调试,与加密逻辑无关。

接下来也不知道怎么入手,那么就来点暴力点的,这么多个case,有没有可能有一些是没有用到的呢?那么在所有的case前面都下一个断点

在这里插入图片描述
然后调试运行,当在断点停下的时候,取消断点再运行下去,直到结束。那么下载来还有断点的case,就是不会运行到的case了。

let cases_list = [27, 34, 41, 48, 101, 117, 124, 147, 258, 283, 380, 400, 449, 459, 468, 469, 473, 479, 481, 485, 491, 496, 506];traverse(ast, {     SwitchCase(path){         if(path.node.test){             if(cases_list.includes(path.node.test.value)){                 path.remove()            }        }    }});

在这里插入图片描述
这时删除了多个case后,依然可以得到正确结果。

继续往后调试,会发现一些控制流的分支是虚假分支,也就是在运行的时候,是恒真或者恒假的分支,这种最好是可以将它还原掉,方便后面真实分支的case合并。

在这里插入图片描述
分析发现,例如在case 331中存在赋值的【this.M[21] = 8;】,这里就可以把这个值记录下来,其他任何没有出现赋值的,都是null了

let cases_dict = { };// 数组虚假分析traverse(ast, {     MemberExpression(path){         if(path.node.object.type === 'ThisExpression' && path.node.property.type === 'Identifier' && path.node.property.name === 'M' && path.parent.type === 'MemberExpression'){             if(path.parentPath.parent.type === 'AssignmentExpression' && path.parentPath.parent.right.type === 'NumericLiteral'){                 cases_dict[path.parentPath.parent.left.property.value] = path.parentPath.parent.right.value;                path.parentPath.parentPath.parentPath.remove()            }        }    }});traverse(ast, {     MemberExpression(path){         if(path.node.object.type === 'ThisExpression' && path.node.property.type === 'Identifier' && path.node.property.name === 'M' && path.parent.type === 'MemberExpression'){             if(cases_dict.hasOwnProperty(path.parent.property.value)){                 path.parentPath.replaceWith(t.numericLiteral(cases_dict[path.parent.property.value]))            }else{                 path.parentPath.replaceWith(t.numericLiteral(0))            }        }    }});

在这里插入图片描述
这样就转变成了字面量

在这里插入图片描述
接着例如case 50,下面的this.T实际就是上面的50,像这种也可以还原为字面量

// 还原this.Ttraverse(ast, {     SwitchCase(path){         if(path.node.test){             if(path.node.consequent.length >1 && path.node.consequent[path.node.consequent.length - 2].expression.right.type === 'BinaryExpression'){                 let item = path.node.consequent[path.node.consequent.length - 2].expression.right;                if(item.left.type === 'BinaryExpression' && item.left.right.type === 'MemberExpression' && item.left.right.property.type === 'Identifier' && item.left.right.property.name === 'T'){                     item.left.right = t.numericLiteral(path.node.test.value);                }            }        }    }});

在这里插入图片描述
最后是case 331的地方

在这里插入图片描述
这里的【V】变量只出现了一次,所以可以进行手动的处理

在这里插入图片描述
接着再将字面量的内容还原一下

在这里插入图片描述
前面会发现很多个都指向了352,那么这个352肯定是一个关键的地方

在这里插入图片描述
最后还有一些是有【+=】或者【-=】的,这种也可以顺带还原一下

traverse(ast, {     SwitchCase(path){         if(path.node.test){             if(path.node.consequent[path.node.consequent.length - 2].expression.right.type === 'NumericLiteral'){                 if(path.node.consequent[path.node.consequent.length - 2].expression.operator === '+='){                     path.node.consequent[path.node.consequent.length - 2].expression.operator = '=';                    path.node.consequent[path.node.consequent.length - 2].expression.right.value = path.node.consequent[path.node.consequent.length - 2].expression.right.value + path.node.test.value;                }else if(path.node.consequent[path.node.consequent.length - 2].expression.operator === '-='){                     path.node.consequent[path.node.consequent.length - 2].expression.operator = '=';                    path.node.consequent[path.node.consequent.length - 2].expression.right.value = path.node.test.value - path.node.consequent[path.node.consequent.length - 2].expression.right.value;                }            }        }    }});

在这里插入图片描述
那么来到这里,ast的部分就算完成了。

在这里插入图片描述
先看看这个case 352,它是指向case 300

在这里插入图片描述
然后case 300,又指向了case 368,那么如下图

在这里插入图片描述
如果把后面的两个节点合并成一个,那么就少了一个节点了,其实相当于把case 368的代码全部放到case 300的下面

在这里插入图片描述

换完以后,再次运行一下代码,也能得到相同的结果,那就说明这么修改是没有问题的,没有影响原来的执行流程。

接着就可以开始分析vmp的代码逻辑了。根据之前2.0的经验以及分析,case 331就是解码那段base64字符串到字节码的逻辑

let b64_code = 'ABt7CAAUSAAACADfSAAACAD1SAAACAAHSAAAC......';  //内容太长省略了let opcode_list = [];let text_list = [];function decrypt_text(item){     let U = 66;    let M = [];    for (let b = 0; b < item.length; b++) {         M.push(String.fromCharCode(24 ^ item.charCodeAt(b) ^ U));        U = 24 ^ item.charCodeAt(b) ^ U;    }    return M.join("")}(function () {     let D = atob(b64_code);    let w = D.charCodeAt(0) << 16 | D.charCodeAt(1) << 8 | D.charCodeAt(2);    for (var k = 3; k < w + 3; k += 3) {         opcode_list.push(D.charCodeAt(k) << 16 | D.charCodeAt(k + 1) << 8 | D.charCodeAt(k + 2));    }    for (let V = w + 3; V < D.length;) {         let E = D.charCodeAt(V) << 8 | D.charCodeAt(V + 1);        let T = D.slice(V + 2, V + 2 + E);        text_list.push(decrypt_text(T));        V += E + 2;    }})();console.log(opcode_list);console.log(text_list);

在这里插入图片描述
可以看到字节码和字符串都解密出来了,接着就从case 352开始入手写代码,相对于2.0,代码逻辑基本没有变化太多,变化的都是因为控制流而影响的

在这里插入图片描述
相当于把2.0逻辑的所有函数展平,然后用控制流来执行,把之前的代码改一改,又能用起来,还原了初始化的代码如下

(function () {   let local_0 = [20, 223, 245, 7, 248, 2, 194, 209, 87, 6, 227, 253, 240, 128, 222, 91, 237, 9, 125, 157, 230, 93, 252, 205, 90, 79, 144, 199, 159, 197, 186, 167, 39, 37, 156, 198, 38, 42, 43, 168, 217, 153, 15, 103, 80, 189, 71, 191, 97, 84, 247, 95, 36, 69, 14, 35, 12, 171, 28, 114, 178, 148, 86, 182, 32, 83, 158, 109, 22, 255, 94, 238, 151, 85, 77, 124, 254, 18, 4, 26, 123, 176, 232, 193, 131, 172, 143, 142, 150, 30, 10, 146, 162, 62, 224, 218, 196, 229, 1, 192, 213, 27, 110, 56, 231, 180, 138, 107, 242, 187, 54, 120, 19, 44, 117, 228, 215, 203, 53, 239, 251, 127, 81, 11, 133, 96, 204, 132, 41, 115, 73, 55, 249, 147, 102, 48, 122, 145, 106, 118, 74, 190, 29, 16, 174, 5, 177, 129, 63, 113, 99, 31, 161, 76, 246, 34, 211, 13, 60, 68, 207, 160, 65, 111, 82, 165, 67, 169, 225, 57, 112, 244, 155, 51, 236, 200, 233, 58, 61, 47, 100, 137, 185, 64, 17, 70, 234, 163, 219, 108, 170, 166, 59, 149, 52, 105, 24, 212, 78, 173, 45, 0, 116, 226, 119, 136, 206, 135, 175, 195, 25, 92, 121, 208, 126, 139, 3, 75, 141, 21, 130, 98, 241, 40, 154, 66, 184, 49, 181, 46, 243, 88, 101, 183, 8, 23, 72, 188, 104, 179, 210, 134, 250, 201, 164, 89, 216, 202, 220, 50, 221, 152, 140, 33, 235, 214];  let local_1 = [120, 50, 98, 101, 99, 98, 119, 100, 103, 107, 99, 119, 97, 99, 110, 111];  let local_2 = [100, 51, 100, 50, 101, 55, 50, 54, 102, 53, 48, 100, 52, 49, 48, 49];  let local_3 = new Array(32);  let local_23 = [462357, 472066609, 943670861, 1415275113, 1886879365, 2358483617, 2830087869, 3301692121, 3773296373, 4228057617, 404694573, 876298825, 1347903077, 1819507329, 2291111581, 2762715833, 3234320085, 3705924337, 4177462797, 337322537, 808926789, 1280531041, 1752135293, 2223739545, 2695343797, 3166948049, 3638552301, 4110090761, 269950501, 741554753, 1213159005, 1684763257];  let local_24 = [2746333894, 1453994832, 1736282519, 2993693404];  let local_25 = new Array(4);  let local_26 = new Array(36);  let local_6 = (local_2[0] & 255) << 24;  let local_7 = (local_2[0 + 1] & 255) << 16;  let local_8 = (local_2[0 + 2] & 255) << 8;  let local_9 = local_2[0 + 3] & 255;  local_25[0] = local_6 | local_7 | local_8 | local_9;  local_6 = (local_2[4] & 255) << 24;  local_7 = (local_2[4 + 1] & 255) << 16;  local_8 = (local_2[4 + 2] & 255) << 8;  local_9 = local_2[4 + 3] & 255;  local_25[1] = local_6 | local_7 | local_8 | local_9;  local_6 = (local_2[8] & 255) << 24;  local_7 = (local_2[8 + 1] & 255) << 16;  local_8 = (local_2[8 + 2] & 255) << 8;  local_9 = local_2[8 + 3] & 255;  local_25[2] = local_6 | local_7 | local_8 | local_9;  local_6 = (local_2[12] & 255) << 24;  local_7 = (local_2[12 + 1] & 255) << 16;  local_8 = (local_2[12 + 2] & 255) << 8;  local_9 = local_2[12 + 3] & 255;  local_25[3] = local_6 | local_7 | local_8 | local_9;  local_26[0] = local_25[0] ^ local_24[0];  local_26[1] = local_25[1] ^ local_24[1];  local_26[2] = local_25[2] ^ local_24[2];  local_26[3] = local_25[3] ^ local_24[3];  local_9 = 0;  while (local_9 < 32) {     let local_27 = local_26[local_9 + 1];    let local_28 = local_26[local_9 + 2];    let local_29 = local_26[local_9 + 3];    let local_30 = local_27 ^ local_28 ^ local_29 ^ local_23[local_9];    let local_16 = new Array(4);    let local_5 = new Array(4);    local_16[0] = 255 & local_30 >>>24;    local_16[0 + 1] = 255 & local_30 >>>16;    local_16[0 + 2] = 255 & local_30 >>>8;    local_16[0 + 3] = 255 & local_30;    local_5[0] = local_0[local_16[0] & 255];    local_5[1] = local_0[local_16[1] & 255];    local_5[2] = local_0[local_16[2] & 255];    local_5[3] = local_0[local_16[3] & 255];    local_6 = (local_5[0] & 255) << 24;    local_7 = (local_5[0 + 1] & 255) << 16;    local_8 = (local_5[0 + 2] & 255) << 8;    local_9 = local_5[0 + 3] & 255;    let local_17 = local_6 | local_7 | local_8 | local_9;    let local_13 = 32 - 13;    let local_14 = (local_17 & 4294967295) << 13;    let local_15 = local_14 | local_17 >>>local_13;    let local_18 = local_15;    local_13 = 32 - 23;    local_14 = (local_17 & 4294967295) << 23;    local_15 = local_14 | local_17 >>>local_13;    let local_19 = local_15;    let local_20 = local_17 ^ local_18 ^ local_19;    let local_31 = local_20;    local_26[local_9 + 4] = local_26[local_9] ^ local_31;    local_3[local_9] = local_26[local_9 + 4];    local_9 = local_9 + 1;  }  window["__ZH__"]["zse"]["zk"] = local_3;  window["__ZH__"]["zse"]["zb"] = local_0;  window["__ZH__"]["zse"]["zm"] = local_1;  function _0x2068(parameter) { }  __g["_encrypt"] = _0x2068;})();

可以看到这里是初始化了一大堆固定值,然后复制给了【zk】、【zb】、【zm】这三个对象,那么它前面的一大堆计算其实也不用管,因为也没有涉及到时间和随机数,也就是说计算前是定值,那么计算后,也一定是一个定值,那么我们直接用计算后的定值就可以了。最后两句是定义了一个函数,赋值给了【__g】的【_encrypt】属性,这一点和2.0是一模一样的。

那么接着还原一下【_0x2068】内部的逻辑

function _0x2068(parameter) {     let local_47 = Date["now"]();    let local_48 = [48, 53, 57, 48, 53, 51, 102, 55, 100, 49, 53, 101, 48, 49, 100, 55];    let local_44 = Date["now"]();    if (typeof window == "undefined") {       local_44 = 1;    }    if (!(typeof window == "undefined")) {       if (typeof document == "undefined") {         local_44 = 2;      }      if (!(typeof document == "undefined")) {         if (typeof navigator == "undefined") {           local_44 = 3;        }        if (!(typeof navigator == "undefined")) {           if (typeof location == "undefined") {             local_44 = 4;          }          if (!(typeof location == "undefined")) {             if (typeof history == "undefined") {               local_44 = 5;            }            if (!(typeof history == "undefined")) {               if (typeof screen == "undefined") {                 local_44 = 6;              }              if (!(typeof screen == "undefined")) {                 if (typeof navigator["userAgent"] == "undefined") {                   local_44 = 7;                }                if (!(typeof navigator["userAgent"] == "undefined")) {                   if (window["name"] == "nodejs") {                     local_44 = 8;                  }                  if (!(window["name"] == "nodejs")) {                     if (document["toString"]()["indexOf"]("HTMLDocument") == -1) {                       local_44 = 10;                    }                    if (!(document["toString"]()["indexOf"]("HTMLDocument") == -1)) {                       if (navigator["toString"]()["indexOf"]("Navigator") == -1) {                         local_44 = 11;                      }                      if (!(navigator["toString"]()["indexOf"]("Navigator") == -1)) {                         if (location["toString"]()["indexOf"]("http") == -1) {                           local_44 = 12;                        }                        if (!(location["toString"]()["indexOf"]("http") == -1)) {                           if (history["toString"]()["indexOf"]("History") == -1) {                             local_44 = 13;                          }                          if (!(history["toString"]()["indexOf"]("History") == -1)) {                             if (screen["toString"]()["indexOf"]("Screen") == -1) {                               local_44 = 14;                            }                            if (!(screen["toString"]()["indexOf"]("Screen") == -1)) {                               if (navigator["userAgent"]["toLowerCase"]()["indexOf"]("headless") !== -1) {                                 local_44 = 15;                              }                              if (!(navigator["userAgent"]["toLowerCase"]()["indexOf"]("headless") !== -1)) {                                 let local_45 = Object["getOwnPropertyDescriptor"];                                if (local_45["toString"]()["indexOf"]("native code") == -1) {                                   local_44 = 16;                                }                                if (!(local_45["toString"]()["indexOf"]("native code") == -1)) {                                   if (typeof document["createElement"] == "undefined") {                                     local_44 = 17;                                  }                                  if (!(typeof document["createElement"] == "undefined")) {                                     if (document["createElement"]("canvas")["getContext"]("2d")["toString"]()["indexOf"]("Canvas") == -1) {                                       local_44 = 22;                                    }                                    if (!(document["createElement"]("canvas")["getContext"]("2d")["toString"]()["indexOf"]("Canvas") == -1)) {                                       if (typeof window["buffer"] !== "undefined") {                                         local_44 = 24;                                      }                                      if (!(typeof window["buffer"] !== "undefined")) {                                         if (typeof window["emit"] !== "undefined") {                                           local_44 = 25;                                        }                                        if (!(typeof window["emit"] !== "undefined")) {                                           if (typeof window["callPhantom"] !== "undefined") {                                             local_44 = 26;                                          }                                          if (!(typeof window["callPhantom"] !== "undefined")) {                                             if (typeof window["__phantomas"] !== "undefined") {                                               local_44 = 27;                                            }                                            if (!(typeof window["__phantomas"] !== "undefined")) {                                               if (typeof window["_phantom"] !== "undefined") {                                                 local_44 = 28;                                              }                                              if (!(typeof window["_phantom"] !== "undefined")) {                                                 if (typeof window["WebPage"] !== "undefined") {                                                   local_44 = 29;                                                }                                                if (!(typeof window["WebPage"] !== "undefined")) {                                                   if (typeof window["fxdriver_id"] !== "undefined") {                                                     local_44 = 30;                                                  }                                                  if (!(typeof window["fxdriver_id"] !== "undefined")) {                                                     if (typeof window["__fxdriver_unwrapped"] !== "undefined") {                                                       local_44 = 31;                                                    }                                                    if (!(typeof window["__fxdriver_unwrapped"] !== "undefined")) {                                                       if (typeof window["domAutomation"] !== "undefined") {                                                         local_44 = 32;                                                      }                                                      if (!(typeof window["domAutomation"] !== "undefined")) {                                                         if (typeof window["ubot"] !== "undefined") {                                                           local_44 = 33;                                                        }                                                        if (!(typeof window["ubot"] !== "undefined")) {                                                           if (typeof window["CasperError"] !== "undefined") {                                                             local_44 = 34;                                                          }                                                          if (!(typeof window["CasperError"] !== "undefined")) {                                                             if (typeof window["casper"] !== "undefined") {                                                               local_44 = 35;                                                            }                                                            if (!(typeof window["casper"] !== "undefined")) {                                                               if (typeof window["patchRequire"] !== "undefined") {                                                                 local_44 = 36;                                                              }                                                              if (!(typeof window["patchRequire"] !== "undefined")) {                                                                 if (typeof document["$cdc_asdjflasutopfhvcZLmcfl_"] !== "undefined") {                                                                   local_44 = 37;                                                                }                                                                if (!(typeof document["$cdc_asdjflasutopfhvcZLmcfl_"] !== "undefined")) {                                                                   if (navigator["webdriver"] == true) {                                                                     local_44 = 38;                                                                  }                                                                  if (!(navigator["webdriver"] == true)) {                                                                     if (typeof document["__webdriver_script_fn"] !== "undefined") {                                                                       local_44 = 39;                                                                    }                                                                    if (!(typeof document["__webdriver_script_fn"] !== "undefined")) {                                                                       if (typeof window["_resourceLoader"] !== "undefined") {                                                                         local_44 = 40;                                                                      }                                                                      if (!(typeof window["_resourceLoader"] !== "undefined")) {                                                                         if (typeof window["_sessionHistory"] !== "undefined") {                                                                           local_44 = 41;                                                                        }                                                                        if (!(typeof window["_sessionHistory"] !== "undefined")) {                                                                           if (typeof window["global"] !== "undefined") {                                                                             local_44 = 42;                                                                          }                                                                          if (!(typeof window["global"] !== "undefined")) {                                                                             if (typeof Object["getPrototypeOf"](alert) !== "function") {                                                                               local_44 = 43;                                                                            }                                                                            if (!(typeof Object["getPrototypeOf"](alert) !== "function")) {                                                                               if (typeof document["getElementById"] == "undefined") {                                                                                 local_44 = 44;                                                                              }                                                                              if (!(typeof document["getElementById"] == "undefined")) {                                                                                 if (typeof Object["getPrototypeOf"](document["getElementById"]) !== "function") {                                                                                   local_44 = 45;                                                                                }                                                                                if (!(typeof Object["getPrototypeOf"](document["getElementById"]) !== "function")) {                                                                                   if (typeof document["getElementsByClassName"] == "undefined") {                                                                                     local_44 = 46;                                                                                  }                                                                                  if (!(typeof document["getElementsByClassName"] == "undefined")) {                                                                                     if (window["__proto__"]["constructor"]["toString"]()["indexOf"]("[native code]") == -1) {                                                                                       local_44 = 48;                                                                                    }                                                                                    if (!(window["__proto__"]["constructor"]["toString"]()["indexOf"]("[native code]") == -1)) {                                                                                       if (typeof window["__nightmare"] !== "undefined") {                                                                                         local_44 = 49;                                                                                      }                                                                                      if (!(typeof window["__nightmare"] !== "undefined")) {                                                                                         if (new Error()["stack"]["indexOf"]("localhost") !== -1) {                                                                                           local_44 = 50;                                                                                        }                                                                                        if (!(new Error()["stack"]["indexOf"]("localhost") !== -1)) {                                                                                           if (new Error()["stack"]["indexOf"]("puppeteer") !== -1) {                                                                                             local_44 = 51;                                                                                          }                                                                                          if (!(new Error()["stack"]["indexOf"]("puppeteer") !== -1)) {                                                                                             if (navigator["userAgent"]["toLowerCase"]()["indexOf"]("phantomjs") !== -1) {                                                                                               local_44 = 52;                                                                                            }                                                                                            if (!(navigator["userAgent"]["toLowerCase"]()["indexOf"]("phantomjs") !== -1)) {                                                                                               if (navigator["userAgent"]["toLowerCase"]()["indexOf"]("electron") !== -1) {                                                                                                 local_44 = 53;                                                                                              }                                                                                              if (!(navigator["userAgent"]["toLowerCase"]()["indexOf"]("electron") !== -1)) {                                                                                                 if (location["href"]["indexOf"]("localhost") !== -1) {                                                                                                   local_44 = 54;                                                                                                }                                                                                                if (!(location["href"]["indexOf"]("localhost") !== -1)) {                                                                                                   if (window["spawn"]) {                                                                                                     local_44 = 56;                                                                                                  }                                                                                                  if (!window["spawn"]) {                                                                                                     if (typeof window["_Selenium_IDE_Recorder"] !== "undefined") {                                                                                                       local_44 = 57;                                                                                                    }                                                                                                    if (!(typeof window["_Selenium_IDE_Recorder"] !== "undefined")) {                                                                                                       if (typeof window["_selenium"] !== "undefined") {                                                                                                         local_44 = 58;                                                                                                      }                                                                                                      if (!(typeof window["_selenium"] !== "undefined")) {                                                                                                         if (typeof window["__webdriver_evaluate"] !== "undefined") {                                                                                                           local_44 = 59;                                                                                                        }                                                                                                        if (!(typeof window["__webdriver_evaluate"] !== "undefined")) {                                                                                                           if (typeof window["__selenium_evaluate"] !== "undefined") {                                                                                                             local_44 = 60;                                                                                                          }                                                                                                          if (!(typeof window["__selenium_evaluate"] !== "undefined")) {                                                                                                             if (typeof window["__webdriver_script_function"] !== "undefined") {                                                                                                               local_44 = 61;                                                                                                            }                                                                                                            if (!(typeof window["__webdriver_script_function"] !== "undefined")) {                                                                                                               if (typeof window["__fxdriver_evaluate"] !== "undefined") {                                                                                                                 local_44 = 62;                                                                                                              }                                                                                                              if (!(typeof window["__fxdriver_evaluate"] !== "undefined")) {                                                                                                                 if (typeof window["__driver_unwrapped"] !== "undefined") {                                                                                                                   local_44 = 63;                                                                                                                }                                                                                                                if (!(typeof window["__driver_unwrapped"] !== "undefined")) {                                                                                                                   local_44 = 0;                                                                                                                }                                                                                                              }                                                                                                            }                                                                                                          }                                                                                                        }                                                                                                      }                                                                                                    }                                                                                                  }                                                                                                }                                                                                              }                                                                                            }                                                                                          }                                                                                        }                                                                                      }                                                                                    }                                                                                  }                                                                                }                                                                              }                                                                            }                                                                          }                                                                        }                                                                      }                                                                    }                                                                  }                                                                }                                                              }                                                            }                                                          }                                                        }                                                      }                                                    }                                                  }                                                }                                              }                                            }                                          }                                        }                                      }                                    }                                  }                                }                              }                            }                          }                        }                      }                    }                  }                }              }            }          }        }      }    }    let local_49 = local_44;    let local_41 = [];    let local_42 = parameter["length"];    let local_12 = 0;    while (local_12 < local_42) {       let local_43 = parameter["charCodeAt"](local_12);      local_41["push"](local_43 & 255);      local_12 = local_12 + 1;    }    let local_50 = local_41;    let local_51 = Date["now"]() - local_47;    if (local_51 >10000) {       local_49 = 126;    }    if (!(local_51 >10000)) { }    let local_52 = Math["floor"](Math["random"]() * 127);    local_50["unshift"](local_49);    local_50["unshift"](local_52);    let local_21 = local_50["length"] % 16;    let local_10 = 16 - local_21;    let local_33 = 0;    while (local_33 < local_10) {       local_50["push"](local_10);      local_33 = local_33 + 1;    }    let local_34 = local_50["slice"](0, 16);    let local_35 = new Array(16);    let local_11 = 0;    while (local_11 < 16) {       local_35[local_11] = local_34[local_11] ^ local_48[local_11] ^ 42;      local_11 = local_11 + 1;    }    let local_36 = __g["r"](local_35);    let local_37 = local_36["slice"]();    let local_38 = local_50["slice"](16, local_50["length"]);    let local_39 = __g["x"](local_38, local_36);    local_37 = local_37["concat"](local_39);    let local_53 = local_37;    let local_54 = local_53["length"] % 3;    if (local_54 == 1) {       local_53["push"]("\0");      local_53["push"]("\0");    }    if (!(local_54 == 1)) { }    if (local_54 == 2) {       local_53["push"]("\0");    }    if (!(local_54 == 2)) { }    let local_55 = "6fpLR" + "qJO8M/c3j" + "nYxFkUV" + "C4ZIG12SiH=5v0mXDazWB" + "Tsuw7QetbKdoPyAl+hN9rgE";    let local_56 = 0;    let local_57 = "";    let local_13 = local_53["length"] - 1;    while (local_13 >= 0) {       let local_58 = 8 * (local_56 % 4);      local_56 = local_56 + 1;      let local_59 = local_53[local_13] ^ 58 >>>local_58 & 255;      local_58 = 8 * (local_56 % 4);      local_56 = local_56 + 1;      local_59 = local_59 | (local_53[local_13 - 1] ^ 58 >>>local_58 & 255) << 8;      local_58 = 8 * (local_56 % 4);      local_56 = local_56 + 1;      local_59 = local_59 | (local_53[local_13 - 2] ^ 58 >>>local_58 & 255) << 16;      local_57 = local_57 + local_55["charAt"](local_59 & 63);      local_57 = local_57 + local_55["charAt"](local_59 >>>6 & 63);      local_57 = local_57 + local_55["charAt"](local_59 >>>12 & 63);      local_57 = local_57 + local_55["charAt"](local_59 >>>18 & 63);      local_13 = local_13 - 3;    }    return local_57;  }

环境检测部分有点长,但是却发现,3.0的逻辑也是先进行环境检测,再进行加密函数的,那么到这里就可以确定,之前说的修改字节码的方法是可以实现的。

修改的核心思路那之前的基本相似,就是当第一次出现跳转指令的时候,把pc寄存器的值修改到第二次跳转的后面

在这里插入图片描述
3.0的跳转代码在case 443,第一次出现跳转时,pc寄存器的值为【1284】,这里的this.b就是要转跳的值,在第一次跳转的时候暂时不用管。

在这里插入图片描述
接着进入到第二次跳转,此时的pc寄存器位【1288】,并且记录一下this.b为【2066】。这时就可以尝试修改。但是这里有一个逗号表达式,不方便我们修改,那么手动把它搞成一个一个语句。

在这里插入图片描述
但是这样还不对,因为环境检测还不是一个正确的值,那么直接设置为检验通过的值

在这里插入图片描述
那么加上检测校验正确的值,那么就应该可以了,前面仅仅保留一些必要的代码

window=global;Math.random = function(){     return 0.50};

在这里插入图片描述

这时运行发现,结果也是一模一样的,那么说明修改字节码的方案完成。

算法还原

那么把vmp反汇编出来了,那么算法还原的难度也就基本没有了,相当于就是把js的逻辑写成其他语言的逻辑

def x_zse_96_b64encode(md5_bytes: bytes):    h = {         "zk": [1170614578, 1024848638, 1413669199, -343334464, -766094290, -1373058082, -143119608, -297228157, 1933479194, -971186181, -406453910, 460404854, -547427574, -1891326262, -1679095901, 2119585428, -2029270069, 2035090028, -1521520070, -5587175, -77751101, -2094365853, -1243052806, 1579901135, 1321810770, 456816404, -1391643889, -229302305, 330002838, -788960546, 363569021, -1947871109],        "zb": [20, 223, 245, 7, 248, 2, 194, 209, 87, 6, 227, 253, 240, 128, 222, 91, 237, 9, 125, 157, 230, 93, 252, 205, 90, 79, 144, 199, 159, 197, 186, 167, 39, 37, 156, 198, 38, 42, 43, 168, 217, 153, 15, 103, 80, 189, 71, 191, 97, 84, 247, 95, 36, 69, 14, 35, 12, 171, 28, 114, 178, 148, 86, 182, 32, 83, 158, 109, 22, 255, 94, 238, 151, 85, 77, 124, 254, 18, 4, 26, 123, 176, 232, 193, 131, 172, 143, 142, 150, 30, 10, 146, 162, 62, 224, 218, 196, 229, 1, 192, 213, 27, 110, 56, 231, 180, 138, 107, 242, 187, 54, 120, 19, 44, 117, 228, 215, 203, 53, 239, 251, 127, 81, 11, 133, 96, 204, 132, 41, 115, 73, 55, 249, 147, 102, 48, 122, 145, 106, 118, 74, 190, 29, 16, 174, 5, 177, 129, 63, 113, 99, 31, 161, 76, 246, 34, 211, 13, 60, 68, 207, 160, 65, 111, 82, 165, 67, 169, 225, 57, 112, 244, 155, 51, 236, 200, 233, 58, 61, 47, 100, 137, 185, 64, 17, 70, 234, 163, 219, 108, 170, 166, 59, 149, 52, 105, 24, 212, 78, 173, 45, 0, 116, 226, 119, 136, 206, 135, 175, 195, 25, 92, 121, 208, 126, 139, 3, 75, 141, 21, 130, 98, 241, 40, 154, 66, 184, 49, 181, 46, 243, 88, 101, 183, 8, 23, 72, 188, 104, 179, 210, 134, 250, 201, 164, 89, 216, 202, 220, 50, 221, 152, 140, 33, 235, 214],        "zm": [120, 50, 98, 101, 99, 98, 119, 100, 103, 107, 99, 119, 97, 99, 110, 111]    }    def left_shift(x, y):        x, y = ctypes.c_int32(x).value, y % 32        return ctypes.c_int32(x << y).value    def Unsigned_right_shift(x, y):        x, y = ctypes.c_uint32(x).value, y % 32        return ctypes.c_uint32(x >>y).value    def Q(e, t):        return left_shift((4294967295 & e), t) | Unsigned_right_shift(e, 32 - t)    def G(e):        t = list(struct.pack(">i", e))        n = [h['zb'][255 & t[0]], h['zb'][255 & t[1]], h['zb'][255 & t[2]], h['zb'][255 & t[3]]]        r = struct.unpack(">i", bytes(n))[0]        return r ^ Q(r, 2) ^ Q(r, 10) ^ Q(r, 18) ^ Q(r, 24)    def g_r(e):        n = list(struct.unpack(">iiii", bytes(e)))        [n.append(n[r] ^ G(n[r + 1] ^ n[r + 2] ^ n[r + 3] ^ h['zk'][r])) for r in range(32)]        return list(struct.pack(">i", n[35]) + struct.pack(">i", n[34]) + struct.pack(">i", n[33]) + struct.pack(">i", n[32]))    def g_x(e, t):        n = []        i = 0        for _ in range(len(e), 0, -16):            o = e[16 * i: 16 * (i + 1)]            a = [o[c] ^ t[c] for c in range(16)]            t = g_r(a)            n += t            i += 1        return n    local_48 = [48, 53, 57, 48, 53, 51, 102, 55, 100, 49, 53, 101, 48, 49, 100, 55]    local_50 = bytes([63, 0]) + md5_bytes  # 随机数  0 是环境检测通过    local_50 = x_zse_96_V3.pad(bytes(local_50))    local_34 = local_50[:16]    local_35 = [local_34[local_11] ^ local_48[local_11] ^ 42 for local_11 in range(16)]    local_36 = g_r(local_35)    local_38 = local_50[16:]    local_39 = g_x(local_38, local_36)    local_53 = local_36 + local_39    local_55 = "6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE"    local_56 = 0    local_57 = ""    for local_13 in range(len(local_53) - 1, 0, -3):        local_58 = 8 * (local_56 % 4)        local_56 = local_56 + 1        local_59 = local_53[local_13] ^ Unsigned_right_shift(58, local_58) & 255        local_58 = 8 * (local_56 % 4)        local_56 = local_56 + 1        local_59 = local_59 | (local_53[local_13 - 1] ^ Unsigned_right_shift(58, local_58) & 255) << 8        local_58 = 8 * (local_56 % 4)        local_56 = local_56 + 1        local_59 = local_59 | (local_53[local_13 - 2] ^ Unsigned_right_shift(58, local_58) & 255) << 16        local_57 = local_57 + local_55[local_59 & 63]        local_57 = local_57 + local_55[Unsigned_right_shift(local_59, 6) & 63]        local_57 = local_57 + local_55[Unsigned_right_shift(local_59, 12) & 63]        local_57 = local_57 + local_55[Unsigned_right_shift(local_59, 18) & 63]    return local_57

运行一下测试代码

在这里插入图片描述
结果也是完全正确的,那么算法还原的方案也完成了。

那么有了编码,会不会也有解码的方法呢?那肯定是有的,因为如果没有,服务器又怎么验证传上来的结果对不对呢。解码方法其实就是编码方法的逆运算。

例如加法和减法互为逆运算,因为一个数字我加上一个数,再减去这个数,还是得到原来的数字。
乘法和除法互为逆运算、异或和自身互为逆运算等等。那么如果要得到解码方法,相当于就是自己写一个逆运算的方法,按照前面的逻辑可以尝试编写

class x_zse_96_V3(object):    local_48 = [48, 53, 57, 48, 53, 51, 102, 55, 100, 49, 53, 101, 48, 49, 100, 55]    local_55 = "6fpLRqJO8M/c3jnYxFkUVC4ZIG12SiH=5v0mXDazWBTsuw7QetbKdoPyAl+hN9rgE"    h = {         "zk": [1170614578, 1024848638, 1413669199, -343334464, -766094290, -1373058082, -143119608, -297228157, 1933479194, -971186181, -406453910, 460404854, -547427574, -1891326262, -1679095901, 2119585428, -2029270069, 2035090028, -1521520070, -5587175, -77751101, -2094365853, -1243052806, 1579901135, 1321810770, 456816404, -1391643889, -229302305, 330002838, -788960546, 363569021, -1947871109],        "zb": [20, 223, 245, 7, 248, 2, 194, 209, 87, 6, 227, 253, 240, 128, 222, 91, 237, 9, 125, 157, 230, 93, 252, 205, 90, 79, 144, 199, 159, 197, 186, 167, 39, 37, 156, 198, 38, 42, 43, 168, 217, 153, 15, 103, 80, 189, 71, 191, 97, 84, 247, 95, 36, 69, 14, 35, 12, 171, 28, 114, 178, 148, 86, 182, 32, 83, 158, 109, 22, 255, 94, 238, 151, 85, 77, 124, 254, 18, 4, 26, 123, 176, 232, 193, 131, 172, 143, 142, 150, 30, 10, 146, 162, 62, 224, 218, 196, 229, 1, 192, 213, 27, 110, 56, 231, 180, 138, 107, 242, 187, 54, 120, 19, 44, 117, 228, 215, 203, 53, 239, 251, 127, 81, 11, 133, 96, 204, 132, 41, 115, 73, 55, 249, 147, 102, 48, 122, 145, 106, 118, 74, 190, 29, 16, 174, 5, 177, 129, 63, 113, 99, 31, 161, 76, 246, 34, 211, 13, 60, 68, 207, 160, 65, 111, 82, 165, 67, 169, 225, 57, 112, 244, 155, 51, 236, 200, 233, 58, 61, 47, 100, 137, 185, 64, 17, 70, 234, 163, 219, 108, 170, 166, 59, 149, 52, 105, 24, 212, 78, 173, 45, 0, 116, 226, 119, 136, 206, 135, 175, 195, 25, 92, 121, 208, 126, 139, 3, 75, 141, 21, 130, 98, 241, 40, 154, 66, 184, 49, 181, 46, 243, 88, 101, 183, 8, 23, 72, 188, 104, 179, 210, 134, 250, 201, 164, 89, 216, 202, 220, 50, 221, 152, 140, 33, 235, 214],        "zm": [120, 50, 98, 101, 99, 98, 119, 100, 103, 107, 99, 119, 97, 99, 110, 111]    }    @staticmethod    def pad(data_to_pad):        padding_len = 16 - len(data_to_pad) % 16        padding = chr(padding_len).encode() * padding_len        return data_to_pad + padding    @staticmethod    def unpad(padded_data):        padding_len = padded_data[-1]        return padded_data[:-padding_len]    @staticmethod    def left_shift(x, y):        x, y = ctypes.c_int32(x).value, y % 32        return ctypes.c_int32(x << y).value    @staticmethod    def Unsigned_right_shift(x, y):        x, y = ctypes.c_uint32(x).value, y % 32        return ctypes.c_uint32(x >>y).value    @classmethod    def Q(cls, e, t):        return cls.left_shift((4294967295 & e), t) | cls.Unsigned_right_shift(e, 32 - t)    @classmethod    def G(cls, e):        t = list(struct.pack(">i", e))        n = [cls.h['zb'][255 & t[0]], cls.h['zb'][255 & t[1]], cls.h['zb'][255 & t[2]], cls.h['zb'][255 & t[3]]]        r = struct.unpack(">i", bytes(n))[0]        return r ^ cls.Q(r, 2) ^ cls.Q(r, 10) ^ cls.Q(r, 18) ^ cls.Q(r, 24)    @classmethod    def g_r(cls, e):        n = list(struct.unpack(">iiii", bytes(e)))        [n.append(n[r] ^ cls.G(n[r + 1] ^ n[r + 2] ^ n[r + 3] ^ cls.h['zk'][r])) for r in range(32)]        return list(struct.pack(">i", n[35]) + struct.pack(">i", n[34]) + struct.pack(">i", n[33]) + struct.pack(">i", n[32]))    @classmethod    def re_g_r(cls, e):        n = [0] * 32 + list(struct.unpack(">iiii", bytes(e)))[::-1]        for r in range(31, -1, -1):            n[r] = cls.G(n[r + 1] ^ n[r + 2] ^ n[r + 3] ^ cls.h['zk'][r]) ^ n[r + 4]        return list(struct.pack(">i", n[0]) + struct.pack(">i", n[1]) + struct.pack(">i", n[2]) + struct.pack(">i", n[3]))    @classmethod    def g_x(cls, e, t):        n = []        i = 0        for _ in range(len(e), 0, -16):            o = e[16 * i: 16 * (i + 1)]            a = [o[c] ^ t[c] for c in range(16)]            t = cls.g_r(a)            n += t            i += 1        return n    @classmethod    def re_g_x(cls, e, t):        n = []        i = 0        for _ in range(len(e), 0, -16):            o = e[16 * i: 16 * (i + 1)]            a = cls.re_g_r(o)            t = [a[c] ^ t[c] for c in range(16)]            n += t            t = o            i += 1        return n    @classmethod    def b64encode(cls, md5_bytes: bytes, device: int = 0, seed: int = 63) ->str:        local_50 = bytes([seed, device]) + md5_bytes  # 随机数  0 是环境检测通过        local_50 = cls.pad(bytes(local_50))        local_34 = local_50[:16]        local_35 = [local_34[local_11] ^ cls.local_48[local_11] ^ 42 for local_11 in range(16)]        local_36 = cls.g_r(local_35)        local_38 = local_50[16:]        local_39 = cls.g_x(local_38, local_36)        local_53 = local_36 + local_39        local_56 = 0        local_57 = ""        for local_13 in range(len(local_53) - 1, 0, -3):            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_59 = local_53[local_13] ^ cls.Unsigned_right_shift(58, local_58) & 255            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_59 = local_59 | (local_53[local_13 - 1] ^ cls.Unsigned_right_shift(58, local_58) & 255) << 8            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_59 = local_59 | (local_53[local_13 - 2] ^ cls.Unsigned_right_shift(58, local_58) & 255) << 16            local_57 = local_57 + cls.local_55[local_59 & 63]            local_57 = local_57 + cls.local_55[cls.Unsigned_right_shift(local_59, 6) & 63]            local_57 = local_57 + cls.local_55[cls.Unsigned_right_shift(local_59, 12) & 63]            local_57 = local_57 + cls.local_55[cls.Unsigned_right_shift(local_59, 18) & 63]        return local_57    @classmethod    def b64decode(cls, x_zse_96: str) ->dict:        local_56 = 0        local_57 = []        for local_13 in range(0, len(x_zse_96), 4):            local_59 = (cls.local_55.index(x_zse_96[local_13 + 3]) << 18) + (cls.local_55.index(x_zse_96[local_13 + 2]) << 12) + (cls.local_55.index(x_zse_96[local_13 + 1]) << 6) + cls.local_55.index(x_zse_96[local_13])            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_57.append((local_59 & 255) ^ cls.Unsigned_right_shift(58, local_58))            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_57.append(((local_59 >>8) & 255) ^ cls.Unsigned_right_shift(58, local_58))            local_58 = 8 * (local_56 % 4)            local_56 = local_56 + 1            local_57.append(((local_59 >>16) & 255) ^ cls.Unsigned_right_shift(58, local_58))        local_36, local_39 = local_57[-16:][::-1], local_57[:-16][::-1]        local_38 = cls.re_g_x(local_39, local_36)        local_35 = cls.re_g_r(local_36)        local_34 = [local_35[local_11] ^ cls.local_48[local_11] ^ 42 for local_11 in range(16)]        local_50 = cls.unpad(bytes(local_34 + local_38))        return {             'seed': local_50[0],            'device': local_50[1],            'md5_bytes': local_50[2:]        }

在这里插入图片描述
非常好的,得到了相同的结果,说明解码函数没有问题了。

后记

这次的分析比2.0的更加详细,其中是因为3.0来说确实是难了一些
1.结果不是完全固定的
2.存在控制流
3.存在两个时间控制的反调试
4.从编码方法中推导出解码方法

更多内容欢迎加入我的星球学习

在这里插入图片描述

未经允许不得转载:含笑九泉网 » 【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶