老规矩 PEID查壳 显示什么也没找到 先别管 拖到OD里搜索字符串看看
可以发现最上面的字符串,其实挺像KEY的,输入试试,发现这个并不是KEY。
进入字符串“这就是我要的flag!!!”上方下个断点,先让程序跑一下,然后断下来开始单步调试
单步了几下发现一个字符串的处理,我输入的是“1234”
注意左上角的EAX寄存器,我运行了几下发现程序在逐个处理我输入的字符串,这个循环我光看汇编看不懂,等会到IDA里分析去
再向下单步
又来到了一个字符串,这个字符串之前见过,7个kow,单步发现程序把kow的每个字符+1,变成了7个lpx
程序运行结束,顺手爆破了一下,并没有什么卵用
接下来用IDA分析
注意进去之后会看到这个_security_check_cookie(x)函数......我以前不懂,还以为这肯定就是存key的地方,后来查了查才知道这是为了保护堆栈平衡设立的,与key无关。
我们F5反汇编一下就可以看到程序大致怎么处理我们的字符串的:
int __usercall sub_401000@(char a1@)
{
signed int v1; // ecx@1
char *v2; // eax@2
__int128 *v3; // eax@8
int v4; // esi@10
char v5; // al@11
char v7; // [sp-4h] [bp-68h]@10
char ourinput[32]; // [sp+0h] [bp-64h]@1
__int128 v9; // [sp+20h] [bp-44h]@1
int v10; // [sp+30h] [bp-34h]@1
__int16 v11; // [sp+34h] [bp-30h]@1
__int64 v12; // [sp+36h] [bp-2Eh]@1
__int128 v13; // [sp+40h] [bp-24h]@1
__int64 v14; // [sp+50h] [bp-14h]@1
__int16 v15; // [sp+58h] [bp-Ch]@1
int v16; // [sp+5Ah] [bp-Ah]@1
v15 = 125;
_mm_storeu_si128((__m128i *)&v13, _mm_loadu_si128((const __m128i *)&xmmword_413E34));
v10 = 1869313903;
_mm_storel_epi64((__m128i *)&v14, _mm_loadl_epi64((const __m128i *)&qword_413E44));
v16 = 0;
_mm_storeu_si128((__m128i *)&v9, _mm_loadu_si128((const __m128i *)&xmmword_413E50));
v11 = 119;
_mm_storel_epi64((__m128i *)&v12, 0i64);
sub_4012D1("你懂的:", ourinput[0]);
sub_401127("%s", ourinput);
v1 = 0;
if ( ourinput[0] )
{
v2 = ourinput;
do
{
*v2++ += v1;
if ( v1 >= 5 )
v1 = 0;
else
++v1;
}
while ( *v2 );
}
if ( (_BYTE)v9 )
{
v3 = &v9;
do
{
++*(_BYTE *)v3;
v3 = (__int128 *)((char *)v3 + 1);
}
while ( *(_BYTE *)v3 );
}
v7 = a1;
v4 = 0;
if ( (_BYTE)v13 )
{
while ( 1 )
{
v5 = ourinput[v4];
if ( !v5 || *((_BYTE *)&v13 + v4) != v5 )
break;
++v4;
if ( !*((_BYTE *)&v13 + v4) )
goto LABEL_16;
}
sub_4012D1("flag不对呦,再试试呀,加油!\n", v7);
}
LABEL_16:
if ( !*((_BYTE *)&v13 + v4) )
sub_4012D1("这就是我要的flag!!!\n", ourinput[0]);
sub_4011C7("pause");
return 0;
}
我把我们输入的字符串叫做ourinput,可以看到处理字符串的函数有三个,两个加密,一个验算答案,两个字符串的处理我们刚才都在OD里看过了,我们来看看具体是怎么处理的
第一个:
if ( ourinput[0] )
{
v2 = ourinput;
do
{
*v2++ += v1;
if ( v1 >= 5 )
v1 = 0;
else
++v1;
}
while ( *v2 );
}
可以看到这个是把我们输入的字符串分成6个一组,每组每个字节加上该字节的索引值,这是处理我们输入的字符串的过程
第二个:
if ( (_BYTE)v9 )
{
v3 = &v9;
do
{
++*(_BYTE *)v3;
v3 = (__int128 *)((char *)v3 + 1);
}
while ( *(_BYTE *)v3 );
}
这个就是每个字节加上1,很明显这就是kow变成lpx的过程
第三个:
if ( (_BYTE)v13 )
{
while ( 1 )
{
v5 = ourinput[v4];
if ( !v5 || *((_BYTE *)&v13 + v4) != v5 )
break;
++v4;
if ( !*((_BYTE *)&v13 + v4) )
goto LABEL_16;
}
sub_4012D1("flag不对呦,再试试呀,加油!\n", v7);
}
可以看到这个是一个验算的函数,而且,这里面根本就没提到第二个函数中所用的字符串,其实那个字符串只是个障眼法
真正的对比字符串是v13,我们可以想办法找到v13的值,然后同样分成6个一组,减去各自的索引值即可。
还原结果如下: