ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

无扩展名文件类型识别:从file命令到魔数比对全指南

无扩展名文件类型识别:从file命令到魔数比对全指南 很多人第一次遇到“无扩展名文件”是在网盘下载或者微信接收文件的时候——文件名明明写着“项目资料”或者“final”下载到本地却没有任何后缀。双击弹窗问你要用哪个程序打开面对一列看不懂的软件名只能瞎猜。我过去几年在运维和处理服务器数据的过程中几乎每周都要面对这类文件说句实话识别无扩展名文件的文件类型这件事本身并不难真正难的是你脑子里有没有一套清晰的判断路径。这篇文章就围绕最核心的两个关键词——无扩展名文件、文件类型把这个判断路径从原理到实操完整拆一遍。这篇文章不是只教你敲一条命令就完事而是会把底层原理、核心工具、手工探测方法、批量处理思路和一些极容易踩的坑都覆盖到。适合刚入行的运维、日常需要处理数据的开发以及经常从各种渠道下载文件、发出“这到底是什么文件”疑问的普通用户。1. 无扩展名文件从哪里来先搞清楚你面对的是哪种情况识别一个没有扩展名的文件第一步不是急着用工具去扫描而是先看一眼这个文件的来源和上下文。这一步很多人会忽略但恰恰是最高效的信息来源。1.1 下载与传输场景最常见的来源就是网络下载。浏览器下载的时候如果服务器没有返回正确的 Content-Type或者下载链接本身不带文件名后缀保存下来的文件就会没有扩展名。这种文件通常是 PDF、ZIP 压缩包、Office 文档或者安装镜像。我记得有一次从某下载站拉了一个名为“installer”的文件大小有 700 多 MB直觉告诉我这要么是 ISO 镜像要么是 DMG结果 file 一看还真是 ISO 9660 镜像。另外微信、钉钉这类聊天工具在传输文件时如果接收方设备不支持对应文件类型也会把扩展名剥掉或改掉。这种情况文件通常不大以文档和图片为主。1.2 存储与备份场景服务器场景里更容易出现无扩展名文件。日志切割后的旧文件、临时目录里的缓存、数据库导出的数据文件很多都不带扩展名。还有一些是自己写的脚本把文件写到磁盘时忘了加后缀或者改名时手误删掉了后面的扩展名。这类文件的特点是内容通常是纯文本、JSON、CSV 或者某种服务的专有格式比如 MySQL 的 ibd 文件、Redis 的 dump.rdb本质上也没有统一扩展名。1.3 安全与取证场景在信息安全领域无扩展名文件几乎是日常。攻击者经常把恶意脚本、木马、图片马改成无扩展名的形式存放或者反过来把一个可执行文件伪装成图片文件的扩展名。这种情况下的类型识别就不是打开文件看看那么简单了你得确认文件内容到底是什么而不是看它表面叫什么。取证工具对这类文件尤其敏感第一步必然是算 hash、识别魔数、确认 PE/ELF 结构然后才是行为分析。了解来源之后你对文件可能是什么类型已经有了一个初步的判断范围。接下来就是用工具去验证而不是真的盲猜。2. file 命令最直接的类型识别武器在所有识别无扩展名文件类型的手段里Linux/macOS 下的file命令是最经典、最可靠、也是我使用频率最高的工具没有之一。它不需要文件扩展名直接读取文件内容特征来输出类型这正是我们需要的。2.1 file 命令的基本用法在终端里执行file 无扩展名的文件路径比如我有一个文件名叫data_202406执行后输出data_202406: gzip compressed data, was data_202406.tar, last modified: Mon Jun 10 08:23:45 2024, from Unix就这么一行输出信息量极大格式是 gzip 压缩数据原始文件名是data_202406.tar还能看到最后修改时间。这说明这个无扩展名文件其实就是 tar 包压缩后的产物重命名并去除扩展名后保存了下来。再看一个例子secret.bin: PE32 executable (GUI) Intel 80386, for MS Windows这是 Windows 下的可执行程序即使文件叫secret.bin也掩盖不了它是 PE32 可执行文件的事实。如果你只需要 MIME 类型可以加参数file --mime-type 文件名输出类似文件名: application/pdf这个输出很干净适合在脚本里做逻辑判断。还有一种输出是file --mime会带出编码比如text/plain; charsetutf-8在有需要精确判断文本编码的场合很有用。2.2 输出信息解读file 命令并不只是“猜”很多第一次用 file 命令的人会以为它只是根据文件扩展名猜类型这是一个非常大的误解。file命令的核心是一个叫“魔法文件”magic file的规则库里面记录了成千上万种文件格式的特征字节序列也就是文件头、文件尾、特定偏移位置上的特定字节。执行时file会读取目标文件开头的若干字节与规则库逐条比对命中哪条就输出哪条对应的描述。这个规则库在 Linux 上通常位于/usr/share/file/magic下编译后的二进制规则文件是magic.mgc。你也可以写自己的规则并放在~/.magic里通过-m参数指定file -m ~/.magic 文件名file命令本身从 BSD 时代就存在到现在仍在持续维护更新新出的文件格式比如新版 Office 的 Open XML 格式、新图片格式 AVIF 之类都会及时加进规则库。所以长期使用下来它对新格式的识别能力比你自己写脚本要强得多。2.3 注意 file 输出中的“附加描述”有时候 file 命令会在类型后面输出额外信息这些信息并不是废话。举个例子archive.bin: Zip archive data, at least v2.0 to extract, compression methoddeflate这里的compression methoddeflate表示压缩方式是 deflate绝大多数 ZIP 都是这种方式。如果看到compression methodstore说明 ZIP 里的文件没有压缩只是存储。这个差异在你处理加密压缩包或分析恶意样本时很有用。再比如document: PDF document, version 1.7不仅告诉你这是 PDF还告诉你内部版本是 1.7。如果你怀疑文件被篡改过用这个信息去对比官方格式规范能快速定位异常。3. 魔数识别类型判断的底层原理与手工验证file命令很强大但如果有一天你手里只有一台 Windows 电脑没有 Linux 环境又或者想理解到底层原理而不只是背命令你必须知道魔数Magic Number这个概念。3.1 文件魔数是什么几乎所有有结构的文件格式设计者都会在文件最开头放一串具有辨识度的固定字节用于标识这个文件的格式。这就是魔数也叫文件签名。文件系统识别类型靠扩展名应用软件识别类型靠的就是这串魔数。比如 PNG 图片开头 8 个字节固定是89 50 4E 47 0D 0A 1A 0A转成 ASCII 看是\x89PNG\r\n\x1a\n。PDF 文件开头通常是25 50 44 46也就是 ASCII 的%PDF。ZIP 压缩包开头是50 4B 03 04ASCII 是PK\x03\x04。这些字节你可以直接用一个十六进制查看工具打开文件验证不需要依赖任何现成的识别软件。3.2 常见文件格式魔数速查表我在实际工作中对照频率最高的一份表文件类型魔数十六进制ASCII 可读部分说明JPEGFF D8 FF无以 FFD8FF 开头PNG89 50 4E 47 0D 0A 1A 0A\x89PNG固定 8 字节GIF47 49 46 38GIF8后接 7a 或 9aPDF25 50 44 46%PDF固定 4 字节ZIP50 4B 03 04PK空 ZIP 为 50 4B 05 06RAR52 61 72 21Rar!固定 4 字节gzip1F 8B无固定 2 字节开头bzip242 5A 68BZh固定 3 字节ELFLinux 可执行7F 45 4C 46\x7fELF固定 4 字节PEWindows 可执行4D 5AMZ固定 2 字节开头SQLite53 51 4C 69 74 65SQLite固定 6 字节7z37 7A BC AF 27 1C7z固定 6 字节这张表背下来之后很多简单的判断一眼就能完成。3.3 十六进制手工探测实操假设你拿到一个无扩展名文件unknown在 Linux 上可以用xxd或者hexdump查看文件头xxd -l 32 unknown输出00000000: 504b 0304 1400 0000 0800 5b6f 3b58 0000 PK........[o;X.. 00000010: 0000 0000 0000 0000 0000 2100 0000 0000 ..........!.....第一行开头是50 4b 03 04对应 ASCII 的PK\x03\x04几乎可以断言这是 ZIP 压缩包。然后用unzip -t验证一下就能确认。再比如00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000 .ELF............开头四个字节是7F 45 4C 46就是 ELF 魔数这是一个 Linux 下的可执行文件或者共享库。即使是在 Windows 上你也可以用 PowerShell 读取文件头部字节来手动判断命令也不复杂Format-Hex -Path .\unknown -Count 16核心逻辑是一样的解析头部字节比对已知魔数。学会这一手你就掌握了和file命令同源的能力。4. 辅助工具组合拳strings、hexdump 与文本特征分析光靠魔数能解决 70% 的格式识别问题但还有一部分文件并不具备特别明显的魔数或者魔数被某种封装格式包住了。这时候就需要辅助工具来进一步确认。4.1 strings 提取可读字符串判断文本、脚本和配置类文件无扩展名文件的类型识别除了看文件头还可以看文件内的字符串。strings命令会扫描文件中连续的可见 ASCII/UTF-8 字符串并打印出来。如果一个文件前半部分是这种内容strings unknown | head -30{name: test, version: 1.0.0, dependencies: []}那基本可以判断这是一个 JSON 文件最多只是前面带了一些空白或者 BOM 头。如果再看到#!/usr/bin/env python3这种行那这就是一个 Python 脚本只不过扩展名被剥掉了。这个思路在处理脚本类文件、配置文件、日志文件时非常高效因为这些文件本身就是人类可读的纯文本不需要复杂的特征码判断。strings还能配合grep精准定位关键信息strings unknown | grep -i begin base64如果在输出里看到begin 644或者base64相关字样说明这很可能是一封邮件原始文件或者 uuencode 编码过的数据。4.2 hexdump 查看文件头之外的结构信息xxd -l 32只能看文件头 32 字节有些格式的关键结构不在文件头而在文件尾或者固定偏移处。比如 ZIP 文件有 End of Central Directory 记录在文件末尾如果你怀疑某个文件是 ZIP 但头部被破坏了可以使用xxd unknown | tail -20在文件尾部找50 4B 05 06。如果能找到即使头部对不上也说明文件存在 ZIP 结构。著名的 PNG 文件 IEND 块结束标志是00 00 00 00 49 45 4E 44 AE 42 60 82看文件尾也能验证它是否是一个完整的 PNG。这种情况下只看头部会判断失误必须结合头部和尾部综合分析。4.3 文本文件的编码与语言识别有时候识别出来的结果是一个文本文件但你不确定它具体是什么类型比如是 CSV 还是 TSV 还是日志。这种情况下先判断文本编码和分隔符比猜测类型更实际。file -i unknown输出类似unknown: text/plain; charsetutf-8这是 UTF-8 编码的纯文本。如果你的文件是 GBK/GB2312 中文编码file 命令可能不会直接识别出来但可以用iconv转码测试iconv -f GBK -t UTF-8 unknown /dev/null没有报错说明是合法的 GBK 编码。然后你可以用head -1 unknown看第一行内容再判断它是不是结构化文本。第一行如果是一堆逗号分隔的表头那就是 CSV如果是制表符那就是 TSV如果有time... level...这种模式就是日志。这一套组合拳打下来绝大多数文本类和压缩类文件都无所遁形。5. 实战流程一个无扩展名文件的完整辨识过程前面讲的都是知识点这一章我带你完整走一遍真实的识别流程。我就拿最近处理过的一个文件举例文件名叫update.bin是从一台旧服务器上拷下来的没有扩展名大小约 2.3 MB。5.1 第一步从文件头建立初步判断先执行最基本的 file 命令file update.bin结果update.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.8, not stripped第一次看到这个输出时我还愣了几秒一个叫 update.bin 的文件居然是一个 32 位的 Linux 可执行程序。原有的判断可能是个固件或二进制补丁被推翻了但输出信息告诉我它是一个动态链接的可执行文件而且没有剥离符号表not stripped这意味着可以用strings去提取里面的调试信息。5.2 第二步执行权限标志与动态链接库检查先用ls -l看一下权限位ls -l update.bin-rw-r--r-- 1 root root 2388992 Jun 10 08:23 update.bin没有执行权限这进一步增加了它是一个需要手动运行的程序的可能性但也可能是被人去掉了 x 位之后再打包上传防止误执行。用readelf查看它的动态依赖readelf -d update.bin | grep NEEDED0x00000001 (NEEDED) Shared library: [libc.so.6] 0x00000001 (NEEDED) Shared library: [libm.so.6]只有 libc 和 libm 两个依赖说明这是一个相对底层的程序不是用 Python/Go 这类自带运行时打包的东西。Go 语言编译出的 ELF 通常是静态链接的不会出现 NEEDED 条目Python 脚本在 ELF 里又会有明显的python3解释器路径。所以这一对比基本上可以断定这是一个 C 语言编译的原生程序。5.3 第三步提取字符串判断程序用途再提取字符串看看里面都写了什么用于判断程序原本的用途strings update.bin | grep -i usage\|version\|http\|ftp\|tcp | head -30输出usage: update [-s server] [-p port] [-f file] version 2.1.0 http:// Connection refused Received invalid response到这里这个无扩展名文件的全貌基本清楚了一个用 C 写的、32 位的 Linux 更新客户端通过 HTTP 协议从远端服务器拉取文件并更新本地系统。如果没有这一层层剥离只靠扩展名缺失就下结论很容易把可执行文件当成普通数据忽略掉。5.4 第四步对比 hash 与病毒扫描不至于误判确认是程序的最后一步是对它的 hash 进行比对看它是否与已知的官方版本一致。同时我习惯跑一次杀毒引擎扫描避免它本身就是恶意程序sha256sum update.bin如果这个文件的哈希能在厂商官网或软件仓库里查得到那就能完全对应上版本。如果查不到那就要谨慎处理了不能乱执行。6. 批量识别与自动化脚本思路几十个无扩展名文件怎么办现实工作中很少只处理一个无扩展名文件。服务器临时目录里经常会躺着一堆没有后缀的文件一个个去 file 显然不现实这时候就要借助脚本完成批量识别和分类。6.1 批量场景的需求拆解自动化处理无扩展名文件核心需求就三个遍历目录找出所有没有扩展名的文件识别每个文件的类型根据类型做后续处理重命名、移动、删除或者记录到报告里要注意的是这里的“没有扩展名”不能简单理解为文件名不含点。比如v1.2.3这种文件名点只是文件名的一部分后面跟着3并不是扩展名。要准确判断可以用下面这种思路文件名包含点但点的位置不在最后一部分——换句话说最后一个点之后如果还有一个点或没有字符就不算扩展名。更稳妥的做法是只看最后一个点后面的字符串长度和常见扩展名集合做匹配。6.2 一个简单的 Shell 批量识别脚本用 find 和 file 组合可以快速产出报告#!/bin/bash # 批量识别无扩展名文件并输出类型报告 find /path/to/dir -type f -name *.* ! -name *.*.* | while read f; do # 取最后一个点之后的内容如果为空则视为无扩展名 ext${f##*.} if [ -n $ext ]; then continue fi type$(file -b $f | cut -d, -f1) echo $f|$type done这个脚本会把所有不含点的文件识别出来并输出“路径|类型”的表格。实际使用中记得把/path/to/dir换成你自己的目录。输出重定向到文件后直接就能用表格软件打开筛选。6.3 Python 脚本自行解析魔数并分类如果不用 file 命令想用纯 Python 手动识别魔数代码也很短。我写过一个简化版import os import sys from pathlib import Path def detect_magic(file_path): with open(file_path, rb) as f: head f.read(8) if head.startswith(b\\x89PNG\\r\\n\\x1a\\n): return PNG image if head.startswith(b\\xff\\xd8\\xff): return JPEG image if head.startswith(b%PDF): return PDF document if head.startswith(bPK\\x03\\x04): return ZIP archive if head.startswith(b\\x7fELF): return ELF executable if head.startswith(bMZ): return PE executable if head.startswith(bSQLite format 3\\x00): return SQLite database if head.startswith(b{\\n) or head.startswith(b[): return JSON text return unknown if __name__ __main__: for p in sys.argv[1:]: print(f{p}: {detect_magic(p)})Python 方案的好处是灵活你可以针对自己的业务特点加入特定规则比如识别公司内部自研的文件格式。缺点是需要自己维护规则库遇到新文件格式就要手动加不像 file 命令那样开箱即用。6.4 自动重命名的建议识别出文件类型后自动补全扩展名是很自然的后续动作。我的经验是重命名之前先输出一遍完整的管理报告再让脚本根据文件类型映射关系去重命名不要一把梭直接改。映射关系可以这样定type_map { PNG image: .png, JPEG image: .jpg, PDF document: .pdf, ZIP archive: .zip, ELF executable: , SQLite database: .sqlite, }注意 ELF 可执行文件不需要扩展名补了反而别扭。另外重命名之前一定要检查目标文件名是否已存在避免覆盖。7. 踩坑记录与边界情况识别不是每次都能一次到位识别无扩展名文件类型虽然大部分时候 file 命令和魔数比对都能直接给出答案但依然存在不少边界情况。我根据自己的经验把最容易踩的坑列出来。7.1 file 命令的误判场景file命令有时会误判。最容易遇到的情况是一个文件既符合 A 格式的魔数又包含大量 B 格式的特征。比如 PDF 文件里内嵌了 JavaScriptfile命令输出有时会带出可疑脚本的提示。还有一种情况是老版本 file 命令的规则库里没有新格式把新格式误判成旧格式。比如 HEIC 图片在老版本里可能被识别成 JPEG因为没有 HEIC 的规则。解决办法是定期升级 file 版本或者交叉验证。以图片为例识别图片不要只信 file 的输出可以结合identifyImageMagick 工具进一步验证identify -verbose unknown_image如果identify能正常解析出图像的尺寸、颜色空间、位深那基本可以确认这就是一张可解析的图片。反之如果 file 判断是 PNG但 identify 直接报错那这个文件很可能只是碰巧带有 PNG 魔数实际内容是被篡改或损坏的。7.2 魔数伪造与伪装文件在恶意样本分析里魔数是完全可以伪装的。一段数据可以在开头写上%PDF四个字节让所有基于魔数的工具都认为这是 PDF而实际内容可能是脚本或可执行代码。这也就是为什么遇到可疑文件时不能只看表面上是什么格式必须用对应的解析器去实际解析一遍。以图片为例真实解析一张图片要做的是检查文件头魔数、检查 IHDR 宽高字段是否合理、检查数据块 CRC 是否匹配。如果你只是简单盖一个 PNG 文件头在恶意代码上CRC 校验就会失败解析器也会直接报错。在判断无扩展名文件的安全性时多一步解析验证是很有必要的。7.3 空文件与纯文本的特殊情况0 字节的空文件没有任何内容任何工具都无法识别它是什么类型。file 输出会是empty没有任何进一步的信息。这种情况下只能靠上下文推断。还有一种情况是文件内容本身是纯文本但没有明显的魔数。比如一个无扩展名文件内容是“Hello, world”file命令会输出ASCII text这个结果没有告诉你它原先可能是 .txt、.log、.csv 还是 .md。你需要打开文件看内容、看结构再决定给它补什么扩展名。这一步不能偷懒。7.4 大文件与网络文件系统处理大文件时要注意一个细节file 命令默认只读取文件头部一小段字节所以处理几百 GB 的文件也很快不会卡。但如果你用xxd去查看大文件头部一定要加限制参数比如xxd -l 64否则终端会被刷爆。网络文件系统比如 NFS、SMB 挂载上的无扩展名文件识别速度取决于网络延迟。file 命令在每次判断时如果规则库没命中会尝试读取更多的字节在弱网环境下可能会慢这是正常的不是程序卡死了。批量处理时建议先将大文件复制到本地再识别否则反复读远程文件头会非常耗时。8. 跨平台工具箱Windows 和 macOS 下的识别方案前面大部分例子基于 Linux 环境但现实中很多读者用的是 Windows或者工作中需要在多个系统之间切换。这里单独说一下跨平台方案。8.1 Windows 环境Windows 不自带 file 命令但有这么几条路安装 Git for Windows里面自带 Git Bash 环境包含了file命令。这是最省事的方式。使用 WSL装个 Ubuntu 子系统的体验和原生 Linux 几乎一样。纯 PowerShell 方案用Format-Hex查看文件头配合查表判断。适合不想装额外工具的场景。我的实践是尽量用 Git Bash 里的 file 命令因为它能利用完整规则库识别能力最强。8.2 macOS 环境macOS 自带 file 命令直接使用即可file 无扩展名文件macOS 自带的 file 版本通常比 Linux 上的新对新格式支持得更好。另外 macOS 的mdls命令也可以查看文件元数据对某些文档和媒体类型有帮助mdls -name kMDItemContentType -name kMDItemKind 无扩展名文件它能给出系统级的文件类型标识可以作为 file 命令输出的交叉参考。9. 一个更进阶的角度根据内容指纹识别具体文件最后聊一个在识别无扩展名文件基础上更深入的点内容指纹。很多时候你不仅仅想知道文件是什么格式还想知道它具体是什么文件、来自哪里、跟已知文件是否一致。这时候需要引入哈希和模糊哈希。9.1 精确哈希与模糊哈希精确哈希是 SHA-256、MD5 这类算法。只要文件内容有一丁点改动哈希值就会完全不同。模糊哈希Fuzzy Hashing典型工具是 ssdeep则不同它计算的是文件局部内容的相似度输出一个“结构相似度”得分。两个文件即使有一处改动也能得到比较高的相似度分数。这在判断一个无扩展名文件是不是某个已知样本的变种时非常有用。比如你怀疑一个无扩展名文件是某个已知恶意软件的新变种直接去对比 ssdeep 相似度如果得分达到 80 以上基本可以确认同源性。9.2 常见文件的指纹库对于常见的文档、图片、压缩包如果你想判断它的具体来源可以用这些现成的指纹库图片EXIF 里的 GPS 信息、相机型号、编辑软件PDF元数据里的作者、创建工具ZIP注释字段、压缩文件内的目录结构ELF/PE编译时间戳、编译器的版本字符串这些信息在无扩展名文件分析中都是非常有价值的证据。我处理过一个无扩展名的 PDF它既没有被识别出文件类型也没有明显的文件名线索但通过提取元数据我发现了作者的邮箱地址直接定位到了来源。9.3 给无扩展名文件做“体检”的完整流程这些年我总结了七个判断步骤每次遇到不确定的文件都会按这个顺序走一遍看上下文确认来源和获取方式file 命令得到初步类型检查文件头魔数和文件尾结构交叉验证提取字符串判断内容特征用对应解析器尝试解析验证可读性计算哈希对比已知库记录结果备份原件再进入后续使用流程这套流程下来几乎没有无法确认的无扩展名文件。如果你能掌握这套流程实际上你已经具备了一个基础数字取证人员的能力。识别无扩展名文件类型算是电脑使用生涯里一件很小但很磨人的事。它不像写代码做功能那样有成就感但能解决这个问题带给你的确定感是实打实的。以后再遇到没有扩展名的文件先别急着删掉或者随便拿一个软件强行打开按上面的方法一步步来绝大多数情况下你自己就能得出结论。
返回列表