WebAssembly(wasm)是基于堆栈的虚拟机的二进制指令格式。wasm被设计为可编程C / C ++ / Rust等高级语言的可移植目标,可在Web上部署客户端和服务器应用程序。
wabt是用于WebAssembly的一套工具,包括:
wat2wasm:从WebAssembly文本格式转换为 WebAssembly二进制格式
wasm2wat: wat2wasm的逆函数,从二进制格式转换回文本格式(也称为.wat)
wasm-objdump:显示有关wasm二进制文件的信息。与objdump类似。
wasm-interp:使用基于堆栈的解释器解码并运行WebAssembly二进制文件
wasm-decompile:将wasm二进制文件反编译为可读的类似C的语法。
wat- desugar:解析规范解释程序支持的.wat文本格式(S表达式,平面语法或混合格式)并打印“规范”平面格式
wasm2c:将WebAssembly二进制文件转换为C源代码和标头
wasm-strip:删除WebAssembly二进制文件的部分
wasm-validate:验证WebAssembly二进制格式的文件
wast2json:将wasm spec测试格式的文件转换为JSON文件和关联的wasm二进制文件
wasm-opcodecnt:计算指令的操作码使用量
spectest-interp:读取Spectest JSON文件,然后在解释器中运行其测试
静态分析
JEB反编译
wabt
https://github.com/WebAssembly/wabt
1 2 3 4 5 6 7 8 9 10 11 12
| git config --global url."https://gitclone.com/".insteadOf https:// git clone --recursive https://github.com/WebAssembly/wabt cd wabt git submodule update --init mkdir build cd build cmake .. cmake --build .
./wasm2wat wasm.wasm -o wasm.wat (反汇编) ./wasm2c wasm.wasm -o wasm.c (反编译,末尾字符串) gcc -c wasm.c -o wasm.o (优化,编译时需要wabt/wasm2c目录中的wasm-rt.h、wasm-rt-impl.c、wasm-rt-impl.h三个文件)
|
1 2 3
| apt install wabt
wasm-decompile wasm.wasm
|
注入代码
通过注入代码的方式,来查看内存对应的字符。插入 run()
函数后。
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 43 44 45 46 47
| wasm = i.instance.exports; memories = [wasm.memory] viewDWORD = (addr) =>{ const arr = new Uint32Array(memories[0].buffer.slice(addr, addr + 16)); return arr; }; viewChar = (addr, size = 16) =>{ const arr = new Uint8Array(memories[0].buffer.slice(addr, addr + size)); return String.fromCharCode.apply(null, arr); }; viewHEX = (addr, size = 16) =>{ const arr = new Uint8Array(memories[0].buffer.slice(addr, addr + size)); return (Array.from(arr, x =>x.toString(16).padStart(2, '0')).join(' ')); }; viewHexCode = (addr, size = 16) =>{ const arr = new Uint8Array(memories[0].buffer.slice(addr, addr + size)); return (Array.from(arr, x =>'0x' + x.toString(16).padStart(2, '0')).join(', ')); }; dumpMemory = (addr, size = 16) =>{ const arr = new Uint8Array(memories[0].buffer.slice(addr, addr + size)); return arr; }; viewString = (addr, size = 16) =>{ const arr = new Uint8Array(memories[0].buffer.slice(addr, addr + size)); let max = size; for (let i = 0; i < size; i++) { if (arr[i] === 0) { max = i; break; } } return String.fromCharCode.apply(null, arr.slice(0, max)); }; search = function(stirng) { const m = new Uint8Array(memories[0].buffer); const k = Array.from(stirng, x =>x.charCodeAt()); const match = (j) =>{ return k.every((b, i) =>m[i + j] === b); }; const max = Math.min(10_000_000, m.byteLength || m.length); for (let i = 0; i < max; i++) { if (match(i)) { console.info(i); } } console.info('done'); }
|
调用:
viewChar($var1.value,16)
动态调试
- 在对应文件夹开⼀个简易服务器,使用python创建⼀个简易的本地局域网:
python -m http.server -b localhost
,之后便可以直接用浏览器访问: http://localhost:8000/wasm.html
- 跟进调试,F12打开调试器,打开wasm文件
- ctrl+f 查找main函数,点击地址栏下好断点,然后刷新,可以看见被断下
- F8执行(等价于IDA的F9),F10单步步过,F11单步步入,然后跟进,直到要求我们输⼊flag
- 查看内存的方法:作用域-模块,点击memory右上角的图标
参考:
wasm逆向——(极客大挑战2021wasm