源码
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
| #include<stdio.h> #include <string.h> char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; void base64_encode(char raw[], char encode[]) { int code_len = strlen(raw); int final_len = 0; if (code_len % 3) { final_len = (code_len / 3 + 1) * 4; } else final_len = (code_len / 3) * 4; int i = 0, i_ = 0; for (; i < code_len; i += 3, i_ += 4) { encode[i_] = base64_table[raw[i] >> 2]; encode[i_ + 1] = base64_table[((raw[i] & 0x03) << 4) | ((raw[i + 1] & 0xf0) >> 4)]; encode[i_ + 2] = base64_table[((raw[i + 1] & 0x0f) << 2) | ((raw[i + 2] & 0xc0) >> 6)]; encode[i_ + 3] = base64_table[raw[i + 2] & 0x3f]; } if (code_len % 3 == 1) { encode[i_ - 1] = '='; encode[i_ - 2] = '='; } else if (code_len % 3 == 2) { encode[i_ - 1] = '='; } return; } int Findindex(char c) { for (int i = 0; i < strlen(base64_table); ++i) { if (c == base64_table[i]) return i; } } void base64_decode(char* encode, char* decode) { int decode_len=0; int encode_len = strlen(encode); if (strstr(encode, "==")) decode_len = (encode_len / 4) * 3 - 2; else if (strstr(encode, "=")) decode_len = (encode_len / 4) * 3 - 1; else decode_len = (encode_len / 4) * 3; int i = 0, i_ = 0; for (; i < encode_len; i_ += 4, i += 3) { decode[i] = (Findindex(encode[i_]) << 2) | (Findindex(encode[i_ + 1]) & 0x30) >> 4; decode[i + 1] = ((Findindex(encode[i_ + 1]) & 0xf) << 4) | ((Findindex(encode[i_ + 2]) & 0x3c) >> 2); decode[i + 2] = ((Findindex(encode[i_ + 2]) & 0x3) << 6) | ((Findindex(encode[i_ + 3]))); } decode[decode_len] = 0; return; } int main() { char raw[] = "hgame{123456}"; char encode[100] = { 0 }; char decode[100] = { 0 }; base64_encode(raw, encode); printf("%s", encode); base64_decode(encode, decode); printf("\n%s", decode); }
|
细节剖析
编码部分
记录长度
首先要先计算长度,base64就是将三个字节扩展为四个字节,所以要分成有余数和整除两种情况
1 2 3 4 5 6 7 8
| int code_len = strlen(raw); int final_len = 0; if (code_len % 3) { final_len = (code_len / 3 + 1) * 4; } else final_len = (code_len / 3) * 4;
|
编码部分
首先先介绍一下两个工具,&和|,和一些数据做&运算可以取到我们想要的位,而|运算可以将两部分结合在一起
编码后的索引最多为六位,原先的数据可以是八位的
encode[i]
这个其实是最好实现的,因为只需要取到前六位,所以直接>>2就可以实现
1
| encode[i_] = base64_table[raw[i] >> 2];
|
encode[i+1]
先明确我们的需求,从第一个数据获取最后的两位与第二个数据获取的前四位结合。需要用到&,这里的结合就需要用到|。
首先要保证我们取到的是两位的,需要&0x03
因为只有最后两位是1,根据按位&,当有一个是0时运算后结果必定为0,所以就可以取到最后两位
下一步是移到正确的位置,需要用到位移运算符
这里第一个数的最后两位编码后是放在前面两位的位置,所以需要<<4
接下来取剩余的四位
剩余的四位来自第二个数的前四位
可以先&0xf0使得后四位都为0,当然也可以不用,直接>>4
最后使用|结合起来
0 |
0 |
0 |
1 |
1 |
0 |
0 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
|
|
|
|
|
与 |
运 |
算 |
|
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1
| encode[i_ + 1] = base64_table[((raw[i] & 0x03) << 4) | ((raw[i + 1]) >> 4)];
|
encode[i+2]
要用到第二个数据的后四位和第三个数据的前两位
同理,要保留第二个数据的后四位并且去除前四位,就需要&0xf,移到正确的位置<<2,剩余两位留给第三个数据的前两位
要取到第三个数据的前两位
可以直接>>6保证前两位移动到最后的两位,也可以先用&0xc0清除后四位,因为是00110000,最后结合起来就可以了
1
| encode[i_ + 2] = base64_table[((raw[i + 1] & 0x0f) << 2) | ((raw[i + 2] ) >> 6)];
|
encode[i+3]
只需要取到第三个数据的后六位就可以了
直接&0x3f,因为0x3f的二进制是00111111
1
| encode[i_ + 3] = base64_table[raw[i + 2] & 0x3f];
|
填加=
如果编码前的数据长度%3不等于0,需要使用=填充
如果多出一位的话,根据经过上述过程会变成两位,所以最后两位需要用到=来填充
多出两位,经过上述过程变成三位,所以只需填充最后一位为=
1 2 3 4 5 6 7 8 9
| if (code_len % 3 == 1) { encode[i_ - 1] = '='; encode[i_ - 2] = '='; } else if (code_len % 3 == 2) { encode[i_ - 1] = '='; }
|
解码部分
六位还原为八位
去除=
先计算解码后的长度,每四个对应三个,最后减去=的长度
1 2 3 4 5 6 7 8
| int decode_len=0; int encode_len = strlen(encode); if (strstr(encode, "==")) decode_len = (encode_len / 4) * 3 - 2; else if (strstr(encode, "=")) decode_len = (encode_len / 4) * 3 - 1; else decode_len = (encode_len / 4) * 3;
|
decode[i]
其实和上面过程正好相反,取第一个编码数据的后六位(因为前两位是填充的0)和第二个编码数据的前两位,在此之前需要先去除前两位填充的0,所以要&0x30,根据上面的过程,我们需要的是00110000,正好是0x30
再用|结合
1
| decode[i] = (Findindex(encode[i_]) << 2) | (Findindex(encode[i_ + 1]) & 0x30) >> 4;
|
decode[i+1]
需要第二个编码数据的后四位和第三个编码数据的前四位,也是需要先去除填充的0
所以第二个数据&0xf保证取到后四位,<<4移到前四位
第三个数据&0x3c(00111100)进行去除填充的前两位0以及取到需要的那四位,再>>2,最后使用|结合
1
| decode[i + 1] = ((Findindex(encode[i_ + 1]) & 0xf) << 4) | ((Findindex(encode[i_ + 2]) & 0x3c) >> 2);
|
decode[i+2]
第三个编码数据的后两位和第四个编码数据的那六位
和上述过程类似,就不赘述了
字符数组结束
最后记得填加字符数组结束符
写在最后
base64还可以魔改,进行变表操作或者在编码过程中参杂异或(Dasctf2022)