强网杯2019逆向 lebel:WASM,XTEA

参考:
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
zZG4ubmV0L3ExdVRydXRo,size_16,color_FFFFFF,t_70)

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}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

q1uTruth

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值