目录
[GDOUCTF 2023]Check_Your_Luck
[GDOUCTF 2023]Tea
[GDOUCTF 2023]doublegame
[GDOUCTF 2023]Check_Your_Luck
打开题目是一串代码,明显的z3约束器求解
直接上脚本
import z3
from z3 import Real
s = z3.Solver()
v=Real('v')
x=Real('x')
y=Real('y')
w=Real('w')
z=Real('z')
s.add(v * 23 + w * -32 + x * 98 + y * 55 + z * 90 == 333322)
s.add(v * 123 + w * -322 + x * 68 + y * 67 + z * 32 == 707724)
s.add(v * 266 + w * -34 + x * 43 + y * 8 + z * 32 == 1272529)
s.add(v * 343 + w * -352 + x * 58 + y * 65 + z * 5 == 1672457)
s.add(v * 231 + w * -321 + x * 938 + y * 555 + z * 970 == 3372367)
r = s.check()
print(r)
if repr(r) == 'sat':
result = s.model()
print(result)
else:
print('无解')
flag:NSSCTF{4544_123_677_1754_777}
[GDOUCTF 2023]Tea
打开题目,查壳,无壳,分析代码
function2是加密过程,进入查看
可以发现是被魔改的xtea算法,算法进行了33轮,每次sum值不同,且i加密多异或了一个sum ,从function3中可以拿到加密后的数据。sub_140011339函数可以拿到key值
别忘了最后的输出还有一个小操作
写脚本解密
#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]
unsigned int key[4] = {2233,4455,6677,8899 };
unsigned int a[10];
a[0] = 0x1A800BDA;
a[1] = 0xF7A6219B;
a[2] = 0x491811D8;
a[3] = 0xF2013328;
a[4] = 0x156C365B;
a[5] = 0x3C6EAAD8;
a[6] = 0x84D4BF28;
a[7] = 0xF11A7EE7;
a[8] = 0x3313B252;
a[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;
a[v3] -= (sum + key[(sum >> 11) & 3]) ^ (a[i] + ((a[i] >> 5) ^ (16 * a[i])));
a[i] -= sum ^ (a[v3] + ((a[v3] >> 5) ^ (16 * a[v3]))) ^ (sum + key[sum & 3]);
++v5;
} while (v5 <= 32);
}
for (int k = 0; k < 10; ++k)
{
for (int m = 3; m >= 0; m--)
printf("%c", (a[k] >> (8 * m)));
}return 0;
}
注意:数据是unsigned int 型才可以得到flag。
[GDOUCTF 2023]doublegame
看到这个题目很懵,没得头绪,幸好有工作室的好学长指点。
可以看到刚打开是一个贪吃蛇游戏,需要将分数刷到很高(具体多少忘记了这里修改过分数的程序,可以在汇编窗口右键patching-change byte-进行修改操作 再点击editor-patch program-apply patcheds to input file)就可以,修改后何以很轻易的跳过第一个游戏
另外补充一个,怎么让ida显示中文;可以在options-general-string中选择gbk
没有可以右键insert添加,这样遇到中文数据就可以选择所有的中文数据 alt+a显示中文了。
现在进入第二个游戏,第二个游戏是一个走迷宫的游戏
void __noreturn sub_140012CF0()
{
__int64 *v0; // rdi
__int64 i; // rcx
__int64 v2; // [rsp+20h] [rbp+0h] BYREF
_WORD v3[22]; // [rsp+30h] [rbp+10h] BYREF
char v4[44]; // [rsp+5Ch] [rbp+3Ch] BYREF
char v5[44]; // [rsp+88h] [rbp+68h] BYREF
char v6[22]; // [rsp+B4h] [rbp+94h] BYREF
char v7[22]; // [rsp+CAh] [rbp+AAh] BYREF
char v8[44]; // [rsp+E0h] [rbp+C0h] BYREF
char v9[44]; // [rsp+10Ch] [rbp+ECh] BYREF
char v10[44]; // [rsp+138h] [rbp+118h] BYREF
char v11[22]; // [rsp+164h] [rbp+144h] BYREF
char v12[22]; // [rsp+17Ah] [rbp+15Ah] BYREF
char v13[44]; // [rsp+190h] [rbp+170h] BYREF
char v14[44]; // [rsp+1BCh] [rbp+19Ch] BYREF
char v15[24]; // [rsp+1E8h] [rbp+1C8h] BYREF
int j; // [rsp+214h] [rbp+1F4h]
int v17; // [rsp+234h] [rbp+214h]
int v18; // [rsp+254h] [rbp+234h]
int v19; // [rsp+274h] [rbp+254h]
int v20; // [rsp+294h] [rbp+274h]
char v21[100]; // [rsp+2C0h] [rbp+2A0h] BYREF
char v22[100]; // [rsp+324h] [rbp+304h] BYREF
_BYTE v23[828]; // [rsp+388h] [rbp+368h] BYREF
char input; // [rsp+6C4h] [rbp+6A4h]
int v25; // [rsp+6E4h] [rbp+6C4h]
int v26; // [rsp+704h] [rbp+6E4h]
v0 = &v2;
for ( i = 448i64; i; --i )
{
*v0 = -858993460;
v0 = (v0 + 4);
}
sub_14001141A(&unk_1400290A6);
strcpy(v3, "000000000000000000000");
strcpy(&v3[11], "0 0 0 0 0 0 0");
strcpy(v4, "0 0 0 00000 00000 0 0");
strcpy(&v4[22], "0 0 0 0");
strcpy(v5, "0 000 000 0 000 0 0 0");
strcpy(&v5[22], "0 0 0 0 0 0 0 0");
strcpy(v6, "0 0 0 00000 000 000 0");
strcpy(v7, "0 0 0 0 0 0 ");
strcpy(v8, "0 000 0 0 000 0 0 0 0");
strcpy(&v8[22], "0 0 0 0 0 0 0 0 0");
strcpy(v9, "0 00000 000 000 0 0 0");
strcpy(&v9[22], "0 0 0 0 0");
strcpy(v10, "000 0 0 0 000 0 0 0 0");
strcpy(&v10[22], "0 0 0 0 0 0 * 0 0 0 0");
strcpy(v11, "0 0000000 0 000 00000");
strcpy(v12, "@ 0 0 0");
strcpy(v13, "0 0 0 0 0 00000000000");
strcpy(&v13[22], "0 0 0 0 0");
strcpy(v14, "000 0 00000 0 000 000");
strcpy(&v14[22], "0 0 0 0 0");
strcpy(v15, "000000000000000000000");
v12[4] = '0'; // 【12】【4】这里被墙堵住
strcpy(v21, "Please to save the cat!");
memset(&v21[24], 0, 0x4Cui64);
strcpy(v22, "the score is saving cat's key!\n");
memset(&v22[32], 0, 0x44ui64);
qmemcpy(
v23,
"oh,the door open!\n恭喜你完成了第一个任务!请从头用最快的方式把猫带出去\n",
0x47ui64);
memset(&v23[71], 0, 729);
printf("path\n");
v25 = 0;
v26 = 0;
v17 = 15;
v18 = 0;
v19 = 7;
v20 = 20;
for ( j = 0; j <= 20; ++j )
puts(&v3[11 * j]);
printf("Please to save the cat!\n"); // 先救猫猫再逃出迷宫
while ( v17 != v19 || v18 != v20 ) // 要先到达猫猫的位置
{
input = getchar();
switch ( input ) // 要从【15】【0】到【7】【20】
{
case 's': // 下
if ( *(&v3[11 * v17 + 11] + v18) != '0' )
{
*(&v3[11 * v17++] + v18) = 32;
*(&v3[11 * v17] + v18) = 64;
}
break;
case 'w': // 上
if ( *(&v3[11 * v17 - 11] + v18) != '0' )
{
*(&v3[11 * v17--] + v18) = 32;
*(&v3[11 * v17] + v18) = 64;
}
break;
case 'a': // 左
if ( *(&v3[11 * v17 - 1] + v18 + 1) != '0' )
{
if ( *(&v3[11 * v17 - 1] + v18 + 1) == '*' )
v7[20] = 48;
*(&v3[11 * v17] + v18--) = ' ';
*(&v3[11 * v17] + v18) = '@';
}
break;
default:
if ( input == 'd' && *(&v3[11 * v17] + v18 + 1) != '0' )// 右
{
*(&v3[11 * v17] + v18++) = ' ';
*(&v3[11 * v17] + v18) = '@';
}
break;
}
system("cls");
for ( j = 0; j <= 20; ++j )
puts(&v3[11 * j]); // 输出路径
puts(&v21[100 * v25]);
if ( v7[20] == '0' ) // 救到猫后增加一个墙
{
v26 = sub_140011433(0i64);
if ( v26 == 13376013 ) // 得到key等于13376013
{
v25 = 1;
v7[20] = ' '; // 输入正确的Key墙消失
*(&v3[11 * v17] + v18) = ' ';
v17 = 15; // 回到原点
v18 = 0;
v12[0] = '@';
++v25;
}
else
{
printf("error");
}
}
}
system("cls");
Sleep(0x1F4u);
Sleep(0xBB8u);
function();
exit(0);
}
迷宫先是要去救猫,救到猫时出现墙堵住路,输入正确的key,墙消失,回到原点,再逃出迷宫
可以看到最后function函数里显示flag为
The flag is HZCTF{md5(path)+score}
所以可以找到最后的路径为dddssssddwwwwddssddwwwwwwddddssaassddddwwwwddwwwwddd
再进行32位小写md5+分数即为flag
NSSCTF{811173b05afff098b4e0757962127eac13371337}