在ctf里reverse经常会有base64相关的题型,每次写到这类题型只能凭经验猜测考点来解题,特此出一个base64相关的博客,加深对base64的理解,下次再看到伪代码也不会觉得慌了,毕竟纵使实现形式不同,代码的核心逻辑还是大差不差的。
base64编码原理及代码实现
1. 简介
base64编码是一种以64个可打印字符来表示和传输数据的编码,通常base64编码的数据是没有含义的,base64编码后的数据要通过base64解码才有意义。
2. 原理
base64编码是由64(2^6)个可打印字符来表示的,占用6位,这6位数据正好从0到63对应64个字符,而1 byte = 8 bit,如何用6bit来表示1byte(8bit) 的数据呢,如果每3byte为一组,对应每4个base64编码数据,3 * 8 bit = 4 * 6 bit,这样就解决了这个问题。
2.1 base64编码表
索引和字符的对应关系被叫做base64编码表,如下图所示
base64编码表 | |
---|---|
索引 | 字符 |
0 | A |
1 | B |
2 | C |
3 | D |
4 | E |
5 | F |
6 | G |
7 | H |
8 | I |
9 | J |
10 | K |
11 | L |
12 | M |
13 | N |
14 | O |
15 | P |
16 | Q |
17 | R |
18 | S |
19 | T |
20 | U |
21 | V |
22 | W |
23 | X |
24 | Y |
25 | Z |
26 | a |
27 | b |
28 | c |
29 | d |
30 | e |
31 | f |
32 | g |
33 | h |
34 | i |
35 | j |
36 | k |
37 | l |
38 | m |
39 | n |
40 | o |
41 | p |
42 | q |
43 | r |
44 | s |
45 | t |
46 | u |
47 | v |
48 | w |
49 | x |
50 | y |
51 | z |
52 | 0 |
53 | 1 |
54 | 2 |
55 | 3 |
56 | 4 |
57 | 5 |
58 | 6 |
59 | 7 |
60 | 8 |
61 | 9 |
62 | + |
63 | / |
2.2 当待编码数据正好为3的倍数时
2.2.1 对abc进行base64编码
以abc这个数据为例,对其进行base64编码,如下图所示,abc——>YWNE
可以看到3字节的倍数的数据是可以完美适配base64编码的,但如果我想将非3字节倍数的数据进行base64编码呢?
2.3 当待编码数据不为3的倍数时
2.3.1 对a进行base64编码
a占一个字节(8 bit),而base64编码最少需要3 * 8 bit= 24 bit,因此需要在a后面再补2个空字符,这一组字符中,前6 bit的大小仍为24,对应的字符为Y,还剩下2 bit,则需要补0 直至补满6 bit,补满之后的数据大小为16,对应字符为Q,接下来的 2*6 bit = 12 bit 则填 2个 =,得到base64编码后的数据YQ
==,这样就解决了对一个字节进行base64编码的问题
2.3.2 对ab进行base64编码
ab共占二个字节(2 *8 bit = 16 bit),由上可知需要再补 1 byte达到 3 byte,对应的4个base64字符,其中,前两个分别为Y和Q,对于第三个,其前4bit是有数据的,而后2bit则需要补0,大小为8,对应字符为I,最后6 bit则用一个=来补齐。这样得到base64编码后的数据YWI=
2.4 小结
根据上面的个例,我们可以往数据量更大的情况考虑,假设一个数据有n byte(n > 3),如果 n % 3 = 0 ,则其base64编码后不存在=,因为它正好是 24 byte的倍数;如果n % 3 = 1,由于n = 3 * m + 1,3 * m 正好是24 byte的倍数,这部分不存在=,剩下最后 1 byte ,则按照上面2.3.1的步骤对其进行操作,后补 = =;如果n % 3 = 2,由于n = 3 * m + 2,3 * m 正好是24 byte的倍数,这部分不存在=,剩下最后 2 byte ,则按照上面2.3.2的步骤对其进行操作,后补 = 。
3. 代码实现
对于base64编码的代码,我这里学了两个版本,其中一个是在写ctf的reverse题时看到的,由于赛题是伪代码且逻辑不完整,所以我对其进行了完善,还有一个是我在学base64编码的时候看到的,其中第一个的代码量较小且更加利于理解,如果第一个的代码无法接受的也可以看看第二个,因人而异。
贴一下那道ctf的wp:
[WUSTCTF2020]level3
3.1 其一
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//data是代编码数据
char data[] = "abcdefsdadasdsad2131324rfa2615";
//data_len表示代编码数据能分成几组(3 * 8bit = 4 * 6 bit = 24bit)
int data_len= strlen(data) / 3;
//data_add表示分组后剩下几个
int data_add = strlen(data) % 3;
//开辟空间给src数组
char *src = (char*)malloc(data_len * 4 + 4 + 1);//+4是为了给可能多的1或2个数据及后边要添的=留位置,+1是为了给'\0'留位置来表示字符串结尾
int temp = 0;
int tmp = 0;
int n =0;
//以4 * 6bit为单位将编码后的值传入src
while (temp < data_len)
{
src[n++] = base64_table[data[tmp] >> 2