Sekiro

Sekiro:https://github.com/virjar/sekiro

  • Linux & Mac:执行脚本 build_demo_server.sh,之后得到产出发布压缩包:sekiro-service-demo/target/sekiro-release-demo.zip

  • Windows:可以直接下载:https://oss.virjar.com/sekiro/sekiro-demo

然后在本地运行(需要有 Java 环境,自行配置):

  • Linux & Mac:bin/sekiro.sh
  • Windows:bin/sekiro.bat

sekiro_web_client.js(下载地址:https://sekiro.virjar.com/sekiro-doc/assets/sekiro_web_client.js) 注入到浏览器环境

然后通过 SekiroClient 和 Sekiro 服务器通信,即可直接 RPC 调用浏览器内部方法,官方提供的 SekiroClient 代码样例如下:

function guid() {
    function S4() {
        return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    }
    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
}

var client = new SekiroClient("wss://sekiro.virjar.com/business/register?group=rpc&clientId="+guid());

client.registerAction("clientTime",function(request, resolve, reject){
    resolve(""+new Date());
})

group:业务类型(接口组),每个业务一个 group,group 下面可以注册多个终端(SekiroClient),同时 group 可以挂载多个 Action;

clientId:指代设备,多个设备使用多个机器提供 API 服务,提供群控能力和负载均衡能力;

SekiroClient:服务提供者客户端,主要场景为手机/浏览器等。最终的 Sekiro 调用会转发到 SekiroClient。每个 client 需要有一个惟一的 clientId;

registerAction:接口,同一个 group 下面可以有多个接口,分别做不同的功能;

resolve:将内容传回给客户端的方法;

request:客户端传过来的请求,如果请求里有多个参数,可以以键值对的方式从里面提取参数然后再做处理。

可以将 sekiro_web_client.js 与 SekiroClient 通信代码写在一起,然后根据需求,改写一下通信部分代码:

1.ws 链接改为:ws://127.0.0.1:5620/business-demo/register?group=rpc-test&clientId=,自定义 grouprpc-test

2.注册一个事件 registerActiongetH5fingerprint

3.resolve 返回的结果为 utility.getH5fingerprint(request["url"]),即加密并返回客户端传过来的 url 参数。

(function () {
    'use strict';
    function SekiroClient(wsURL) {
        this.wsURL = wsURL;
        this.handlers = {};
        this.socket = {};
        // check
        if (!wsURL) {
            throw new Error('wsURL can not be empty!!')
        }
        this.webSocketFactory = this.resolveWebSocketFactory();
        this.connect()
    }

    SekiroClient.prototype.resolveWebSocketFactory = function () {
        if (typeof window === 'object') {
            var theWebSocket = window.WebSocket ? window.WebSocket : window.MozWebSocket;
            return function (wsURL) {

                function WindowWebSocketWrapper(wsURL) {
                    this.mSocket = new theWebSocket(wsURL);
                }

                WindowWebSocketWrapper.prototype.close = function () {
                    this.mSocket.close();
                };

                WindowWebSocketWrapper.prototype.onmessage = function (onMessageFunction) {
                    this.mSocket.onmessage = onMessageFunction;
                };

                WindowWebSocketWrapper.prototype.onopen = function (onOpenFunction) {
                    this.mSocket.onopen = onOpenFunction;
                };
                WindowWebSocketWrapper.prototype.onclose = function (onCloseFunction) {
                    this.mSocket.onclose = onCloseFunction;
                };

                WindowWebSocketWrapper.prototype.send = function (message) {
                    this.mSocket.send(message);
                };

                return new WindowWebSocketWrapper(wsURL);
            }
        }
        if (typeof weex === 'object') {
            // this is weex env : https://weex.apache.org/zh/docs/modules/websockets.html
            try {
                console.log("test webSocket for weex");
                var ws = weex.requireModule('webSocket');
                console.log("find webSocket for weex:" + ws);
                return function (wsURL) {
                    try {
                        ws.close();
                    } catch (e) {
                    }
                    ws.WebSocket(wsURL, '');
                    return ws;
                }
            } catch (e) {
                console.log(e);
                //ignore
            }
        }
        //TODO support ReactNative
        if (typeof WebSocket === 'object') {
            return function (wsURL) {
                return new theWebSocket(wsURL);
            }
        }
        // weex 和 PC环境的websocket API不完全一致,所以做了抽象兼容
        throw new Error("the js environment do not support websocket");
    };

    SekiroClient.prototype.connect = function () {
        console.log('sekiro: begin of connect to wsURL: ' + this.wsURL);
        var _this = this;
        // 不check close,让
        // if (this.socket && this.socket.readyState === 1) {
        //     this.socket.close();
        // }
        try {
            this.socket = this.webSocketFactory(this.wsURL);
        } catch (e) {
            console.log("sekiro: create connection failed,reconnect after 2s");
            setTimeout(function () {
                _this.connect()
            }, 2000)
        }

        this.socket.onmessage(function (event) {
            _this.handleSekiroRequest(event.data)
        });

        this.socket.onopen(function (event) {
            console.log('sekiro: open a sekiro client connection')
        });

        this.socket.onclose(function (event) {
            console.log('sekiro: disconnected ,reconnection after 2s');
            setTimeout(function () {
                _this.connect()
            }, 2000)
        });
    };

    SekiroClient.prototype.handleSekiroRequest = function (requestJson) {
        console.log("receive sekiro request: " + requestJson);
        var request = JSON.parse(requestJson);
        var seq = request['__sekiro_seq__'];

        if (!request['action']) {
            this.sendFailed(seq, 'need request param {action}');
            return
        }
        var action = request['action'];
        if (!this.handlers[action]) {
            this.sendFailed(seq, 'no action handler: ' + action + ' defined');
            return
        }

        var theHandler = this.handlers[action];
        var _this = this;
        try {
            theHandler(request, function (response) {
                try {
                    _this.sendSuccess(seq, response)
                } catch (e) {
                    _this.sendFailed(seq, "e:" + e);
                }
            }, function (errorMessage) {
                _this.sendFailed(seq, errorMessage)
            })
        } catch (e) {
            console.log("error: " + e);
            _this.sendFailed(seq, ":" + e);
        }
    };

    SekiroClient.prototype.sendSuccess = function (seq, response) {
        var responseJson;
        if (typeof response == 'string') {
            try {
                responseJson = JSON.parse(response);
            } catch (e) {
                responseJson = {};
                responseJson['data'] = response;
            }
        } else if (typeof response == 'object') {
            responseJson = response;
        } else {
            responseJson = {};
            responseJson['data'] = response;
        }


        if (Array.isArray(responseJson)) {
            responseJson = {
                data: responseJson,
                code: 0
            }
        }

        if (responseJson['code']) {
            responseJson['code'] = 0;
        } else if (responseJson['status']) {
            responseJson['status'] = 0;
        } else {
            responseJson['status'] = 0;
        }
        responseJson['__sekiro_seq__'] = seq;
        var responseText = JSON.stringify(responseJson);
        console.log("response :" + responseText);
        this.socket.send(responseText);
    };

    SekiroClient.prototype.sendFailed = function (seq, errorMessage) {
        if (typeof errorMessage != 'string') {
            errorMessage = JSON.stringify(errorMessage);
        }
        var responseJson = {};
        responseJson['message'] = errorMessage;
        responseJson['status'] = -1;
        responseJson['__sekiro_seq__'] = seq;
        var responseText = JSON.stringify(responseJson);
        console.log("sekiro: response :" + responseText);
        this.socket.send(responseText)
    };

    SekiroClient.prototype.registerAction = function (action, handler) {
        if (typeof action !== 'string') {
            throw new Error("an action must be string");
        }
        if (typeof handler !== 'function') {
            throw new Error("a handler must be function");
        }
        console.log("sekiro: register action: " + action);
        this.handlers[action] = handler;
        return this;
    };

    
    
    
    function guid() {
        function S4() {
            return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
        }

        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    }

    var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=rpc&clientId=" + guid());

    client.registerAction("_token", function (request, resolve, reject) {
        resolve(_token(request["url"]));
    })

})();

查看分组列表:http://127.0.0.1:5620/business-demo/groupList

查看队列状态:http://127.0.0.1:5620/business-demo/clientQueue?group=test

调用转发:http://127.0.0.1:5620/business-demo/invoke?group=test&action=test&param=testparm

使用 Python 调用

import requests

url = "http://127.0.0.1:5620/business-demo/invoke?group=rpc-test&action=_token"

# 如果有参数
params = {
    'page': 2
}

response = requests.request("GET", url, params=params)

print(response.text)

由于 RPC 是一直挂载在同一个浏览器上的,所以针对风控较严格的站点

比如检测 UA、IP 与加密参数绑定之类的,那么 PRC 调用太频繁就不太行了,当然也可以研究研究浏览器群控技术,操纵多个不同浏览器可以一定程度上缓解这个问题



jsrpc2

项目地址:https://github.com/jxhczhl/JsRpc/

一、找到加密的地方

将加密的组成一个函数

在控制台输入window.dcpeng = ct.update,其中ct.update为加密函数

二、此时在本地双击编译好的文件win64-localhost.exe,启动服务

三、之后在控制台注入ws,即将JsEnv.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) {
    this.send(action + atob("aGxeX14") + e);
}

四、连接通信,在控制台输入命令

var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=v&name=test")

五、注册一个方法

// 注册一个方法 第一个参数get_v为方法名,
// 第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)
// param是可传参参数,可以忽略
demo.regAction("get_v", function (resolve, param) {
    // var res = "好困啊" + param;
    var res = dcpeng();  // 参数放这个里面 param
    resolve(JSON.stringify(res));
})

六、之后就可以在浏览器中访问数据了,打开网址 http://127.0.0.1:12080/go?group={}&name={}&action={}&param={} ,这是调用的接口 group和name填写上面注入时候的,action是注册的方法名,param是可选的参数,这里续用上面的例子,网页就是:http://127.0.0.1:12080/go?group=v&name=test&action=get_v

_token = function(){
    a = Date.parse(new Date()) / 1000
    return window.btoa(a)+('|')+binb2b64(hex_sha1(window.btoa(core_sha1(a)))) + b64_sha1(a)
}

 posted on 2022-02-18 17:24  Rannie`  阅读(597)  评论(0编辑  收藏  举报
去除动画
找回动画