相关文章推荐
稳重的芹菜  ·  aix shell脚本sleep毫秒执行一次-掘金·  2 年前    · 
刚毅的韭菜  ·  使用DataGrip连接SQL ...·  2 年前    · 
冷冷的牛肉面  ·  树莓派ffmpeg直播推送,nginx视频流 ...·  2 年前    · 
Code  ›  文件格式的幻数File Format and Magic Number_文件幻数_BloodyEve的博客
文件目录 文件头 文本文件格式 图片文件格式
https://blog.csdn.net/qq_35980805/article/details/106725455
活泼的打火机
2 年前
    • 前言
    • Magic Number是什么?
    • 文件类型的Magic number
      • 常见文件头幻数
      • 幻数在zipfile.py中的应用
        • zip文件格式
          • 中央目录结束记录End of central directory record(EOCD)
          • 中央目录结构Central directory
          • 文件数据区file data
        • 判断是否为zip的py代码
          • is_zipfile 判断是否为zip文件
          • _check_zipfile 检查zip文件格式幻数是否为正确
          • _EndRecData 读取中央目录结束记录
          • 中央目录结束记录的幻数
        • 补充:文章中引用的位反序的分析
        • 附录
          • struct支持的格式
          • End of central directory record

          最近在看python自带的zipfile的时候发现有一个函数判断文件是否为zip格式时,采用解析获取文件的魔数并以此来判断文件类型。感觉甚为神奇。

          小白的学习之旅开启,下面就幻数、文件类型的幻数以及有什么应用做分享~

          Magic Number是什么?

          简单来说就是看似毫无意义的数值,却能在程序中完成某种功能,例如:

          //  位元反序
          //  magic number - 0x00082082、0x01122408、255
          //  input:   0123456
          //  output:  6543210
          // unsigned char 的高两位无意义
          unsigned char revert(unsigned char i){
              return ((i * 0x00082082) & 0x01122408) % 255;
          

          (对幻数有了解,不想看作者瞎比比的可以直接跳到最后看看位反序的算法思路,很有趣哦~~PS||:如果你觉得幻数在算法中的应用很有趣,想了解其他数学思想的幻数实现方式可以阅读知乎的这篇文章进行探索《magic number的由来和推导》)

          在编程中有以下三类使用(From Wiki:)

          1. 无法解释其意义的唯一值或者在程序中多次被引用但又可被命名常数所替代的值。
          2. 用来识别文本类型的一个常量数值或字符。
          3. 不易被误解为其他意义的特有值,如全局唯一标识符。

          文件类型的Magic number

            虽然从刚才的内容中可以看到magic number有三种用法,但在这里我仅对magic number在识别文件类型的应用——即format indicators格式指示器做学习分享。

            首先我们要知道文件类型是操作系统为存储信息而使用的对信息的特殊编码,用于识别内部存储的资料。即每一类信息,可以以一种或多种文件格式保存在电脑存储中。在操作系统中,幻数是用于标识文件格式的常量;通过用常量来区分运行时文件类型。接下来了解一下常见的文件幻数。如果对magic number在文件格式应用起源很感兴趣的同学可以去看看
          wiki。

          常见文件头幻数

          JPEG (jpg),文件头:FFD8FF
          PNG (png),文件头:89504E47
          GIF (gif),文件头:47494638
          TIFF (tif),文件头:49492A00
          Windows Bitmap (bmp),文件头:424D
          CAD (dwg),文件头:41433130
          Adobe Photoshop (psd),文件头:38425053
          Rich Text Format (rtf),文件头:7B5C727466
          XML (xml),文件头:3C3F786D6C
          HTML (html),文件头:68746D6C3E
          Email [thorough only] (eml),文件头:44656C69766572792D646174653A
          Outlook Express (dbx),文件头:CFAD12FEC5FD746F
          Outlook (pst),文件头:2142444E
          MS Word/Excel (xls.or.doc),文件头:D0CF11E0
          MS Access (mdb),文件头:5374616E64617264204A
          WordPerfect (wpd),文件头:FF575043
          Adobe Acrobat (pdf),文件头:255044462D312E
          Quicken (qdf),文件头:AC9EBD8F
          Windows Password (pwl),文件头:E3828596
          ZIP Archive (zip),文件头:504B0304
          RAR Archive (rar),文件头:52617221
          Wave (wav),文件头:57415645
          AVI (avi),文件头:41564920
          Real Audio (ram),文件头:2E7261FD
          Real Media (rm),文件头:2E524D46
          MPEG (mpg),文件头:000001BA
          MPEG (mpg),文件头:000001B3
          Quicktime (mov),文件头:6D6F6F76
          Windows Media (asf),文件头:3026B2758E66CF11
          MIDI (mid),文件头:4D546864
          

          幻数在zipfile.py中的应用

            python自带的zipfile.py中用幻数来判断文件是否为zip包。但是在讲解代码前,需要再科普一下zip文件格式。

          zip文件格式

            这里简要的对zip文件格式进行一个简介(zip详细格式内容参照官方文档)。zip文书格式由文件数据区、中央目录结构、中央目录结束节(文件尾)组成。其中中央目录结束节中有一个字段保存了中央目录开始处的偏移。

          文件数据区
          中央目录结构
          中央目录结束记录
          中央目录结束记录End of central directory record(EOCD)
          • 中央目录结束记录:被用于标识中央目录结束,可以通过读取它来找到中央目录并解析整个文件结构。结构EndLocator的目录结束标记固定值为0x06054b50。
            • 结构包含:
            signature目标结束标记:0x06054b504bytes
            elDiskNumber当前磁盘编号2bytes
            elStartDiskNumber中央目录开始位置的磁盘编号2bytes
            elEntriesOnDisk该磁盘上所记录的核心目录数量2bytes
            elEntriesInDirectory中央目录结构总数2bytes
            elDirectorySize中央目录的大小4bytes
            elStartDicOffetInDisk相对启动磁盘编号的中央目录启动偏移量4bytes
            elCommentLen注释长度2bytes
            elComment注释内容大小不定
          中央目录结构Central directory
          • 中央目录结构:位于文件数据区后,用于保存所有文件的路径信息和对应文件数据结构区在文件中的偏移。其中中央文件头标识固定为0x02014b50。

            • 结构包括:
            signature中央目录文件header标识:0x02014b50
            deVersionMadeBy压缩所用的pkware版本
            deVersionMadeBy压缩所用的pkware版本
            deVersionToExtract解压所需pkware的最低版本
            deFlags通用位标记
            deCompression压缩方法
            deFileTime文件最后修改时间
            deFileDate文件最后修改日期
            deCrcCRC-32校验码
            deCompressedSize压缩后的大小
            deUncompressedSize未压缩的大小
            deFileNameLength文件名长度
            deExtraFieldLength扩展域长度
            deFileCommentLength文件注释长度
            deDiskNumberStart文件开始位置的磁盘编号
            deInternalAttributes内部文件属性
            deExternalAttributes外部文件属性
            deHeaderOffset本地文件头的相对位移
            deFileName目录文件名
            deExtraField扩展域
            deFileComment文件注释内容
            • 中央目录结构区由中央目录结构的数组组成(包含目录文件名、扩展域、不定长度的文件注释内容)。
            • 可以通过计算中央目录结构偏移量来遍历所有的文件(next_offset 下个中央目录结构偏移,current_offset当前中央目录结构偏移):next_offset = current_offset + sizeof(DirEntry) - 3sizeof(char) + deFileNameLength + deExtraFieldLength + deFileCommentLength;
            • CRC32校验码与对应文件的数据压缩数据的CRC32校验码可做比对,若不同则数据损坏。<>
          文件数据区file data
          • 文件数据区:保存所有压缩文件数据的区,位于文件头,由压缩数据结构的数组构成。其中文件头标识固定为0x04034b50
            • 结构如下:
            signature文件头标识:0x04034b50
            frVersion解压文件所需 pkware最低版本
            frFlags通用比特标志位(置比特0位=加密)
            frCompression压缩方式
            frFileTime文件最后修改时间
            frFileDate文件最后修改日期
            frCrcCRC-32校验码
            frCompressedSize压缩后的大小
            frUncompressedSize未压缩的大小
            frFileNameLength文件名长度
            frExtraFieldLength扩展区长度
            frFileName文件名
            frExtraField扩展区
            frData压缩数据

          其中frCompression的取值有0~12,代表的含义各不相同。有兴趣可以wiki看看。

          判断是否为zip的py代码

          以下是截取的zipfile.py文件中的部分代码:

          is_zipfile 判断是否为zip文件
          def is_zipfile(filename):
              result = False
              try:
                  if hasattr(filename, "read"):   #解读:判断文件是否有"read"属性
                      result = _check_zipfile(fp=filename)    #解读:通过_check_zipfile判断文件是否为zip
                  else:
                      with open(filename, "rb") as fp:    #解读:若文件没有“read"属性,则以二进制格式打开文件
                          result = _check_zipfile(fp) #解读:通过_check_zipfile判断文件是否为zip
              except OSError:
              return result
          

          zipfile.py中以is_zipfile方法快速判断文件是否为zip格式。首先判断文件是否有属性“read”,若有则通过_check_zipfile快速判断文件是否为zip;若文件无“read”属性,则以二进制格式打开该文件(文件指针位于文件的开头)再通过_check_zipfile判断文件是否为zip。

          那么神奇的_check_zipfile是怎样的呢?

          _check_zipfile 检查zip文件格式幻数是否为正确
          def _check_zipfile(fp):
              try:
                  if _EndRecData(fp): #解读:若文件有正确的文件格式幻数,则返回真
                      return True 
              except OSError:
              return False
          

          _check_zipfile通过_EndRecDate方法读取文件的中央目录结束记录,以此检查zip文件格式。至此,追溯到读取文件标志数据的方法_EndRecData,那我们继续往下分析。

          _EndRecData 读取中央目录结束记录
          def _EndRecData(fpin):
              #解读:确定文件大小
              fpin.seek(0, 2) #解读:移动文件读取指针到文件末尾
              filesize = fpin.tell() #解读:返回文件指针当前的位置,即返回文件大小
              #解读:检查是否为无压缩注释内容的zip文件,若无压缩注释内容则按照以下方式处理(此时,中央目录结束记录是文件的最后一个数据项)
              try:
                  fpin.seek(-sizeEndCentDir, 2)   #解读:移动件读取指针到中央目录结束记录前,sizeEndCentDir为22位
              except OSError:
                  return None
              data = fpin.read()  #解读:读取中央目录结束记录EOCD传给data
              if (len(data) == sizeEndCentDir and
                  data[0:4] == stringEndArchive and
                  data[-2:] == b"\000\000"):
                  # 解读:如果中央目录结束记录没有注释内容且目录结束标记signature准确,则对data进行解包
                  # 判断条件:
                  # 1. 读取的data的大小与zip文件格式既定的中央目录结束记录大小一致(这里的size大小计算在下一个小节进行讲解)
                  # 2. data前4位是signature,是一个固定值。signature等于stringEndArchive(定义的值为b"PK\x05\x06")
                  # 3. 由于没有注释内容,故注释长度为0,注释长度有2位,故data最后两位为b"\000\000"
                  endrec = struct.unpack(structEndArchive, data)  #解读:以strucEndArchive模式串进行解包(模式串在下一个小节进行讲解),解包返回一个元组
                  endrec=list(endrec) #解读: 将解包出来的元组数据转换为列表
                  #解读:对列表附加一个空白的注释和开始偏移量
                  endrec.append(b"")
                  endrec.append(filesize - sizeEndCentDir) #解读: 由于是空白注释,故开始偏移量为文件大小-中央目录文件结束记录(22bytes)大小
                  #解读: 读取"Zip64 end of central directory" 结构(这个结构在本文没有进行介绍,可阅读zip官方文档进行了解)
                  return _EndRecData64(fpin, -sizeEndCentDir, endrec)
              #解读: 若这是一个带压缩注释内容的zip文件或非zip文件,则需要搜索文件末尾以获取中央目录结束记录的标识(此时注释内容是文件的最后一个数据项,并且长度最长为64k)。假定中央目录结束记录的幻数不会出现在注释内容中。
              maxCommentStart = max(filesize - (1 << 16) - sizeEndCentDir, 0) #解读:计算最长注释内容的文件指针位置,文件大小-2^16-中央目录文件结束记录大小(22bytes)
              fpin.seek(maxCommentStart, 0)   #解读:移动文件读取指针到中央目录结束记录前
              data = fpin.read()
              start = data.rfind(stringEndArchive)    #解读:返回stringEndArchive(b"PK\005\006")最后出现的位置,若无匹配则返回-1
              if start >= 0:
                  #解读:读取中央目录结束记录
                  recData = data[start:start+sizeEndCentDir]
                  if len(recData) != sizeEndCentDir:
                      # 解读: 若读取的中央目录结束记录大小与zip文件定义的大小不符,则该文件有损坏,返回none
                      return None
                  endrec = list(struct.unpack(structEndArchive, recData)) #解读::以strucEndArchive模式串进行解包,并将元组结果转为列表
                  commentSize = endrec[_ECD_COMMENT_SIZE] #解读: zip官方文件声明的注释内容大小,为7
                  comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize]    #解读:读取注释内容
                  endrec.append(comment)  #解读:对列表添加注释内容和开始偏移量
                  endrec.append(maxCommentStart + start)
                  #解读: 读取"Zip64 end of central directory"
                  return _EndRecData64(fpin, maxCommentStart + start - filesize,
                                       endrec)
              # 无法找到有效的中央目录结束记录,返回none
              return None
          

          此函数返回文件的中央目录结束记录值或返回none。

          已知的是end of central directory record值存储在zip的“End of central dir”结构(这个结构是个列表)中的第九项,相对启动磁盘编号的中央目录启动偏移量保存在列表的第十项。最终的值由方法_EndRecData64对Zip64 end of central directory record进行读取并返回中央目录结束值。

          _EndRecData64在此不进行具体阐述,有兴趣的同学可以阅读zipfile.py中的def _EndRecData64(fpin, offset, endrec)及zip官方文档中的Zip64 end of central directory record进行学习。

          中央目录结束记录的幻数
          structEndArchive = b"<4s4H2LH"
          stringEndArchive = b"PK\005\006"
          sizeEndCentDir = struct.calcsize(structEndArchive)
          
          • structEndArchive 的值b"<4s4H2LH" 是二进制数,代表的意思是:
            • “<” - 小端模式
            • “4s” - 长度为4的字符串
            • “4H” - 长度为4的无符号短整型
            • “2L” - 长度为2的无符号长整型
            • “H” - 长度为1的无符号整型
            • b"<4s4H2LH"正好是中央目录结束记录ECOD的小端结构

          (备注:struct支持的格式可见附录。对大端小端有兴趣的同学请wiki。)

          • stringEndArchive是中央目录结束记录的前四位值,在zip官方文档中定义的是 b’PK\x05\x06’。
          • sizeEndCentDir是中央目录结束记录的大小,zip文件官方定义的是22bytes。
            • struct.calcsize 用于计算给定的格式b"<4s4H2LH"占用多少字节内存。

          (备注:中央目录结束记录ECOD可见附录。)

          补充:文章中引用的位反序的分析

          unsigned char revert(unsigned char i) {
              return ((i * 0x00082082) & 0x01122408) % 255;
          

          位反序的算法通过三个运算就完成了反转。这里记录下其的分析过程(假设输入为“abcdef”)。

          i = abcdef
                                        abcdef
          * 0x00082082    10000010000010000010
          ------------------------------------
                                       abcdef
                                 abcdef  
                           abcdef
                     abcdef  
          ------------------------------------
                     abcdefabcdefabcdefabcdef0
          

          这里的乘项0x00082082将6位位元向左扩展了3次,并在右侧补一个0。

                           abcdefabcdefabcdefabcdef0
          and 0x01122408   1000100100010010000001000
          ------------------------------------------
                           a000e00b000f00c000000d000
                           ^   ^  ^   ^  ^      ^
                           |   |  |   |  |      |
                          25  21 18   14 11     4
          

          上一步运算的结果abcdefabcdefabcdefabcdef0和 0x01122408 进行与运算后,剩下在 4 / 11 / 14 / 18 / 21 / 25 位分别保持输入 6 位二进制的各个位。

          a000e00b000f00c000000d000 的 256 进制表示为 axyz
          其中 x = e00b0  y = f00c00  z = d000
          因 a + x + y + z < 255
          则 axyz % 255 = a + x + y + z
                                             e00b0
                                     +      f00c00
                                     +        d000
                                     +           a
          ---------------------------------------------
                                            fedcba
          

          最后可以看到位元反序算法完成~

          struct支持的格式

          FormatC TypePython字节数
          xpad byteno value1
          ccharstring of length 11
          bsigned charinteger1
          Bunsigned charinteger1
          ?_Boolbool1
          hshortinteger2
          Hunsigned shortinteger2
          iintinteger4
          Iunsigned intinteger or long4
          llonginteger4
          Lunsigned longlong4
          qlong longlong8
          Qunsigned long longlong8
          ffloatfloat4
          ddoublefloat8
          schar[]string1
          pchar[]string1
          Pvoid *long
          1. q和Q只在机器支持64位操作时有意思
          2. 每个格式前可以有一个数字,表示个数
          3. s格式表示一定长度的字符串,p表示的是pascal字符串
          4. P用来转换一个指针,其长度和机器字长相关
          5. 最后一个可以用来表示指针类型的,占4个字节

          End of central directory record

          数据字节数
          end of central dir signature4 bytes (0x06054b50)
          number of this disk2 bytes
          number of the disk with the start of the central directory2 bytes
          total number of entries in the central directory on this disk2 bytes
          total number of entries in the central directory2 bytes
          size of the central directory4 bytes
          offset of start of central directory with respect to the starting disk number4 bytes
          .ZIP file comment length2 bytes
          .ZIP file comment(variable size)
          var magicNumber = require('magicnumber'); magicNumber.findSmallest(); 检查数字 (123) 是否是幻数的候选者 var magicNumber = require('magicnumber'); console.log('Is 123 a candidate for the magic number? ' + magicNumber.create(123).isValid()); 如何执行单元测试 nodeunit test/magicNumberFinder.js Java简单魔术 这是一个“魔术”数字包,它允许从文件和字节数组确定内容类型(MIME类型)。 它利用magic(5)Unix内容类型文件来实现与Java中检测文件内容的Unix file(1)命令相同的功能。 它使用内部配置文件,或者可以读取/etc/magic , /usr/share/file/magic或其他magic(5)文件,并从File , InputStream或byte[]确定文件内容。 有关更多信息,请访问。 可以在找到源代码。 Maven软件包通过发布 享受,格雷·沃森 要开始使用,请使用SimpleMagic软件包,如下所示: // create a magic utility using the internal magic file ContentInfoUtil util = new ContentInfoUtil (); // if you 5 分钟幻数 快速随机数生成器 我是一个编码菜鸟。 我半辈子都在学习中文,在中国生活,却觉得浪费了我半辈子。 我很幸运遇到了一位好朋友 Mark Powers,他让我看到了能够创造永恒的东西。 因此,对于我的教学工作,有人问我是否有随机数选择器。 使用我掌握的基本 jQuery,我在 5 分钟内完成了我们的演示! 实现幻数转换,如用Javascript和CSS编写的GIF中所示。 我从SNS网站上找到了GIF图片,但不知道其来源。 如果您知道,请给我留下麻烦,我将为您感激:) 计时器: : 日期时间: : 您可以在example文件夹中找到更多演示。 使用bower将illusion-number安装到您的项目中: $ bower install illusion-number 然后包含.js和.css文件: < link rel =" stylesheet " href =" /beep/bower_components/illusion-number/dist/illusion-number.min.css " > < script src =" /beep/bower_components/illusion-number/dist/illusion 通常在应用开发中,文件读写也可能使用文件的幻数。例如读取文件时,用它来判断文件的格式是否匹配,如果不匹配则报告错误不处理文件,或者尝试读取文件的幻数标记来识别。 通常我们根据文件的后缀名来判断文件的类型,一般情况下,这样做是没有问题的,但是如果手动的把文件的后缀名进行了修改(比如,test.txt,修改后的文件为test.doc),此时我们根据后缀名获取文件类型就是错误的。 如果要准确的获取到文件的类型,可以通过获取文件的幻数来判断文件的类型。可以参考以下的方式: 一、获取方法参考: Find File Type From Magic Number Of File In MVC 5 二、常用文件幻数参考: https://gis... 文件上传漏洞是指文件上传功能`没有对上传的文件做合理严谨的过滤`,导致用户可以利用此功能,上传能`被服务端解析执行的文件`,并通过此文件获得`执行服务端命令的能力`。 1、magic number https://www.garykessler.net/library/file_sigs.html 这个文件头部,各种文件的默认数据格式 https://gist.github.com/Qti3e/6341245314bf3513abb080677cd1c93b 常用版本的json: "123": { "signs": [ "0,00001A00051004" "mime": "appl 魔数这个词在不同领域代表不同的含义。在计算机领域,魔数有两个含义,一指用来判断文件类型的魔数;二指程序代码中的魔数,也称魔法值。判断文件类型的魔数很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。因此这几个字节的内容也被称为魔数(magicnumber),因为根据这几个字节的内容就可以确定文件类型。FreeBSD上ELF文件的magicnumber就是文件的前四个字节依次为"7f454c46",对应的ascii字符串即“^?ELF”。或者。......
 
推荐文章
稳重的芹菜  ·  aix shell脚本sleep毫秒执行一次-掘金
2 年前
刚毅的韭菜  ·  使用DataGrip连接SQL Server 2017数据库 - 乐百川 - 简书
2 年前
冷冷的牛肉面  ·  树莓派ffmpeg直播推送,nginx视频流接受转码,客户端hls rtmp视_weiyang_tang的博客-CSDN博客
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号