qwb2019-webassembly

介绍

qwb2019的一道逆向题,第一次做wasm的题目,竟然出奇的顺利。

分析方法

  1. 利用GitHub上的wabt可以将他反编译成c源码,不过可读性较差,再次编译一下(不能编译成可执行文件,只能编译成目标文件.o),用ida查看就可以静态调试了。
  2. 在本地用python开一个http服务,chrome访问,就可以动态调试了。

分析流程

  1. ida可以看到,main函数是f16函数,其中只调用了f54和f15。
  2. 动态调试可知,f54为输入函数,f15是应该就是check函数。
  3. 分析f15,发现是用xtea加密了flag的前32位,密钥为’\x00’*16
  4. 最后的check是就是用加密的结果依此异或一个值,最后的和要为0,所以每一位都要为0,也就是说异或值就是密文。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 密钥置0
i64_store(Z_envZ_memory, key, 0LL);
i64_store(Z_envZ_memory, key + 8, 0LL);

// 第一部分加密
v38 = i32_load(Z_envZ_memory, input + 4 );
v53 = i32_load(Z_envZ_memory, input );
do
{
v53 += (((v38 >> 5) ^ 16 * v38) + v38) ^ ((unsigned __int64) i32_load(Z_envZ_memory, key + 4 * (v42 & 3)) + v42);
v42 -= 0x61C88647;
v38 += ((unsigned __int64) i32_load(Z_envZ_memory, key + 4 * ( (v42 >> 11) & 3)) + v42) ^ (((v53 >> 5) ^ 16 * v53) + v53);
++v46;
}
while ( v46 != 32 );
i32_store( Z_envZ_memory, input, v53 );
i32_store( Z_envZ_memory, input + 4, v38 );

// 最后的check
v34 = (((unsigned __int64) i32_load8_s( Z_envZ_memory, input + 34 ) ^ '1') & 0xFF) + v33;
v35 = (((unsigned __int64) i32_load8_s( Z_envZ_memory, input + 35 ) ^ 'd') & 0xFF) + v34;
v36 = (((unsigned __int64) i32_load8_s( Z_envZ_memory, input + 36 ) ^ '8') & 0xFF) + v35;
if ( v36 == -( ( (unsigned int) i32_load8_s( Z_envZ_memory, input + 37 ) ^ '}') & 0xFF) )
{...

解答方法

前32为密文用xtea解密,后6位直接用就好了,

需要注意的是python的xtea是错的,没仔细去看代码,但至少用在这里是不行的。

后记:其实是可以的,只是要在new的时候指定小端序就可以了。xtea.new('\x00'*16, endian="<")

脚本

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
#include <stdio.h>
#include <stdint.h>

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
unsigned int i;
uint32_t 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()
{
uint32_t const k[4] = {0, 0, 0, 0};
unsigned int r = 32;
uint32_t v[9] = {0x617557c1, 0x132dd474, 0x8ae20062, 0xedcb0e50, 0x6f202041, 0xbefc26d0, 0x7767ab98, 0x092d7139, 0};

for (size_t i = 0; i < 8; i += 2)
decipher(r, &v[i], k);

printf("%s741d8}\n", (char *)v);
return 0;
}