0x00 base64的原理
编码方式
计算机储存数据以字节为单位,一个位有八个字节,比如“abc”字符串,这是底层的数据结构
a b c
01100001 01100010 01100011
三个字符对应3×8=24个位,同时24可以看成6×4的积,故把三个字节组合后,以六个字节为一组分割可以分割出4组,但是为了符合计算机的储存结构,每组空出来的两个高位要补上0。
00011000 00010110 00001001 00100011
同时,新的四个字节每个可以表示一个整数,如果将这些整数映射到一张特定的码表上,便会得到一个新的字符串。例如这是标准base64的码表:
那么刚刚新的四个字节就被表示成了:
Y(24) W(22) J(9) j(35)
于是一串base64码就出来了:abc->YWJj
假如加密内容长度不是3的倍数怎么办?
我们可以通过补全字节的方法用0补全字节数到3的倍数,然后在base64码后用‘=’表明补全字节的数量。例如“abcd”字符串:
a b c d
01100001 01100010 01100011 01100100
00011000(Y) 00010110(W) 00001001(J) 00100011(j) 00011001(Z) 00000000(A) 00000000(A) 00000000(A)
//最后两个A要替换为=,因为转换后具有有效信息的只是前6个字节
得到base64码YWJjZA==
代码实现
利用3变4,不够3补为3的逻辑,我们可以利用C语言以三个字节为一组利用位运算符进行base64转换(个人认为三个字节一组循环处理是最高效的)
这是编码部分的C程序,标明了一些细节:
void base64_encode(char *src,char *result){
int fill_bit=0;
int data_length;
int result_length;
int index;
bool full=true;
data_length=strlen(src);
//printf("length:%d\n",data_length);
fill_bit=((3-strlen(src)%3)%3); //计算未满字节,注意除去3的情况
for(int k=0;k>2;
result[j++]=table[index];
index=((src[i]&3)<<4)+(src[i+1]>>4);
result[j++]=table[index];
index=((src[i+1]&15)<<2)+(src[i+2]>>6);
result[j++]=table[index];
index=(src[i+2]&63);
result[j++]=table[index];
//<< >> 运算符的优先级低于+ -,注意加括号
}
result_length=strlen(result);
for(int k=0;k
解码方式
按照上面的思维,很容易想到只要把=替换为A并且把4个字节合并为3个字节即可还原出原码。不过这里有个容易搞错的东西,解码时每个字符字节对应的二进制数据并不是这个字符的ASCII码,而是这个字符在码表中的下标。(在这里出了bug卡了一下,所以有点印象)
下面是C的实现方法:
int findchr(char *array,char ch){
for(int i=0;i
void base64_decode(char *src,char *result){
int base_len;
int j=0;
base_len=strlen(src);
//printf("length:%d\n",base_len);
for(int i=0;i>4);
result[j++]=(findchr(table,src[i+1])<<4)+(findchr(table,src[i+2])>>2);
result[j++]=((findchr(table,src[i+2])&3)<<6)+(findchr(table,src[i+3]));
//注意编码是table对应的编码,不是原来的ascii码
//按位运算符优先级低于位移运算符,注意括号
}
}
0x01 base64的延伸
传输图片
有时候可以把图片的数据用base64编码,达到利于传输的目的
变种
很多二进制表通过对码表等进行加密或者位移的方法进行混淆,特别是用标准码表翻译出乱码时要留意这点
base64在逆向题中的特征
- 通常会在strings视图中出现明显的码表或者一些base64码(其它base加密同理)
- 通常decode的函数会有定长的循环同时带有很多位运算和指针操作啥的,利用这点可以快速锁定关键函数
- 使用位移运算写的算法通常会带有有几个关键的整数,比如:4,2,6,15,63啥的,反正就是位运算涉及到的常数,可以用来作为关键函数的标志
0x02 完整源码
水平有限,欢迎指正!
#include
#include
#include
#include
char table[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char padding_char='=';
int findchr(char *array,char ch){
for(int i=0;i>2;
result[j++]=table[index];
index=((src[i]&3)<<4)+(src[i+1]>>4);
result[j++]=table[index];
index=((src[i+1]&15)<<2)+(src[i+2]>>6);
result[j++]=table[index];
index=(src[i+2]&63);
result[j++]=table[index];
//<< >> 运算符的优先级低于+ -,注意加括号
}
result_length=strlen(result);
for(int k=0;k>4);
result[j++]=(findchr(table,src[i+1])<<4)+(findchr(table,src[i+2])>>2);
result[j++]=((findchr(table,src[i+2])&3)<<6)+(findchr(table,src[i+3]));
//注意编码是table对应的编码,不是原来的ascii码
//按位运算符优先级低于位移运算符,注意括号
}
}
int main(){
char words[100]="abc";
char en_words[100]="";
char de_words[100]="";
base64_encode(words,en_words);
printf("encode:%s\n",en_words);
base64_decode(en_words,de_words);
printf("decode:%s\n",de_words);
return 0;
}