01--AFCTF-re3(MD5)

解题过程

先脱壳,然后拖进64位IDA,定位到主函数F5查看伪C代码,代码如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax
  unsigned __int64 v4; // kr08_8
  char *v5; // ST18_8
  char *v6; // ST20_8
  char *v7; // ST28_8
  char *v8; // ST30_8
  bool v9; // cf
  bool v10; // zf
  signed __int64 v11; // rcx
  char *v12; // rsi
  const char *v13; // rdi
  signed int i; // [rsp+38h] [rbp-88h]
  char v15[48]; // [rsp+40h] [rbp-80h]
  char v16; // [rsp+70h] [rbp-50h]
  __int16 v17; // [rsp+76h] [rbp-4Ah]
  char dest[40]; // [rsp+90h] [rbp-30h]
  unsigned __int64 v19; // [rsp+B8h] [rbp-8h]

  v19 = __readfsqword(0x28u);
  puts("Please input flag:");
  __isoc99_scanf("%s", &v16);
  if ( (unsigned int)sub_401773(&v16) )
  {
    puts("Format Error !!");
    result = 0LL;
  }
  else
  {
    v4 = strlen(&v16) + 1;
    strncpy(dest, (const char *)&v17, (signed int)v4 - 8);
    dest[(signed int)v4 - 8] = 0;
    v5 = strtok(dest, "_");
    v6 = strtok(0LL, "_");
    v7 = strtok(0LL, "_");
    v8 = strtok(0LL, "_");
    sub_4016DC(v5, 0);
    sub_4016DC(v6, 4);
    sub_4016DC(v7, 8);
    sub_4016DC(v8, 12);
    for ( i = 0; ; ++i )
    {
      v9 = (unsigned int)i < 0xF;
      v10 = i == 15;
      if ( i > 15 )
        break;
      sprintf(&v15[2 * i], "%02x", (unsigned __int8)digest[i]);
    }
    v11 = 33LL;
    v12 = v15;
    v13 = "f58e6a50d0dbe91515913c1002c77002";
    do
    {
      if ( !v11 )
        break;
      v9 = (unsigned __int8)*v12 < *v13;
      v10 = *v12++ == *v13++;
      --v11;
    }
    while ( v10 );
    if ( (!v9 && !v10) == v9 )                  // v9=0;v10=1
      puts("Success!! You are right !!");
    else
      puts("You are wrong !!");
    result = 0LL;
  }
  return result;
}

主函数先通过sub_401773函数判断输入的flag是否符合格式,这里的格式是指开头为“afctf{”,结尾为“}”,afctf{……}中被三个“_”分成四段,每一段长度为4且所有字符都是小写英文字母。
sub_4016DC函数对四段字符串进行某操作,然后将digest[i]中的数据与串”f58e6a50d0dbe91515913c1002c77002”进行对比,如果相等则输出”Success!! You are right !!”。而主函数中调用的四个sub_4016DC函数的参数不同,下面我们跟进这个函数,发现这个函数内容与MD5相似,按照网上MD5代码修改函数名称。

unsigned __int64 __fastcall sub_4016DC(const char *a1, int a2)
{
  char v3; // [rsp+20h] [rbp-60h]
  unsigned __int64 v4; // [rsp+78h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  MD5Init(&v3);                                 // 初始化缓冲区
  MD5Update(&v3, (char *)a1, strlen(a1));
  Md5Final(&v3, (__int64)&digest[a2]);
  return __readfsqword(0x28u) ^ v4;
}

MD5Init函数和MD5Update函数与MD5代码相同,传进来的四个不同的参数a2在Md5Final函数中用到,继续跟进:

unsigned __int64 __fastcall sub_4008E4(_DWORD *a1, __int64 a2)
{
  int v2; // eax
  unsigned int v3; // ST1C_4
  unsigned int v5; // [rsp+18h] [rbp-18h]
  char v6; // [rsp+20h] [rbp-10h]
  unsigned __int64 v7; // [rsp+28h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  v5 = (*a1 >> 3) & 0x3F;
  if ( v5 > 0x37 )
    v2 = 120 - v5;
  else
    v2 = 56 - v5;
  v3 = v2;
  Md5Encode((__int64)&v6, (__int64)a1, 8u);
  MD5Update(a1, (char *)&unk_603060, v3);
  MD5Update(a1, &v6, 8u);
  Md5Encode(a2, (__int64)(a1 + 2), 4u);
  return __readfsqword(0x28u) ^ v7;
}

发现这个函数在Md5Encode(a2, (_int64)(a1 + 2), 4u);与MD5不同,相当于是通过MD5Encode计算的值只有前四个传回给了digest[i]。总的来说就是进行四次MD5计算,将每次得到的消息值得前四个字节依次传给digest数组去进行后面的匹配,而每一次传进去计算的字符串就是我们之前通过“”分割出来的四个字符。
由于在进行flag格式判断的时候就限定了这四段字符均为小写英文字母,所以可以尝试爆破,代码如下:

import hashlib
import sys

for a in range(0x61, 0x7b):
    for b in range(0x61, 0x7b):
        for c in range(0x61, 0x7b):
            for d in range(0x61, 0x7b):
                m = hashlib.md5()
                m.update((chr(a) + chr(b) + chr(c) + chr(d)).encode("utf-8"))
                if m.hexdigest()[0:8] == "f58e6a50":
                    print(chr(a) + chr(b) + chr(c) + chr(d))
                if m.hexdigest()[0:8] == "d0dbe915":
                    print(chr(a) + chr(b) + chr(c) + chr(d))
                if m.hexdigest()[0:8] == "15913c10":
                    print(chr(a) + chr(b) + chr(c) + chr(d))
                if m.hexdigest()[0:8] == "02c77002":
                    print(chr(a) + chr(b) + chr(c) + chr(d))

运行结果为:

dead
live
oops
zoom

得出本题flag为:afct{dead_live_oops_zoom}

总结

从这个题目可以看出对一些基本密码算法还不太熟悉,在阅读伪C代码的过程中花费了很多功夫。
循环左移运算:x循环左移n位可以表示为 (x<<n)or(x>>Ln) ( x << n ) o r ( x >> L − n )

MD5算法及其实现
SHA1算法实现及详解
SHA-256算法实现
DES和3DES加密算法C语言实现
AES算法实现分析
RC4加密算法的原理及实现
IDEA加密算法实现

你可能感兴趣的:(Reverse)