JS逆向-小红薯X-S环境分析

前言

听说这是个抓的比较严格的网址,这里就不作太深的分析,仅是将一些环境点给分析出来。
详细的可以看这位大佬的(玩的就是一个风险转移)
小红书x-s新版分析(2023-05-30失效)

一、分析

看波封面
在这里插入图片描述

加密点在这哈,没啥技巧,往上跟就看到了
在这里插入图片描述

window._webmsxyw这个就是一整个js抠出来就行了,补环境,讲究粗暴!
l和a在网页上复制下来即可,大概就是这么个样子。
在这里插入图片描述

先让他正常跑起来再说吧,跑个结果试试,保险起见,先用vm2去跑,防止它检测我们的node环境

const fs = require('fs')
const {
    
     VM, VMScript } = require('vm2');
// console.time('myTimer');
const sandbox = {
    
    
    require: require,
    console: console
};
const codeFile = `${
      
      __dirname}/test.js`;
const vm = new VM({
    
    
    sandbox: sandbox,
    require: {
    
    
        external: true
    }
});
const script = new VMScript(fs.readFileSync(codeFile), `${
      
      __dirname}/我正在调试的代码.js`);
debugger;
result = vm.run(script);
console.log(result)

先补一些最基本的对象吧,然后挨个上个简单代理(为什么不用自己的链式代理,那玩意太长了,我看着烦,偷个懒随机整个)
在这里插入图片描述

然后就可以开始造了,下面是简单代理

better.proxy = function (o, callerName){
    
    
    return new Proxy(o, {
    
    
            set(target, property, value){
    
    
                console.table([{
    
    "类型":"set-->","调用者":callerName,"属性":property,"值":value}]);
                return Reflect.set(...arguments);
            },
            get(target, property, receiver){
    
    
                if (property!=="Math"){
    
    
                    console.table([{
    
    "类型":"get<--","调用者":callerName,"属性":property,"值":target[property]}]);
                }
                return Reflect.get(...arguments);
            },
        }
    );
};

运行一下,可以看到是检测了它这个Symbol(Symbol.toStringTag)属性的
在这里插入图片描述
打开浏览器去看一下,这个属性是有值的,那我们可以去补一下,上面那两个属性都是取不到值的, Symbol(nodejs.util.inspect.custom)这属性,是 Node.js 中用于自定义对象的字符串表示形式的 Symbol。
在这里插入图片描述
所以我们接下来就是要补这个Symbol(Symbol.toStringTag)了,顺带把他原型链也给补了(这里就简单补了一层,并没有太深,问,就是我懒)

var Window = function Window(){
    
    
    throw new TypeError("Illegal constructor");
};

Object.defineProperties(Window.prototype, {
    
    
    [Symbol.toStringTag]:{
    
    
        value:"Window",
        configurable:false, // 通常为 false,不允许删除属性并修改其特性。
        writable:true, // 通常为 true,允许重新赋值。
        enumerable:true // 通常为 true,可以出现在 for...in 循环或 Object.keys() 方法中。
    }
});
window.__proto__ = Window.prototype;

上面那个解决了,往下看,这三个又缺了。补呗,navigator和window是一样的补法。
在这里插入图片描述
这里留个小细节Navigator,后续会遇到,先正常配置就行。剩下两个方法,直接补上去就行了。不给代码了哈,自己老实补。
在这里插入图片描述
后面遇到这种,想必大家都已经学会怎么补了,接下来的就不带大家补这个toString了
在这里插入图片描述
这是上面的一些toString补完后,出现的一个需要补的参数AudioContext方法,在这里呢,我们秉承的原则就是,是对象就代理,直接给他扔一个上去(接下来有关对象的操作都得这么干)
在这里插入图片描述
接下来就到了这一步,很多人在这个方法上出现了不同的歧义
在这里插入图片描述我就展示一下,我的理解,这个方法是在Document的原型上面的,他的作用就是创建一个元素,也就是对象,俗话说万物皆对象,那么我就返回一个对象回去,然后代理它,看看它被取了什么值不就行了吗,这里以canvas为例代理了一下这个canvas_obj对象。(如果是div或者span对象怎么办,创建呗,返回对象的对象过去就行了,有检测原型就补原型就完事了。缺啥补啥,没检测就别浪费时间补了)
在这里插入图片描述
通过我上面的补环境思路,大概随便补一点,数据就出来了,这个时候拿去用肯定是过不了的(我试过),大家观察这个XYW后面的ey开头像不像base64加密,既然像,就拿去解密看看是什么东西
在这里插入图片描述
base64解密网址
在这里插入图片描述
可以看到,其中只有这个payload需要我们去搞清楚是怎么来的,其他的参数几乎是可以固定死的。那我们就需要回到网页上去看看,这是咋加密出来的。
在这里插入图片描述

先把js放上去,然后可以看到第1点就是出我们的X-S的地方。第2点就是返回值了,那么我们就需要在第1点的时候对这个_ace_dcca5[0]做一个hook操作

_ace_dcca5 = new Proxy(_ace_dcca5,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
    set: function(target, prop, value) {
    
    
        // debugger; // 在这里设置断点或执行自定义逻辑
        target[prop] = value;
        // 设置属性的值
        try {
    
    
            if (value['_ace_4de55'] && value['_ace_4de55'].startsWith("XYW")) {
    
    
                debugger ;
            }
        } catch (error) {
    
    
        }
        return true;
    }
});

这里要看好了。第一次是XYW。
在这里插入图片描述
第二次就是XYW_了,那下一次就是我们要的经过base64加密的东西了
在这里插入图片描述
那在这里,我们就要一步步跟进去看了。一直跟到这里就可以发现,这个base64出现了。
在这里插入图片描述
我们接着跟_ace_34d1(3, 49),参数别忘了带进去。
在这里插入图片描述
跟到这一步,其实就能发现一些端倪了。再里面我已经跟过了,这个效果就是把_ace_66赋值给_ace_936这个值,可以发现,这个_ace_66不是赋值进来的,那就代表了什么,这东西是个全局的啊。
在这里插入图片描述
在这里插入图片描述
这个里面的下标49就是我们要的base64加密的东西,那接下来就hook它了。
在这里插入图片描述
这是hook代码,你直接拿去无脑hook是行不通的。要跟进去搞。

_ace_66 = new Proxy(_ace_66,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
  set: function(target, prop, value) {
    
    
    if (prop === "49") {
    
    
      debugger; // 在第 49 个下标被设置时设置断点或执行自定义逻辑
    }
    console.log(value)
    target[prop] = value; // 设置属性的值
    return true; // 表示设置成功
  }
});

在这里插入图片描述
这是成功hook住的样子,这个数据熟悉吧,就是被拿来base64的源数据,
在这里插入图片描述
二话不说,hook它
在这里插入图片描述

_ace_66 = new Proxy(_ace_66,{
    
    
    get: function(target, prop) {
    
    
        return target[prop];
        // 返回属性的值
    },
    set: function(target, prop, value) {
    
    
        try {
    
    
            if (value && value.startsWith("cd")) {
    
    
                debugger ;
            }
        } catch (error) {
    
    }
        if (prop === "49") {
    
    
            debugger ;// 在第 49 个下标被设置时设置断点或执行自定义逻辑
        }
        console.log(value)
        target[prop] = value;
        // 设置属性的值
        return true;
        // 表示设置成功
    }
});

往上跟,可以发现是又是这个_ace_936,跟到这里又到头了,看了一下这个_ace_dcca5是个全局的,好家伙,继续hook。
在这里插入图片描述
继续往上走
在这里插入图片描述
又回到这里了,好家伙
在这里插入图片描述

其实可以发现又跟到死胡同里面了,那我们就接着hook,直接把这个全局变量的值都打印出来,通过打印日志可以发现有个stackInputstackOutput,好家伙这不就真相出来了
就是根据x1=f060e018ee22aede6cdd2393dadd54f9;x2=0|0|0|1|0|0|1|0|0|0|1|0|0|0|0;x3=18995b7979bwbgf099rbj41o4mscuki8wn6a4ep3250000419053;x4=1690954928490;这个加密来的
在这里插入图片描述
那分解一下这个参数,可以得到x1,x2,x3,x4,x4明显就是时间戳,不用管,x3是cookie的a1,也不用管,只有x1和x2还不知道怎么获取的。但是不用慌,在上面打印的过程中,想必大家也看到了x1,x2,x3,x4,就是在_ace_66 hook的时候,接下来我们回去再hook一次_ace_66 看看。
在这里插入图片描述
可以发现,这个url这串字符串出现后,才出现了x1,x1这个长度,有经验的读者应该就会联想到md5,确实这个也就是url这串字符串加密后的md5,这下x1就解决了,只剩一个x2了。
MD5加密地址
在这里插入图片描述
不用着急,打印信息不仅仅只有x1,x2也出来了
在这里插入图片描述
不过往上面看,x2没啥头绪,那么我们就可以回到我们自己的nodejs环境去看看,和浏览器有什么不一样了

二、验证

不需要回去hook我们自己的程序了,根据hook_ace_66的时候,可以发现都是统一经过这个函数出来的,我们回去打印这个函数就可以了。
在这里插入图片描述
记得用cmd看,并且筛选一下信息,不然你看不过来
在这里插入图片描述
可以发现nodejs里面只有x2是和浏览器不一样的,那我们再往上看看打印信息,你会发现,经过了这个set->userAgentx2的某个值就变成1了,这就是之前说的小细节,在浏览器这么做可是不行的噢。
在这里插入图片描述
所以我们就需要做点措施,直接定为不可被set,再看看

Object.defineProperty(Navigator.prototype, "userAgent", {
    
    
    value:'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
    writable: false
  });

看,这就变为0了,还有一个1和浏览器的不一样,我们再往上看看。浏览器的是0|0|0|1|0|0|1|0|0|0|1|0|0|0|0我们的是0|0|0|1|0|0|1|0|0|1|1|0|0|0|0
在这里插入图片描述
往上看看,那个不一样的1在哪,看,经过调用window.Window就变成1了,那必然有问题啊,我们的nodejs的window.Window是

var Window = function Window(){
    
    
    throw new TypeError("Illegal constructor");
};

在这里插入图片描述
浏览器的是window.Window是window本身,那就浅浅瞎操作一下window.Window=window,好家伙这次就和浏览器一样了
在这里插入图片描述
然后把结果拿去请求。到这里小红书的X-S环境就补完了,嘎嘎乱杀。记得请求的data要和加密的一样噢!
在这里插入图片描述

在这里插入图片描述
大概js环境有个300行,还有一些写的没用的。挺简单的,大家可以动手试一下。

借鉴

小红书x-s新版分析(2023-05-30失效)

猜你喜欢

转载自blog.csdn.net/qq_41866988/article/details/132058203