rea-reverse

Identify a File Type by Its Magic Bytes, Then Pick the Right Tool

Written for REA 6.4.0Updated 8 min read
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’s inspect-artifact report 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.dll as PE32 with ILONLY and STRONGNAMESIGNED flags and metadata v4.0.30319, and rejected the Mach-O file with “Not a PE file (no MZ/PE header).”.
  • WASM to WAT parsed mystery-e as 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