一、jsrpc工具
用的是github上一位大神所写的工具,里面有写具体用法
https://github.com/jxhczhl/JsRpc
点进去下载安装包
下载本地版,https/wss版本需要在当前目录放证书。
下载后直接双击运行,开启服务
api 简介: /list :查看当前连接的ws服务 (get) /ws :浏览器注入ws连接的接口 (ws | wss) /go :获取数据的接口 (get | post) /execjs :传递jscode给浏览器执行 (get | post)
二、改写加密js文件
用JsRPC的话不用去扣代码,所以先定位到加密js文件
用chrome浏览器的overrides功能去覆盖这个文件实现改写
覆盖成功后就会在文件夹下面生成对应的路径与js文件,这个文件可以进行编辑
格式化下文件,把加密函数赋值给全局变量,方便我们调用,需要ctrl+s保存下,这个是新加的,其余不要改 window.zhihu_signature = F(r).encrypt; window.zhihu_md5 = f;
刷新验证下是否走了我们改写的js文件,在控制台用赋值的全局变量测试是可以出结果的
没问题就可以进行rpc调用了
三、rpc调用
1、远程调用: 浏览器预先注册js方法 传递函数名调用 注入JS,构建通信环境,
把项目文档的JsEnv.js文件内容复制到控制台
打开JsEnv 复制粘贴到网站控制台(注意:可以在浏览器开启的时候就先注入环境,不然要放开调试断点才能注入)
注入环境后连接通信
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");
注册一个方法,第一个参数get_sign为方法名,第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)
demo.regAction("get_sign", function (resolve, param) { const args_url = param['str']; console.log("参数值:", param['str']); res = window._zhihu_signature(window.zhihu_md5()(args_url)) console.log("调用结果:", res); resolve(res); })
总的就是把下面这段代码复制到控制台:
function Hlclient(wsURL) {
this.wsURL = wsURL;
this.handlers = {};
this.socket = {};
if (!wsURL) {
throw new Error('wsURL can not be empty!!')
}
this.connect()
this.handlers["_execjs"]=function (resolve,param){
var res=eval(param)
if (!res){
resolve("没有返回值")
}else{
resolve(res)
}
}
}
Hlclient.prototype.connect = function () {
console.log('begin of connect to wsURL: ' + this.wsURL);
var _this = this;
try {
this.socket["ySocket"] = new WebSocket(this.wsURL);
this.socket["ySocket"].onmessage = function (e) {
try{
let blob=e.data
blob.text().then(data =>{
_this.handlerRequest(data);
})
}catch{
console.log("not blob")
_this.handlerRequest(blob)
}
}
} catch (e) {
console.log("connection failed,reconnect after 10s");
setTimeout(function () {
_this.connect()
}, 10000)
}
this.socket["ySocket"].onclose = function () {
console.log("connection failed,reconnect after 10s");
setTimeout(function () {
_this.connect()
}, 10000)
}
};
Hlclient.prototype.send = function (msg) {
this.socket["ySocket"].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;
var result=JSON.parse(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);
})
}else{
var param=result["param"]
try {
param=JSON.parse(param)
}catch (e){
console.log("")
}
theHandler(function (response) {
_this.sendResult(action, response);
},param)
}
} catch (e) {
console.log("error: " + e);
_this.sendResult(action+e);
}
}
Hlclient.prototype.sendResult = function (action, e) {
this.send(action + atob("aGxeX14") + e);
}
// 注入环境后连接通信
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=zzz&name=hlg");
// 第二个参数为函数,resolve里面的值是想要的值(发送到服务器的)
demo.regAction("get_sign", function (resolve, param) {
const args_url = param['str'];
console.log("参数值:", param['str']);
res = window.zhihu_signature(window.zhihu_md5()(args_url))
console.log("调用结果:", res);
resolve(res);
})
运行后,会打印一个true的log
2、用python代码访问接口,获得js端的返回值
import requests, urllib, execjs, json
headers = {
"Connection": "keep-alive",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"x-zse-93": "101_3_3.0",
"x-ab-param": "",
"x-api-version": "3.0.91",
"x-zst-81": "",
"sec-ch-ua-mobile": "?0",
"x-requested-with": "fetch",
"x-zse-96": "2.0_dW8LrkY7pTSI9IQ+Fa9Qwzgr1U9WYDPAAHiXssdcDHCPpW1c2nSP6x=i=saDJrPN",
"x-app-za": "OS=Web",
"sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\"",
"sec-ch-ua-platform": "\"Windows\"",
"Accept": "*/*",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Dest": "empty",
"Accept-Language": "zh-CN,zh;q=0.9"
}
cookies = {
"d_c0": "ACAX5eGJjRWPTvSFbO5sIWWD9JTXphKeDpw=|1663055747",
}
params = {
"gk_version": "gz-gaokao",
"t": "general",
"q": "带土",
"correction": "1",
"offset": "0",
"limit": "20",
"filter_fields": "",
"lc_idx": "0",
"show_all_topics": "0",
"search_source": "Normal"
}
args_url = '101_3_3.0+/api/v4/search_v3?' + urllib.parse.urlencode(params) + '+' + cookies["d_c0"] + '+' + headers["x-zst-81"]
url = "http://localhost:12080/go"
data = {
"group": "zzz",
"name": "hlg",
"action":"get_sign",
"param":json.dumps({"str":args_url})
}
res = requests.post("http://localhost:12080/go", data=data).json() #这里获取加密值
print(res)
x_zse_96 = "2.0_" + res["data"]
print(x_zse_96)
headers["x-zse-96"] = x_zse_96 #替换得到的加密值,再请求对应接口
url = "https://www.zhihu.com/api/v4/search_v3"
response = requests.get(url, headers=headers, cookies=cookies, params=params)
print(response.text)
print(response)
运行python代码时,控制台会打印出对应log
成功跑出结果
上面是把代码直接复制到控制台上,也可以新建一个snippet,在里面运行代码