JavaScript逆向必学姿势JSRPC

admin 2026-06-24 05:31:35 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文介绍JsRPC工具实现浏览器JavaScript方法远程调用的技术方案,通过WebSocket连接使服务器能调用客户端注册的JS函数。关键步骤包括服务端启动、浏览器环境注入、函数注册及HTTP接口调用,并演示了加密函数调试等逆向工程实战场景。文档提供完整的代码示例和操作流程,具备较强的实践指导价值。 综合评分: 72 文章分类: WEB安全,逆向分析,安全工具,实战经验,安全开发


cover_image

JavaScript 逆向必学姿势 JSRPC

Gh0xE9 Gh0xE9

Gh0xE9

2026年6月23日 08:46 北京

在小说阅读器读本章

去阅读

语法基础可以看看 JavaScript

JsRPC

仓库地址:GitHub – jxhczhl/JsRpc: 远程调用(rpc)浏览器方法,免去抠代码补环境

在网站的控制台新建一个WebScoket客户端链接到服务器通信,调用服务器的接口 服务器会发送信息给客户端 客户端接收到要执行的方法执行完js代码后把获得想要的内容发回给服务器,服务器接收到后再显示出来 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) 服务端 ./jsrpc 启动

浏览器进行注入

// 先复制
var rpc_client_id, Hlclient = function (wsURL) {
    this.wsURL = wsURL;
    this.handlers = {
        _execjs: function (resolve, param) {
            try {
                var fn = newFunction('return (async () => { ' + param + ' })()');
                var result = fn();
                if (result && typeof result.then === 'function') {
                    result.then(function(res) {
                        resolve(res !== undefined ? res : "执行成功(无返回值)");
                    }).catch(function(err) {
                        resolve("执行错误: " + (err.message || err));
                    });
                } else {
                    resolve(result !== undefined ? result : "执行成功(无返回值)");
                }
            } catch (err) {
                resolve("语法错误: " + (err.message || err));
            }
        }
    };
    this.socket = undefined;
    if (!wsURL) {
        thrownewError('wsURL can not be empty!!')
    }
    this.connect()
}
Hlclient.prototype.connect = function () {
    if (this.wsURL.indexOf("clientId=") === -1 && rpc_client_id) {
        this.wsURL += "&clientId=" + rpc_client_id
    }
    console.log('begin of connect to wsURL: ' + this.wsURL);
    var _this = this;
    try {
        this.socket = newWebSocket(this.wsURL);
        this.socket.onmessage = function (e) {
            _this.handlerRequest(e.data)
        }
    } catch (e) {
        console.log("connection failed,reconnect after 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._reportActions();
    });
    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') {
        thrownewError("an func_name must be string");
    }
    if (typeof func !== 'function') {
        thrownewError("must be function");
    }
    console.log("register func_name: " + func_name);
    this.handlers[func_name] = func;
    this._reportActions();
    returntrue
}
Hlclient.prototype._reportActions = function () {
    var actions = Object.keys(this.handlers);
    if (this.socket && this.socket.readyState === WebSocket.OPEN) {
        this.send(JSON.stringify({
            "action": "_registerActions",
            "message_id": "",
            "response_data": JSON.stringify(actions)
        }));
    }
}
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"]) {
            const async_result = theHandler(function (response) {
                _this.sendResult(action, message_id, response);
            })
            if (async_result && typeof async_result.then === "function") {
                async_result.catch(e => {
                    _this.sendResult(action, message_id, "" + e);
                });
            }
            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)
        }
    }
    this.send(JSON.stringify({"action": action, "message_id": message_id, "response_data": e}));
}
// 注入环境后连接通信
var demo = newHlclient("ws://127.0.0.1:12080/ws?group=zzz");
// 可选
//var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&clientId=hliang/"+new Date().getTime())

然后浏览器进行链接

Python代码打开进行 hook

import requests

js_code = """
console.log(add(1,2))
"""

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

记录加密函数

例如现在有一个btoa函数,我想要使用jsrpc来调用这个函数

测试一下

然后需要向JsRPC中注册这些函数

// 有参函数
demo.regAction("mybtoa", function (resolve, param) {
    var res = mybtoa(String(param));
    resolve(res);
})

说明一下参数

接着发送get请求, 或post请求到本机的12080端口, 即可获取调用结果

[127.0.0.1:12080/go?group=zzz&action=mybtoa&param=asdsadasdsad](http://127.0.0.1:12080/go?group=zzz&action=mybtoa&param=asdsadasdsad)

举个例子,例如我们要调试一些带加密参数的代码,先在控制台进行记录,然后打上断点

//时间戳
window.time = Date.parse
//requestId
window.id = p
//v函数
window.v1 = v
//签名
window.m = a.a.MD5
//加密
window.enc = l

接着注册函数

//md5函数
demo.regAction("req", function (resolve,param) {
    //请求头
    let timestamp = time(newDate());
    let requestid = id();
    let v_data = JSON.stringify(v1(param));
    let sign = m(v_data + requestid + timestamp).toString();
    //加密请求体
    let encstr = enc(v_data);

    let res = {
        "timestamp":timestamp,
        "requestid":requestid,
        "encstr":encstr,
        "sign":sign
    };
    resolve(res);
})

然后发送数据包

http://127.0.0.1:12080/go
group=zzz&action=req&param={
    "password": "123",
    "username": "test",
    "validCode": "p5zk"
}

免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:Gh0xE9 Gh0xE9 Gh0xE9《JavaScript 逆向必学姿势 JSRPC》

评论:0   参与:  0