文章总结: 本文介绍JsRPC工具实现浏览器JavaScript方法远程调用的技术方案,通过WebSocket连接使服务器能调用客户端注册的JS函数。关键步骤包括服务端启动、浏览器环境注入、函数注册及HTTP接口调用,并演示了加密函数调试等逆向工程实战场景。文档提供完整的代码示例和操作流程,具备较强的实践指导价值。 综合评分: 72 文章分类: WEB安全,逆向分析,安全工具,实战经验,安全开发
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¶m=asdsadasdsad](http://127.0.0.1:12080/go?group=zzz&action=mybtoa¶m=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¶m={
"password": "123",
"username": "test",
"validCode": "p5zk"
}
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Gh0xE9 Gh0xE9 Gh0xE9《JavaScript 逆向必学姿势 JSRPC》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论