WASM的全称是WebAssembly,是一种使用浏览器运行非javascript代码的技术,常见的一般是用javascript调用C/C++文件,执行其中的方法

要逆向的话,就得找到 .wasm 后缀的文件,找到加密逻辑,然后用python进行调用,来看两个案例,方便理解

spa14

题目地址:https://spa14.scrape.center/

直接在 network 中找到 wasm文件,然后保存到本地

那要怎么打开wasm文件呢,直接以文本形式打开的话,是一大串乱码

这里有一个专门解析wasm源码的网站:https://webassembly.github.io/wabt/demo/wasm2wat/, 点击upload上传文件

源码看不懂的话,先放一边,来分析一下原网站

要获取数据的话,就得找到sign的生成逻辑,打xhr断点追栈(也可以直接搜 **sign:**)

来看看 sign的生成逻辑:

1
2
n = (this.page - 1) * this.limit;
e = this.$wasm.asm.encrypt(n, parseInt(Math.round((new Date).getTime() / 1e3).toString()));
  • parseInt 我之前有介绍过,这里纯数字,位数也是默认的10位,可以直接用 int() 复写
  • (new Date).getTime() Math.round(f) 分别是 13位时间戳和四舍五入取整的意思

关键是这里 this.$wasm.asm.encrypt ,很明显是调用了wasm文件的asm对象的encrypt方法

进入 encrypt的FunctionLocation内部查看

可以看到进入wasm文件的内部,这些代码其实c++文件经过了Emscripten编译转换了的结果,这和前面我们去网站解析的结果几乎是一样的

网站使用的是javascript调用了wasm文件,那么如何用python调用呢?有两种方案,这里先介绍第一种也是比较常用的一种,

pywasm, A WebAssembly interpreter written in pure Python

使用方法这里就不介绍了,直接上代码测试:

1
2
3
4
5
In [5]: runtime = pywasm.load("Wasm.wasm")
In [6]: sign = runtime.exec("encrypt", [40, 1655562331])

In [7]: sign
Out[7]: 551870508

对比一下网站的运行结果,发现是一致的

代码整理

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
import requests
import pywasm
import time


def get_sign(offset):
runtime = pywasm.load("Wasm.wasm")
t = int(time.time())
sign = runtime.exec("encrypt", [offset, t])
return sign

def main():
url = "https://spa14.scrape.center/api/movie"
offset = 0
params = {
"limit": 10,
"offset": offset,
"sign": get_sign(offset),
}

resp = requests.get(url, params=params)
print(resp.json())


if __name__ == "__main__":
main()

猿人学第十五关

题目地址:https://match.yuanrenxue.com/match/15

还是先抓包分析一下加密字段m,打上xhr断点

可以看到重要的加密代码都在这里面了

  1. m 的值即为 window.m() 函数的结果
  2. m() 函数里面又调用了 window.q() 函数
  3. q() 又是 instance的encode函数,而instance其实就是整理wasm文件

还是先进入 encode函数内部看一看

同样还是看不懂,不过没关系,大概看一下函数名和接收的参数就行

把 wasm文件下到本地解析一下:https://match.yuanrenxue.com/static/match/match15/main.wasm

解析结果基本和浏览器的一致,也是接收两个参数,接下来就是调用wasm文件模拟加密了,这里我会使用第二种方式,也是运行效率较高的一种:wasmer,安装命令如下,分别安装了 wasmer包和cranelift编译器

1
pip install wasmer wasmer_compiler_cranelift

参照pypi的介绍进行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
In [2]: from wasmer import engine, Store, Module, Instance^M
...: from wasmer_compiler_cranelift import Compiler

# 定义保存引擎和编译器的存储
In [3]: store = Store(engine.JIT(Compiler))
# 编译wasm模块用于执行
In [4]: module = Module(store, open('main.wasm', 'rb').read())
# 实例化模块
In [9]: instance = Instance(module)

In [10]: t1 = 827803911
In [11]: t2 = 827803895
In [12]: instance.exports.encode(t1, t2)
Out[12]: 17352512

和原网站的结果对比,保持一致,说明运行成功

代码整理

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
40
41
42
import requests
import time

from random import uniform
from wasmer import engine, Store, Module, Instance, wat2wasm
from wasmer_compiler_cranelift import Compiler


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


def use_wasmer(t1, t2):
store = Store(engine.JIT(Compiler))
module = Module(store, open("main.wasm", "rb").read())
instance = Instance(module)
result = instance.exports.encode(t1, t2)
return result


def main():
for page in range(1, 6):
api = "https://match.yuanrenxue.com/api/match/15"
t1 = int(time.time()) // 2
t2 = int(time.time()) // 2 - round(uniform(0, 1) * 50 + 1)
result = use_wasmer(t1, t2)
m = str(result) + "|" + str(t1) + "|" + str(t2)
params = {
"page": page,
"m": m,
}
res = requests.get(
api, params=params, headers=yuanrenxue_headers
)
print(res.json())


if __name__ == "__main__":
main()

彩蛋

在整理资料的时候发现还有第三种调用wasm的方式:

pywasm3,据说是采用了最快的WebAssembly 解释器 wasm3

安装:pip install pywasm3

报错:Failed building wheel for pywasm3,Microsoft Visual C++ 14.0 is required.

Usage example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import wasm3, base64

# WebAssembly binary
WASM = base64.b64decode("AGFzbQEAAAABBgFgAX4"
"BfgMCAQAHBwEDZmliAAAKHwEdACAAQgJUBEAgAA"
"8LIABCAn0QACAAQgF9EAB8Dws=")

env = wasm3.Environment()
rt = env.new_runtime(1024)
mod = env.parse_module(WASM)
rt.load(mod)
wasm_fib = rt.find_function("fib")
result = wasm_fib(24)
print(result)