先上代码
// ==UserScript==
// @name solcat
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Simulate a POST request to the Solcat API
// @author Your Name
// @match https://solcat.game/*
// @match https://api.solcat.game/*
// @grant GM_xmlhttpRequest
// @grant unsafeWindow
// @run-at document-start
// @connect api.solcat.game
// ==/UserScript==
const payload = {
event: "update",
score: 204,
coins: 80,
gameSpeed: 1.112620234488441,
usingResurrectionPowerUp: 0
};
(function() {
'use strict';
// 保存原始的fetch函数
let oldFetch = window.fetch;
// 示例调用
// 劫持fetch函数
function hookFetch(...args) {
return new Promise((resolve, reject) => {
// 检查请求的URL是否匹配
if (args.length > 0) {
let url = args[0] instanceof Request ? args[0].url : args[0];
console.log("触发了fetch请求:", url);
//检测是否是get score fetch
if(url === 'https://api.solcat.game/v1/game/'+unsafeWindow.sessionId){
console.log('获取到get score,下面打印参数');
console.log(args);
console.log(args[1].headers);
debugger;
// 调用函数
sendGameUpdateRequest(payload,args[1].headers);
// 不调用oldFetch,直接resolve
resolve(new Response(null, { status: 200, statusText: 'OK', url })); // 或者根据需要构造一个假的响应
}
// 检查是否有onProgress回调
if (args[1] && args[1].onProgress) {
console.log("检测到onProgress回调");
}
}
// 调用原始的fetch函数
oldFetch.apply(window, args).then((response) => {
// 打印响应的URL
console.log("响应来自:", response.url);
// 克隆响应对象以便可以多次读取
let clonedResponse = response.clone();
// 检查响应是否可读(状态码200-299)
if (response.ok) {
clonedResponse.json().then((result) => {
console.log("返回数据:", result);
// 检查返回数据中是否有sessionId
if (result.data && result.data.sessionId) {
// 将sessionId记录到window对象下
unsafeWindow.sessionId = result.data.sessionId; // 记录sessionId
console.log("已记录sessionId:", unsafeWindow.sessionId);
}
}).catch((error) => {
console.log("解析JSON失败:", error);
});
}
// 将原始响应传递给resolve
resolve(response);
}).catch((error) => {
// 将错误传递给reject
console.log("fetch请求失败:", error);
reject(error);
});
});
}
// 替换全局的fetch函数
unsafeWindow.fetch = hookFetch; // 使用unsafeWindow确保在页面上下文中执行
function sendGameUpdateRequest(payload,headers) {
const url = "https://api.solcat.game/v1/game/"+unsafeWindow.sessionId;
GM_xmlhttpRequest({
method: "POST",
url: url,
headers: headers,
data: JSON.stringify(payload),
onload: function(response) {
if (response.status === 200) {
const responseData = JSON.parse(response.responseText);
console.log("伪造getscore Response Data:", responseData);
} else {
console.log("伪造getscore Request failed with status:", response.status);
}
},
onerror: function(err) {
console.log("Request error:", err);
}
});
}
})();
这是一个solcat的游戏(类似神庙逃亡)的hook代码,每隔一段时间拾取金币会向v1/game/+sessionid的地址发送一个更新分数和金币的请求,类似这样的payload=》
{
"event": "update",
"score": 38,
"coins": 4,
"gameSpeed": 1.112620234489441,
"usingResurrectionPowerUp": 0
}
,通过上述代码hook发现每次update的时候请求头中的sign都会变,不更新sign就会403,追踪到
function _JS_WebRequest_SetRequestHeader(requestId, header, value) {
var requestOptions = wr.requests[requestId];
if (!requestOptions) {
return
}
var _header = UTF8ToString(header);
var _value = UTF8ToString(value);
requestOptions.init.headers[_header] = _value
}
这个函数似乎是修改header中的sign的,但是看调用栈sign的算法好像和wasm有关 ,
"df": _JS_WebRequest_SetRequestHeader, =》 framework.js中
df call $a.df, =》 wasm文件中
哥哥这个要怎么fetch这个wasm文件https://solcat.game/pc_v_4/Build/wasm来还原sign的算法呀