编码过程演示
让我们通过一个具体的例子来看看它是如何工作的。
假设我们要编码字符串:”Man”
第一步:获取 ASCII 码并转换为二进制
M= 77 =01001101a= 97 =01100001n= 110 =01101110
将它们连成一串 24 bit 的数据流:
010011010110000101101110
第二步:按 6 bit 一组进行拆分
我们将上面的 24 bit 拆分为 4 组:
010011010110000101101110
第三步:计算十进制值并查表
010011= 19 查表得 T010110= 22 查表得 W000101= 5 查表得 F101110= 46 查表得 u
结果: “Man” 的 Base64 编码结果为TWFu。
关于“=”号(Padding 补位)
你经常会看到 Base64 字符串的末尾有一个或两个等号 ==。这是为什么?
因为并不是所有数据都能刚好被 3 整除。
- 如果剩 1 个字节(8 bit):需要补 4 bit 的 0 凑成 12 bit(2 个 6 bit 组)。结果会产生 2 个 Base64 字符,剩下 2 个位置用
=填充。 - 如果剩 2 个字节(16 bit):需要补 2 bit 的 0 凑成 18 bit(3 个 6 bit 组)。结果会产生 3 个 Base64 字符,剩下 1 个位置用
=填充。
例子:编码字母 “A”
- ASCII: 65 (
01000001) - 分组:
- 第一组 6 bit:
010000(16 -> Q) - 剩余 2 bit
01,后面补四个 0:010000(16 -> Q)
- 第一组 6 bit:
- 结果:
QQ。 - 填充: Base64 要求输出长度必须是 4 的倍数,所以补两个等号。
- 最终结果:
QQ==
变异的Base64
1 | //此题为0xGame 2025 Week4的题目 |
- 这个代码看起来困难
- 其实确实有点困难
- 首先这个a2并非int类型,ida分析的只是==伪代码==,并非正式代码,这个是一个指针
*(_BYTE *)(v8 + a2)这里就能看出来,v8明明是int类型的,*还为取值符,更能证明a2是一个指针 (i + a1) >> 2) & 0x3F这里计算的其实是第一个字符的前六个字节,算为第一部分(i + a1 + 1) >> 4) | (unsigned __int8)(16 * *(_BYTE *)(i + a1))) & 0x3F这里计算的是第二部分,16 * 一个字节,就相当于左移4位(乘2的n次幂相当于左移n位)(((int)*(unsigned __int8 *)(i + a1 + 2) >> 6) | (unsigned __int8)(4 * *(_BYTE *)(i + a1 + 1))) & 0x3F这里就是第三部分了((int)*(unsigned __int8 *)(i + a1) >> 2) & 0x3F这就是最后部分了,第四部分了- 最后就可以看出来第二部分和第三部分顺序错误了,找脚本改一下就好了
1 | import base64 |
关于本文
由 GuQing 撰写,采用 CC BY-NC 4.0 许可协议。