一道比较复杂的算法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()