2021数字中国虎符CTF Re题复现

2021虎符CTF Re

1.redemption_code

分析:

​ 这题的重点在于理解验证兑换码函数 server_check_redemption_code 的算法

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
28
29
30
int server_check_redemption_code(char *str, char *input)
{
int len1 = strlen(str);
int len2 = strlen(input);
int tmp = len2;
char s[14*256]={0};
int maxsize = tmp << 10;
s[input[0]] = 1;
int i,j;
int v4;
for (i = 1; i < tmp; ++i)
{
for (j = 0; j < 256; ++j)
{
if (j != input[i])
s[256 * i + j] = s[256 * v4 + j];
else
s[256 * i + j] = i + 1;
}
v4 = s[256 * v4 + input[i]];
}
int v7 = 0,k;
for (k = 0; k < len1; ++k)
{
v7 = s[256 * v7 + str[k]]; //能在str中找到与flag[i]相同的字符就移向下一行继续找
if (v7 == tmp) //str中包含flag的全部字符
return k - tmp + 1; // =7
}
return -1;
}

​ 用图来解释一下

​ s数组相当于一个14*256的二维数组(输入长度得是14),函数内双重循环代表在以 flag[i] 为下标的地方存储当前的 i+1 值(第几个字符)

str1[] = “Ninja Must Die 3 Is A Cruel Game, So Hard For Me”
str2[] = “I Love Ninja Must Die 3. Beautiful Art And Motive Operation Is Creative.”

​ pre 函数检测 str1,根据check函数最后的循环返回结果不能是 -1,确定 flag 字符串在 str1中

​ 检测 str2 的时候因为返回值要是7,所以 flag 结尾字符是 str2[20] = ‘e’,flag = “Ninja Must Die”

验证:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int server_check_redemption_code(char *str, char *input)
{
int len1 = strlen(str);
int len2 = strlen(input);
int tmp = len2;
char s[14*256]={0};
int maxsize = tmp << 10;
s[input[0]] = 1;
int i,j;
int v4;
for (i = 1; i < tmp; ++i)
{
for (j = 0; j < 256; ++j)
{
if (j != input[i])
s[256 * i + j] = s[256 * v4 + j];
else
s[256 * i + j] = i + 1;
}
v4 = s[256 * v4 + input[i]];
}
int v7 = 0,k;
for (k = 0; k < len1; ++k)
{
v7 = s[256 * v7 + str[k]];
if (v7 == tmp)
return k - tmp + 1; // =7
}
return -1;
}

int main()
{
char str1[]="Ninja Must Die 3 Is A Cruel Game, So Hard For Me";
char str2[]="I Love Ninja Must Die 3. Beautiful Art And Motive Operation Is Creative.";
char flag[]="Ninja Must Die";
printf("%d",server_check_redemption_code(str1,flag));
printf("%d",server_check_redemption_code(str2,flag));
system("pause");
return 0;
}

2.GoEncrypt

分析:

​ go 语言逆向,main_main 函数反汇编后可以看到有个 main_check ,main_NewCipher ,两个 Encrypt 加密,最后 internal_bytealg_Equal 比较
​ check 里面有 flag 的格式

^flag{([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})}

​ 可以构造 flag 进行调试

flag{abababab-abab-abab-abab-abababababab}

​ 图中的函数将输入转为16进制,如 0xabababababababababababababababab,之后再分割成4个16进制数
​ 加密的函数根据特点可以知道是 XTEA 加密

​ 调试到 encrypt 函数时可以发现两个 encrypt 分别对两个4位的16进制数进行加密,密钥如下

​ 之后可以在 internal_bytealg_Equal 函数内找到 flag 的密文

​ 前面那些看起来像是汇编代码的实际调试时并不执行,关键只在这一部分

脚本:

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
28
#include<stdio.h>
#include<stdlib.h>
#define uint32_t unsigned int

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x12345678, sum = delta * num_rounds;
for (i = 0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0] = v0; v[1] = v1;
}

int main()
{
//uint32_t v1[2]={0x0EC311F0,0x45C79AF3 };
uint32_t v[2] = { 0xedf5d910,0x542702cb};
uint32_t const k[4] = { 0x10203,0x4050607,0x8090a0b,0xc0d0e0f };
unsigned int r = 32;//num_rounds建议取值为32
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
decipher(r, v, k);
printf("解密后的数据:0x%x 0x%x\n", v[0], v[1]);
return 0;
}

3.Crackme

​ 反汇编出来 500 多行,要调试吐了。。。

分析:

​ 先是输入一个长度为17的字符串,然后输入一个数字,对输入的数字进行运算后再判断

​ 虽然这个函数 ida 提示说结果可能错误,但是实际运算跟反汇编出来的是一样的(比赛当天因为这个卡了好久,一直在试图调试出正确的运算过程)

​ 想要得到这个数字可以采取爆破的方法,多次输入并调试来缩小范围

​ 之后把第一个输入的字符串分为前7个和后10个,分别异或加密

脚本:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<stdio.h>
#include<math.h>
double cal(double a1,double a2)
{
double temp = pow(a1, a2 - 1.0);
double result = temp / exp(a1);
return result;
}

int main()
{
int v94 = 0.0;
for(v94=90000;v94<100000000;v94++)
{
double v15 = 0.0;
double v16 = 0.0;
double v17 = 0.0;
double v18 = (double)((int)v94 / 12379) + 1.0;
do
{
v16 = v16 + cal(v17, v18) * 0.001;
v17 = v17 + 0.001;
}
while ( v17 <= 100.0 );
double v19 = (int)(v16 + v16 + 3.0);
double v20 = 0.0;
double v21 = (double)(v94 % 12379) + 1.0;
do
{
v15 = v15 + cal(v20, v21) * 0.001;
v20 = v20 + 0.001;
}
while ( v20 <= 100.0 );
if(v19 == 0x13b03 && (int)(v15 + v15 + 3.0) == 0x5a2)
{
printf("%d",v94);
break;
}
}
system("pause");
return 0;
}//99038
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
a7 = [0x08, 0x4D, 0x59, 0x06, 0x73, 0x02, 0x40]
number = "99038198076198076198076198076198076"
#输入数字乘2接到后面
k = [0xe0,0x95,0xba,0x60,0xc9,0x66,0x2a,0x24,0xb2,0x36]
a10 = [0xB2, 0xD6, 0x8E, 0x3F, 0xAA, 0x14, 0x53, 0x54, 0xC6, 0x06]
flag = []
for i in range(7):
c = chr(a7[i] ^ ord(number[i]))
flag.append(c)

flag1 = ''.join(flag)
print(flag1)
#1ti5K3y
for i in range(10):
d = chr(k[i] ^ a10[i])
flag.append(d)

flag_last = ''.join(flag)
print(flag_last)
#1ti5K3yRC4_crypt0
#后面那个原来是RC4吗
作者

0wl

发布于

2021-04-05

更新于

2021-11-16

许可协议

评论