本题是考密码学,用到了AES,RSA,SHA512,MD5,不过算法还是很简单的,只要RSA的大数分解能完成,就很容易做出来,分析如下:
IDA中F5,主函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
time_t v3; // eax
unsigned int v4; // kr04_4
int result; // eax
char v6; // [esp+8h] [ebp-12Ch]
char v7; // [esp+9h] [ebp-12Bh]
__int16 v8; // [esp+69h] [ebp-CBh]
char v9; // [esp+6Bh] [ebp-C9h]
char v10; // [esp+6Ch] [ebp-C8h]
char v11; // [esp+6Dh] [ebp-C7h]
__int16 v12; // [esp+131h] [ebp-3h]
char v13; // [esp+133h] [ebp-1h]
v6 = 0;
v10 = 0;
memset(&v7, 0, 0x60u);
v8 = 0;
v9 = 0;
memset(&v11, 0, 0xC4u);
v12 = 0;
v13 = 0;
v3 = time(0);
sub_411AD8(v3);
sub_411A90(aEnterYourName);
scanf(aS, &v6);
v4 = strlen(&v6) + 1;
if ( v4 - 1 < 3 || v4 - 1 > 0x14 ) //名字长度判断
{
sub_411A90(aBadName);
result = -1;
}
else
{
sub_411A90(aEnterYourSn);
scanf(aS, &v10);
if ( strlen(&v10) == 64 ) //SN长度判断
{
sub_401380((int)&v6, v4 - 1, (int)&v10, 64); //验证
result = 0;
}
else
{
sub_411A90(aBadSn);
result = -1;
}
}
return result;
}
可以看出输入的名字长度可以在3-20这间,SN长度为64,验证函数是sub_401380:
void __cdecl sub_401380(char *name, unsigned int lenName, char *sSn, int lenSN)
{
char v4; // [esp+4h] [ebp-70h]
BYTE sn2[32]; // [esp+14h] [ebp-60h]
BYTE sn[32]; // [esp+34h] [ebp-40h]
BYTE sn1[32]; // [esp+54h] [ebp-20h]
if ( lenName >= 3 && lenName <= 0x14 && lenSN == 64 )
{
if ( sub_401000(sSn, 64, (int)sn) != 32 // 16进制串sSn转BYTE sn[32]
|| (AESCode(sn, 32, (int)sn1, keyAES, 128, 0), RSACode((int)sn1, 32, (int)sn2), sn2[0])// sn2[0]要等于0
|| sn2[1] != 2 // sn2[1]要等于2
|| sn2[15] ) // sn2[15]要等于0,这儿sn2前16字节只判断了三个字节,另13个字节可以为任意,就出现了多解
{
sub_411A90(aBadSn);
}
else
{
hashCode(name, lenName, (int)&v4); // 计算name的hash值
if ( !memcmp(&v4, &sn2[16], 0x10u) ) // 比较sn2后16字节要等于name的hash
sub_411A90(aCongratulation); // 成功
}
}
}
AESCode是AES算法,通过一个函数,由参数bEncode来指定完成加密或解密
int __cdecl AESCode(BYTE *bufOut, int lenOut, BYTE *bufIn, BYTE *keyAES, int lenBit, BOOL bEncode)
{
BYTE *v6; // esi
int v7; // ebx
char v9; // [esp+4h] [ebp-F4h]
if ( bEncode == 1 ) // 同一个函数完成加密解密,由bEncode判断是加密还是解密
sub_404FE0(keyAES, lenBit, (unsigned int *)&v9);
else
sub_4053A0(keyAES, lenBit, (unsigned int *)&v9);
if ( lenOut / 16 <= 0 )
return lenOut;
v6 = bufOut;
v7 = lenOut / 16;
do
{
sub_404FB0(v6, &v6[bufIn - bufOut], &v9, bEncode);
v6 += 16;
--v7;
}
while ( v7 );
return lenOut;
}
RSACode对输入完成RSA解密,返回值高位在前
signed int __cdecl RSACode(BYTE *bufIn, int lenIn, BYTE *bufOut)
{
_DWORD *N; // esi
_DWORD *e; // edi
_DWORD *m; // ebx
_DWORD *c; // ebp
signed int v8; // eax
_DWORD *lpMem; // [esp+0h] [ebp-24h]
BYTE bN[32]; // [esp+4h] [ebp-20h]
BYTE *lenIna; // [esp+2Ch] [ebp+8h]
bN[0] = 105; //高位在前 N = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39
bN[8] = 57;
bN[31] = 57;
bN[1] = -126;
bN[2] = 48;
bN[3] = 40;
bN[4] = 87;
bN[5] = 116;
bN[6] = 101;
bN[7] = -85;
bN[9] = -111;
bN[10] = -33;
bN[11] = 4;
bN[12] = 81;
bN[13] = 70;
bN[14] = -7;
bN[15] = 29;
bN[16] = 85;
bN[17] = 109;
bN[18] = -18;
bN[19] = -120;
bN[20] = 112;
bN[21] = -124;
bN[22] = 93;
bN[23] = -114;
bN[24] = -31;
bN[25] = -51;
bN[26] = 60;
bN[27] = -9;
bN[28] = 126;
bN[29] = 74;
bN[30] = 12;
if ( lenIn != 32 )
return 0;
lpMem = sub_406170();
N = sub_406650();
e = sub_406650();
m = sub_406650();
c = sub_406650();
BigIntFromBytes(bufIn, 32, c);
BigIntFromInt(e, 65537);
BigIntFromBytes(bN, 32, N);
BigIntPowMod(m, c, e, N, lpMem); // 大数计算 m = c ^ e mod N
v8 = 0;
lenIna = (BYTE *)(*m + 31);
do // 返回 m 到 bufOut, 高位在前
bufOut[++v8 - 1] = *lenIna--;
while ( v8 < 32 );
sub_4065F0(N);
sub_4065F0(e);
sub_4065F0(m);
sub_4065F0(c);
sub_4061D0(lpMem);
return 32;
}
再来看hashCode:
signed int __cdecl hashCode(const void *bufIn, unsigned int lenIn, PBYTE bufOut)
{
BYTE bufTmp[20]; // [esp+8h] [ebp-DCh]
char v5[200]; // [esp+1Ch] [ebp-C8h]
qmemcpy(v5, bufIn, lenIn);
*(_DWORD *)&bufTmp[lenIn + 20] = dword_41D038;// bufIn尾部加四字节 DE ED BE EF
Sha512MD5((PBYTE)v5, lenIn + 4, bufTmp); // 计算Sha512 再 MD5
*(_DWORD *)&bufTmp[16] = dword_41D030; // 16字节中间结果后面再加四字节 B9 79 37 9E
Sha512MD5(bufTmp, 20, bufOut); // 再计算 Sha512 再 MD5
return 16;
}
signed int __cdecl Sha512MD5(PBYTE bufIn, int lenIn, PBYTE bufOut)
{
BYTE v4[64]; // [esp+0h] [ebp-40h]
SHA512((int)bufIn, lenIn, v4);
MD5((int)v4, 64, bufOut);
return 16;
}
由以上分析可以看出本用了好几种加密算法,不过验证算法还是比较简单的:
设输入的sSN转字节数据为sn:
sn1 = AESDecode(sn)
sn2 = RSACode(sn1)
hashname = hashCode(name)
if(sn2[0] = 0 && sn2[1] == 2 && sn[15] == 0 && sn[16-31] == hashname)
{
//成功
}
所以只要sn2[0,1,15-31]满足条件就成功,而sn2[2-14]可以为任意值,这就会多解了
关于求解方法:
hashname 可以直接由程序中的 hashCode(name)直接得出,而AES运算也可直接用程序中的函数AESCode函数计算出来,只需将参数bEncode改成1,输入改成sn1就可以了,难一点的就是RSA算法:
N = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39
E = 0x10001
在http://www.factordb.com/分解N得到:
p = 0x979BE0C9EECE7426C9FD28C2D6E7772B
q = 0xB22831D15714EB91CD83340B4837182B
再用RSATool计算出D:
D = 0x390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD
有了N,D,就可用大数计算器由sn2算出sn1:
hashCode('KCTF') = sn2[16-31] = 14AF58AD4D76D59D8D2171FFB4CA2231
由于sn2[2-14]没有验证,可以为任意值,全取0有:
sn2 = 0x0002000000000000000000000000000014AF58AD4D76D59D8D2171FFB4CA2231
由N,D,用大数计算器计算得到sn1 = sn2 ^ D mod N
sn1 = 46FD7E72B31A3CB32B2DB098B3597825056A8AC4CF13CD127B95D2B22D9F2E45
OD调试程序,在 004013DF 下断,输入以上sn1,断下后改堆栈中第六个参数为1,步过CALL,第三个参数所指地址就能得到sn:
EF 58 9F 33 33 82 26 68 83 B1 3D 8D F4 C6 C4 C2 A7 86 C2 E7 D9 53 8E 4A 3D 98 E7 B6 CF CD DC E1
即 sSN 为 EF589F333382266883B13D8DF4C6C4C2A786C2E7D9538E4A3D98E7B6CFCDDCE1
本题中计算用到的算法还是有好几种,有AES,RSA,SHA512,MD5不过不明白为什么结果有13字节没有验证,明显的BUG,这样必然多解