Identify a File Type by Its Magic Bytes, Then Pick the Right Tool
On this page
Short answer: look at the first few bytes, not the extension. On Linux or macOS, file mystery names the format, and xxd -l 16 mystery shows the raw header. On Windows, Format-Hex -Path mystery -Count 16 shows it in PowerShell. Match the bytes against the table below. If they start with PK, you have a ZIP container, and the file names inside it (not the first bytes) tell you whether it is an APK, a NuGet package, a JAR or an Office document. Then open it with the tool built for that format. The routing table at the end points to this site’s browser tools and the matching REA commands.
Who this is for: developers who received an attachment, a build artifact or a firmware dump with no extension, or with an extension they don’t trust, and want to know what it is before opening it.
What you will learn:
- What magic bytes are and why extensions are only a hint.
- The signatures you will meet most often in development and reverse engineering.
- How to tell ZIP-based formats apart.
- How
file,xxd, PowerShell and REA’sinspect-artifactreport the same files. - Which tool to open each format with.
Samples used: eight files I made myself or took from open-source releases, all renamed to mystery-a … mystery-h so that no extension gives anything away: a C program compiled with clang on macOS, busybox from the OpenWrt 25.12.5 root filesystem, Newtonsoft.Json.dll and the .nupkg it ships in (from nuget.org, MIT license), a one-function WebAssembly module built with wat2wasm, a gzipped text file, the OpenWrt SquashFS image, and the package’s PNG icon.
Treat an unknown file as untrusted. Identify it statically first; don’t double-click it, run it or open it in an app that executes macros. If it came from someone else’s system, make sure you are allowed to analyze it. See REA’s DISCLAIMER.md.
Why the first bytes beat the extension
Most binary formats begin with a fixed signature, the “magic number”, so that loaders and parsers can reject the wrong input quickly. The Linux kernel checks for \x7fELF, Windows checks for MZ, a browser’s WebAssembly engine checks for \0asm. The extension is only a naming convention: anyone can rename a file, and many formats (APK, JAR, DOCX, NUPKG) share one container.
Signatures have limits too. Text formats such as JSON, HAR files and source maps have no magic; you recognize them by parsing. Some magic values collide: CA FE BA BE starts both a universal Mach-O binary and a Java .class file. And a signature only says what the file claims to be. A truncated or corrupted file can start correctly and still fail to parse.
Step 1: Look at the header
$ file mystery-*
mystery-a: Mach-O 64-bit executable arm64
mystery-b: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-mips-sf.so.1, no section header
mystery-c: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows
mystery-d: Zip archive data, at least v2.0 to extract, compression method=deflate
mystery-e: WebAssembly (wasm) binary module version 0x1 (MVP)
mystery-f: gzip compressed data, was "note.txt", ... from Unix, truncated
mystery-g: Squashfs filesystem, little endian, version 4.0, xz compressed, 4626494 bytes, 1214 inodes, ...
mystery-h: PNG image data, 512 x 512, 8-bit/color RGBA, non-interlaced
That is file 5.41 as shipped with macOS. It reads the header through a large signature database and often adds details: the CPU, the .NET marker, image dimensions. Don’t take every word literally, though. It called the gzip file “truncated”, yet gzip -tv mystery-f reported OK. The bytes themselves are the ground truth:
$ xxd -l 16 mystery-c
00000000: 4d5a 9000 0300 0000 0400 0000 ffff 0000 MZ..............
$ xxd -l 16 mystery-e
00000000: 0061 736d 0100 0000 0107 0160 027f 7f01 .asm.......`....
$ xxd -l 16 mystery-g
00000000: 6873 7173 be04 0000 286c 426a 0000 0400 hsqs....(lBj....
On Windows without xxd, PowerShell has a built-in cmdlet. Microsoft’s Format-Hex documentation uses exactly this case, a PDF renamed to an unknown extension, as its example:
Format-Hex -Path .\mystery-c -Count 16
-Count and -Offset need PowerShell 6.2 or later. I did not run this one (no Windows machine for this article), so check the column layout against the documentation.
Step 2: Match the signature
These are the values from my samples plus a few common neighbors. Every row with a sample name was checked with xxd on that file.
| First bytes (hex) | ASCII | Format | Sample |
|---|---|---|---|
7F 45 4C 46 |
.ELF |
ELF: Linux, Android .so, most firmware binaries |
mystery-b |
4D 5A |
MZ |
PE/COFF: Windows EXE, DLL, .NET assembly (the offset of the PE\0\0 header is stored at 0x3C) |
mystery-c |
CF FA ED FE / CE FA ED FE |
Mach-O 64-bit / 32-bit, little-endian | mystery-a |
|
CA FE BA BE |
Universal Mach-O or Java class | ||
50 4B 03 04 |
PK.. |
ZIP and everything built on it | mystery-d |
00 61 73 6D |
.asm |
WebAssembly module (next 4 bytes: version) | mystery-e |
1F 8B |
gzip | mystery-f |
|
FD 37 7A 58 5A 00 |
.7zXZ. |
xz | (made with xz) |
68 73 71 73 |
hsqs |
SquashFS, little-endian (sqsh is big-endian) |
mystery-g |
27 05 19 56 |
U-Boot legacy uImage (firmware kernels) | OpenWrt .bin |
|
89 50 4E 47 0D 0A 1A 0A |
.PNG.... |
PNG | mystery-h |
62 70 6C 69 73 74 30 30 |
bplist00 |
Apple binary property list | |
25 50 44 46 |
%PDF |
PDF (Microsoft’s Format-Hex example) |
A signature that sits at offset 0 is the easy case. Firmware puts several formats back to back, so a SquashFS can begin megabytes into a file. For that, use a scanner that searches the whole file, which the Binwalk 3 guide covers.
Step 3: Split the ZIP family
PK\x03\x04 only means “ZIP”. To find out which ZIP, list the entries:
$ unzip -l mystery-d | head
1104 03-08-2023 07:01 LICENSE.md
2413 03-08-2023 07:06 Newtonsoft.Json.nuspec
8956 03-08-2023 07:01 packageIcon.png
653 03-08-2023 07:06 [Content_Types].xml
580880 03-08-2023 07:09 lib/net20/Newtonsoft.Json.dll
A .nuspec plus lib/<framework>/ folders means a NuGet package. The marker files for the common cases:
| Inside the ZIP | It is |
|---|---|
AndroidManifest.xml, classes.dex, resources.arsc |
Android APK |
base/manifest/AndroidManifest.xml, BundleConfig.pb |
Android App Bundle (AAB) |
Payload/<Name>.app/ |
iOS IPA |
META-INF/MANIFEST.MF with .class files |
Java JAR |
*.nuspec, lib/<tfm>/ |
NuGet package |
[Content_Types].xml with word/, xl/ or ppt/ |
Office DOCX, XLSX, PPTX |
AppxManifest.xml |
MSIX / AppX |
Tools differ in how far they go here. REA 6.4.0’s inspect-artifact, from REA, the open-source project by morluto, checks the ZIP signature from the bytes, then takes the subtype from the extension: .apk, .ipa, .msix/.msixbundle, .appx/.appxbundle, otherwise plain zip (this logic is in dist/domain/zipPackageFormat.js). I ran it on mystery-d and got root_format: "zip", with the eight DLLs inside classified as pe from their own bytes. Then I copied the same NuGet package to renamed.apk, and it reported root_format: "apk". Same bytes, different answer, so list the entries yourself before you trust a ZIP subtype.
This site’s APK & AAB analyzer looks at the contents instead. Given the NuGet package, it opened the ZIP and reported “No AndroidManifest.xml was found.”. It decides between APK and AAB by whether AndroidManifest.xml or base/manifest/AndroidManifest.xml is present, not by the file name.
Step 4: Ask REA for the whole picture
For native and container formats, rea inspect-artifact <path> classifies by bytes and suggests what to do next. These are the results from REA 6.4.0 on macOS, one per sample:
| Sample | root_format |
Suggested next probes |
|---|---|---|
mystery-a (Mach-O) |
mach-o |
open_binary |
mystery-b (ELF) |
elf |
open_binary |
mystery-c (.NET DLL) |
pe |
inspect_managed_artifact, then open_binary |
mystery-d (NuGet) |
zip (25 nodes in the graph) |
none |
mystery-e, -f, -g, -h |
error: “Cannot open artifact: unsupported binary format.” |
For the PE file, REA added a low-confidence hypothesis: “The PE artifact may contain managed CLI metadata or native code; inventory bytes alone do not distinguish them.” That is why it suggests the managed check first. inspect-artifact is an archive and app-package inventory, not a general file identifier: WebAssembly, gzip, SquashFS and images are outside it, which is why file and xxd come first. Its .pkg (xar!) and .dmg (koly trailer) checks also require the matching extension. Installing REA is covered in Getting Started with REA.
Step 5: Open it with the right tool
Once you know the format, this is where to go. The browser tools parse files locally in a Web Worker and do not upload them. On 2026-10-14 I ran my samples through the analyzer code of the binary, .NET, WASM, APK and ASAR tools (results below); the other rows list the tool and command for that format.
| Format | Browser tool on this site | REA 6.4.0 | Guide |
|---|---|---|---|
| ELF, PE, Mach-O | Native binary overview | inspect-artifact, then analyze with Ghidra |
ELF, PE and Mach-O viewer |
| .NET assembly (PE with CLI header) | .NET metadata and IL viewer | inspect-managed-artifact |
Check a .NET DLL’s target framework |
| APK / AAB (ZIP) | APK & AAB analyzer | inspect-android-package |
Unpack an APK or AAB |
| WebAssembly | WASM to WAT | none | Inspect WebAssembly |
Electron app.asar |
ASAR viewer | inspect-artifact (needs the .asar name) |
Unpack an ASAR file |
| HAR (JSON) or Fiddler SAZ (ZIP) | HAR analyzer | inspect-web-network-capture |
HAR file analyzer guide |
| JavaScript source map (JSON) | Source map tool | trace-web-source-location |
Recover source from a bundle |
| Firmware (uImage, SquashFS, …) | none | inspect-firmware-regions, extract-firmware (Linux only) |
Extract SquashFS from firmware |
| EVM contract bytecode (hex text) | EVM selector extractor | inspect-evm-interface |
EVM function selectors |
Some results from those runs:
- Binary overview accepts any file and decides by magic. It opened
busybox(ELF32, MIPS, big-endian, stripped) and refused the SquashFS image and the WASM module with “unknown magic”. - .NET viewer read
Newtonsoft.Json.dllas PE32 withILONLYandSTRONGNAMESIGNEDflags and metadatav4.0.30319, and rejected the Mach-O file with “Not a PE file (no MZ/PE header).”. - WASM to WAT parsed
mystery-eas version 1 with one exported function,add, of type(i32, i32) -> (i32). - ASAR viewer rejected the NuGet ZIP with “Not an ASAR archive”. ASAR has no fixed magic; the tool checks that the first 16 bytes form its size and header pickles.
The file pickers on the .NET, WASM, ASAR and APK pages filter by extension (.dll,.exe, .wasm, .asar, .apk,.aab). If your file has no extension, drag it onto the page instead; the tools check the content, not the name.
FAQ
Can a file have a valid signature and still be something else? Yes. Polyglot files are valid in two formats at once, and ZIP data appended to another file is common. The signature at offset 0 tells you how the file will be loaded, not everything it contains.
What about text files?
They have no magic. Check whether the file is valid JSON (HAR files and source maps are JSON), look for a shebang (#!), or let file guess from the content.
Is file available on Windows?
Not by default. Use Format-Hex and the table above, or run file inside WSL.
Why does REA call my .zip an apk?
For ZIP containers, 6.4.0 takes the subtype from the extension. Rename the file correctly, or list its entries first.
Authorised use: identify and analyze files you own, open-source software, CTF challenges or files you are authorized to examine.
Next steps
- Native binaries in depth: ELF, PE and Mach-O Viewer Online
- Firmware blobs with several formats inside: Extract SquashFS from a Firmware .bin
- Install REA and connect it to your agent: Getting Started with REA
- All browser tools: Tools