base64实现

base64_bg 很多人一听“密码学”,脑子里就会浮现神秘的魔法和复杂公式。但古典密码学其实很简单:它只是对字符的位置、顺序或对应关系做一些调整,把原本能看懂的文字变得“看起来乱七八糟”。凯撒密码、维吉尼亚密码、替换密码……这些方法没有魔法,只有移动、替换和排列。理解它们,你就掌握了密码学的最基础思路,也为现代编码和加密打下了直观的感受

Base64 概念

Base64已经成为网络上常见的传输8bit字节的编码方式之一。一般在做数据的传输时,系统之间的报文交互都需要使用Base64对明文进行编码,然后再进行加密,最后才传输。那么Base64的作用是什么?

在数据传输时经常遇到一类情况:使用全ASCII英文字母没问题,但是涉及中文就会乱码,或者网络传输的字符并不完全时可打印的字符,如二进制文件、图片等。Base64就是为了解决此问题

它是基于64个可打印的字符来表示二进制的数据的一种方法
我们常见的编码选择是,A-Za-z0-9+/=,这个表示的大写字母26个,小写字母26个,还有数字0-9,外加上+/,最后使用=进行填充,对于等于号我们后面再讲解,base64的核心思想就是
3 个字节(3 × 8 = 24 位) → 4 个 Base64 字符(4 × 6 = 24 位)
说明白就是

  1. 先把数据按照三个字节一组进行整体分块
  2. 把每组字节转化成二进制合并起来
  3. 把这组合起来的24位分割成4份,每份6位
  4. 每组 6 位的值范围是 0~63,用查表法映射成对应字符
  5. 如果最后不够 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;
}