BUUCTF-CrackMe略解

先总体看看

int __usercall wmain@<eax>(int a1@<ebx>)
{
     
  FILE *v1; // eax
  FILE *v2; // eax
  char v4; // [esp+3h] [ebp-405h]
  char v5; // [esp+4h] [ebp-404h]
  char v6; // [esp+5h] [ebp-403h]
  char v7; // [esp+104h] [ebp-304h]
  char v8; // [esp+105h] [ebp-303h]
  char v9; // [esp+204h] [ebp-204h]
  char v10; // [esp+205h] [ebp-203h]
  char v11; // [esp+304h] [ebp-104h]
  char v12; // [esp+305h] [ebp-103h]

  printf("Come one! Crack Me~~~\n");
  v11 = 0;
  memset(&v12, 0, 0xFFu);
  v9 = 0;
  memset(&v10, 0, 0xFFu);
  while ( 1 )
  {
     
    do
    {
     
      do
      {
     
        printf("user(6-16 letters or numbers):");// 输入用户名
        scanf("%s", &v11);
        v1 = (FILE *)sub_4024BE();
        fflush(v1);
      }
      while ( !sub_401000(&v11) );              // 检查用户名合法性
      printf("password(6-16 letters or numbers):");
      scanf("%s", &v9);                         // 输入密码
      v2 = (FILE *)sub_4024BE();
      fflush(v2);
    }
    while ( !sub_401000(&v9) );                 // 检查密码合法性
    sub_401090(&v11);                           // 处理byte数组
    v7 = 0;
    memset(&v8, 0, 0xFFu);
    v5 = 0;
    memset(&v6, 0, 0xFFu);
    v4 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&v7, &v5);
    if ( sub_401830(a1, (int)&v11, &v9) )       // 处理用户名和密码
    {
     
      if ( v4 )
        break;
    }
    printf(&v5);
  }
  printf(&v7);
  return 0;
}

ida伪代码关注变量类型

进入sub_401090

char *__cdecl sub_401090(char *a1)
{
     
  char *result; // eax
  char v2; // ST1F_1
  char v3; // ST22_1
  char v4; // ST20_1
  signed int v5; // [esp+Ch] [ebp-18h]
  char *v6; // [esp+10h] [ebp-14h]
  char *v7; // [esp+14h] [ebp-10h]
  signed int i; // [esp+18h] [ebp-Ch]
  unsigned __int8 v9; // [esp+23h] [ebp-1h]

  for ( i = 0; i < 256; ++i )
    byte_416050[i] = i;
  v5 = 0;
  v9 = 0;
  v6 = 0;
  result = a1;
  v7 = a1;
  do
  {
     
    LOBYTE(result) = *v7;
    v2 = *v7++;
  }
  while ( v2 );
  while ( v5 < 256 )
  {
     
    v3 = byte_416050[v5];
    v9 += v3 + a1[(_DWORD)v6];
    v4 = byte_416050[v9];
    ++v6;
    byte_416050[v9] = v3;
    byte_416050[v5] = v4;
    result = v6;
    if ( (signed int)v6 >= v7 - (a1 + 1) )
      v6 = 0;
    ++v5;
  }
  return result;
}

可以看到用户名a1没有被更改,改变的是byte数组
其中

 v9 += v3 + a1[(_DWORD)v6];
    v4 = byte_416050[v9];

非常不合理,因为随着v9在增大,一定会大于255,会造成数组越界,其实v9是个8位数,不会大于255,会循环下去

unsigned __int8 v9; // [esp+23h] [ebp-1h]

那打扰了。。。

反调试

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

  v5 = 0;
  v6 = 0;
  v12 = 0;
  v11 = 0;
  v17 = 0;
  memset(&v18, 0, 0xFFu);
  v15 = 0;
  memset(&v16, 0, 0xFFu);
  v10 = 0;
  v7 = 0;
  v4 = 0;
  while ( v7 < strlen(a2) )
  {
     
    if ( isdigit(a2[v7]) )
    {
     
      v9 = a2[v7] - 48;                         // 取原始数字
    }
    else if ( isxdigit(a2[v7]) )
    {
     
      if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
        a2[v7] = 34;
      v9 = (a2[v7] | 0x20) - 87;                // 十六进制保留原始含义
    }
    else
    {
     
      v9 = ((a2[v7] | 0x20) - 97) % 6 + 10;     // 其他小写处理后,%6 + 10
    }
    v10 = v9 + 16 * v10;
    if ( !((signed int)(v7 + 1) % 2) )          // v7为奇数结算一次
    {
     
      *(&v15 + v4++) = v10;
      ebx0 = v4;
      v10 = 0;
    }
    ++v7;                                       // 总得来说,就是 
                                                // 前一位*16 + 后一位 = v15 的一位
                                                // 换言之,就是v15 = 两个十六进制组合起来的值
  }
  while ( v6 < 8 )
  {
     
    v11 += byte_416050[++v12];
    v13 = byte_416050[v12];
    v8 = byte_416050[v11];
    byte_416050[v11] = v13;
    byte_416050[v12] = v8;
    if ( *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70 )// 反调试
      v13 = v11 + v12;
    *(&v17 + v6) = byte_416050[(unsigned __int8)(v8 + v13)] ^ *(&v15 + v5);// 处理v17
    if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )// 反调试
    {
     
      v11 = -83;
      v12 = 43;
    }
    sub_401710((int)&v17, (const char *)a1, v6++);// 反调试
    v5 = v6;
    if ( v6 >= (unsigned int)(&v15 + strlen(&v15) + 1 - &v16) )
      v5 = 0;
  }
  v14 = 0;
  sub_401470(ebx0, &v17, &v14);                 // 用v17处理v14
  												//这个函数说明v17 = dbappsec
  return v14 == 43924;
}

其中__readfsdword(0x30u)是反调试
sub_401710()里的GetStartupInfoW(…)也是反调试
更多有关反调试的知识请看 ctfwiki
或者https://www.cnblogs.com/Crisczy/p/7575521.html

动态调试

题目给了我们用户名welcomebeijing
说明byte数组是固定的,进一步说明处理v17的那行代码中
byte_416050[(unsigned __int8)(v8 + v13)]也是固定的
而v17我们有了,即可反推
我们不必自己计算这些值,直接让程序输入用户名跑起来即可得到
所以动态调试,注意绕过反调试
BUUCTF-CrackMe略解_第1张图片
之后上python

box = [0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd]
a =[100,98,97,112,112,115,101,99]
for i in range(8):
    print(hex(a[i]^box[i])[2:],end = '')

得出结果md5加密即可

你可能感兴趣的:(BUUCTF-CrackMe略解)