CTF密码学之Base64,Base32,Base16

Base64

介绍

Base64可以将ASCII字符串或者是二进制编码成只包含A—Z,a—z,0—9,+,/ 这64个字符( 26个大写字母,26个小写字母,10个数字,1个+,一个 / 刚好64个字符)。这64个字符用6个bit位就可以全部表示出来,一个字节有8个bit 位,那么还剩下两个bit位,这两个bit位用0来补充。其实,一个Base64字符仍然是8个bit位,但是有效部分只有右边的6个 bit,左边两个永远是0。Base64的编码规则是将3个8位字节(3×8=24位)编码成4个6位的字节(4×6=24位),之后在每个6位字节前面,补充两个0,形成4个8位字节的形式,那么取值范围就变成了0~63。又因为2的6次方等于64,所以每6个位组成一个单元。一般在CTF逆向题目中base64的加密过程主要是用自定义的索引表,所以如果能一眼能看出是base64加密就会节约很多时间。

加密过程

  • base64的编码都是按字符串长度,以每3个8bit的字符为一组,
  • 然后针对每组,首先获取每个字符的ASCII编码,
  • 然后将ASCII编码转换成8bit的二进制,得到一组3*8=24bit的字节
  • 然后再将这24bit划分为4个6bit的字节,并在每个6bit的字节前面都填两个高位0,得到4个8bit的字节
  • 然后将这4个8bit的字节转换成10进制,对照Base64编码表 ,得到对应编码后的字符。

索引表如下

索引 对应字符 索引 对应字符 索引 对应字符 索引 对应字符
0 A 17 R 34 i 51 z
1 B 18 S 35 j 52 0
2 C 19 T 36 k 53 1
3 D 20 U 37 l 54 2
4 E 21 V 38 m 55 3
5 F 22 W 39 n 56 4
6 G 23 X 40 o 57 5
7 H 24 Y 41 p 58 6
8 I 25 Z 42 q 59 7
9 J 26 a 43 r 60 8
10 K 27 b 44 s 61 9
11 L 28 c 45 t 62 +
12 M 29 d 46 u 63 /
13 N 30 e 47 v
14 O 31 f 48 w
15 P 32 g 49 x
16 Q 33 h 50 y

例子

第一个例子以base64加密SLF为例子,过程如下

字符串      S       L        F
ASCII      83      80       76
二进制   01010011‬  01001100  01000110
合并       01010011‬0100110001000110
6位      010100     110100	 110001    000110
补零	 00010100   00110100   00110001	 00000110
进制       20        52         49         6
对照       U         0          x          G

SLF -> U0xG

第二个例子以base64加密M为例子,过程如下

字符串      M
ASCII      77
二进进   01001101
合并     01001101
6位      010011     01
补零	 00010011   00010000
进制       19        16
对照       T         Q         =         =

M -> TQ==

实现

最上面的base64char索引表可以自定义,这里用c实现

#include 
#include 

// 全局常量定义
const char * base64char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char padding_char = '=';

/*编码代码
* const unsigned char * sourcedata, 源数组
* char * base64 ,码字保存
*/
int base64_encode(const unsigned char * sourcedata, char * base64)
{
	int i = 0, j = 0;
	unsigned char trans_index = 0;    // 索引是8位,但是高两位都为0
	const int datalength = strlen((const char*)sourcedata);
	for (; i < datalength; i += 3){
		// 每三个一组,进行编码
		// 要编码的数字的第一个
		trans_index = ((sourcedata[i] >> 2) & 0x3f);
		base64[j++] = base64char[(int)trans_index];
		// 第二个
		trans_index = ((sourcedata[i] << 4) & 0x30);
		if (i + 1 < datalength){
			trans_index |= ((sourcedata[i + 1] >> 4) & 0x0f);
			base64[j++] = base64char[(int)trans_index];
		}
		else{
			base64[j++] = base64char[(int)trans_index];

			base64[j++] = padding_char;

			base64[j++] = padding_char;

			break;   // 超出总长度,可以直接break
		}
		// 第三个
		trans_index = ((sourcedata[i + 1] << 2) & 0x3c);
		if (i + 2 < datalength){ // 有的话需要编码2个
			trans_index |= ((sourcedata[i + 2] >> 6) & 0x03);
			base64[j++] = base64char[(int)trans_index];

			trans_index = sourcedata[i + 2] & 0x3f;
			base64[j++] = base64char[(int)trans_index];
		}
		else{
			base64[j++] = base64char[(int)trans_index];

			base64[j++] = padding_char;

			break;
		}
	}

	base64[j] = '\0';

	return 0;
}

/** 在字符串中查询特定字符位置索引
* const char *str ,字符串
* char c,要查找的字符
*/
int num_strchr(const char *str, char c) // 
{
	const char *pindex = strchr(str, c);
	if (NULL == pindex){
		return -1;
	}
	return pindex - str;
}
/* 解码
* const char * base64 码字
* unsigned char * dedata, 解码恢复的数据
*/
int base64_decode(const char * base64, unsigned char * dedata)
{
	int i = 0, j = 0;
	int trans[4] = { 0, 0, 0, 0 };
	for (; base64[i] != '\0'; i += 4){
		// 每四个一组,译码成三个字符
		trans[0] = num_strchr(base64char, base64[i]);
		trans[1] = num_strchr(base64char, base64[i + 1]);
		// 1/3
		dedata[j++] = ((trans[0] << 2) & 0xfc) | ((trans[1] >> 4) & 0x03);

		if (base64[i + 2] == '='){
			continue;
		}
		else{
			trans[2] = num_strchr(base64char, base64[i + 2]);
		}
		// 2/3
		dedata[j++] = ((trans[1] << 4) & 0xf0) | ((trans[2] >> 2) & 0x0f);

		if (base64[i + 3] == '='){
			continue;
		}
		else{
			trans[3] = num_strchr(base64char, base64[i + 3]);
		}

		// 3/3
		dedata[j++] = ((trans[2] << 6) & 0xc0) | (trans[3] & 0x3f);
	}

	dedata[j] = '\0';

	return 0;
}

// 测试
int main()
{
	const unsigned char str[] = "a45rbcd";
	const unsigned char *sourcedata = str;
	char base64[128];
	base64_encode(sourcedata, base64);

	printf("编码:%s\n", base64);

	char dedata[128];

	base64_decode(base64, (unsigned char*)dedata);

	printf("译码:%s", dedata);

	getchar();
	getchar();
	return 0;
}

输出如下

C:\Users\thunder>"D:\AlgorithmTest.exe"
编码:YTQ1cmJjZA==
译码:a45rbcd

C:\Users\thunder>

上面的代码是base64加密和解密字符串a45rbcd我们用IDA查看,base64char即是我们的索引表

int __cdecl base64_encode(const char *sourcedata, char *base64)
{
  int v2; // STEC_4
  int v3; // STEC_4
  int v4; // STEC_4
  signed int datalength; // [esp+D0h] [ebp-2Ch]
  unsigned __int8 trans_index; // [esp+DFh] [ebp-1Dh]
  unsigned __int8 trans_indexa; // [esp+DFh] [ebp-1Dh]
  int j; // [esp+E8h] [ebp-14h]
  int ja; // [esp+E8h] [ebp-14h]
  int jb; // [esp+E8h] [ebp-14h]
  int i; // [esp+F4h] [ebp-8h]

  i = 0;
  j = 0;
  datalength = j__strlen(sourcedata);
  while ( i < datalength )
  {
    base64[j] = base64char[((signed int)(unsigned __int8)sourcedata[i] >> 2) & 0x3F]; # 右移
    ja = j + 1;
    trans_index = 16 * sourcedata[i] & 0x30;
    if ( i + 1 >= datalength )
    {
      base64[ja] = base64char[trans_index];
      v2 = ja + 1;
      base64[v2++] = padding_char;
      base64[v2] = padding_char;
      j = v2 + 1;
      break;
    }
    base64[ja] = base64char[((signed int)(unsigned __int8)sourcedata[i + 1] >> 4) & 0xF | trans_index]; # 右移
    jb = ja + 1;
    trans_indexa = 4 * sourcedata[i + 1] & 0x3C;
    if ( i + 2 >= datalength )
    {
      base64[jb] = base64char[trans_indexa];
      v4 = jb + 1;
      base64[v4] = padding_char;
      j = v4 + 1;
      break;
    }
    base64[jb] = base64char[((signed int)(unsigned __int8)sourcedata[i + 2] >> 6) & 3 | trans_indexa]; # 右移
    v3 = jb + 1;
    base64[v3] = base64char[sourcedata[i + 2] & 0x3F];
    j = v3 + 1;
    i += 3;
  }
  base64[j] = 0;
  return 0;
}

辨别

其实辨别很简单,有很多的方法,最简单的方法就是动态调试,直接用OD或者IDA动态调试,多输入几组数据,观察加密后的字符串,存在=这种字符串多半都有base64加密。 如果不能动态调试那就用IDA静态观察,观察索引表,观察对输入的操作,比如上面很明显的三次右移操作。

解密

一般解密用python来实现

import base64
s = 'key' # 要加密的字符串
a = base64.b64encode(s) # 加密

print a

print base64.b64decode(a) # 解密

在线解密网站 : https://www.qqxiuzi.cn/bianma/base.php

Base32

原理

Base32编码是使用32个可打印字符(字母A-Z和数字2-7)对任意字节数据进行编码的方案,编码后的字符串不用区分大小写并排除了容易混淆的字符,可以方便地由人类使用并由计算机处理。

符号 符号 符号 符号
0 A 8 I 16 Q 24 Y
1 B 9 J 17 R 25 Z
2 C 10 K 18 S 26 2
3 D 11 L 19 T 27 3
4 E 12 M 20 U 28 4
5 F 13 N 21 V 29 5
6 G 14 O 22 W 30 6
7 H 15 P 23 X 31 7
填充 =

Base32将任意字符串按照字节进行切分,并将每个字节对应的二进制值(不足8比特高位补0)串联起来,按照5比特一组进行切分,并将每组二进制值转换成十进制来对应32个可打印字符中的一个。

由于数据的二进制传输是按照8比特一组进行(即一个字节),因此Base32按5比特切分的二进制数据必须是40比特的倍数(5和8的最小公倍数)。例如输入单字节字符“%”,它对应的二进制值是“100101”,前面补两个0变成“00100101”(二进制值不足8比特的都要在高位加0直到8比特),从左侧开始按照5比特切分成两组:“00100”和“101”,后一组不足5比特,则在末尾填充0直到5比特,变成“00100”和“10100”,这两组二进制数分别转换成十进制数,通过上述表格即可找到其对应的可打印字符“E”和“U”,但是这里只用到两组共10比特,还差30比特达到40比特,按照5比特一组还需6组,则在末尾填充6个“=”。填充“=”符号的作用是方便一些程序的标准化运行,大多数情况下不添加也无关紧要,而且,在URL中使用时必须去掉“=”符号。

与Base64相比,Base32具有许多优点:

  • 适合不区分大小写的文件系统,更利于人类口语交流或记忆。
  • 结果可以用作文件名,因为它不包含路径分隔符 “/”等符号。
  • 排除了视觉上容易混淆的字符,因此可以准确的人工录入。(例如,RFC4648符号集忽略了数字“1”、“8”和“0”,因为它们可能与字母“I”,“B”和“O”混淆)。
  • 排除填充符号“=”的结果可以包含在URL中,而不编码任何字符。

Base32也比Base16有优势:

  • Base32比Base16占用的空间更小。(1000比特数据Base32需要200个字符,而Base16则为250个字符)

Base32的缺点:

  • Base32比Base64多占用大约20%的空间。因为Base32使用8个ASCII字符去编码原数据中的5个字节数据,而Base64是使用4个ASCII字符去编码原数据中的3个字节数据。

解密

import base64
s = 'key' # 要加密的字符串
a = base64.b32encode(s) # 加密

print a

print base64.b32decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

Base16

原理

Base16编码使用16个ASCII可打印字符(数字0-9和字母A-F)对任意字节数据进行编码。Base16先获取输入字符串每个字节的二进制值(不足8比特在高位补0),然后将其串联进来,再按照4比特一组进行切分,将每组二进制数分别转换成十进制,在下述表格中找到对应的编码串接起来就是Base16编码。可以看到8比特数据按照4比特切分刚好是两组,所以Base16不可能用到填充符号“=”。

Base16编码后的数据量是原数据的两倍:1000比特数据需要250个字符(即 250*8=2000 比特)。换句话说:Base16使用两个ASCII字符去编码原数据中的一个字节数据。

编码 编码
0 0 8 8
1 1 9 9
2 2 10 A
3 3 11 B
4 4 12 C
5 5 13 D
6 6 14 E
7 7 15 F

Base16编码是一个标准的十六进制字符串(注意是字符串而不是数值),更易被人类和计算机使用,因为它并不包含任何控制字符,以及Base64和Base32中的“=”符号。输入的非ASCII字符,使用UTF-8字符集。

解密

import base64
s = 'key' # 要加密的字符串
a = base64.b16encode(s) # 加密

print a

print base64.b16decode(a) # 解密

在线网站 : https://www.qqxiuzi.cn/bianma/base.php

参考链接:

http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html

https://blog.csdn.net/u011491972/article/details/52800177

你可能感兴趣的:(密码篇)