Base64编码原理学习
本文最后更新于 188 天前,其中的信息可能已经过时,如有错误可以直接在文章下留言

最近做了一道 BUUCTF 的 Misc 板块的 Base64 隐写的题目,发现不了解清楚 Base64 编码的根本原理的话,好像也难以理清 Base64 隐写的原理,从接触 CTF 以来 Base64 就是接触得最多的编码方式,无论是 Misc、Crypto、Web、Reverse 反正几乎所有 CTF 方向的入门题里都有它的身影。可恨的是我还没搞清楚它的编码原理,所以就抽空在 B 站和 CSDN 上的文章里面学习了一下。

首先 Base64 编码当然是可逆的,所以不能依赖它进行加密。

Base64 编码过程

首先,要让原始的待编码数据每三个字节作为一组,每个字节是 8 个 bit,所以一共是 24 个 bit,我感觉理解这一步在理解 Base64 编码步骤里面十分重要。

接着,再将待编码数据的每一个字符的 Ascii 值转化为二进制,比如字母’A’的 Ascii 值为 65,转化为二进制之后为 1000001,因为每个字节是 8 个 bit,所以要在 1 前面在补个 0 方便后续编码,所以就是 01000001。

三个字节的数据进行这一步操作后就是 24 个 bit 的二进制数据,我们对此从前往后,重新以每 6 个 bit 的二进制数据为一组,得到四组,然后将每组的二进制数据转化为十进制,再对应 Base64 编码表,这个十进制数据在码表中对应的值即为编码后的数据,也就是说三个字符数据 Base64 之后会得到 4 个字符数据,实验一下,如图,确实如此,Man 在经过编码后变成了 TWFu

那么问题又来了,什么是 Base64 码表?

Base64 编码表(table)

也没什么,就是用来将以 6 个 bit 位为一组的二进制数据重新转换为十进制之后,对照来获得编码后的数据的对照编码表,它的常规编码表一般是下面这个,网上的在线 Base64 编码默认通常也是这个编码表

可以看到,总共有 64 个字符,是英文字母的大写和小写,加上 1234567890+/

比如 010000 转化为十进制为 16,对应的就是编码表中的 Q。

而在 CTF 的题目中,Base64 的码表可能会被修改,无论是 Misc 还是 Reverse。比如下面这题

[WUSTCTF2020]level3 – Arnold’s Blog (arnold66.top)

上面我介绍的文字太多可能还不够清楚,接下来是我对 ‘Arnold’ 6 个字符的手动进行 Base64 编码,看完这个应该就会明白一些。如图,第二行为每个字符在 Ascii 码表对应的值

第三行我将字符对应的 Ascii 表中对应的值转化为 8 个 bit 的二进制数据后,又以每 6 个 bit 为一组得到第四行

然后再转化回十进制,在 Base64 编码表中寻找对应的字符即可得到 Base64 编码后的数据,QXJub2xk

但是 Base64 强调以 3 个字节为一组,像 Arnold 总共 6 个字节,为三的倍数刚刚好,那么不是三的倍数的情况下又会如何编码呢?比如 woman、hello,这时候就会出现一个问题就是将二进制数据以 6 个为一组时,到最后的数据不够 6 个了,如下图所示

当二进制数据以 6 个 bit 为一组分完后,会最后剩下一个 4 个 bit 的数据 1110,这时候我们就要在这些数据后面补上两个 0,使得其为 6 个 bit 为一组的二进制数据,并且再补 6 个 0 作为一组,为什么要再补 6 个 0 呢?因为之前强调了 Base64 要让原始的待编码数据中每三个字节作为一组,每三个字节以 6 个 bit 分组会得到 4 组,因此最后要补全四组,再加上一组 000000,而在 Base64 中 000000 代表的就是等于号,这就是为啥 Base64 编码中经常会出现等于号,我们也不难发现当待编码数据的字节数为 3n+1 时,Base64 编码后会出现 2 个‘=’,而当带编码的数据字节数为 3n+2 时,Base64 编码会出现 1 个‘=’,例如 woman 进行 Base64 编码之后是 d29tYW4=,life 进行 Base64 编码之后是则是 bGlmZQ==。

我们再来看看再 Python 中如何借助库实现对 base64 的编码和解码,代码如下

import base64
strings=b'Arnold666666'
en=base64.b64encode(strings)
print(en)
de=base64.b64decode(en)
print(de)

然后是再逆向过程中经常遇到的 base64 换表

import base64
import string
str1 = "NUGry2uhKgV2KgV2"
string1 = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
# string1是改过之后的base64表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# string2是原始的base64表
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))

然后看看 Base64 加密在 C 语言当中的实现,我们在逆向工程中通常面对的是 IDA 中 C 语言的 Base64 加密,可以参考文章

Base64 加密算法以及在 IDA 中的识别 – Qsons – 博客园 (cnblogs.com)

C 语言实现 Base64 编码 / 解码_c 语言 base64 库 - CSDN 博客

unsigned char *base64_encode(unsigned char *str)
{
long len;
long str_len;
unsigned char *res;
int i,j;
//定义base64编码表
unsigned char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//计算经过base64编码后的字符串长度
str_len=strlen(str);
if(str_len % 3 == 0)
len=str_len/3*4;
else
len=(str_len/3+1)*4;
res=malloc(sizeof(unsigned char)*len+1);
res[len]='\0';
//以3个8位字符为一组进行编码
for(i=0,j=0;i<len-2;j+=3,i+=4)
{
res[i]=base64_table[str[j]>>2]; //取出第一个字符的前6位并找出对应的结果字符
res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)]; //将第一个字符的后位与第二个字符的前4位进行组合并找到对应的结果字符
res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)]; //将第二个字符的后4位与第三个字符的前2位组合并找出对应的结果字符
res[i+3]=base64_table[str[j+2]&0x3f]; //取出第三个字符的后6位并找出结果字符
}
switch(str_len % 3)
{
case 1:
res[i-2]='=';
res[i-1]='=';
break;
case 2:
res[i-1]='=';
break;
}
return res;
}

反正要在 IDA 中能识别出 Base64 加密算法,我已经识别不出来好几次了😅

这就是 Base64 的编码原理了,而在 Base64 解码的过程中因为解码的过程的性质会出现 Base64 隐写的题目,后续再写。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇