参考:
https://xz.aliyun.com/t/5170
https://www.anquanke.com/post/id/179556
https://developer.mozilla.org/zh-CN/docs/WebAssembly/Understanding_the_text_format
https://bbs.ichunqiu.com/thread-46903-1-1.html
TEA & XTEA
明文64位,key128位,分组32位,32轮加密
识别:<< ,>>,^ ,delta
wasm知识
函数刚调用时,栈为空
load,store操作内存
get,set操作栈
对于wasm,所有的字符串会被存放在二进制文件的末尾,以此能获取一些关键的信息
字符串偏移和函数参数
call indirect相关的: elem , type , table…
js层实现和wasm实现
wasm 转 c
大致流程
1.WebAsseembly.Memory() 创建内存实例,.buffer返回一个ArrayBuffer
2.updateGlobalBufferViews:
function updateGlobalBufferViews() {
Module[“HEAP8”] = HEAP8 = new Int8Array(buffer);
Module[“HEAP16”] = HEAP16 = new Int16Array(buffer);
Module[“HEAP32”] = HEAP32 = new Int32Array(buffer);
Module[“HEAPU8”] = HEAPU8 = new Uint8Array(buffer);
Module[“HEAPU16”] = HEAPU16 = new Uint16Array(buffer);
Module[“HEAPU32”] = HEAPU32 = new Uint32Array(buffer);
Module[“HEAPF32”] = HEAPF32 = new Float32Array(buffer);
Module[“HEAPF64”] = HEAPF64 = new Float64Array(buffer)
}
3.fetch then编译并实例化wasm代码,运行后就左边就出现了wasm
4.js导入wasm的导出函数
var exports = instance.exports;
Module[“asm”] = exports;
var _free = Module[“_free”] = function() {
return Module[“asm”][“_free”].apply(null, arguments)
};
var _main = Module[“_main”] = function() {
return Module[“asm”][“_main”].apply(null, arguments)
};
var _malloc = Module[“_malloc”] = function() {
return Module[“asm”][“_malloc”].apply(null, arguments)
};
var _memcpy = Module[“_memcpy”] = function() {
return Module[“asm”][“_memcpy”].apply(null, arguments)
};
var _memset = Module[“_memset”] = function() {
return Module[“asm”][“_memset”].apply(null, arguments)
};
var _sbrk = Module[“_sbrk”] = function() {
return Module[“asm”][“_sbrk”].apply(null, arguments)
};
var establishStackSpace = Module[“establishStackSpace”] = function() {
return Module[“asm”][“establishStackSpace”].apply(null, arguments)
};
var stackAlloc = Module[“stackAlloc”] = function() {
return Module[“asm”][“stackAlloc”].apply(null, arguments)
};
var stackRestore = Module[“stackRestore”] = function() {
return Module[“asm”][“stackRestore”].apply(null, arguments)
};
var stackSave = Module[“stackSave”] = function() {
return Module[“asm”][“stackSave”].apply(null, arguments)
};
var dynCall_ii = Module[“dynCall_ii”] = function() {
return Module[“asm”][“dynCall_ii”].apply(null, arguments)
};
var dynCall_iiii = Module[“dynCall_iiii”] = function() {
return Module[“asm”][“dynCall_iiii”].apply(null, arguments)
};
export对应的函数
(export “___errno_location” (func $func26))
(export “_free” (func $func18))
(export “_main” (func $func16))
(export “_malloc” (func $func17))
(export “_memcpy” (func $func69))
(export “_memset” (func $func70))
(export “_sbrk” (func $func71))
(export “dynCall_ii” (func $func72))
(export “dynCall_iiii” (func $func73))
(export “establishStackSpace” (func $func14))
(export “stackAlloc” (func $func11))
(export “stackRestore” (func $func13))
(export “stackSave” (func $func12))
5.执行wasm main函数进入wasm空间
var ret = Module[“_main”](argc, argv, 0);
浏览器动态调试
由二进制文件末尾的回显信息知输入形式为flag{},输入方式为第一个输入框点确定后,跳出第二个输入框点取消出现回显信息
wasm _main函数call 了f54 f15,现在要确定三个问题:f54,f15,js层哪个对输入做了加密
js层搜Input:下断,断在了result = window.prompt("Input: "); ,查看函数调用堆栈,确定f54和输入无关
run(js层函数) -> doRun ->callMain -> _main(f16,wasm 的 export函数) -> _main进入wasm空间->
5个wasmcall(f54,f32,f34,call_indirect $type0,call $import3 即_syscall145,是js的导出) -> _syscall145进了js空间 ->doReadv ->read->read->get_char
继续跟进js层,发现buffer[offset + i] = result写入了wasmMemory(buffer = wasmMemory.buffer),确定js层没对输入做加密
静态分析f15
__int64 __fastcall f15(unsigned int a1)
{
unsigned int part1; // [rsp+10h] [rbp-50h]
unsigned int v3; // [rsp+10h] [rbp-50h]
unsigned int v4; // [rsp+10h] [rbp-50h]
unsigned int v5; // [rsp+10h] [rbp-50h]
unsigned int sum; // [rsp+14h] [rbp-4Ch]
unsigned int v7; // [rsp+14h] [rbp-4Ch]
unsigned int v8; // [rsp+14h] [rbp-4Ch]
unsigned int v9; // [rsp+14h] [rbp-4Ch]
int index; // [rsp+18h] [rbp-48h]
unsigned int v11; // [rsp+18h] [rbp-48h]
unsigned int v12; // [rsp+18h] [rbp-48h]
unsigned int v13; // [rsp+18h] [rbp-48h]
int i; // [rsp+1Ch] [rbp-44h]
int j; // [rsp+1Ch] [rbp-44h]
int k; // [rsp+1Ch] [rbp-44h]
unsigned int part0; // [rsp+20h] [rbp-40h]
unsigned int key; // [rsp+28h] [rbp-38h]
int v19; // [rsp+3Ch] [rbp-24h]
int v20; // [rsp+3Ch] [rbp-24h]
int v21; // [rsp+3Ch] [rbp-24h]
int v22; // [rsp+3Ch] [rbp-24h]
int v23; // [rsp+3Ch] [rbp-24h]
int v24; // [rsp+3Ch] [rbp-24h]
int v25; // [rsp+3Ch] [rbp-24h]
int v26; // [rsp+3Ch] [rbp-24h]
int v27; // [rsp+3Ch] [rbp-24h]
int v28; // [rsp+3Ch] [rbp-24h]
int v29; // [rsp+3Ch] [rbp-24h]
int v30; // [rsp+3Ch] [rbp-24h]
int v31; // [rsp+3Ch] [rbp-24h]
int v32; // [rsp+3Ch] [rbp-24h]
int v33; // [rsp+3Ch] [rbp-24h]
int v34; // [rsp+3Ch] [rbp-24h]
int v35; // [rsp+3Ch] [rbp-24h]
int v36; // [rsp+3Ch] [rbp-24h]
int v37; // [rsp+3Ch] [rbp-24h]
int v38; // [rsp+3Ch] [rbp-24h]
int v39; // [rsp+3Ch] [rbp-24h]
int v40; // [rsp+3Ch] [rbp-24h]
int v41; // [rsp+3Ch] [rbp-24h]
int v42; // [rsp+3Ch] [rbp-24h]
int v43; // [rsp+3Ch] [rbp-24h]
int v44; // [rsp+3Ch] [rbp-24h]
int v45; // [rsp+3Ch] [rbp-24h]
int v46; // [rsp+3Ch] [rbp-24h]
int v47; // [rsp+3Ch] [rbp-24h]
int v48; // [rsp+3Ch] [rbp-24h]
int v49; // [rsp+3Ch] [rbp-24h]
int v50; // [rsp+3Ch] [rbp-24h]
int v51; // [rsp+3Ch] [rbp-24h]
int v52; // [rsp+3Ch] [rbp-24h]
int v53; // [rsp+3Ch] [rbp-24h]
int v54; // [rsp+3Ch] [rbp-24h]
sum = 0;
index = 0;
if ( ++wasm_rt_call_stack_depth > 0x1F4u )
wasm_rt_trap(7LL);
key = g7;
g7 += 32;
i64_store(Z_envZ_memory, key, 0LL);
i64_store(Z_envZ_memory, key + 8, 0LL);
part1 = i32_load(Z_envZ_memory, a1 + 4); // 加载内存中的4个byte
part0 = i32_load(Z_envZ_memory, a1);
do
{
part0 += (((part1 >> 5) ^ (16 * part1)) + part1) ^ ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * (sum & 3))
+ sum);
sum -= 0x61C88647; // sum += delta;
part1 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * ((sum >> 11) & 3)) + sum) ^ (((part0 >> 5) ^ (16 * part0))
+ part0);
++index;
}
while ( index != 32 );
i32_store(Z_envZ_memory, a1, part0); // 存回去
i32_store(Z_envZ_memory, a1 + 4, part1);
v7 = 0;
v3 = i32_load(Z_envZ_memory, a1 + 12);
v11 = i32_load(Z_envZ_memory, a1 + 8);
for ( i = 0; i != 32; ++i )
{
v11 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * (v7 & 3)) + v7) ^ (((v3 >> 5) ^ (16 * v3)) + v3);
v7 -= 0x61C88647;
v3 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * ((v7 >> 11) & 3)) + v7) ^ (((v11 >> 5) ^ (16 * v11)) + v11);
}
i32_store(Z_envZ_memory, a1 + 8, v11);
i32_store(Z_envZ_memory, a1 + 12, v3);
v8 = 0;
v4 = i32_load(Z_envZ_memory, a1 + 20);
v12 = i32_load(Z_envZ_memory, a1 + 16);
for ( j = 0; j != 32; ++j )
{
v12 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * (v8 & 3)) + v8) ^ (((v4 >> 5) ^ (16 * v4)) + v4);
v8 -= 0x61C88647;
v4 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * ((v8 >> 11) & 3)) + v8) ^ (((v12 >> 5) ^ (16 * v12)) + v12);
}
i32_store(Z_envZ_memory, a1 + 16, v12);
i32_store(Z_envZ_memory, a1 + 20, v4);
v9 = 0;
v5 = i32_load(Z_envZ_memory, a1 + 28);
v13 = i32_load(Z_envZ_memory, a1 + 24);
for ( k = 0; k != 32; ++k )
{
v13 += ((unsigned __int64)i32_load(Z_envZ_memory, key + 4 * (v9 & 3)) + v9) ^ (((v5 >> 5) ^ (16 * v5)) + v5);
v9 -= 0x61C88647;
v5 += (v9 + (unsigned __int64)i32_load(Z_envZ_memory, key + 4 * ((v9 >> 11) & 3))) ^ (((v13 >> 5) ^ (16 * v13)) + v13);
}
i32_store(Z_envZ_memory, a1 + 24, v13);
i32_store(Z_envZ_memory, a1 + 28, v5);
v19 = ((unsigned __int8)part0 ^ 0x20) + (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 1) ^ 0x43) & 0xFF);
v20 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 2) ^ 0x6F) & 0xFF) + v19;
v21 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 3) ^ 0xFFFFFFBD) & 0xFF) + v20;
v22 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 4) ^ 0x73) & 0xFF) + v21;
v23 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 5) ^ 0xFFFFFFE9) & 0xFF) + v22;
v24 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 6) ^ 0xFFFFFFA6) & 0xFF) + v23;
v25 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 7) ^ 0xFFFFFFDC) & 0xFF) + v24;
v26 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 8) ^ 0xFFFFFFAB) & 0xFF) + v25;
v27 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 9) ^ 0x28) & 0xFF) + v26;
v28 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 10) ^ 0xFFFFFFE4) & 0xFF) + v27;
v29 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 11) ^ 0x6D) & 0xFF) + v28;
v30 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 12) ^ 0xFFFFFF97) & 0xFF) + v29;
v31 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 13) ^ 0xFFFFFFC9) & 0xFF) + v30;
v32 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 14) ^ 0x4A) & 0xFF) + v31;
v33 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 15) ^ 0xFFFFFF80) & 0xFF) + v32;
v34 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 16) ^ 0xFFFFFFF9) & 0xFF) + v33;
v35 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 17) ^ 0x46) & 0xFF) + v34;
v36 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 18) ^ 0x6F) & 0xFF) + v35;
v37 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 19) ^ 0x71) & 0xFF) + v36;
v38 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 20) ^ 0x19) & 0xFF) + v37;
v39 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 21) ^ 0xFFFFFF8D) & 0xFF) + v38;
v40 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 22) ^ 0x13) & 0xFF) + v39;
v41 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 23) ^ 0xFFFFFF98) & 0xFF) + v40;
v42 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 24) ^ 0xFFFFFFC7) & 0xFF) + v41;
v43 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 25) ^ 0x16) & 0xFF) + v42;
v44 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 26) ^ 0x25) & 0xFF) + v43;
v45 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 27) ^ 0x3C) & 0xFF) + v44;
v46 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 28) ^ 0xFFFFFF9D) & 0xFF) + v45;
v47 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 29) ^ 0x6B) & 0xFF) + v46;
v48 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 30) ^ 0xFFFFFFF2) & 0xFF) + v47;
v49 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 31) ^ 0xFFFFFFBA) & 0xFF) + v48;
v50 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 32) ^ 0x66) & 0xFF) + v49; //f
v51 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 33) ^ 0x37) & 0xFF) + v50; //7
v52 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 34) ^ 0x39) & 0xFF) + v51; //9
v53 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 35) ^ 0x30) & 0xFF) + v52; //0
v54 = (((unsigned __int64)i32_load8_s(Z_envZ_memory, a1 + 36) ^ 0x35) & 0xFF) + v53; //5
if ( v54 == -(((unsigned int)i32_load8_s(Z_envZ_memory, a1 + 37) ^ 0x7D) & 0xFF) )
f66(2524LL, key + 16); // 盲猜回显函数
else
f66(2531LL, key + 24);
f67();
g7 = key;
return (unsigned int)--wasm_rt_call_stack_depth;
}
f66 两个参数相差7,而wasm文件末尾字符串也相差7,所以定是printf
&0xFF转char
4次XTEA加密了4个64位数,但是key不知道,需要动态调试,然后进行了一堆异或,最后结果要等于ord(‘}’)^0x7D = 0,由此可以得到32字节XTEA加密结果(内存低位也是数字低位)和最后6位为f7905
动态调试得key
key = g7;
g7 += 32;
i64_store(Z_envZ_memory, key, 0LL);
i64_store(Z_envZ_memory, key + 8, 0LL);
要找最后一次i64_store(Z_envZ_memory, key + 8, 0LL);执行之后的值,发现key为0
动态验证f15的参数a1是输入存储的地址
call $func54
get_local $var1
set_global $global7
get_local $var0
call $func15
call func54后栈要么为空要么有返回值 get,set抵消 那么get_local $var0应该是参数,动态调试确实为输入存储的地址
解题脚本
#include "stdafx.h"
void encipher(unsigned int num_rounds, unsigned int v[2], unsigned int const key[4]) {
unsigned int i;
unsigned int v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
for (i = 0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
}
v[0] = v0; v[1] = v1;
}
void decipher(unsigned int num_rounds, unsigned int v[2], unsigned int const key[4]) {
unsigned int i;
unsigned int v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta*num_rounds;
for (i = 0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}
int main()
{
unsigned int v[2];
unsigned int const k[4] = { 0, 0, 0, 0 };
unsigned int r = 32;
v[0] = 0xbd6f4320, v[1] = 0xdca6e973;
decipher(r, v, k);
printf("%c%c%c%c%c%c%c%c", (v[0] & 0xff), ((v[0] >> 8) & 0xff), ((v[0] >> 0x10) & 0xff), ((v[0] >> 0x18) & 0xff), (v[1] & 0xff), ((v[1] >> 8) & 0xff), ((v[1] >> 0x10) & 0xff), ((v[1] >> 0x18) & 0xff));
v[0] = 0x6de428ab, v[1] = 0x804ac997;
decipher(r, v, k);
printf("%c%c%c%c%c%c%c%c", (v[0] & 0xff), ((v[0] >> 8) & 0xff), ((v[0] >> 0x10) & 0xff), ((v[0] >> 0x18) & 0xff), (v[1] & 0xff), ((v[1] >> 8) & 0xff), ((v[1] >> 0x10) & 0xff), ((v[1] >> 0x18) & 0xff));
v[0] = 0x716f46f9, v[1] = 0x98138d19;
decipher(r, v, k);
printf("%c%c%c%c%c%c%c%c", (v[0] & 0xff), ((v[0] >> 8) & 0xff), ((v[0] >> 0x10) & 0xff), ((v[0] >> 0x18) & 0xff), (v[1] & 0xff), ((v[1] >> 8) & 0xff), ((v[1] >> 0x10) & 0xff), ((v[1] >> 0x18) & 0xff));
v[0] = 0x3c2516c7, v[1] = 0xbaf26b9d;
decipher(r, v, k);
printf("%c%c%c%c%c%c%c%c", (v[0] & 0xff), ((v[0] >> 8) & 0xff), ((v[0] >> 0x10) & 0xff), ((v[0] >> 0x18) & 0xff), (v[1] & 0xff), ((v[1] >> 8) & 0xff), ((v[1] >> 0x10) & 0xff), ((v[1] >> 0x18) & 0xff));
printf("f7905}");
return 0;
}
//flag{5b40b27967b2ff515fdd8af65a1f7905}