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断点
可以看到重要的加密代码都在这里面了
- m 的值即为 window.m() 函数的结果
- m() 函数里面又调用了 window.q() 函数
- 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))
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
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)
|