前言
摸pizza大佬!
-。-做这两题好想哭-。-
我好菜丫,大佬直接看伪汇编写脚本。我呢,脚本都快看不懂。
wasm
wasm虐我千百遍我却待她如初恋。-。
之前遇到过类似的,但是没有仔细的研究,只知道夜影师傅的blog有写过类似的文章,但是没写全。
首先是用wabt
项目将wasm
转化为c
代码,虽然效果感人,但至少比wat
要强一点。
一直想要调试它,但我真的好菜,网上找了好多demo
不过报了好多奇奇怪怪的错误,夜影师傅都被我气哭了-。-
所以我是真的不会,只是收集了一下相关的例题和脚本,以及一些套路。
0x1.easywasm
全场只有pizza
大佬一个人做出来了,ORZ!
菜鸡的我,虽然也看到了32组md5还有23333333...
,但是然并软。
伪c
代码太多了,我稍微整理了下。。其实我感觉找关键的api
能把大致的过程理出来,但是这具体的加密过程实在是看不出来丫。
i0 = _strlen(i0); //获取长度
i1 = 32u;
i0 = i0 == i1;
if (i0) { //判断是否为32
L2:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //取出输入的一个字节
i32_store8(Z_envZ_memory, (u64)(i0), i1); //store 到哪里去呢 Z_envZ_memory是之前初始化的变量
_md5(i0, i1, i2);
L3:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //中间做了一些变化,
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i32_store8(Z_envZ_memory, (u64)(i0), i1);
if (i0) {
goto L3;}
_md5(i0, i1, i2); //再次md5加密
L4:
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i32_store8(Z_envZ_memory, (u64)(i0), i1);
if (i0) {
goto L4;}
i1 = (*Z_envZ_memoryBaseZ_i); //取出固定数据 进行比较
i0 = _memcmp(i0, i1, i2);
if (i0) {
i0 = 0u;
p0 = i0;
goto B0;
}
i1 = 32u; //循环部分 循环32次
i0 = i0 < i1;
if (i0) {
goto L2;}
i0 = 1u;
} else {
i0 = 0u;
}
B0:;
FUNC_EPILOGUE;
return i0;
大概就是个这么个东西吧。所以代码如下(基本上借用pizza师傅,莫怪,实在太菜-。-):
from hashlib import md5
a=['562fe3cc50014c260d9e8cf4ed38c77a',
'c022ad0cc0075a9ab14b412a1082d5f3',
'64c286cfc623aa8d7df7c088ebf7d718',
'83664bdee4b613b7e7a51b5213470a8d',
'b020bf598aaa2b3e03ed02c85436268a',
'4fdac5ac807506938103e775c50099ed',
'4fdac5ac807506938103e775c50099ed',
'c231d607b6823fd0a68e813760809754',
'd168c21d10371a5ab61bcfe6c759ef6e',
'f60d709ccf989d849028f97a03d2f3ba',
'a0184f8240e2fe46861dc8d15a819cb0',
'9dbec414336e741e9c73422df59de297',
'6fb5209d8fc8bb8507245bcfa24ae11f',
'6fb5209d8fc8bb8507245bcfa24ae11f',
'00c77fbc60a5bfc466d3d069876ec348',
'00c77fbc60a5bfc466d3d069876ec348',
'df33464fb471c46abaf691c000a0e30d',
'4fdac5ac807506938103e775c50099ed',
'f60d709ccf989d849028f97a03d2f3ba',
'fcc94a20596f2619868f3a4bf52eadf7',
'00c77fbc60a5bfc466d3d069876ec348',
'd168c21d10371a5ab61bcfe6c759ef6e',
'9dbec414336e741e9c73422df59de297',
'fcc94a20596f2619868f3a4bf52eadf7',
'9b37db091979bedf00a7095851ba6f59',
'00c77fbc60a5bfc466d3d069876ec348',
'f60d709ccf989d849028f97a03d2f3ba',
'fcc94a20596f2619868f3a4bf52eadf7',
'd168c21d10371a5ab61bcfe6c759ef6e',
'f60d709ccf989d849028f97a03d2f3ba',
'183342997ffed4b3189e977d077a60b4',
'f404a3368d2d8f57464f739d4ed01c0e']
flag = ""
for i in xrange(0, 32):
ci = 0
for cc in xrange(0x20, 0x7F):
ch = chr(cc)
x = md5("2333333333333333333333333333333" + ch).hexdigest()
x = md5(x).hexdigest()
if(x == a[i]):
ci = cc
break
assert(ci != 0)
flag += chr(ci)
print(flag)
0x2.wasm_3(sces60107)
之前的练习题,后来才发现这题原来是*ctf
的题目。
同样的先转化成伪c
代码,感觉是一个套路撒,又是一个check
函数,最后又是一段数据。-。-
static u32 _check(u32 p0) {
FUNC_PROLOGUE;
if (i0) {
i0 = 160u;
(*Z_envZ_abortStackOverflowZ_vi)(i0);
}
i0 = (*Z_envZ__strlenZ_ii)(i0); //获取长度
i1 = 24u;
i0 = i0 != i1;
if (i0) {
goto Bfunc;
}
i0 = (*Z_envZ_memoryBaseZ_i);
L2:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //取出一个字节
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i0 = (u32)((s32)i0 < (s32)i1); //这里有个判断。。不太清楚什么用处
if (i0) {
goto L2;}
_EncryptCBC(i0, i1, i2, i3); //进行_EncryptCBC 加密
i0 = (*Z_envZ_memoryBaseZ_i); //后面一堆没怎么看懂。。太菜了我-。-
L3:
i1 = i32_load(Z_envZ_memory, (u64)(i1));
i32_store(Z_envZ_memory, (u64)(i0), i1);
if (i0) {
goto L3;}
i0 = 0u;
L4:
if (i0) {
goto B5;
}
L7:
i0 = !(i0);
if (i0) {
goto B8;
}
i0 = i32_load8_s(Z_envZ_memory, (u64)(i0));
i0 = i32_load(Z_envZ_memory, (u64)(i0));
i0 = i0 != i1;
if (i0) {
i0 = 8u;
goto B5;
}
goto L7;
B8:;
i1 = 1u;
goto L4;
B5:;
i1 = 8u;
i0 = i0 == i1;
if (i0) {
i0 = 0u;
goto Bfunc;
} else {
i0 = i0 == i1;
if (i0) {
goto Bfunc;
}
}
i0 = 0u;
goto Bfunc;
Bfunc:;
FUNC_EPILOGUE;
return i0;
}
基本上的流程还是差不多。接下来看一下算法。
EncryptCBC(u32 p0, u32 p1, u32 p2, u32 p3)
其中还调用了f9
,f10
,f11
,然后在f10
这个函数中发现可疑的数据2654435769u==0x9e3779b9
,百度下,提示是tea
算法,于是找了找tea
算法的c
实现,对比一下基本上可以确定EncryptCBC
就是tea
算法,而且tea
是可逆的,这时我们结合着算法再来看一遍check
过程,应该会比较清晰。
cipher
是那一串数据,key
应该就是webasmintersting
,每四个对应一个32位无符号数。由于flag长度为24,每一轮加密需要2个32位数,因此总共加密了三轮,解密时注意小端序!!
但是我在写解密脚本时,怎么都不对-。-难道不是标准的tea
算法???所以我又仔细的瞅了瞅f10
函数,还真的不是标准的tea
算法。。
i1 = 3u;
i0 <<= (i1 & 31);
i0 ^= i1;
i0 ^= i1;
i1 = 5u;
i0 >>= (i1 & 31);
i0 ^= i1;
v0 += ((v1<<3) ^ k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<3) ^ k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
修改脚本如下:
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<3) ^ k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<3) ^ k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
void getflag(uint32_t v[2]){
unsigned int i=0;
for(i=0;i<4;i++){
printf("%c", (v[0]>>(8*i)&0xff));
}
for(i=0;i<4</