BUUCTF-crackMe1

题目链接:下载

可以知道用户名是welcomebeijing,flag是密码的MD5加密。

无壳,拖入IDA进入主函数。

int wmain()
{
  FILE *v0; // eax
  FILE *v1; // eax
  char v3; // [esp+3h] [ebp-405h]
  char v4; // [esp+4h] [ebp-404h] BYREF
  char v5[255]; // [esp+5h] [ebp-403h] BYREF
  char Format; // [esp+104h] [ebp-304h] BYREF
  char v7[255]; // [esp+105h] [ebp-303h] BYREF
  char v8; // [esp+204h] [ebp-204h] BYREF
  char v9[255]; // [esp+205h] [ebp-203h] BYREF
  char v10; // [esp+304h] [ebp-104h] BYREF
  char v11[255]; // [esp+305h] [ebp-103h] BYREF

  printf("Come one! Crack Me~~~\n");
  v10 = 0;
  memset(v11, 0, sizeof(v11));
  v8 = 0;
  memset(v9, 0, sizeof(v9));
  while ( 1 )                                   // 死循环
  {
    do
    {
      do
      {
        printf("user(6-16 letters or numbers):");// 输入用户名
        scanf("%s", &v10);
        v0 = (FILE *)sub_4024BE();
        fflush(v0);
      }
      while ( !(unsigned __int8)sub_401000(&v10) );
      printf("password(6-16 letters or numbers):");// 输入密码
      scanf("%s", &v8);
      v1 = (FILE *)sub_4024BE();
      fflush(v1);
    }
    while ( !(unsigned __int8)sub_401000(&v8) );
    sub_401090(&v10);                           // 对用户名操作
    Format = 0;
    memset(v7, 0, sizeof(v7));
    v4 = 0;
    memset(v5, 0, sizeof(v5));
    v3 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&Format, &v4);// v3恒等于1
    if ( (unsigned __int8)sub_401830(&v10, &v8) )// 关键函数
    {
      if ( v3 )
        break;                                  // 唯一的跳出循环
    }
    printf(&v4);                                // please try again
  }
  printf(&Format);                              // Congratulations
  return 0;
}

在主函数中可以看见,下图

发现是个未定义函数,跟进,发现是一个小的花指令。 红框中的全部nop掉。然后声明为函数即可。然后观察这个函数sub_4011A0,把数组里的元素变为字符:

 所以主函数中 printf(&v4) 就是输出"Please try again", printf(&Format) 就是输出"Congratulations"。即

我们想要输出Congratulaions就要跳出这个while(1)这个大循环,所以执行break,所以v3必需不为0,所以进入和v3有关的函数:

返回1,所以v3 肯定不是0,所以肯定能执行到break,跳出循环,但要求sub_4011A0()函数的返回值不是0,才能执行到break,所以跟进函数sub_4011A0这个关键函数。

bool __cdecl sub_401830(int a1, const char *a2)
{
  int v3; // [esp+18h] [ebp-22Ch]
  int v4; // [esp+1Ch] [ebp-228h]
  int v5; // [esp+28h] [ebp-21Ch]
  unsigned int v6; // [esp+30h] [ebp-214h]
  char v7; // [esp+36h] [ebp-20Eh]
  char v8; // [esp+37h] [ebp-20Dh]
  char v9; // [esp+38h] [ebp-20Ch]
  unsigned __int8 v10; // [esp+39h] [ebp-20Bh]
  unsigned __int8 v11; // [esp+3Ah] [ebp-20Ah]
  char v12; // [esp+3Bh] [ebp-209h]
  int v13; // [esp+3Ch] [ebp-208h] BYREF
  char v14; // [esp+40h] [ebp-204h] BYREF
  char v15[255]; // [esp+41h] [ebp-203h] BYREF
  char v16; // [esp+140h] [ebp-104h] BYREF
  char v17[255]; // [esp+141h] [ebp-103h] BYREF

  v4 = 0;
  v5 = 0;
  v11 = 0;
  v10 = 0;
  v16 = 0;
  memset(v17, 0, sizeof(v17));
  v14 = 0;
  memset(v15, 0, sizeof(v15));
  v9 = 0;
  v6 = 0;
  v3 = 0;
  while ( v6 < strlen(a2) )
  {
    if ( isdigit(a2[v6]) )                      // 判断是否是数字字符
    {
      v8 = a2[v6] - 48;                         // 变为纯数字
    }
    else if ( isxdigit(a2[v6]) )                // 判断是否是a-f,A-F
    {
      if ( *((_DWORD *)NtCurrentPeb()->SubSystemData + 3) != 2 )// 反调试
        a2[v6] = 34;
      v8 = (a2[v6] | 0x20) - 87;
    }
    else
    {
      v8 = ((a2[v6] | 0x20) - 97) % 6 + 10;     // 得到10-16
    }
    __rdtsc();
    __rdtsc();
    v9 = v8 + 16 * v9;                          // 得到对应一字节的十六进制所对应的十进制数
    if ( !((int)(v6 + 1) % 2) )                 // v6为奇数
    {
      *(&v14 + v3++) = v9;                      // 把这个十六进制数放入v14
      v9 = 0;
    }
    ++v6;
  }
  while ( v5 < 8 )
  {
    v10 += byte_416050[++v11];
    v12 = byte_416050[v11];
    v7 = byte_416050[v10];
    byte_416050[v10] = v12;
    byte_416050[v11] = v7;
    if ( ((int)NtCurrentPeb()->UnicodeCaseTableData & 0x70) != 0 )// 反调试
      v12 = v10 + v11;
    *(&v16 + v5) = byte_416050[(unsigned __int8)(v7 + v12)] ^ *(&v14 + v4);// 异或
    if ( (unsigned __int8)*(_DWORD *)&NtCurrentPeb()->BeingDebugged )// 反调试
    {
      v10 = -83;
      v11 = 43;
    }
    sub_401710(&v16, a1, v5++);
    v4 = v5;
    if ( v5 >= (unsigned int)(&v14 + strlen(&v14) + 1 - v15) )
      v4 = 0;
  }
  v13 = 0;
  sub_401470(&v16, &v13);
  return v13 == 43924;
}

倒着分析:前面提了为了让这个函数返回值为1,所以v13必须等于43924。和v13有关的只有上面的sub_401470()函数,跟进:

_DWORD *__usercall sub_401470@<eax>(int a1@<ebx>, _BYTE *a2, _DWORD *a3)
{
  _DWORD *_EAX; // eax
  char v5; // al
  char _AL; // al
  _DWORD *result; // eax

  if ( *a2 != 'd' )
    *a3 ^= 3u;
  else
    *a3 |= 4u;
  if ( a2[1] != 'b' )
  {
    *a3 &= 0x61u;
    _EAX = (_DWORD *)*a3;
  }
  else
  {
    _EAX = a3;
    *a3 |= 0x14u;
  }
  __asm { aam }
  if ( a2[2] != 'a' )
    *a3 &= 0xAu;
  else
    *a3 |= 0x84u;
  if ( a2[3] != 'p' )
    *a3 >>= 7;
  else
    *a3 |= 0x114u;
  if ( a2[4] != 'p' )
    *a3 *= 2;
  else
    *a3 |= 0x380u;
  if ( *((_DWORD *)NtCurrentPeb()->SubSystemData + 3) != 2 )
  {
    if ( a2[5] != 'f' )
      *a3 |= 0x21u;
    else
      *a3 |= 0x2DCu;
  }
  if ( a2[5] != 's' )
  {
    v5 = (char)a3;
    *a3 ^= 0x1ADu;
  }
  else
  {
    *a3 |= 0xA04u;
    v5 = (char)a3;
  }
  _AL = v5 - (~(a1 >> 5) - 1);
  __asm { daa }
  if ( a2[6] != 'e' )
    *a3 |= 0x4Au;
  else
    *a3 |= 0x2310u;
  if ( a2[7] != 'c' )
  {
    *a3 &= 0x3A3u;
    result = (_DWORD *)*a3;
  }
  else
  {
    result = a3;
    *a3 |= 0x8A10u;
  }
  return result;
}

发现就是if判断,根据判断结果进行相应运算,所以尝试计算一下,哪些可以等于43924。发现当if条件不成立时,v13就会等于43924,所以也知道了a2即v17=‘dbappsec’。

好了继续分析这个关键函数:

是一个while循环,a2就是用户输入的密码,先通过isdigit函数判断是否输入的是数字字符,如果是就减去'0'的ascii码使变为纯数字,如果不是则通过isxdigit函数判断是否为十六进制数a-f/A-F。(v9 = (a2[v7] | 0x20) - 87;这个我实在没搞懂) ,v9 = ((a2[v7] | 0x20) - 97) % 6 + 10;则是把非数字字符和十六进制字符的字符转化为数字10-15。

然后看一下对v10赋值的这个语句,这个是不是很像求十六进制所对应的十进制数的方法。所以通过if语句判断当组成2个十六进制数后就存入v15数组中,相当于又把v10这个十进制数变为对应的十六进制。

然后看:

 有一个异或运算,我们已经知道v17==‘dbappsec’。那知道数组byte_416050不就可以了嘛,所以用动态调试看一看参与异或运算的值是多少。找到对应异或运算的汇编代码下断点。然后运行,输入用户名和随便一个密码就可以发现

因为是个while循环,所以继续f9运行看其他参与运算的值。最终可知为[0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd],之后就是代码:

import hashlib
str="dbappsec"
flag=""
a=[0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd]
for i in range(8):
    flag+=(hex(a[i]^ord(str[i]))[2:])
print(flag)
m=hashlib.md5()
flag=flag.encode('utf-8')
m.update(flag)
print(m.hexdigest())

#密码:4eb5f3992391a1ae
#md5后:d2be2981b84f2a905669995873d6a36c

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值