WebAssembly

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)

动态调试

  1. 在对应文件夹开⼀个简易服务器,使用python创建⼀个简易的本地局域网:python -m http.server -b localhost,之后便可以直接用浏览器访问: http://localhost:8000/wasm.html
  2. 跟进调试,F12打开调试器,打开wasm文件
  3. ctrl+f 查找main函数,点击地址栏下好断点,然后刷新,可以看见被断下
  4. F8执行(等价于IDA的F9),F10单步步过,F11单步步入,然后跟进,直到要求我们输⼊flag
  5. 查看内存的方法:作用域-模块,点击memory右上角的图标

参考:

wasm逆向——(极客大挑战2021wasm