XMAN【我真的好菜-同pizza师傅修炼笔记一】easyvm&easywasm

前言

摸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;i++){
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值