我们说我有以下内容:
image_data = """iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="""
这只是一个点图像(来自https://en.wikipedia.org/wiki/Data_URI_scheme)。但我不知道它是图像还是文本等。是否有可能理解它只有这个编码的字符串?我在Python中尝试它,但它也是一般性问题。因此,对这两者的任何见解都非常受欢迎。
答案 0 :(得分:7)
你不能,至少没有解码,因为有助于识别文件类型的字节分布在base64字符上,而这些字符并不直接与整个字节对齐。每个字符编码6个位,这意味着每4个字符编码3个字节。
识别文件类型需要访问不同块大小的字节。例如,JPEG图像可以从字节FF D8或FF D9中识别,但是两个字节;后面的第三个字节也必须编码为4字符块的一部分。
可以做的是解码刚好的base64字符串来进行文件类型指纹识别。因此,您可以解码前4个字符以获得3个字节,然后使用前两个字符来查看对象是否为JPEG图像。可以从第一个或最后一个字节序列中识别出大量文件格式(PNG图像可以由前8个字节识别,GIF由前6个字符识别,等等)。从base64字符串解码那些字节是微不足道的。
您的样本是PNG图像;您可以使用imghdr
module:
>>> import imghdr
>>> image_data = """iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="""
>>> sample = image_data[:44].decode('base64') # 33 bytes / 3 times 4 is 44 base64 chars
>>> for tf in imghdr.tests:
... res = tf(sample, None)
... if res:
... break
...
>>> print res
png
我只使用了base64数据的前33个字节来回显imghdr.what()
函数从你传递的文件中读取的内容(它读取32个字节,但该数字不会除以3)。
有一个等效的soundhdr
module,还有python-magic
project允许您传递多个字节来确定文件类型。
答案 1 :(得分:3)
当然,你可以。对于我能想到的问题,几乎没有简单的方法:
每个base64字符对6位输入进行编码,因此您可以按如下方式对它们进行关联:
Base64: AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHH
Data: xxxxxxxxyyyyyyyyzzzzzzzzqqqqqqqqwwwwwwwweeeeeeee
如果你想提取4个字节的数据,从偏移量1开始,如下所示:
................................
Base64: AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHH
Data: xxxxxxxxyyyyyyyyzzzzzzzzqqqqqqqqwwwwwwwweeeeeeee
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
然后,要仅解码您想要的部分,您需要知道位距离。它们很容易计算,只需将字节距离乘以8.现在,在您知道需要32位后,从第8位开始,您可以找到包含起始位的base64字符。为此,请将您的offset
和offset+length
加密为6:
start = bit 8 = char 1 + bit 2
end = bit 40 = char 6 + bit 4
好吧,这映射到上面的方案 - 你的span在1个完整的base64 char和2位之后开始,并在6个完整的base64 chars和4位之后结束。
现在,在您知道所需的确切base64字符后,您需要解码它们。为此,利用现有的base64解码器是有意义的,因此我们不需要自己处理base64编码。要做到这一点,你应该知道base64代码的每4个字符对应3个字节的数据。所以,这里有诀窍 - 你可以在你提取的base64代码前加上乱码,直到base64和byte边界对齐 - 并且知道base64解码器会产生多少无效输入,抛弃过多。
因此,前置多少取决于位余数的值。如果起始位余数为0,则表示A
和x
已对齐,因此无需进行任何更改:
|==========================
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
|==========================
如果位余数为2,则需要预先添加一个base64 char,并在解码后抛出一个前导字节:
##|==================
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
|==================
如果位余数为4,则需要预先添加两个base64字符,并在解码后丢弃两个前导字节:
####|==========
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
|==========
同样适用于尾随。如果结束位余数为零,则不做任何更改:
===|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
===|
如果结束位余数为2,则需要附加两个base64字符,并抛出两个尾随字节:
=========##|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
===========|
如果结束位余数为4,则需要附加一个base64字符,并抛出一个尾随字节:
===============####|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data: ...xxxxxxxxyyyyyyyyzzzzzzzz...
===================|
因此,对于上面的合成示例,需要预先添加一个字符(而不是A
),并附加一个字符(代替H
):
................................
Base64: ??????BBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGG??????
Data: ????????yyyyyyyyzzzzzzzzqqqqqqqqwwwwwwww????????
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
现在,解码后,从头部和尾部扔出额外的字节,你就完成了。
想象一下,你有像?PNG\r\n??????IHDR
这样的魔法。然后,要检查base64编码的字符串是否与您的魔法匹配,您可以识别魔法中已知的字节,以及它们的位偏移和长度:
"PNG\r\n" -> offset = 8, length = 40
"IHDR" -> offset = 96, length = 32
所以,使用我们上面的想法:
"PNG\r\n" -> start = 8 ( char 1, bits = 2 ), end = 48 ( char 8, bits = 0 )
"IHDR" -> start = 96 ( char 16, bits = 0 ), end = 128 ( char 21, bits = 2 )
要解码"PNG\r\n"
部分,你需要取7个完整的base64字符,从字符1开始,然后加上1个字符,解码,丢弃1个前导字节并进行比较。
要解码"IHDR"
部分,你需要取6个base64字符,从char 16开始,然后追加2个字符,解码,抛出2个尾随字节并进行比较。
我上面描述的替代方法不是翻译数据,而是自己翻译魔法。
所以,如果你有魔术?PNG\r\n??????IHDR
(我已经替换了\r
和\n
用于演示目的),就像上面的示例一样,当编码为base64时,它看起来像这样:
Data: [?PN] [Grn] [???] [???] [IHD] [R??]
Base64: (?~BO) (Rw0K) (????) (????) (SUhE) (Ug==)
在?~BO
部分中,~
符号仅部分随机。让我们按位看一下这个结构:
Data: ????????PPPPPPPPNNNNNNNN
Base64: ??????~~~~~~BBBBBBOOOOOO
因此,~
只有两个低位是真正未知的,这意味着您可以在对数据进行魔法测试时使用该信息,以缩小魔法范围。
对于这种特殊情况,这里列出了所有编码的详尽清单:
Data: ??????00PPPPPPPPNNNNNNNN
Base64: ??????FFFFFFBBBBBBOOOOOO => ?FBO
Data: ??????01PPPPPPPPNNNNNNNN
Base64: ??????VVVVVVBBBBBBOOOOOO => ?VBO
Data: ??????10PPPPPPPPNNNNNNNN
Base64: ??????llllllBBBBBBOOOOOO => ?lBO
Data: ??????11PPPPPPPPNNNNNNNN
Base64: ??????111111BBBBBBOOOOOO => ?1BO
同样适用于尾随R??
组,但因为有4个未定义位而不是2个,所以排列列表更长:
Ug?? <= 0000???? ????????
Uh?? <= 0001???? ????????
Ui?? <= 0010???? ????????
Uj?? <= 0011???? ????????
Uk?? <= 0100???? ????????
Ul?? <= 0101???? ????????
Um?? <= 0110???? ????????
Un?? <= 0111???? ????????
Uo?? <= 1000???? ????????
Up?? <= 1001???? ????????
Uq?? <= 1010???? ????????
Ur?? <= 1011???? ????????
Us?? <= 1100???? ????????
Ut?? <= 1101???? ????????
Uu?? <= 1110???? ????????
Uv?? <= 1111???? ????????
所以,在regexp中,?PNG\r\n??????IHDR
的base64-magic看起来像这样:
rx = re.compile(b'^.[FVl1]BORw0K........SUhEU[g-v]')
if rx.match(base64.b64encode(b'xPNG\r\n123456IHDR789foobar')):
print('Yep, it works!')
答案 2 :(得分:-2)
它的PNG图像
import base64
encoded_string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
decoded_string = base64.b64decode(encoded_string)
print 'Decoded :', decoded_string
输出:
python base_decode.py
Decoded : �PNG