先來看一下base64的概念
Base64要求把每三個8Bit的字節轉換為四個6Bit的字節(38 = 46 = 24),然后把6Bit再添兩位高位0,組成四個8Bit的字節,也就是說,轉換后的字符串理論上將要比原來的長1/3。
規則
關於這個編碼的規則:
1.把3個字符變成4個字符
2.每76個字符加一個換行符
3.最后的結束符也要處理
4.10個數字,26個大寫字母,26個小寫字母,1個+,一個/剛好64個字符
例子
為方便理解,舉下例子
轉換前 11111011, 11101111, 10111110 (二進制)
首先按每6位分開 111110, 111110, 111110, 111110
再到最高位添加兩個0
轉換后 00111110, 00111110, 00111110, 00111110 (二進制)
上面的三個字節是原文,下面的四個字節是轉換后的Base64編碼,其前兩位均為0。
轉換后,我們用一個碼表來得到我們想要的字符串(也就是最終的Base64編碼),這個表是這樣的:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 索引 對應字符0 A1 B2 C3 D4 E5 F6 G7 H8 I9 J10 K11 L12 M13 N14 O15 P16 Q17 R18 S19 T20 U21 V22 W23 X24 Y25 Z26 a27 b28 c29 d30 e31 f32 g33 h34 i35 j36 k37 l38 m39 n40 o41 p42 q43 r44 s45 t46 u47 v48 w49 x50 y51 z52 053 154 255 356 457 558 659 760 861 962 +63 /
雖然python解base64很方便
最好還是先寫個腳本加深對base64的理解
文末我會貼出我的調試腳本
那么base64隱寫到底是什么東西呢?
關鍵是base64解碼的時候
1.檢查base64編碼后面有幾個等於號
2.把字符串按照base64表轉換成46的倍數位數二進制
3.__刪除等於號的個數8的bit__(等於號不在base64規定的范圍內,只是為了補足長度,所以解碼時要刪除)
4.按照6個bit一組轉成字符
此處的關鍵就是,解碼的時候,會刪除等於號的個數8的bit
且我們只用6個bit表示一個等於號(xxxxxx)
那么,意思就是我們可以控制__等於號個數2bit__的字符
NJCTF2017有一道misc是base64隱寫
從里面抽出一條來
1
2
3
4
5
|
import base64
s =
"QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHm="
print base64.b64decode(s)
print s
print base64.b64encode(base64.b64decode(s))
|
會發現返回
1
2
3
|
By reason of his fallen divinity
QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHm=
QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHk=
|
會發現有區別,但是並不影響正常顯示
來分析一下為啥,就拿我的昵稱舉例
首先是把每位字符轉化成ascii的二進制形式
1
2
|
c | 0 | 1 | 4
01100011 | 00110000 | 00110001 | 00110100
|
進行base64編碼時,按每6位分開,如果不能被6整除要用0補齊
1
2
|
011000 11|0011 0000|00 110001 | 001101 00
011000 | 110011 | 000000 | 110001 | 001101 | 000000
|
並且所有高位補兩個0至8位
1
2
|
011000 | 110011 | 000000 | 110001 | 001101 | 000000
00011000 | 00110011 | 00000000 | 00110001 | 00001101 | 00000000
|
這樣才能剛好用64個不同形式表示
1
2
|
00011000 | 00110011 | 00000000 | 00110001 | 00001101 | 00000000
Y | z | A | x | N | A
|
但是會發現字符只有6位數不夠被4整除
所以到字符后面添加2個等號
1
2
|
YzAxNA
YzAxNA==
|
解密時
先找出YzAxNA==每位字符在base64碼表中的位置
由於等號沒有我就不表示了
1
2
|
Y | z | A | x | N | A
011000 | 110011 | 000000 | 110001 | 001101 | 000000
|
將二進制連接起來並按每八位划分
1
2
|
011000110011000000110001001101000000
01100011 | 00110000 | 00110001 | 00110100 | 0000
|
會發現末尾多了等號數*2bit的0
這些0在解密時是要被刪除的
所以base64隱寫的精髓就是這幾bit可控
如
1
2
3
4
5
|
YzAxNA==
YzAxNB==
YzAxNC==
......
YzAxNO==
|
解密的到的都是一樣的結果
調試腳本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# -*- coding:utf-8 -*-
base =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def encode(str):
s = str
s_bin =
""
for i in range(0,len(s)):
s_bin = s_bin + bin(ord(s[i])).replace(
"0b","").rjust(8,"0") #把每個字符二進制化
print s_bin
if len(s_bin) % 6 != 0: #判斷二進制長度能否被6整除
for j in range(1,6):
if (len(s_bin) + j) % 6 == 0:
bin_length = len(s_bin) + j
#獲取二進制長度
break
s_bin = s_bin.ljust(bin_length,
"0") #把二進制長度補到能被6整除
print s_bin
encode =
""
for k in range(0,len(s_bin)/6):
index = k *
6
each_bin = s_bin[index:index+
6].zfill(8) #把6Bit再添兩位高位0
print each_bin
each_enc = int(each_bin,
2) #轉化為10進制
print each_enc
encode = encode + base[each_enc]
# print encode
if len(encode) % 4 != 0: #判斷解密后字符串是否能被4整除,如果不能,要在末尾加=
for l in range(1,4):
if (len(encode) + l) % 4 == 0:
encode_length = len(encode) + l
print encode.ljust(encode_length,"=")
def raw(str): #獲取每位字符的二進制形式
s = str
for i in range(0,len(s)):
print s[i],bin(ord(s[i])).replace("0b","").rjust(8,"0")
|