题目是一个大佬发的,说是西南赛区的一道逆向题目
解压出来是一个exe文件
运行起来是这样的
先看文件信息,没有加壳,64位程序
用IDA64位打开,分析代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
double v4; // xmm1_8
int v5; // xmm0_4
int i; // eax
float v7; // xmm2_4
const char *v8; // rbx
__m128i si128; // xmm2
__int64 v10; // rbx
unsigned int v11; // eax
const char *v12; // rbx
int v14; // [rsp+20h] [rbp-98h] BYREF
int v15[3]; // [rsp+24h] [rbp-94h] BYREF
__int128 v16[5]; // [rsp+30h] [rbp-88h]
v3 = time64(0i64);
srand(v3);
v4 = (double)rand() / 100000.0;
*(float *)&v5 = v4 + 491.0;
dword_14000563C = v5;
*(float *)&dword_140005638 = v4 + 10.0;
if ( IsDebuggerPresent() )
LABEL_22:
exit(1);
printf("\n");
printf("\n");
printf(asc_140003288);
printf("\n");
printf(asc_1400032C8);
printf("\n");
printf(asc_1400032F0);
printf("\n");
printf(asc_140003318);
printf("\n");
printf(asc_140003348);
printf("\n");
printf(asc_140003378);
printf("\n");
printf(asc_1400033A0);
printf("\n");
printf(asc_1400033D0);
printf("\n");
printf(asc_140003408);
printf("\n");
printf("---------------------------------------------------------------------------\n");
printf("\n");
printf(asc_140003490);
printf("\n");
printf(asc_1400034A8, dword_14000563C);
printf("\n");
printf(asc_1400034C0, dword_140005638);
printf("\n");
printf(a1);
printf("\n");
printf(a2);
printf("\n");
printf(asc_140003510);
scanf("%d", &v14, 1i64);
for ( i = v14; v14 == 1; i = v14 )
{
printf("\n");
printf(asc_140003528);
scanf("%d", v15, 3i64);
if ( v15[0] > 9 || (v7 = (float)v15[0], (float)v15[0] > (float)(501.0 - *(float *)&dword_14000563C)) )
{
v8 = (const char *)&unk_140003540;
}
else if ( v15[0] > 0 )
{
v8 = (const char *)&unk_140003568;
*(float *)&dword_14000563C = *(float *)&dword_14000563C + v7;
*(float *)&dword_140005638 = *(float *)&dword_140005638 - v7;
}
else
{
v8 = asc_140003558;
}
printf("\n");
printf(v8);
printf("\n");
if ( *(float *)&dword_140005638 <= 0.0 )
{
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("f");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("l");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("ag");
printf("{md5(");
si128 = _mm_load_si128((const __m128i *)&xmmword_1400035F0);
v10 = 0i64;
v11 = _mm_cvtsi128_si32(si128);
v16[2] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400035D0);
v16[1] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400035E0);
v16[0] = (__int128)si128;
v16[3] = (__int128)si128;
for ( v16[4] = 0i64; v11; v11 = *((_DWORD *)v16 + v10) )
{
printf("%c", v11);
++v10;
}
if ( IsDebuggerPresent() )
goto LABEL_22;
printf(")");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("}");
}
printf("---------------------------------------------------------------------------\n");
printf("\n");
printf(asc_140003490);
printf("\n");
printf(asc_1400034A8, dword_14000563C);
printf("\n");
printf(asc_1400034C0, dword_140005638);
printf("\n");
printf(a1);
printf("\n");
printf(a2);
printf("\n");
printf(asc_140003510);
scanf("%d", &v14, 1i64);
}
v12 = (const char *)&unk_140003578;
if ( i != 2 )
v12 = asc_140003590;
printf("\n");
printf(v12);
getchar();
getchar();
return 0;
}
一点点分析,这里面有好几个检测动态调试的函数
if ( IsDebuggerPresent() )
LABEL_22:
exit(1);
这里这段就是输出flag了,跟进查看,也看不出什么,只能用动态调试了
printf("f");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("l");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("ag");
printf("{md5(");
si128 = _mm_load_si128((const __m128i *)&xmmword_1400035F0);
v10 = 0i64;
v11 = _mm_cvtsi128_si32(si128);
v16[2] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400035D0);
v16[1] = (__int128)_mm_load_si128((const __m128i *)&xmmword_1400035E0);
v16[0] = (__int128)si128;
v16[3] = (__int128)si128;
for ( v16[4] = 0i64; v11; v11 = *((_DWORD *)v16 + v10) )
{
printf("%c", v11);
++v10;
}
if ( IsDebuggerPresent() )
goto LABEL_22;
printf(")");
if ( IsDebuggerPresent() )
goto LABEL_22;
printf("}");
x64debug 打开,f8单步运行,运行到这里调用的是main函数
f7跟进查看
这里对应的就是开始的那个检测动态调试的地方
然后就是一步步运行分析了,我是分析过的,我就直接说简单方法了:搜索字符串
这里有个输出md5( 的,那么在注释里面应该有这段字符串,直接搜索
过滤一下,双击跳转
在这之前下个断点,记下地址 00007FF73737143F
执行到这个检测这个debug的地方
把下面那一句 test eax,eax 改成 jmp 00007FF73737143F,选择剩下字节以nop
填充
然后直接执行跳到那里就行
执行结果: ag{md5(女神早已不是女神,这里flag已经出来了,可以不用继续了
后面这两个检测是否有动态调试的函数,后面的输出也只是把括号补全而已
补全后的结果:ag{md5(女神早已不是女神)}
然后再md5加密一下
md5(女神早已不是女神,32) = 056a5ae5aa88a2151e96ba83948b83aa
md5(女神早已不是女神,16) = aa88a2151e96ba83
最终flag:
flag{056a5ae5aa88a2151e96ba83948b83aa} 或者 flag{aa88a2151e96ba83} (咱也没进到线下赛,也不知道哪个对的)