Js逆向之远程调用(rpc)免去抠代码补环境

流氓凡 资源分享 2023-01-10 4.77 K 0

目前现在大多数站点都有多多少少的请求签名校验,只有携带签名参数后端校验通过后才会返回数据,这也是最基本的风控规则了。

那对于js逆向来说,比较麻烦的就是扣算法的代码,代码扣下来还需要补环境信息,各种调试比较麻烦。接下来介绍个比较简单也是比较通用的解决方案,并且有搭建好的免费Java后端服务API供调用。

目标站点

https://www.iesdouyin.com/share/user/MS4wLjABAAAA-DwFCTwY-oeXt8FRTN5E_BlkcBGnXWBBCp-_BbcDYi0

image.png

这是我们要抓取的接口和接口中变化的参数:x-bogus、_signature、msToken三个参数 (还有其他环境必须浏览器ua,浏览器指纹等这里不做更加详细说明)


编写Rpc代码

首先我们需要确定,这三个签名参数是在什么时候生成的,这时候就需要各种调试debug 了,比较麻烦也不演示了我直接说了,是在发送请求的时候直接生成的,那么我们可以直接创建一个xhr请求来试试是否正常

function getSign() {
    var xhr = new XMLHttpRequest();
        xhr.open('get', 'https://www.iesdouyin.com/web/api/v2/aweme/post/?reflow_source=reflow_page&sec_uid=MS4wLjABAAAA-DwFCTwY-oeXt8FRTN5E_BlkcBGnXWBBCp-_BbcDYi0&count=21&max_cursor=0', false);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.setRequestHeader('salute-by', 'douyin');
        xhr.send(null);
        return xhr.responseURL; 
}

getSign();

image.png

看到了测试结果是正常的,那么好办了,剩下的直接连接websocket服务api传递参数就行了。

更改后的js代码如下:

function Hlclient(wsURL) {
    this.wsURL = wsURL;
    this.handlers = {};
    this.socket = {};

    if (!wsURL) {
        throw new Error('wsURL can not be empty!!')
    }
    this.connect()
}

Hlclient.prototype.connect = function () {
    console.log('begin of connect to wsURL: ' + this.wsURL);
    var _this = this;
    try {
        this.socket["ySocket"] = new WebSocket(this.wsURL);
        this.socket["ySocket"].onmessage = function (e) {
            console.log("send func", e.data);
            _this.handlerRequest(e.data);
        }
    } catch (e) {
        console.log("connection failed,reconnect after 10s");
        setTimeout(function () {
            _this.connect()
        }, 10000)
    }
    this.socket["ySocket"].onclose = function () {
        console.log("connection failed,reconnect after 10s");
        setTimeout(function () {
            _this.connect()
        }, 10000)
    }

};
Hlclient.prototype.send = function (msg) {
    this.socket["ySocket"].send(msg)
}

Hlclient.prototype.regAction = function (func_name, func) {
    if (typeof func_name !== 'string') {
        throw new Error("an func_name must be string");
    }
    if (typeof func !== 'function') {
        throw new Error("must be function");
    }
    console.log("register func_name: " + func_name);
    this.handlers[func_name] = func;

}
Hlclient.prototype.handlerRequest = function (requestJson) {
    var _this = this;
    var result=JSON.parse(requestJson);
    //console.log(result)
    if (!result['action']) {
        this.sendResult('','need request param {action}');
        return
    }
    action=result["action"]
    var theHandler = this.handlers[action];
    if (!theHandler ||theHandler==undefined){
        this.sendResult(action,'action not found');
        return
    }
    try {
        if (!result["param"]){
            theHandler(function (response) {
                _this.sendResult(action, response);
            })
        }else{
            theHandler(function (response) {
                _this.sendResult(action, response);
            },result["param"])
        }

    } catch (e) {
        console.log("error: " + e);
        _this.sendResult(action+e);
    }
}

Hlclient.prototype.sendResult = function (action, e) {
    let resultMessage = {
      action,
      data:e
    }
    this.send(JSON.stringify(resultMessage));
}

// 只需要关注这里就行了
var service = new Hlclient("wss://api.wslmf.com/websocket/jsrpc/xb/douyin");
// 这里是执行js内方法的,并且返回结果,如果其他业务只需要更改这里面逻辑即可
service.regAction("sign", function (resolve, param) {
    var xhr = new XMLHttpRequest();
    xhr.open('get', param, false);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.setRequestHeader('salute-by', 'douyin');
    xhr.send(null);
    resolve(xhr.responseURL);
})


将上面代码直接添加到控制台执行

image.png

Api地址及参数说明

接口地址:api.wslmf.com/jsrpc/do

请求方式:POST

请求数据类型:application/json

请求参数示例:

{
  "action": "sign",
  "group": "xb",
  "name": "douyin",
  "param": "https://www.iesdouyin.com/web/api/v2/aweme/post/?reflow_source=reflow_page&sec_uid=MS4wLjABAAAA-DwFCTwY-oeXt8FRTN5E_BlkcBGnXWBBCp-_BbcDYi0&count=21&max_cursor=0"
}

参数说明:

action: 对应js代码中的 `sign` 参数,必须一致
group和name对应wss地址中的xb和douyin
param:表示要传递的参数,多个参数自行传递json或者base64都行,到时候在js代码中解析下就行

测试请求及查看结果

image.png

油猴脚本

那为了方便,不必每次都在控制台里面执行这一大段js代码,我将其封装了一个油猴脚本:https://greasyfork.org/zh-CN/scripts/457965-json-rpc-demo

这样当浏览器窗口打开后就会自动执行了

注意事项

当这个服务api公开了后,可能会有各种group和name,请注意这必须是唯一的,但因为websocket仅作为中转服务,我没有做校验,因此自己使用的时候尽可能的起含自己特色的唯一的名字

最后

参考开源项目地址:https://gitee.com/ripperTs/jsrpc

(此项目是后端是Go写的而不是Java,因此跟此Api不兼容,感兴趣的可以自行看看哈!)


评论