Rev

FinalEncrypt

附件给了三个文件,一个是FinalEncrypt,一个是flag.md.enc,一个是Encryption.exe.enc
明显两个加密文件,先看看FinalEncrypt
直接运行,回显提示了用法
运行回显
用 IDA64 反编译一下,关键点在后面
主要逻辑
跟进一下文件加密函数,可以看到是对文件进行了chacha20加密
chacha20加密
直接解密较难操作,考虑用 FinalEncrypt 加密一个文件,然后用同样的随机数种子去生成密钥,再用 FinalEncrypt 解密
首先是我们保持文件的修改时间不变,从压缩包中提取文件
tar --atime-preserve -xvf encrypted.tar.xz
然后用 stat 命令查看文件的访问时间
修改时间查看
得到两文件的修改时间2024-05-08 16:20:22.000000000 +08002024-05-08 16:21:59.000000000 +0800
然后将这两个时间转换成Unix时间戳
Unix时间戳转换
得到17151564221715156519
时间戳
随便生成一个测试文件
echo "test" > test.txt
GDB 调试 FinalEncrypt
gdb --args ./FinalEncrypt -re test.txt
在 time 函数处下断,开始调试

1
2
b time
r

gdb调试
一直按 n 步过,到这条汇编语句
明显这两条语句是调用了寄存器rax的值作为随机数种子
然后调用 srand 函数
set $rax=1715156422 修改 rax 的值
然后按 c 继续执行
修改rax寄存器
获得了Encryption.exe.enc加密的key
507CD82354B1A821ED46A45FAF06D53D0F941C24E085D511F244410B2666056462E126287107616B308DDB193B62036C89C7112C8E713828BC8E8C5079ED221A
key1
同理,我们可以得到flag.md.enc的key
CB0C24457D0BE9695EFDD94C533DE2036E0E6E706C1B06471DBE334DA3195C32F46C8078C7311D068270A10EAE446D0553FA1F628CE4336A39DA852730625324
key2
然后我们用这两个key去解密两个文件
./FinalEncrypt -d Encryption.exe.enc 507CD82354B1A821ED46A45FAF06D53D0F941C24E085D511F244410B2666056462E126287107616B308DDB193B62036C89C7112C8E713828BC8E8C5079ED221A
./FinalEncrypt -d flag.md.enc CB0C24457D0BE9695EFDD94C533DE2036E0E6E706C1B06471DBE334DA3195C32F46C8078C7311D068270A10EAE446D0553FA1F628CE4336A39DA852730625324
解密文件
flag.md文件为密文e07816e1dba1da61536634bef2c3b6346d533cc3b6b834e3beb634c80264143c34e36400bb4daa6902ff643414e3b8344dff6634b8b66db6bbc33834143461ab147e04
看看 Encryption.exe
加密逻辑就是用随机数生成v8的首位,然后进行一系列变换生成一个加密表,最后加密flag
加密表生成
__ROR1__是ida的内置位移函数可以在ida/plugins/defs.h查看__ROR1__定义
ROR1
ROL

有两个参数:(value, int count)
第一个参数为左移的数,第二个参数为左移的位数。
如果第二个参数值为负数,则实际上为循环右移 -count位。
该函数的实现细节为:
先得到value的位数,然后count对位数取模。
如果count值大于0,则先右移-count取模的结果,然后在左移取模的结果,得到的两个数相或,即为循环左移的结果。
如果count值小于0,先左移在右移即可。
举例来说: value = 0110, count = 6
value为4位数, 6 % 4 = 2,
0110先右移4-2=2位,得到0001,然后在左移2位,得到1000,0001 | 1000结果为1001,即循环左移结果为1001。

理解了这个函数,我们可以模拟加密表的生成方式生成加密表,爆破查找正确的加密表来解密flag
因为根据加密结果来看,加密表的值不超过0xff,所以我们可以爆破第一个字节,然后用相同的方法生成剩余的字节
ida生成的伪代码可以直接使用,最后的exp如下

exp

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
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <string.h>
int __ROR1__(unsigned __int8 value,int x){
value= (value>>x) | (value<<(8-x));
return value;
}

int main(){
unsigned int v1; // eax
char v2; // al
char v3; // al
int v4; // ecx
int v5; // eax
int v6; // ecx
int v7; // eax
int v8; // ecx
int v9; // eax
int v10; // ecx
int result; // eax
char v12; // [esp+1Ah] [ebp-Eh]
unsigned __int8 v13; // [esp+1Bh] [ebp-Dh]
char v14; // [esp+1Bh] [ebp-Dh]
char v15; // [esp+1Bh] [ebp-Dh]
int v16; // [esp+1Ch] [ebp-Ch]
unsigned __int8 a1[256]={0};
int i;
for(i=1; i<256; i++){
memset(a1,0,sizeof(a1));
a1[0] = i;
v12 = 1;
v13 = 1;
do
{
if ( v12 >= 0 )
v2 = 0;
else
v2 = 27;
v12 ^= (2 * v12) ^ v2;
v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13;
v15 = (16 * v14) ^ v14;
if ( v15 >= 0 )
v3 = 0;
else
v3 = 9;
v13 = v15 ^ v3;
v4 = v13;
v4 = __ROR1__(v13, 7);
v5 = v4 ^ (v13 ^ *a1);
v6 = v13;
v6 = __ROR1__(v13, 6);
v7 = v6 ^ v5;
v8 = v13;
v8 = __ROR1__(v13, 5);
v9 = v8 ^ v7;
v10 = v13;
v10 = __ROR1__(v13, 4);
result = v10 ^ v9;
a1[v12] = result;
}while ( v12 != 1 );
if(a1[0x44]==0xe0 && a1[0x41]==0x78 && a1[0x53]==0x16 && a1[0x43]==0xe1 && a1[0x54]==0xdb && a1[0x46]==0xa1 ){//用DASCTF前缀验证加密表的正确性
printf("随机种子为%d\n",i);
break;
}
}
for(i=0; i<256; i++){
if(i%16 == 0) printf("\n");
printf("0x%02x ",a1[i]);

}
std::string enc = "e07816e1dba1da61536634bef2c3b6346d533cc3b6b834e3beb634c80264143c34e36400bb4daa6902ff643414e3b8344dff6634b8b66db6bbc33834143461ab147e04";
std::string flag = "";
for (size_t i = 0; i < enc.length(); i += 2) {
std::string byteString = enc.substr(i, 2);
int s1;
std::istringstream(byteString) >> std::hex >> s1;
for(int j=0; j<256; j++){
if(a1[j] == s1){
flag += (char)j;
break;
}
}
}

printf("\n%s\n",flag.c_str());
}
/*
随机种子为152

0x98 0x87 0x8c 0x80 0x09 0x90 0x94 0x3e 0xcb 0xfa 0x9c 0xd0 0x05 0x2c 0x50 0x8d
0x31 0x79 0x32 0x86 0x01 0xa2 0xbc 0x0b 0x56 0x2f 0x59 0x54 0x67 0x5f 0x89 0x3b
0x4c 0x06 0x68 0xdd 0xcd 0xc4 0x0c 0x37 0xcf 0x5e 0x1e 0x0a 0x8a 0x23 0xca 0xee
0xff 0x3c 0xd8 0x38 0xe3 0x6d 0xfe 0x61 0xfc 0xe9 0x7b 0x19 0x10 0xdc 0x49 0x8e
0xf2 0x78 0xd7 0xe1 0xe0 0x95 0xa1 0x5b 0xa9 0xc0 0x2d 0x48 0xd2 0x18 0xd4 0x7f
0xa8 0x2a 0xfb 0x16 0xdb 0x07 0x4a 0xa0 0x91 0x30 0x45 0xc2 0xb1 0xb7 0xa3 0x34
0x2b 0x14 0x51 0x00 0xb8 0xb6 0xc8 0x7e 0xbe 0x02 0xf9 0x84 0xab 0xc7 0x64 0x53
0xaa 0x58 0xbb 0x74 0x69 0x66 0xc3 0x0e 0x47 0x4d 0x21 0xda 0xeb 0x04 0x08 0x29
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
DASCTF{7ou_h@ve_5o1ved_4he_fina1_4ncrypti0n_a4d_y0u_de5erv3_a_7lag}
*/

baby_rop

简单运行一下提示输入flag
fa9edb79613cf276.png
用ida打开发现是一个rop题目,静态看不到什么有用的信息
用动态调试看一下
前面是一些读入和随机数的初始化,不用太关注
运行到这里会有一个flag长度的比较
如果长度为32位,寄存器RDIRSI的值相等,就会继续往下走,否则会直接退出
953b152024865ae2.png
继续往下运行,会得到key的值HDIN2024
72a2d0e63acea6e5.png
同时运行进入 500044函数可以发现对输入进行了异或和加
a23d9dcd8b6284c9.png
继续往下运行可以发现比较
RDI存放输入加密后的结果,RSI存放密文
4086f71d2dea1296.png
如果RDIRSI相等,就会继续往下走,否则会直接退出
所以要每次输入正确8位后,再次调试到这里进行获取下一个8位的密文进行解密
如此循环,直到解密完整个密文

exp

1
2
3
4
5
6
7
8
9
10
enc = [11131674077274786132, 10336780887984666816, 4152170666298469215, 9026692794781294446]
key_chunks = [int.from_bytes(b'HDIN2024', byteorder='little')]

flag = ''
for i in range(len(enc)):
result = int.to_bytes((enc[i] - key_chunks[0]) ^ key_chunks[0], length=8, byteorder='little')
flag += result.decode('utf-8')

print(flag)
# DASCTF{R0p_is_so_cr34y_1n_re!!!}

Realeazy

在刚进入该题目时,首先看到的应该是MainActivity,这里是一个魔改的xtea。这里如果分析一下的话会发现输入的前五个字节根本没有被使用到,再者如果看下控件对应的方法或者xml文件的属性,甚至于弹窗的语句都是有一点不一样的,都可以发现这个Activity是假的。真正的默认启动活动是ProxyActivity
但是假如说这些都没有发现的话,那么这里以java代码来解开这个Tea也是提示性的flag。
28563af370a249f9.png
明文为flag??{Thisafakeflaghhh}
当我们进入ProxyActivity时,会发现主要调用了一个本地方法。
003271b03ee2fab5.png
当我进入so文件库进行查看时,这个文件所有的函数被加了混淆
710b5d603645f992.png
这里的混淆是间接跳转加平坦化。
写脚本或者其他方法去掉混淆后
c42874a7f5fa9ba1.png
前五位作为dword_708的索引进行一个校验,使用z3求解即可,但是这里真的很丑陋。
这部分源码
a10b33e1a8038db7.png

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
from z3 import*
key=[BitVec("%d"% i,16)for i in range(5)]
s=Solver()
array2=[127, 127, 122, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79,
78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 59, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31,
30, 29, 28, 27,
26, 25, 24, 23, 23, 21, 20, 19, 18, 17, 16, 15, 14,
13, 12, 11, 10, 8, 8, 7, 6, 5, 4, 3, 2, 1, 0]
S=Array("S",BitVecSort(16),BitVecSort(16))
for i in range(5):
s.add(key[i]>0)
s.add(key[i]<128)
for i, v in enumerate(array2):
s.add(S[i] == v)
s.add(((Select(S,key[2])*key[2]+Select(S,key[1]))*2^4)==6064)
s.add((Select(S,key[3])+Select(S,key[2])+Select(S,key[1]))^(key[1]+key[2]+key[3])==126)
s.add(Select(S,key[3])*3^Select(S,key[0])==227)
s.add(Select(S,key[3])^1234^Select(S,key[2])-234==-1112)
s.add((key[4]^key[3]-key[0]^2)+100==0)
if s.check()==sat:
result=s.model()
key_values = [result[key[i]].as_long() for i in range(5)]
print(key_values)
else:
print("no")

b41a3012ee79e6a1.png
解出key后会发现key进行一种运算生成了七个大数。%48是为了保证是09
567773bb9d1452c6.png
0f16e9ba8ff979bf.png
然后进行了大数运算最后校验。
由于我没有实现大数除法(真的很抱歉)所以都只是简单的加减,唯一的乘法也只是用来生成加减的数上。
所以只需要从网上搜索大数运算脚本或者在线网站应该都是可以实现的。
解出后24位输入为114514131452125252550000
最终输入为: rea1!114514131452125252550000
Flag{114514131452125252550000}

exp

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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
void init_getString();
void addBigNumbers(char* num1, char* num2, char* result);
void multiplyBigNumbers(char* num1, char* num2, char* result);
void subtract(char* num1, char* num2);
void divideBigNumbers(char* dividend, char* divisor, char* quotient, char* remainder);
void subtractBigNumbers(char* num1, char* num2, char* result);
int main() {
char result[15][50] = { 0 };
char str2[8][50] = { "196953039747318175251969","087842128656209064340878","314775811925536997033147","205664900834427886122056","550317693381954711697325","441206782290845600786234","778139475569172533479503","669028564478063422568412" };
multiplyBigNumbers(str2[6], str2[7], result[11]);
multiplyBigNumbers(str2[3], str2[2], result[2]);
char ccc[50] = "656162506106829140369823576190558316391273550953";
strcpy(result[12], ccc);
subtractBigNumbers(result[12], result[11], result[13]);
addBigNumbers(result[13],str2[5],result[6]);
subtractBigNumbers(result[6],str2[4],result[7]);
subtractBigNumbers(result[7],result[2],result[8]);
addBigNumbers(result[8],str2[1],result[9]);

subtractBigNumbers(result[9],str2[0],result[10]);
printf("%s", result[10]);
}
void reverse(char* str)
{
int len = strlen(str);
int i, j;
for (i = 0, j = len - 1; i < j; i++, j--)
{
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}

void addBigNumbers(char* num1, char* num2, char* result)
{
int carry = 0;
int i = 0;

int len1 = strlen(num1);
int len2 = strlen(num2);
int maxLen = (len1 > len2) ? len1 : len2;

for (i = 0; i < maxLen; i++)
{
int digit1 = (i < len1) ? num1[len1 - 1 - i] - '0' : 0;
int digit2 = (i < len2) ? num2[len2 - 1 - i] - '0' : 0;

int sum = digit1 + digit2 + carry;
result[i] = (sum % 10) + '0';
carry = sum / 10;
}

if (carry > 0)
{
result[i] = carry + '0';
i++;
}

result[i] = '\0';
reverse(result);
}

void multiplyBigNumbers(char* num1, char* num2, char* result)
{
int len1 = strlen(num1);
int len2 = strlen(num2);

int i, j, k;
int* products = (int*)malloc(sizeof(int) * (len1 + len2));

// 初始化数组
for (i = 0; i < len1 + len2; i++)
{
products[i] = 0;
}

// 逐位相乘
for (i = len1 - 1; i >= 0; i--)
{
for (j = len2 - 1; j >= 0; j--)
{
int digit1 = num1[i] - '0';
int digit2 = num2[j] - '0';

int product = digit1 * digit2;

int pos1 = i + j;
int pos2 = i + j + 1;

int sum = product + products[pos2];

products[pos1] += sum / 10;
products[pos2] = sum % 10;
}
}
// 构建结果字符串
int index = 0;
for (i = 0; i < len1 + len2; i++)
{
if (index == 0 && products[i] == 0)
{
continue; // 忽略结果字符串开头的0
}

result[index] = products[i] + '0';
index++;
}

if (index == 0) // 如果结果为0,则将结果设置为"0"
{
result[index++] = '0';
}

result[index] = '\0';

free(products);
reverse(result);
}
void subtractBigNumbers(char* num1, char* num2, char* result) {
int len1 = strlen(num1);
int len2 = strlen(num2);
int diff[100] = { 0 }; // 存储差值的数组,假设结果长度不超过100

// 逐位相减并存储在 diff 数组中
int borrow = 0;
int i;
for (i = 0; i < len1 || i < len2; i++) {
int digit1 = (i < len1) ? num1[len1 - 1 - i] - '0' : 0;
int digit2 = (i < len2) ? num2[len2 - 1 - i] - '0' : 0;
int diffDigit = digit1 - digit2 - borrow;
if (diffDigit < 0) {
diffDigit += 10;
borrow = 1;
}
else {
borrow = 0;
}
diff[i] = diffDigit;
}

// 从 diff 数组中提取结果字符串
while (i > 0 && diff[i - 1] == 0) {
i--;
}

if (i == 0) {
strcpy(result, "0"); // 结果为零的情况
}
else {
int j = 0;
while (i > 0) {
result[j++] = diff[--i] + '0';
}
result[j] = '\0';
}
}
void subtract(char* num1, char* num2) {
int len1 = strlen(num1);
int len2 = strlen(num2);

// 借位标志
int borrow = 0;

for (int i = 0; i < len2; ++i) {
int diff = (num1[i] - borrow) - num2[i];
if (diff < 0) {
diff += 10;
borrow = 1;
}
else {
borrow = 0;
}
num1[i] = diff + '0';
}

// 处理高位的借位
for (int i = len2; i < len1 && borrow; ++i) {
int diff = (num1[i] - borrow) - '0';
if (diff < 0) {
diff += 10;
borrow = 1;
}
else {
borrow = 0;
}
num1[i] = diff + '0';
}
}