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

最近做了一道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隐写的题目,下面这一题就是Base64隐写的题目。

[ACTF新生赛2020]base64隐写 – Arnold’s Blog (arnold66.top)

文末附加内容
暂无评论

发送评论 编辑评论


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