撕开 File 的底裤,探寻十六进制的秘密。
实际开发工作中,经常遇到要校验文件类型的需求。
因为文件扩展名可修改的特性,很有些不正常的文件,通过强制修改文件扩展名等方式,“猪鼻子插大葱,装像”。
例如重命名 .jpg 格式的图片为 .png、重命名 .mp4 格式的视频为 .mov,或者重命名.png格式的图片为.mp4格式的视频,想把图片或视频的格式改了,从文件扩展名上看貌似是改了,实则并没有。所以单纯从文件扩展名来判断文件类型和格式,并不准确。需要找到一种可以准确解析文件类型和格式的方法。
关键词: 文件签名 、 魔术数字 、 文件扩展名 、 ArrayBuffer 、 DataView 、 File signatures 、 magic number
等我捋一捋,弄几个demo,组织一下语言,再来编写
识别文件类型的几种方式
以 React-logo.png 图片为例
文件扩展名
扩展名是指文件名中最后一个点 . 号后的字母序列。扩展名方式的一个特点是,更改文件扩展名会导致系统误判文件格式。
内部元数据(Internal metadata)
识别文件格式的第二种方法是使用文件本身存储的有关格式的信息,这些信息要么是为此目的而设的信息,要么是在某些格式的文件中始终位于特定位置的二进制字符串。由于最容易找到它们的地方是在开头,因此当此类区域大于几个字节时,通常称为文件头,如果只有几个字节长, 则称为魔术数字。
文件头(File header)
文件头中包含的元数据通常存储在文件开头,但也可能存在于其他区域,通常包括结尾,具体取决于文件格式或所含数据类型。基于字符(文本)的文件通常具有基于字符的头,而二进制格式通常具有二进制头。
采用这种方式可以更好的防止对文件格式发生误判,并且可以给出关于文件格式的更详细的信息。除了识别文件格式外,文件头还可能包含有关文件及其内容的元数据。例如,大多数图像文件存储有关图像格式、大小、分辨率和色彩空间的信息,以及可选的创作信息,例如图像的制作者、制作时间和地点、使用的相机型号和摄影设置(Exif)等。
操作系统可以使用文件头来快速收集有关文件的信息,而无需将所有内容加载到内存中,但这样做会比直接从目录信息中读取占用更多的计算机资源。
例如GIF图形文件是将文件开始处的六个字节作为特征签名的,它可以是GIF87a或者GIF89a。但也有些文件很难通过这种方式识别,比如HTML文件。
魔术数字(magic number)
魔术数字指缺乏解释或命名的独特数值,也可以指其他非数字的值,例如字符,字符串等等。
合并文件类型元数据的一种方法是将“魔术数字”存储在文件本身中,最初,该术语用于表示文件开头的一组特定的 2 字节标识符,但由于任何二进制序列都可以视为数字,因此可以使用文件格式的任何特征来唯一区分它,以进行识别。
在特定文件格式中加入固定数值和固定字符串,然后便可以通过检查文件是否包含这些数据来快速地识别文件格式。例如:GIF文件开头会包含GIF87a(47 49 46 38 37 61)或GIF89a(47 49 46 38 39 61)这两种字符串。
魔法数字方法可以更好地保证格式能够被正确识别,并且通常可以确定有关文件的更精确信息。由于合理可靠的“魔法数字”测试可能相当复杂,并且必须根据魔法数据库中的每种可能性对每个文件进行有效测试,因此这种方法相对低效,尤其是对于显示大量文件列表。此外,必须从文件本身读取数据,这会增加延迟。
外部元数据(External metadata)
存储文件格式的最后一种方法是将有关格式的信息明确存储在文件系统中,而不是文件本身中。
这种方法将元数据与主数据和名称分开,但与文件扩展名或“魔法数字”相比,其可移植性较差,因为格式必须在不同的文件系统之间转换。
识别方式对比
| 文件扩展名 | 内部元数据 | 外部元数据 | |
|---|---|---|---|
| 解析速度 | 快 | 较慢 | -- |
| 准确度 | 不一定准确 | 准确 | 移植性差 |
为保证准确度,采用解析内部元数据的方式来进行识别文件。为了提高解析速度,只解析内部元数据中文件签名的部分,校验魔术数字是否匹配。
1.理论依据
文件格式
文件格式(file format),又称文件类型,是指电脑为了存储信息而使用的对信息的特殊编码方式,用于识别内部储存的资料。文件格式是信息在计算机文件中存储时进行编码的标准方式。它指定如何使用位在数字存储介质中对信息进行编码。文件格式可以是专有的,也可以是免费的。
比如有的储存图片,有的储存程序,有的储存文字信息。每一类信息,都可以以一种或多种文件格式保存在电脑存储中。
每一种文件格式通常会有一种或多种文件扩展名可以用来识别,但也可能没有扩展名。扩展名可以帮助应用程序识别的文件格式。
同一个文件格式,用不同的程序处理可能产生截然不同的结果。例如Word 文件,用Microsoft Word观看的时候,可以看到文本的内容,而以无格式方式在音乐播放软体中播放,产生的则是噪声。一种文件格式对某些软体会产生有意义的结果,对另一些软体来看,就像是毫无用途的数位垃圾。
MIME类型
MIME用一个类型/子类型表示文件的类型。例如:text/html代表文件是HTML文件,image/gif表示GIF文件。
MIME 类型不是文件元数据的一部分,而是通过文件的扩展名或文件签名推断出来的。
文件系统的元数据中通常不会包含 MIME 类型,它是操作系统或网络协议(如 HTTP)用来识别文件类型的一种方式。
操作系统或网络传输中会根据文件扩展名或内容动态识别 MIME 类型,而不是依赖于文件本身存储 MIME 类型。
文件扩展名
扩展名(Filename Extension)是早期操作系统用来标志文件格式的一种机制。以DOS来说,一个扩展名是跟在文件主名后面的,由一个分隔符号分隔。在一个像“example.txt”的文件名中,example是文件主名,txt为扩展名,表示这个文件是一个纯文字文件,句号“.”就是文件主名与扩展名的分隔符号。
由于没有标准的扩展名列表,因此多种格式可以使用相同的扩展名。有心传播病毒的黑客会伪装文件名肆意传播病毒,危害用户信息安全。
文件扩展名更重要的作用是让系统决定当用户想打开这个文件的时候用哪种软件运行。
这种命名法有着很大的安全缺陷,所以某些操作系统已经不再遵循扩展名的规范,而是采用更精确的文件魔术数字(magic number)来判断文件类型。不过Windows系列的操作系统即使是最新的Windows 11都依然保持这种命名格式。
扩展名本身不是文件元数据的一部分,重命名文件扩展名不会改变元数据。
文件签名(File signatures)
文件签名用于识别或验证文件内容的数据,此类签名也称为魔术数字或魔术字节,是位于文件开头的一个独特的字节序列。
工作原理
软件程序利用文件签名来了解文件类型并选择适当的操作。例如,扩展名为 .docx 的文件具有特定的文件签名,表明它是 Microsoft Word 文档。当用户尝试打开文件时,软件首先读取文件签名以确认其类型,然后再继续显示内容。
文件签名通常由固定的字节序列组成,通常采用十六进制或 ASCII 格式。这些序列对于每种文件类型都是唯一的,并且由软件开发人员预定义。文件签名通常位于文件的开头,但也可以在文件的其他位置找到。
文件签名的主要目的是提供一种可靠且有效的方法来识别不同平台和操作系统上的文件类型。这样一来,软件程序就可以正确识别和处理文件,而不管使用什么文件扩展名。通过依赖文件签名,应用程序可以避免潜在的安全风险,并防止用户在错误的程序中错误地打开文件。
2.数据获取
技术实现: xiazhaohui.com/test
JavaScript实现
- js读取文件
- 转成
arrayBuffer数据流 - 将
arrayBuffer放到DataView - 使用
DataView底层接口getInt8/getUit8读取十六进制字节
xxd 命令
xxd 是一个用于查看和编辑文件十六进制表示的工具,通常预装在类 Unix 操作系统(如 Linux 和 macOS)中。它可以将二进制文件转换为可读的十六进制格式,方便进行分析和调试。你也可以将十六进制数据再转换回原始的二进制格式,xxd 支持双向操作。
主要功能
- 将文件转为十六进制显示:
xxd可以读取一个二进制文件,并以十六进制和 ASCII 格式显示文件内容。 - 十六进制编辑:你可以编辑十六进制数据,并将修改后的内容保存回文件。
- 转换回原始格式:
xxd不仅可以将文件转换为十六进制,还可以将格式化后的十六进制数据重新转换为二进制文件。
# 查看文件的十六进制表示
xxd React-log.png
# 只显示前100个字节
xxd -l 100 example.png
# 转换并保存为十六进制文件
xxd React-logo.png > React-logo.hex
3.数据分析
采用文件扩展名+MIME类型+文件签名的多重验证方式,来校验文件类型
文件签名
Media type列表:www.iana.org/assignments…
小工具:
图片(png、jpg、jpeg、gif)
PNG
datatracker.ietf.org/doc/html/rf…
PNG 类型的文件 magic number 为
89 50 4E 47 0D 0A 1A 0A,数据流的前八个字节始终包含此十六进制值。
暂时无法在飞书文档外展示此内容
复制react-logo.png文件,并重命名为react-logo的副本.mp4,文件扩展名name和媒体类型mime type改变了。但是文件元数据的魔术数字并没有改变,依旧是png的文件签名,这是由对应文件标准规范制定的语法结构。
有效的 PNG 图像必须包含 IHDR块、一个或多个 IDAT 块以及一个 IEND 块。
JPG/JPEG
jpg/jpeg格式的图片文件,magic number 为
FF D8 FF DB、FF D8 FF E0、FF D8 FF E1、FF D8 FF EE
标准不同,magic number也有不同。
JPEG File Interchange Format(JFIF)
JPEG 文件交换格式是一项图像文件格式标准。JPEG 容器格式的基本规范在 JPEG 标准的附件 B 中定义,称为JPEG 交换格式(JIF)。JFIF 建立在 JIF 的基础上,解决了 JIF 的一些限制,包括不必要的复杂性、组件样本注册、分辨率、宽高比和色彩空间。
JFIF与较新的可交换图像文件格式(Exif)互相不兼容。
Exchangeable image file format(Exif)
可交换图像文件格式是一种标准,它指定了数码相机(包括智能手机)、扫描仪和其他处理数码相机录制的图像和声音文件的系统使用的图像、声音和辅助标签的格式。该标准包括Exif图像文件规范和Exif音频文件规范。
当 Exif 用于JPEG文件时,Exif 数据存储在 JPEG 定义的实用程序应用程序段之一中,即 APP1(段标记 0xFFE1),实际上其中包含整个 TIFF 文件。
GIF
gif格式的图片文件,magic number 为
47 49 46 38 39 61、47 49 46 38 37 61
图像互换格式(英语:Graphics Interchange Format,简称GIF)是一种位图,以8位色(即256种颜色)重现真彩色的图像。它实际上是一种压缩文档,采用LZW压缩算法进行编码,有效地减少了图像文件在网络上传输的时间。它是目前万维网广泛应用的网络传输图像格式之一。
GIF 文件的文件头包括一个 6 字节的固定标识符,指示文件格式和版本号:
-
GIF87a:早期版本,定义于 1987 年。
-
GIF89a:较新的版本,定义于 1989 年。
虽然两者都是 GIF 文件格式,但 GIF89a 引入了更多的功能和改进。以下是它们的主要区别:
透明度支持
- GIF87a:不支持透明背景。这意味着所有像素都必须有一个具体的颜色,无法指定某个颜色作为透明色。
- GIF89a:引入了透明度支持,可以指定某个颜色索引为透明色。这样在浏览器或其他支持透明 GIF 的工具中,背景可以显示为透明。
图像间延迟控制(动画)
- GIF87a:不支持动画。只能显示静态图片。
- GIF89a:支持动画。
GIF89a引入了帧延迟功能,可以设置多个帧组成一个简单的动画序列,并且可以控制帧之间的显示时间。
应用扩展块
- GIF87a:没有扩展块的概念,因此不支持元数据或扩展功能。
- GIF89a:引入了扩展块的概念,可以嵌入元数据、控制动画帧的播放顺序和时间间隔,还可以包括应用扩展块以便自定义数据的存储。
循环播放
-
GIF87a:没有循环播放功能,GIF 文件仅显示一次。
-
GIF89a:允许控制是否循环播放,使用扩展块来定义动画是否在结束时重新开始。
gif由多种不同类型块组成
- 未标记块:文件头、逻辑屏幕描述符、全局颜色表、局部颜色表
- 控制块:图形控制扩展
- 图形渲染块:纯文本扩展、图像描述符
- 特殊用途块:应用扩展、评论扩展、数据流结束标记
- 图像数据块:图像数据
图源:[gif-parser]带你如何用js读取gif图片数据流,解码gif
图源:[gif-parser]带你如何用js读取gif图片数据流,解码gif
视频(mp4、mov)
MP4
mp4格式的视频文件,magic number 为
00 00 00 xx 66 74 79 70 69 73 6F 6D、00 00 00 xx 66 74 79 70 6D 70 34 31、00 00 00 xx 66 74 79 70 6D 70 34 31
制定视频文件流中文件签名规范的标准化组织主要是 ISO/IEC 和 MPEG,其中 ISO/IEC 14496 系列标准(特别是第12部分,即 ISO/IEC 14496-12)规定了 MP4 文件格式的基本结构和文件签名的相关规范。
ISO/IEC 14496-12 是定义 MP4 文件格式的核心标准,它定义了所谓的 ISO Base Media File Format (ISOBMFF) 。这个标准描述了文件的结构,包括如何存储视频、音频和其他媒体数据,如何使用 ftyp box 来标识文件类型,以及如何确保文件的兼容性和可扩展性。
主要内容:
-
文件结构:标准规定了 MP4 文件的组织方式,包括各个 box(例如
ftyp,moov,mdat等)的定义。 -
文件头 (
ftypbox) :标准详细描述了如何使用ftypbox 来定义文件类型和兼容性,以及ftypbox 的大小、类型和兼容性品牌(major_brand, minor_version, compatible_brands)的格式。 -
兼容性:标准中规定了如何通过
ftypbox 指定文件的兼容性(例如isom,mp41,mp42等),这些信息帮助播放器和处理器识别和解析文件。
MPEG 是一个由多个国家和公司组成的标准化组织,负责制定与视频、音频和其他多媒体数据相关的标准。MPEG 标准系列中,MPEG-4 系列(包括 MP4 格式)是 ISO/IEC 14496 标准的基础。
主要内容:
-
MPEG-4 Part 12: 基于 ISO/IEC 14496-12 的文件格式标准,用于多媒体内容的封装。
-
MPEG-4 Part 14: 具体定义了 MP4 文件格式的扩展。
签名 66747970 就是 ftyp 的十六进制表示。前面的字节(如 00000020 或 00000018)表示 ftyp box 的大小,包含了 box 头和内容的总字节数。
-
00000018 66747970:
00000018表示 box 的大小为 24 字节(16进制的18等于 24)。66747970表示该 box 是ftyp文件类型。
-
00000020 66747970:
00000020表示 box 的大小为 32 字节(16进制的20等于 32)。66747970同样是ftyp。
不同的 MP4 文件签名,虽然表示的内容和作用类似,但由于文件头的长度、编码器差异和文件兼容性信息的不同,文件头字节数会有所变化。
一些常见的 MP4 文件签名示例
00000018 66747970 69736F6D:66747970 是 ftyp,69736F6D 是 isom,表示这个 MP4 文件是以 ISO Base Media File Format (ISO/IEC 14496-12) 进行封装的。
00000020 66747970 6D703431:66747970 是 ftyp,6D703431 是 mp41,表示这个 MP4 文件使用了 MP4 v1 (ISO 14496-1) 格式。
00000024 66747970 6D703432:66747970 是 ftyp,6D703432 是 mp42,表示这个 MP4 文件使用了 MP4 v2 格式。
MOV
mov格式的视频文件,magic number 为
00 00 00 xx 66 74 79 70 71 74 20 20
MOV 格式是 Apple 的 QuickTime 文件格式的一部分,通常以 .mov 为扩展名。MOV 文件的签名(magic numbers)帮助识别文件格式及其结构。MOV 文件格式基于 ISO Base Media File Format(ISO/IEC 14496-12),并使用与 MP4 文件格式类似的签名。
MP4 和 MOV 格式的视频文件在文件头部结构上确实存在相似之处,尤其是开头的魔术数字部分。它们的文件结构基于相同的ISO 基本媒体文件格式 (ISO Base Media File Format, ISO/IEC 14496-12) ,因此在前几个字节的结构上有一定的共性。具体来说,它们的文件头通常是以类似的 00 00 00 xx 66 74 79 70 开始,但 xx 之后的值和后续的字节可以用来区别 MP4 和 MOV 格式。
MOV 文件的文件头通常以 00000014 66747970 71742020 开始,其中 00000014 是 ftyp box 的大小,66747970 是 ftyp 的十六进制表示,71742020是qt,表示 QuickTime 文件格式。
音频(mp3、wav、m4a、aac、flac、ogg)
MP3 (MPEG-1 Audio Layer 3)
wav格式的音频文件,magic number 为
49 44 33、FF FB、FF F3
魔术数字: MP3 文件的魔术数字可以是 49 44 33 (ID3 标签),也可以是 MPEG 音频帧头 FF FB、FF F3 等。
WAV (Waveform Audio File Format)
wav格式的音频文件,magic number 为
52 49 46 46
魔术数字: 52 49 46 46 (RIFF)
-
解析:
-
WAV 文件基于 RIFF (Resource Interchange File Format) 文件格式。
-
文件的前四个字节是
52 49 46 46,即 ASCII 字符串 "RIFF",接着是文件大小,然后是57 41 56 45(WAVE),表示这是一个 WAV 文件。
-
M4A (MPEG-4 Audio Layer)
m4a格式的音频文件,magic number 为
00 00 00 xx 66 74 79 70 4D 34 41 20
魔术数字: 00 00 00 1C 66 74 79 70 4D 34 41 20
-
解析:
-
M4A 是基于 MPEG-4 容器格式的音频文件,它的魔术数字标识是典型的 MP4 文件标识。
-
前四个字节表示文件头长度(通常为
00 00 00 1C),然后是 "ftyp" box 类型的标识符 (66 74 79 70)。 -
接下来的字节
4D 34 41 20("M4A ") 指示这是一个 M4A 文件。
-
其中:
00 00 00 20是 box 的长度。66 74 79 70("ftyp") 标识文件类型框。ftyp是文件的文件类型标识符,它定义了文件的类型(例如 MP4、M4A),并指定该文件所使用的兼容标准。4D 34 41 20("M4A ") 表示 M4A 文件。
AAC (Advanced Audio Coding)
aac格式的音频文件,magic number 为
FF F1、FF F9、41 44 49 46
魔术数字: AAC 文件的魔术数字依赖于它的封装格式,通常有两种封装方式:ADTS 和 ADIF。
ADTS 封装的 AAC:
-
魔术数字:
FF F1或FF F9 -
解析:
- ADTS (Audio Data Transport Stream) 是 AAC 的流式封装格式。
- 魔术数字的前两个字节
FF F1表示这是 MPEG-4 音频,FF F9表示 MPEG-2 音频。
ADIF 封装的 AAC:
-
魔术数字:
41 44 49 46(ADIF) -
解析:
-
ADIF (Audio Data Interchange Format) 是另一种封装格式,通常出现在非流式存储中。
-
前四个字节为
41 44 49 46,即 ASCII 字符串 "ADIF"。
-
FLAC (Free Lossless Audio Codec)
flac格式的音频文件,magic number 为
66 4C 61 43
魔术数字: 66 4C 61 43 (fLaC)
解析: FLAC 文件的前四个字节是 66 4C 61 43,即 ASCII 字符串 "fLaC"。
OGG (Ogg Vorbis Audio)
aac格式的音频文件,magic number 为
4F 67 67 53
魔术数字: 4F 67 67 53 (OggS)
解析: OGG 文件格式的魔术数字是 4F 67 67 53,即 ASCII 字符串 "OggS",这标识了文件为 Ogg 容器格式。
这是一项巨大的工作,只能窥其豹一,到此为止吧。
4、文件签名参考表
| 文件类型 | 文件格式 | MIME 类型 | 文件签名数据 | 备注 |
|---|---|---|---|---|
| 图片 | png | image/png | 89 50 4E 47 0D 0A 1A 0A | |
| jpg/jpeg | image/jpeg | FF D8 FF DB FF D8 FF E0 FF D8 FF E1 FF D8 FF EE | 文件格式标准:JFIF、Exif | |
| gif | image/gif | 47 49 46 38 39 61 47 49 46 38 37 61 | 前三个字节称为签名,接下来的三个字节称为版本(GIF89a、GIF87a) | |
| 视频 | mp4 | video/mp4 | 00 00 00 xx 66 74 79 70 69 73 6F 6D00 00 00 xx 66 74 79 70 6D 70 34 3100 00 00 xx 66 74 79 70 6D 70 34 32 | Box大小的范围:00 00 00 08 至 FF FF FF FFftyp:66 74 79 70兼容标准:isom、mp41、mp42 |
| mov | video/quicktime | 00 00 00 xx 66 74 79 70 71 74 20 20 | Box大小的范围:00 00 00 08 至 FF FF FF FFftyp:66 74 79 70兼容标准:qt | |
| webm | video/webm | 1A 45 DF A3 | EBML 标识符,指示这是一个 EBML 编码的文件 | |
| flv | video/x-flv | 46 4C 56 | ASCII 字符串 "FLV" | |
| 音频 | mp3 | audio/mpeg | 49 44 33FF FBFF F3 | |
| aac | audio/aac | FF F1 FF F9 41 44 49 46 | AAC 文件的魔术数字依赖于它的封装格式,通常有两种封装方式:ADTS 和 ADIFADTS:魔术数字的前两个字节 FF F1 表示这是 MPEG-4 音频,FF F9 表示 MPEG-2 音频ADIF:前四个字节为 41 44 49 46,即 ASCII 字符串 "ADIF" | |
| wav | audio/wav | 52 49 46 46 | WAV 文件基于 RIFF (Resource Interchange File Format) 文件格式文件的前四个字节是 52 49 46 46,即 ASCII 字符串 "RIFF",接着是文件大小,然后是 57 41 56 45 (WAVE),表示这是一个 WAV 文件 | |
| m4a | audio/x-m4a | 00 00 00 xx 66 74 79 70 4D 34 41 20 | M4A 是基于 MPEG-4 容器格式的音频文件,它的魔术数字标识是典型的 MP4 文件标识Box大小的范围:00 00 00 08 至 FF FF FF FFftyp:66 74 79 70兼容标准:M4A | |
| flac | 66 4C 61 43 | ASCII 字符串 "fLaC" | ||
| ogg | 4F 67 67 53 | ASCII 字符串 "OggS" | ||
| Txt | txt | text/plain | E5 8C 97 E5 9B BD | 暂未验证 |
| Excel | xls | 暂未验证 | ||
| xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | 50 4B 03 04 0A 00 | 暂未验证 | |
| application/pdf | 25 50 44 46 2D 31 2E 37 | 暂未验证 | ||
| PPT | pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation | 50 4B 03 04 | 暂未验证 |
| Word | doc | 50 4B 03 04 | 暂未验证 | |
| docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | 50 4B 03 04 | 暂未验证 | |
| ZIP | zip | application/zip | 50 4B 03 04 | 暂未验证 |
参考文档
未完待续。。。