SOAR
题解
从题目描述中,我们可以得到一个文件,SOAR-challenge.pdf。用PDF查看器打开它,没发现什么。用一个普通的文件查看器/16进制查看器打开它,一眼就能看到一个"PK", 里面藏了一个zip压缩文件。
把zip提取出来,我们可以看到他有密码保护。这个密码太简单,猜猜就能猜出来,密码是"dso"。或者也可以暴力破解,几秒钟就出来。
解压后有一个文件"soar", file
告诉我们他是一个 ELF 文件。
用IDA反编译它
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
char v4; // al
char v5; // cl
__int64 v6; // rax
int v7; // kr00_4
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
__int64 v12; // rax
unsigned __int64 v13; // rax
char v14; // si
unsigned __int64 v15; // rax
int v17; // [rsp+Ch] [rbp-6C4h]
int v18; // [rsp+Ch] [rbp-6C4h]
int v19; // [rsp+10h] [rbp-6C0h]
char v20; // [rsp+10h] [rbp-6C0h]
int v21; // [rsp+14h] [rbp-6BCh]
time_t timer; // [rsp+18h] [rbp-6B8h] BYREF
__int64 i; // [rsp+20h] [rbp-6B0h]
__int64 j; // [rsp+28h] [rbp-6A8h]
__int64 v25; // [rsp+30h] [rbp-6A0h]
__int64 k; // [rsp+38h] [rbp-698h]
__int64 l; // [rsp+40h] [rbp-690h]
char *filename; // [rsp+48h] [rbp-688h]
_BYTE *v29; // [rsp+50h] [rbp-680h]
struct tm *v30; // [rsp+58h] [rbp-678h]
FILE *stream; // [rsp+60h] [rbp-670h]
void *ptr; // [rsp+68h] [rbp-668h]
int v33[47]; // [rsp+70h] [rbp-660h]
int v34; // [rsp+12Ch] [rbp-5A4h]
int v35[64]; // [rsp+130h] [rbp-5A0h]
int v36[68]; // [rsp+230h] [rbp-4A0h] BYREF
__int64 v37[48]; // [rsp+340h] [rbp-390h] BYREF
__int64 v38[66]; // [rsp+4C0h] [rbp-210h] BYREF
v38[65] = __readfsqword(0x28u);
filename = (char *)malloc(0xD50C51uLL);
j = 0LL;
v25 = 0LL;
v29 = malloc(0xD50C51uLL);
for ( i = 2LL; i <= 13962321; ++i )
v29[i] = 1;
for ( i = 2LL; i <= 13962321; ++i )
{
if ( v29[i] )
{
for ( j = i * i; j <= 13962320; j += i )
v29[j] = 0;
}
}
time(&timer);
v30 = localtime(&timer);
v19 = v30->tm_min;
v33[0] = 54;
v33[1] = 38;
v33[2] = 24;
v33[3] = 11;
v33[4] = 36;
v33[5] = 17;
v33[6] = 18;
v33[7] = 16;
v33[8] = 31;
v33[9] = 40;
v33[10] = 32;
v33[11] = 51;
v33[12] = 35;
v33[13] = 30;
v33[14] = 22;
v33[15] = 47;
v33[16] = 55;
v33[17] = 13;
v33[18] = 37;
v33[19] = 44;
v33[20] = 33;
v33[21] = 20;
v33[22] = 21;
v33[23] = 12;
v33[24] = 26;
v33[25] = 48;
v33[26] = 28;
v33[27] = 29;
v33[28] = 14;
v33[29] = 25;
v33[30] = 46;
v33[31] = 23;
v33[32] = 49;
v33[33] = 52;
v33[34] = 41;
v33[35] = 57;
v33[36] = 19;
v33[37] = 50;
v33[38] = 42;
v33[39] = 45;
v33[40] = 53;
v33[41] = 56;
v33[42] = 27;
v33[43] = 43;
v33[44] = 39;
v33[45] = 34;
v33[46] = 15;
v34 = 5;
qmemcpy(v37, &unk_2040, sizeof(v37));
filename[47] = 0;
for ( i = 0LL; i <= 46; ++i )
{
v37[i] -= 106496LL;
v37[i] -= 3153LL;
v37[i] -= v19 ^ (LODWORD(v37[47]) + v34);
filename[v33[i] - (LODWORD(v37[47]) + v34)] = v37[i];
}
i = 0LL;
stream = fopen(filename, "r");
if ( stream )
{
fseek(stream, 0LL, 2);
v3 = ftell(stream);
i = v3 == (v19 ^ (LODWORD(v37[47]) + v34)) + 101606;
fclose(stream);
}
v21 = v30->tm_hour;
if ( i )
{
for ( k = 0LL; k <= 13962320; ++k )
{
if ( v29[k] )
{
ptr = malloc(0x2AuLL);
v17 = k;
j = 0LL;
do
{
if ( v17 % 16 <= 9 )
v4 = 48;
else
v4 = 87;
v5 = v4 + v17 % 16;
v6 = j++;
*((_BYTE *)ptr + v6) = v5;
v7 = v17;
v17 /= 16;
}
while ( v7 / 16 );
v8 = j;
LODWORD(j) = j + 1;
*((_BYTE *)ptr + v8) = 0;
v18 = j;
l = k;
k = 0LL;
for ( j = (int)j - 2; k < j; --j )
{
v20 = *((_BYTE *)ptr + k);
*((_BYTE *)ptr + k) = *((_BYTE *)ptr + j);
*((_BYTE *)ptr + j) = v20;
++k;
}
k = l;
for ( l = 0LL; l < v18 - 1; ++l )
{
v9 = v25++;
filename[v9] = *((_BYTE *)ptr + l);
}
v10 = v25++;
filename[v10] += 67;
v11 = v25++;
filename[v11] = filename[v11];
v12 = v25++;
filename[v12] += 76;
filename[v25 - 2] = 83;
free(ptr);
}
}
}
qmemcpy(v38, &unk_21C0, 0x208uLL);
qmemcpy(v36, &unk_23E0, 0x104uLL);
for ( i = 0LL; (unsigned __int64)i <= 0x3F; ++i )
{
if ( v36[i] - v21 <= 0 )
v13 = v21 - v36[i];
else
v13 = v36[i] - v21;
v14 = filename[v38[v13 % 0x41]];
if ( v36[i] - v21 <= 0 )
v15 = v21 - v36[i];
else
v15 = v36[i] - v21;
v35[v15 % 0x41] = v14;
}
for ( i = 0LL; (unsigned __int64)i <= 0x3F; ++i )
{
if ( (i & 1) != 0 )
{
if ( v21 == LODWORD(v38[64]) + v36[64] )
{
filename[i] = v35[i];
if ( i == 1 )
j += 3LL;
}
}
else
{
filename[i] = v35[i];
if ( !i )
j = 0LL;
}
}
filename[i] = 0;
printf((const char *)*(&fStr + j), filename);
return 0;
}
我们可以注意到两件有意思的事情:
1. 程序不需要任何输入,但是它读取了系统时间,根据系统时间不同执行的东西不太一样
2. 程序读取了一个文件,对这个文件可能有什么特殊要求
文件名称并没有明文存储,而是需要用当前的系统时间的分钟数与一些数据异或得到。因为只有60种可能,这里就暴力就好了。结论是正确的分钟数是 43,文件名是 "3CHOLARSHIP"。
然后仔细看这部分:
if ( stream )
{
fseek(stream, 0LL, 2);
v3 = ftell(stream);
i = v3 == (v19 ^ (LODWORD(v37[47]) + v34)) + 101606;
fclose(stream);
}
v21 = v30->tm_hour;
if ( I )
{
.....
程序只会在 i 等于 1 的时候继续运行, 所以 v3 == (v19 ^ (LODWORD(v37[47]) + v34)) + 101606
必须成立。 v3 是文件长度, 因为 fseek
把当前文件指针移到文件末尾,然后 ftell
返回当前指针位置。 v37[47] 的值除了 qmemcpy(v37, &unk_2040, sizeof(v37));
以外都没被修改过, 所以我们知道他是 6。 v19 是当前分钟的值 43,v34 是 5. 计算下, (v19 ^ (LODWORD(v37[47]) + v34)) + 101606
事实上就是 101638。所以,我们执行
with open("3CHOLARSHIP", "w") as f:
f.write("A"*101638)
来创建正确长度的这个文件。
下一部分跟最开始类似,用系统时间的小时数解密。因此我们就把24种可能性暴力一遍。正确的时间是 11 点。
所以,在 11:43 运行这个程序(或者修改系统时间到这个时间) 就可以得到 flag, DSO-NUS{654c7b655ed2e3eb42f1d886786c14fe5a757e416f5373de5cd2e4089b870eb5da}