1 描述
防守方在发布CrackMe时,应向大众公开一组用户名和序列号,即 “ Name/Serial ” 。根据这组公开的用户名/序列号,得到密钥,然后再计算出本平台指定用户名“KCTF”得到的序列号即可成功。
判胜条件 1)若攻击方找出特定用户名(“KCTF”,不含引号)的序列号,经KCTF系统自动确认,将认定攻击方获胜;
新玩法,win32逆向
2 解题过程
首先通过运行程序,找到字符串
"【请输入您的用户名与序列号】\n"
然后找到了主函数,sub_8719D0
2.1 sub_8719D0
int sub_8719D0()
{
unsigned int v0; // kr00_4
signed int v1; // esi
unsigned __int8 v2; // cl
char v3; // dl
signed int v4; // esi
__int128 *v5; // edi
int v6; // eax
unsigned int v7; // ecx
int v8; // eax
unsigned int v9; // esi
bool v10; // cf
char *v11; // ecx
unsigned int v12; // edx
__int8 v13; // al
char *v14; // ecx
unsigned int v15; // edx
char v16; // al
__int128 *v17; // ecx
_DWORD *v18; // edx
unsigned int v19; // esi
void *v20; // esi
unsigned int v22; // [esp+8h] [ebp-E8h]
int v23; // [esp+Ch] [ebp-E4h]
int v24; // [esp+10h] [ebp-E0h]
int v25; // [esp+14h] [ebp-DCh]
int v26; // [esp+18h] [ebp-D8h]
int v27; // [esp+1Ch] [ebp-D4h]
__m128i v28; // [esp+60h] [ebp-90h]
__int128 v29; // [esp+70h] [ebp-80h]
__int128 sn; // [esp+80h] [ebp-70h]
__int128 v31; // [esp+90h] [ebp-60h]
char v32; // [esp+A0h] [ebp-50h]
__int128 v33; // [esp+B0h] [ebp-40h]
__int128 v34; // [esp+C0h] [ebp-30h]
char v35; // [esp+D0h] [ebp-20h]
printf((int)"【请输入您的用户名与序列号】\n");
printf((int)&unk_88E748);
scanf("%16s", &name, 17); // 输入用户名
v0 = strlen((const char *)&name);
sub_8729C0((__m128i *)((char *)&name + v0), 0, 16 - v0);// 取前4位,并将后面的所有值补0
printf((int)&unk_88E760);
v32 = 0;
sn = 0i64;
v31 = 0i64;
v33 = 0i64;
v34 = 0i64;
v35 = 0;
xmmword_8912F8 = 0i64;
scanf("%33s", &sn, 33);
v1 = 0;
do
{
v2 = *((_BYTE *)&sn + 2 * v1);
v3 = v2 - '7';
if ( v2 <= '9' )
v3 = *((_BYTE *)&sn + 2 * v1);
*((_BYTE *)&xmmword_8912F8 + v1) = *((_BYTE *)&sn + 2 * v1 + 1)
+ 16 * v3
- (*((_BYTE *)&sn + 2 * v1 + 1) > (unsigned __int8)'9' ? '7' : '0');
++v1;
}
while ( v1 < 16 );
v4 = 0;
v5 = &v33;
do
{
sub_871990((int)v5, "%02X", *((unsigned __int8 *)&xmmword_8912F8 + v4++));
v5 = (__int128 *)((char *)v5 + 2);
}
while ( v4 < 16 ); // 加载到内存中
v6 = strcmp((const char *)&sn, (const char *)&v33);// 比较16字节数据是否相同,相同返回0
if ( v6 )
v6 = -(v6 < 0) | 1;
if ( v6 )
{
printf((int)" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
printf((int)&unk_88E770); // 请输入合法序列号
}
else
{
v22 = 0;
v23 = 0;
v24 = 0x67452301;
v25 = 0xEFCDAB89;
v28 = _mm_xor_si128((__m128i)name, (__m128i)xmmword_8912F8);// 异或结果为:其实我更喜欢孙坚
v26 = 0x98BADCFE;
v27 = 0x10325476;
v29 = 0i64;
sub_871000((int)&v28, &v22, 0x10u); // md5
v7 = (v22 >> 3) & 0x3F; // v22=0x80
v8 = 'x' - v7; // v7=0x10
v9 = '8' - v7;
v10 = v7 < 0x38;
v11 = (char *)&v22 + 1;
if ( !v10 )
v9 = v8;
v12 = 0;
do
{
v13 = *(v11 - 1);
v11 += 4;
v28.m128i_i8[v12] = v13;
v28.m128i_i8[v12 + 1] = *(v11 - 4);
v28.m128i_i8[v12 + 2] = *(v11 - 3);
v28.m128i_i8[v12 + 3] = *(v11 - 2);
v12 += 4;
}
while ( v12 < 8 ); // 将v28第一个字节改为0x80,前几个置0,后8个不变
sub_871000((int)&unk_8908C0, &v22, v9);
sub_871000((int)&v28, &v22, 8u);
v14 = (char *)&v24 + 1;
v15 = 0;
do
{
v16 = *(v14 - 1);
v14 += 4;
*((_BYTE *)&v29 + v15) = v16;
*((_BYTE *)&v29 + v15 + 1) = *(v14 - 4);
*((_BYTE *)&v29 + v15 + 2) = *(v14 - 3);
*((_BYTE *)&v29 + v15 + 3) = *(v14 - 2);
v15 += 4;
}
while ( v15 < 0x10 ); // V14的值放到v29
v17 = &v29;
v18 = &unk_8908B0;
v19 = 12;
while ( *(_DWORD *)v17 == *v18 ) // 主要验证逻辑
{
v17 = (__int128 *)((char *)v17 + 4);
++v18;
v10 = v19 < 4;
v19 -= 4;
if ( v10 )
{
v20 = &unk_88E700; // 正确提示
goto LABEL_22;
}
}
v20 = &unk_88E714; // 错误提示
LABEL_22:
printf((int)" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n");
printf((int)v20);
}
sub_87507E("\npause\n");
return 0;
}
##
2.2 程序的主要逻辑
- 输入用户名,长度不够16字节,用0x00补齐
- 输入序列号,转化为16进制,然后存放到内存
- 然后输入序列号与转化为十六进制后再编码,进行比较,也就是一个自检,输出的结果放在v6中。根据v6的值判断输入的序列号是不是合法
- 然后存入几个幻数,幻数一看就是md5,然后可以点进去看一些位运算特征,进一步确定(不是关键)
- md5算法的输入参数是:幻数+异或结果v28,也就是用户名(name)异或序列号(xmmword_8912F8)的结果。
- 然后就是关键的比较v17 == *v18,也就是判断由异或结果计算的哈希,是不是和原来的哈希:DA E5 23 10 06 71 95 71 4B A2 CE E2 33 2B B8 66,相等.
3 破解思想
根据规则,给定用户名和序列号正确的异或结果是:C6 E4 CA B5 CE D2 B8 FC CF B2 BB B6 CB EF BC E1‘其实我更喜欢孙坚’。
需要构造的用户名是:KCTF,所以两者异或,即可解题,脚本如下:
sn=[0xC6,0xE4,0xCA,0xB5,0xCE,0xD2,0xB8,0xFC,0xCF,0xB2,0xBB,0xB6,0xCB,0xEF,0xBC,0xE1]
name=[0x4b,0x43,0x54,0x46]
flag=[]
for i in range(4):
flag.append(sn[i]^name[i])
for i in range(4,len(sn)):
flag.append(sn[i]^0x00)
print flag
flag1=''
for i in flag:
flag1+=chr(i).encode('hex')
print flag1.upper()