暑假联合训练-week1-Reverse-wp

暑假联合训练-week1-Reverse-wp

2024.7.12 by Shen_Fan

首先是由于某人和我说一道题让他们逆一天,然后把题出难了。

其次是我不知道这玩意还分week1和week2,这下下周没活了

接下来按分数升序依次给出各个题目的wp

find_bomb

本周的签到题,目前已经看到的做法包括但不限于硬扫、动态调试找雷位置然后扫、找到输出flag部分然后复制代码逻辑自己跑、直接动态调试跳到输出部分让他输出flag。这里就不再赘述怎么写。

不幸的是由于打印flag后没有暂停,程序会直接退出,直接卡住了一堆硬扫的人。其实只要自己开个终端在终端里运行就行了

rc4_sp

略微修改后的rc4,通过与标准rc4对比后发现仅仅是rc4初始化部分将密钥放入k盒时做了一点小操作

image-20240712132152049

不难发现就是和k3y数组索引循环+1的部分作了异或,在标准程序中添加修改后直接进行解密即可拿到flag

base64-sp

打开发现是标准base64,直接对密文解码后得到

image-20240712132433502

提示有些函数在main之前运行,对密文按x查找引用找到该函数

image-20240712132554919

可以发现不但给密文作了异或还把表给换了。可以通过动态调试或复制后本地运行的方法获取之后的密文和表,进行base64解码后得到flag。

ezupx

upx壳,但是被小小地modified了,可以通过x64dbg手动脱壳后分析。手动脱壳在网上有很多教程,之前的博客“2023SharkCTF-week3-Reverse-wp”中的shell一题也是相同的壳,已经详细介绍了如何手动脱壳,在此不再赘述

直接进入到脱壳后的加密逻辑部分

通过字符串表查找引用找到主函数。主函数经过逻辑分析后发现flag长度为41且形如flag{XXXXXXXX-XXXXXXXX-XXXXXXX-XXXXXXXX},每个X代表一个16进制数(0-9,A-F)。之后的操作就是把这四个X连在一起变成一个int128送进函数

image-20240712135541252

进入加密函数

image-20240712140609099

不难写出等价的c代码

1
2
3
4
5
6
7
8
9
10
void encry(__uint128_t input) {
int round = 128;
do {
__uint128_t v4 = 0x1145140000000000;
if (input & 1)
v4 = 0xF191981F00000000;
v4 <<= 64;
input = (input >> 1) ^ v4;
}
}

容易发现,当inpuit为偶数时,input会在右移丢失一位后异或上(0x1145140000000000 << 64),使得最高位为0。input为奇数时,input会在右移丢失一位后异或上(0xF191981F00000000 << 64),使得最高位为1,因此有了最高位的值可以知道右移丢失的那一位的值,并还原出异或前的数据,基于此原理写出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   __uint128_t a = 0xF191981F, b = 0x11451400;
a <<= 96, b <<= 96;
__uint128_t mask = 1;
mask <<= 127;
for (int i = 0; i < 128; ++i) {
__int128_t tmp;
if (flag & mask) {
flag ^= a;
tmp = 1;
} else {
flag ^= b;
tmp = 0;
}
flag <<= 1;
flag |= tmp;
}

将密文反向解密后即可得到flag

ezmaze

三维的迷宫,考DFS算法

没啥可说的,把我写的DFS放上来就差不多了吧

yysy这题出题的原理也是通过DFS生成迷宫再DFS求解的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
char maze[10][10][10]; // 这里要改成题目中的迷宫字符串。

char path[1000];
void findpath(int x, int y, int z, int len) {
if (x == 9 && y == 9 && z == 9) {
path[len] = 0;
printf("%s\n", path);
return;
}
maze[x][y][z] = 'X';
int dirs[6][3] = {
{0, 0, 1}, {0, 0, -1},
{0, 1, 0}, {0, -1, 0},
{1, 0, 0}, {-1, 0, 0}
};
for (int i = 0; i < 6; i++) {
int nx = x + dirs[i][0];
int ny = y + dirs[i][1];
int nz = z + dirs[i][2];
if (nx < 0 || nx >= 10 || ny < 0 || ny >= 10 || nz < 0 || nz >= 10) continue;
if (maze[nx][ny][nz] == '.') {
path[len] = "FBLRUD"[i];
findpath(nx, ny, nz, len + 1);
}
}
maze[x][y][z] = '.';
}

然后输入路径就可以得到flag

ezsnake

炒冷饭,以前的题目加了个花指令就放上来了

原题在博客“2023SharkCTF-week2-Reverse-wp”的Hungry_Snake

这里仅对花指令作出一些解释

image-20240712144825014

首先是开门的一个jmp到指令中间,即EB FF机器码使得下一条指令从FF开始解析。所以只需要把EB给nop了就行

image-20240712145007333

这样就正常了

image-20240712145105363

然后是下面一个基于call的花指令,该花指令·执行流程如下:

  1. push rbp 将rbp的值压栈
  2. call $+5 将rip(call的下一条指令的地址)压栈,$+5恰好跳转到下一条指令到pop rbp
  3. pop rbp 将rip的值pop至rbp
  4. add rbp, 8,将rbp的值+8,此时rbp中的地址为地址结尾为175D的那个地方,为pop rbp
  5. push rbp 将rbp的值压栈
  6. retn 返回到栈顶的地址,也就是刚刚压栈的rbp的值,即之前压栈的那个rip再+8
  7. 地址结尾为175D的那个地方反编译得到pop rbp,恢复rbp的值,至此花指令结束根据

根据以上的分析,从最开始的push rbp到最后的pop rbp中间都是没用的花指令,全给他nop了

image-20240712150205470

然后就可以愉快地f5了

image-20240712150242666

之后的就去看原题罢

ezvm

学弟说想要VM,于是就有了VM

(然而直到我写下这句话的现在学弟还在日VM)

先不放wp,下周接着写[狗头]

ezret

感谢佬cia1lo写的wp