Reserve 题目

Last updated on November 5, 2023 pm

reserve

关于校园官网上一些道题目的讲解

base64

下载发现这是一个PE32位文件,运行之后发现要求我们输入一个字符串,随便输入一个,弹出wrong,于是直接打开IDA发现没有main函数,于是先按shift+f12搜索字符串wrong,顺着这个字符串就能够找到调用他的函数,找到调用的主函数后,分析反编译出来的代码,主函数是将我们输入的字符串经过一个函数加密后,与主函数内的一个字符串进行对比,分析函数,发现其为变种的base64编码,写一个脚本,将对比的那串字符串,根据表,转换成六位二进制,再根据ascii转换成字符串,就能得出flag了。

tree

不难发现这个文件是一个ELF64位文件,在linux里运行,没有弹出提示,将这个文件丢进IDA里,先找主函数,分析主函数并结合题目TREE我们不难发现,这个程序首先是读取标准输入流里的32个字符,并按照层序排列的方式构建一棵二叉树,接着有一个验证函数,他以后序遍历的方式来读取二叉树里面的内容,并与他内置的32个两位十六进制数进行比较,具体过程是这样的,先读取第一个,将其赋值给tmp,tmp与内置第一个两位十六进制数进行比较,相同就++check,并继续比较,不同就退出程序,第二次比较,先将tmp与读取的第二个数进行一个虚假的异或(满足异或的特性但有不同),赋值給读取出的数,再赋值给tmp,并比较是否与第二个十六进制数相等,相等就++check,后面就重复这个过程,当检查完32个字符后,全对就会输出Congratulation!,解题方法也很简单,先用C语言复刻这个程序

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include <stdio.h>
#include <stdlib.h>
__int64 __fastcall customXOR(char root, unsigned __int8 tmp)
{
char v2; // dl
char v4; // [rsp+1h] [rbp-1h]
char v5; // [rsp+1h] [rbp-1h]
unsigned __int8 v6; // [rsp+1h] [rbp-1h]

v2 = 16
* ((((8
* ((((4
* ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 8 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 8 | (root & 1 ^ ((tmp & 2) != 0)) & 8 | root & 8) != 0) ^ ((tmp & 0x10) != 0))) & 0x10 | (4 * ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 0x10 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 0x10 | (root & 1 ^ ((tmp & 2) != 0)) & 0x10 | root & 0x10) != 0) ^ ((tmp & 0x20) != 0));
v4 = v2 | (8
* ((((4
* ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 8 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 8 | (root & 1 ^ ((tmp & 2) != 0)) & 8 | root & 8) != 0) ^ ((tmp & 0x10) != 0))) & 0xEF | (4 * ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 0xE7 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 0xE3 | root & 1 ^ ((tmp & 2) != 0) | root & 0xE0;
v5 = (32
* (((v2 & 0x20 | (8
* ((((4
* ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 8 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 8 | (root & 1 ^ ((tmp & 2) != 0)) & 8 | root & 8) != 0) ^ ((tmp & 0x10) != 0))) & 0x20 | (4 * ((((2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 4 | (root & 1 ^ ((tmp & 2) != 0)) & 4 | root & 4) != 0) ^ ((tmp & 8) != 0))) & 0x20 | (2 * ((((root & 1 ^ ((tmp & 2) != 0)) & 2 | root & 2) != 0) ^ ((tmp & 4) != 0))) & 0x20 | (root & 1 ^ ((tmp & 2) != 0)) & 0x20 | root & 0x20) != 0) ^ ((tmp & 0x40) != 0))) | v4 & 0xDF;
v6 = (((tmp >> 7) ^ ((v5 & 0x40) != 0)) << 6) | v5 & 0xBF;
return (((v6 >> 7) ^ tmp & 1) << 7) | v6 & 0x7Fu;
}
// 全局变量,用于存储异或操作的临时结果
unsigned char tmp; //
int check = 0; // 用于验证是否所有节点都已访问

// 定义二叉树节点结构
typedef struct TreeNode {
unsigned char value;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;

// 创建二叉树节点
TreeNode* createNode(unsigned char value) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}

// 后序遍历并进行异或操作
unsigned __int64 __fastcall postorderTraversal(TreeNode* root) {
unsigned __int64 result = 0xFE974D705ED41790LL;
__int64 v3[5];
v3[0] = 0xC14D242A3480E17DLL;
v3[1] = 0x4B4E6AB68979708ELL;
v3[2] = 0xFE974D705ED41790LL;
v3[3] = 0xC5474CF22F3C924FLL;
if (root == NULL) return result;
postorderTraversal(root->left);
postorderTraversal(root->right);
// 进行异或操作
if (check) {
int b = *((unsigned char *)v3 + check);
unsigned char a = customXOR(b,tmp);
root->value= a;
printf("%c", a);
root->value=customXOR(root->value, tmp); // 如果不是第一个节点,就和tmp异或
}
tmp = root->value; // 更新tmp
if (tmp != *((unsigned char*)v3+ check)) { // 如果和隐藏字符串不匹配,就退出程序
printf("不匹配\n");
exit(0);
}
return ++check; // 更新check
}

// 根据层序遍历的数组创建完全二叉树
TreeNode* createTreeByLevelOrder(unsigned char* values, int size) {
if (size == 0) return NULL;
TreeNode** nodes = (TreeNode**)malloc(size * sizeof(TreeNode*));
for (int i = 0; i < size; ++i) {
nodes[i] = createNode(values[i]);
}
for (int i = 0; i < size; ++i) {
if (2 * i + 1 < size) nodes[i]->left = nodes[2 * i + 1];
if (2 * i + 2 < size) nodes[i]->right = nodes[2 * i + 2];
}
TreeNode* root = nodes[0];
free(nodes);
return root;
}

// 主函数
int main() {
unsigned char values[32]="flag{abcdefghijklmnopqrstuvwxyz}";
printf("启动\n");
// 读取32个字符
//for (int i = 0; i < 32; ++i) {
// scanf("%c", &values[i]);
// }
// 创建二叉树
TreeNode* root = createTreeByLevelOrder(values, 32);
// 后序遍历并进行异或操作
postorderTraversal(root);
// 输出结果
/*for (int i = 0; i < 32; ++i) {
printf("%c", values[i]);
}*/
printf("\n");
if (check == 32) { // 如果所有节点都匹配,就输出"Congratulation!"和flag
printf("Congratulation!\n");
}
else
{
printf("wrong");
}

return 0;
}

根据异或运算的结合律,打印出来,在根据二叉树后序遍历的顺序,还原出原来的flag就行了(我这里是直接手画,太丑就不看了),得出flag是flag{15t_A1_u5e_p01n75r5_4_50u1}


Reserve 题目
https://txpoki.github.io/2023/11/05/reserve/
Author
John Doe
Posted on
November 5, 2023
Updated on
November 5, 2023
Licensed under