【Web逆向】【JS逆向系列】某乎x96参数3.0版本与jsvmp进阶
2022-9-30 11:23:47 Author: mp.weixin.qq.com(查看原文) 阅读量:17 收藏

作者坛账号:漁滒

前言

距离上一次的某乎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 jsdom
npm install canvas

然后在头部加上jsdom的代码

 复制代码 隐藏代码
const{JSDOM}=require("jsdom");
const dom=new JSDOM("<!DOCTYPE html><p>Hello world</p>");
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("<!DOCTYPE html><p>Hello world</p>");
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("<!DOCTYPE html><p>Hello world</p>",{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("<!DOCTYPE html><p>Hello world</p>",{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("<!DOCTYPE html><p>Hello world</p>",{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("<!DOCTYPE html><p>Hello world</p>",{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.T
traverse(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.从编码方法中推导出解码方法

-官方论坛
www.52pojie.cn
--推荐给朋友
公众微信号:吾爱破解论坛
或搜微信号:pojie_52

文章来源: https://mp.weixin.qq.com/s?__biz=MjM5Mjc3MDM2Mw==&mid=2651138466&idx=1&sn=1fadb8377568ffc541387675de903190&chksm=bd50b9f68a2730e0bd406e92f0edc9598113e35ea16ca3658d0703b47793f019d07252c9c598#rd
如有侵权请联系:admin#unsafe.sh