image.png

目标是 sign 参数的生成,直接搜

image.png

进入sign函数内部查看

image.png

这里 _index_bg_wasm__WEBPACK_IMPORTED_MODULE_0__ 名字很长,其实就是 wasm 模块的意思

image.png

前面已经写过文章介绍了三种方法,python如何调用wasm文件,直接上代码试试:

文件地址:https://match.yuanrenxue.com/api/match20/wasm

1
2
3
In [1]: import pywasm

In [2]: runtime = pywasm.load('wasm.wasm')

报错:Exception: pywasm: missing import ./index_bg.js.__wbg_instanceof_Window_434ce1849eb4e0fc

果然没那么简单 😅 看一下源码,从外部js导入了其他函数

image.png

没办法,只能硬着头皮去肝wasm源码了,先上一段wasm 基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
                  |  代表导出函数 sign  |
伪id 71 | export修饰 | $var0,$var1 参数名 i32代表类型 int32
(func $sign (;71;) (export "sign") (param $var0 i32) (param $var1 i32) (param $var2 i32)
local 为当前函数内部用到的变量
(local $var3 i32) (local $var4 i32) (local $var5 i32) (local $var6 i32) (local $var7 i32) (local $var8 i32) (local $var9 i32) (local $var10 i32) (local $var11 i32) (local $var12 i32) (local $var13 i32) (local $var14 i32) (local $var15 i32) (local $var16 i32) (local $var17 i32) (local $var18 i32) (local $var19 i32) (local $var20 i32) (local $var21 i32) (local $var22 i32) (local $var23 i32) (local $var24 i32) (local $var25 i32) (local $var26 i32) (local $var27 i32) (local $var28 i32) (local $var29 i32) (local $var30 i32) (local $var31 i32) (local $var32 i32) (local $var33 i32) (local $var34 i32) (local $var35 i32) (local $var36 i64)
global.get $global0 global.get 从全局变量中读东西
local.set $var3 local.set 写到局部变量中 读写一般都是成对出现的,一般为赋值操作 (约等于$var3 = $global0)
i32.const 80 也是读操作,从类型定义了一个80的数
local.set $var4 写入到 $var4 (约等于:$var4 = 80
local.get $var3 读$var3
local.get $var4 读$var4
i32.sub 写操作,i32的减法操作
local.set $var5 写入到 $var5 (约等于:$var5 = $var3 - $var4)堆栈操作 先读的在前
local.get $var5
global.set $global0
local.get $var5
local.get $var1
i32.store offset=64 内存操作,store:缓存,load:读取,offset:偏移量
local.get $var5
local.get $var2
i32.store offset=68
i32.const 16
local.set $var6
local.get $var5
local.get $var6
i32.add 加操作,也属于写操作
local.set $var7
local.get $var7
local.get $var1
local.get $var2
call $func246 执行函数操作,可看之前的get操作一直到set停止,这里就相当于 将$var7,$var1,$var2,传入函数执行
local.get $var5
i32.load offset=16
local.set $var8

image.png

加密步骤其实只有这一步,len0 就是 content 的长度,下面几步是从内存中获取值,进入到 wasm 内部查看 sign函数

image.png

再次进入 sign 函数

image.png

一直往下滑,直到看到 md5 字样,然后再次进入函数内部

image.png

此时发现 content 长度发生变化,看一下长度,大概是经过了md5加密,那就先来看一下明文是啥?

image.png

这段代码的意思,大概相当于 md5($var56, $var55),利用 getStringFromWasm0 函数,我们可以查看传入的明文

1
2
getStringFromWasm0(1114192, 31)
>>> '1|1662882794000D#uqGdcw41pWeNXm'

就是在 content 加了一段盐值,经过多次调试发现,盐值不变

验证一下是否为标准的md5加密:

image.png

结果一直,至此逆向分析过程结束

python 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests
import time
import hashlib


yuanrenxue_headers = {
"User-Agent": "yuanrenxue.project",
"Cookie": "sessionid=o231j68mvw81ma7y5o199raleeqp9ft5"
}


def get_sign(page, ts):
data = f"{page}|{ts}D#uqGdcw41pWeNXm".encode()
return hashlib.md5(data).hexdigest()



def main():
total = 0
for page in range(1, 6):
api = "https://match.yuanrenxue.com/api/match/20"
t1 = int(time.time() * 1000)
sign = get_sign(page, t1)
params = {
"page": page,
"sign": sign,
"t": t1,
}
res = requests.get(
api, params=params, headers=yuanrenxue_headers
)
result = res.json()["data"]
print(result)
total += sum(d['value'] for d in result)
print(total)

if __name__ == "__main__":
main()

运行结果:

image.png

过关!

image.png

参考链接:

猿人学第20题(题目会不定时更新)_久见Bug了的博客-CSDN博客_猿人学

【JavaScript 逆向】猿人学 web 第二十题:新年挑战_Yy_Rose的博客-CSDN博客