🌈据说,看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。
前言:
🧡作者简介:大家好我是 user_from_future ,意思是 “ 来自未来的用户 ” ,寓意着未来的自己一定很棒~
✨个人主页:点我直达,在这里肯定能找到你想要的~
👍专栏介绍:猿人学WEB题目专解 ,提供猿人学WEB题目总计20题的解题思路与方法,如有讲述错误,请不吝赐教。
想看往期历史文章,可以浏览此博文: 历史文章目录
,后续所有文章发布都会同步更新此博文~
题目网址
题目详情
界面是新的颜色,让我们计算 加和。
题目思路
老样子,进入 request
函数,参数入口挺直观的:
打上断点,点击下一页,就找到了我们的核心函数:
看得出来,这次是引用的“加强版”的 wasm
,上一次简单版的 wasm
还是在第15题。
人懒了,先再像第6题一样试一次 RPC 吧。
使用 RPC 远程过程调用解决
上次说到,为什么会有想用这个的想法呢?原因有几个:
- 使用 RPC 远程过程调用很简单。
- 函数入口明确,可以直接调用出结果。
- 懒得逆向 / 逆向恐惧 / 不会逆向 等原因。
- 没有用过 RPC 远程过程调用,想尝试。
这次很明确,函数入口明确 + 懒得逆向。
在 window.sign
函数调用前,加断点断住,然后在控制台注入 RPC
框架代码:
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;
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);
})
}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);
}
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
}
var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=yrx&name=q20");
取消断点,继续运行即可。
单页 RPC 的Python代码:
index = 1
t = int(time.time()) * 1000
url = 'http://localhost:12080/execjs'
data = {
'group': 'yrx',
'name': "q20",
'jscode': f'sign("{index}|{t}")'
}
sign = requests.post(url, data=data).json()['data']
url = f"https://match.yuanrenxue.cn/api/match/20?page={index}&sign={sign}&t={t}"
print(requests.get(url, headers=headers).json())
真是太轻松了,让我都不想处理 wasm
文件解决了~
正常处理 wasm 文件解决
首先,我们尝试向第15题一样,直接用 pywasm
导入 wasm
,没想到报错:
我们去搜一下又发现:
居然用万恶的 eval
来加载!
那先不管这个,我们来看看 sign
函数:
看到调用 wasm
时没有返回值,但最终是由另外一个函数返回的,现在看到几个数字,我们刷新或点下一页看看他会不会变化:
发现除了函数输入值 content
其他的所有数字都是定值!
再看看他返回的函数:
意思就是从指定内存获取数据,所以我们也可以通过这个函数获取 wasm
执行时的数据。
先回到 wasm
文件里。
在本地又不太好浏览,所以我们使用 wabt 将 wasm
格式转换为 wat
格式:
这下才看的舒服,然后搜索 export
看看导出了哪些:
确认过眼神,sign
就是我们想要的函数,全局搜索 $sign
,配合浏览器断点调试:
我们发现这 wat
转出来就是浏览器的简化版。等到我们熟练驾驭 wasm
的时候再来看这文件吧,我们先去浏览器调试看值。
先贴一下刚才的两个值:r0 = 1114360, r1 = 32
,用函数获取值就是 getStringFromWasm0(1114360, 32)
。
我们将所有有关 sign
的函数打个断点,看看他会跳转到哪里去,每跳一次,就更改到 sign
栈,然后执行函数获取值看看。
我们发现在这个函数之前,获取到的值都是那一串:
经过那个 call
就得到了我们的值:
这样我们就压缩了查找的范围,多这样找几次,我们就能获得结果了!
第二次找到的值变化范围:
很明显能发现,这个 sign
执行了 MD5
,然后我们兴致冲冲的去执行 MD5
,发现:window.sign('1|1682259720000')
得到 '0acdc2760793e6bc16dc58225b31f654'
,但我们使用 MD5
加密的结果却是:84c504783d8bc98d3160554f19b99241
,难道这是魔改的 md5
?
算了,还是继续看变值吧:
在这里没有变化是我没想到的:
只能硬着头皮继续找下去了…
第三次值的变化:
执行完 call
,在这里用 32
的长度还报错了,往下试 31
可以:
可以看到后面跟了一串字符,我们刷新看看他变不变:
发现是一个固定值,我们先记录一下:D#uqGdcw41pWeNXm
(不保证每个人的都一样)
然后把这新的值 MD5
加密一下:0acdc2760793e6bc16dc58225b31f654
,和它的加密结果一模一样!
解题源码
import time
import hashlib
import requests
import jsonpath
headers = {
'cookie': 'sessionid=6r3zqh6xc6lhl12enp6l07m7rasokbxo',
'User-Agent': 'yuanrenxue.project'
}
value = 0
for index in range(1, 6):
t = int(time.time()) * 1000
# 使用 RPC 远程过程调用解决:var demo = new Hlclient("ws://127.0.0.1:12080/ws?group=yrx&name=q20");
# url = 'http://localhost:12080/execjs'
# data = {
# 'group': 'yrx',
# 'name': "q20",
# 'jscode': f'sign("{index}|{t}")'
# }
# sign = requests.post(url, data=data).json()['data']
# 正常逆向 WASM 解决
sign = hashlib.md5(f'{index}|{t}D#uqGdcw41pWeNXm'.encode('utf-8')).hexdigest()
url = f"https://match.yuanrenxue.cn/api/match/20?page={index}&sign={sign}&t={t}"
# value += sum(v['value'] for v in requests.get(url, headers=headers).json()['data'])
value += sum(jsonpath.jsonpath(requests.get(url, headers=headers).json(), '$..value'))
print(value)