您现在的位置是:网站首页 > 博客日记 >

油猴脚本+JSRPC远程调用浏览器原生函数,逆向免扣js

作者:YXN-python 阅读量:76 发布日期:2025-04-02

JSRPC 技术通过远程调用浏览器原生函数,可绕过逆向分析直接获取加密结果。

此文只做简单讲解,详情可以移步官方文档

开源地址:https://github.com/jxhczhl/JsRpc

 

1、启动服务

下载编译后的可执行文件后运行,地址:https://github.com/jxhczhl/JsRpc/releases

 

2、启动 web 连接

两种方式:直接在浏览器控制台输入脚本代码或者添加油猴脚本。

脚本地址:https://github.com/jxhczhl/JsRpc/blob/main/resouces/JsEnv_Dev.js

注意:启动之后,控制台可以关,但是注入的网页不要关

添加油猴脚本如下:

// ==UserScript==
// @name         JSRPC
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  JSRPC
// @author       jxhczhl
// @match        http*://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    var rpc_client_id, Hlclient = function (wsURL) {
        this.wsURL = wsURL;
        this.handlers = {
            _execjs: function (resolve, param) {
                var res = eval(param)
                if (!res) {
                    resolve("没有返回值")
                } else {
                    resolve(res)
                }
            }
        };
        this.socket = undefined;
        if (!wsURL) {
            throw new Error('wsURL不能为空!!')
        }
        this.connect()
    }
    Hlclient.prototype.connect = function () {
        if (this.wsURL.indexOf("clientId=") === -1 && rpc_client_id) {
            this.wsURL += "&clientId=" + rpc_client_id
        }
        console.log('开始连接到wsURL: ' + this.wsURL);
        var _this = this;
        try {
            this.socket = new WebSocket(this.wsURL);
            this.socket.onmessage = function (e) {
                _this.handlerRequest(e.data)
            }
        } catch (e) {
            console.log("连接失败,10s后重新连接...");
            setTimeout(function () {
                _this.connect()
            }, 10000)
        }
        this.socket.onclose = function () {
            console.log('rpc已关闭');
            setTimeout(function () {
                _this.connect()
            }, 10000)
        }
        this.socket.addEventListener('open', (event) => {
            console.log("rpc连接成功");
        });
        this.socket.addEventListener('error', (event) => {
            console.error('rpc连接出错,请检查是否打开服务端:', event.error);
        })
    };
    Hlclient.prototype.send = function (msg) {
        this.socket.send(msg)
    }
    Hlclient.prototype.regAction = function (func_name, func) {
        if (typeof func_name !== 'string') {
            throw new Error("func_name必须是字符串");
        }
        if (typeof func !== 'function') {
            throw new Error("必须是函数");
        }
        console.log("register func_name: " + func_name);
        this.handlers[func_name] = func;
        return true
    }
    Hlclient.prototype.handlerRequest = function (requestJson) {
        var _this = this;
        try {
            var result = JSON.parse(requestJson)
            } catch (error) {
                console.log("请求信息解析错误", requestJson);
                return
            }
        if (result["registerId"]) {
            rpc_client_id = result['registerId']
            return
        }
        if (!result['action'] || !result["message_id"]) {
            console.warn('没有方法或者消息id,不处理');
            return
        }
        var action = result["action"], message_id = result["message_id"]
        var theHandler = this.handlers[action];
        if (!theHandler) {
            this.sendResult(action, message_id, 'action没找到');
            return
        }
        try {
            if (!result["param"]) {
                theHandler(function (response) {
                    _this.sendResult(action, message_id, response);
                })
                return
            }
            var param = result["param"]
            try {
                param = JSON.parse(param)
            } catch (e) {
            }
            theHandler(function (response) {
                _this.sendResult(action, message_id, response);
            }, param)
        } catch (e) {
            console.log("error: " + e);
            _this.sendResult(action, message_id, e);
        }
    }
    Hlclient.prototype.sendResult = function (action, message_id, e) {
        if (typeof e === 'object' && e !== null) {
            try {
                e = JSON.stringify(e)
            } catch (v) {
                console.log(v)//不是json无需操作
            }
        }
        this.send(JSON.stringify({"action": action, "message_id": message_id, "response_data": e}));
    }
    window.Hlclient = Hlclient;
    console.log('jsrpc!');
    var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");
})();

如果是控制台注入的,则还需要在控制台建立连接,如果是油猴脚本,上面代码添加了就不需要了

var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz");

3、连接测试

import requests

js_code = """
(function(){
    console.log("测试执行")
    return "执行成功"
})()
"""

url = "http://localhost:12080/execjs"
data = {
    "group": "zzz",  # 此参数必传
    "code": js_code
}
res = requests.post(url, data=data)
print(res.text)
# {"data":"执行成功","group":"zzz","name":"1bb0a0f5-c0c5-4ec3-92c2-8293b8d45eb6","status":"200"}

 

4、实战测试

地址:https://www.yixiuna.top/

执行完后可以在网站看到一个一秒钟的消息弹窗

import requests

js_code = """
(function(){
    console.log(showMessage('测试弹窗'));
    return '执行弹窗成功'
})()
"""

url = "http://localhost:12080/execjs"
data = {
    "group": "zzz",
    "code": js_code
}
res = requests.post(url, data=data)
print(res.text)

5、api 简介

  • /list :查看当前连接的ws服务 (get)
  • /ws :浏览器注入ws连接的接口 (ws | wss)
  • /wst :ws测试使用-发啥回啥 (ws | wss)
  • /go :获取数据的接口 (get | post)
  • /execjs :传递jscode给浏览器执行 (get | post)
  • /page/cookie :直接获取当前页面的cookie (get)
  • /page/html :获取当前页面的html (get)

 

 

YXN-python

2025-04-02