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 <stdio.h>
#include <string.h>

// 全局常量定義
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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章