这道题做出来的时候还是非常激动的,不枉费本菜鸡从中午肝到半夜/(ㄒoㄒ)/~~
这道题的壳是出题方自己写的,强度其实是非常不错的,这里我投机取巧了,直接dump内存后转静态分析,这壳子直接木大
后面的哥隆尺算法卡了我好久,最后只能去找金牌爷了= =
下面是照搬的发在看雪的WriteUp:
肝了个一血出来哈哈哈
直接运行,然后x32dbg附加,注意开启sharpod的anti anti attach,然后ollydumpex,点search image,选择module、binary(virtual),dump下来,用ida打开,定位到sub_2F12B0函数
int sub_2F12B0()
{
int v0; // esi
char v1; // al
char *v2; // ecx
unsigned __int8 *v3; // ecx
unsigned __int8 v4; // al
signed int v5; // eax
signed int v6; // ecx
signed int length; // ebx
unsigned __int8 v8; // al
char v9; // al
signed int v10; // edx
int v11; // esi
char v12; // cl
char v13; // al
char v14; // cl
signed int v15; // eax
__int16 v16; // ax
signed int half_len; // ebx
int v18; // edi
int v19; // ecx
signed int v20; // esi
signed int v21; // esi
int v22; // eax
int v23; // eax
__int16 v24; // ax
int v25; // esi
int v26; // eax
int v27; // ecx
int v28; // edx
int v29; // eax
__int16 v31; // [esp+Ch] [ebp-514h]
int v32; // [esp+10h] [ebp-510h]
int v33; // [esp+14h] [ebp-50Ch]
unsigned __int16 v34; // [esp+18h] [ebp-508h]
__int16 v35; // [esp+18h] [ebp-508h]
int v36; // [esp+1Ch] [ebp-504h]
int v37; // [esp+20h] [ebp-500h]
unsigned __int8 input; // [esp+23h] [ebp-4FDh]
int v39[100]; // [esp+24h] [ebp-4FCh]
char flag[400]; // [esp+1B4h] [ebp-36Ch]
char table[256]; // [esp+344h] [ebp-1DCh]
__int16 dest[50]; // [esp+444h] [ebp-DCh]
char v43[100]; // [esp+4A8h] [ebp-78h]
char v44; // [esp+50Ch] [ebp-14h]
int v45; // [esp+50Dh] [ebp-13h]
int v46; // [esp+511h] [ebp-Fh]
int v47; // [esp+515h] [ebp-Bh]
__int16 v48; // [esp+519h] [ebp-7h]
v0 = *(_DWORD *)&unk_314474;
memset(*(_DWORD *)&unk_314474, 0, 256);
v1 = *(_BYTE *)&unk_3138B0;
if ( *(_BYTE *)&unk_3138B0 )
{
v2 = (_BYTE *)&unk_3138B0;
do
{
*(++v2 - 1) = v1 ^ 0xE4;
v1 = *v2;
}
while ( *v2 );
}
sub_2F5D70(v0, 0x3138B0, strlen((const char *)&unk_3138B0) + 1);// Correct
v45 = 0x9091948A;
v46 = 0x9681B7C4;
v3 = (unsigned __int8 *)&v44;
v47 = 0xDE88858D;
v4 = 0xADu;
v48 = 0xEE;
do
{
*(++v3 - 1) = v4 ^ 0xE4;
v4 = *v3;
}
while ( *v3 ); // Input Serial:\n
v36 = **(_DWORD **)(*(_DWORD *)(*(_DWORD *)&unk_314478 + 0x3C) + *(_DWORD *)&unk_314478 + 0x28);
printf(&v44);
memset(v39, 0, 400);
memset(dest, 0, 200);
memset(flag, 255, 400);
memset(table, 255, 256);
v5 = 0;
do
{
table[v5 + '0'] = v5;
++v5;
}
while ( v5 <= 9 );
v6 = 0;
do
{
table[v6 + 'A'] = v6 + 10;
++v6;
}
while ( v6 <= 5 );
input = 0;
length = 0;
scanf(0x310CDC, &input, 1); // %c
v8 = input;
if ( input != 10 )
{
while ( length < 360 )
{
v9 = table[v8];
if ( v9 == -1 )
{
LABEL_52:
printf(0x310C6C);
printf(0x310C80); // 一个礼貌的开场白还是要的
return 0;
}
flag[length++] = v9;
scanf(0x310CDC, &input, 1); // %c
v8 = input;
if ( input == '\n' )
goto LABEL_14;
}
LABEL_32:
printf(0x310C6C);
printf(0x310C70); // \f换页
return 0;
}
LABEL_14:
v10 = 0;
if ( length > 0 )
{
v11 = *(_DWORD *)&unk_31446C;
while ( v11 )
{
v12 = flag[v10];
if ( v12 == -1 )
goto LABEL_52;
v13 = flag[v10 + 1];
if ( v13 == -1 )
goto LABEL_52;
v14 = v13 + 16 * v12;
v15 = v10 >> 1;
v10 += 2;
*((_BYTE *)dest + v15) = v14; // 4个输入构成一个int16
if ( v10 >= length )
goto LABEL_20;
}
goto LABEL_32;
}
LABEL_20:
v16 = 0;
half_len = length >> 2;
v18 = 0;
v33 = 0;
if ( half_len <= 0 )
goto LABEL_57;
v19 = 0;
v32 = 0;
do
{
v34 = -v16;
sub_2F6305(v19);
v20 = 0x65;
do
{
v31 = sub_2F62E4();
--v20;
}
while ( v20 );
sub_2F6305(v34);
v21 = 0x65;
do
{
v35 = sub_2F62E4();
--v21;
}
while ( v21 );
v22 = (*(int (__stdcall **)(_DWORD))&unk_30B144)(0);// GetActiveWindow(0)
if ( v22 )
{
v23 = (*(int (__stdcall **)(int))&unk_30B148)(v22);// GetWindowLongA(v22,GWL_STYLE)
if ( v23 == 0x17CF0000 || v23 == 0x16CF0000 || v23 == 0x97CF0000 )
goto LABEL_51;
}
v24 = dest[v32]; // v32是满足条件v31的输入的index
if ( v31 == v24 )
{
v39[v18++] = v32; // 将满足条件的index入队
}
else if ( v35 != v24 )
{
goto LABEL_52;
}
v16 = v33++ + 1;
v19 = (unsigned __int16)v33;
v32 = (unsigned __int16)v33;
}
while ( (unsigned __int16)v33 < half_len );
if ( v18 < 12 )
{
LABEL_57:
printf(0x310C6C);
printf(0x310C9C); // 行星太少了
return 0;
}
if ( v39[1] - v39[0] > *(&v37 + v18) - *(&v36 + v18) )
{
printf(0x310C6C);
printf(0x310CA8); // 穿越虫洞还得讲个顺序
return 0;
}
memset(v43, 0, 100);
v25 = 0;
if ( v18 - 1 <= 0 )
{
LABEL_49:
if ( !*(_DWORD *)&unk_314470 )
return 0;
printf(*(_DWORD *)&unk_314474); // Correct
return 0;
}
v26 = v36;
while ( 1 )
{
if ( !v26 )
{
printf(0x310C6C);
printf(0x310C80); // 一个礼貌的开场白还是要的
LABEL_47:
v26 = v36;
goto LABEL_48;
}
v27 = v25 + 1;
if ( v25 + 1 < v18 )
break;
LABEL_48:
if ( ++v25 >= v18 - 1 )
goto LABEL_49;
}
v28 = v39[v25];
while ( 1 )
{
v29 = v39[v27] - v28; // v29每次必须不一样
if ( v43[v29] ) // 队列中的indexes,两两的差必须唯一
break;
++v27;
v43[v29] = 1;
if ( v27 >= v18 )
goto LABEL_47;
}
LABEL_51:
printf(0x310C6C);
printf(0x310CC0); // 有人说这题根本无解,你信吗
return 0;
}
遇到30xxxx的地址,从x32dbg里查看到底是啥内容
解密脚本:
int gl;
int sub_2F62E4()
{
unsigned int v1;
v1 = 214013 * gl + 2531011;
gl = v1;
return (v1 >> 16) & 0x7FFF;
}
void sub_2F6305(int a1)
{
gl = a1;
}
int main()
{
int list[] = { 0,2,6,24,29,40,43,55,68,75,76,85 };
__int16 v16, v31, v35;
unsigned __int16 v34;
int v18, v19, v20, v21, v32;
int n = 85;
v16 = 0;
v19 = 0;
v32 = 0;
do
{
v34 = -v16;
sub_2F6305(v19);
v20 = 0x65;
do
{
v31 = sub_2F62E4();
--v20;
} while (v20);
sub_2F6305(v34);
v21 = 0x65;
do
{
v35 = sub_2F62E4();
--v21;
} while (v21);
bool isin = false;
for (size_t i = 0; i < 12; i++)
{
if (list[i] == v16)
{
isin = true;
break;
}
}
if (isin)
{
if ((v31 / 16 & 0xF) < 10)
cout << (v31 / 16 & 0xF);
else
cout << char((v31 / 16 & 0xF) - 10 + 'A');
if ((v31 & 0xF) < 10)
cout << (v31 & 0xF);
else
cout << char((v31 & 0xF) - 10 + 'A');
if ((v31 / 4096 & 0xF) < 10)
cout << (v31 / 4096 & 0xF);
else
cout << char((v31 / 4096 & 0xF) - 10 + 'A');
if ((v31 / 256 & 0xF) < 10)
cout << (v31 / 256 & 0xF);
else
cout << char((v31 / 256 & 0xF) - 10 + 'A');
}
else
{
if ((v35 / 16 & 0xF) < 10)
cout << (v35 / 16 & 0xF);
else
cout << char((v35 / 16 & 0xF) - 10 + 'A');
if ((v35 & 0xF) < 10)
cout << (v35 & 0xF);
else
cout << char((v35 & 0xF) - 10 + 'A');
if ((v35 / 4096 & 0xF) < 10)
cout << (v35 / 4096 & 0xF);
else
cout << char((v35 / 4096 & 0xF) - 10 + 'A');
if ((v35 / 256 & 0xF) < 10)
cout << (v35 / 256 & 0xF);
else
cout << char((v35 / 256 & 0xF) - 10 + 'A');
}
v16++;
v19++;
v32++;
} while (n--);
return 0;
}
list中的值要满足的条件是:从[0,89]中选择12个互不相同的数,使它们任意两个数之差都不相同,这里我问了4位ACM金牌爷,2个多小时后有人把结果弄出来了
程序执行的思路就是,读入至多360个字符,只能0-9、A-F,把每4个输入组合成一个类似0x1234这样的short,如果与v31一样,则把下标加入v39队列,如果与v35一样,则继续,如果都不一样,则结束程序。如果v39的元素小于12则结束程序。之后会判断v39里面的数(也就是满足v31的输入的下标)是否两两之差唯一,是的话就输出Correct。
生成v31和v35的算法是可以本地写代码跑出来的,只要输入的满足“下标不在list里的输入等于v35,否则等于v31”的就是flag
补充金牌爷算法:
#include <stdio.h>
int c[100];
int a[100];
int abs(int x)
{
return x <0?-x:x;
}
void work(int x,int n,int k)
{
for(int i=0;i<n;i++)
c[x-a[i]]+=k;
}
int check(int x,int n)
{
for(int i=0;i<n;i++)
{
if(c[x-a[i]] > 0)
return 0;
}
return 1;
}
void dfs(int now,int n)
{
if(n == 12)
{
for(int i=0;i<n;i++)
{
printf("%d,",a[i]);
}
printf("\n");
}
if(90-now < 12-n) return ;
for(int i=now;i<=89;i++)
{
if(check(i,n))
{
work(i,n,1);
a[n] = i;
dfs(i+1,n+1);
work(i,n,-1);
}
}
}
int main()
{
dfs(0,0);
return 0;
}
注意,这段代码能跑出多组解,但是其余解不满足程序的要求,会输出“穿越虫洞要讲究顺序”,因此上面那组解应该是唯一满足题目限制的解