Python,有人能仅仅通过base64编码猜测文件的类型吗?

Python,有人能仅仅通过base64编码猜测文件的类型吗?,python,base64,Python,Base64,假设我有以下几点: image_data = """iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==""" 这只是一个点图像(来自)。但我不知道它是图像还是文本等。是否有可能理解它是什么,只有这个编码字符串?我在Python中尝试了它,但这也是一个一般性的问题。因此,这两方面的任何见解都是非常受欢迎的。您不能,至

假设我有以下几点:

image_data = """iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="""

这只是一个点图像(来自)。但我不知道它是图像还是文本等。是否有可能理解它是什么,只有这个编码字符串?我在Python中尝试了它,但这也是一个一般性的问题。因此,这两方面的任何见解都是非常受欢迎的。

您不能,至少不能不解码,因为帮助识别文件类型的字节分布在base64字符中,而base64字符与整个字节不直接对齐。每个字符编码6位,这意味着每4个字符编码3个字节

标识文件类型需要访问不同块大小的字节。例如,可以从字节FF D8或FF D9识别JPEG图像,但这是两个字节;后面的第三个字节也必须作为4字符块的一部分进行编码

您可以做的是对base64字符串进行足够的解码,以进行文件类型指纹识别。因此,您可以解码前4个字符以获得3个字节,然后使用前两个字符查看对象是否为JPEG图像。仅从第一个或最后一个字节序列就可以识别大量文件格式(PNG图像可以通过前8个字节识别,GIF可以通过前6个字节识别,等等)。仅从base64字符串中解码这些字节并不重要

您的示例是一个PNG图像;您可以使用以下方法测试图像类型:

我只使用base64数据的前33个字节,以响应
imghdr.what()
函数将从您传递给它的文件中读取的内容(它读取32个字节,但该数字不除以3)

有一个等价的,还有一个允许您传入若干字节以确定文件类型的。这是一个PNG图像

import base64

encoded_string = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='

decoded_string = base64.b64decode(encoded_string)
print 'Decoded :', decoded_string
输出:

python base_decode.py 
Decoded : �PNG

当然可以。我能想到的解决这个问题的方法很少:

部分解码 每个base64字符编码6位输入,因此您可以按如下方式将它们关联起来:

Base64: AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHH
Data:   xxxxxxxxyyyyyyyyzzzzzzzzqqqqqqqqwwwwwwwweeeeeeee
如果要从偏移量1开始提取4个字节的数据,如下所示:

                ................................
Base64: AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHH
Data:   xxxxxxxxyyyyyyyyzzzzzzzzqqqqqqqqwwwwwwwweeeeeeee
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Data:   [?PN]  [Grn]  [???]  [???]  [IHD]  [R??]
Base64: (?~BO) (Rw0K) (????) (????) (SUhE) (Ug==)
rx = re.compile(b'^.[FVl1]BORw0K........SUhEU[g-v]')
if rx.match(base64.b64encode(b'xPNG\r\n123456IHDR789foobar')):
    print('Yep, it works!')
然后,要仅解码您想要的部分,您需要知道位距离。它们很容易计算,只需将字节距离乘以8即可。现在,在您知道需要32位后,从第8位开始,您可以找到base64字符包含的起始位。为此,将您的
偏移量
偏移量+长度
除以6:

start = bit  8 = char 1 + bit 2
end   = bit 40 = char 6 + bit 4
好的,这映射到上面的方案-您的跨度在1个完整的base64字符和2位之后开始,在6个完整的base64字符和4位之后结束

现在,在您知道您想要的确切base64字符后,您需要对它们进行解码。要做到这一点,利用现有的base64解码器是有意义的,因此我们不需要自己处理base64编码。要做到这一点,您应该知道base64代码的每4个字符对应3个字节的数据。所以,这里有一个诀窍-您可以在提取的base64代码前加上乱码,直到base64和字节边界对齐-并且知道base64解码器将产生多少无效输入,抛出多余的

所以,前置多少取决于位余数的值。如果起始位余数为0,则表示
A
x
对齐,因此无需更改:

           |==========================
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
           |==========================
如果位余数为2,则需要在一个base64字符前加上前缀,并在解码后抛出一个前导字节:

                 ##|==================
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
                   |==================
                       ####|==========
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
                           |==========
如果位余数为4,则需要预加两个base64字符,并在解码后抛出两个前导字节:

                 ##|==================
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
                   |==================
                       ####|==========
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
                           |==========
拖尾也是如此。如果结束位余数为零,则无更改:

        ===|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
        ===|
如果结束位余数为2,则需要附加两个base64字符,并抛出两个尾随字节:

        =========##|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
        ===========|
如果结束位余数为4,则需要追加一个base64字符,并抛出一个尾随字节:

        ===============####|
Base64: ...AAAAAABBBBBBCCCCCCDDDDDD...
Data:   ...xxxxxxxxyyyyyyyyzzzzzzzz...
        ===================|
因此,对于上面的合成示例,需要在前面加一个字符(而不是
A
),然后再加一个字符(代替
H
):

现在,在解码之后,从头部和尾部抛出额外的字节,就完成了

实例 想象一下你有一个像
?PNG\r\n?????IHDR
这样的魔法。然后,要检查base64编码字符串是否与magic匹配,您可以在magic中识别已知的字节及其位偏移量和长度:

"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字符,从字符16开始,然后附加2个字符,解码,抛出2个尾随字节并进行比较

翻译魔术 我上面描述的另一种方法不是翻译数据,而是翻译魔术本身

因此,如果您有magic
?PNG\r\n?????IHDR
(出于演示目的,我已替换了
\r
\n
),如上面的示例所示,当编码为base64时,它如下所示:

                ................................
Base64: AAAAAABBBBBBCCCCCCDDDDDDEEEEEEFFFFFFGGGGGGHHHHHH
Data:   xxxxxxxxyyyyyyyyzzzzzzzzqqqqqqqqwwwwwwwweeeeeeee
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Data:   [?PN]  [Grn]  [???]  [???]  [IHD]  [R??]
Base64: (?~BO) (Rw0K) (????) (????) (SUhE) (Ug==)
rx = re.compile(b'^.[FVl1]BORw0K........SUhEU[g-v]')
if rx.match(base64.b64encode(b'xPNG\r\n123456IHDR789foobar')):
    print('Yep, it works!')
?~BO
部分中,
~
符号只是部分随机的。让我们按位查看该构造:

Data:   ????????PPPPPPPPNNNNNNNN
Base64: ??????~~~~~~BBBBBBOOOOOO
因此,只有两个较低的
~
位是真正未知的,这意味着您可以在针对数据测试magic时使用这些信息,以缩小magic的范围

对于这种特殊情况,以下是所有编码的详尽列表:

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???? ????????

OP不是问这个示例是图像还是文本,而是如何确定它。问题:这也是一个一般性问题。从Wikipedia链接上可以明显看出,OP知道问题中的数据示例是一个红色小点的PNG文件,所以问题是如何确定base64编码文件的文件类型。顺便说一句,您不需要导入模块来执行标准bas