这里直接贴上当时分析的结果,根据程序行为不难猜出是数独问题(还得靠猜)
main:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char *mart; // rbp
int flagValue; // r14d
int sum; // r12d
__int64 i; // rbx
char chr; // al
int tmp; // eax
char *md5str; // rax
mart = matrix;
flagValue = 1;
sum = 0;
puts("Welcome to Solver!");
do
{
for ( i = 0LL; i != 9; ++i )
{
if ( !mart[i] ) // 如果矩阵元素为零则输入字符
{
chr = getchar();
if ( (chr - '1') > 8u ) // 所以输入的必须是数字,否则错误
flagValue = 0;
else
mart[i] = chr - '0'; // 数字
}
tmp = mart[i];
sum += tmp;
}
mart += 9; // 一次处理九个字符
}
while ( mart != &matrix[81] ); // 一共81
if ( flagValue && verify() ) // 盲猜是数独
{
puts("You Win!");
__snprintf_chk(buf, 32LL, 1LL, 32LL, "%d"); // 405
md5str = str2md5(buf, strlen(buf));
__printf_chk(1LL, "flag is: flag{%s}\n\n", md5str);// flag{bbcbff5c1f1ded46c25d28119a85c6c2}
exit(0);
}
puts("Again~");
return 0;
}
verify(验证):
尤其是当时看到九个一组以及按列检查就反应过来应该是数独
__int64 verify()
{
char *matr; // rsi
__int64 j; // rcx
_DWORD *CHECK; // rdi
__int64 tmp; // rdi
char *matr2; // rsi
__int64 k; // rcx
_DWORD *check2; // rdi
__int64 chr; // rdi
__int64 iii; // r8
char *matr3; // r9
__int64 len3; // rcx
_DWORD *check3; // rdi
int cout; // r10d
char *matr4; // rdi
__int64 i; // rcx
__int64 tmpchr; // rbx
matr = matrix;
LABEL_2:
j = 10LL;
CHECK = check;
while ( j )
{
*CHECK++ = 0; // 初始化空间
--j;
}
while ( 1 )
{
tmp = matr[j];
if ( check[tmp] ) // 不能访问已访问过的
return 0LL;
++j;
check[tmp] = 1; // 置一
if ( j == 9 )
{
matr += 9; // 九个一组
if ( matr != &matrix[81] )
goto LABEL_2;
matr2 = matrix;
LABEL_9:
k = 10LL;
check2 = check;
while ( k )
{
*check2++ = 0; // 再次清空check数组
--k;
}
while ( 1 )
{
chr = matr2[9 * k]; // 检查第一列
if ( check[chr] )
return 0LL;
++k;
check[chr] = 1;
if ( k == 9 )
{
if ( ++matr2 != &matrix[9] )
goto LABEL_9;
iii = 0LL;
while ( 2 )
{ // 检查第二列
matr3 = &matrix[iii];
do
{
len3 = 10LL;
check3 = check;
cout = 3;
while ( len3 ) // 清空check
{
*check3++ = 0;
--len3;
}
matr4 = matr3;
while ( 2 )
{
for ( i = 0LL; i != 3; ++i )
{
tmpchr = matr4[i];
if ( check[tmpchr] ) // 也是不能有1
return 0LL;
check[tmpchr] = 1;
}
matr4 += 9;
if ( --cout )
continue;
break;
}
matr3 += 3;
}
while ( matr3 != &matr2[iii] );
iii += 27LL;
if ( iii != 81 )
continue;
break;
}
return 1LL;
}
}
}
}
}
先跟进martix然后将数据读取出来
然后询问ChatGpt直接得到答案
以及给出解题代码和运行结果:
然后填数独就可以过了
#include
int main()
{
int sum = 0;
unsigned char matrix[81] =
{
5, 3, 0, 0, 7, 0, 0, 0, 0, 6,
0, 0, 1, 9, 5, 0, 0, 0, 0, 9,
8, 0, 0, 0, 0, 6, 0, 8, 0, 0,
0, 6, 0, 0, 0, 3, 4, 0, 0, 8,
0, 3, 0, 0, 1, 7, 0, 0, 0, 2,
0, 0, 0, 6, 0, 6, 0, 0, 0, 0,
2, 8, 0, 0, 0, 0, 4, 1, 9, 0,
0, 5, 0, 0, 0, 0, 8, 0, 0, 7,
9
};
int grid[81] = {
5, 3, 4, 6, 7, 8, 9, 1, 2,
6, 7, 2, 1, 9, 5, 3, 4, 8,
1, 9, 8, 3, 4, 2, 5, 6, 7,
8, 5, 9, 7, 6, 1, 4, 2, 3,
4, 2, 6, 8, 5, 3, 7, 9, 1,
7, 1, 3, 9, 2, 4, 8, 5, 6,
9, 6, 1, 5, 3, 7, 2, 8, 4,
2, 8, 7, 4, 1, 9, 6, 3, 5,
3, 4, 5, 2, 8, 6, 1, 7, 9
};
int len = 0;
for (int i = 0; i < 81; i++)
{
if (!matrix[i])
{
len++;
printf("%d", grid[i]);//输出需要填充的序列
//468912723481342575971422657913948591537428763345261
}
}
return 0;
}
linux下运行程序,输入序列:468912723481342575971422657913948591537428763345261
即可得到答案:
这里可能是识别问题,找到这三块红色区域查看汇编
然后可以发现这里莫名其妙定义了四个字节数据,按u取消掉下方指令定义
然后从这段数据头(0x763处)按p识别为函数,即可可以看到tea逻辑
另外两块红色区域同理,重新识别后可以看到主函数(如果看不到那就对tea函数交叉引用向上找)
这里给出我分析美化后的主函数:
不过这里有一个关键问题就是他的key和关键数据并不能通过静态分析看到,所以需要动态分析了
__int64 __fastcall Main()
{
__int64 result; // rax
int i; // [rsp+Ch] [rbp-464h]
int j; // [rsp+10h] [rbp-460h]
int chr1; // [rsp+14h] [rbp-45Ch] BYREF
int chr2; // [rsp+18h] [rbp-458h]
int v5; // [rsp+1Ch] [rbp-454h]
int key[8]; // [rsp+20h] [rbp-450h] BYREF
int d1; // [rsp+40h] [rbp-430h]
int d2; // [rsp+44h] [rbp-42Ch]
int b1; // [rsp+48h] [rbp-428h]
int b2; // [rsp+4Ch] [rbp-424h]
int v11; // [rsp+50h] [rbp-420h]
int buffer[258]; // [rsp+60h] [rbp-410h] BYREF
unsigned __int64 v13; // [rsp+468h] [rbp-8h]
v13 = __readfsqword(0x28u);
key[0] = 5;
key[1] = 2;
key[2] = dword_7FA16F6F8464; // 猜测也是一位数,可以爆破
key[3] = dword_7FA16F6F8454;
key[4] = 0;
memset(buffer, 0, 0x400uLL);
sub_7FA16F6F5524();
sub_7FA16F6F5554();
sub_7FA16F6F5594(); // 这几个函数找不到
sub_7FA16F6F5574(&unk_7FA16F6F6469, buffer);
chr1 = 0;
chr2 = 0;
v5 = 0;
for ( i = 0; buffer[i]; i += 2 ) // 也就是每次对buffer的两个字符进行tea加密
{
chr1 = buffer[i];
chr2 = buffer[i + 1];
Tea(dword_7FA16F6F8474, &chr1, key); // 一次加密两个字符
buffer[i] = chr1; // 更新buffer串
buffer[i + 1] = chr2;
}
d1 = 0;
d2 = 0;
b1 = 0;
b2 = 0;
v11 = 0;
for ( j = 0; buffer[j]; j += 2 ) // 比较函数
{
d1 = desStr[j];
d2 = desStr[j + 1];
b1 = buffer[j];
b2 = buffer[j + 1];
if ( d1 != b1 || d2 != b2 )
break;
}
sub_7FA16F6F5524();
result = 0LL;
if ( v13 != __readfsqword(0x28u) )
return sub_7FA16F6F5544();
return result;
}
如果直接使用ida远程动调会提示错误:
Input file is a dynamic library, it cannot be run by itself.
Please specify the host application (Debugger, Process options)
提示这是个动态库文件,需要附加进程进行调试,那么我们可以使用IDA的附加调试功能
成功附加后会发现按f7或者f8都没反应,此时程序等待输出,先到linux虚拟机输入一个选项
如果输入1,后续调试比较难找,总之就是找到Loading字符串,注意不能跳过了
(直接搜索字符串搜索不到,这题应该是有SMC的操作对程序代码进行解密)
下方的call sun_7f006f7c2536就是主函数,跟进重新识别即可看到逻辑(这里不做详细介绍,以选项2为主)
如果输入2
一直按f7,最后可以发现程序会卡住,并且还能看见InputFlag字符串,这里就是主函数了
往上翻找到起始地址,对loc_loc_7F3BB0FB6536按p识别为函数就可以看到主函数逻辑了
然后还有一些没有被正确识别的函数,需要手动跟进按p识别为函数,然后回到反汇编界面按f5重新识别即可
key的值:
tea加密循环次数的值(所以这个tea循环36次而非32次):
加密后的data,跟进可以提取出数据
#include
void Tea(int len, unsigned int* buffer, int* key)
{
int i; // [rsp+24h] [rbp-14h]
unsigned int v5; // [rsp+28h] [rbp-10h]
unsigned int v6; // [rsp+2Ch] [rbp-Ch]
unsigned int v7; // [rsp+30h] [rbp-8h]
v5 = *buffer;
v6 = buffer[1];
v7 = 0xd9b6d99c;
for (i = 0; i < len; ++i)
{
v6 -= (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (key[(v7 >> 11) & 3] + v7);
v7 += 0x41104111;
v5 -= (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (key[v7 & 3] + v7);
}
*buffer = v5;
buffer[1] = v6;
}
int main()
{
int buffer[14] =
{
-318921983,
1639894517,
-1197577091,
-835265432,
1265521566,
1680782596,
1425658684,
1829167973,
-360235693,
-1537112825,
-676229584,
-1000652734,
0,
0,
};
int key[5] = { 5,2,9,7,0 };
int chr[2] = { 0 };
for (int i = 0; buffer[i]; i += 2) // 也就是每次对buffer的两个字符进行tea加密
{
chr[0] = buffer[i];
chr[1] = buffer[i + 1];
Tea(36, chr, key); // 一次加密两个字符
buffer[i] = chr[0]; // 更新buffer串
buffer[i + 1] = chr[1];
}
unsigned char* p = (unsigned char*)buffer;
printf("%s", p);
//Thisisflag{cdfec405-3f4b-457e-92fe-f6446098ee2e}
return 0;
}
简单分析了一下程序逻辑
这里复现时询问ChatGpt回答我的是FFT算法,而不是DCT,让其输出解密脚本也一直错误,看来还是得多了解一下这个算法
参考大佬文章:2023春秋杯春季赛WP-REVERSE(AK)
能用cv2库函数进行处理
import numpy as np
import cv2
x = np.array([370.75,234.362,-58.0834,59.8212,88.8221,-30.2406,21.8316,49.9781,-33.5259,2.69675,43.5386,-30.2925,-28.0754,27.593,-2.53962,-27.1883,-5.60777,-0.263937,6.80326,8.03022,-6.34681,-0.89506,-6.80685,-13.6088,27.0958,29.8439,-21.7688,-20.6925,-13.2155,-37.0994,2.23679,37.6699,-3.5,9.85188,57.2806,13.5715,-20.7184,8.6816,3.59369,-4.5302,4.22203,-28.8166,-23.695,31.2268,6.58823,-39.9966,-20.7877,-19.7624,-22.031,16.3285,2.07557,-26.2521,16.1914,18.3976,-26.9295,3.03769,41.0412,20.2598,14.991,6.99392,-22.3752,-7.24466,8.96299,-10.4874], dtype=np.float64)
x = cv2.idct(x)
x = x.ravel()
flag=''
for i in range(len(x)):
flag+=chr(round(x[i]))
print(flag)#flag{9ab488a7-5b11-1b15-04f2-c230704ecf72}
关于DCT算法这几篇文章不错: