文章目录
题目附件
链接:https://pan.baidu.com/s/1W0GisS4R-rHYHK4Bu167_g?pwd=nw4c
Check_Your_Luck
可以看到五条方程,根据方程可以求解出对应的值
Z3库解不出来,所以用了在线解方程云算网(原名云算子)-在线求解线性方程组 (yunsuan.info)
flag: NSSCTF{4544_123_677_1754_777}
TEA
基本逻辑:
- 调用show函数,提示输入输出
- 以十六进制读取输入,可以得知共10*4=40字符
- 设置key数组
- 将input复制到arr数组中
- tea加密
- judge判断是否正确
- 输出flag
mainfun代码:
__int64 mainfun()
{
char *v0; // rdi
__int64 i; // rcx
char v3[32]; // [rsp+0h] [rbp-20h] BYREF
char v4; // [rsp+20h] [rbp+0h] BYREF
int v5; // [rsp+24h] [rbp+4h]
int result; // [rsp+44h] [rbp+24h]
unsigned int key[12]; // [rsp+68h] [rbp+48h] BYREF
int input[16]; // [rsp+98h] [rbp+78h] BYREF
int arr[31]; // [rsp+D8h] [rbp+B8h] BYREF
int j; // [rsp+154h] [rbp+134h]
int k; // [rsp+174h] [rbp+154h]
int m; // [rsp+194h] [rbp+174h]
v0 = &v4;
for ( i = 102i64; i; --i )//没作用
{
*v0 = 0xCCCCCCCC;
v0 += 4;
}
sub_7FF7B3C8137F(&unk_7FF7B3C93009); //没作用
v5 = 32;//tea循环次数
result = 0;
key[0] = 1234;
key[1] = 5678;
key[2] = 9012;
key[3] = 3456; //设置key,但是这里设置的值是混淆的,下方会重新设置
memset(input, 0, 40ui64);
arr[15] = 0;
arr[23] = 0;
show(); //提示flag输入是以0x12345678这样的形式
for ( j = 0; j < 10; ++j )
scanf("%x", &input[j]); //以16进制读取,共10个,所以有10*4=40字符
setKey(key); //设置key
copy(input, arr); //将input的内容复制到arr
encode(input, key); //对input加密
result = judge(input); //判断加密后的数组是否正确
if ( result )
{
printf("you are right\n");
for ( k = 0; k < 10; ++k )
{
for ( m = 3; m >= 0; --m )
printf("%c", (arr[k] >> (8 * m))); //如果正确则输出input,即flag
}
}
else
{
printf("fault!\nYou can go online and learn the tea algorithm!");
}
sub_7FF7B3C81311(v3, &unk_7FF7B3C8AE90);
return 0i64;
}
show函数
setKey函数
encode函数(tea算法):
__int64 __fastcall tea(unsigned int *arr, unsigned int *key)
{
__int64 result; // rax
int v3; // [rsp+44h] [rbp+24h]
int i; // [rsp+64h] [rbp+44h]
unsigned int v5; // [rsp+84h] [rbp+64h]
unsigned int sum; // [rsp+C4h] [rbp+A4h]
result = sub_7FF7B3C8137F(&unk_7FF7B3C93009);//没用
for ( i = 0; i <= 8; ++i )
{
v5 = 0;
sum = 0xF462900 * i;//delta与标准的tea并不同
v3 = i + 1;
do
{
++v5;
arr[i] += sum ^ (arr[v3] + ((arr[v3] >> 5) ^ (16 * arr[v3]))) ^ (sum + key[sum & 3]);
arr[v3] += (sum + key[(sum >> 11) & 3]) ^ (arr[i] + ((arr[i] >> 5) ^ (16 * arr[i])));
sum += 0xF462900;//关键算法和tea区别不大
}
while ( v5 <= 32 ); // 注意这里是从0到32,共33次循环
result = (i + 1);
}
return result;
}
judge函数
解题脚本
#include <stdio.h>
int main()
{
int v3; // [rsp+44h] [rbp+24h]
int i; // [rsp+64h] [rbp+44h]
unsigned int v5; // [rsp+84h] [rbp+64h]
int sum; // [rsp+C4h] [rbp+A4h]
int key[4] = {2233,4455,6677,8899 };
unsigned int arr[10];
arr[0] = 0x1A800BDA;
arr[1] = 0xF7A6219B;
arr[2] = 0x491811D8;
arr[3] = 0xF2013328;
arr[4] = 0x156C365B;
arr[5] = 0x3C6EAAD8;
arr[6] = 0x84D4BF28;
arr[7] = 0xF11A7EE7;
arr[8] = 0x3313B252;
arr[9] = 0xDD9FE279;
for (i = 8; i >=0; i--)
{
v5 = 0;
sum = 0xF462900 * i;
for (int j = 0; j < 33; j++)
sum += 0xF462900;
v3 = i + 1;
do
{
sum -= 0xF462900;
arr[v3] -= (sum + key[(sum >> 11) & 3]) ^ (arr[i] + ((arr[i] >> 5) ^ (16 * arr[i])));
arr[i] -= sum ^ (arr[v3] + ((arr[v3] >> 5) ^ (16 * arr[v3]))) ^ (sum + key[sum & 3]);
++v5;
} while (v5 <= 32);//33次循环
}
for (int k = 0; k < 10; ++k)
{
for (int m = 3; m >= 0; --m)
printf("%c", (arr[k] >> (8 * m)));
}
//HZCTF{hzCtf_94_re666fingcry5641qq}
return 0;
doublegame
进来找不到关键函数,查看strings看到类似迷宫的东西,根据字符串可以定位到关键函数
然后一路用交叉引用往上找就可以找到第一和第二个游戏的函数
snakefun
第一个游戏贪吃蛇
通过patch可以修改得分判断条件,从而进入game2
patch在edit>keypatch(或patch progeam)>patcher
然后会弹出窗口
修改之后在patch program>apply patches to即可保存修改后的程序(记得改一下程序名)
__int64 __fastcall snakefun(int a1, int a2)
{
char *v2; // rdi
__int64 i; // rcx
char v5[32]; // [rsp+0h] [rbp-20h] BYREF
char v6; // [rsp+20h] [rbp+0h] BYREF
char chr[212]; // [rsp+24h] [rbp+4h] BYREF
v2 = &v6;
for ( i = 10i64; i; --i )
{
*(_DWORD *)v2 = -858993460;
v2 += 4;
}
sub_7FF6BD89141A((__int64)&unk_7FF6BD8A90A6);
if ( dword_7FF6BD8A1E60[42 * a2 + 42 * dword_7FF6BD8A0178 + a1 + dword_7FF6BD8A0174] == 2 )
{
++dword_7FF6BD8A0170;
score += 10;
sub_7FF6BD8911A9(7i64);
sub_7FF6BD89130C(0i64, 22i64);
printf(aD_2, (unsigned int)score); // 当前得分
setFood(); // 生成食物
}
else if ( dword_7FF6BD8A1E60[42 * a2 + 42 * dword_7FF6BD8A0178 + a1 + dword_7FF6BD8A0174] == 1
|| dword_7FF6BD8A1E60[42 * a2 + 42 * dword_7FF6BD8A0178 + a1 + dword_7FF6BD8A0174] == 4 )
{
Sleep(0x3E8u);
system("cls");
sub_7FF6BD8911A9(7i64);
sub_7FF6BD89130C(28i64, 8i64);
sub_7FF6BD8911DB(); // 分数还差一点
if ( score > 16 ) // score13371337
sub_7FF6BD89136B(); // jump to game 2
sub_7FF6BD8910E6(); // 保存最高记录失败
sub_7FF6BD89130C(28i64, 11i64);
printf("GAME OVER");
while ( 1 )
{
sub_7FF6BD89130C(28i64, 14i64);
printf(asc_7FF6BD89D320); // 是否再来一局
scanf_0("%c", chr);
if ( chr[0] == 'y' || chr[0] == 'Y' )
break;
if ( chr[0] == 'n' || chr[0] == 'N' )
{
sub_7FF6BD89130C(28i64, 16i64);
exit(0);
}
sub_7FF6BD89130C(28i64, 16i64);
printf(asc_7FF6BD89D3A0); // 选择错误
}
system("cls");
someidea(); // 提示
}
return sub_7FF6BD8913B1(v5, &unk_7FF6BD89CFC0);
}
迷宫
第二个游戏走迷宫救猫
这是迷宫图:
注意这里多了一堵墙
由于patchd的分数要大于16,贪吃蛇游戏吃两个食物撞墙之后即可进入游戏2
游戏2就是走迷宫了,一次输入一个操作符然后回车即可
下面还有一个大循环
关键循环
v24 = 0;
key = 0;
hang = 15;
lie = 0;
shang = 7; // 8行
slie = 20; // 21列
for ( j = 0; j <= 20; ++j )
puts(&Buffer[22 * j]); // 打印迷宫
printf("Please to save the cat!\n");
while ( hang != shang || lie != slie ) // 好像不是走到@的位置
{
chr = getchar();
switch ( chr )
{
case 's':
if ( Buffer[22 * hang + 22 + lie] != '0' )
{
Buffer[22 * hang++ + lie] = ' ';
Buffer[22 * hang + lie] = '@';
}
break;
case 'w':
if ( Buffer[22 * hang - 22 + lie] != '0' )
{
Buffer[22 * hang-- + lie] = ' ';
Buffer[22 * hang + lie] = '@';
}
break;
case 'a':
if ( Buffer[22 * hang - 1 + lie] != '0' )
{
if ( Buffer[22 * hang - 1 + lie] == '*' )// 先到*这里
v7[20] = '0'; //设置墙
Buffer[22 * hang + lie--] = ' ';
Buffer[22 * hang + lie] = '@';
}
break;
default:
if ( chr == 'd' && Buffer[22 * hang + 1 + lie] != '0' )
{
Buffer[22 * hang + lie++] = ' ';
Buffer[22 * hang + lie] = '@';
}
break;
}
system("cls");
for ( j = 0; j <= 20; ++j )
puts(&Buffer[22 * j]); // 打印迷宫
puts(&v19[100 * v24]); // 请救猫
if ( v7[20] == '0' )
{
key = judge(0i64); // 判断key
if ( key == 13376013 )
{
v24 = 1;
v7[20] = ' '; // 打通终点前的墙
Buffer[22 * hang + lie] = ' ';
hang = 15;
lie = 0;
v11[0] = '@'; // 回到起点
++v24;
}
else
{
printf("error");
}
}
}
循环功能:
-
shang,slie是终点;hang,lie是当前位置
-
从@的位置出发,经路径dddssssddwwwwddssddwwwwwwddddssa救到小猫后回到起点;同时小猫所在位置由’*‘变为’ ',且第8行第20列的空格变为0
-
然后要求输入key,key可以根据judge函数反推出来是13371337
-
再从起点出发,经路径dddssssddwwwwddssddwwwwwwddddssaassddddwwwwddwwwwddd走出迷宫
-
所以path:dddssssddwwwwddssddwwwwwwddddssaassddddwwwwddwwwwddd key:13371337
-
根据提示可以求出flag:NSSCTF{811173b05afff098b4e0757962127eac13371337}
提示函数:
L!S!
1.安装BinDiff
这里需要使用到BinDiff,下载地址zynamics.com
点击msi文件即可下载,如果没反应可以右键复制链接用迅雷等下载工具下载
安装时注意路径即可,默认在C盘,可以自己修改,他会自动绑定IDA的路径
这里就不演示BinDiff分析数据库文件了,相对比较麻烦,直接用ida
2.IDA分析
先用ida分别打开ls-original和ls-patched,生成对应数据库文件
然后打开ls-patched.i64文件,选择Edit>Plugins>BinDiff,
他会弹出弹窗,选择Diff Database
再选中ls-original.i64文件
注意: 文件不能保存在中文路径下,必须是英文路径,否则可能会报错找不到数据库文件
Error while diffing: Export of the secondary database failed: File not found: C:\Users\admin\Desktop\网安比赛\GDOUCTF2023\L!s!\ls-original.i64
ida分析之后会出现几个新的窗口,最关键的是这个Matched Functions窗口,绿色代表匹配一致,往下翻在最后可以发现extract_dirs_from_files函数的颜色是黄绿色,双击,按f5反汇编(从文件中提取目录)
反汇编窗口往下翻可以看到关键函数,这个循环的基本功能应该是对lmao数组的数据进行异或,但并不容易的值用于异或的中间值是什么
参考了大佬的wp可以使用cyberchef工具爆破出来
先拖入From Hex,再拖入XOR Brute Force,运行后在输出窗口找到key=4c即可发现flag
flag:HZCTF{b1ndiff_1s_a_us3ful_t00l}