xctf-writeup-app3

app3 解题报告

下载得到一个.ab文件

这个一个备份数据文件,解析工具可以使用abe下载链接

解析

1
java -jar .\abe.jar unpack .\app3.ab app3.tar

得到压缩文件,解压缩之后

1586360239520

打开apk没发现什么有用的东西

JEB分析:

MainActivity onCreate方法和onClick方法

1586360618484

1586360754188

可以看到这里面有输入name和password之后出现的界面,只是单纯的启动anotherActivity

如下:

1586360836532

(略智障)那么应该不在这里,结合onCreate方法里面的信息,以及调用a方法,以及附带的2个db文件,合理猜测使用了加密数据库,信息存储在数据库里,接下来分析a方法:

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
private void a() {
SQLiteDatabase.loadLibs(this);
// 这个方法大概就是实例化一个SQLiteOpenHelper类 新建数据库并实例化Demo.db
this.b = new a_class(this, "Demo.db", null, 1);
// 实例化一个contentValues 类,并放入key value
ContentValues v0 = new ContentValues();
v0.put("name", "Stranger");
v0.put("password", Integer.valueOf(123456));
// 实例化一个对象
a_a_class v1 = new a_a_class();
String v2 = v1.a(v0.getAsString("name"), v0.getAsString("password")); //v2 = stra1234
// 调用b方法得到v3
String v3 = v1.b(v2, v0.getAsString("password"));
// 传入的是密钥
this.a = this.b.getWritableDatabase(v1.a(v2 + v3).substring(0, 7));
this.a.insert("TencentMicrMsg", null, v0);
}

public class a_a_class {
private String a;

public a_a_class() {
this.a = "yaphetshan";
}

public String a(String arg4, String arg5) {
String v0 = arg4.substring(0, 4);
String v1 = arg5.substring(0, 4);
return v0 + v1;
}

public String a(String arg3) {
new a_b_class();
return a_b_class.b(arg3 + this.a);
}

public String b(String arg2, String arg3) {
new a_b_class();
return a_b_class.a(arg2);
}
}

getWritableDatabase 获取读写对象时候附带密码 ,可以知道加密密钥即是

之后,分析加密算法:

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
package com.example.yaphetshan.tencentwelcome.a;

import java.security.MessageDigest;

/* compiled from: SHA1Manager */
public class b {
public static final String a(String str) {
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] bytes = str.getBytes();
// 信息摘要算法为md5
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(bytes);
// 以下代码将字节转换为16进制的字符
char[] cArr2 = new char[(16 * 2)];
int i = 0;
for (byte b : instance.digest()) {
int i2 = i + 1;
cArr2[i] = cArr[(b >>> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception e) {
return null;
}
}

public static final String b(String str) {
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
byte[] bytes = str.getBytes();
MessageDigest instance = MessageDigest.getInstance("SHA-1");
instance.update(bytes);
char[] cArr2 = new char[(r4 * 2)];
int i = 0;
for (byte b : instance.digest()) {
int i2 = i + 1;
cArr2[i] = cArr[(b >>> 4) & 15];
i = i2 + 1;
cArr2[i2] = cArr[b & 15];
}
return new String(cArr2);
} catch (Exception e) {
return null;
}
}
}

可以看到一个是md5算法,一个是sha1算法,知道算法之后,写exp如下:

1
2
3
4
5
6
7
8
9
10
11
v2 = "Stra1234"
# String v3 = v1.b(v2, v0.getAsString("password")); = b.a(v2)
h = md5()
h.update(v2.encode(encoding='utf-8'))
v3 = h.hexdigest()
# v1.a(v2 + v3).substring(0, 7)
str = v2 + v3 + "yaphetshan"
h1 = sha1()
h1.update(str.encode(encoding='utf-8'))
ans = h1.hexdigest()[0:7]
print(ans)

得到结果ae56f99

使用密钥解密,得到VGN0ZntIM2xsMF9Eb19ZMHVfTG92M19UZW5jM250IX0=

base64解密得到flagTctf{H3ll0_Do_Y0u_Lov3_Tenc3nt!}

总结

这个题目主要是应对Android的备份问题,在AndroidManifest.xmlallowBackup属性设置为true之后,用户可以通过adb backupadb restore对应用数据进行备份和恢复。

而对于一些聊天类app,聊天记录等信息都是在本地存放,所以加解密算法也应该保存在本地,通过这种方式,可以分析出密码,从而解出聊天记录数据。也就是在这道题目中,我们所做的事。包括:

  1. 解压缩ab
  2. 获取关键信息
  3. 分析加密算法写出对应密钥
  4. 利用密钥打开数据库获取信息

补充

0.1 MD5算法

一种被广泛使用的密码hash算法,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

MD5是输入不定长度信息,输出固定长度128-bits的算法。经过程序流程,生成四个32位数据,最后联合起来成为一个128-bits的hash值

img

一个MD5运算— 由类似的64次循环构成,分成4组16次。F 一个非线性函数;一个函数运算一次。Mi 表示一个 32-bits 的输入数据,Ki 表示一个 32-bits 常数,用来完成每次不同的计算。

0.1.1 算法原理

0.1.1.1 数据填充

填充待加密的消息,使得长度length满足 $(length = 448 )mod 512$ $(byte = 56)mod 64$

因此消息长度扩展为$N * 512 + 448$位,也即$N * 64 + 56$字节

填充方法是在数据后面添加一个1其余位填0

0.1.1.1 添加长度

在第一步得到的结果后,添加64位的数据表示填充前的信息长度length,如果数据长度大于$2^{64}$,取64,最终信息长度为 $(N+1) * 512$位

0.1.1.1 初始化变量
1
2
3
4
A=0x01234567;
B=0x89abcdef;
C=0xfedcba98;
D=0x76543210;
0.1.1.2 数据处理

以512位为1组,处理函数如下,输入为3个32位的数据,输出为1个32位的数据

1
2
3
4
F(x, y, z) = (x & y) | ((~x) & z);
G(x, y, z) = (x & y) | (y & (~z));
H(x, y, z) = x ^ y ^ z;
I(x, y, z) = y ^ (x | (~z));

对进入循环的512位消息分组的16个32位数据分别进行如下操作:

使用临时变量a,b,c,d中的三个经F,G,H,I变换后的结果与第4个相加,再加上32位字和一个32位字的加法常数,并将所得值循环左移若干位,最后将所得结果加上a,b,c,d之一,并返回A,B,C,D,由此完成一次循环。

0.1.1.3 算法特征
  1. 数据长度初始填充到448位,填充方法为补10*

  2. 会增加一个64位代表长度

  3. 会使用初始化的4个常量数据

  4. 输出是128位

  5. 输入的64个常量

    1
    { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 };

0.1.2 算法逆向特征

0.1.2.1 MD5Init()

这一步作用是初始化ABCD

0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476

0.1.2.2 MD5Update()

FF(a ,b ,c ,d ,Mj ,s ,ti ) 操作为 a = b + ( (a + F(b,c,d) + Mj + ti) << s) //循环位移

GG(a ,b ,c ,d ,Mj ,s ,ti ) 操作为 a = b + ( (a + G(b,c,d) + Mj + ti) << s)

HH(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + H(b,c,d) + Mj + ti) << s)

II(a ,b ,c ,d ,Mj ,s ,ti) 操作为 a = b + ( (a + I(b,c,d) + Mj + ti) << s)

四轮64步

0.1.2.3 MD5Final()

所有这些完成之后,将a、b、c、d分别在原来基础上再加上A、B、C、D。 即a = a + A,b = b + B,c = c + C,d = d + D

0.1.2.4 代码
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
/* 伪c代码逆向主特征 */

void __fastcall MD5Transform(unsigned int *state, unsigned __int8 *block)
{
unsigned int *v2; // r4@1
unsigned int v3; // r5@1
unsigned int v4; // r6@1
unsigned int v5; // ST14_4@1
unsigned int v6; // r7@1
int v7; // r1@1
int v8; // r1@1
int v9; // r2@1
------skip--------
int v132; // r0@1
int v133; // r3@1
unsigned int v134; // r5@1
unsigned int v135; // r3@1
unsigned int x[64]; // [sp+48h] [bp-118h]@1

v2 = state;
v3 = state[1];
v4 = *state;
v5 = state[2];
v6 = state[3];
MD5Decode(x, block, 0x40u);
v7 = __ROR4__(x[0] - 680876936 + v4 + (v6 & ~v3 | v5 & v3), 25);
v8 = v7 + v3;
v9 = __ROR4__(x[1] - 389564586 + v6 + (v5 & ~v8 | v3 & v8), 20);
v10 = v9 + v8;
v11 = __ROR4__(x[2] + 606105819 + v5 + (v8 & v10 | v3 & ~v10), 15);
v12 = v11 + v10;
v13 = __ROR4__(v3 + x[3] - 1044525330 + (v10 & v12 | v8 & ~v12), 10);
v14 = v13 + v12;
v15 = __ROR4__(v8 + x[4] - 176418897 + (v12 & v14 | v10 & ~v14), 25);
v16 = v15 + v14;
v17 = __ROR4__(v10 + x[5] + 1200080426 + (v14 & v16 | v12 & ~v16), 20);
v18 = v17 + v16;
v19 = __ROR4__(v12 + x[6] - 1473231341 + (v16 & v18 | v14 & ~v18), 15);
v20 = v19 + v18;
v21 = __ROR4__(v14 + x[7] - 45705983 + (v18 & v20 | v16 & ~v20), 10);
v22 = v21 + v20;
v23 = __ROR4__(v16 + x[8] + 1770035416 + (v20 & v22 | v18 & ~v22), 25);
v24 = v23 + v22;
v25 = __ROR4__(v18 + x[9] - 1958414417 + (v22 & v24 | v20 & ~v24), 20);
v26 = v25 + v24;
v27 = __ROR4__(v20 + x[10] - 42063 + (v24 & v26 | v22 & ~v26), 15);
v28 = v27 + v26;
v29 = __ROR4__(v22 + x[11] - 1990404162 + (v26 & v28 | v24 & ~v28), 10);
v30 = v29 + v28;
v31 = __ROR4__(v24 + x[12] + 1804603682 + (v28 & v30 | v26 & ~v30), 25);
v32 = v31 + v30;
v33 = __ROR4__(x[13] - 40341101 + v26 + (v30 & v32 | v28 & ~v32), 20);
v34 = v33 + v31 + v30;
v35 = __ROR4__(x[14] - 1502002290 + v28 + ((v31 + v30) & v34 | ~v34 & v30), 15);
v36 = v35 + v34;
v37 = __ROR4__(x[15] + 1236535329 + v30 + (v34 & v36 | ~v36 & (v31 + v30)), 10);
v38 = v37 + v36;
v39 = __ROR4__(x[1] - 165796510 + v32 + (~v34 & v36 | v34 & v38), 27);
v40 = v39 + v38;
v41 = __ROR4__(x[6] - 1069501632 + v34 + (~v36 & v38 | v36 & v40), 23);
v42 = v41 + v40;
v43 = __ROR4__(v36 + x[11] + 643717713 + (v40 & ~v38 | v38 & v42), 18);
v44 = v43 + v42;
v45 = __ROR4__(v38 + x[0] - 373897302 + (v42 & ~v40 | v40 & v44), 12);
v46 = v45 + v44;
v47 = __ROR4__(v40 + x[5] - 701558691 + (v44 & ~v42 | v42 & v46), 27);
v48 = v47 + v46;
v49 = __ROR4__(v42 + x[10] + 38016083 + (v46 & ~v44 | v44 & v48), 23);
v50 = v49 + v48;
v51 = __ROR4__(v44 + x[15] - 660478335 + (v48 & ~v46 | v46 & v50), 18);
v52 = v51 + v50;
v53 = __ROR4__(v46 + x[4] - 405537848 + (v50 & ~v48 | v48 & v52), 12);
v54 = v53 + v52;
v55 = __ROR4__(v48 + x[9] + 568446438 + (v52 & ~v50 | v50 & v54), 27);
v56 = v55 + v54;
v57 = __ROR4__(v50 + x[14] - 1019803690 + (v54 & ~v52 | v52 & v56), 23);
v58 = v57 + v56;
v59 = __ROR4__(v52 + x[3] - 187363961 + (v56 & ~v54 | v54 & v58), 18);
v60 = v59 + v58;
v61 = __ROR4__(v54 + x[8] + 1163531501 + (v58 & ~v56 | v56 & v60), 12);
v62 = v61 + v60;
v63 = __ROR4__(v56 + x[13] - 1444681467 + (v60 & ~v58 | v58 & v62), 27);
v64 = v63 + v62;
v65 = __ROR4__(v58 + x[2] - 51403784 + (v62 & ~v60 | v60 & v64), 23);
v66 = v65 + v64;
v67 = __ROR4__(x[7] + 1735328473 + v60 + (v64 & ~v62 | v62 & v66), 18);
v68 = v67 + v66;
v69 = __ROR4__(x[12] - 1926607734 + v62 + (v66 & ~v64 | v64 & v68), 12);
v70 = v69 + v68;
v71 = __ROR4__(x[5] - 378558 + v64 + (v68 ^ v66 ^ v70), 28);
v72 = v71 + v70;
v73 = __ROR4__(x[8] - 2022574463 + v66 + (v70 ^ v68 ^ v72), 21);
v74 = v73 + v72;
v75 = __ROR4__(x[11] + 1839030562 + v68 + (v72 ^ v70 ^ v74), 16);
v76 = v75 + v74;
v77 = __ROR4__(x[14] - 35309556 + v70 + (v74 ^ v72 ^ v76), 9);
v78 = v77 + v76;
v79 = __ROR4__((v76 ^ v74 ^ v78) + x[1] - 1530992060 + v72, 28);
v80 = v79 + v78;
v81 = __ROR4__((v78 ^ v76 ^ v80) + x[4] + 1272893353 + v74, 21);
v82 = v81 + v80;
v83 = __ROR4__((v80 ^ v78 ^ v82) + x[7] - 155497632 + v76, 16);
v84 = v83 + v82;
v85 = __ROR4__((v82 ^ v80 ^ v84) + x[10] - 1094730640 + v78, 9);
v86 = v85 + v84;
v87 = __ROR4__((v84 ^ v82 ^ v86) + x[13] + 681279174 + v80, 28);
v88 = v87 + v86;
v89 = __ROR4__((v86 ^ v84 ^ v88) + x[0] - 358537222 + v82, 21);
v90 = v89 + v88;
v91 = __ROR4__((v88 ^ v86 ^ v90) + x[3] - 722521979 + v84, 16);
v92 = v91 + v90;
v93 = __ROR4__((v90 ^ v88 ^ v92) + x[6] + 76029189 + v86, 9);
v94 = v93 + v92;
v95 = __ROR4__((v92 ^ v90 ^ v94) + x[9] - 640364487 + v88, 28);
v96 = v95 + v94;
v97 = __ROR4__((v94 ^ v92 ^ v96) + x[12] - 421815835 + v90, 21);
v98 = v97 + v96;
v99 = __ROR4__(v92 + x[15] + 530742520 + (v96 ^ v94 ^ v98), 16);
v100 = v99 + v98;
v101 = __ROR4__(v94 + x[2] - 995338651 + (v98 ^ v96 ^ v100), 9);
v102 = v101 + v100;
v103 = __ROR4__(x[0] - 198630844 + v96 + ((~v98 | v102) ^ v100), 26);
v104 = v103 + v102;
v105 = __ROR4__(x[7] + 1126891415 + v98 + ((~v100 | v104) ^ v102), 22);
v106 = v105 + v104;
v107 = __ROR4__(x[14] - 1416354905 + v100 + ((~v102 | v106) ^ v104), 17);
v108 = v107 + v106;
v109 = __ROR4__(v102 + x[5] - 57434055 + ((~v104 | v108) ^ v106), 11);
v110 = v109 + v108;
v111 = __ROR4__(x[12] + 1700485571 + v104 + ((~v106 | v110) ^ v108), 26);
v112 = v111 + v110;
v113 = __ROR4__(x[3] - 1894986606 + v106 + ((~v108 | v112) ^ v110), 22);
v114 = v113 + v112;
v115 = __ROR4__(x[10] - 1051523 + v108 + ((~v110 | v114) ^ v112), 17);
v116 = v115 + v114;
v117 = __ROR4__(x[1] - 2054922799 + v110 + ((~v112 | v116) ^ v114), 11);
v118 = v117 + v116;
v119 = __ROR4__(x[8] + 1873313359 + v112 + ((~v114 | v118) ^ v116), 26);
v120 = v119 + v118;
v121 = __ROR4__(x[15] - 30611744 + v114 + ((~v116 | v120) ^ v118), 22);
v122 = v121 + v120;
v123 = __ROR4__(x[6] - 1560198380 + v116 + ((~v118 | v122) ^ v120), 17);
v124 = v123 + v122;
v125 = __ROR4__(x[13] + 1309151649 + v118 + ((~v120 | v124) ^ v122), 11);
v126 = v125 + v124;
v127 = __ROR4__(x[4] - 145523070 + v120 + ((~v122 | v126) ^ v124), 26);
v128 = v127 + v126;
v129 = __ROR4__(x[11] - 1120210379 + v122 + ((~v124 | v128) ^ v126), 22);
v130 = v129 + v128;
v131 = __ROR4__(x[2] + 718787259 + v124 + ((~v126 | v130) ^ v128), 17);
v132 = v131 + v130;
v133 = __ROR4__(x[9] - 343485551 + v126 + ((~v128 | v132) ^ v130), 11);
*v2 += v128;
v134 = v2[3];
v2[1] += v132 + v133;
v135 = v2[2];
v2[3] = v134 + v130;
v2[2] = v135 + v132;
}
1
2
3
4
5
6
7
# python 代码
import hashlib

m = hashlib.md5()
m.update("123".encode("utf-8"))
result = m.hexdigest()
print (result)

0.2 SHA1

0.2.1 算法原理

由美国国家安全局设计,主要适用于数字签名标准里面定义的数字签名算法。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。

0.2.1.1 分组

对于任意长度的明文,SHA1的明文分组过程与MD5相类似,首先需要对明文添加位数,使明文总长度为448(mod512)位。在明文后添加位的方法是第一个添加位是l,其余都是0。然后将真正明文的长度(没有添加位以前的明文长度)以64位表示,附加于前面已添加过位的明文后,此时的明文长度正好是512位的倍数。与MD5不同的是SHA1的原始报文长度不能超过2的64次方,另外SHA1的明文长度从低位开始填充。

0.2.1.2 分组

经过添加位数处理的明文,其长度正好为512位的整数倍,然后按512位的长度进行分组(block),可以划分成L份明文分组,我们用Y0,Y1,……YL-1表示这些明文分组。对于每一个明文分组,都要重复反复的处理,这些与MD5是相同的。

对于512位的明文分组,SHA1将其再分成16份子明文分组(sub-block),每份子明文分组为32位,我们使用M[k](k= 0, 1,……15)来表示这16份子明文分组。

0.2.1.3 扩充

将这16份子明文分组扩充到80份子明文分组,我们记为W[k](k= 0, 1,……79),扩充的方法如下。

1
2
3
> W t = M t , 当0≤t≤15
> W t = ( W t-3 ⊕ W t-8⊕ W t-14⊕ W t-16 ) <<< 1, 当16≤t≤79
>
0.2.1.3 运算

![SHA-1 - Wikipedia](xctf-writeup-app3/SHA-1 - Wikipedia.png)

SHA1有4轮运算,每一轮包括20个步骤,一共80步,当第1轮运算中的第1步骤开始处理时,A、B、C、D、E五个链接变量中的值先赋值到另外5个记录单元A′,B′,C′,D′,E′中。这5个值将保留,用于在第4轮的最后一个步骤完成之后与链接变量A,B,C,D,E进行求和操作。

4轮运算,共80个步骤使用同一个操作程序

1
2
> A,B,C,D,E←[(A<<<5)+ ft(B,C,D)+E+Wt+Kt],A,(B<<<30),C,D
>

其中 ft(B,C,D)为逻辑函数,Wt为子明文分组W[t],Kt为固定常数。这个操作程序的意义为:

a、将[(A<<<5)+ ft(B,C,D)+E+Wt+Kt]的结果赋值给链接变量A;

b、将链接变量A初始值赋值给链接变量B;

c、将链接变量B初始值循环左移30位赋值给链接变量C;

d、将链接变量C初始值赋值给链接变量D;

e、将链接变量D初始值赋值给链接变量E。

0.2.1 算法特征

  1. 消息填充和MD5一样,512位为一组,然后16组x32位,然后扩充位80组x32位,进行4*20次运算

  2. 使用该函数进行计算A,B,C,D,E←[(A<<5)+ ft(B,C,D)+E+Wt+Kt],A,(B<<30),C,D,f函数伪代码在下方

    1
    2
    3
    4
    #define F0(x,y,z) ((x & y) | ((~x) & z))
    #define F1(x,y,z) (x ^ y ^ z)
    #define F2(x,y,z) ((x & y) | (x & z)|(y & z))
    #define F3(x,y,z) (x ^ y ^ z)
  3. 常数

    1
    2
    3
    4
    #define K0 0x5a827999L
    #define K1 0x6ed9eba1L
    #define K2 0x8f1bbcdcL
    #define K3 0xca62c1d6L
  4. 初始化寄存器hash值

    1
    2
    3
    4
    5
    #define H0 0x67452301L
    #define H1 0xefcdab89L
    #define H2 0x98badcfeL
    #define H3 0x10325476L
    #define H4 0xc3d2e1f0L

0.3 Base64编码

是一种基于64个可打印字符来表示二进制数据的表示方法。转换的时候,将3字节的数据,先后放入一个24位的缓冲区中,先来的字节占高位。数据不足3字节的话,于缓冲器中剩下的比特用0补足。每次取出6比特(因为${2^{6}=64}$),按照其值选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中的字符作为编码后的输出,直到全部输入数据转换完成。

1586443872149

特征
  1. 会有索引表
  2. 每次取出3字节数据放入24位缓冲区,然后每次取6位编码

资源

了解常用加解密算法并简单逆向