诺莫18
原题:
开始:
放入IDA:
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);
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;
}
sub_401000()判断输入字符长度为6-16,并且为字母或者字符
sub_401090(&v11)没有对v11进行操作,不影响密码和流程,跳过
v4 = ((int (__cdecl *)(char *, char *))loc_4011A0)(&v7, &v5);
看后面printf(&v5);
v5的数据没有,使用动态调试:
一个跳出这个循环就对了,那就要
sub_401830(a1, (int)&v11, &v9)
v4均满足if,v4是由两个已知量输入的,所以我们不用管
sub_401830(a1, (int)&v11, &v9):
bool __usercall sub_401830@<al>(int ebx0@<ebx>, int user, const char *pow)
{
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(pow) )
{
if ( isdigit(pow[v7]) )
{
v9 = pow[v7] - 48;
}
else if ( isxdigit(pow[v7]) )
{
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
pow[v7] = 34;
v9 = (pow[v7] | 0x20) - 87;
}
else
{
v9 = ((pow[v7] | 0x20) - 97) % 6 + 10; // 将a,b,c...转换为16进制对应的10进制数值
}
v10 = v9 + 16 * v10;
if ( !((signed int)(v7 + 1) % 2) )
{
*(&v15 + v4++) = v10;
ebx0 = v4;
v10 = 0;
}
++v7;
}
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);
if ( *(_DWORD *)(__readfsdword(0x30u) + 2) & 0xFF )
{
v11 = -83;
v12 = 43;
}
sub_401710((int)&v17, (const char *)user, v6++);
v5 = v6;
if ( v6 >= (unsigned int)(&v15 + strlen(&v15) + 1 - &v16) )
v5 = 0;
}
v14 = 0;
sub_401470(ebx0, &v17, &v14);
return v14 == 43924;
}
分析:
将输入字符串进行操作后与byte_416050[(unsigned __int8)(v8 + v13)]数据亦或再放入sub_401470(ebx0, &v17, &v14);。这是关键流程,倒着看,要让v14 == 43924,进入sub_401470(ebx0, &v17, &v14):
_DWORD *__usercall sub_401470@<eax>(int a1@<ebx>, _BYTE *a2, _DWORD *a3)
{
int v3; // ST28_4
int v4; // ecx
_DWORD *_EAX; // eax
int v6; // edx
int v8; // ST20_4
int v9; // eax
int v10; // edi
int v11; // ST1C_4
int v12; // edx
char v13; // di
int v14; // ST18_4
int v15; // eax
int v16; // ST14_4
int v17; // edx
char v18; // al
int v19; // ST10_4
int v20; // ecx
char _AL; // al
int v23; // ST0C_4
int v24; // eax
_DWORD *result; // eax
int v26; // edx
if ( *a2 == 'd' )
{
*a3 |= 4u;
v4 = *a3;
}
else
{
*a3 ^= 3u;
}
v3 = *a3;
if ( a2[1] == 'b' )
{
_EAX = a3;
*a3 |= 0x14u;
v6 = *a3;
}
else
{
*a3 &= 0x61u;
_EAX = (_DWORD *)*a3;
}
__asm { aam }
if ( a2[2] == 'a' )
{
*a3 |= 0x84u;
v9 = *a3;
}
else
{
*a3 &= 0xAu;
}
v8 = *a3;
v10 = ~(a1 >> -91);
if ( a2[3] == 'p' )
{
*a3 |= 0x114u;
v12 = *a3;
}
else
{
*a3 >>= 7;
}
v11 = *a3;
v13 = v10 - 1;
if ( a2[4] == 'p' )
{
*a3 |= 0x380u;
v15 = *a3;
}
else
{
*a3 *= 2;
}
v14 = *a3;
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
{
if ( a2[5] == 102 )
{
*a3 |= 0x2DCu;
v17 = *a3;
}
else
{
*a3 |= 0x21u;
}
v16 = *a3;
}
if ( a2[5] == 's' )
{
*a3 |= 0xA04u;
v18 = (char)a3;
v20 = *a3;
}
else
{
v18 = (char)a3;
*a3 ^= 0x1ADu;
}
v19 = *a3;
_AL = v18 - v13;
__asm { daa }
if ( a2[6] == 'e' )
{
*a3 |= 0x2310u;
v24 = *a3;
}
else
{
*a3 |= 0x4Au;
}
v23 = *a3;
if ( a2[7] == 'c' )
{
result = a3;
*a3 |= 0x8A10u;
v26 = *a3;
}
else
{
*a3 &= 0x3A3u;
result = (_DWORD *)*a3;
}
return result;
}
加入每一个if都成立,验证a3的值是不是43924
a3 = 0
a3 |= 0x4
a3 |= 0x14
a3 |= 0x84
a3 |= 0x114
a3 |= 0x380
#a3 |= 0x2DCu 反调试的不要
a3 |= 0xA04
a3 |= 0x2310
a3 |= 0x8A10
print(a3) #43924 #dbappsec
输出结果就是43924
所以v17的值是’dbappsec’
现在研究前面的算法:
while ( v7 < strlen(pow) )
{
if ( isdigit(pow[v7]) )
{
v9 = pow[v7] - 48;
}
else if ( isxdigit(pow[v7]) )
{
if ( *(_DWORD *)(*(_DWORD *)(__readfsdword(0x30u) + 24) + 12) != 2 )
pow[v7] = 34;
v9 = (pow[v7] | 0x20) - 87;
}
else
{
v9 = ((pow[v7] | 0x20) - 97) % 6 + 10; // 将a,b,c...转换为16进制对应的10进制数值
}
v10 = v9 + 16 * v10;
if ( !((signed int)(v7 + 1) % 2) )
{
*(&v15 + v4++) = v10;
ebx0 = v4;
v10 = 0;
}
++v7;
}
如果是字符是数字,就转成相应的值,如果是字母,就转成相应的16进制,如:a转成10,b转成11(第二个if不需要管,单个字符字符不肯能识别为16进制)
然后两个两个组合看成16进制,转化为10进制寸到v15。
简而言之就是把一个字符串两个两个看成一个16进制,转换为10进制存到数组中
byte_416050我们也需要动态调试(再IDA找到偏移地址):
查找ecx值,8次
[0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd]
逆向:
import hashlib
a3 = 0
a3 |= 0x4
a3 |= 0x14
a3 |= 0x84
a3 |= 0x114
a3 |= 0x380
#a3 |= 0x2DCu 反调试的不要
a3 |= 0xA04
a3 |= 0x2310
a3 |= 0x8A10
print(a3) #43924 #dbappsec
x = [0x2a, 0xd7, 0x92, 0xe9, 0x53, 0xe2, 0xc4, 0xcd]
y = ['d','b','a','p','p','s','e','c']
m = ""
for i in range(len(y)):
m += hex(ord(y[i])^x[i])
m = m.replace("0x","")
n = hashlib.md5()
n.update(m.encode("utf-8"))
print(n.hexdigest())
根据题目要求,要进行md5加密
套上flag
flag{d2be2981b84f2a905669995873d6a36c}