js RPC及Sekro的使用

有时候遇到一些加密参数,我们不想知道它是怎么生成的,只想取它的值,这种情况下就适合使用RPC了
第一种使用websocket的方式,通过油猴脚本把浏览器生成好的加密参数通过socket通信方式发送到本地
以这个网址为例,这是一个rs的网址
http://www.nhc.gov.cn/
查看一下源代码,懂得都懂
在这里插入图片描述

油猴脚本代码

// ==UserScript==
// @name         ws
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://www.fangdi.com.cn/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    window.ws = new WebSocket('ws://127.0.0.1:8010/');
    ws.onopen = function(){
        console.log("连接服务器成功");
        ws.send("Browser start");
    };
    ws.onclose = function(){
        console.log("服务器关闭");
    };
    ws.onerror = function(){
        console.log("连接出错");
    };
    ws.onmessage = function(evt){
        console.log(evt.data)
        console.log(document.cookie);
    }
})();

本地使用python去连接

import asyncio
import websockets

# 把接受的消息打印后返回回去
async def recv_msg(websocket):
    while True:
        recv_text = await websocket.recv()
        print(recv_text)
        await websocket.send(recv_text)

async def main(websocket, path):
    await recv_msg(websocket)

start_server = websockets.serve(main, 'localhost', 8010)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

先运行python,再刷新对应的网址
在控制台输入ws.send(document.cookie)
python就可以接受到cookie
效果如下
在这里插入图片描述

发现只有一个sVoELocvxVW0T,应该有三个cookie的才对
这里需要在浏览器上设置一下
在这里插入图片描述

打了勾的是无法获取,需要双击后点击取消掉
现在三个cookie都能拿到
试下能不能拿到网页数据
在这里插入图片描述

上面的方式虽然可以拿到cookie,但是每次都需要去控制台send
能不能做成api的形式
请求一下就可以拿到cookie的
当然有了,这里大力推荐渣总的Sekrio框架

Sekrio

渣总开发的框架,现成轮子使用、更简单的接入rpc
github: https://github.com/virjar/sekiro
官方文档:https://sekiro.virjar.com/sekiro-doc/index.html
我用的是window,操作以window为例

安装

先把代码下载下来
然后双击运行 build_demo_server.sh
它会自动构建
在这里插入图片描述

构建完成之后
在sekiro-service-demo\target\sekiro-release-demo\bin下有两个启动文件,bat结尾的win,sh是linux
运行sekiro.bat
在这里插入图片描述

可以链接查看是否正常启动 http://127.0.0.1:5620/business-demo/groupList

样例

先把案例跑通,
https://sekiro.virjar.com/sekiro-doc/assets/sekkiro_js_demo.html页面源码都复制下来
创建一个html文件粘贴进去
然后把其中代码修改一下

<script type="text/javascript" src="sekiro_web_client.js"></script> 
改为
<script type="text/javascript" src="https://sekiro.virjar.com/sekiro-doc/assets/sekiro_web_client.js"></script>

它里面连接的是自己的服务器
如果要把服务器改为自己的话,就把

var client = new SekiroClient("wss://sekiro.virjar.com/business/register?group=ws-group&clientId=" + guid());
改为
var client = new SekiroClient("ws://127.0.0.1:5620/business-demo/register?group=ws-group&clientId=" + guid());

在这里插入图片描述

运行,在浏览器打开控制台可以看到
在这里插入图片描述

浏览器访问 http://127.0.0.1:5620/business-demo/groupList
看到服务已开启

{"data":["test","ws-group"],"ok":true,"status":0}

用python测试一下

import requests
params ={
    "group":"ws-group",#接口名称
    "action":"executeJs",#注册的服务名
}
res = requests.get("http://127.0.0.1:5620/business-demo/invoke", params=params)
print(res.text)

在这里插入图片描述

修改端口

Sekrio默认的端口是5620
需要修改的话
在项目根目录下\sekiro-service-demo\target\sekiro-release-demo\conf\config.properties进行修改即可

rs网站实战

这里的话还是用上面的网址
这里我们需要油猴来做服务

编辑脚本

新建油猴脚本
在这里插入图片描述

访问https://sekiro.virjar.com/sekiro-doc/assets/sekiro_web_client.js
把sekrio依赖的js脚本复制放到油猴里
并上面样例的html里的这几行也复制进油猴里
在这里插入图片描述

修改下executeJs接口,获取cookie后把值传出来
在这里插入图片描述

完整的油猴脚本:

// ==UserScript==
// @name         sekrio
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  try to take over the world!
// @author       You
// @match        http://www.nhc.gov.cn/*
// @grant        none
// ==/UserScript==

(function() {

    /*
  Copyright (C) 2020 virjar <virjar@virjar.com> for https://github.com/virjar/sekiro

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


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鐜鐨剋ebsocket 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;
    // 涓峜heck 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=ws-group&clientId=" + guid());

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

    client.registerAction("executeJs", function (request, resolve, reject) {
        var code = request['code'];
        if (!code) {
            reject("need param:{code}");
            return;
        }

        //code = "return " + code;

        console.log("executeJs: " + code);

        try {
            // var result = new Function(code)();
            var result = document.cookie;
            resolve(result);
        } catch (e) {
            reject("error: " + e);
        }

    });


})();

保存运行

保存脚本,刷新页面
这样显示表示脚本注入成功
在这里插入图片描述

服务注册也是成功的
在这里插入图片描述

接口调用

import requests
params ={
    "group":"ws-group",#接口名称
    "action":"executeJs",#注册的服务名
    "code": "document.cookie"
}
res = requests.get("http://127.0.0.1:5620/business-demo/invoke", params=params)
print(res.text)

在这里插入图片描述

经过测试正常拿到页面数据
再也不用当心逆向js代码而掉头发了

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值