reverse之第十更

一道比较复杂的算法Crack,主要考查CRC校验算法,AES加密,以及base64加密算法。

程序运行之后发现提示内容是Try angin!但是搜索字符串并没有,但是看到一个很像base64的字符串,过去发现各种算法以及调用,在函数开头下断,果然加密算法都在这个地方。

在IDA中分析该函数

char __thiscall sub_401880(int this)
{
  int v1; // ebx@1
  char v2; // al@1
  char v3; // dh@1
  char v4; // ch@1
  char v5; // ah@1
  char v6; // dl@1
  char v7; // cl@1
  unsigned int v8; // esi@7
  unsigned int v9; // eax@7
  char *v10; // ecx@7
  char result; // al@8
  unsigned int v12; // esi@10
  wchar_t *v13; // edi@11
  unsigned int v14; // ecx@13
  unsigned int v15; // kr18_4@13
  unsigned int v16; // kr1C_4@14
  CHAR *v17; // ecx@18
  char v18; // [sp+4h] [bp-1F8h]@1
  char v19; // [sp+5h] [bp-1F7h]@1
  char v20; // [sp+6h] [bp-1F6h]@1
  char v21; // [sp+7h] [bp-1F5h]@1
  char v22; // [sp+8h] [bp-1F4h]@1
  char v23; // [sp+9h] [bp-1F3h]@1
  char v24; // [sp+Ah] [bp-1F2h]@1
  char v25; // [sp+Bh] [bp-1F1h]@1
  char v26[200]; // [sp+Ch] [bp-1F0h]@13
  char v27[132]; // [sp+D4h] [bp-128h]@10
  __int128 v28; // [sp+158h] [bp-A4h]@13
  __int128 v29; // [sp+168h] [bp-94h]@13
  __int64 v30; // [sp+178h] [bp-84h]@13
  int v31; // [sp+180h] [bp-7Ch]@13
  char v32; // [sp+184h] [bp-78h]@13
  char Dst; // [sp+185h] [bp-77h]@13
  CHAR v34; // [sp+1BCh] [bp-40h]@7
  int v35; // [sp+1D0h] [bp-2Ch]@10
  int v36; // [sp+1D4h] [bp-28h]@10
  int v37; // [sp+1D8h] [bp-24h]@10
  int v38; // [sp+1DCh] [bp-20h]@10
  CHAR v39[4]; // [sp+1E0h] [bp-1Ch]@1
  int v40; // [sp+1E4h] [bp-18h]@1
  __int16 v41; // [sp+1E8h] [bp-14h]@1
  CHAR MultiByteStr[4]; // [sp+1ECh] [bp-10h]@1
  int v43; // [sp+1F0h] [bp-Ch]@1
  int v44; // [sp+1F4h] [bp-8h]@1

  v1 = this;
  *(_DWORD *)v39 = 0x20756F59;
  v40 = '!niW';
  v41 = '\0';
  *(_DWORD *)MultiByteStr = ' yrT';
  v2 = *(_BYTE *)this;
  v3 = *(_BYTE *)(this + 1);
  v4 = *(_BYTE *)(this + 2);
  v5 = *(_BYTE *)(v1 + 3);
  v6 = *(_BYTE *)(v1 + 4);
  v7 = *(_BYTE *)(v1 + 5);
  v18 = v2;
  v43 = 'iaga';
  v44 = '!n';
  v19 = v3;
  v20 = v4;
  v21 = 0;
  v22 = v5;
  v23 = v6;
  v24 = v7;
  v25 = 0;
  if ( (unsigned __int8)(v2 - 65) > 0x19u
    || (unsigned __int8)(v3 - 65) > 0x19u
    || (unsigned __int8)(v4 - 65) > 0x19u
    || (unsigned __int8)(v5 - 65) > 0x19u
    || (unsigned __int8)(v6 - 65) > 0x19u
    || (unsigned __int8)(v7 - 65) > 0x19u )
  {
    result = sub_4016D0(MultiByteStr);
  }
  else
  {
    v8 = CRC((int)&v18, strlen(&v18));
    v9 = CRC((int)&v22, strlen(&v22));
    sub_401490(&WideCharStr, 30, L"%08x%08x", v8, v9);
    sub_401840(&WideCharStr, &v34);
    sub_4016D0(MultiByteStr);
    v10 = (char *)v1;
    do
      result = *v10++;
    while ( result );
    if ( !(((_BYTE)v10 - ((_BYTE)v1 + 1)) & 0xF) )
    {
      v35 = 0x1020304;
      v36 = 0x7080900;
      v37 = 0xE0F0506;
      v38 = 0xA0B0C0D;
      sub_401380((_BYTE *)v1, (int)v27, strlen((const char *)v1), (int)&v34, (int)&v35);
      v12 = 0;
      if ( strlen((const char *)v1) )
      {
        v13 = &WideCharStr;
        do
        {
          sub_401490(v13, 3, L"%02x", (unsigned __int8)v27[v12++]);
          v13 += 2;
        }
        while ( v12 < strlen((const char *)v1) );
      }
      sub_401840(&WideCharStr, v27);
      sub_401570(strlen(v27), (int)v27, v26);
      v31 = '=X7V';
      _mm_storeu_si128((__m128i *)&v28, _mm_loadu_si128((const __m128i *)&xmmword_4034F4));
      v32 = 0;
      _mm_storeu_si128((__m128i *)&v29, _mm_loadu_si128((const __m128i *)&xmmword_403504));
      _mm_storel_epi64((__m128i *)&v30, _mm_loadl_epi64((const __m128i *)&qword_403514));
      memset(&Dst, 0, 0x37u);
      v14 = 0;
      v15 = strlen((const char *)&v28);
      if ( v15 )
      {
        v16 = strlen(v26);
        while ( v14 < v16 )
        {
          if ( *((_BYTE *)&v28 + v14) != v26[v14] )
          {
            v17 = MultiByteStr;
            goto LABEL_19;
          }
          if ( ++v14 >= v15 )
            break;
        }
      }
      v17 = v39;
LABEL_19:
      result = sub_4016D0(v17);
    }
  }
  return result;
}

首先发现,成功和失败的提示被定义为字符串保存在函数中,程序运行之后才会出现。程序的大致流程是先将输入的前六位取出,判断是不是大写字母,如果不是就失败。

之后将前六位分成两组,求其CRC32的校验码,CRC的算法如下:

uint crc32( uchar *buf, int len)
{
    uint ret = 0xFFFFFFFF;
    int   i;
    if( !init )
    {
         init_table();
         init = 1;
    }
    for(i = 0; i < len;i++)
    {
         ret = CRC32[((ret & 0xFF) ^ buf[i])] ^ (ret >> 8);
    }
     ret = ~ret;
    return ret;
}

CRC算法求解关键在于CRC表的生成,一般都是动态生成的,也可以静态定义,不过很麻烦。第一次求CRC初始的ret是-1,之后的ret就是上一次所求的CRC,以此原理将两部分的CRC转换为8位的16进制连接起来作为下面AES加密的密钥。

加密之后的结果转换为16进制字符串,进行base64编码,与程序给出的XQXgWk5yLADiXx6hVQCAKxTjVQXiWxPhXASCKALhV7X=比较,base64转换过程中的置换表与平常的有些区别

0987654321ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba+/

最终求正确的注册码必须要枚举密钥,反解AES然后比较解密结果的前六位与密钥是否一致,脚本如下:

# _*_ coding:utf-8 _*_

from Crypto.Cipher import AES
import string
from zlib import crc32

def crack():
    atable='0987654321ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba+/'
    btable='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    base='XQXgWk5yLADiXx6hVQCAKxTjVQXiWxPhXASCKALhV7X='
    transtab=string.maketrans(atable,btable)
    iv='\x04\x03\x02\x01\x00\x09\x08\x07\x06\x05\x0f\x0e\x0d\x0c\x0b\x0a'
    table=list(string.uppercase)
    c=base.translate(transtab).decode('base64').decode('hex')
    count=0
    for i1 in table:
        for i2 in table:
            for i3 in table:
                for i4 in table:
                    for i5 in table:
                        for i6 in table:
                            key='%08x'%(crc32(i1+i2+i3)&0xffffffff)+'%08x'%(crc32(i4+i5+i6)&0xffffffff)
                            crypto=AES.new(key,AES.MODE_CBC,iv)
                            m=crypto.decrypt(c)
                            count+=1
                            if count%5000000==0:
                                print count
                            if m[:6]==i1+i2+i3+i4+i5+i6:
                                print 'flag:'+m
                                return
if __name__=='__main__':
    crack()
有钱的捧个钱场
0%