base64实现
很多人一听“密码学”,脑子里就会浮现神秘的魔法和复杂公式。但古典密码学其实很简单:它只是对字符的位置、顺序或对应关系做一些调整,把原本能看懂的文字变得“看起来乱七八糟”。凯撒密码、维吉尼亚密码、替换密码……这些方法没有魔法,只有移动、替换和排列。理解它们,你就掌握了密码学的最基础思路,也为现代编码和加密打下了直观的感受
Base64 概念
Base64已经成为网络上常见的传输8bit字节的编码方式之一。一般在做数据的传输时,系统之间的报文交互都需要使用Base64对明文进行编码,然后再进行加密,最后才传输。那么Base64的作用是什么?
在数据传输时经常遇到一类情况:使用全ASCII英文字母没问题,但是涉及中文就会乱码,或者网络传输的字符并不完全时可打印的字符,如二进制文件、图片等。Base64就是为了解决此问题
A-Za-z0-9+/=,这个表示的大写字母26个,小写字母26个,还有数字0-9,外加上+/,最后使用=进行填充,对于等于号我们后面再讲解,base64的核心思想就是
- 先把数据按照三个字节一组进行整体分块
- 把每组字节转化成二进制合并起来
- 把这组合起来的24位分割成4份,每份6位
- 每组 6 位的值范围是 0~63,用查表法映射成对应字符
- 如果最后不够 3 字节,用
=补齐
整体数据在分块的时候会遇见不是3的倍数的情况,这时候就会用0进行位的填充,然后最后6位如果都是0的就会使用
=进行填充
前面也说了,是根据查表法进行对应字符的映射,那么CFR的标准是什么呢?
| 十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 | 十进制 | 二进制 | 字符 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 000000 | A | 16 | 010000 | Q | 32 | 100000 | g | 48 | 110000 | w |
| 1 | 000001 | B | 17 | 010001 | R | 33 | 100001 | h | 49 | 110001 | x |
| 2 | 000010 | C | 18 | 010010 | S | 34 | 100010 | i | 50 | 110010 | y |
| 3 | 000011 | D | 19 | 010011 | T | 35 | 100011 | j | 51 | 110011 | z |
| 4 | 000100 | E | 20 | 010100 | U | 36 | 100100 | k | 52 | 110100 | 0 |
| 5 | 000101 | F | 21 | 010101 | V | 37 | 100101 | l | 53 | 110101 | 1 |
| 6 | 000110 | G | 22 | 010110 | W | 38 | 100110 | m | 54 | 110110 | 2 |
| 7 | 000111 | H | 23 | 010111 | X | 39 | 100111 | n | 55 | 110111 | 3 |
| 8 | 001000 | I | 24 | 011000 | Y | 40 | 101000 | o | 56 | 111000 | 4 |
| 9 | 001001 | J | 25 | 011001 | Z | 41 | 101001 | p | 57 | 111001 | 5 |
| 10 | 001010 | K | 26 | 011010 | a | 42 | 101010 | q | 58 | 111010 | 6 |
| 11 | 001011 | L | 27 | 011011 | b | 43 | 101011 | r | 59 | 111011 | 7 |
| 12 | 001100 | M | 28 | 011100 | c | 44 | 101100 | s | 60 | 111100 | 8 |
| 13 | 001101 | N | 29 | 011101 | d | 45 | 101101 | t | 61 | 111101 | 9 |
| 14 | 001110 | O | 30 | 011110 | e | 46 | 101110 | u | 62 | 111110 | + |
| 15 | 001111 | P | 31 | 011111 | f | 47 | 101111 | v | 63 | 111111 | / |
base64的实现
我们先不去写实际代码,我们先手写一下,先推敲一下是什么思路
我就以duck来进行简单的推敲吧
我们看第一条按照3个字节进行拆分,那么就是 duc为一组,k自己为一组,然后我们把每组都转成二进制并且合并起来,再看上面的码表进行对照翻译
- d => 01100100
- u => 01110101
- c => 01101011
这几个合并起来就是011001000111010101101011
再做一个分组就011001 000111 010101 100011
然后我们进行查表 ‘ZHVj’ 显而易见就是这个字符串,然后就是我们k的部分了
- k => 01101011
k并不是3个字节,所以他分割出来的我们后面是要补0的01101011 00000000 00000000
进行base64分组以后就是011010 110000 000000 000000
然后还是查表,结果就是aw==
最后duck的base64编码就是ZHVjaw==
接下来就该我们的代码进行实现了, 我选择go语言来实现,C语言版本也会放上来
代码实现
package main
import "fmt"
var base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
func EncodeBase64(data []byte) string {
var result []byte
n := len(data)
for i := 0; i < n; i += 3 {
var b [3]byte
remaining := n - i
copy(b[:], data[i:])
// 24 位数据
val := uint(b[0])<<16 | uint(b[1])<<8 | uint(b[2])
// 取 6 位一组
for j := 18; j >= 0; j -= 6 {
index := (val >> j) & 0x3F
result = append(result, base64Table[index])
}
// 填充 '='
if remaining == 1 {
result[len(result)-2] = '='
result[len(result)-1] = '='
} else if remaining == 2 {
result[len(result)-1] = '='
}
}
return string(result)
}
func main() {
data := []byte("Hello World!")
encoded := EncodeBase64(data)
fmt.Println(encoded) // 输出: "SGVsbG8gV29ybGQh"
}算法具体可以问问GPT,这个是根据索引进行映射 这个就是编码的代码了,解码代码各位可以发挥想象自己实现一下,接下来就是C语言的实现和QDBI的插桩分析了,我对QDBI的学习也不算很多,处于刚刚接触 贴一下C的代码就结束吧,QBDI我现在功力确实不够
char *encodeBase64(const unsigned char *data, size_t len)
{
size_t outLen = 4 * ((len + 2) / 3); // 输出长度
char *out = (char *)malloc(outLen + 1);
if (!out)
return NULL;
out[outLen] = '\0';
size_t i, j;
for (i = 0, j = 0; i < len; i += 3) {
unsigned char b0 = data[i];
unsigned char b1 = (i + 1 < len) ? data[i + 1] : 0;
unsigned char b2 = (i + 2 < len) ? data[i + 2] : 0;
unsigned int val = (b0 << 16) | (b1 << 8) | b2;
out[j++] = base64Table[(val >> 18) & 0x3F];
out[j++] = base64Table[(val >> 12) & 0x3F];
out[j++] = (i + 1 < len) ? base64Table[(val >> 6) & 0x3F] : '=';
out[j++] = (i + 2 < len) ? base64Table[val & 0x3F] : '=';
}
return out;
}