诺莫21
知识点:
动态调试,亦或,爆破
开始:
放入IDA:
int __cdecl main(int argc, const char **argv, const char **envp)
{
_DWORD *v4; // [esp+1Ch] [ebp-A0h]
int v5; // [esp+20h] [ebp-9Ch]
int v6; // [esp+24h] [ebp-98h]
int v7; // [esp+28h] [ebp-94h]
int v8; // [esp+2Ch] [ebp-90h]
char v9; // [esp+30h] [ebp-8Ch]
int v10; // [esp+50h] [ebp-6Ch]
int v11; // [esp+70h] [ebp-4Ch]
sub_402620();
v5 = 1701148529;
v6 = 101;
v7 = 0;
v8 = 0;
v4 = malloc(0x408u);
puts("Plz solve the puzzle:");
sub_40ED00("%32s", &v9);
if ( check1(&v9)
&& (sub_401B60(&v10, &v9), sub_401850(v4, &v5, strlen(&v5)), sub_4018D0(v4, &v10, 8), sub_401950(&v10)) )
{
sub_401BA0(&v9, &v11);
sub_40ED20("Congrats!\n%s\n", &v11);
}
else
{
puts("Failed!");
}
return 0;
}
逻辑简单
Congrats上面有个if,让if成立就正确
check1(&v9):
判断输入的字符16位a-f或者0-9(注意:0-9能满足是因为v2前面是unsigned,数字减去58是负数,会溢出结果大于0x38)
&&后面是一个,运算符,就是只看最后一个(如a = (1,2,3)最终a = 3),那我们直接看最后一个函数:
sub_401950(&v10):
bool __cdecl sub_401950(_BYTE *a1)
{
_BYTE *v1; // ecx
bool result; // al
v1 = a1;
while ( 2 )
{
switch ( *v1 )
{
case 0:
dword_40F028 &= dword_40F038;
dword_40F02C *= dword_40F028;
goto LABEL_4;
case 1:
if ( !dword_40F02C )
goto LABEL_6;
dword_40F028 /= dword_40F02C;
dword_40F024 += dword_40F034;
goto LABEL_4;
case 2:
dword_40F030 ^= dword_40F034;
dword_40F03C += dword_40F020;
goto LABEL_4;
case 3:
dword_40F03C -= dword_40F030;
dword_40F030 &= dword_40F024;
goto LABEL_4;
case 4:
dword_40F034 *= dword_40F020;
dword_40F02C -= dword_40F038;
goto LABEL_4;
case 5:
dword_40F020 ^= dword_40F02C;
dword_40F038 -= dword_40F03C;
goto LABEL_4;
case 6:
if ( !dword_40F03C )
goto LABEL_6;
dword_40F034 |= dword_40F024 / dword_40F03C;
dword_40F024 /= dword_40F03C;
goto LABEL_4;
case 7:
dword_40F038 += dword_40F028;
dword_40F034 |= dword_40F024;
goto LABEL_4;
case 8:
dword_40F020 *= dword_40F02C;
dword_40F030 -= dword_40F03C;
goto LABEL_4;
case 9:
dword_40F028 += dword_40F034;
dword_40F02C ^= dword_40F030;
LABEL_4:
if ( ++v1 != a1 + 8 )
continue;
result = (dword_40F038 == 231)
+ (dword_40F034 == 14456)
+ (dword_40F030 == 14961)
+ (dword_40F02C == -13264)
+ (dword_40F028 == 16)
+ (dword_40F024 == 104)
+ (dword_40F020 == -951) == 7;
if ( dword_40F03C != -239 )
goto LABEL_6;
break;
default:
LABEL_6:
result = 0;
break;
}
return result;
}
}
从代码中得知v10一共由8位,每一位是0-9的数字,我们就可以利用条件暴力破解:
#include<stdio.h>
int f(int* a1);
void f2(int a1, int* a2);
int main(void)
{
int num[8];
for (int i = 10000000;i < 1000000000; i++)
{
f2(i, num);
if (f(num))
{
printf("%d", i);
break;
}
}
return 0;
}
void f2(int a1, int* a2)
{
int i = 0;
for (i = 7; i >= 0; i--)
{
a2[i] = a1 % 10;
a1 = a1 / 10;
}
}
int f(int* num)
{
int
dword_40F000 = 0x0000000A,
dword_40F020 = 0x0000008A,
dword_40F024 = 0x000001A1,
dword_40F028 = 0x0000012A,
dword_40F02C = 0x00000269,
dword_40F030 = 0x00000209,
dword_40F034 = 0x00000068,
dword_40F038 = 0x0000039F,
dword_40F03C = 0x000002C8;
int result;
int i = 0;
while(1)
{
switch (num[i])
{
case 0:
dword_40F028 &= dword_40F038;
dword_40F02C *= dword_40F028;
goto LABEL_4;
case 1:
if (!dword_40F02C)
goto LABEL_6;
dword_40F028 /= dword_40F02C;
dword_40F024 += dword_40F034;
goto LABEL_4;
case 2:
dword_40F030 ^= dword_40F034;
dword_40F03C += dword_40F020;
goto LABEL_4;
case 3:
dword_40F03C -= dword_40F030;
dword_40F030 &= dword_40F024;
goto LABEL_4;
case 4:
dword_40F034 *= dword_40F020;
dword_40F02C -= dword_40F038;
goto LABEL_4;
case 5:
dword_40F020 ^= dword_40F02C;
dword_40F038 -= dword_40F03C;
goto LABEL_4;
case 6:
if (!dword_40F03C)
goto LABEL_6;
dword_40F034 |= dword_40F024 / dword_40F03C;
dword_40F024 /= dword_40F03C;
goto LABEL_4;
case 7:
dword_40F038 += dword_40F028;
dword_40F034 |= dword_40F024;
goto LABEL_4;
case 8:
dword_40F020 *= dword_40F02C;
dword_40F030 -= dword_40F03C;
goto LABEL_4;
case 9:
dword_40F028 += dword_40F034;
dword_40F02C ^= dword_40F030;
LABEL_4:
if (++i != 8)
continue;
result = (dword_40F038 == 231)
+ (dword_40F034 == 14456)
+ (dword_40F030 == 14961)
+ (dword_40F02C == -13264)
+ (dword_40F028 == 16)
+ (dword_40F024 == 104)
+ (dword_40F020 == -951) == 7;
if (dword_40F03C != -239)
goto LABEL_6;
break;
default:
LABEL_6:
result = 0;
break;
}
return result;
}
}
//result = 61495072
结果是61495072
然后思考:v10肯定是v9推到的,所以我们能逆向推出v9,就得出答案
sub_401B60(&v10, &v9):
int __cdecl sub_401B60(int a1, _BYTE *a2)
{
_BYTE *v2; // ebx
int i; // esi
char v4; // ST08_1
_BYTE *v5; // ST00_4
int result; // eax
v2 = a2;
for ( i = a1; *v2; result = sub_40ED40(v5, "%02X", v4) )
{
v4 = i;
v5 = v2;
v2 += 2;
++i;
}
return result;
}
将v5(a2)的数据,v4(a1)数据放到一个函数中处理。
函数中还有%02X,猜测是把输入的数据(字符串)转换为16进制存到v10
使用OD动态可以验证:
数据一样
然后观察后面的两个函数
sub_401850:
因为传入的是两个已知数据和一个未知数据,可以使用动态得出未知数据v4
sub_401850:
传入了v10我们得看看对v10做改变没
_DWORD *__cdecl sub_4018D0(_DWORD *tab, _BYTE *sou, int a3)
{
int v3; // edx
int v4; // ecx
_DWORD *v5; // esi
_BYTE *v6; // ebx
int v7; // edi
unsigned int *v8; // eax
int v9; // edx
_DWORD *v10; // ebp
_DWORD *v11; // ST00_4
unsigned int v12; // ebp
_DWORD *result; // eax
v3 = *tab;
v4 = tab[1];
v5 = tab + 2;
if ( a3 > 0 )
{
v6 = sou;
v7 = *tab;
do
{
v7 = (v7 + 1);
v8 = &v5[v7];
v9 = *v8;
v4 = (*v8 + v4);
v10 = &v5[v4];
v11 = v10;
v12 = *v10;
*v8 = v12;
*v11 = v9;
*v6++ ^= v5[(v9 + v12)];
}
while ( v6 != &sou[a3] );
v3 = v7;
}
result = tab;
*tab = v3;
tab[1] = v4;
return result;
}
v5[(v9 + v12)]都是已知数据
使用OD动态调试:
找到8个数据
tab = [0x7c,0xab,0x2d,0x91,0x2f,0x98,0xed,0xa9]
然后因为前面是%02x,两字节对齐,所以后面v10就只有8位
写出逆向脚本:
tab = [0x7c,0xab,0x2d,0x91,0x2f,0x98,0xed,0xa9]
v10 = [0x6,0x1,0x4,0x9,0x5,0x0,0x7,0x2]
flag = ""
for i in range(8):
ch = tab[i]^v10[i]
print("%x"%ch, end = "")
结果:7aaa29982a98eaab
输入7aaa29982a98eaab得到flag