本教程仅供学习交流,不得用于任何商业用途!!!
任何非法商业使用及商业利益冲突带来的法律纠纷,与此无关,概不负责!!!
前言
一些网站,由于js逆向的成本太高而请求量不大,这个时候选择jsrpc 可以剩下不少时间
目录
5、打开编译好的文件(我的是windows 所有打开的是window_amd64.exe) 这一步就是就是创建服务端
7、注入JS,构建通信环境(JsRpc/resouces/JsEnv_Dev.js at main · jxhczhl/JsRpc)创建客户端
8、服务端 客户端 建立连接 在客户端控制台输入下面任意一条代码
(一) 客户端和服务端以及建立连接后 调用execjs接口就行
(二) 其次找到加密函数,使它暴露在全局变量中,这里我是通过替换文件直接修改代码将其暴露
目标网站:某银理财
jsrpc工具网址:GitHub - jxhczhl/JsRpc: 远程调用(rpc)浏览器方法,免去抠代码补环境 (需要科学上网)
1、下载适配自己操作系统的版本以及源码
2、将源码解压 然后把下载的版本放到源码根目录下
3、源码的一个目录结构
main.go (服务器的主代码)
resouces/JsEnv_De.js (客户端注入js环境)
config.yaml (可选配置文件)
4、rpc实现原理
在网站的控制台新建一个WebScoket客户端链接到服务器通信,调用服务器的接口 服务器会发送信息给客户端 客户端接收到要执行的方法执行完js代码后把获得想要的内容发回给服务器 服务器接收到后再显示出来
5、打开编译好的文件(我的是windows 所有打开的是window_amd64.exe) 这一步就是就是创建服务端
6、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)
说明:接口用?group分组 如 "ws://127.0.0.1:12080/ws?group={}" 以及可选参数 clientId clientId说明:以group分组后,如果有注册相同group的 可以传入这个id来区分客户端,如果不传 服务程序会自动生成一个。当访问调用接口时,服务程序随机发送请求到相同group的客户端里。
//注入例子 group可以随便起名(必填) http://127.0.0.1:12080/go?group={}&action={}¶m={} //这是调用的接口 group填写上面注入时候的,action是注册的方法名,param是可选的参数 param可以传string类型或者object类型(会尝试用JSON.parse)
7、注入JS,构建通信环境(JsRpc/resouces/JsEnv_Dev.js at main · jxhczhl/JsRpc)创建客户端
function Hlclient(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 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 = new WebSocket(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.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("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;
return true
}
//收到消息后这里处理,
Hlclient.prototype.handlerRequest = function (requestJson) {
var _this = this;
try {
var result = JSON.parse(requestJson)
} catch (error) {
console.log("catch error", requestJson);
result = transjson(requestJson)
}
//console.log(result)
if (!result['action']) {
this.sendResult('', 'need request param {action}');
return
}
var action = result["action"]
var theHandler = this.handlers[action];
if (!theHandler) {
this.sendResult(action, 'action not found');
return
}
try {
if (!result["param"]) {
theHandler(function (response) {
_this.sendResult(action, response);
})
return
}
var param = result["param"]
try {
param = JSON.parse(param)
} catch (e) {}
theHandler(function (response) {
_this.sendResult(action, response);
}, param)
} catch (e) {
console.log("error: " + e);
_this.sendResult(action, e);
}
}
Hlclient.prototype.sendResult = function (action, e) {
if (typeof e === 'object' && e !== null) {
try {
e = JSON.stringify(e)
} catch (v) {
console.log(v)//不是json无需操作
}
}
this.send(action + atob("aGxeX14") + e);
}
function transjson(formdata) {
var regex = /"action":(?<actionName>.*?),/g
var actionName = regex.exec(formdata).groups.actionName
stringfystring = formdata.match(/{..data..:.*..\w+..:\s...*?..}/g).pop()
stringfystring = stringfystring.replace(/\\"/g, '"')
paramstring = JSON.parse(stringfystring)
tens = `{"action":` + actionName + `,"param":{}}`
tjson = JSON.parse(tens)
tjson.param = paramstring
return tjson
}
8、服务端 客户端 建立连接 在客户端控制台输入下面任意一条代码
// 注入环境后连接通信
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&clientId=hliang/"+new Date().getTime())
执行完成后,通过查看服务端会发现有一个新上线的连接,此时已经连接成功了
9、远程调用方法介绍
一、接口传js代码让浏览器执行
(一) 客户端和服务端以及建立连接后 调用execjs接口就行
import requests
js_code = """
(function(){
console.log("test")
return "执行成功"
})()
"""
url = "http://localhost:12080/execjs"
data = {
"group": "zzz",
"code": js_code
}
res = requests.post(url, data=data)
print(res.text)
二、浏览器预先注册js方法 传递函数名调用
(一) 无参数获取值
// 注册一个方法 第一个参数hello为方法名,
// 第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)
demo.regAction("hello", function (resolve) {
//这样每次调用就会返回“好困啊+随机整数”
var Js_sjz = "好困啊"+parseInt(Math.random()*1000);
resolve(Js_sjz);
})
访问接口,获得js端的返回值
http://127.0.0.1:12080/go?group=zzz&action=hello
(二) 带参数获取值
//写一个传入字符串,返回base64值的接口(调用内置函数btoa)
demo.regAction("hello2", function (resolve,param) {
//这样添加了一个param参数,http接口带上它,这里就能获得
var base666 = btoa(param)
resolve(base666);
})
访问接口,获得js端的返回值
http://127.0.0.1:12080/go?group=zzz&action=hello2¶m=123456
(三) 带多个参数 并且使用post方式取值
//假设有一个函数 需要传递两个参数
function hlg(User,Status){
return User+"说:"+Status;
}
demo.regAction("hello3", function (resolve,param) {
//这里还是param参数 param里面的key 是先这里写,但到时候传接口就必须对应的上
res=hlg(param["user"],param["status"])
resolve(res);
})
访问接口,获得js端的返回值
url = "http://127.0.0.1:12080/go"
data = {
"group": "zzz",
"action": "hello3",
"param": json.dumps({"user":"黑脸怪","status":"好困啊"})
}
print(data["param"]) #dumps后就是长这样的字符串{"user": "\u9ed1\u8138\u602a", "status": "\u597d\u56f0\u554a"}
res=requests.post(url, data=data) #这里换get也是可以的
print(res.text)
(四) 获取页面基础信息
resp = requests.get("http://127.0.0.1:12080/page/html?group=zzz") # 直接获取当前页面的html
resp = requests.get("http://127.0.0.1:12080/page/cookie?group=zzz") # 直接获取当前页面的cookie
10、项目实操
(一) 首先找到目标网址它的载荷是加密的
(二) 其次找到加密函数,使它暴露在全局变量中,这里我是通过替换文件直接修改代码将其暴露
通过测试已经暴露成功了
(三) 选择合适的方式获取值
这里加密函数只需要接收一个参数,可以选择带参获取值,这里定义一个名为data_encode的函数
demo.regAction("data_encode", function (resolve,param) {
//这样添加了一个param参数,http接口带上它,这里就能获得
resolve(window.SmAencrypt(param));
})
然后到浏览器(客户端)注册这个函数,true表示注册成功
然后编写python代码去访问,结果显示可以正常拿到加密数据真nice
import requests
data = '{"order":"desc","currCode":"","current":2,"finType":"P","keyWord":"","orderBy":"ipoBgnDt","pageSize":5,"prodClcMode":"01","prodLimit":[],"saCode":"","saleState":"","userId":0}'
url = f"http://127.0.0.1:12080/go?group=zzz&action=data_encode¶m={data}"
res = requests.get(url)
print(res.text)