Showing preview only (889K chars total). Download the full file or copy to clipboard to get everything.
Repository: ReFirmLabs/binwalk
Branch: master
Commit: a417b4dcf742
Files: 270
Total size: 828.3 KB
Directory structure:
gitextract_b67t48qd/
├── .dockerignore
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── quality.yaml
│ └── rust-release-binary.yml
├── .gitignore
├── CARGO_README.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── README.md
├── build_docker.sh
├── dependencies/
│ ├── README.md
│ ├── pip.sh
│ ├── requirements.txt
│ ├── src.sh
│ └── ubuntu.sh
├── fuzzing/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── main.rs
├── scripts/
│ └── binwalk-ui
├── src/
│ ├── binwalk.rs
│ ├── cliparser.rs
│ ├── common.rs
│ ├── display.rs
│ ├── entropy.rs
│ ├── extractors/
│ │ ├── androidsparse.rs
│ │ ├── arcadyan.rs
│ │ ├── autel.rs
│ │ ├── bmp.rs
│ │ ├── bzip2.rs
│ │ ├── cab.rs
│ │ ├── common.rs
│ │ ├── csman.rs
│ │ ├── dahua_zip.rs
│ │ ├── dmg.rs
│ │ ├── dtb.rs
│ │ ├── dumpifs.rs
│ │ ├── dxbc.rs
│ │ ├── encfw.rs
│ │ ├── gif.rs
│ │ ├── gpg.rs
│ │ ├── gzip.rs
│ │ ├── inflate.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── jpeg.rs
│ │ ├── linux.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── pcap.rs
│ │ ├── pem.rs
│ │ ├── png.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── sevenzip.rs
│ │ ├── squashfs.rs
│ │ ├── srec.rs
│ │ ├── svg.rs
│ │ ├── swapped.rs
│ │ ├── tarball.rs
│ │ ├── trx.rs
│ │ ├── tsk.rs
│ │ ├── ubi.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── yaffs2.rs
│ │ ├── zlib.rs
│ │ └── zstd.rs
│ ├── extractors.rs
│ ├── json.rs
│ ├── lib.rs
│ ├── magic.rs
│ ├── main.rs
│ ├── signatures/
│ │ ├── aes.rs
│ │ ├── android_bootimg.rs
│ │ ├── androidsparse.rs
│ │ ├── apfs.rs
│ │ ├── arcadyan.rs
│ │ ├── arj.rs
│ │ ├── autel.rs
│ │ ├── binhdr.rs
│ │ ├── bmp.rs
│ │ ├── btrfs.rs
│ │ ├── bzip2.rs
│ │ ├── cab.rs
│ │ ├── cfe.rs
│ │ ├── chk.rs
│ │ ├── common.rs
│ │ ├── compressd.rs
│ │ ├── copyright.rs
│ │ ├── cpio.rs
│ │ ├── cramfs.rs
│ │ ├── csman.rs
│ │ ├── dahua_zip.rs
│ │ ├── deb.rs
│ │ ├── dkbs.rs
│ │ ├── dlink_tlv.rs
│ │ ├── dlke.rs
│ │ ├── dlob.rs
│ │ ├── dmg.rs
│ │ ├── dms.rs
│ │ ├── dpapi.rs
│ │ ├── dtb.rs
│ │ ├── dxbc.rs
│ │ ├── ecos.rs
│ │ ├── efigpt.rs
│ │ ├── elf.rs
│ │ ├── encfw.rs
│ │ ├── encrpted_img.rs
│ │ ├── ext.rs
│ │ ├── fat.rs
│ │ ├── gif.rs
│ │ ├── gpg.rs
│ │ ├── gzip.rs
│ │ ├── hashes.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── jpeg.rs
│ │ ├── linux.rs
│ │ ├── logfs.rs
│ │ ├── luks.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── ntfs.rs
│ │ ├── openssl.rs
│ │ ├── packimg.rs
│ │ ├── pcap.rs
│ │ ├── pchrom.rs
│ │ ├── pdf.rs
│ │ ├── pe.rs
│ │ ├── pem.rs
│ │ ├── pjl.rs
│ │ ├── pkcs_der.rs
│ │ ├── png.rs
│ │ ├── qcow.rs
│ │ ├── qnx.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── rsa.rs
│ │ ├── rtk.rs
│ │ ├── seama.rs
│ │ ├── sevenzip.rs
│ │ ├── shrs.rs
│ │ ├── squashfs.rs
│ │ ├── srec.rs
│ │ ├── svg.rs
│ │ ├── tarball.rs
│ │ ├── tplink.rs
│ │ ├── trx.rs
│ │ ├── ubi.rs
│ │ ├── uboot.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── xz.rs
│ │ ├── yaffs.rs
│ │ ├── zip.rs
│ │ ├── zlib.rs
│ │ └── zstd.rs
│ ├── signatures.rs
│ ├── structures/
│ │ ├── android_bootimg.rs
│ │ ├── androidsparse.rs
│ │ ├── apfs.rs
│ │ ├── arj.rs
│ │ ├── autel.rs
│ │ ├── binhdr.rs
│ │ ├── bmp.rs
│ │ ├── btrfs.rs
│ │ ├── cab.rs
│ │ ├── chk.rs
│ │ ├── common.rs
│ │ ├── cpio.rs
│ │ ├── cramfs.rs
│ │ ├── csman.rs
│ │ ├── deb.rs
│ │ ├── dkbs.rs
│ │ ├── dlink_tlv.rs
│ │ ├── dlob.rs
│ │ ├── dmg.rs
│ │ ├── dms.rs
│ │ ├── dpapi.rs
│ │ ├── dtb.rs
│ │ ├── dxbc.rs
│ │ ├── efigpt.rs
│ │ ├── elf.rs
│ │ ├── ext.rs
│ │ ├── fat.rs
│ │ ├── gif.rs
│ │ ├── gzip.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── linux.rs
│ │ ├── logfs.rs
│ │ ├── luks.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── ntfs.rs
│ │ ├── openssl.rs
│ │ ├── packimg.rs
│ │ ├── pcap.rs
│ │ ├── pchrom.rs
│ │ ├── pe.rs
│ │ ├── png.rs
│ │ ├── qcow.rs
│ │ ├── qnx.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── rtk.rs
│ │ ├── seama.rs
│ │ ├── sevenzip.rs
│ │ ├── shrs.rs
│ │ ├── squashfs.rs
│ │ ├── svg.rs
│ │ ├── tplink.rs
│ │ ├── trx.rs
│ │ ├── ubi.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── xz.rs
│ │ ├── yaffs.rs
│ │ ├── zip.rs
│ │ └── zstd.rs
│ └── structures.rs
└── tests/
├── arcadyan.rs
├── arj.rs
├── bmp.rs
├── bzip2.rs
├── common/
│ └── mod.rs
├── cramfs.rs
├── gzip.rs
├── jpeg.rs
├── matter_ota.rs
├── mbr.rs
├── pdf.rs
├── png.rs
├── qcow.rs
├── riff.rs
├── romfs.rs
├── sevenzip.rs
├── squashfs.rs
├── squashfs_v2.rs
├── yaffs2.rs
├── zip.rs
└── zip_truncated.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
target
Dockerfile
build_docker.sh
================================================
FILE: .gitattributes
================================================
* text eol=lf
*.md text eol=lf
*.png binary
*.bin binary
================================================
FILE: .github/workflows/quality.yaml
================================================
name: Quality Checks
on:
pull_request:
branches:
- "*"
jobs:
lychee-link-check:
name: Link check
runs-on: ubuntu-latest
steps:
- name: Code checkout
uses: actions/checkout@v4
- name: Link Checker
uses: lycheeverse/lychee-action@v1.8.0
with:
fail: true
fmt:
name: Formatting (rustfmt)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- target: x86_64-unknown-linux-gnu
steps:
- name: Code checkout
uses: actions/checkout@v4
- name: Install Rust toolchain (stable)
uses: dtolnay/rust-toolchain@stable
with:
target: ${{ matrix.platform.target }}
components: rustfmt
- name: Formatting (rustfmt)
run: cargo fmt -- --check
lint:
name: Lint (clippy)
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
platform:
- target: x86_64-unknown-linux-gnu
steps:
- name: Code checkout
uses: actions/checkout@v4
- name: Install Rust toolchain (stable)
uses: dtolnay/rust-toolchain@stable
with:
target: ${{ matrix.platform.target }}
components: clippy
- name: Clippy (all crates)
run: cargo clippy --locked --target=${{ matrix.platform.target }} --workspace --all-targets -- -D warnings
- name: Check build did not modify any files
run: test -z "$(git status --porcelain)"
================================================
FILE: .github/workflows/rust-release-binary.yml
================================================
name: Release Build
on:
push:
tags:
- 'v*'
jobs:
release:
name: Release - ${{ matrix.platform.os-name }}
strategy:
matrix:
platform:
- os-name: Linux-x86_64
runs-on: ubuntu-24.04
target: x86_64-unknown-linux-gnu
- os-name: Linux-aarch64
runs-on: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
- os-name: macOS-x86_64
runs-on: macOS-latest
target: x86_64-apple-darwin
- os-name: macOS-aarch64
runs-on: macOS-latest
target: aarch64-apple-darwin
- os-name: Windows-x86_64
runs-on: windows-latest
target: x86_64-pc-windows-msvc
- os-name: Windows-aarch64
runs-on: windows-11-arm
target: aarch64-pc-windows-msvc
runs-on: ${{ matrix.platform.runs-on }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Update CHANGELOG
if: startsWith(github.ref, 'refs/tags/')
id: changelog
uses: requarks/changelog-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref_name }}
writeToFile: true
changelogFilePath: "CHANGELOG.md"
- name: Build binary
uses: houseabsolute/actions-rust-cross@v1
with:
command: build
target: ${{ matrix.platform.target }}
args: "--locked --release"
strip: true
- name: Publish artifacts and release
if: startsWith(github.ref, 'refs/tags/')
uses: houseabsolute/actions-rust-release@v0
with:
executable-name: binwalk
target: ${{ matrix.platform.target }}
release-tag-prefix: v
changes-file: "CHANGELOG.md"
action-gh-release-parameters: |
{
"draft": false,
"prerelease": false,
"overwrite": true,
"generate_release_notes": true,
"make_latest": true,
"body_path": "CHANGELOG.md",
"token": "${{ secrets.GITHUB_TOKEN }}"
}
- name: Commit CHANGELOG.md
if: startsWith(github.ref, 'refs/tags/') && matrix.platform.os-name == 'Linux-x86_64'
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: master
commit_message: 'docs: update CHANGELOG.md for ${{ github.ref_name }} [skip ci]'
file_pattern: CHANGELOG.md
================================================
FILE: .gitignore
================================================
target
================================================
FILE: CARGO_README.md
================================================
# binwalk
A Rust implementation of the Binwalk firmware analysis tool.
## System Requirements
Building requires the following system packages:
```
build-essential libfontconfig1-dev liblzma-dev
```
## Example
```
use binwalk::Binwalk;
// Create a new Binwalk instance
let binwalker = Binwalk::new();
// Read in the data to analyze
let file_data = std::fs::read("/tmp/firmware.bin").expect("Failed to read from file");
// Scan the file data and print the results
for result in binwalker.scan(&file_data) {
println!("{:#?}", result);
}
```
================================================
FILE: Cargo.toml
================================================
[package]
name = "binwalk"
version = "3.1.1"
edition = "2024"
authors = ["Craig Heffner <heffnercj@gmail.com>"]
license = "MIT"
readme = "CARGO_README.md"
repository = "https://github.com/ReFirmLabs/binwalk"
description = "Analyzes data for embedded file types"
keywords = ["binwalk", "firmware", "analysis"]
[dependencies]
log = "0.4.22"
base64 = "0.22.1"
chrono = "0.4.38"
walkdir = "2.5.0"
entropy = "0.4.2"
colored = "3.0.0"
termsize = "0.1"
crc32-v2 = "0.0.5"
crc32c = "0.6.8"
liblzma = "0.4.2"
bzip2 = "0.6.0"
threadpool = "1.8.1"
serde_json = "1.0"
env_logger = "0.11.5"
flate2 = "1.1.2"
adler32 = "1.2.0"
md5 = "0.8.0"
miniz_oxide = "0.8.0"
aho-corasick = "1.1.3"
serde = { version = "1.0", features = ["derive"] }
clap = { version = "4.5.16", features = ["derive"] }
xxhash-rust = { version = "0.8.12", features = ["xxh32"] }
hex = "0.4.3"
delink = { git = "https://github.com/devttys0/delink" }
plotly = { version = "0.13.1", features = ["kaleido", "kaleido_download"] }
[dependencies.uuid]
version = "1.17.0"
features = [
"v4", # Lets you generate random UUIDs
"fast-rng", # Use a faster (but still sufficiently random) RNG
"macro-diagnostics", # Enable better diagnostics for compile-time UUIDs
]
[profile.release]
lto = true
================================================
FILE: Dockerfile
================================================
## Scratch build stage
FROM ubuntu:25.04 AS build
ARG BUILD_DIR="/tmp"
ARG BINWALK_BUILD_DIR="${BUILD_DIR}/binwalk"
ARG SASQUATCH_FILENAME="sasquatch_1.0.deb"
ARG SASQUATCH_BASE_FILE_URL="https://github.com/onekey-sec/sasquatch/releases/download/sasquatch-v4.5.1-5/"
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC
COPY . ${BINWALK_BUILD_DIR}
WORKDIR ${BINWALK_BUILD_DIR}
# Pull build needs, build dumpifs, lzfse, dmg2img, vfdecrypt, and binwalk
# Cleaning up our mess here doesn't matter, as anything generated in
# this stage won't make it into the final image unless it's explicitly copied
RUN apt-get update -y \
&& apt-get upgrade -y \
&& apt-get -y --no-install-recommends install \
ca-certificates \
tzdata \
curl \
git \
wget \
build-essential \
clang \
zlib1g \
zlib1g-dev \
liblz4-1 \
libsrecord-dev \
liblzma-dev \
liblzo2-dev \
libucl-dev \
liblz4-dev \
libbz2-dev \
libssl-dev \
pkg-config \
&& curl -L -o "${SASQUATCH_FILENAME}" "${SASQUATCH_BASE_FILE_URL}\sasquatch_1.0_$(dpkg --print-architecture).deb" \
&& git clone https://github.com/askac/dumpifs.git ${BUILD_DIR}/dumpifs \
&& git clone https://github.com/lzfse/lzfse.git ${BUILD_DIR}/lzfse \
&& git clone https://github.com/Lekensteyn/dmg2img.git ${BUILD_DIR}/dmg2img \
&& rm ${BUILD_DIR}/dumpifs/dumpifs \
&& make -C ${BUILD_DIR}/dumpifs dumpifs \
&& make -C ${BUILD_DIR}/lzfse install \
&& make -C ${BUILD_DIR}/dmg2img dmg2img vfdecrypt HAVE_LZFSE=1 \
&& curl https://sh.rustup.rs -sSf | sh -s -- -y \
&& . /root/.cargo/env \
&& cargo build --release
## Prod image build stage
FROM ubuntu:25.04
ARG BUILD_DIR="/tmp"
ARG BINWALK_BUILD_DIR="${BUILD_DIR}/binwalk"
ARG DEFAULT_WORKING_DIR="/analysis"
ARG SASQUATCH_FILENAME="sasquatch_1.0.deb"
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
ENV UV_SYSTEM_PYTHON=1 UV_BREAK_SYSTEM_PACKAGES=1
WORKDIR ${BUILD_DIR}
# Copy the build artifacts from the scratch build stage
COPY --from=build ${BINWALK_BUILD_DIR}/${SASQUATCH_FILENAME} ${BUILD_DIR}/${SASQUATCH_FILENAME}
COPY --from=build /usr/local/bin/lzfse ${BUILD_DIR}/dumpifs/dumpifs ${BUILD_DIR}/dmg2img/dmg2img ${BUILD_DIR}/dmg2img/vfdecrypt ${BINWALK_BUILD_DIR}/target/release/binwalk /usr/local/bin/
# Install dependencies, create default working directory, and remove clang & friends.
# clang is needed to build python-lzo and vmlinux-to-elf, but it's not needed
# afterward, so it's safe to remove and reduces the image size by ~400MB.
# Those two packages could be built in the scratch stage and copied over from it,
# but that would require that I untangle the Eldritch Horror that is the
# pip build process, and that's not a particular monster that I'm up to slaying today.
RUN apt-get update -y \
&& apt-get upgrade -y \
&& apt-get -y install --no-install-recommends \
ca-certificates \
tzdata \
python3 \
7zip \
zstd \
srecord \
tar \
unzip \
sleuthkit \
cabextract \
curl \
wget \
git \
lz4 \
lzop \
unrar \
unyaffs \
zlib1g \
zlib1g-dev \
liblz4-1 \
libsrecord-dev \
liblzma-dev \
liblzo2-dev \
libucl-dev \
liblz4-dev \
libbz2-dev \
libssl-dev \
libfontconfig1-dev \
libpython3-dev \
7zip-standalone \
cpio \
device-tree-compiler \
clang \
&& dpkg -i ${BUILD_DIR}/${SASQUATCH_FILENAME} \
&& rm ${BUILD_DIR}/${SASQUATCH_FILENAME} \
&& CC=clang uv pip install uefi_firmware jefferson ubi-reader git+https://github.com/marin-m/vmlinux-to-elf \
&& uv cache clean \
&& apt-get purge clang -y \
&& apt autoremove -y \
&& rm -rf /var/cache/apt/archives /var/lib/apt/lists/* /bin/uv /bin/uvx \
&& mkdir -p ${DEFAULT_WORKING_DIR} \
&& chmod 777 ${DEFAULT_WORKING_DIR}
WORKDIR ${DEFAULT_WORKING_DIR}
# Run as the default ubuntu user
USER ubuntu
# Enable this environment variable to remove extractor top-level symlink,
# as the symlink target path in the docker environment will not match that of the host.
ENV BINWALK_RM_EXTRACTION_SYMLINK=1
ENTRYPOINT [ "binwalk" ]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 devttys0
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Binwalk v3
This is an updated version of the Binwalk firmware analysis tool, re-written in Rust for speed and accuracy.

## What does it do?
Binwalk can identify, and optionally extract, files and data that have been embedded inside of other files.
While its primary focus is firmware analysis, it supports a [wide variety](https://github.com/ReFirmLabs/binwalk/wiki/Supported-Signatures) of file and data types.
Through [entropy analysis](https://github.com/ReFirmLabs/binwalk/wiki/Generating-Entropy-Graphs), it can even help to identify unknown compression or encryption!
Binwalk can be customized and [integrated](https://github.com/ReFirmLabs/binwalk/wiki/Using-the-Rust-Library) into your own Rust projects.
## How do I get it?
The easiest way to install Binwalk and all dependencies is to [build a Docker image](https://github.com/ReFirmLabs/binwalk/wiki/Building-A-Binwalk-Docker-Image).
Binwalk can also be [installed](https://github.com/ReFirmLabs/binwalk/wiki/Cargo-Installation) via the Rust package manager.
Or, you can [compile from source](https://github.com/ReFirmLabs/binwalk/wiki/Compile-From-Source)!
## How do I use it?
Usage is _**simple**_, analysis is _**fast**_, and results are _**detailed**_:
```
binwalk DIR-890L_AxFW110b07.bin
```

Use `--help`, or check out the [Wiki](https://github.com/ReFirmLabs/binwalk/wiki#usage) for more advanced options!
================================================
FILE: build_docker.sh
================================================
#!/usr/bin/env bash
docker build --build-arg SCRIPT_DIRECTORY=$PWD -t binwalkv3 .
================================================
FILE: dependencies/README.md
================================================
# Binwalk Dependencies
These scripts install the required Binwalk build and runtime system dependencies, except for the Rust compiler itself.
Execute the appropriate script for your operating system (e.g., `ubuntu.sh` for Ubuntu).
## ubuntu.sh
This script installs *all* required dependencies for Ubuntu-based systems, including the dependencies listed in `pip.sh` and `src.sh`.
This should work for most Debian / Debian-based systems as well, but is only tested on Ubuntu.
## pip.sh
This script installs all Python-based dependencies via `pip3`.
It should be sourced by higher-level scripts (e.g., `ubuntu.sh`).
## src.sh
This script builds and installs all source-based dependencies.
It should be sourced by higher-level scripts (e.g., `ubuntu.sh`).
================================================
FILE: dependencies/pip.sh
================================================
#!/bin/bash
# Install pip dependencies.
# Requires that pip3 is already installed.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if command -v uv >/dev/null 2>&1; then
uv pip install -r "$SCRIPT_DIR/requirements.txt"
else
pip install -r "$SCRIPT_DIR/requirements.txt" --break-system-packages
fi
================================================
FILE: dependencies/requirements.txt
================================================
uefi_firmware
jefferson
ubi-reader
lz4
zstandard
git+https://github.com/marin-m/vmlinux-to-elf
================================================
FILE: dependencies/src.sh
================================================
#!/bin/bash
# Install dependencies from source.
# Requires that git and build tools (make, gcc, etc) are already installed.
# Install dumpifs
cd /tmp
git clone https://github.com/askac/dumpifs.git
cd /tmp/dumpifs
make dumpifs
cp ./dumpifs /usr/local/bin/dumpifs
cd /tmp
rm -rf /tmp/dumpifs
# Install LZFSE utility and library
cd /tmp
git clone https://github.com/lzfse/lzfse.git
cd /tmp/lzfse
make install
cd /tmp
rm -rf /tmp/lzfse
# Install dmg2img with LZFSE support
cd /tmp
git clone https://github.com/Lekensteyn/dmg2img.git
cd /tmp/dmg2img
make dmg2img HAVE_LZFSE=1
make install
cd /tmp
rm -rf /tmp/dmg2img
================================================
FILE: dependencies/ubuntu.sh
================================================
#!/bin/bash
# Get the path to this script's directory, regardless of where it is run from
SCRIPT_DIRECTORY=$(dirname -- "$( readlink -f -- "$0"; )")
# Install dependencies from apt repository
DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get -y install \
7zip \
zstd \
srecord \
tar \
unzip \
sleuthkit \
cabextract \
curl \
wget \
git \
lz4 \
lzop \
unrar \
unyaffs \
python3-pip \
build-essential \
clang \
liblzo2-dev \
libucl-dev \
liblz4-dev \
libbz2-dev \
zlib1g-dev \
libfontconfig1-dev \
liblzma-dev \
libssl-dev \
7zip-standalone \
cpio \
device-tree-compiler
# Install sasquatch Debian package
curl -L -o sasquatch_1.0.deb "https://github.com/onekey-sec/sasquatch/releases/download/sasquatch-v4.5.1-5/sasquatch_1.0_$(dpkg --print-architecture).deb"
dpkg -i sasquatch_1.0.deb
rm sasquatch_1.0.deb
# Install Python dependencies
source "${SCRIPT_DIRECTORY}/pip.sh"
# Install dependencies from source
source "${SCRIPT_DIRECTORY}/src.sh"
================================================
FILE: fuzzing/Cargo.toml
================================================
[package]
name = "fuzz"
version = "0.1.0"
edition = "2024"
[dependencies]
afl = "*"
binwalk = { path = "../" }
================================================
FILE: fuzzing/README.md
================================================
# Fuzzing Binwalk
Fuzz testing for Binwalk is done through [AFL++](https://aflplus.plus).
At the moment code coverage is not 100% complete, but exercises the file parsing code, which is the most problematic and error-prone.
## Fuzzer Dependencies
You must have a C compiler and `make` installed, as well as the `cargo-afl` crate:
```
sudo apt install build-essentials
cargo install cargo-afl
```
## Building the Fuzzer
```
cargo afl build --release
```
## Running the Fuzzer
You must provide an input directory containing sample files for the fuzzer to mutate.
You must provide an output directory for the fuzzer to save crash results to.
```
cargo afl fuzz -i input_directory -o output_directory ./target/release/fuzz
```
================================================
FILE: fuzzing/src/main.rs
================================================
use afl::fuzz;
use binwalk::Binwalk;
fn main() {
// AFL makes this real simple...
fuzz!(|data: &[u8]| {
// Initialize binwalk, no extraction
let binwalker = Binwalk::new();
// Scan the data provided by AFL
binwalker.scan(&data.to_vec());
});
}
================================================
FILE: scripts/binwalk-ui
================================================
#! /bin/bash -
# https://unix.stackexchange.com/questions/290696/display-stdout-and-stderr-in-two-separate-streams
#
# This script will run binwalk in a split screen, displaying results in the top screen and debug output in the bottom screen.
# The `screen` utility must be installed.
# If BINWALK_PATH is not set, assume it is in the default cargo target path
if [[ -z "${BINWALK_PATH}" ]]; then
BINWALK_PATH="$(cd "$(dirname $0)" && pwd)/../target/release/binwalk"
fi
# If no RUST_LOG level is defined, default to `info`
if [[ -z "${RUST_LOG}" ]]; then
export RUST_LOG=info
fi
tmpdir=$(mktemp -d) || exit
trap 'rm -rf "$tmpdir"' EXIT INT TERM HUP
FIFO=$tmpdir/FIFO
mkfifo "$FIFO" || exit
conf=$tmpdir/conf
cat > "$conf" << 'EOF' || exit
split -h
focus
screen -t stderr sh -c 'tty > "$FIFO"; read done < "$FIFO"'
focus
screen -t stdout sh -c 'read tty < "$FIFO"; eval "$CMD" 2> "$tty"; echo "[Command exited with status $?, press enter to exit]"; read prompt; echo done > "$FIFO"'
EOF
CMD="$BINWALK_PATH $*"
export FIFO CMD
screen -mc "$conf"
================================================
FILE: src/binwalk.rs
================================================
//! Primary Binwalk interface.
use aho_corasick::AhoCorasick;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path;
use uuid::Uuid;
#[cfg(windows)]
use std::os::windows;
#[cfg(unix)]
use std::os::unix;
use crate::common::{is_offset_safe, read_file};
use crate::extractors;
use crate::magic;
use crate::signatures;
/// Returned on initialization error
#[derive(Debug, Default, Clone)]
pub struct BinwalkError {
pub message: String,
}
impl BinwalkError {
pub fn new(message: &str) -> Self {
BinwalkError {
message: message.to_string(),
}
}
}
/// Analysis results returned by Binwalk::analyze
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct AnalysisResults {
/// Path to the file that was analyzed
pub file_path: String,
/// File signature results, as returned by Binwalk::scan
pub file_map: Vec<signatures::common::SignatureResult>,
/// File extraction results, as returned by Binwalk::extract.
/// HashMap key is the corresponding SignatureResult.id value in `file_map`.
pub extractions: HashMap<String, extractors::common::ExtractionResult>,
}
/// Analyze files / memory for file signatures
///
/// ## Example
///
/// ```
/// use binwalk::Binwalk;
///
/// let target_file = "/bin/ls";
/// let data_to_scan = std::fs::read(target_file).expect("Unable to read file");
///
/// let binwalker = Binwalk::new();
///
/// let signature_results = binwalker.scan(&data_to_scan);
///
/// for result in &signature_results {
/// println!("Found '{}' at offset {:#X}", result.description, result.offset);
/// }
/// ```
#[derive(Debug, Default, Clone)]
pub struct Binwalk {
/// Count of all signatures (short and regular)
pub signature_count: usize,
/// Count of all magic patterns (short and regular)
pub pattern_count: usize,
/// The base file requested for analysis
pub base_target_file: String,
/// The base output directory for extracted files
pub base_output_directory: String,
/// A list of signatures that must start at offset 0
pub short_signatures: Vec<signatures::common::Signature>,
/// A list of magic bytes to search for throughout the entire file
pub patterns: Vec<Vec<u8>>,
/// Maps patterns to their corresponding signature
pub pattern_signature_table: HashMap<usize, signatures::common::Signature>,
/// Maps signatures to their corresponding extractors
pub extractor_lookup_table: HashMap<String, Option<extractors::common::Extractor>>,
}
impl Binwalk {
/// Create a new Binwalk instance with all default values.
/// Equivalent to `Binwalk::configure(None, None, None, None, None, false)`.
///
/// ## Example
///
/// ```
/// use binwalk::Binwalk;
///
/// let binwalker = Binwalk::new();
/// ```
#[allow(dead_code)]
pub fn new() -> Binwalk {
Binwalk::configure(None, None, None, None, None, false).unwrap()
}
/// Create a new Binwalk instance.
///
/// If `target_file_name` and `output_directory` are specified, the `output_directory` will be created if it does not
/// already exist, and a symlink to `target_file_name` will be placed inside the `output_directory`. The path to this
/// symlink is placed in `Binwalk.base_target_file`.
///
/// The `include` and `exclude` arguments specify include and exclude signature filters. The String values contained
/// in these arguments must match the `Signature.name` values defined in magic.rs.
///
/// Additional user-defined signatures may be provided via the `signatures` argument.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_102_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::Binwalk;
///
/// // Don't scan for these file signatures
/// let exclude_filters: Vec<String> = vec!["jpeg".to_string(), "png".to_string()];
///
/// let binwalker = Binwalk::configure(None,
/// None,
/// None,
/// Some(exclude_filters),
/// None,
/// false)?;
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_102_0(); }
/// ```
pub fn configure(
target_file_name: Option<String>,
output_directory: Option<String>,
include: Option<Vec<String>>,
exclude: Option<Vec<String>>,
signatures: Option<Vec<signatures::common::Signature>>,
full_search: bool,
) -> Result<Binwalk, BinwalkError> {
let mut new_instance = Binwalk {
..Default::default()
};
// Target file is optional, especially if being called via the library
if let Some(target_file) = target_file_name {
// Set the target file path, make it an absolute path
match path::absolute(&target_file) {
Err(_) => {
return Err(BinwalkError::new(&format!(
"Failed to get absolute path for '{target_file}'"
)));
}
Ok(abspath) => {
new_instance.base_target_file = abspath.display().to_string();
}
}
// If an output extraction directory was also specified, initialize it
if let Some(extraction_directory) = output_directory {
// Make the extraction directory an absolute path
match path::absolute(&extraction_directory) {
Err(_) => {
return Err(BinwalkError::new(&format!(
"Failed to get absolute path for '{extraction_directory}'"
)));
}
Ok(abspath) => {
new_instance.base_output_directory = abspath.display().to_string();
}
}
// Initialize the extraction directory. This will create the directory if it
// does not exist, and create a symlink inside the directory that points to
// the specified target file.
match init_extraction_directory(
&new_instance.base_target_file,
&new_instance.base_output_directory,
) {
Err(e) => {
return Err(BinwalkError::new(&format!(
"Failed to initialize extraction directory: {e}"
)));
}
Ok(new_target_file_path) => {
// This is the new base target path (a symlink inside the extraction directory)
new_instance.base_target_file = new_target_file_path.clone();
}
}
}
}
// Load all internal signature patterns
let mut signature_patterns = magic::patterns();
// Include any user-defined signature patterns
if let Some(user_defined_signature_patterns) = signatures {
signature_patterns.extend(user_defined_signature_patterns);
}
// Load magic signatures
for signature in signature_patterns.clone() {
// Check if this signature should be included
if !include_signature(&signature, &include, &exclude) {
continue;
}
// Keep a count of total unique signatures that are supported
new_instance.signature_count += 1;
// Keep a count of the total number of magic patterns
new_instance.pattern_count += signature.magic.len();
// Create a lookup table which associates each signature to its respective extractor
new_instance
.extractor_lookup_table
.insert(signature.name.clone(), signature.extractor.clone());
// Each signature may have multiple magic bytes associated with it
for pattern in signature.magic.clone() {
if signature.short && !full_search {
// These are short patterns, and should only be searched for at the very beginning of a file
new_instance.short_signatures.push(signature.clone());
break;
} else {
/*
* Need to keep a mapping of the pattern index and its associated signature
* so that when a match is found it can be resolved back to the signature from
* which it came.
*/
new_instance
.pattern_signature_table
.insert(new_instance.patterns.len(), signature.clone());
// Add these magic bytes to the list of patterns
new_instance.patterns.push(pattern.to_vec());
}
}
}
Ok(new_instance)
}
/// Scan a file for magic signatures.
/// Returns a list of validated magic signatures representing the known contents of the file.
///
/// ## Example
///
/// ```
/// use binwalk::Binwalk;
///
/// let target_file = "/bin/ls";
/// let data_to_scan = std::fs::read(target_file).expect("Unable to read file");
///
/// let binwalker = Binwalk::new();
///
/// let signature_results = binwalker.scan(&data_to_scan);
///
/// for result in &signature_results {
/// println!("{:#X} {}", result.offset, result.description);
/// }
///
/// assert!(signature_results.len() > 0);
/// ```
pub fn scan(&self, file_data: &[u8]) -> Vec<signatures::common::SignatureResult> {
const FILE_START_OFFSET: usize = 0;
let mut index_adjustment: usize = 0;
let mut next_valid_offset: usize = 0;
let mut previous_valid_offset = None;
let available_data = file_data.len();
// A list of identified signatures, representing a "map" of the file data
let mut file_map: Vec<signatures::common::SignatureResult> = vec![];
/*
* Check beginning of file for short signatures.
* These signatures are only valid if they occur at the very beginning of a file.
* This is typically because the signatures are very short and they are likely
* to occur randomly throughout the file, so this prevents having to validate many
* false positve matches.
*/
for signature in &self.short_signatures {
for magic in signature.magic.clone() {
let magic_start = FILE_START_OFFSET + signature.magic_offset;
let magic_end = magic_start + magic.len();
if file_data.len() > magic_end && file_data[magic_start..magic_end] == magic {
debug!(
"Found {} short magic match at offset {:#X}",
signature.description, magic_start
);
if let Ok(mut signature_result) = (signature.parser)(file_data, magic_start) {
// Auto populate some signature result fields
signature_result_auto_populate(&mut signature_result, signature);
// Add this signature to the file map
file_map.push(signature_result.clone());
info!(
"Found valid {} short signature at offset {:#X}",
signature_result.name, FILE_START_OFFSET
);
// Only update the next_valid_offset if confidence is high; these are, after all, short signatures
if signature_result.confidence >= signatures::common::CONFIDENCE_HIGH {
next_valid_offset = signature_result.offset + signature_result.size;
}
// Only one signature can match at fixed offset 0
break;
} else {
debug!(
"{} short signature match at offset {:#X} is invalid",
signature.description, FILE_START_OFFSET
);
}
}
}
}
/*
* Same pattern matching algorithm used by fgrep.
* This will search for all magic byte patterns in the file data, all at once.
* https://en.wikipedia.org/wiki/Aho–Corasick_algorithm
*/
let grep = AhoCorasick::new(self.patterns.clone()).unwrap();
debug!("Running Aho-Corasick scan");
/*
* Outer loop wrapper for AhoCorasick scan loop. This will loop until:
*
* 1) next_valid_offset exceeds available_data
* 2) previous_valid_offset <= next_valid_offset
*/
while is_offset_safe(available_data, next_valid_offset, previous_valid_offset) {
// Update the previous valid offset in praparation for the next loop iteration
previous_valid_offset = Some(next_valid_offset);
debug!("Continuing scan from offset {next_valid_offset:#X}");
/*
* Run a new AhoCorasick scan starting at the next valid offset in the file data.
* This will loop until:
*
* 1) All data has been exhausted, in which case previous_valid_offset and next_valid_offset
* will be identical, causing the outer while loop to break.
* 2) A valid signature with a defined size is found, in which case next_valid_offset will
* be updated to point the end of the valid signature data, causing a new AhoCorasick
* scan to start at the new next_valid_offset file location.
*/
for magic_match in grep.find_overlapping_iter(&file_data[next_valid_offset..]) {
// Get the location of the magic bytes inside the file data
let magic_offset: usize = next_valid_offset + magic_match.start();
// Get the signature associated with this magic signature
let magic_pattern_index: usize = magic_match.pattern().as_usize();
let signature: signatures::common::Signature = self
.pattern_signature_table
.get(&magic_pattern_index)
.unwrap()
.clone();
debug!(
"Found {} magic match at offset {:#X}",
signature.description, magic_offset
);
/*
* Invoke the signature parser to parse and validate the signature.
* An error indicates a false positive match for the signature type.
*/
if let Ok(mut signature_result) = (signature.parser)(file_data, magic_offset) {
// Calculate the end of this signature's data
let signature_end_offset = signature_result.offset + signature_result.size;
// Sanity check the reported offset and size vs file size
if signature_end_offset > available_data {
info!("Signature {} extends beyond EOF; ignoring", signature.name);
// Continue inner loop
continue;
}
// Auto populate some signature result fields
signature_result_auto_populate(&mut signature_result, &signature);
// Add this signature to the file map
file_map.push(signature_result.clone());
info!(
"Found valid {} signature at offset {:#X}",
signature_result.name, signature_result.offset
);
// Only update the next_valid_offset if confidence is at least medium
if signature_result.confidence >= signatures::common::CONFIDENCE_MEDIUM {
// Only update the next_valid offset if the end of the signature reported the size of its contents
if signature_result.size > 0 {
// This file's signature has a known size, so there's no need to scan inside this file's data.
// Update next_valid_offset to point to the end of this file signature and break out of the
// inner loop.
next_valid_offset = signature_end_offset;
break;
}
}
} else {
debug!(
"{} magic match at offset {:#X} is invalid",
signature.description, magic_offset
);
}
}
}
debug!("Aho-Corasick scan found {} magic matches", file_map.len());
/*
* A file's magic bytes do not always start at the beginning of a file, meaning that it is possible
* that the order in which the signatures were found in the file data is not the order in which we
* want to process/validate the signatures. Each signature's parser function will report the correct
* starting offset for the signature, so sort the file_map by the SignatureResult.offset value.
*/
file_map.sort();
next_valid_offset = 0;
/*
* Now that signatures are in the correct order, identify and any overlapping signatures
* (such as gzip files identified within a tarball archive), signatures with the same reported offset,
* and any signatures with an invalid reported size (i.e., the size extends beyond the end of available file_data).
*/
for mut i in 0..file_map.len() {
// Some entries may have been removed from the file_map list in previous loop iterations; adjust the index accordingly
i -= index_adjustment;
// Make sure the file map index is valid
if file_map.is_empty() || i >= file_map.len() {
break;
}
let this_signature = file_map[i].clone();
let remaining_available_size = file_data.len() - this_signature.offset;
// Check if the previous file map entry had the same reported starting offset as this one
if i > 0 && this_signature.offset == file_map[i - 1].offset {
// Get the previous signature in the file map
let previous_signature = file_map[i - 1].clone();
// If this file map entry and the conflicting entry do not have the same confidence level, default to the one with highest confidence
if this_signature.confidence != previous_signature.confidence {
debug!(
"Conflicting signatures at offset {:#X}; defaulting to the signature with highest confidence",
this_signature.offset
);
// If this signature is higher confidence, invalidate the previous signature
if this_signature.confidence > previous_signature.confidence {
file_map.remove(i - 1);
index_adjustment += 1;
// Else, this signature has a lower confidence; invalidate this signature and continue to the next signature in the list
} else {
file_map.remove(i);
index_adjustment += 1;
continue;
}
// Conflicting signatures have identical confidence levels; defer to the previously vetted signature
} else {
debug!(
"Conflicting signatures at offset {:#X} with the same confidence; first come, first served",
this_signature.offset
);
file_map.remove(i);
index_adjustment += 1;
continue;
}
// Else, if the offsets don't conflict, make sure this signature doesn't fall inside a previously identified signature's data
} else if this_signature.offset < next_valid_offset {
debug!(
"Signature {} at offset {:#X} contains conflicting data; ignoring",
this_signature.name, this_signature.offset
);
file_map.remove(i);
index_adjustment += 1;
continue;
}
// If we've made it this far, make sure this signature's data doesn't extend beyond EOF and that the file data doesn't wrap around
if this_signature.size > remaining_available_size
|| ((this_signature.offset + this_signature.size) as isize) < 0
{
debug!(
"Signature {} at offset {:#X} claims its size extends beyond EOF; ignoring",
this_signature.name, this_signature.offset
);
file_map.remove(i);
index_adjustment += 1;
continue;
}
// This signature looks OK, update the next_valid_offset to be the end of this signature's data, only if we're fairly confident in the signature
if this_signature.confidence >= signatures::common::CONFIDENCE_MEDIUM {
next_valid_offset = this_signature.offset + this_signature.size;
}
}
/*
* Ideally, all signatures would report their size; some file formats do not specify a size, and the only
* way to determine the size is to extract the file format (compressed data, for example).
* For signatures with a reported size of 0, update their size to be the start of the next signature, or EOF.
* This makes the assumption that there are no false positives or false negatives.
*
* False negatives (i.e., there is some other file format or data between this signature and the next that
* was not correctly identified) is less problematic, as this will overestimate the size of this signature,
* but most extraction utilities don't care about this extra trailing data being included.
*
* False positives (i.e., some data inside of this signature is identified as some other file type) can cause
* this signature's file data to become truncated, which will inevitably result in a failed, or partial, extraction.
*
* Thus, signatures must be very good at validating magic matches and eliminating false positives.
*/
for i in 0..file_map.len() {
if file_map[i].size == 0 {
// Index of the next file map entry, if any
let next_index = i + 1;
// By default, assume this signature goes to EOF
let mut next_offset: usize = file_data.len();
// If there are more entries in the file map
if next_index < file_map.len() {
// Look through all remaining file map entries for one with medium to high confidence
for file_map_entry in file_map.iter().skip(next_index) {
if file_map_entry.confidence >= signatures::common::CONFIDENCE_MEDIUM {
// If a signature of at least medium confidence is found, assume that *this* signature ends there
next_offset = file_map_entry.offset;
break;
}
}
}
file_map[i].size = next_offset - file_map[i].offset;
warn!(
"Signature {}:{:#X} size is unknown; assuming size of {:#X} bytes",
file_map[i].name, file_map[i].offset, file_map[i].size
);
} else {
debug!(
"Signature {}:{:#X} has a reported size of {:#X} bytes",
file_map[i].name, file_map[i].offset, file_map[i].size
);
}
}
debug!("Found {} valid signatures", file_map.len());
file_map
}
/// Extract all extractable signatures found in a file.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_529_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::Binwalk;
///
/// let target_path = std::path::Path::new("tests")
/// .join("inputs")
/// .join("gzip.bin")
/// .display()
/// .to_string();
///
/// let extraction_directory = std::path::Path::new("tests")
/// .join("extractions")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&extraction_directory);
/// let binwalker = Binwalk::configure(Some(target_path),
/// Some(extraction_directory.clone()),
/// None,
/// None,
/// None,
/// false)?;
///
/// let file_data = std::fs::read(&binwalker.base_target_file).expect("Unable to read file");
///
/// let scan_results = binwalker.scan(&file_data);
/// let extraction_results = binwalker.extract(&file_data, &binwalker.base_target_file, &scan_results);
///
/// assert_eq!(scan_results.len(), 1);
/// assert_eq!(extraction_results.len(), 1);
/// assert_eq!(std::path::Path::new(&extraction_directory)
/// .join("gzip.bin.extracted")
/// .join("0")
/// .join("decompressed.bin")
/// .exists(), true);
/// # std::fs::remove_dir_all(&extraction_directory);
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_529_0(); }
/// ```
pub fn extract(
&self,
file_data: &[u8],
file_name: impl Into<String>,
file_map: &Vec<signatures::common::SignatureResult>,
) -> HashMap<String, extractors::common::ExtractionResult> {
let file_path = file_name.into();
let mut extraction_results: HashMap<String, extractors::common::ExtractionResult> =
HashMap::new();
// Spawn extractors for each extractable signature
for signature in file_map {
// Signatures may opt to not perform extraction; honor this request
if signature.extraction_declined {
continue;
}
// Get the extractor for this signature
let extractor = self.extractor_lookup_table[&signature.name].clone();
match &extractor {
None => continue,
Some(_) => {
// Run an extraction for this signature
let mut extraction_result =
extractors::common::execute(file_data, &file_path, signature, &extractor);
if !extraction_result.success {
debug!(
"Extraction failed for {} (ID: {}) {:#X} - {:#X}",
signature.name, signature.id, signature.offset, signature.size
);
// Calculate all available data from the start of this signature to EOF
let available_data = file_data.len() - signature.offset;
/*
* If extraction failed, it could be due to truncated data (signature matching is not perfect ya know!)
* In that case, make one more attempt, this time provide the extractor all the data possible.
*/
if signature.size < available_data {
// Create a duplicate signature, but set its reported size to the length of all available data
let mut new_signature = signature.clone();
new_signature.size = available_data;
debug!(
"Trying extraction for {} (ID: {}) again, this time from {:#X} - {:#X}",
new_signature.name,
new_signature.id,
new_signature.offset,
new_signature.size
);
// Re-run the extraction
extraction_result = extractors::common::execute(
file_data,
&file_path,
&new_signature,
&extractor,
);
}
}
// Update the HashMap with the result of this extraction attempt
extraction_results.insert(signature.id.clone(), extraction_result);
}
}
}
extraction_results
}
/// Analyze a data buffer and optionally extract the file contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_672_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::{Binwalk, common};
///
/// let target_path = std::path::Path::new("tests")
/// .join("inputs")
/// .join("gzip.bin")
/// .display()
/// .to_string();
///
/// let extraction_directory = std::path::Path::new("tests")
/// .join("extractions")
/// .display()
/// .to_string();
///
/// let file_data = common::read_file(&target_path).expect("Failed to read file data");
///
/// # std::fs::remove_dir_all(&extraction_directory);
/// let binwalker = Binwalk::configure(Some(target_path),
/// Some(extraction_directory.clone()),
/// None,
/// None,
/// None,
/// false)?;
///
/// let analysis_results = binwalker.analyze_buf(&file_data, &binwalker.base_target_file, true);
///
/// assert_eq!(analysis_results.file_map.len(), 1);
/// assert_eq!(analysis_results.extractions.len(), 1);
/// assert_eq!(std::path::Path::new(&extraction_directory)
/// .join("gzip.bin.extracted")
/// .join("0")
/// .join("decompressed.bin")
/// .exists(), true);
/// # std::fs::remove_dir_all(&extraction_directory);
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_672_0(); }
/// ```
pub fn analyze_buf(
&self,
file_data: &[u8],
target_file: impl Into<String>,
do_extraction: bool,
) -> AnalysisResults {
let file_path = target_file.into();
// Return value
let mut results: AnalysisResults = AnalysisResults {
file_path: file_path.clone(),
..Default::default()
};
// Scan file data for signatures
debug!("Analysis start: {file_path}");
results.file_map = self.scan(file_data);
// Only extract if told to, and if there were some signatures found in this file
if do_extraction && !results.file_map.is_empty() {
// Extract everything we can
debug!(
"Submitting {} signature results to extractor",
results.file_map.len()
);
results.extractions = self.extract(file_data, &file_path, &results.file_map);
}
debug!("Analysis end: {file_path}");
results
}
/// Analyze a file on disk and optionally extract its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_binwalk_rs_745_0() -> Result<binwalk::Binwalk, binwalk::BinwalkError> {
/// use binwalk::Binwalk;
///
/// let target_path = std::path::Path::new("tests")
/// .join("inputs")
/// .join("gzip.bin")
/// .display()
/// .to_string();
///
/// let extraction_directory = std::path::Path::new("tests")
/// .join("extractions")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&extraction_directory);
/// let binwalker = Binwalk::configure(Some(target_path),
/// Some(extraction_directory.clone()),
/// None,
/// None,
/// None,
/// false)?;
///
/// let analysis_results = binwalker.analyze(&binwalker.base_target_file, true);
///
/// assert_eq!(analysis_results.file_map.len(), 1);
/// assert_eq!(analysis_results.extractions.len(), 1);
/// assert_eq!(std::path::Path::new(&extraction_directory)
/// .join("gzip.bin.extracted")
/// .join("0")
/// .join("decompressed.bin")
/// .exists(), true);
/// # std::fs::remove_dir_all(&extraction_directory);
/// # Ok(binwalker)
/// # } _doctest_main_src_binwalk_rs_745_0(); }
/// ```
#[allow(dead_code)]
pub fn analyze(&self, target_file: impl Into<String>, do_extraction: bool) -> AnalysisResults {
let file_path = target_file.into();
let file_data = match read_file(&file_path) {
Err(_) => {
error!("Failed to read data from {file_path}");
b"".to_vec()
}
Ok(data) => data,
};
self.analyze_buf(&file_data, &file_path, do_extraction)
}
}
/// Initializes the extraction output directory
fn init_extraction_directory(
target_file: &str,
extraction_directory: &str,
) -> Result<String, std::io::Error> {
// Create the output directory, equivalent of mkdir -p
match fs::create_dir_all(extraction_directory) {
Ok(_) => {
debug!("Created base output directory: '{extraction_directory}'");
}
Err(e) => {
error!("Failed to create base output directory '{extraction_directory}': {e}");
return Err(e);
}
}
// Create a Path for the target file
let target_path = path::Path::new(&target_file);
// Build a symlink path to the target file in the extraction directory
let link_target_path_str = format!(
"{}{}{}",
extraction_directory,
path::MAIN_SEPARATOR,
target_path.file_name().unwrap().to_str().unwrap()
);
// Create a path for the symlink target path
let link_path = path::Path::new(&link_target_path_str);
if link_path.exists() {
return Ok(link_target_path_str);
}
debug!(
"Creating symlink from {} -> {}",
link_path.display(),
target_path.display()
);
// Create a symlink from inside the extraction directory to the specified target file
#[cfg(unix)]
{
match unix::fs::symlink(target_path, link_path) {
Ok(_) => Ok(link_target_path_str),
Err(e) => {
error!(
"Failed to create symlink {} -> {}: {}",
link_path.display(),
target_path.display(),
e
);
Err(e)
}
}
}
#[cfg(windows)]
{
match std::fs::hard_link(target_path, link_path) {
Ok(_) => {
return Ok(link_target_path_str);
}
Err(e) => {
error!(
"Failed to create hardlink {} -> {}: {}",
link_path.display(),
target_path.display(),
e
);
return Err(e);
}
}
}
}
/// Returns true if the signature should be included for file analysis, else returns false.
fn include_signature(
signature: &signatures::common::Signature,
include: &Option<Vec<String>>,
exclude: &Option<Vec<String>>,
) -> bool {
if let Some(include_signatures) = include {
for include_str in include_signatures {
if signature.name.to_lowercase() == include_str.to_lowercase() {
return true;
}
}
return false;
}
if let Some(exclude_signatures) = exclude {
for exclude_str in exclude_signatures {
if signature.name.to_lowercase() == exclude_str.to_lowercase() {
return false;
}
}
return true;
}
true
}
/// Some SignatureResult fields need to be auto-populated.
fn signature_result_auto_populate(
signature_result: &mut signatures::common::SignatureResult,
signature: &signatures::common::Signature,
) {
signature_result.id = Uuid::new_v4().to_string();
signature_result.name = signature.name.clone();
signature_result.always_display = signature.always_display;
}
================================================
FILE: src/cliparser.rs
================================================
use clap::{CommandFactory, Parser};
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct CliArgs {
/// List supported signatures and extractors
#[arg(short = 'L', long)]
pub list: bool,
/// Read data from standard input
#[arg(short, long)]
pub stdin: bool,
/// Supress normal stdout output
#[arg(short, long)]
pub quiet: bool,
/// During recursive extraction display *all* results
#[arg(short, long)]
pub verbose: bool,
/// Automatically extract known file types
#[arg(short, long)]
pub extract: bool,
/// Carve both known and unknown file contents to disk
#[arg(short, long)]
pub carve: bool,
/// Recursively scan extracted files
#[arg(short = 'M', long)]
pub matryoshka: bool,
/// Search for all signatures at all offsets
#[arg(short = 'a', long)]
pub search_all: bool,
/// Generate an entropy graph with Plotly
#[arg(short = 'E', long, conflicts_with = "extract")]
pub entropy: bool,
/// Save entropy graph as a PNG file
#[arg(short, long)]
pub png: Option<String>,
/// Log JSON results to a file ('-' for stdout)
#[arg(short, long)]
pub log: Option<String>,
/// Manually specify the number of threads to use
#[arg(short, long)]
pub threads: Option<usize>,
/// Do no scan for these signatures
#[arg(short = 'x', long, value_delimiter = ',', num_args = 1..)]
pub exclude: Option<Vec<String>>,
/// Only scan for these signatures
#[arg(short = 'y', long, value_delimiter = ',', num_args = 1.., conflicts_with = "exclude")]
pub include: Option<Vec<String>>,
/// Extract files/folders to a custom directory
#[arg(short, long, default_value = "extractions")]
pub directory: String,
/// Path to the file to analyze
pub file_name: Option<String>,
}
pub fn parse() -> CliArgs {
let args = CliArgs::parse();
if std::env::args().len() == 1 {
CliArgs::command()
.print_help()
.expect("Failed to print help output");
std::process::exit(0);
}
args
}
================================================
FILE: src/common.rs
================================================
//! Common Functions
use chrono::prelude::DateTime;
use log::{debug, error};
use std::fs::File;
use std::io::Read;
/// Read a data into memory, either from disk or from stdin, and return its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_common_rs_11_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::common::read_input;
///
/// let file_data = read_input("/etc/passwd", false)?;
/// assert!(file_data.len() > 0);
/// # Ok(())
/// # } _doctest_main_src_common_rs_11_0(); }
/// ```
pub fn read_input(file: impl Into<String>, stdin: bool) -> Result<Vec<u8>, std::io::Error> {
if stdin { read_stdin() } else { read_file(file) }
}
/// Read data from standard input and return its contents.
pub fn read_stdin() -> Result<Vec<u8>, std::io::Error> {
let mut stdin_data = Vec::new();
match std::io::stdin().read_to_end(&mut stdin_data) {
Err(e) => {
error!("Failed to read data from stdin: {e}");
Err(e)
}
Ok(nbytes) => {
debug!("Loaded {nbytes} bytes from stdin");
Ok(stdin_data)
}
}
}
/// Read a file data into memory and return its contents.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_common_rs_48_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::common::read_file;
///
/// let file_data = read_file("/etc/passwd")?;
/// assert!(file_data.len() > 0);
/// # Ok(())
/// # } _doctest_main_src_common_rs_48_0(); }
/// ```
pub fn read_file(file: impl Into<String>) -> Result<Vec<u8>, std::io::Error> {
let mut file_data = Vec::new();
let file_path = file.into();
match File::open(&file_path) {
Err(e) => {
error!("Failed to open file {file_path}: {e}");
Err(e)
}
Ok(mut fp) => match fp.read_to_end(&mut file_data) {
Err(e) => {
error!("Failed to read file {file_path} into memory: {e}");
Err(e)
}
Ok(file_size) => {
debug!("Loaded {file_size} bytes from {file_path}");
Ok(file_data)
}
},
}
}
/// Calculates the CRC32 checksum of the given data.
///
/// ## Notes
///
/// Uses initial CRC value of 0.
///
/// ## Example
///
/// ```
/// use binwalk::common::crc32;
///
/// let my_data: &[u8] = b"ABCD";
///
/// let my_data_crc = crc32(my_data);
///
/// assert_eq!(my_data_crc, 0xDB1720A5);
/// ```
pub fn crc32(data: &[u8]) -> u32 {
crc32_v2::crc32(0, data)
}
/// Converts an epoch time to a formatted time string.
///
/// ## Example
///
/// ```
/// use binwalk::common::epoch_to_string;
///
/// let timestamp = epoch_to_string(0);
///
/// assert_eq!(timestamp, "1970-01-01 00:00:00");
/// ```
pub fn epoch_to_string(epoch_timestamp: u32) -> String {
let date_time = DateTime::from_timestamp(epoch_timestamp.into(), 0);
match date_time {
Some(dt) => dt.format("%Y-%m-%d %H:%M:%S").to_string(),
None => "".to_string(),
}
}
/// Get a C-style NULL-terminated string from the provided list of u8 bytes.
/// Return value does not include the terminating NULL byte.
fn get_cstring_bytes(raw_data: &[u8]) -> Vec<u8> {
let mut cstring: Vec<u8> = vec![];
for raw_byte in raw_data {
if *raw_byte == 0 {
break;
} else {
cstring.push(*raw_byte);
}
}
cstring
}
/// Get a C-style NULL-terminated string from the provided array of u8 bytes.
///
/// ## Example
///
/// ```
/// use binwalk::common::get_cstring;
///
/// let raw_data: &[u8] = b"this_is_a_c_string\x00";
///
/// let string = get_cstring(raw_data);
///
/// assert_eq!(string, "this_is_a_c_string");
/// ```
pub fn get_cstring(raw_data: &[u8]) -> String {
let raw_string = get_cstring_bytes(raw_data);
let string: String = match String::from_utf8(raw_string) {
Err(_) => "".to_string(),
Ok(s) => s.clone(),
};
string
}
/// Returns true if the provided byte is an ASCII number
///
/// ## Example
///
/// ```
/// use binwalk::common::is_ascii_number;
///
/// assert!(is_ascii_number(0x31));
/// assert!(!is_ascii_number(0xFE));
/// ```
pub fn is_ascii_number(b: u8) -> bool {
const ZERO: u8 = 48;
const NINE: u8 = 57;
(ZERO..=NINE).contains(&b)
}
/// Returns true if the provided byte is a printable ASCII character
///
/// ## Example
///
/// ```
/// use binwalk::common::is_printable_ascii;
///
/// assert!(is_printable_ascii(0x41));
/// assert!(!is_printable_ascii(0xFE));
/// ```
pub fn is_printable_ascii(b: u8) -> bool {
const ASCII_MIN: u8 = 0x0A;
const ASCII_MAX: u8 = 0x7E;
(ASCII_MIN..=ASCII_MAX).contains(&b)
}
/// Validates data offsets to prevent out-of-bounds access and infinite loops while parsing file formats.
///
/// ## Notes
///
/// - `next_offset` must be within the bounds of `available_data`
/// - `previous_offset` must be less than `next_offset`, or `None`
///
/// ## Example
///
/// ```
/// use binwalk::common::is_offset_safe;
///
/// let my_data: &[u8] = b"ABCD";
/// let available_data = my_data.len();
///
/// assert!(is_offset_safe(available_data, 0, None));
/// assert!(!is_offset_safe(available_data, 4, None));
/// assert!(is_offset_safe(available_data, 2, Some(1)));
/// assert!(!is_offset_safe(available_data, 2, Some(2)));
/// assert!(!is_offset_safe(available_data, 1, Some(2)));
/// ```
pub fn is_offset_safe(
available_data: usize,
next_offset: usize,
last_offset: Option<usize>,
) -> bool {
// If a previous file offset was specified, ensure that it is less than the next file offset
if let Some(previous_offset) = last_offset {
if previous_offset >= next_offset {
return false;
}
}
// Ensure that the next file offset is within the bounds of available file data
if next_offset >= available_data {
return false;
}
true
}
================================================
FILE: src/display.rs
================================================
use crate::binwalk::AnalysisResults;
use crate::extractors;
use crate::signatures;
use colored::ColoredString;
use colored::Colorize;
use log::error;
use std::collections::HashMap;
use std::io;
use std::io::Write;
use std::time;
const DELIM_CHARACTER: &str = "-";
const DEFAULT_TERMINAL_WIDTH: u16 = 200;
const COLUMN1_WIDTH: usize = 35;
const COLUMN2_WIDTH: usize = 35;
fn terminal_width() -> usize {
let terminal_width: u16 = match termsize::get() {
Some(ts) => ts.cols,
None => DEFAULT_TERMINAL_WIDTH,
};
terminal_width as usize
}
fn line_delimiter() -> String {
let mut delim: String = "".to_string();
for _i in 0..terminal_width() {
delim += DELIM_CHARACTER;
}
delim
}
fn center_text(text: &str) -> String {
let mut padding_width: i32;
let mut centered_string: String = "".to_string();
match ((terminal_width() / 2) - (text.len() / 2)).try_into() {
Err(_e) => padding_width = 0,
Ok(value) => padding_width = value,
}
if padding_width < 0 {
padding_width = 0;
}
for _i in 0..padding_width {
centered_string += " ";
}
centered_string += text;
centered_string
}
fn pad_to_length(text: &str, len: usize) -> String {
let mut pad_size: i32;
let mut padded_string = String::from(text);
match (len - text.len()).try_into() {
Err(_e) => pad_size = 0,
Ok(value) => pad_size = value,
}
if pad_size < 0 {
pad_size = 0;
}
for _i in 0..pad_size {
padded_string += " ";
}
padded_string
}
fn line_wrap(text: &str, prefix_size: usize) -> String {
let mut this_line = "".to_string();
let mut formatted_string = "".to_string();
let max_line_size: usize = terminal_width() - prefix_size;
for word in text.split_whitespace() {
if (this_line.len() + word.len()) < max_line_size {
this_line = this_line + word + " ";
} else {
formatted_string = formatted_string + &this_line + "\n";
for _i in 0..prefix_size {
formatted_string += " ";
}
this_line = word.to_string() + " ";
}
}
formatted_string = formatted_string + &this_line;
formatted_string.trim().to_string()
}
fn print_column_headers(col1: &str, col2: &str, col3: &str) {
let header_string = format!(
"{}{}{}",
pad_to_length(col1, COLUMN1_WIDTH),
pad_to_length(col2, COLUMN2_WIDTH),
col3
);
println!("{}", header_string.bold().bright_blue());
}
fn print_delimiter() {
println!("{}", line_delimiter().bold().bright_blue());
}
fn print_header(title_text: &str) {
println!();
println!("{}", center_text(title_text).bold().magenta());
print_delimiter();
print_column_headers("DECIMAL", "HEXADECIMAL", "DESCRIPTION");
print_delimiter();
}
fn print_footer() {
print_delimiter();
println!();
}
fn print_signature(signature: &signatures::common::SignatureResult) {
let decimal_string = format!("{}", signature.offset);
let hexadecimal_string = format!("{:#X}", signature.offset);
let display_string = format!(
"{}{}{}",
pad_to_length(&decimal_string, COLUMN1_WIDTH),
pad_to_length(&hexadecimal_string, COLUMN2_WIDTH),
line_wrap(&signature.description, COLUMN1_WIDTH + COLUMN2_WIDTH)
);
if signature.confidence >= signatures::common::CONFIDENCE_HIGH {
println!("{}", display_string.green());
} else if signature.confidence >= signatures::common::CONFIDENCE_MEDIUM {
println!("{}", display_string.yellow());
} else {
println!("{}", display_string.red());
}
}
fn print_signatures(signatures: &Vec<signatures::common::SignatureResult>) {
for signature in signatures {
print_signature(signature);
}
}
fn print_extraction(
signature: &signatures::common::SignatureResult,
extraction: Option<&extractors::common::ExtractionResult>,
) {
let extraction_message: ColoredString;
match extraction {
None => {
extraction_message = format!(
"[#] Extraction of {} data at offset {:#X} declined",
signature.name, signature.offset
)
.bold()
.yellow();
}
Some(extraction_result) => {
if extraction_result.success {
extraction_message = format!(
"[+] Extraction of {} data at offset {:#X} completed successfully",
signature.name, signature.offset
)
.bold()
.green();
} else {
extraction_message = format!(
"[-] Extraction of {} data at offset {:#X} failed!",
signature.name, signature.offset
)
.bold()
.red();
}
}
}
println!("{extraction_message}");
}
fn print_extractions(
signatures: &Vec<signatures::common::SignatureResult>,
extraction_results: &HashMap<String, extractors::common::ExtractionResult>,
) {
let mut delimiter_printed: bool = false;
for signature in signatures {
let mut printable_extraction: bool = false;
let mut extraction_result: Option<&extractors::common::ExtractionResult> = None;
// Only print extraction results if an extraction was attempted or explicitly declined
if signature.extraction_declined {
printable_extraction = true
} else if extraction_results.contains_key(&signature.id) {
printable_extraction = true;
extraction_result = Some(&extraction_results[&signature.id]);
}
if printable_extraction {
// Only print the delimiter line once
if !delimiter_printed {
print_delimiter();
delimiter_printed = true;
}
print_extraction(signature, extraction_result);
}
}
}
pub fn print_analysis_results(quiet: bool, extraction_attempted: bool, results: &AnalysisResults) {
if quiet {
return;
}
// Print signature results
print_header(&results.file_path);
print_signatures(&results.file_map);
// If extraction was attempted, print extraction results
if extraction_attempted {
print_extractions(&results.file_map, &results.extractions);
}
// Print the footer text
print_footer();
}
// Used by print_signature_list
#[derive(Debug, Default, Clone)]
struct SignatureInfo {
name: String,
is_short: bool,
has_extractor: bool,
extractor: String,
description: String,
}
pub fn print_signature_list(quiet: bool, signatures: &Vec<signatures::common::Signature>) {
let mut extractor_count: usize = 0;
let mut signature_count: usize = 0;
let mut sorted_descriptions: Vec<String> = vec![];
let mut signature_lookup: HashMap<String, SignatureInfo> = HashMap::new();
if quiet {
return;
}
// Print column headers
print_delimiter();
print_column_headers(
"Signature Description",
"Signature Name",
"Extraction Utility",
);
print_delimiter();
// Loop through all signatures
for signature in signatures {
// Convenience struct for storing some basic info about each signature
let mut signature_info = SignatureInfo {
..Default::default()
};
// Keep track of signature name, description, and if the signature is a "short" signature
signature_info.name = signature.name.clone();
signature_info.is_short = signature.short;
signature_info.description = signature.description.clone();
// Keep track of which signatures have associated extractors, and if so, what type of extractor
match &signature.extractor {
None => {
signature_info.has_extractor = false;
signature_info.extractor = "None".to_string();
}
Some(extractor) => {
signature_info.has_extractor = true;
match &extractor.utility {
extractors::common::ExtractorType::External(command) => {
signature_info.extractor = command.to_string();
}
extractors::common::ExtractorType::Internal(_) => {
signature_info.extractor = "Built-in".to_string();
}
extractors::common::ExtractorType::None => error!(
"An invalid extractor type exists for the '{}' signature",
signature.description
),
}
}
}
// Increment signature count
signature_count += 1;
// If there is an extractor for this signature, increment extractor count
if signature_info.has_extractor {
extractor_count += 1;
}
// Keep signature descriptions in a separate list, which wil be sorted alphabetically for display
sorted_descriptions.push(signature_info.description.clone());
// Lookup table associating signature descriptions with their SignatureInfo struct
signature_lookup.insert(signature.description.clone(), signature_info.clone());
}
// Sort signature descriptions alphabetically
sorted_descriptions.sort_by_key(|description| description.to_lowercase());
// Print signatures, sorted alphabetically by description
for description in sorted_descriptions {
let siginfo = &signature_lookup[&description];
let display_line = format!(
"{}{}{}",
pad_to_length(&description, COLUMN1_WIDTH),
pad_to_length(&siginfo.name, COLUMN2_WIDTH),
siginfo.extractor
);
if siginfo.is_short {
println!("{}", display_line.yellow());
} else {
println!("{}", display_line.green());
}
}
print_delimiter();
println!();
println!("Total signatures: {signature_count}");
println!("Extractable signatures: {extractor_count}");
}
pub fn print_stats(
quiet: bool,
run_time: time::Instant,
file_count: usize,
signature_count: usize,
pattern_count: usize,
) {
const MS_IN_A_SECOND: f64 = 1000.0;
const SECONDS_IN_A_MINUTE: f64 = 60.0;
const MINUTES_IN_AN_HOUR: f64 = 60.0;
let mut file_plural = "";
let mut units = "milliseconds";
let mut display_time: f64 = run_time.elapsed().as_millis() as f64;
if quiet {
return;
}
// Format the output time in a more human-readable manner
if display_time >= MS_IN_A_SECOND {
display_time /= MS_IN_A_SECOND;
units = "seconds";
if display_time >= SECONDS_IN_A_MINUTE {
display_time /= SECONDS_IN_A_MINUTE;
units = "minutes";
if display_time >= MINUTES_IN_AN_HOUR {
display_time /= MINUTES_IN_AN_HOUR;
units = "hours";
}
}
}
if file_count != 1 {
file_plural = "s";
}
println!(
"Analyzed {file_count} file{file_plural} for {signature_count} file signatures ({pattern_count} magic patterns) in {display_time:.1} {units}"
);
}
pub fn print_plain(quiet: bool, msg: &str) {
if !quiet {
print!("{msg}");
let _ = io::stdout().flush();
}
}
pub fn println_plain(quiet: bool, msg: &str) {
if !quiet {
println!("{msg}");
}
}
================================================
FILE: src/entropy.rs
================================================
use crate::common::read_input;
use entropy::shannon_entropy;
use plotly::layout::{Axis, Layout};
use plotly::{ImageFormat, Plot, Scatter};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct EntropyError;
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct BlockEntropy {
pub end: usize,
pub start: usize,
pub entropy: f32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct FileEntropy {
pub file: String,
pub blocks: Vec<BlockEntropy>,
}
/// Splits the supplied data up into blocks and calculates the entropy of each block.
fn blocks(data: &[u8]) -> Vec<BlockEntropy> {
const BLOCK_COUNT: usize = 2048;
let mut offset: usize = 0;
let block_size = if data.len() < BLOCK_COUNT {
data.len()
} else {
data.len() / BLOCK_COUNT
};
let mut chunker = data.chunks(block_size);
let mut entropy_blocks: Vec<BlockEntropy> = vec![];
loop {
match chunker.next() {
None => break,
Some(block_data) => {
let mut block = BlockEntropy {
..Default::default()
};
block.start = offset;
block.entropy = shannon_entropy(block_data);
block.end = block.start + block_data.len();
offset = block.end;
entropy_blocks.push(block);
}
}
}
entropy_blocks
}
pub fn plot(
file_path: impl Into<String>,
stdin: bool,
out_file: Option<String>,
) -> Result<FileEntropy, EntropyError> {
let mut x: Vec<usize> = Vec::new();
let mut y: Vec<f32> = Vec::new();
let target_file: String = file_path.into();
let mut file_entropy = FileEntropy {
file: target_file.clone(),
..Default::default()
};
// Read in the target file data
if let Ok(file_data) = read_input(&target_file, stdin) {
// Calculate the entropy of each file block
file_entropy.blocks = blocks(&file_data);
for block in &file_entropy.blocks {
x.push(block.start);
x.push(block.end);
y.push(block.entropy);
y.push(block.entropy);
}
let mut plot = Plot::new();
let trace = Scatter::new(x, y);
let layout = Layout::new()
.title("Entropy Graph")
.x_axis(Axis::new().title("File Offset"))
.y_axis(Axis::new().title("Entropy").range(vec![0, 8]));
plot.add_trace(trace);
plot.set_layout(layout);
match out_file {
None => plot.show(),
Some(out_file_name) => {
// TODO: Switch to plotly_static, which is the recommended way to do this
#[allow(deprecated)]
plot.write_image(&out_file_name, ImageFormat::PNG, 2048, 1024, 1.0);
}
}
return Ok(file_entropy);
}
Err(EntropyError)
}
================================================
FILE: src/extractors/androidsparse.rs
================================================
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::androidsparse;
/// Defines the internal extractor function for extracting Android Sparse files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::androidsparse::android_sparse_extractor;
///
/// match android_sparse_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn android_sparse_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_android_sparse),
..Default::default()
}
}
/// Android sparse internal extractor
pub fn extract_android_sparse(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "unsparsed.img";
let mut result = ExtractionResult {
..Default::default()
};
// Parse the sparse file header
if let Ok(sparse_header) = androidsparse::parse_android_sparse_header(&file_data[offset..]) {
let available_data: usize = file_data.len();
let mut last_chunk_offset: Option<usize> = None;
let mut processed_chunk_count: usize = 0;
let mut next_chunk_offset: usize = offset + sparse_header.header_size;
while is_offset_safe(available_data, next_chunk_offset, last_chunk_offset) {
// Parse the next chunk's header
match androidsparse::parse_android_sparse_chunk_header(&file_data[next_chunk_offset..])
{
Err(_) => {
break;
}
Ok(chunk_header) => {
// If not a dry run, extract the data from the next chunk
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
let chunk_data_start: usize = next_chunk_offset + chunk_header.header_size;
let chunk_data_end: usize = chunk_data_start + chunk_header.data_size;
if let Some(chunk_data) = file_data.get(chunk_data_start..chunk_data_end) {
if !extract_chunk(
&sparse_header,
&chunk_header,
chunk_data,
OUTFILE_NAME,
&chroot,
) {
break;
}
} else {
break;
}
}
processed_chunk_count += 1;
last_chunk_offset = Some(next_chunk_offset);
next_chunk_offset += chunk_header.header_size + chunk_header.data_size;
}
}
}
// Make sure the number of processed chunks equals the number of chunks reported in the sparse flie header
if processed_chunk_count == sparse_header.chunk_count {
result.success = true;
result.size = Some(next_chunk_offset - offset);
}
}
result
}
// Extract a sparse file chunk to disk
fn extract_chunk(
sparse_header: &androidsparse::AndroidSparseHeader,
chunk_header: &androidsparse::AndroidSparseChunkHeader,
chunk_data: &[u8],
outfile: &str,
chroot: &Chroot,
) -> bool {
if chunk_header.is_raw {
// Raw chunks are just data chunks stored verbatim
if !chroot.append_to_file(outfile, chunk_data) {
return false;
}
} else if chunk_header.is_fill {
// Fill chunks are block_count blocks that contain a repeated sequence of data (typically 4-bytes repeated over and over again)
for _ in 0..chunk_header.block_count {
let mut i = 0;
let mut fill_block: Vec<u8> = vec![];
// Fill each block with the repeated data
while i < sparse_header.block_size {
fill_block.extend(chunk_data);
i += chunk_data.len();
}
// Append fill block to file
if !chroot.append_to_file(outfile, &fill_block) {
return false;
}
}
} else if chunk_header.is_dont_care {
let mut null_block: Vec<u8> = vec![];
// Build a block full of NULL bytes
while null_block.len() < sparse_header.block_size {
null_block.push(0);
}
// Write block_count NULL blocks to disk
for _ in 0..chunk_header.block_count {
if !chroot.append_to_file(outfile, &null_block) {
return false;
}
}
}
true
}
================================================
FILE: src/extractors/arcadyan.rs
================================================
use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};
use crate::extractors::lzma::lzma_decompress;
/// Defines the internal extractor for Arcadyn Obfuscated LZMA
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::arcadyan::obfuscated_lzma_extractor;
///
/// match obfuscated_lzma_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn obfuscated_lzma_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_obfuscated_lzma),
..Default::default()
}
}
/// Internal extractor for Arcadyn Obfuscated LZMA
pub fn extract_obfuscated_lzma(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const LZMA_DATA_OFFSET: usize = 4;
const MIN_DATA_SIZE: usize = 0x100;
const MAX_DATA_SIZE: usize = 0x1B0000;
let mut result = ExtractionResult {
..Default::default()
};
let available_data: usize = file_data.len() - offset;
// Sanity check data size
if available_data <= MAX_DATA_SIZE && available_data > MIN_DATA_SIZE {
// De-obfuscate the LZMA data
let deobfuscated_data = arcadyan_deobfuscator(&file_data[offset..]);
// Do a decompression on the LZMA data (actual LZMA data starts 4 bytes into the deobfuscated data)
result = lzma_decompress(&deobfuscated_data, LZMA_DATA_OFFSET, output_directory);
}
result
}
fn arcadyan_deobfuscator(obfuscated_data: &[u8]) -> Vec<u8> {
const BLOCK_SIZE: usize = 32;
const P1_START: usize = 0;
const P1_END: usize = 4;
const BLOCK1_START: usize = P1_END;
const BLOCK1_END: usize = BLOCK1_START + BLOCK_SIZE;
const P2_START: usize = BLOCK1_END;
const P2_END: usize = 0x68;
const BLOCK2_START: usize = P2_END;
const BLOCK2_END: usize = BLOCK2_START + BLOCK_SIZE;
const P3_START: usize = BLOCK2_END;
let mut deobfuscated_data: Vec<u8> = vec![];
// Get the "parts" and "blocks" of the obfuscated header
let p1 = obfuscated_data[P1_START..P1_END].to_vec();
let b1 = obfuscated_data[BLOCK1_START..BLOCK1_END].to_vec();
let p2 = obfuscated_data[P2_START..P2_END].to_vec();
let b2 = obfuscated_data[BLOCK2_START..BLOCK2_END].to_vec();
let p3 = obfuscated_data[P3_START..].to_vec();
// Swap "block1" and "block2"
deobfuscated_data.extend(p1);
deobfuscated_data.extend(b2);
deobfuscated_data.extend(p2);
deobfuscated_data.extend(b1);
deobfuscated_data.extend(p3);
// Nibble swap each byte in what is now "block1"
for swapped_byte in deobfuscated_data
.iter_mut()
.take(BLOCK1_END)
.skip(BLOCK1_START)
{
*swapped_byte = ((*swapped_byte & 0x0F) << 4) + ((*swapped_byte & 0xF0) >> 4);
}
let mut i: usize = BLOCK1_START;
// Byte swap each byte in what is now "block1"
while i < BLOCK1_END {
let b1 = deobfuscated_data[i];
let b2 = deobfuscated_data[i + 1];
deobfuscated_data[i] = b2;
deobfuscated_data[i + 1] = b1;
i += 2;
}
deobfuscated_data
}
================================================
FILE: src/extractors/autel.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::autel::parse_autel_header;
const BLOCK_SIZE: usize = 256;
/// Defines the internal extractor function for deobfuscating Autel firmware
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::autel::autel_extractor;
///
/// match autel_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn autel_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(autel_deobfuscate),
..Default::default()
}
}
/// Internal extractor for obfuscated Autel firmware
/// https://gist.github.com/sector7-nl/3fc815cd2497817ad461bfbd393294cb
pub fn autel_deobfuscate(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTPUT_FILE_NAME: &str = "autel.decoded";
let mut result = ExtractionResult {
..Default::default()
};
// Parse and validate the header
if let Ok(autel_header) = parse_autel_header(&file_data[offset..]) {
// Get the start and end offsets of the actual encoded data
let data_start = offset + autel_header.header_size;
let data_end = data_start + autel_header.data_size;
// Get the encoded data
if let Some(autel_data) = file_data.get(data_start..data_end) {
// Interate through each block of the encoded data
let mut block_iter = autel_data.chunks(BLOCK_SIZE);
loop {
match block_iter.next() {
None => {
// EOF
result.size = Some(autel_header.data_size);
result.success = true;
break;
}
Some(block_bytes) => {
// Decode the data block
let decoded_block = decode_autel_block(block_bytes);
// Write to file, if requested
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
if !chroot.append_to_file(OUTPUT_FILE_NAME, &decoded_block) {
break;
}
}
}
}
}
}
}
result
}
/// Block decoder for autel encoded firmware.
/// block_data *must* be 256 bytes in size, or less.
fn decode_autel_block(block_data: &[u8]) -> Vec<u8> {
// Lookup table for encoding/decoding bytes
let encoding_table: Vec<(usize, usize)> = vec![
(54, 147),
(96, 129),
(59, 193),
(191, 0),
(45, 130),
(96, 144),
(27, 129),
(152, 0),
(44, 180),
(118, 141),
(115, 129),
(210, 0),
(13, 164),
(27, 133),
(20, 192),
(139, 0),
(28, 166),
(17, 133),
(19, 193),
(224, 0),
(20, 161),
(145, 0),
(14, 193),
(12, 132),
(18, 161),
(17, 140),
(29, 192),
(246, 0),
(115, 178),
(28, 132),
(155, 0),
(12, 132),
(31, 165),
(20, 136),
(27, 193),
(142, 0),
(96, 164),
(18, 133),
(145, 0),
(23, 132),
(13, 165),
(13, 148),
(23, 193),
(19, 132),
(27, 178),
(83, 137),
(146, 0),
(145, 0),
(18, 166),
(96, 148),
(13, 193),
(159, 0),
(96, 166),
(20, 129),
(20, 193),
(27, 132),
(9, 160),
(96, 148),
(13, 192),
(159, 0),
(96, 180),
(142, 0),
(31, 193),
(155, 0),
(7, 166),
(224, 0),
(20, 192),
(27, 132),
(28, 160),
(17, 149),
(19, 193),
(96, 132),
(76, 164),
(208, 0),
(80, 192),
(78, 132),
(96, 160),
(27, 144),
(24, 193),
(140, 0),
(96, 178),
(17, 141),
(12, 193),
(224, 0),
(14, 161),
(17, 141),
(151, 0),
(14, 132),
(16, 165),
(96, 137),
(13, 193),
(155, 0),
(20, 161),
(29, 141),
(23, 192),
(24, 132),
(27, 178),
(10, 133),
(96, 192),
(140, 0),
(14, 180),
(17, 133),
(16, 192),
(144, 0),
(11, 163),
(13, 141),
(96, 192),
(17, 132),
(12, 178),
(96, 141),
(28, 192),
(27, 132),
(27, 130),
(18, 141),
(96, 193),
(31, 132),
(96, 181),
(13, 140),
(23, 193),
(224, 0),
(27, 166),
(142, 0),
(27, 192),
(24, 132),
(12, 183),
(96, 133),
(84, 192),
(14, 132),
(27, 178),
(10, 140),
(155, 0),
(9, 132),
(17, 160),
(56, 133),
(96, 192),
(82, 132),
(13, 160),
(27, 137),
(20, 193),
(139, 0),
(28, 161),
(145, 0),
(19, 192),
(118, 132),
(115, 165),
(20, 132),
(145, 0),
(14, 132),
(12, 167),
(146, 0),
(17, 193),
(29, 132),
(96, 176),
(28, 144),
(27, 193),
(140, 0),
(31, 180),
(148, 0),
(27, 192),
(14, 132),
(83, 160),
(18, 137),
(17, 193),
(23, 132),
(13, 165),
(13, 145),
(151, 0),
(147, 0),
(27, 178),
(96, 137),
(19, 193),
(159, 0),
(14, 160),
(25, 148),
(17, 193),
(142, 0),
(16, 180),
(27, 136),
(14, 193),
(224, 0),
(17, 178),
(12, 144),
(224, 0),
(28, 132),
(27, 160),
(13, 141),
(11, 193),
(96, 132),
(27, 165),
(30, 140),
(224, 0),
(146, 0),
(31, 165),
(29, 129),
(96, 192),
(140, 0),
(31, 161),
(24, 145),
(140, 0),
(96, 132),
(27, 165),
(29, 140),
(31, 192),
(154, 0),
(14, 161),
(27, 145),
(140, 0),
(18, 132),
(23, 167),
(96, 140),
(21, 129),
(14, 132),
(17, 165),
(9, 137),
(12, 193),
(155, 0),
(18, 161),
(96, 141),
(27, 192),
(148, 0),
(29, 178),
(23, 133),
(24, 192),
(155, 0),
(10, 180),
(96, 133),
(28, 192),
(14, 132),
(31, 130),
(28, 129),
(18, 193),
(31, 132),
(12, 180),
(13, 144),
(96, 193),
(31, 132),
(96, 160),
(13, 141),
(27, 193),
(18, 132),
(23, 181),
(26, 140),
(27, 193),
(156, 0),
(96, 166),
(79, 141),
(211, 0),
(76, 132),
(77, 160),
(75, 133),
(206, 0),
(182, 0),
(96, 129),
(59, 133),
(191, 0),
(173, 0),
];
assert!(block_data.len() <= BLOCK_SIZE);
let mut decoded_block: Vec<u8> = vec![];
for (i, byte) in block_data.iter().enumerate() {
let encoding = encoding_table[i];
decoded_block.push(((((*byte as usize) + encoding.0) ^ encoding.1) % 256) as u8);
}
decoded_block
}
================================================
FILE: src/extractors/bmp.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::bmp::{get_dib_header_size, parse_bmp_file_header};
/// Defines the internal extractor function for carving out GIF images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::bmp::bmp_extractor;
///
/// match bmp_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn bmp_extractor() -> Extractor {
Extractor {
do_not_recurse: true,
utility: ExtractorType::Internal(extract_bmp_image),
..Default::default()
}
}
pub fn extract_bmp_image(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "image.bmp";
let mut result = ExtractionResult {
..Default::default()
};
result.success = false;
// Parse the bmp_file_header
if let Ok(bmp_file_header) = parse_bmp_file_header(&file_data[offset..]) {
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader
// The size of the BMP file header
const BMP_FILE_HEADER_SIZE: usize = 14;
// Retrieve the size of the header following the BMP file header
if let Ok(bmp_header_size) =
get_dib_header_size(&file_data[(offset + BMP_FILE_HEADER_SIZE)..])
{
// The offset that points to the image data cannot point into the second header
if bmp_file_header.bitmap_bits_offset >= (BMP_FILE_HEADER_SIZE + bmp_header_size) {
// If it was parsed successfully, get the file size
result.size = Some(bmp_file_header.size);
result.success = true;
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success =
chroot.carve_file(OUTFILE_NAME, file_data, offset, bmp_file_header.size);
}
}
}
}
result
}
================================================
FILE: src/extractors/bzip2.rs
================================================
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use bzip2::{Decompress, Status};
/// Defines the internal extractor function for decompressing BZIP2 files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::bzip2::bzip2_extractor;
///
/// match bzip2_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn bzip2_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(bzip2_decompressor),
..Default::default()
}
}
/// Internal extractor for decompressing BZIP2 data
pub fn bzip2_decompressor(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
// Size of decompression buffer
const BLOCK_SIZE: usize = 900 * 1024;
// Output file for decompressed data
const OUTPUT_FILE_NAME: &str = "decompressed.bin";
let mut result = ExtractionResult {
..Default::default()
};
let mut bytes_written: usize = 0;
let mut stream_offset: usize = 0;
let bzip2_data = &file_data[offset..];
let mut decompressed_buffer = [0; BLOCK_SIZE];
let mut decompressor = Decompress::new(false);
let available_data = bzip2_data.len();
let mut previous_offset = None;
/*
* Loop through all compressed data and decompress it.
*
* This has a significant performance hit since 1) decompression takes time, and 2) data is
* decompressed once during signature validation and a second time during extraction (if extraction
* was requested).
*
* The advantage is that not only are we 100% sure that this data is valid BZIP2 data, but we
* can also determine the exact size of the BZIP2 data.
*/
while is_offset_safe(available_data, stream_offset, previous_offset) {
previous_offset = Some(stream_offset);
// Decompress a block of data
match decompressor.decompress(&bzip2_data[stream_offset..], &mut decompressed_buffer) {
Err(_) => {
// Break on decompression error
break;
}
Ok(status) => {
match status {
Status::RunOk => break,
Status::FlushOk => break,
Status::FinishOk => break,
Status::MemNeeded => break,
Status::Ok => {
stream_offset = decompressor.total_in() as usize;
}
Status::StreamEnd => {
result.success = true;
result.size = Some(decompressor.total_in() as usize);
}
}
// Decompressed a block of data, if extraction was requested write the decompressed block to the output file
if output_directory.is_some() {
let n: usize = (decompressor.total_out() as usize) - bytes_written;
let chroot = Chroot::new(output_directory);
if !chroot.append_to_file(OUTPUT_FILE_NAME, &decompressed_buffer[0..n]) {
// If writing data to file fails, break
break;
}
bytes_written += n;
}
// If everything has been processed successfully, we're done; break.
if result.success {
break;
}
}
}
}
result
}
================================================
FILE: src/extractors/cab.rs
================================================
use crate::extractors;
/// Describes how to run the cabextract utility to extract MS CAB archives
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::cab::cab_extractor;
///
/// match cab_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn cab_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("cabextract".to_string()),
extension: "cab".to_string(),
arguments: vec![extractors::common::SOURCE_FILE_PLACEHOLDER.to_string()],
exit_codes: vec![0],
..Default::default()
}
}
================================================
FILE: src/extractors/common.rs
================================================
use crate::signatures::common::SignatureResult;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::path;
use std::process;
use walkdir::WalkDir;
#[cfg(windows)]
use std::os::windows;
#[cfg(unix)]
use std::os::unix;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
/// This contstant in command line arguments will be replaced with the path to the input file
pub const SOURCE_FILE_PLACEHOLDER: &str = "%e";
/// Return value of InternalExtractor upon error
#[derive(Debug, Clone)]
pub struct ExtractionError;
/// Built-in internal extractors must provide a function conforming to this definition.
/// Arguments: file_data, offset, output_directory.
pub type InternalExtractor = fn(&[u8], usize, Option<&str>) -> ExtractionResult;
/// Enum to define either an Internal or External extractor type
#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum ExtractorType {
External(String),
Internal(InternalExtractor),
#[default]
None,
}
/// Describes extractors, both external and internal
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Extractor {
/// External command or internal function to execute
pub utility: ExtractorType,
/// File extension expected by an external command
pub extension: String,
/// Arguments to pass to the external command
pub arguments: Vec<String>,
/// A list of successful exit codes for the external command
pub exit_codes: Vec<i32>,
/// Set to true to disable recursion into this extractor's extracted files
pub do_not_recurse: bool,
}
/// Stores information about a completed extraction
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ExtractionResult {
/// Size of the data consumed during extraction, if known; should be populated by the constructor
pub size: Option<usize>,
/// Extractor success status; should be populated by the constructor
pub success: bool,
/// Extractor name, automatically populated by extractors::common::execute
pub extractor: String,
/// Set to true to disable recursion into this extractor's extracted files.
/// Automatically populated with the corresponding Extractor.do_not_recurse field by extractors::common::execute.
pub do_not_recurse: bool,
/// The output directory where the extractor dropped its files, automatically populated by extractors::common::execute
pub output_directory: String,
}
/// Stores information about external extractor processes. For internal use only.
#[derive(Debug)]
pub struct ProcInfo {
pub child: process::Child,
pub exit_codes: Vec<i32>,
pub carved_file: String,
}
/// Provides chroot-like functionality for internal extractors
#[derive(Debug, Default, Clone)]
pub struct Chroot {
/// The chroot directory passed to Chroot::new
pub chroot_directory: String,
}
impl Chroot {
/// Create a new chrooted instance. All file paths will be effectively chrooted in the specified directory path.
/// The chroot directory path will be created if it does not already exist.
///
/// If no directory path is specified, the chroot directory will be `/`.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new(&std::env::temp_dir())
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(&chroot.chroot_directory, &chroot_dir);
/// assert_eq!(std::path::Path::new(&chroot_dir).exists(), true);
/// # std::fs::remove_dir_all(&chroot_dir);
/// ```
pub fn new(chroot_directory: Option<&str>) -> Chroot {
let mut chroot_instance = Chroot {
..Default::default()
};
match chroot_directory {
None => {
// Default path is '/'
chroot_instance.chroot_directory = path::MAIN_SEPARATOR.to_string();
}
Some(chroot_dir) => {
// Attempt to ensure that the specified path is absolute. If this fails, just use the path as given.
match path::absolute(chroot_dir) {
Ok(pathbuf) => {
chroot_instance.chroot_directory = pathbuf.display().to_string();
}
Err(_) => {
chroot_instance.chroot_directory = chroot_dir.to_string();
}
}
}
}
// Create the chroot directory if it does not exist
if !path::Path::new(&chroot_instance.chroot_directory).exists() {
match fs::create_dir_all(&chroot_instance.chroot_directory) {
Ok(_) => {
debug!(
"Created new chroot directory {}",
chroot_instance.chroot_directory
);
}
Err(e) => {
error!(
"Failed to create chroot directory {}: {}",
chroot_instance.chroot_directory, e
);
}
}
}
chroot_instance
}
/// Joins two paths, ensuring that the final path does not traverse outside of the chroot directory.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
/// use std::path::MAIN_SEPARATOR;
///
/// let chroot_dir = std::path::Path::new(&std::env::temp_dir())
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// let dir_name = "etc";
/// let file_name = "passwd";
/// let abs_path = format!("{}{}{}{}", MAIN_SEPARATOR, dir_name, MAIN_SEPARATOR, file_name);
/// let abs_path_dir = format!("{}{}", MAIN_SEPARATOR, dir_name);
/// let rel_path_dir = format!("..{}..{}..{}{}", MAIN_SEPARATOR, MAIN_SEPARATOR, MAIN_SEPARATOR, dir_name);
/// let abs_path_file = format!("{}{}", MAIN_SEPARATOR, file_name);
/// let rel_path_file = format!("..{}..{}..{}{}", MAIN_SEPARATOR, MAIN_SEPARATOR, MAIN_SEPARATOR, file_name);
///
/// let path1 = chroot.safe_path_join(&abs_path_dir, file_name);
/// let expected_path1 = std::path::Path::new(&chroot_dir).join(dir_name).join(file_name);
///
/// let path2 = chroot.safe_path_join(&abs_path_dir, &rel_path_file);
/// let expected_path2 = std::path::Path::new(&chroot_dir).join(file_name);
///
/// let path3 = chroot.safe_path_join(&rel_path_dir, &abs_path_file);
/// let expected_path3 = std::path::Path::new(&chroot_dir).join(dir_name).join(file_name);
///
/// let path4 = chroot.safe_path_join(&chroot_dir, &abs_path);
/// let expected_path4 = std::path::Path::new(&chroot_dir).join(dir_name).join(file_name);
///
/// assert_eq!(path1, expected_path1.display().to_string());
/// assert_eq!(path2, expected_path2.display().to_string());
/// assert_eq!(path3, expected_path3.display().to_string());
/// assert_eq!(path4, expected_path4.display().to_string());
/// # std::fs::remove_dir_all(&chroot_dir);
/// ```
pub fn safe_path_join(&self, path1: impl Into<String>, path2: impl Into<String>) -> String {
// Join and sanitize both paths; retain the leading '/' (if there is one)
let mut joined_path: String = self.sanitize_path(
&format!("{}{}{}", path1.into(), path::MAIN_SEPARATOR, path2.into()),
true,
);
// If the joined path does not start with the chroot directory,
// prepend the chroot directory to the final joined path.
// on Windows: If no chroot directory is specified, skip the operation
if cfg!(windows) && self.chroot_directory == path::MAIN_SEPARATOR.to_string() {
// do nothing and skip
} else if !joined_path.starts_with(&self.chroot_directory) {
joined_path = format!(
"{}{}{}",
self.chroot_directory,
path::MAIN_SEPARATOR,
joined_path
);
}
self.strip_double_slash(&joined_path)
}
/// Given a file path, returns a sanitized path that is chrooted inside the specified chroot directory.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new(&std::env::temp_dir())
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let file_name = "test.txt";
///
/// let chroot = Chroot::new(Some(&chroot_dir));
/// let path = chroot.chrooted_path(file_name);
///
/// assert_eq!(path, std::path::Path::new(&chroot_dir).join(file_name).display().to_string());
/// ```
pub fn chrooted_path(&self, file_path: impl Into<String>) -> String {
self.safe_path_join(file_path, "".to_string())
}
/// Creates a regular file in the chrooted directory and writes the provided data to it.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_213_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let file_data: &[u8] = b"foobar";
///
/// let file_name = "created_file.txt";
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_file(file_name, file_data), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, std::str::from_utf8(file_data)?);
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_213_0(); }
/// ```
pub fn create_file(&self, file_path: impl Into<String>, file_data: &[u8]) -> bool {
let safe_file_path: String = self.chrooted_path(file_path);
if !path::Path::new(&safe_file_path).exists() {
match fs::write(safe_file_path.clone(), file_data) {
Ok(_) => {
return true;
}
Err(e) => {
error!("Failed to write data to {safe_file_path}: {e}");
}
}
} else {
error!("Failed to create file {safe_file_path}: path already exists");
}
false
}
/// Carve data and write it to a new file.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_255_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// const CARVE_SIZE: usize = 6;
///
/// let data: &[u8] = b"foobarJUNK";
///
/// let file_name = "carved_file.txt";
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.carve_file(file_name, data, 0, CARVE_SIZE), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, std::str::from_utf8(&data[0..CARVE_SIZE])?);
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// } _doctest_main_src_extractors_common_rs_255_0(); }
/// ```
pub fn carve_file(
&self,
file_path: impl Into<String>,
data: &[u8],
start: usize,
size: usize,
) -> bool {
let mut retval: bool = false;
if let Some(file_data) = data.get(start..start + size) {
retval = self.create_file(file_path, file_data);
} else {
error!(
"Failed to create file {}: data offset/size are invalid",
file_path.into()
);
}
retval
}
/// Creates a device file in the chroot directory.
///
/// Note that this does *not* create a real device file, just a regular file containing the device file info.
fn create_device(
&self,
file_path: impl Into<String>,
device_type: &str,
major: usize,
minor: usize,
) -> bool {
let device_file_contents: String = format!("{device_type} {major} {minor}");
self.create_file(file_path, &device_file_contents.clone().into_bytes())
}
/// Creates a character device file in the chroot directory.
///
/// Note that this does *not* create a real character device, just a regular file containing the text `c <major> <minor>`.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_312_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let dev_major: usize = 1;
/// let dev_minor: usize = 2;
/// let file_name = "char_device";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_character_device(file_name, dev_major, dev_minor), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, "c 1 2");
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_312_0(); }
/// ```
pub fn create_character_device(
&self,
file_path: impl Into<String>,
major: usize,
minor: usize,
) -> bool {
self.create_device(file_path, "c", major, minor)
}
/// Creates a block device file in the chroot directory.
///
/// Note that this does *not* create a real block device, just a regular file containing the text `b <major> <minor>`.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_345_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let dev_major: usize = 1;
/// let dev_minor: usize = 2;
/// let file_name = "block_device";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_block_device(file_name, dev_major, dev_minor), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, "b 1 2");
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_345_0(); }
/// ```
pub fn create_block_device(
&self,
file_path: impl Into<String>,
major: usize,
minor: usize,
) -> bool {
self.create_device(file_path, "b", major, minor)
}
/// Creates a fifo file in the chroot directory.
///
/// Note that this does *not* create a real fifo, just a regular file containing the text `fifo`.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_377_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let file_name = "fifo_file";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_fifo(file_name), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, "fifo");
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_377_0(); }
/// ```
pub fn create_fifo(&self, file_path: impl Into<String>) -> bool {
self.create_file(file_path, b"fifo")
}
/// Creates a socket file in the chroot directory.
///
/// Note that this does *not* create a real socket, just a regular file containing the text `socket`.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_401_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let file_name = "socket_file";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_socket(file_name), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, "socket");
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_401_0(); }
/// ```
pub fn create_socket(&self, file_path: impl Into<String>) -> bool {
self.create_file(file_path, b"socket")
}
/// Append the provided data to the specified file in the chroot directory.
///
/// If the specified file does not exist, it will be created.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_426_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let file_data: &[u8] = b"foobar";
/// let file_name = "append.txt";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.append_to_file(file_name, file_data), true);
/// assert_eq!(std::fs::read_to_string(std::path::Path::new(&chroot_dir).join(file_name))?, std::str::from_utf8(file_data)?);
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_426_0(); }
/// ```
pub fn append_to_file(&self, file_path: impl Into<String>, data: &[u8]) -> bool {
let safe_file_path: String = self.chrooted_path(file_path);
if !self.is_symlink(&safe_file_path) {
match fs::OpenOptions::new()
.create(true)
.append(true)
.open(safe_file_path.clone())
{
Err(e) => {
error!("Failed to open file '{safe_file_path}' for appending: {e}");
}
Ok(mut fp) => match fp.write(data) {
Err(e) => {
error!("Failed to append to file '{safe_file_path}': {e}");
}
Ok(_) => {
return true;
}
},
}
} else {
error!("Attempted to append data to a symlink: {safe_file_path}");
}
false
}
/// Creates a directory in the chroot directory.
///
/// Equivalent to mkdir -p.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let dir_name = "my_directory";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_directory(dir_name), true);
/// assert_eq!(std::path::Path::new(&chroot_dir).join(dir_name).exists(), true);
/// # std::fs::remove_dir_all(&chroot_dir);
/// ```
pub fn create_directory(&self, dir_path: impl Into<String>) -> bool {
let safe_dir_path: String = self.chrooted_path(dir_path);
match fs::create_dir_all(safe_dir_path.clone()) {
Ok(_) => {
return true;
}
Err(e) => {
error!("Failed to create output directory {safe_dir_path}: {e}");
}
}
false
}
/// Delete a directory in the chroot directory.
///
/// Equivalent to rm -rf.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let dir_name = "my_directory";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_directory(dir_name), true);
/// assert_eq!(chroot.remove_directory(dir_name), true);
/// assert_eq!(chroot.remove_directory("i_dont_exist"), true);
/// # std::fs::remove_dir_all(&chroot_dir);
/// ```
pub fn remove_directory(&self, dir_path: impl Into<String>) -> bool {
let safe_dir_path: String = self.chrooted_path(dir_path);
match fs::exists(safe_dir_path.clone()) {
Ok(dir_exists) => {
if !dir_exists {
return true;
}
}
Err(e) => {
error!("Failed to check if directory {safe_dir_path} exists: {e:?}");
return false;
}
}
match fs::remove_dir_all(safe_dir_path.clone()) {
Ok(_) => return true,
Err(e) => error!("Failed to delete directory {safe_dir_path}: {e}"),
}
false
}
/// Set executable permissions on an existing file in the chroot directory.
///
/// ## Example
///
/// ```
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let file_name = "runme.exe";
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
/// chroot.create_file(file_name, b"AAAA");
///
/// assert_eq!(chroot.make_executable(file_name), true);
/// # std::fs::remove_dir_all(&chroot_dir);
/// ```
#[allow(dead_code)]
pub fn make_executable(&self, file_path: impl Into<String>) -> bool {
// Make the file globally executable
const UNIX_EXEC_FLAG: u32 = 1;
let safe_file_path: String = self.chrooted_path(file_path);
match fs::metadata(safe_file_path.clone()) {
Err(e) => {
error!("Failed to get permissions for file {safe_file_path}: {e}");
}
Ok(_metadata) => {
#[cfg(unix)]
{
let mut permissions = _metadata.permissions();
let mode = permissions.mode() | UNIX_EXEC_FLAG;
permissions.set_mode(mode);
match fs::set_permissions(&safe_file_path, permissions) {
Err(e) => {
error!("Failed to set permissions for file {safe_file_path}: {e}");
}
Ok(_) => {
return true;
}
}
}
#[cfg(windows)]
{
return true;
}
}
}
false
}
/// Creates a symbolic link in the chroot directory, named `symlink_path`, which points to `target_path`.
///
/// Note that both the symlink and target paths will be sanitized to stay in the chroot directory.
/// Both the target path will be converted into a path relative to the symlink file path.
///
/// ## Example
///
/// ```
/// # fn main() { #[allow(non_snake_case)] fn _doctest_main_src_extractors_common_rs_571_0() -> Result<(), Box<dyn std::error::Error>> {
/// use binwalk::extractors::common::Chroot;
///
/// let chroot_dir = std::path::Path::new("tests")
/// .join("binwalk_unit_tests")
/// .display()
/// .to_string();
///
/// let symlink_name = "symlink";
/// let target_path = "target";
///
/// let expected_symlink_path = std::path::Path::new(&chroot_dir).join(symlink_name);
/// let expected_target_path = std::path::Path::new(&chroot_dir).join(target_path);
///
/// # std::fs::remove_dir_all(&chroot_dir);
/// let chroot = Chroot::new(Some(&chroot_dir));
///
/// assert_eq!(chroot.create_symlink(symlink_name, target_path), true);
/// assert_eq!(std::fs::canonicalize(expected_symlink_path)?.to_str(), expected_target_path.to_str());
/// # std::fs::remove_dir_all(&chroot_dir);
/// # Ok(())
/// # } _doctest_main_src_extractors_common_rs_571_0(); }
/// ```
pub fn create_symlink(
&self,
symlink_path: impl Into<String>,
target_path: impl Into<String>,
) -> bool {
let target = target_path.into();
let symlink = symlink_path.into();
// Chroot the symlink file path and create a Path object
let safe_symlink = self.chrooted_path(&symlink);
let safe_symlink_path = path::Path::new(&safe_symlink);
// Normalize the symlink target path to a chrooted absolute path
let safe_target = if target.starts_with(path::MAIN_SEPARATOR) {
// If the target path is absolute, just chroot it inside the chroot directory
self.chrooted_path(&target)
} else {
// Get the symlink file's parent directory path
let relative_dir: String = match safe_symlink_path.parent() {
None => {
// There is no parent, or parent is the root directory; assume the root directory
path::MAIN_SEPARATOR.to_string()
}
Some(parent_dir) => {
// Got the parent directory
parent_dir.display().to_string()
}
};
// Join the target path with its relative directory, ensuring it does not traverse outside
// the specified chroot directory
self.safe_path_join(&relative_dir, &target)
};
// Remove the chroot directory from the target and symlink paths.
// This results in each being an absolute path that is relative to the chroot directory,
// e.g., '/my_chroot_dir/bin/busybox' -> '/bin/busybox'.
//
// Note: need at least one leading '/', so if the chroot directory is just '/', just use the string as-is.
let mut safe_target_rel_path = if self.chroot_directory == path::MAIN_SEPARATOR.to_string()
{
safe_target.clone()
} else {
safe_target.replacen(&self.chroot_directory, "", 1)
};
let safe_symlink_rel_path = if self.chroot_directory == path::MAIN_SEPARATOR.to_string() {
safe_symlink.clone()
} else {
safe_symlink.replacen(&self.chroot_directory, "", 1)
};
// Count the number of path separators (minus the leading one) and an '../' to the target
// path for each; e.g., '/bin/busybox' -> '..//bin/busybox'.
for _i in 0..safe_symlink_rel_path.matches(path::MAIN_SEPARATOR).count() - 1 {
safe_target_rel_path = format!("..{}{}", path::MAIN_SEPARATOR, safe_target_rel_path);
}
// Add a '.' at the beginning of any paths that start with '/', e.g., '/tmp' -> './tmp'.
if safe_target_rel_path.starts_with(path::MAIN_SEPARATOR) {
safe_target_rel_path = format!(".{safe_target_rel_path}");
}
// Replace any instances of '//' with '/'
safe_target_rel_path = self.strip_double_slash(&safe_target_rel_path);
// The target path is now a safely chrooted path that is relative to the symlink file path.
// Ex:
//
// Original symlink: "/my_chroot_dir/usr/sbin/ls" is a symlink to "/bin/busybox"
// Safe relative symlink: "/my_chroot_dir/usr/sbin/ls" is a symlink to "./../../bin/busybox"
let safe_target_path = path::Path::new(&safe_target_rel_path);
#[cfg(unix)]
{
match unix::fs::symlink(safe_target_path, safe_symlink_path) {
Ok(_) => true,
Err(e) => {
error!("Failed to create symlink from {symlink} -> {target}: {e}");
false
}
}
}
#[cfg(windows)]
{
// let sym = match safe_target_path.is_dir() {
// true => windows::fs::symlink_dir(safe_target_path, safe_symlink_path),
// false => windows::fs::symlink_file(safe_target_path, safe_symlink_path),
// };
match windows::fs::symlink_dir(safe_target_path, safe_symlink_path) {
Ok(_) => {
return true;
}
Err(e) => {
error!(
"Failed to create symlink from {} -> {}: {}",
symlink, target, e
);
return false;
}
}
}
}
/// Returns true if the file path is a symlink.
fn is_symlink(&self, file_path: &str) -> bool {
if let Ok(metadata) = fs::symlink_metadata(file_path) {
return metadata.file_type().is_symlink();
}
false
}
/// Replace `//` with `/`. This is for asthetics only.
fn strip_double_slash(&self, path: &str) -> String {
let mut stripped_path = path.to_owned();
let single_slash = path::MAIN_SEPARATOR.to_string();
let double_slash = format!("{single_slash}{single_slash}");
while stripped_path.contains(&double_slash) {
stripped_path = stripped_path.replace(&double_slash, &single_slash);
}
stripped_path
}
/// Interprets a given path containing '..' directories.
fn sanitize_path(&self, file_path: &str, preserve_root_path_sep: bool) -> String {
const DIR_TRAVERSAL: &str = "..";
let mut exclude_indicies: Vec<usize> = vec![];
let mut sanitized_path: String = "".to_string();
if preserve_root_path_sep && file_path.starts_with(path::MAIN_SEPARATOR) {
sanitized_path = path::MAIN_SEPARATOR.to_string();
}
// Split the file path on '/'
let path_parts: Vec<&str> = file_path.split(path::MAIN_SEPARATOR).collect();
// Loop through each part of the file path
for (i, path_part) in path_parts.iter().enumerate() {
// If this part of the path is '..', don't include it in the final sanitized path
if *path_part == DIR_TRAVERSAL {
exclude_indicies.push(i);
if i > 0 {
// Walk backwards through the path parts until a non-excluded part is found, then mark that part for exclusion as well
let mut j = i - 1;
while j > 0 && exclude_indicies.contains(&j) {
j -= 1;
}
exclude_indicies.push(j);
}
// If this part of the path is an empty string, don't include that either (happens if the original file path has '//' in it)
} else if path_part.is_empty() {
exclude_indicies.push(i);
}
}
// Concatenate each non-excluded part of the file path, with each part separated by '/'
for (i, path_part) in path_parts.iter().enumerate() {
if !exclude_indicies.contains(&i) {
#[cfg(windows)]
{
// on Windows: in the first loop run, we cannot really prepend a '\' to drive letters like 'C:'
if sanitized_path.is_empty() {
sanitized_path = path_part.to_string();
continue;
}
}
sanitized_path = format!("{}{}{}", sanitized_path, path::MAIN_SEPARATOR, path_part);
}
}
self.strip_double_slash(&sanitized_path)
}
}
/// Recursively walks a given directory and returns a list of regular non-zero size files in the given directory path.
#[allow(dead_code)]
pub fn get_extracted_files(directory: &str) -> Vec<String> {
let mut regular_files: Vec<String> = vec![];
for entry in WalkDir::new(directory).into_iter() {
match entry {
Err(_e) => continue,
Ok(entry) => {
let entry_path = entry.path();
// Query file metadata *without* following symlinks
match fs::symlink_metadata(entry_path) {
Err(_e) => continue,
Ok(md) => {
// Only interested in non-empty, regular files
if md.is_file() && md.len() > 0 {
regular_files.push(entry_path.display().to_string());
}
}
}
}
}
}
regular_files
}
/// Executes an extractor for the provided SignatureResult.
pub fn execute(
file_data: &[u8],
file_path: &str,
signature: &SignatureResult,
extractor: &Option<Extractor>,
) -> ExtractionResult {
let mut result = ExtractionResult {
..Default::default()
};
// Create an output directory for the extraction
if let Ok(output_directory) = create_output_directory(file_path, signature.offset) {
// Make sure a defalut extractor was actually defined (this function should not be called if signature.extractor is None)
match &extractor {
None => {
error!(
"Attempted to extract {} data, but no extractor is defined!",
signature.name
);
}
Some(default_extractor) => {
let extractor_definition: Extractor;
// If the signature result specified a preferred extractor, use that instead of the default signature extractor
if let Some(preferred_extractor) = &signature.preferred_extractor {
extractor_definition = preferred_extractor.clone();
} else {
extractor_definition = default_extractor.clone();
}
// Decide how to execute the extractor depending on the extractor type
match &extractor_definition.utility {
ExtractorType::None => {
error!(
"Signature {}: an extractor of type None is invalid!",
signature.name
);
}
ExtractorType::Internal(func) => {
debug!("Executing internal {} extractor", signature.name);
// Run the internal extractor function
result = func(file_data, signature.offset, Some(&output_directory));
// Set the extractor name to "<signature name>_built_in"
result.extractor = format!("{}_built_in", signature.name);
}
ExtractorType::External(cmd) => {
// Spawn the external extractor command
match spawn(
file_data,
file_path,
&output_directory,
signature,
extractor_definition.clone(),
) {
Err(e) => {
error!(
"Failed to spawn external extractor for '{}' signature: {}",
signature.name, e
);
}
Ok(proc_info) => {
// Wait for the external process to exit
match proc_wait(proc_info) {
Err(_) => {
warn!("External extractor failed!");
}
Ok(ext_result) => {
result = ext_result;
// Set the extractor name to the name of the extraction utility
result.extractor = cmd.to_string();
}
}
}
}
}
}
// Populate these ExtractionResult fields automatically for all extractors
result.output_directory = output_directory.clone();
result.do_not_recurse = extractor_definition.do_not_recurse;
// If the extractor reported success, make sure it extracted something other than just an empty file
if result.success && !was_something_extracted(&result.output_directory) {
result.success = false;
warn!("Extractor exited successfully, but no data was extracted");
}
}
}
// Clean up extractor's output directory if extraction failed
if !result.success {
if let Err(e) = fs::remove_dir_all(&output_directory) {
warn!(
"Failed to clean up extraction directory {output_directory} after extraction failure: {e}"
);
}
}
}
result
}
/// Spawn an external extractor process.
fn spawn(
file_data: &[u8],
file_path: &str,
output_directory: &str,
signature: &SignatureResult,
mut extractor: Extractor,
) -> Result<ProcInfo, std::io::Error> {
let chroot = Chroot::new(None);
// This function *only* handles execution of external extraction utilities; internal extractors must be invoked directly
let command = match &extractor.utility {
ExtractorType::External(cmd) => cmd.clone(),
ExtractorType::Internal(_ext) => {
error!("Tried to run an internal extractor as an external command!");
return Err(std::io::Error::other(
"attempt to execute an internal extractor as an external command",
));
}
ExtractorType::None => {
error!("An extractor command was defined, but is set to None!");
return Err(std::io::Error::other(
"invalid external command of type None",
));
}
};
// Carved file path will be <output directory>/<signature.name>_<hex offset>.<extractor.extension>
let carved_file = format!(
"{}{}{}_{:X}.{}",
output_directory,
path::MAIN_SEPARATOR,
signature.name,
signature.offset,
extractor.extension
);
info!(
"Carving data from {} {:#X}..{:#X} to {}",
file_path,
signature.offset,
signature.offset + signature.size,
carved_file
);
// If the entirety of the source file is this one file type, no need to carve a copy of it, just create a symlink
if signature.offset == 0 && signature.size == file_data.len() {
if !chroot.create_symlink(&carved_file, file_path) {
return Err(std::io::Error::other(
"Failed to create carved file symlink",
));
}
} else {
// Copy file data to carved file path
if !chroot.carve_file(&carved_file, file_data, signature.offset, signature.size) {
return Err(std::io::Error::other("Failed to carve data to disk"));
}
}
// Replace all "%e" command arguments with the path to the carved file
for i in 0..extractor.arguments.len() {
if extractor.arguments[i] == SOURCE_FILE_PLACEHOLDER {
extractor.arguments[i] = carved_file.clone();
}
}
info!("Spawning process {} {:?}", command, extractor.arguments);
match process::Command::new(&command)
.args(&extractor.arguments)
.stdout(process::Stdio::null())
.stderr(process::Stdio::null())
.current_dir(output_directory)
.spawn()
{
Err(e) => {
error!(
"Failed to execute command {}{:?}: {}",
command, extractor.arguments, e
);
Err(e)
}
Ok(child) => {
// If the process was spawned successfully, return some information about the process
let proc_info = ProcInfo {
child,
carved_file: carved_file.clone(),
exit_codes: extractor.exit_codes,
};
Ok(proc_info)
}
}
}
/// Waits for an extraction process to complete.
/// Returns ExtractionError if the extractor was prematurely terminated, else returns an ExtractionResult.
fn proc_wait(mut worker_info: ProcInfo) -> Result<ExtractionResult, ExtractionError> {
// The standard exit success value is 0
const EXIT_SUCCESS: i32 = 0;
// Block until child process has terminated
match worker_info.child.wait() {
// Child was terminated from an external signal, status unknown, assume failure but do nothing else
Err(e) => {
error!("Failed to retreive child process status: {e}");
Err(ExtractionError)
}
// Child terminated with an exit status
Ok(status) => {
// Assume failure until proven otherwise
let mut extraction_success: bool = false;
// Clean up the carved file used as input to the extractor
debug!("Deleting carved file {}", worker_info.carved_file);
if let Err(e) = fs::remove_file(worker_info.carved_file.clone()) {
warn!(
"Failed to remove carved file '{}': {}",
worker_info.carved_file, e
);
};
// Check the extractor's exit status
match status.code() {
None => {
extraction_success = false;
}
Some(code) => {
// Make sure the extractor's exit code is an expected one
if code == EXIT_SUCCESS || worker_info.exit_codes.contains(&code) {
extraction_success = true;
} else {
warn!("Child process exited with unexpected code: {code}");
}
}
}
// Return an ExtractionResult with the appropriate success status
Ok(ExtractionResult {
success: extraction_success,
..Default::default()
})
}
}
}
// Create an output directory in which to place extraction results
fn create_output_directory(file_path: &str, offset: usize) -> Result<String, std::io::Error> {
let chroot = Chroot::new(None);
// Output directory will be: <file_path.extracted/<hex offset>
let output_directory = format!(
"{}.extracted{}{:X}",
file_path,
path::MAIN_SEPARATOR,
offset
);
// First, remove the output directory if it exists from a previous run
if !chroot.remove_directory(&output_directory) {
return Err(std::io::Error::other("Directory deletion failed"));
}
// Create the output directory, equivalent of mkdir -p
if !chroot.create_directory(&output_directory) {
return Err(std::io::Error::other("Directory creation failed"));
}
Ok(output_directory)
}
/// Returns true if the size of the provided extractor output directory is greater than zero.
/// Note that any intermediate/carved files must be deleted *before* calling this function.
fn was_something_extracted(output_directory: &str) -> bool {
let output_directory_path = path::Path::new(output_directory);
debug!("Checking output directory {output_directory} for results");
// Walk the output directory looking for something, anything, that isn't an empty file
for entry in WalkDir::new(output_directory).into_iter() {
match entry {
Err(e) => {
warn!("Failed to retrieve output directory entry: {e}");
continue;
}
Ok(entry) => {
// Don't include the base output directory path itself
if entry.path() == output_directory_path {
continue;
}
debug!("Found output file {}", entry.path().display());
match fs::symlink_metadata(entry.path()) {
Err(_e) => continue,
Ok(md) => {
if md.len() > 0 {
return true;
}
}
}
}
}
}
false
}
================================================
FILE: src/extractors/csman.rs
================================================
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::csman::{CSManEntry, parse_csman_entry, parse_csman_header};
use miniz_oxide::inflate;
use std::collections::HashMap;
/// Defines the internal extractor function for CSMan DAT files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::csman::csman_extractor;
///
/// match csman_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn csman_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_csman_dat),
..Default::default()
}
}
/// Validate and extract CSMan DAT file entries
pub fn extract_csman_dat(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const COMPRESSED_HEADER_SIZE: usize = 2;
// Return value
let mut result = ExtractionResult {
..Default::default()
};
let mut csman_entries: Vec<CSManEntry> = Vec::new();
// Parse the CSMAN header
if let Ok(csman_header) = parse_csman_header(&file_data[offset..]) {
// Calulate the start and end offsets of the CSMAN entries
let entries_start: usize = offset + csman_header.header_size;
let entries_end: usize = entries_start + csman_header.data_size;
// Get the CSMAN entry data
if let Some(raw_entry_data) = file_data.get(entries_start..entries_end) {
let mut entry_data = raw_entry_data.to_vec();
// If the entries are compressed, decompress it (zlib compression)
if csman_header.compressed {
if let Some(compressed_data) = raw_entry_data.get(COMPRESSED_HEADER_SIZE..) {
match inflate::decompress_to_vec(compressed_data) {
Err(_) => {
return result;
}
Ok(decompressed_data) => {
entry_data = decompressed_data.clone();
}
}
}
}
// Offsets for processing CSMAN entries in entry_data
let mut next_offset: usize = 0;
let mut previous_offset = None;
let available_data: usize = entry_data.len();
// Loop while there is still data that can be safely parsed
while is_offset_safe(available_data, next_offset, previous_offset) {
// Get the next entry's data
match entry_data.get(next_offset..) {
None => {
break;
}
Some(next_entry_data) => {
// Parse the next entry
match parse_csman_entry(next_entry_data, &csman_header.endianness) {
Err(_) => {
break;
}
Ok(entry) => {
if entry.eof {
// Last entry should be an EOF marker; an EOF marker should always exist.
// There should be at least one valid entry.
result.success = !csman_entries.is_empty();
break;
} else {
// Append this entry to the list of entries and update the offsets to process the next entry
csman_entries.push(entry.clone());
previous_offset = Some(next_offset);
next_offset += entry.size;
}
}
}
}
}
}
// If all entries were processed successfully
if result.success {
// Update the reported size of data processed
result.size = Some(csman_header.header_size + csman_header.data_size);
// If extraction was requested, extract each entry using the entry key as the file name
if output_directory.is_some() {
// All files will be written inside the provided output directory
let chroot = Chroot::new(output_directory);
// There may be more than one entry with the same key; track the key and how many times it was encountered
let mut processed_entries: HashMap<usize, usize> = HashMap::new();
// Loop through all entries
for entry in csman_entries {
// File name is [key value, in ASCII hex].dat
let mut file_name = format!("{:08X}.dat", entry.key);
// If this key value has already been extracted, file name is [key value, in ASCII hex].dat_[count]
if processed_entries.contains_key(&entry.key) {
file_name = format!("{}_{}", file_name, processed_entries[&entry.key]);
processed_entries.insert(entry.key, processed_entries[&entry.key] + 1);
} else {
processed_entries.insert(entry.key, 1);
}
if !chroot.create_file(&file_name, &entry.value) {
result.success = false;
break;
}
}
}
}
}
}
result
}
================================================
FILE: src/extractors/dahua_zip.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::signatures::zip::find_zip_eof;
/// Defines the internal extractor function for carving Dahua ZIP files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dahua_zip::dahua_zip_extractor;
///
/// match dahua_zip_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn dahua_zip_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_dahua_zip),
..Default::default()
}
}
/// Carves out a Dahua ZIP file and converts it to a normal ZIP file
pub fn extract_dahua_zip(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "dahua.zip";
const ZIP_HEADER: &[u8] = b"PK";
let mut result = ExtractionResult {
..Default::default()
};
// Locate the end of the zip archive
if let Ok(zip_info) = find_zip_eof(file_data, offset) {
// Calculate total size of the zip archive, report success
result.size = Some(zip_info.eof - offset);
result.success = true;
// If extraction was requested, carve the zip archive to disk, replacing the Dahua ZIP magic bytes
// with the standard ZIP magic bytes.
if output_directory.is_some() {
// Start and end offsets of the data to carve
let start_data = offset + ZIP_HEADER.len();
let end_data = offset + result.size.unwrap();
let chroot = Chroot::new(output_directory);
// Get the data to carve
match file_data.get(start_data..end_data) {
None => {
result.success = false;
}
Some(zip_data) => {
// First write the normal ZIP header magic bytes to disk
if !chroot.create_file(OUTFILE_NAME, ZIP_HEADER) {
result.success = false;
} else {
// Append the rest of the ZIP archive to disk
result.success = chroot.append_to_file(OUTFILE_NAME, zip_data);
}
}
}
}
}
result
}
================================================
FILE: src/extractors/dmg.rs
================================================
use crate::extractors;
/// Describes how to run the dmg2img utility to convert DMG images to MBR
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dmg::dmg_extractor;
///
/// match dmg_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn dmg_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("dmg2img".to_string()),
extension: "dmg".to_string(),
arguments: vec![
"-i".to_string(), // Input file
extractors::common::SOURCE_FILE_PLACEHOLDER.to_string(),
"-o".to_string(), // Output file
"mbr.img".to_string(),
],
exit_codes: vec![0, 1],
..Default::default()
}
}
================================================
FILE: src/extractors/dtb.rs
================================================
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::dtb::{parse_dtb_header, parse_dtb_node};
use log::error;
/// Defines the internal extractor function for extracting Device Tree Blobs
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dtb::dtb_extractor;
///
/// match dtb_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn dtb_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_dtb),
..Default::default()
}
}
/// Internal extractor for extracting Device Tree Blobs
pub fn extract_dtb(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
let mut heirerarchy: Vec<String> = Vec::new();
let mut result = ExtractionResult {
..Default::default()
};
// Parse the DTB file header
if let Ok(dtb_header) = parse_dtb_header(&file_data[offset..]) {
// Get all the DTB data
if let Some(dtb_data) = file_data.get(offset..offset + dtb_header.total_size) {
// DTB node entries start at the structure offset specified in the DTB header
let mut entry_offset = dtb_header.struct_offset;
let mut previous_entry_offset = None;
let available_data = dtb_data.len();
// Loop over all DTB node entries
while is_offset_safe(available_data, entry_offset, previous_entry_offset) {
// Parse the next DTB node entry
let node = parse_dtb_node(&dtb_header, dtb_data, entry_offset);
// Beginning of a node, add it to the heirerarchy list
if node.begin {
if !node.name.is_empty() {
heirerarchy.push(node.name.clone());
}
// End of a node, remove it from the heirerarchy list
} else if node.end {
if !heirerarchy.is_empty() {
heirerarchy.pop();
}
// End of the DTB structure, return success only if the whole DTB structure was parsed successfully up to the EOF marker
} else if node.eof {
result.success = true;
result.size = Some(available_data);
break;
// DTB property, extract it to disk
} else if node.property {
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
let dir_path = heirerarchy.join(std::path::MAIN_SEPARATOR_STR);
let file_path = chroot.safe_path_join(&dir_path, &node.name);
if !chroot.create_directory(dir_path) {
break;
}
if !chroot.create_file(file_path, &node.data) {
break;
}
}
// The only other supported node type is NOP
} else if !node.nop {
error!("Unknown or invalid DTB node");
break;
}
// Update offsets to parse the next DTB structure entry
previous_entry_offset = Some(entry_offset);
entry_offset += node.total_size;
}
}
}
result
}
================================================
FILE: src/extractors/dumpifs.rs
================================================
use crate::extractors;
/// Describes how to run the dumpifs utility to extract QNX IFS images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dumpifs::dumpifs_extractor;
///
/// match dumpifs_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn dumpifs_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("dumpifs".to_string()),
extension: "ifs".to_string(),
arguments: vec![
"-x".to_string(), // Extract the image
extractors::common::SOURCE_FILE_PLACEHOLDER.to_string(),
],
exit_codes: vec![0],
..Default::default()
}
}
================================================
FILE: src/extractors/dxbc.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::dxbc::parse_dxbc_header;
/// Defines the internal extractor function for carving out DXBC images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::dxbc::dxbc_extractor;
///
/// match dxbc_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn dxbc_extractor() -> Extractor {
Extractor {
do_not_recurse: true,
utility: ExtractorType::Internal(extract_dxbc_file),
..Default::default()
}
}
pub fn extract_dxbc_file(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "shader.dxbc";
let mut result = ExtractionResult {
..Default::default()
};
if let Ok(header) = parse_dxbc_header(&file_data[offset..]) {
// Report success
result.size = Some(header.size);
result.success = true;
// Do extraction, if requested
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success =
chroot.carve_file(OUTFILE_NAME, file_data, offset, result.size.unwrap());
}
}
result
}
================================================
FILE: src/extractors/encfw.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
/// Defines the internal extractor function for decrypting known encrypted firmware
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::encfw::encfw_extractor;
///
/// match encfw_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn encfw_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(encfw_decrypt),
..Default::default()
}
}
/// Attempts to decrypt known encrypted firmware images
pub fn encfw_decrypt(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTPUT_FILE_NAME: &str = "decrypted.bin";
let mut result = ExtractionResult {
..Default::default()
};
if let Ok(decrypted_data) = delink::decrypt(&file_data[offset..]) {
result.success = true;
// Write to file, if requested
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success = chroot.create_file(OUTPUT_FILE_NAME, &decrypted_data);
}
}
result
}
================================================
FILE: src/extractors/gif.rs
================================================
use crate::common::is_offset_safe;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::common::StructureError;
use crate::structures::gif::{parse_gif_extension, parse_gif_header, parse_gif_image_descriptor};
/// Defines the internal extractor function for carving out GIF images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::gif::gif_extractor;
///
/// match gif_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn gif_extractor() -> Extractor {
Extractor {
do_not_recurse: true,
utility: ExtractorType::Internal(extract_gif_image),
..Default::default()
}
}
/// Parses and carves a GIF image from a file
pub fn extract_gif_image(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "image.gif";
let mut result = ExtractionResult {
..Default::default()
};
// Parse the GIF header
if let Ok(gif_header) = parse_gif_header(&file_data[offset..]) {
// GIF data follows the gif header
if let Some(gif_image_data) = file_data.get(offset + gif_header.size..) {
// Determine the size of the GIF image data
if let Some(gif_data_size) = get_gif_data_size(gif_image_data) {
// Report success
result.size = Some(gif_header.size + gif_data_size);
result.success = true;
// Do extraction, if requested
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success =
chroot.carve_file(OUTFILE_NAME, file_data, offset, result.size.unwrap());
}
}
}
}
result
}
/// Returns the size of the GIF data that follows the GIF header
fn get_gif_data_size(gif_data: &[u8]) -> Option<usize> {
// GIF block types
const EXTENSION: u8 = 0x21;
const TERMINATOR: u8 = 0x3B;
const IMAGE_DESCRIPTOR: u8 = 0x2C;
let mut next_offset: usize = 0;
let mut previous_offset = None;
let available_data = gif_data.len();
// Loop through all GIF data blocks
while is_offset_safe(available_data, next_offset, previous_offset) {
let block_size: Result<usize, StructureError>;
// Get the block type of the next block
match gif_data.get(next_offset) {
None => break,
Some(block_type) => {
// Parse the block type accordingly
if *block_type == IMAGE_DESCRIPTOR {
block_size = parse_gif_image_descriptor(&gif_data[next_offset..]);
} else if *block_type == EXTENSION {
block_size = parse_gif_extension(&gif_data[next_offset..]);
} else if *block_type == TERMINATOR {
// Only return the GIF size if we've found a termination block.
// The +1 is for the size of the block_type u8.
return Some(next_offset + 1);
} else {
break;
}
}
}
// Check if the block was parsed successfully
match block_size {
Err(_) => break,
Ok(this_block_size) => {
// Everything looks OK, go to the next block
previous_offset = Some(next_offset);
next_offset += this_block_size;
}
}
}
// Something went wrong, failure
None
}
================================================
FILE: src/extractors/gpg.rs
================================================
use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};
use crate::extractors::inflate;
/// Defines the internal extractor function for decompressing signed GPG data
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::gpg::gpg_extractor;
///
/// match gpg_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn gpg_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(gpg_decompress),
..Default::default()
}
}
/// Internal extractor for decompressing signed GPG data
pub fn gpg_decompress(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
// Size of the GPG header
const HEADER_SIZE: usize = 2;
let mut exresult = ExtractionResult {
..Default::default()
};
// Do the decompression, ignoring the GPG header
let inflate_result =
inflate::inflate_decompressor(file_data, offset + HEADER_SIZE, output_directory);
// Check that the data decompressed OK
if inflate_result.success {
exresult.success = true;
exresult.size = Some(HEADER_SIZE + inflate_result.size);
}
exresult
}
================================================
FILE: src/extractors/gzip.rs
================================================
use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};
use crate::extractors::inflate;
use crate::structures::gzip::parse_gzip_header;
/// Defines the internal extractor function for decompressing gzip data
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::gzip::gzip_extractor;
///
/// match gzip_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn gzip_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(gzip_decompress),
..Default::default()
}
}
/// Internal extractor for gzip compressed data
pub fn gzip_decompress(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
let mut exresult = ExtractionResult {
..Default::default()
};
// Parse the gzip header
if let Ok(gzip_header) = parse_gzip_header(&file_data[offset..]) {
// Deflate compressed data starts at the end of the gzip header
let deflate_data_start: usize = offset + gzip_header.size;
if file_data.len() > deflate_data_start {
let inflate_result =
inflate::inflate_decompressor(file_data, deflate_data_start, output_directory);
if inflate_result.success {
exresult.success = true;
exresult.size = Some(inflate_result.size);
}
}
}
exresult
}
================================================
FILE: src/extractors/inflate.rs
================================================
use crate::extractors::common::Chroot;
use adler32::RollingAdler32;
use flate2::bufread::DeflateDecoder;
use std::io::Read;
#[derive(Debug, Default, Clone)]
pub struct DeflateResult {
pub size: usize,
pub adler32: u32,
pub success: bool,
}
/// Decompressor for inflating deflated data.
/// For internal use, does not conform to the standard extractor format.
pub fn inflate_decompressor(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> DeflateResult {
// Size of decompression buffer
const BLOCK_SIZE: usize = 8192;
// Output file for decompressed data
const OUTPUT_FILE_NAME: &str = "decompressed.bin";
let mut result = DeflateResult {
..Default::default()
};
let mut adler32_checksum = RollingAdler32::new();
let mut decompressed_buffer = [0; BLOCK_SIZE];
let mut decompressor = DeflateDecoder::new(&file_data[offset..]);
/*
* Loop through all compressed data and decompress it.
*
* This has a significant performance hit since 1) decompression takes time, and 2) data is
* decompressed once during signature validation and a second time during extraction (if extraction
* was requested).
*
* The advantage is that not only are we 100% sure that this data is a valid deflate stream, but we
* can also determine the exact size of the deflated data.
*/
loop {
// Decompress a block of data
match decompressor.read(&mut decompressed_buffer) {
Err(_) => {
// Break on decompression error
break;
}
Ok(n) => {
// Decompressed a block of data, update checksum and if extraction was requested write the decompressed block to the output file
if n > 0 {
adler32_checksum.update_buffer(&decompressed_buffer[0..n]);
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
if !chroot.append_to_file(OUTPUT_FILE_NAME, &decompressed_buffer[0..n]) {
// If writing data to file fails, break
break;
}
}
}
// No data was read, end of compression stream
if n == 0 {
// If some data was actually decompressed, report success and the number of input bytes consumed
if decompressor.total_out() > 0 {
result.success = true;
result.adler32 = adler32_checksum.hash();
result.size = decompressor.total_in() as usize;
}
// Nothing else to do, break
break;
}
}
}
}
result
}
================================================
FILE: src/extractors/iso9660.rs
================================================
use crate::extractors;
use crate::extractors::sevenzip::sevenzip_extractor;
/// Describes how to run the 7z utility to extract ISO images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::iso9660::iso9660_extractor;
///
/// match iso9660_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn iso9660_extractor() -> extractors::common::Extractor {
// Same as the normal 7z extractor, but give the carved file an ISO file extension.
// The file extension matters, and 7z doesn't handle some ISO sub-formats correctly if the file extension is not '.iso'.
let mut extractor = sevenzip_extractor();
extractor.extension = "iso".to_string();
extractor
}
================================================
FILE: src/extractors/jboot.rs
================================================
use crate::common::crc32;
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
use crate::structures::jboot::parse_jboot_sch2_header;
/// Defines the internal extractor function for carving out JBOOT SCH2 kernels
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::jboot::sch2_extractor;
///
/// match sch2_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn sch2_extractor() -> Extractor {
Extractor {
utility: ExtractorType::Internal(extract_jboot_sch2_kernel),
..Default::default()
}
}
/// Extract the kernel described by a JBOOT SCH2 header
pub fn extract_jboot_sch2_kernel(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
// Output file name
const OUTFILE_NAME: &str = "kernel.bin";
let mut result = ExtractionResult {
..Default::default()
};
// Get the SCH2 data
if let Some(sch2_header_data) = file_data.get(offset..) {
// Parse the SCH2 header
if let Ok(sch2_header) = parse_jboot_sch2_header(sch2_header_data) {
let kernel_start: usize = offset + sch2_header.header_size;
let kernel_end: usize = kernel_start + sch2_header.kernel_size;
// Validate the kernel data checksum
if let Some(kernel_data) = file_data.get(kernel_start..kernel_end) {
if crc32(kernel_data) == (sch2_header.kernel_checksum as u32) {
// Everything checks out ok
result.size = Some(sch2_header.header_size + sch2_header.kernel_size);
result.success = true;
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success = chroot.carve_file(
OUTFILE_NAME,
file_data,
kernel_start,
sch2_header.kernel_size,
);
}
}
}
}
}
result
}
================================================
FILE: src/extractors/jffs2.rs
================================================
use crate::extractors;
/// Describes how to run the jefferson utility to extract JFFS file systems
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::jffs2::jffs2_extractor;
///
/// match jffs2_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn jffs2_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
utility: extractors::common::ExtractorType::External("jefferson".to_string()),
extension: "img".to_string(),
arguments: vec![
"-f".to_string(), // Force overwrite if output file, for some reason, exists
"-d".to_string(), // Output to jffs2-root directory
"jffs2-root".to_string(),
extractors::common::SOURCE_FILE_PLACEHOLDER.to_string(),
],
exit_codes: vec![0, 1, 2],
..Default::default()
}
}
================================================
FILE: src/extractors/jpeg.rs
================================================
use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};
/// Defines the internal extractor function for carving out JPEG images
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::jpeg::jpeg_extractor;
///
/// match jpeg_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn jpeg_extractor() -> Extractor {
Extractor {
do_not_recurse: true,
utility: ExtractorType::Internal(extract_jpeg_image),
..Default::default()
}
}
/// Internal extractor for carving JPEG images to disk
pub fn extract_jpeg_image(
file_data: &[u8],
offset: usize,
output_directory: Option<&str>,
) -> ExtractionResult {
const OUTFILE_NAME: &str = "image.jpg";
let mut result = ExtractionResult {
..Default::default()
};
// Find the JPEG EOF to identify the total JPEG size
if let Some(jpeg_data_size) = get_jpeg_data_size(&file_data[offset..]) {
result.size = Some(jpeg_data_size);
result.success = true;
if output_directory.is_some() {
let chroot = Chroot::new(output_directory);
result.success =
chroot.carve_file(OUTFILE_NAME, file_data, offset, result.size.unwrap());
}
}
result
}
/// Parses JPEG markers until the EOF marker is found
fn get_jpeg_data_size(jpeg_data: &[u8]) -> Option<usize> {
const SIZE_FIELD_LENGTH: usize = 2;
const SOS_SCAN_AHEAD_LENGTH: usize = 2;
const MARKER_MAGIC: u8 = 0xFF;
const SOS_MARKER: u8 = 0xDA;
const EOF_MARKER: u8 = 0xD9;
let mut next_marker_offset: usize = 0;
// Most JPEG markers include a size field; these do not
let no_length_markers: Vec<u8> = vec![
0x00, 0x01, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, EOF_MARKER,
];
// In a Start Of Scan block, ignore 0xFF marker magics that are followed by one of these bytes
let sos_skip_markers: Vec<u8> = vec![0x00, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7];
loop {
// Read the marker magic byte
match jpeg_data.get(next_marker_offset) {
None => {
break;
}
Some(marker_magic) => {
// Make sure this is the correct marker magic
if *marker_magic != MARKER_MAGIC {
break;
}
// Include marker magic byte in side of the marker
next_marker_offset += 1;
// Read the marker ID byte
match jpeg_data.get(next_marker_offset) {
None => {
break;
}
Some(marker_id) => {
// Include marker ID byte in the size of the marker
next_marker_offset += 1;
// Most markers have a 2-byte length field after the marker, stored in big-endian
if !no_length_markers.contains(marker_id) {
match jpeg_data
.get(next_marker_offset..next_marker_offset + SIZE_FIELD_LENGTH)
{
None => {
break;
}
Some(size_bytes) => {
next_marker_offset +=
u16::from_be_bytes(size_bytes.try_into().unwrap()) as usize;
}
}
}
// Start Of Scan markers have a size field, but are immediately followed by data not included int
// the size field. Need to scan all the bytes until the next valid JPEG marker is found.
if *marker_id == SOS_MARKER {
loop {
// Get the next two bytes
match jpeg_data.get(
next_marker_offset..next_marker_offset + SOS_SCAN_AHEAD_LENGTH,
) {
None => {
break;
}
Some(next_bytes) => {
// Check if the next byte is a marker magic byte, *and* that it is not followed by a marker escape byte
if next_bytes[0] == MARKER_MAGIC
&& !sos_skip_markers.contains(&next_bytes[1])
{
break;
} else {
// Go to the next byte
next_marker_offset += 1;
}
}
}
}
}
// EOF marker indicates the end of the JPEG image
if *marker_id == EOF_MARKER {
return Some(next_marker_offset);
}
}
}
}
}
}
None
}
================================================
FILE: src/extractors/linux.rs
================================================
use crate::extractors;
/// Describes how to run the vmlinux-to-elf utility to convert raw kernel images to ELF files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::linux::linux_kernel_extractor;
///
/// match linux_kernel_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } else {
/// panic!("Failed to execute external extractor '{}': {}", cmd, e);
/// }
/// }
/// }
/// }
/// ```
pub fn linux_kernel_extractor() -> extractors::common::Extractor {
extractors::common::Extractor {
do_not_recurse: true,
utility: extractors::common::ExtractorType::External("vmlinux-to-elf".to_string()),
extension: "bin".to_string(),
arguments: vec![
// Input file
extractors::common::SOURCE_FILE_PLACEHOLDER.to_string(),
// Output file
"linux_kernel.elf".to_string(),
],
exit_codes: vec![0],
}
}
================================================
FILE: src/extractors/lz4.rs
================================================
use crate::extractors;
/// Describes how to run the lz4 utility to extract LZ4 compressed files
///
/// ```
/// use std::io::ErrorKind;
/// use std::process::Command;
/// use binwalk::extractors::common::ExtractorType;
/// use binwalk::extractors::lz4::lz4_extractor;
///
/// match lz4_extractor().utility {
/// ExtractorType::None => panic!("Invalid extractor type of None"),
/// ExtractorType::Internal(func) => println!("Internal extractor OK: {:?}", func),
/// ExtractorType::External(cmd) => {
/// if let Err(e) = Command::new(&cmd).output() {
/// if e.kind() == ErrorKind::NotFound {
/// panic!("External extractor '{}' not found", cmd);
/// } els
gitextract_b67t48qd/
├── .dockerignore
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── quality.yaml
│ └── rust-release-binary.yml
├── .gitignore
├── CARGO_README.md
├── Cargo.toml
├── Dockerfile
├── LICENSE
├── README.md
├── build_docker.sh
├── dependencies/
│ ├── README.md
│ ├── pip.sh
│ ├── requirements.txt
│ ├── src.sh
│ └── ubuntu.sh
├── fuzzing/
│ ├── Cargo.toml
│ ├── README.md
│ └── src/
│ └── main.rs
├── scripts/
│ └── binwalk-ui
├── src/
│ ├── binwalk.rs
│ ├── cliparser.rs
│ ├── common.rs
│ ├── display.rs
│ ├── entropy.rs
│ ├── extractors/
│ │ ├── androidsparse.rs
│ │ ├── arcadyan.rs
│ │ ├── autel.rs
│ │ ├── bmp.rs
│ │ ├── bzip2.rs
│ │ ├── cab.rs
│ │ ├── common.rs
│ │ ├── csman.rs
│ │ ├── dahua_zip.rs
│ │ ├── dmg.rs
│ │ ├── dtb.rs
│ │ ├── dumpifs.rs
│ │ ├── dxbc.rs
│ │ ├── encfw.rs
│ │ ├── gif.rs
│ │ ├── gpg.rs
│ │ ├── gzip.rs
│ │ ├── inflate.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── jpeg.rs
│ │ ├── linux.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── pcap.rs
│ │ ├── pem.rs
│ │ ├── png.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── sevenzip.rs
│ │ ├── squashfs.rs
│ │ ├── srec.rs
│ │ ├── svg.rs
│ │ ├── swapped.rs
│ │ ├── tarball.rs
│ │ ├── trx.rs
│ │ ├── tsk.rs
│ │ ├── ubi.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── yaffs2.rs
│ │ ├── zlib.rs
│ │ └── zstd.rs
│ ├── extractors.rs
│ ├── json.rs
│ ├── lib.rs
│ ├── magic.rs
│ ├── main.rs
│ ├── signatures/
│ │ ├── aes.rs
│ │ ├── android_bootimg.rs
│ │ ├── androidsparse.rs
│ │ ├── apfs.rs
│ │ ├── arcadyan.rs
│ │ ├── arj.rs
│ │ ├── autel.rs
│ │ ├── binhdr.rs
│ │ ├── bmp.rs
│ │ ├── btrfs.rs
│ │ ├── bzip2.rs
│ │ ├── cab.rs
│ │ ├── cfe.rs
│ │ ├── chk.rs
│ │ ├── common.rs
│ │ ├── compressd.rs
│ │ ├── copyright.rs
│ │ ├── cpio.rs
│ │ ├── cramfs.rs
│ │ ├── csman.rs
│ │ ├── dahua_zip.rs
│ │ ├── deb.rs
│ │ ├── dkbs.rs
│ │ ├── dlink_tlv.rs
│ │ ├── dlke.rs
│ │ ├── dlob.rs
│ │ ├── dmg.rs
│ │ ├── dms.rs
│ │ ├── dpapi.rs
│ │ ├── dtb.rs
│ │ ├── dxbc.rs
│ │ ├── ecos.rs
│ │ ├── efigpt.rs
│ │ ├── elf.rs
│ │ ├── encfw.rs
│ │ ├── encrpted_img.rs
│ │ ├── ext.rs
│ │ ├── fat.rs
│ │ ├── gif.rs
│ │ ├── gpg.rs
│ │ ├── gzip.rs
│ │ ├── hashes.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── jpeg.rs
│ │ ├── linux.rs
│ │ ├── logfs.rs
│ │ ├── luks.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── ntfs.rs
│ │ ├── openssl.rs
│ │ ├── packimg.rs
│ │ ├── pcap.rs
│ │ ├── pchrom.rs
│ │ ├── pdf.rs
│ │ ├── pe.rs
│ │ ├── pem.rs
│ │ ├── pjl.rs
│ │ ├── pkcs_der.rs
│ │ ├── png.rs
│ │ ├── qcow.rs
│ │ ├── qnx.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── rsa.rs
│ │ ├── rtk.rs
│ │ ├── seama.rs
│ │ ├── sevenzip.rs
│ │ ├── shrs.rs
│ │ ├── squashfs.rs
│ │ ├── srec.rs
│ │ ├── svg.rs
│ │ ├── tarball.rs
│ │ ├── tplink.rs
│ │ ├── trx.rs
│ │ ├── ubi.rs
│ │ ├── uboot.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── xz.rs
│ │ ├── yaffs.rs
│ │ ├── zip.rs
│ │ ├── zlib.rs
│ │ └── zstd.rs
│ ├── signatures.rs
│ ├── structures/
│ │ ├── android_bootimg.rs
│ │ ├── androidsparse.rs
│ │ ├── apfs.rs
│ │ ├── arj.rs
│ │ ├── autel.rs
│ │ ├── binhdr.rs
│ │ ├── bmp.rs
│ │ ├── btrfs.rs
│ │ ├── cab.rs
│ │ ├── chk.rs
│ │ ├── common.rs
│ │ ├── cpio.rs
│ │ ├── cramfs.rs
│ │ ├── csman.rs
│ │ ├── deb.rs
│ │ ├── dkbs.rs
│ │ ├── dlink_tlv.rs
│ │ ├── dlob.rs
│ │ ├── dmg.rs
│ │ ├── dms.rs
│ │ ├── dpapi.rs
│ │ ├── dtb.rs
│ │ ├── dxbc.rs
│ │ ├── efigpt.rs
│ │ ├── elf.rs
│ │ ├── ext.rs
│ │ ├── fat.rs
│ │ ├── gif.rs
│ │ ├── gzip.rs
│ │ ├── iso9660.rs
│ │ ├── jboot.rs
│ │ ├── jffs2.rs
│ │ ├── linux.rs
│ │ ├── logfs.rs
│ │ ├── luks.rs
│ │ ├── lz4.rs
│ │ ├── lzfse.rs
│ │ ├── lzma.rs
│ │ ├── lzop.rs
│ │ ├── matter_ota.rs
│ │ ├── mbr.rs
│ │ ├── mh01.rs
│ │ ├── ntfs.rs
│ │ ├── openssl.rs
│ │ ├── packimg.rs
│ │ ├── pcap.rs
│ │ ├── pchrom.rs
│ │ ├── pe.rs
│ │ ├── png.rs
│ │ ├── qcow.rs
│ │ ├── qnx.rs
│ │ ├── rar.rs
│ │ ├── riff.rs
│ │ ├── romfs.rs
│ │ ├── rtk.rs
│ │ ├── seama.rs
│ │ ├── sevenzip.rs
│ │ ├── shrs.rs
│ │ ├── squashfs.rs
│ │ ├── svg.rs
│ │ ├── tplink.rs
│ │ ├── trx.rs
│ │ ├── ubi.rs
│ │ ├── uefi.rs
│ │ ├── uimage.rs
│ │ ├── vxworks.rs
│ │ ├── wince.rs
│ │ ├── xz.rs
│ │ ├── yaffs.rs
│ │ ├── zip.rs
│ │ └── zstd.rs
│ └── structures.rs
└── tests/
├── arcadyan.rs
├── arj.rs
├── bmp.rs
├── bzip2.rs
├── common/
│ └── mod.rs
├── cramfs.rs
├── gzip.rs
├── jpeg.rs
├── matter_ota.rs
├── mbr.rs
├── pdf.rs
├── png.rs
├── qcow.rs
├── riff.rs
├── romfs.rs
├── sevenzip.rs
├── squashfs.rs
├── squashfs_v2.rs
├── yaffs2.rs
├── zip.rs
└── zip_truncated.rs
SYMBOL INDEX (830 symbols across 247 files)
FILE: fuzzing/src/main.rs
function main (line 4) | fn main() {
FILE: src/binwalk.rs
type BinwalkError (line 24) | pub struct BinwalkError {
method new (line 29) | pub fn new(message: &str) -> Self {
type AnalysisResults (line 38) | pub struct AnalysisResults {
type Binwalk (line 67) | pub struct Binwalk {
method new (line 98) | pub fn new() -> Binwalk {
method configure (line 131) | pub fn configure(
method scan (line 263) | pub fn scan(&self, file_data: &[u8]) -> Vec<signatures::common::Signat...
method extract (line 604) | pub fn extract(
method analyze_buf (line 718) | pub fn analyze_buf(
method analyze (line 792) | pub fn analyze(&self, target_file: impl Into<String>, do_extraction: b...
function init_extraction_directory (line 808) | fn init_extraction_directory(
function include_signature (line 883) | fn include_signature(
function signature_result_auto_populate (line 912) | fn signature_result_auto_populate(
FILE: src/cliparser.rs
type CliArgs (line 5) | pub struct CliArgs {
function parse (line 70) | pub fn parse() -> CliArgs {
FILE: src/common.rs
function read_input (line 20) | pub fn read_input(file: impl Into<String>, stdin: bool) -> Result<Vec<u8...
function read_stdin (line 25) | pub fn read_stdin() -> Result<Vec<u8>, std::io::Error> {
function read_file (line 53) | pub fn read_file(file: impl Into<String>) -> Result<Vec<u8>, std::io::Er...
function crc32 (line 92) | pub fn crc32(data: &[u8]) -> u32 {
function epoch_to_string (line 107) | pub fn epoch_to_string(epoch_timestamp: u32) -> String {
function get_cstring_bytes (line 117) | fn get_cstring_bytes(raw_data: &[u8]) -> Vec<u8> {
function get_cstring (line 144) | pub fn get_cstring(raw_data: &[u8]) -> String {
function is_ascii_number (line 165) | pub fn is_ascii_number(b: u8) -> bool {
function is_printable_ascii (line 182) | pub fn is_printable_ascii(b: u8) -> bool {
function is_offset_safe (line 210) | pub fn is_offset_safe(
FILE: src/display.rs
constant DELIM_CHARACTER (line 12) | const DELIM_CHARACTER: &str = "-";
constant DEFAULT_TERMINAL_WIDTH (line 13) | const DEFAULT_TERMINAL_WIDTH: u16 = 200;
constant COLUMN1_WIDTH (line 15) | const COLUMN1_WIDTH: usize = 35;
constant COLUMN2_WIDTH (line 16) | const COLUMN2_WIDTH: usize = 35;
function terminal_width (line 18) | fn terminal_width() -> usize {
function line_delimiter (line 27) | fn line_delimiter() -> String {
function center_text (line 37) | fn center_text(text: &str) -> String {
function pad_to_length (line 59) | fn pad_to_length(text: &str, len: usize) -> String {
function line_wrap (line 79) | fn line_wrap(text: &str, prefix_size: usize) -> String {
function print_column_headers (line 101) | fn print_column_headers(col1: &str, col2: &str, col3: &str) {
function print_delimiter (line 112) | fn print_delimiter() {
function print_header (line 116) | fn print_header(title_text: &str) {
function print_footer (line 124) | fn print_footer() {
function print_signature (line 129) | fn print_signature(signature: &signatures::common::SignatureResult) {
function print_signatures (line 148) | fn print_signatures(signatures: &Vec<signatures::common::SignatureResult...
function print_extraction (line 154) | fn print_extraction(
function print_extractions (line 191) | fn print_extractions(
function print_analysis_results (line 220) | pub fn print_analysis_results(quiet: bool, extraction_attempted: bool, r...
type SignatureInfo (line 240) | struct SignatureInfo {
function print_signature_list (line 248) | pub fn print_signature_list(quiet: bool, signatures: &Vec<signatures::co...
function print_stats (line 345) | pub fn print_stats(
function print_plain (line 389) | pub fn print_plain(quiet: bool, msg: &str) {
function println_plain (line 396) | pub fn println_plain(quiet: bool, msg: &str) {
FILE: src/entropy.rs
type EntropyError (line 8) | pub struct EntropyError;
type BlockEntropy (line 11) | pub struct BlockEntropy {
type FileEntropy (line 18) | pub struct FileEntropy {
function blocks (line 24) | fn blocks(data: &[u8]) -> Vec<BlockEntropy> {
function plot (line 59) | pub fn plot(
FILE: src/extractors/androidsparse.rs
function android_sparse_extractor (line 27) | pub fn android_sparse_extractor() -> Extractor {
function extract_android_sparse (line 35) | pub fn extract_android_sparse(
function extract_chunk (line 101) | fn extract_chunk(
FILE: src/extractors/arcadyan.rs
function obfuscated_lzma_extractor (line 26) | pub fn obfuscated_lzma_extractor() -> Extractor {
function extract_obfuscated_lzma (line 34) | pub fn extract_obfuscated_lzma(
function arcadyan_deobfuscator (line 60) | fn arcadyan_deobfuscator(obfuscated_data: &[u8]) -> Vec<u8> {
FILE: src/extractors/autel.rs
constant BLOCK_SIZE (line 4) | const BLOCK_SIZE: usize = 256;
function autel_extractor (line 28) | pub fn autel_extractor() -> Extractor {
function autel_deobfuscate (line 37) | pub fn autel_deobfuscate(
function decode_autel_block (line 89) | fn decode_autel_block(block_data: &[u8]) -> Vec<u8> {
FILE: src/extractors/bmp.rs
function bmp_extractor (line 26) | pub fn bmp_extractor() -> Extractor {
function extract_bmp_image (line 34) | pub fn extract_bmp_image(
FILE: src/extractors/bzip2.rs
function bzip2_extractor (line 27) | pub fn bzip2_extractor() -> Extractor {
function bzip2_decompressor (line 35) | pub fn bzip2_decompressor(
FILE: src/extractors/cab.rs
function cab_extractor (line 25) | pub fn cab_extractor() -> extractors::common::Extractor {
FILE: src/extractors/common.rs
constant SOURCE_FILE_PLACEHOLDER (line 19) | pub const SOURCE_FILE_PLACEHOLDER: &str = "%e";
type ExtractionError (line 23) | pub struct ExtractionError;
type InternalExtractor (line 27) | pub type InternalExtractor = fn(&[u8], usize, Option<&str>) -> Extractio...
type ExtractorType (line 31) | pub enum ExtractorType {
type Extractor (line 40) | pub struct Extractor {
type ExtractionResult (line 55) | pub struct ExtractionResult {
type ProcInfo (line 71) | pub struct ProcInfo {
type Chroot (line 79) | pub struct Chroot {
method new (line 107) | pub fn new(chroot_directory: Option<&str>) -> Chroot {
method safe_path_join (line 193) | pub fn safe_path_join(&self, path1: impl Into<String>, path2: impl Int...
method chrooted_path (line 236) | pub fn chrooted_path(&self, file_path: impl Into<String>) -> String {
method create_file (line 266) | pub fn create_file(&self, file_path: impl Into<String>, file_data: &[u...
method carve_file (line 313) | pub fn carve_file(
method create_device (line 337) | fn create_device(
method create_character_device (line 376) | pub fn create_character_device(
method create_block_device (line 413) | pub fn create_block_device(
method create_fifo (line 448) | pub fn create_fifo(&self, file_path: impl Into<String>) -> bool {
method create_socket (line 478) | pub fn create_socket(&self, file_path: impl Into<String>) -> bool {
method append_to_file (line 509) | pub fn append_to_file(&self, file_path: impl Into<String>, data: &[u8]...
method create_directory (line 560) | pub fn create_directory(&self, dir_path: impl Into<String>) -> bool {
method remove_directory (line 599) | pub fn remove_directory(&self, dir_path: impl Into<String>) -> bool {
method make_executable (line 644) | pub fn make_executable(&self, file_path: impl Into<String>) -> bool {
method create_symlink (line 711) | pub fn create_symlink(
method is_symlink (line 817) | fn is_symlink(&self, file_path: &str) -> bool {
method strip_double_slash (line 826) | fn strip_double_slash(&self, path: &str) -> String {
method sanitize_path (line 839) | fn sanitize_path(&self, file_path: &str, preserve_root_path_sep: bool)...
function get_extracted_files (line 892) | pub fn get_extracted_files(directory: &str) -> Vec<String> {
function execute (line 918) | pub fn execute(
function spawn (line 1025) | fn spawn(
function proc_wait (line 1120) | fn proc_wait(mut worker_info: ProcInfo) -> Result<ExtractionResult, Extr...
function create_output_directory (line 1172) | fn create_output_directory(file_path: &str, offset: usize) -> Result<Str...
function was_something_extracted (line 1198) | fn was_something_extracted(output_directory: &str) -> bool {
FILE: src/extractors/csman.rs
function csman_extractor (line 29) | pub fn csman_extractor() -> Extractor {
function extract_csman_dat (line 37) | pub fn extract_csman_dat(
FILE: src/extractors/dahua_zip.rs
function dahua_zip_extractor (line 26) | pub fn dahua_zip_extractor() -> Extractor {
function extract_dahua_zip (line 34) | pub fn extract_dahua_zip(
FILE: src/extractors/dmg.rs
function dmg_extractor (line 25) | pub fn dmg_extractor() -> extractors::common::Extractor {
FILE: src/extractors/dtb.rs
function dtb_extractor (line 28) | pub fn dtb_extractor() -> Extractor {
function extract_dtb (line 36) | pub fn extract_dtb(
FILE: src/extractors/dumpifs.rs
function dumpifs_extractor (line 25) | pub fn dumpifs_extractor() -> extractors::common::Extractor {
FILE: src/extractors/dxbc.rs
function dxbc_extractor (line 26) | pub fn dxbc_extractor() -> Extractor {
function extract_dxbc_file (line 34) | pub fn extract_dxbc_file(
FILE: src/extractors/encfw.rs
function encfw_extractor (line 25) | pub fn encfw_extractor() -> Extractor {
function encfw_decrypt (line 33) | pub fn encfw_decrypt(
FILE: src/extractors/gif.rs
function gif_extractor (line 28) | pub fn gif_extractor() -> Extractor {
function extract_gif_image (line 37) | pub fn extract_gif_image(
function get_gif_data_size (line 72) | fn get_gif_data_size(gif_data: &[u8]) -> Option<usize> {
FILE: src/extractors/gpg.rs
function gpg_extractor (line 26) | pub fn gpg_extractor() -> Extractor {
function gpg_decompress (line 34) | pub fn gpg_decompress(
FILE: src/extractors/gzip.rs
function gzip_extractor (line 27) | pub fn gzip_extractor() -> Extractor {
function gzip_decompress (line 35) | pub fn gzip_decompress(
FILE: src/extractors/inflate.rs
type DeflateResult (line 7) | pub struct DeflateResult {
function inflate_decompressor (line 15) | pub fn inflate_decompressor(
FILE: src/extractors/iso9660.rs
function iso9660_extractor (line 26) | pub fn iso9660_extractor() -> extractors::common::Extractor {
FILE: src/extractors/jboot.rs
function sch2_extractor (line 27) | pub fn sch2_extractor() -> Extractor {
function extract_jboot_sch2_kernel (line 35) | pub fn extract_jboot_sch2_kernel(
FILE: src/extractors/jffs2.rs
function jffs2_extractor (line 25) | pub fn jffs2_extractor() -> extractors::common::Extractor {
FILE: src/extractors/jpeg.rs
function jpeg_extractor (line 25) | pub fn jpeg_extractor() -> Extractor {
function extract_jpeg_image (line 34) | pub fn extract_jpeg_image(
function get_jpeg_data_size (line 61) | fn get_jpeg_data_size(jpeg_data: &[u8]) -> Option<usize> {
FILE: src/extractors/linux.rs
function linux_kernel_extractor (line 25) | pub fn linux_kernel_extractor() -> extractors::common::Extractor {
FILE: src/extractors/lz4.rs
function lz4_extractor (line 25) | pub fn lz4_extractor() -> extractors::common::Extractor {
FILE: src/extractors/lzfse.rs
function lzfse_extractor (line 25) | pub fn lzfse_extractor() -> Extractor {
FILE: src/extractors/lzma.rs
function lzma_extractor (line 26) | pub fn lzma_extractor() -> Extractor {
function lzma_decompress (line 34) | pub fn lzma_decompress(
FILE: src/extractors/lzop.rs
function lzop_extractor (line 25) | pub fn lzop_extractor() -> extractors::common::Extractor {
FILE: src/extractors/matter_ota.rs
function matter_ota_extractor (line 26) | pub fn matter_ota_extractor() -> Extractor {
function extract_matter_ota (line 34) | pub fn extract_matter_ota(
FILE: src/extractors/mbr.rs
function mbr_extractor (line 26) | pub fn mbr_extractor() -> Extractor {
function extract_mbr_partitions (line 34) | pub fn extract_mbr_partitions(
FILE: src/extractors/mh01.rs
function mh01_extractor (line 26) | pub fn mh01_extractor() -> Extractor {
function extract_mh01_image (line 34) | pub fn extract_mh01_image(
FILE: src/extractors/pcap.rs
function pcapng_extractor (line 27) | pub fn pcapng_extractor() -> Extractor {
function pcapng_carver (line 36) | pub fn pcapng_carver(
FILE: src/extractors/pem.rs
function pem_key_extractor (line 26) | pub fn pem_key_extractor() -> Extractor {
function pem_certificate_extractor (line 56) | pub fn pem_certificate_extractor() -> Extractor {
function pem_certificate_carver (line 64) | pub fn pem_certificate_carver(
function pem_key_carver (line 78) | pub fn pem_key_carver(
function pem_carver (line 87) | pub fn pem_carver(
function get_pem_size (line 113) | fn get_pem_size(file_data: &[u8], start_of_pem_offset: usize) -> Option<...
FILE: src/extractors/png.rs
function png_extractor (line 27) | pub fn png_extractor() -> Extractor {
function extract_png_image (line 35) | pub fn extract_png_image(
function get_png_data_size (line 66) | fn get_png_data_size(png_chunk_data: &[u8]) -> Option<usize> {
FILE: src/extractors/rar.rs
function rar_extractor (line 25) | pub fn rar_extractor() -> extractors::common::Extractor {
FILE: src/extractors/riff.rs
function riff_extractor (line 26) | pub fn riff_extractor() -> Extractor {
function extract_riff_image (line 35) | pub fn extract_riff_image(
FILE: src/extractors/romfs.rs
type RomFSEntry (line 9) | struct RomFSEntry {
function romfs_extractor (line 51) | pub fn romfs_extractor() -> Extractor {
function extract_romfs (line 59) | pub fn extract_romfs(
function process_romfs_entries (line 119) | fn process_romfs_entries(
function extract_romfs_entries (line 228) | fn extract_romfs_entries(
FILE: src/extractors/sevenzip.rs
function sevenzip_extractor (line 25) | pub fn sevenzip_extractor() -> extractors::common::Extractor {
FILE: src/extractors/squashfs.rs
function squashfs_extractor (line 25) | pub fn squashfs_extractor() -> extractors::common::Extractor {
function squashfs_le_extractor (line 58) | pub fn squashfs_le_extractor() -> extractors::common::Extractor {
function squashfs_be_extractor (line 94) | pub fn squashfs_be_extractor() -> extractors::common::Extractor {
function squashfs_v4_be_extractor (line 130) | pub fn squashfs_v4_be_extractor() -> extractors::common::Extractor {
FILE: src/extractors/srec.rs
function srec_extractor (line 25) | pub fn srec_extractor() -> extractors::common::Extractor {
FILE: src/extractors/svg.rs
function svg_extractor (line 26) | pub fn svg_extractor() -> Extractor {
function extract_svg_image (line 35) | pub fn extract_svg_image(
FILE: src/extractors/swapped.rs
function swapped_extractor_u16 (line 25) | pub fn swapped_extractor_u16() -> Extractor {
function extract_swapped_u16 (line 33) | pub fn extract_swapped_u16(
function extract_swapped (line 43) | fn extract_swapped(
function byte_swap (line 83) | pub fn byte_swap(data: &[u8], n: usize) -> Vec<u8> {
FILE: src/extractors/tarball.rs
function tarball_extractor (line 25) | pub fn tarball_extractor() -> extractors::common::Extractor {
FILE: src/extractors/trx.rs
function trx_extractor (line 27) | pub fn trx_extractor() -> Extractor {
function extract_trx_partitions (line 35) | pub fn extract_trx_partitions(
function trx_crc32 (line 95) | fn trx_crc32(crc_data: &[u8]) -> usize {
FILE: src/extractors/tsk.rs
function tsk_extractor (line 4) | pub fn tsk_extractor() -> extractors::common::Extractor {
FILE: src/extractors/ubi.rs
function ubi_extractor (line 25) | pub fn ubi_extractor() -> extractors::common::Extractor {
function ubifs_extractor (line 38) | pub fn ubifs_extractor() -> extractors::common::Extractor {
FILE: src/extractors/uefi.rs
function uefi_extractor (line 25) | pub fn uefi_extractor() -> extractors::common::Extractor {
FILE: src/extractors/uimage.rs
function uimage_extractor (line 27) | pub fn uimage_extractor() -> Extractor {
function extract_uimage (line 34) | pub fn extract_uimage(
FILE: src/extractors/vxworks.rs
function vxworks_symtab_extractor (line 31) | pub fn vxworks_symtab_extractor() -> Extractor {
function extract_symbol_table (line 40) | pub fn extract_symbol_table(
FILE: src/extractors/wince.rs
function wince_extractor (line 27) | pub fn wince_extractor() -> Extractor {
function wince_dump (line 35) | pub fn wince_dump(
type BlockInfo (line 87) | struct BlockInfo {
type BlockData (line 95) | struct BlockData {
function process_wince_blocks (line 101) | fn process_wince_blocks(blocks_data: &[u8]) -> Option<BlockData> {
FILE: src/extractors/yaffs2.rs
function yaffs2_extractor (line 25) | pub fn yaffs2_extractor() -> extractors::common::Extractor {
FILE: src/extractors/zlib.rs
constant CHECKSUM_SIZE (line 5) | pub const CHECKSUM_SIZE: usize = 4;
function zlib_extractor (line 29) | pub fn zlib_extractor() -> Extractor {
function zlib_decompress (line 37) | pub fn zlib_decompress(
FILE: src/extractors/zstd.rs
function zstd_extractor (line 25) | pub fn zstd_extractor() -> extractors::common::Extractor {
FILE: src/json.rs
constant STDOUT (line 12) | const STDOUT: &str = "-";
constant JSON_LIST_START (line 13) | const JSON_LIST_START: &str = "[\n";
constant JSON_LIST_END (line 14) | const JSON_LIST_END: &str = "\n]\n";
constant JSON_LIST_SEP (line 15) | const JSON_LIST_SEP: &str = ",\n";
type JSONType (line 18) | pub enum JSONType {
type JsonLogger (line 24) | pub struct JsonLogger {
method new (line 30) | pub fn new(log_file: Option<String>) -> JsonLogger {
method close (line 42) | pub fn close(&self) {
method log (line 46) | pub fn log(&mut self, results: JSONType) {
method write_json (line 62) | fn write_json(&self, data: &str) {
FILE: src/magic.rs
function patterns (line 5) | pub fn patterns() -> Vec<signatures::common::Signature> {
FILE: src/main.rs
function main (line 23) | fn main() -> ExitCode {
function should_display (line 255) | fn should_display(results: &AnalysisResults, file_count: usize, verbose:...
function spawn_worker (line 278) | fn spawn_worker(
function carve_file_map (line 319) | fn carve_file_map(file_data: &[u8], results: &binwalk::AnalysisResults) ...
function carve_file_data_to_disk (line 371) | fn carve_file_data_to_disk(
FILE: src/signatures/aes.rs
constant DESCRIPTION_AES_SBOX (line 4) | pub const DESCRIPTION_AES_SBOX: &str = "AES S-Box";
constant DESCRIPTION_AES_FT (line 5) | pub const DESCRIPTION_AES_FT: &str = "AES Forward Table";
constant DESCRIPTION_AES_RT (line 6) | pub const DESCRIPTION_AES_RT: &str = "AES Reverse Table";
constant DESCRIPTION_AES_RCON (line 7) | pub const DESCRIPTION_AES_RCON: &str = "AES RCON";
constant DESCRIPTION_AES_ACC (line 8) | pub const DESCRIPTION_AES_ACC: &str = "AES Acceleration Table";
function aes_sbox_magic (line 11) | pub fn aes_sbox_magic() -> Vec<Vec<u8>> {
function aes_forward_table_magic (line 18) | pub fn aes_forward_table_magic() -> Vec<Vec<u8>> {
function aes_reverse_table_magic (line 22) | pub fn aes_reverse_table_magic() -> Vec<Vec<u8>> {
function aes_rcon_magic (line 26) | pub fn aes_rcon_magic() -> Vec<Vec<u8>> {
function aes_acceleration_table_magic (line 33) | pub fn aes_acceleration_table_magic() -> Vec<Vec<u8>> {
function aes_sbox_parser (line 47) | pub fn aes_sbox_parser(
function aes_forward_table_parser (line 64) | pub fn aes_forward_table_parser(
function aes_reverse_table_parser (line 81) | pub fn aes_reverse_table_parser(
function aes_acceleration_table_parser (line 98) | pub fn aes_acceleration_table_parser(
function aes_rcon_parser (line 115) | pub fn aes_rcon_parser(
FILE: src/signatures/android_bootimg.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "Android boot image";
function android_bootimg_magic (line 10) | pub fn android_bootimg_magic() -> Vec<Vec<u8>> {
function android_bootimg_parser (line 15) | pub fn android_bootimg_parser(
FILE: src/signatures/androidsparse.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "Android sparse image";
function android_sparse_magic (line 9) | pub fn android_sparse_magic() -> Vec<Vec<u8>> {
function android_sparse_parser (line 14) | pub fn android_sparse_parser(
FILE: src/signatures/apfs.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "APple File System";
function apfs_magic (line 8) | pub fn apfs_magic() -> Vec<Vec<u8>> {
function apfs_parser (line 13) | pub fn apfs_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/arcadyan.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Arcadyan obfuscated LZMA";
function obfuscated_lzma_magic (line 8) | pub fn obfuscated_lzma_magic() -> Vec<Vec<u8>> {
function obfuscated_lzma_parser (line 13) | pub fn obfuscated_lzma_parser(
FILE: src/signatures/arj.rs
constant DESCRIPTION (line 4) | pub const DESCRIPTION: &str = "ARJ archive data";
function arj_magic (line 5) | pub fn arj_magic() -> Vec<Vec<u8>> {
function arj_parser (line 9) | pub fn arj_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/autel.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Autel obfuscated firmware";
function autel_magic (line 8) | pub fn autel_magic() -> Vec<Vec<u8>> {
function autel_parser (line 13) | pub fn autel_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/binhdr.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "BIN firmware header";
function bin_hdr_magic (line 8) | pub fn bin_hdr_magic() -> Vec<Vec<u8>> {
function bin_hdr_parser (line 13) | pub fn bin_hdr_parser(file_data: &[u8], offset: usize) -> Result<Signatu...
FILE: src/signatures/bmp.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "BMP image (Bitmap)";
function bmp_magic (line 10) | pub fn bmp_magic() -> Vec<Vec<u8>> {
function bmp_parser (line 15) | pub fn bmp_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/btrfs.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "BTRFS file system";
function btrfs_magic (line 8) | pub fn btrfs_magic() -> Vec<Vec<u8>> {
function btrfs_parser (line 13) | pub fn btrfs_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/bzip2.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "bzip2 compressed data";
function bzip2_magic (line 8) | pub fn bzip2_magic() -> Vec<Vec<u8>> {
function bzip2_parser (line 23) | pub fn bzip2_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/cab.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Microsoft Cabinet archive";
function cab_magic (line 8) | pub fn cab_magic() -> Vec<Vec<u8>> {
function cab_parser (line 13) | pub fn cab_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/cfe.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "CFE bootloader";
function cfe_magic (line 9) | pub fn cfe_magic() -> Vec<Vec<u8>> {
function cfe_parser (line 14) | pub fn cfe_parser(_file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/chk.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "CHK firmware header";
function chk_magic (line 8) | pub fn chk_magic() -> Vec<Vec<u8>> {
function chk_parser (line 13) | pub fn chk_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/common.rs
constant CONFIDENCE_LOW (line 5) | pub const CONFIDENCE_LOW: u8 = 0;
constant CONFIDENCE_MEDIUM (line 6) | pub const CONFIDENCE_MEDIUM: u8 = 128;
constant CONFIDENCE_HIGH (line 7) | pub const CONFIDENCE_HIGH: u8 = 250;
type SignatureError (line 11) | pub struct SignatureError;
type SignatureParser (line 24) | pub type SignatureParser = fn(&[u8], usize) -> Result<SignatureResult, S...
type SignatureResult (line 44) | pub struct SignatureResult {
type Signature (line 68) | pub struct Signature {
FILE: src/signatures/compressd.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "compress'd data";
function compressd_magic (line 9) | pub fn compressd_magic() -> Vec<Vec<u8>> {
function compressd_parser (line 14) | pub fn compressd_parser(
FILE: src/signatures/copyright.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Copyright text";
function copyright_magic (line 8) | pub fn copyright_magic() -> Vec<Vec<u8>> {
function copyright_parser (line 17) | pub fn copyright_parser(
FILE: src/signatures/cpio.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "CPIO ASCII archive";
function cpio_magic (line 9) | pub fn cpio_magic() -> Vec<Vec<u8>> {
function cpio_parser (line 14) | pub fn cpio_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/cramfs.rs
constant DESCRIPTION (line 8) | pub const DESCRIPTION: &str = "CramFS filesystem";
function cramfs_magic (line 11) | pub fn cramfs_magic() -> Vec<Vec<u8>> {
function cramfs_parser (line 16) | pub fn cramfs_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
FILE: src/signatures/csman.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "CSman DAT file";
function csman_magic (line 8) | pub fn csman_magic() -> Vec<Vec<u8>> {
function csman_parser (line 14) | pub fn csman_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/dahua_zip.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Dahua ZIP archive";
function dahua_zip_magic (line 8) | pub fn dahua_zip_magic() -> Vec<Vec<u8>> {
function dahua_zip_parser (line 15) | pub fn dahua_zip_parser(
FILE: src/signatures/deb.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Debian package file";
function deb_magic (line 8) | pub fn deb_magic() -> Vec<Vec<u8>> {
function deb_parser (line 13) | pub fn deb_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/dkbs.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "DKBS firmware header";
function dkbs_magic (line 10) | pub fn dkbs_magic() -> Vec<Vec<u8>> {
function dkbs_parser (line 15) | pub fn dkbs_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/dlink_tlv.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "D-Link TLV firmware";
function dlink_tlv_magic (line 9) | pub fn dlink_tlv_magic() -> Vec<Vec<u8>> {
function dlink_tlv_parser (line 14) | pub fn dlink_tlv_parser(
FILE: src/signatures/dlke.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "DLK encrypted firmware";
function dlke_magic (line 8) | pub fn dlke_magic() -> Vec<Vec<u8>> {
function dlke_parser (line 14) | pub fn dlke_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/dlob.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "DLOB firmware header";
function dlob_magic (line 8) | pub fn dlob_magic() -> Vec<Vec<u8>> {
function dlob_parser (line 13) | pub fn dlob_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/dmg.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "Apple Disk iMaGe";
function dmg_magic (line 10) | pub fn dmg_magic() -> Vec<Vec<u8>> {
function dmg_parser (line 15) | pub fn dmg_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function find_xml_property_list (line 62) | fn find_xml_property_list(file_data: &[u8]) -> Option<usize> {
FILE: src/signatures/dms.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "DMS firmware image";
function dms_magic (line 9) | pub fn dms_magic() -> Vec<Vec<u8>> {
function dms_parser (line 14) | pub fn dms_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/dpapi.rs
constant DESCRIPTION (line 9) | pub const DESCRIPTION: &str = "DPAPI blob data";
function dpapi_magic (line 12) | pub fn dpapi_magic() -> Vec<Vec<u8>> {
function dpapi_parser (line 20) | pub fn dpapi_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/dtb.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Device tree blob (DTB)";
function dtb_magic (line 8) | pub fn dtb_magic() -> Vec<Vec<u8>> {
function dtb_parser (line 13) | pub fn dtb_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/dxbc.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "DirectX shader bytecode";
function dxbc_magic (line 10) | pub fn dxbc_magic() -> Vec<Vec<u8>> {
function dxbc_parser (line 15) | pub fn dxbc_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/ecos.rs
constant EXCEPTION_HANDLER_DESCRIPTION (line 4) | pub const EXCEPTION_HANDLER_DESCRIPTION: &str = "eCos kernel exception h...
function exception_handler_magic (line 7) | pub fn exception_handler_magic() -> Vec<Vec<u8>> {
function exception_handler_parser (line 29) | pub fn exception_handler_parser(
FILE: src/signatures/efigpt.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "EFI Global Partition Table";
function efigpt_magic (line 8) | pub fn efigpt_magic() -> Vec<Vec<u8>> {
function efigpt_parser (line 13) | pub fn efigpt_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
FILE: src/signatures/elf.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "ELF binary";
function elf_magic (line 8) | pub fn elf_magic() -> Vec<Vec<u8>> {
function elf_parser (line 13) | pub fn elf_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/encfw.rs
function encfw_known_firmware (line 7) | fn encfw_known_firmware() -> HashMap<Vec<u8>, String> {
constant DESCRIPTION (line 30) | pub const DESCRIPTION: &str = "Known encrypted firmware";
function encfw_magic (line 33) | pub fn encfw_magic() -> Vec<Vec<u8>> {
function encfw_parser (line 38) | pub fn encfw_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/encrpted_img.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "D-Link Encrpted Image";
function encrpted_img_magic (line 9) | pub fn encrpted_img_magic() -> Vec<Vec<u8>> {
function encrpted_img_parser (line 14) | pub fn encrpted_img_parser(
FILE: src/signatures/ext.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "EXT filesystem";
function ext_magic (line 8) | pub fn ext_magic() -> Vec<Vec<u8>> {
function ext_parser (line 26) | pub fn ext_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/fat.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "FAT file system";
constant MAGIC_OFFSET (line 8) | pub const MAGIC_OFFSET: usize = 0x01FE;
function fat_magic (line 11) | pub fn fat_magic() -> Vec<Vec<u8>> {
function fat_parser (line 16) | pub fn fat_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/gif.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "GIF image";
function gif_magic (line 9) | pub fn gif_magic() -> Vec<Vec<u8>> {
function gif_parser (line 15) | pub fn gif_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/gpg.rs
constant GPG_SIGNED_DESCRIPTION (line 5) | pub const GPG_SIGNED_DESCRIPTION: &str = "GPG signed file";
function gpg_signed_magic (line 8) | pub fn gpg_signed_magic() -> Vec<Vec<u8>> {
function gpg_signed_parser (line 13) | pub fn gpg_signed_parser(
FILE: src/signatures/gzip.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "gzip compressed data";
function gzip_magic (line 10) | pub fn gzip_magic() -> Vec<Vec<u8>> {
function gzip_parser (line 15) | pub fn gzip_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/hashes.rs
constant HASH_MAGIC_LEN (line 4) | const HASH_MAGIC_LEN: usize = 16;
constant CRC32_DESCRIPTION (line 7) | pub const CRC32_DESCRIPTION: &str = "CRC32 polynomial table";
constant SHA256_DESCRIPTION (line 8) | pub const SHA256_DESCRIPTION: &str = "SHA256 hash constants";
constant MD5_DESCRIPTION (line 9) | pub const MD5_DESCRIPTION: &str = "MD5 hash constants";
function crc32_magic (line 12) | pub fn crc32_magic() -> Vec<Vec<u8>> {
function sha256_magic (line 23) | pub fn sha256_magic() -> Vec<Vec<u8>> {
function md5_magic (line 34) | pub fn md5_magic() -> Vec<Vec<u8>> {
function crc32_parser (line 43) | pub fn crc32_parser(file_data: &[u8], offset: usize) -> Result<Signature...
function sha256_parser (line 59) | pub fn sha256_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
function md5_parser (line 75) | pub fn md5_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function hash_endianess (line 92) | fn hash_endianess(file_data: &[u8], offset: usize, magics: Vec<Vec<u8>>)...
FILE: src/signatures/iso9660.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "ISO9660 primary volume";
function iso_magic (line 8) | pub fn iso_magic() -> Vec<Vec<u8>> {
function iso_parser (line 13) | pub fn iso_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/jboot.rs
constant JBOOT_ARM_DESCRIPTION (line 10) | pub const JBOOT_ARM_DESCRIPTION: &str = "JBOOT firmware header";
constant JBOOT_STAG_DESCRIPTION (line 11) | pub const JBOOT_STAG_DESCRIPTION: &str = "JBOOT STAG header";
constant JBOOT_SCH2_DESCRIPTION (line 12) | pub const JBOOT_SCH2_DESCRIPTION: &str = "JBOOT SCH2 header";
function jboot_arm_magic (line 15) | pub fn jboot_arm_magic() -> Vec<Vec<u8>> {
function jboot_stag_magic (line 20) | pub fn jboot_stag_magic() -> Vec<Vec<u8>> {
function jboot_sch2_magic (line 25) | pub fn jboot_sch2_magic() -> Vec<Vec<u8>> {
function jboot_arm_parser (line 35) | pub fn jboot_arm_parser(
function jboot_stag_parser (line 74) | pub fn jboot_stag_parser(
function jboot_sch2_parser (line 112) | pub fn jboot_sch2_parser(
FILE: src/signatures/jffs2.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "JFFS2 filesystem";
function jffs2_magic (line 9) | pub fn jffs2_magic() -> Vec<Vec<u8>> {
function jffs2_parser (line 26) | pub fn jffs2_parser(file_data: &[u8], offset: usize) -> Result<Signature...
function roundup (line 110) | fn roundup(num: usize) -> usize {
FILE: src/signatures/jpeg.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "JPEG image";
function jpeg_magic (line 8) | pub fn jpeg_magic() -> Vec<Vec<u8>> {
function jpeg_parser (line 17) | pub fn jpeg_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/linux.rs
constant LINUX_ARM_ZIMAGE_DESCRIPTION (line 11) | pub const LINUX_ARM_ZIMAGE_DESCRIPTION: &str = "Linux ARM boot executabl...
constant LINUX_BOOT_IMAGE_DESCRIPTION (line 12) | pub const LINUX_BOOT_IMAGE_DESCRIPTION: &str = "Linux kernel boot image";
constant LINUX_KERNEL_VERSION_DESCRIPTION (line 13) | pub const LINUX_KERNEL_VERSION_DESCRIPTION: &str = "Linux kernel version";
constant LINUX_ARM64_BOOT_IMAGE_DESCRIPTION (line 14) | pub const LINUX_ARM64_BOOT_IMAGE_DESCRIPTION: &str = "Linux kernel ARM64...
function linux_boot_image_magic (line 17) | pub fn linux_boot_image_magic() -> Vec<Vec<u8>> {
function linux_kernel_version_magic (line 22) | pub fn linux_kernel_version_magic() -> Vec<Vec<u8>> {
function linux_arm64_boot_image_magic (line 27) | pub fn linux_arm64_boot_image_magic() -> Vec<Vec<u8>> {
function linux_arm_zimage_magic (line 32) | pub fn linux_arm_zimage_magic() -> Vec<Vec<u8>> {
function linux_arm_zimage_parser (line 37) | pub fn linux_arm_zimage_parser(
function linux_arm64_boot_image_parser (line 67) | pub fn linux_arm64_boot_image_parser(
function linux_boot_image_parser (line 99) | pub fn linux_boot_image_parser(
function linux_kernel_version_parser (line 132) | pub fn linux_kernel_version_parser(
function has_linux_symbol_table (line 208) | fn has_linux_symbol_table(file_data: &[u8]) -> bool {
FILE: src/signatures/logfs.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "LogFS file system";
function logfs_magic (line 8) | pub fn logfs_magic() -> Vec<Vec<u8>> {
function logfs_parser (line 13) | pub fn logfs_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/luks.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "LUKS header";
function luks_magic (line 8) | pub fn luks_magic() -> Vec<Vec<u8>> {
function luks_parser (line 13) | pub fn luks_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/lz4.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "LZ4 compressed data";
function lz4_magic (line 9) | pub fn lz4_magic() -> Vec<Vec<u8>> {
function lz4_parser (line 14) | pub fn lz4_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function get_lz4_data_size (line 54) | fn get_lz4_data_size(lz4_data: &[u8], checksum_present: bool) -> Result<...
FILE: src/signatures/lzfse.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "LZFSE compressed data";
function lzfse_magic (line 9) | pub fn lzfse_magic() -> Vec<Vec<u8>> {
function lzfse_parser (line 19) | pub fn lzfse_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/lzma.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "LZMA compressed data";
function lzma_magic (line 9) | pub fn lzma_magic() -> Vec<Vec<u8>> {
function lzma_parser (line 48) | pub fn lzma_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/lzop.rs
constant DESCRIPTION (line 8) | pub const DESCRIPTION: &str = "LZO compressed data";
function lzop_magic (line 11) | pub fn lzop_magic() -> Vec<Vec<u8>> {
function lzop_parser (line 16) | pub fn lzop_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
function get_lzo_data_size (line 44) | fn get_lzo_data_size(
FILE: src/signatures/matter_ota.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Matter OTA firmware";
function matter_ota_magic (line 8) | pub fn matter_ota_magic() -> Vec<Vec<u8>> {
function matter_ota_parser (line 13) | pub fn matter_ota_parser(
FILE: src/signatures/mbr.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "DOS Master Boot Record";
constant MAGIC_OFFSET (line 9) | pub const MAGIC_OFFSET: usize = 0x01FE;
function mbr_magic (line 12) | pub fn mbr_magic() -> Vec<Vec<u8>> {
function mbr_parser (line 17) | pub fn mbr_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/mh01.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "D-Link MH01 firmware image";
function mh01_magic (line 9) | pub fn mh01_magic() -> Vec<Vec<u8>> {
function mh01_parser (line 14) | pub fn mh01_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/ntfs.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "NTFS partition";
function ntfs_magic (line 8) | pub fn ntfs_magic() -> Vec<Vec<u8>> {
function ntfs_parser (line 13) | pub fn ntfs_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/openssl.rs
constant DESCRIPTION (line 8) | pub const DESCRIPTION: &str = "OpenSSL encryption";
function openssl_crypt_magic (line 11) | pub fn openssl_crypt_magic() -> Vec<Vec<u8>> {
function openssl_crypt_parser (line 16) | pub fn openssl_crypt_parser(
function is_salt_invalid (line 47) | fn is_salt_invalid(salt: usize) -> bool {
FILE: src/signatures/packimg.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "PackImg firmware header";
function packimg_magic (line 8) | pub fn packimg_magic() -> Vec<Vec<u8>> {
function packimg_parser (line 13) | pub fn packimg_parser(file_data: &[u8], offset: usize) -> Result<Signatu...
FILE: src/signatures/pcap.rs
constant PCAPNG_DESCRIPTION (line 5) | pub const PCAPNG_DESCRIPTION: &str = "Pcap-NG capture file";
function pcapng_magic (line 8) | pub fn pcapng_magic() -> Vec<Vec<u8>> {
function pcapng_parser (line 13) | pub fn pcapng_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
FILE: src/signatures/pchrom.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Intel serial flash for PCH ROM";
function pch_rom_magic (line 8) | pub fn pch_rom_magic() -> Vec<Vec<u8>> {
function pch_rom_parser (line 13) | pub fn pch_rom_parser(file_data: &[u8], offset: usize) -> Result<Signatu...
FILE: src/signatures/pdf.rs
constant DESCRIPTION (line 4) | pub const DESCRIPTION: &str = "PDF document";
function pdf_magic (line 7) | pub fn pdf_magic() -> Vec<Vec<u8>> {
function pdf_parser (line 13) | pub fn pdf_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/pe.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Windows PE binary";
function pe_magic (line 8) | pub fn pe_magic() -> Vec<Vec<u8>> {
function pe_parser (line 20) | pub fn pe_parser(file_data: &[u8], offset: usize) -> Result<SignatureRes...
FILE: src/signatures/pem.rs
constant PEM_PUBLIC_KEY_DESCRIPTION (line 7) | pub const PEM_PUBLIC_KEY_DESCRIPTION: &str = "PEM public key";
constant PEM_PRIVATE_KEY_DESCRIPTION (line 8) | pub const PEM_PRIVATE_KEY_DESCRIPTION: &str = "PEM private key";
constant PEM_CERTIFICATE_DESCRIPTION (line 9) | pub const PEM_CERTIFICATE_DESCRIPTION: &str = "PEM certificate";
function pem_public_key_magic (line 12) | pub fn pem_public_key_magic() -> Vec<Vec<u8>> {
function pem_private_key_magic (line 22) | pub fn pem_private_key_magic() -> Vec<Vec<u8>> {
function pem_certificate_magic (line 36) | pub fn pem_certificate_magic() -> Vec<Vec<u8>> {
function pem_parser (line 41) | pub fn pem_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function decode_pem_data (line 109) | fn decode_pem_data(pem_file_data: &[u8]) -> Result<usize, SignatureError> {
FILE: src/signatures/pjl.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "HP Printer Job Language data";
function pjl_magic (line 8) | pub fn pjl_magic() -> Vec<Vec<u8>> {
function pjl_parser (line 13) | pub fn pjl_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/pkcs_der.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "PKCS DER hash";
function der_hash_lookups (line 8) | fn der_hash_lookups() -> HashMap<String, Vec<u8>> {
function der_hash_magic (line 36) | pub fn der_hash_magic() -> Vec<Vec<u8>> {
function der_hash_parser (line 41) | pub fn der_hash_parser(file_data: &[u8], offset: usize) -> Result<Signat...
FILE: src/signatures/png.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "PNG image";
function png_magic (line 8) | pub fn png_magic() -> Vec<Vec<u8>> {
function png_parser (line 17) | pub fn png_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/qcow.rs
constant DESCRIPTION (line 4) | pub const DESCRIPTION: &str = "QEMU QCOW Image";
function qcow_magic (line 6) | pub fn qcow_magic() -> Vec<Vec<u8>> {
function qcow_parser (line 10) | pub fn qcow_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/qnx.rs
constant IFS_DESCRIPTION (line 5) | pub const IFS_DESCRIPTION: &str = "QNX IFS image";
function qnx_ifs_magic (line 8) | pub fn qnx_ifs_magic() -> Vec<Vec<u8>> {
function qnx_ifs_parser (line 17) | pub fn qnx_ifs_parser(file_data: &[u8], offset: usize) -> Result<Signatu...
FILE: src/signatures/rar.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "RAR archive";
function rar_magic (line 10) | pub fn rar_magic() -> Vec<Vec<u8>> {
function rar_parser (line 15) | pub fn rar_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function get_rar_size (line 46) | fn get_rar_size(file_data: &[u8], rar_version: usize) -> Result<usize, S...
FILE: src/signatures/riff.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "RIFF image";
function riff_magic (line 8) | pub fn riff_magic() -> Vec<Vec<u8>> {
function riff_parser (line 13) | pub fn riff_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/romfs.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "RomFS filesystem";
function romfs_magic (line 9) | pub fn romfs_magic() -> Vec<Vec<u8>> {
function romfs_parser (line 14) | pub fn romfs_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/rsa.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "RSA encrypted session key";
type RSAKeyDefinition (line 10) | struct RSAKeyDefinition {
function rsa_key_definitions (line 26) | fn rsa_key_definitions() -> Vec<RSAKeyDefinition> {
function rsa_magic (line 137) | pub fn rsa_magic() -> Vec<Vec<u8>> {
function rsa_parser (line 148) | pub fn rsa_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
type RSAKeyInfo (line 192) | struct RSAKeyInfo {
function rsa_key_parser (line 201) | fn rsa_key_parser(
FILE: src/signatures/rtk.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "RTK firmware header";
function rtk_magic (line 8) | pub fn rtk_magic() -> Vec<Vec<u8>> {
function rtk_parser (line 13) | pub fn rtk_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/seama.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "SEAMA firmware header";
function seama_magic (line 8) | pub fn seama_magic() -> Vec<Vec<u8>> {
function seama_parser (line 16) | pub fn seama_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/sevenzip.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "7-zip archive data";
function sevenzip_magic (line 9) | pub fn sevenzip_magic() -> Vec<Vec<u8>> {
function sevenzip_parser (line 14) | pub fn sevenzip_parser(file_data: &[u8], offset: usize) -> Result<Signat...
FILE: src/signatures/shrs.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "SHRS encrypted firmware";
function shrs_magic (line 10) | pub fn shrs_magic() -> Vec<Vec<u8>> {
function shrs_parser (line 15) | pub fn shrs_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/squashfs.rs
constant DESCRIPTION (line 10) | pub const DESCRIPTION: &str = "SquashFS file system";
function squashfs_magic (line 13) | pub fn squashfs_magic() -> Vec<Vec<u8>> {
function squashfs_parser (line 26) | pub fn squashfs_parser(file_data: &[u8], offset: usize) -> Result<Signat...
FILE: src/signatures/srec.rs
constant SREC_DESCRIPTION (line 6) | pub const SREC_DESCRIPTION: &str = "Motorola S-record";
constant SREC_SHORT_DESCRIPTION (line 7) | pub const SREC_SHORT_DESCRIPTION: &str = "Motorola S-record (generic)";
function srec_short_magic (line 10) | pub fn srec_short_magic() -> Vec<Vec<u8>> {
function srec_magic (line 15) | pub fn srec_magic() -> Vec<Vec<u8>> {
function srec_parser (line 20) | pub fn srec_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/svg.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "SVG image";
function svg_magic (line 8) | pub fn svg_magic() -> Vec<Vec<u8>> {
function svg_parser (line 13) | pub fn svg_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/tarball.rs
constant TARBALL_BLOCK_SIZE (line 7) | const TARBALL_BLOCK_SIZE: usize = 512;
constant TARBALL_MAGIC_OFFSET (line 8) | const TARBALL_MAGIC_OFFSET: usize = 257;
constant TARBALL_MAGIC_SIZE (line 9) | const TARBALL_MAGIC_SIZE: usize = 5;
constant TARBALL_SIZE_OFFSET (line 10) | const TARBALL_SIZE_OFFSET: usize = 124;
constant TARBALL_SIZE_LEN (line 11) | const TARBALL_SIZE_LEN: usize = 11;
constant TARBALL_UNIVERSAL_MAGIC (line 12) | const TARBALL_UNIVERSAL_MAGIC: &[u8; 5] = b"ustar";
constant TARBALL_MIN_EXPECTED_HEADERS (line 13) | const TARBALL_MIN_EXPECTED_HEADERS: usize = 10;
constant DESCRIPTION (line 16) | pub const DESCRIPTION: &str = "POSIX tar archive";
function tarball_magic (line 19) | pub fn tarball_magic() -> Vec<Vec<u8>> {
function tarball_parser (line 24) | pub fn tarball_parser(file_data: &[u8], offset: usize) -> Result<Signatu...
function header_checksum_is_valid (line 98) | fn header_checksum_is_valid(header_block: &[u8]) -> bool {
function tarball_entry_size (line 118) | fn tarball_entry_size(tarball_entry_data: &[u8]) -> Result<usize, Signat...
function tarball_octal (line 144) | fn tarball_octal(octal_string: &[u8]) -> usize {
FILE: src/signatures/tplink.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "TP-Link firmware header";
function tplink_magic (line 8) | pub fn tplink_magic() -> Vec<Vec<u8>> {
function tplink_parser (line 13) | pub fn tplink_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
constant RTOS_DESCRIPTION (line 41) | pub const RTOS_DESCRIPTION: &str = "TP-Link RTOS firmware";
function tplink_rtos_magic (line 44) | pub fn tplink_rtos_magic() -> Vec<Vec<u8>> {
function tplink_rtos_parser (line 49) | pub fn tplink_rtos_parser(
FILE: src/signatures/trx.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "TRX firmware image";
function trx_magic (line 9) | pub fn trx_magic() -> Vec<Vec<u8>> {
function trx_parser (line 14) | pub fn trx_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
FILE: src/signatures/ubi.rs
constant UBI_FS_DESCRIPTION (line 9) | pub const UBI_FS_DESCRIPTION: &str = "UBIFS image";
constant UBI_IMAGE_DESCRIPTION (line 10) | pub const UBI_IMAGE_DESCRIPTION: &str = "UBI image";
function ubi_magic (line 13) | pub fn ubi_magic() -> Vec<Vec<u8>> {
function ubifs_magic (line 18) | pub fn ubifs_magic() -> Vec<Vec<u8>> {
function ubifs_parser (line 23) | pub fn ubifs_parser(file_data: &[u8], offset: usize) -> Result<Signature...
function ubi_parser (line 44) | pub fn ubi_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
function get_ubi_image_size (line 76) | fn get_ubi_image_size(ubi_data: &[u8]) -> Result<usize, SignatureError> {
FILE: src/signatures/uboot.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "U-Boot version string";
function uboot_magic (line 8) | pub fn uboot_magic() -> Vec<Vec<u8>> {
function uboot_parser (line 13) | pub fn uboot_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/uefi.rs
constant VOLUME_DESCRIPTION (line 5) | pub const VOLUME_DESCRIPTION: &str = "UEFI PI firmware volume";
constant CAPSULE_DESCRIPTION (line 6) | pub const CAPSULE_DESCRIPTION: &str = "UEFI capsule image";
function uefi_volume_magic (line 9) | pub fn uefi_volume_magic() -> Vec<Vec<u8>> {
function uefi_capsule_magic (line 14) | pub fn uefi_capsule_magic() -> Vec<Vec<u8>> {
function uefi_volume_parser (line 23) | pub fn uefi_volume_parser(
function uefi_capsule_parser (line 64) | pub fn uefi_capsule_parser(
FILE: src/signatures/uimage.rs
constant DESCRIPTION (line 9) | pub const DESCRIPTION: &str = "uImage firmware image";
function uimage_magic (line 12) | pub fn uimage_magic() -> Vec<Vec<u8>> {
function uimage_parser (line 22) | pub fn uimage_parser(file_data: &[u8], offset: usize) -> Result<Signatur...
FILE: src/signatures/vxworks.rs
constant SYMTAB_DESCRIPTION (line 6) | pub const SYMTAB_DESCRIPTION: &str = "VxWorks symbol table";
constant WIND_KERNEL_DESCRIPTION (line 7) | pub const WIND_KERNEL_DESCRIPTION: &str = "VxWorks WIND kernel version";
function wind_kernel_magic (line 10) | pub fn wind_kernel_magic() -> Vec<Vec<u8>> {
function symbol_table_magic (line 16) | pub fn symbol_table_magic() -> Vec<Vec<u8>> {
function wind_kernel_parser (line 29) | pub fn wind_kernel_parser(
function symbol_table_parser (line 62) | pub fn symbol_table_parser(
FILE: src/signatures/wince.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "Windows CE binary image";
function wince_magic (line 9) | pub fn wince_magic() -> Vec<Vec<u8>> {
function wince_parser (line 14) | pub fn wince_parser(file_data: &[u8], offset: usize) -> Result<Signature...
FILE: src/signatures/xz.rs
constant DESCRIPTION (line 8) | pub const DESCRIPTION: &str = "XZ compressed data";
function xz_magic (line 11) | pub fn xz_magic() -> Vec<Vec<u8>> {
function xz_parser (line 16) | pub fn xz_parser(file_data: &[u8], offset: usize) -> Result<SignatureRes...
FILE: src/signatures/yaffs.rs
constant MIN_NUMBER_OF_OBJS (line 6) | const MIN_NUMBER_OF_OBJS: usize = 2;
constant DESCRIPTION (line 9) | pub const DESCRIPTION: &str = "YAFFSv2 filesystem";
function yaffs_magic (line 12) | pub fn yaffs_magic() -> Vec<Vec<u8>> {
function yaffs_parser (line 22) | pub fn yaffs_parser(file_data: &[u8], offset: usize) -> Result<Signature...
function get_page_size (line 69) | fn get_page_size(file_data: &[u8]) -> Result<usize, SignatureError> {
function get_spare_size (line 103) | fn get_spare_size(
function get_image_size (line 130) | fn get_image_size(
function get_file_block_count (line 194) | fn get_file_block_count(
FILE: src/signatures/zip.rs
constant DESCRIPTION (line 7) | pub const DESCRIPTION: &str = "ZIP archive";
function zip_magic (line 10) | pub fn zip_magic() -> Vec<Vec<u8>> {
function zip_parser (line 15) | pub fn zip_parser(file_data: &[u8], offset: usize) -> Result<SignatureRe...
type ZipEOCDInfo (line 80) | pub struct ZipEOCDInfo {
function find_zip_eof (line 86) | pub fn find_zip_eof(file_data: &[u8], offset: usize) -> Result<ZipEOCDIn...
FILE: src/signatures/zlib.rs
constant DESCRIPTION (line 5) | pub const DESCRIPTION: &str = "Zlib compressed file";
function zlib_magic (line 8) | pub fn zlib_magic() -> Vec<Vec<u8>> {
function zlib_parser (line 17) | pub fn zlib_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/signatures/zstd.rs
constant DESCRIPTION (line 6) | pub const DESCRIPTION: &str = "ZSTD compressed data";
function zstd_magic (line 9) | pub fn zstd_magic() -> Vec<Vec<u8>> {
function zstd_parser (line 14) | pub fn zstd_parser(file_data: &[u8], offset: usize) -> Result<SignatureR...
FILE: src/structures/android_bootimg.rs
type AndroidBootImageHeader (line 5) | pub struct AndroidBootImageHeader {
function parse_android_bootimg_header (line 13) | pub fn parse_android_bootimg_header(
FILE: src/structures/androidsparse.rs
type AndroidSparseHeader (line 5) | pub struct AndroidSparseHeader {
function parse_android_sparse_header (line 14) | pub fn parse_android_sparse_header(
type AndroidSparseChunkHeader (line 66) | pub struct AndroidSparseChunkHeader {
function parse_android_sparse_chunk_header (line 77) | pub fn parse_android_sparse_chunk_header(
FILE: src/structures/apfs.rs
constant MAGIC_OFFSET (line 4) | pub const MAGIC_OFFSET: usize = 0x20;
type APFSHeader (line 8) | pub struct APFSHeader {
function parse_apfs_header (line 14) | pub fn parse_apfs_header(apfs_data: &[u8]) -> Result<APFSHeader, Structu...
FILE: src/structures/arj.rs
type ARJHeader (line 6) | pub struct ARJHeader {
function parse_arj_header (line 20) | pub fn parse_arj_header(arj_data: &[u8]) -> Result<ARJHeader, StructureE...
FILE: src/structures/autel.rs
type AutelECCHeader (line 6) | pub struct AutelECCHeader {
function parse_autel_header (line 12) | pub fn parse_autel_header(autel_data: &[u8]) -> Result<AutelECCHeader, S...
FILE: src/structures/binhdr.rs
type BINHeader (line 6) | pub struct BINHeader {
function parse_bin_header (line 14) | pub fn parse_bin_header(bin_hdr_data: &[u8]) -> Result<BINHeader, Struct...
FILE: src/structures/bmp.rs
type BMPFileHeader (line 4) | pub struct BMPFileHeader {
function parse_bmp_file_header (line 9) | pub fn parse_bmp_file_header(bmp_data: &[u8]) -> Result<BMPFileHeader, S...
function get_dib_header_size (line 54) | pub fn get_dib_header_size(bmp_data: &[u8]) -> Result<usize, StructureEr...
FILE: src/structures/btrfs.rs
type BTRFSHeader (line 6) | pub struct BTRFSHeader {
function parse_btrfs_header (line 16) | pub fn parse_btrfs_header(btrfs_data: &[u8]) -> Result<BTRFSHeader, Stru...
FILE: src/structures/cab.rs
type CabinetHeader (line 5) | pub struct CabinetHeader {
function parse_cab_header (line 13) | pub fn parse_cab_header(header_data: &[u8]) -> Result<CabinetHeader, Str...
FILE: src/structures/chk.rs
type CHKHeader (line 6) | pub struct CHKHeader {
function parse_chk_header (line 14) | pub fn parse_chk_header(header_data: &[u8]) -> Result<CHKHeader, Structu...
FILE: src/structures/common.rs
type StructureError (line 14) | pub struct StructureError;
function parse (line 55) | pub fn parse(
function size (line 160) | pub fn size(structure: &Vec<(&str, &str)>) -> usize {
function type_to_size (line 176) | fn type_to_size(ctype: &str) -> Option<usize> {
FILE: src/structures/cpio.rs
constant CPIO_HEADER_SIZE (line 4) | pub const CPIO_HEADER_SIZE: usize = 110;
type CPIOEntryHeader (line 8) | pub struct CPIOEntryHeader {
function parse_cpio_entry_header (line 16) | pub fn parse_cpio_entry_header(cpio_data: &[u8]) -> Result<CPIOEntryHead...
function byte_padding (line 76) | fn byte_padding(n: usize) -> usize {
FILE: src/structures/cramfs.rs
type CramFSHeader (line 5) | pub struct CramFSHeader {
function parse_cramfs_header (line 13) | pub fn parse_cramfs_header(cramfs_data: &[u8]) -> Result<CramFSHeader, S...
FILE: src/structures/csman.rs
type CSManHeader (line 5) | pub struct CSManHeader {
function parse_csman_header (line 13) | pub fn parse_csman_header(csman_data: &[u8]) -> Result<CSManHeader, Stru...
type CSManEntry (line 71) | pub struct CSManEntry {
function parse_csman_entry (line 79) | pub fn parse_csman_entry(
FILE: src/structures/deb.rs
type DebHeader (line 5) | pub struct DebHeader {
function parse_deb_header (line 10) | pub fn parse_deb_header(deb_data: &[u8]) -> Result<DebHeader, StructureE...
FILE: src/structures/dkbs.rs
type DKBSHeader (line 6) | pub struct DKBSHeader {
function parse_dkbs_header (line 16) | pub fn parse_dkbs_header(dkbs_data: &[u8]) -> Result<DKBSHeader, Structu...
FILE: src/structures/dlink_tlv.rs
type DlinkTLVHeader (line 6) | pub struct DlinkTLVHeader {
function parse_dlink_tlv_header (line 15) | pub fn parse_dlink_tlv_header(tlv_data: &[u8]) -> Result<DlinkTLVHeader,...
FILE: src/structures/dlob.rs
type DlobHeader (line 5) | pub struct DlobHeader {
function parse_dlob_header (line 11) | pub fn parse_dlob_header(dlob_data: &[u8]) -> Result<DlobHeader, Structu...
FILE: src/structures/dmg.rs
type DMGFooter (line 5) | pub struct DMGFooter {
function parse_dmg_footer (line 12) | pub fn parse_dmg_footer(dmg_data: &[u8]) -> Result<DMGFooter, StructureE...
FILE: src/structures/dms.rs
type DMSHeader (line 5) | pub struct DMSHeader {
function parse_dms_header (line 10) | pub fn parse_dms_header(dms_data: &[u8]) -> Result<DMSHeader, StructureE...
FILE: src/structures/dpapi.rs
type DPAPIBlobHeader (line 38) | pub struct DPAPIBlobHeader {
function parse_dpapi_blob_header (line 59) | pub fn parse_dpapi_blob_header(dpapi_blob_data: &[u8]) -> Result<DPAPIBl...
function utf8_to_utf16 (line 163) | fn utf8_to_utf16(byte_array: &[u8]) -> Option<Vec<u16>> {
FILE: src/structures/dtb.rs
type DTBHeader (line 6) | pub struct DTBHeader {
function parse_dtb_header (line 17) | pub fn parse_dtb_header(dtb_data: &[u8]) -> Result<DTBHeader, StructureE...
type DTBNode (line 74) | pub struct DTBNode {
function parse_dtb_node (line 86) | pub fn parse_dtb_node(dtb_header: &DTBHeader, dtb_data: &[u8], node_offs...
function dtb_aligned (line 153) | fn dtb_aligned(len: usize) -> usize {
FILE: src/structures/dxbc.rs
type DXBCHeader (line 4) | pub struct DXBCHeader {
function parse_dxbc_header (line 10) | pub fn parse_dxbc_header(data: &[u8]) -> Result<DXBCHeader, StructureErr...
FILE: src/structures/efigpt.rs
constant BLOCK_SIZE (line 4) | const BLOCK_SIZE: usize = 512;
type EFIGPTHeader (line 8) | pub struct EFIGPTHeader {
function parse_efigpt_header (line 13) | pub fn parse_efigpt_header(efi_data: &[u8]) -> Result<EFIGPTHeader, Stru...
type GPTPartitionEntry (line 101) | struct GPTPartitionEntry {
function parse_gpt_partition_entry (line 107) | fn parse_gpt_partition_entry(entry_data: &[u8]) -> Option<GPTPartitionEn...
function lba_to_offset (line 135) | fn lba_to_offset(lba: usize) -> usize {
FILE: src/structures/elf.rs
type ELFHeader (line 6) | pub struct ELFHeader {
function parse_elf_header (line 15) | pub fn parse_elf_header(elf_data: &[u8]) -> Result<ELFHeader, StructureE...
FILE: src/structures/ext.rs
constant SUPERBLOCK_SIZE (line 5) | pub const SUPERBLOCK_SIZE: usize = 1024;
constant SUPERBLOCK_OFFSET (line 8) | pub const SUPERBLOCK_OFFSET: usize = 1024;
type EXTHeader (line 12) | pub struct EXTHeader {
function parse_ext_header (line 23) | pub fn parse_ext_header(ext_data: &[u8]) -> Result<EXTHeader, StructureE...
FILE: src/structures/fat.rs
type FATHeader (line 5) | pub struct FATHeader {
function parse_fat_header (line 11) | pub fn parse_fat_header(fat_data: &[u8]) -> Result<FATHeader, StructureE...
FILE: src/structures/gif.rs
type GIFHeader (line 6) | pub struct GIFHeader {
function parse_gif_header (line 13) | pub fn parse_gif_header(gif_data: &[u8]) -> Result<GIFHeader, StructureE...
type GIFFlags (line 41) | pub struct GIFFlags {
function parse_gif_flags (line 47) | fn parse_gif_flags(flags: usize) -> GIFFlags {
function parse_gif_image_descriptor (line 64) | pub fn parse_gif_image_descriptor(gif_data: &[u8]) -> Result<usize, Stru...
function parse_gif_sub_blocks (line 100) | fn parse_gif_sub_blocks(sub_block_data: &[u8]) -> Result<usize, Structur...
function parse_gif_extension (line 126) | pub fn parse_gif_extension(extension_data: &[u8]) -> Result<usize, Struc...
FILE: src/structures/gzip.rs
type GzipHeader (line 7) | pub struct GzipHeader {
function parse_gzip_header (line 16) | pub fn parse_gzip_header(header_data: &[u8]) -> Result<GzipHeader, Struc...
FILE: src/structures/iso9660.rs
type ISOHeader (line 5) | pub struct ISOHeader {
function parse_iso_header (line 10) | pub fn parse_iso_header(iso_data: &[u8]) -> Result<ISOHeader, StructureE...
FILE: src/structures/jboot.rs
type JBOOTArmHeader (line 7) | pub struct JBOOTArmHeader {
function parse_jboot_arm_header (line 17) | pub fn parse_jboot_arm_header(jboot_data: &[u8]) -> Result<JBOOTArmHeade...
type JBOOTStagHeader (line 93) | pub struct JBOOTStagHeader {
function parse_jboot_stag_header (line 101) | pub fn parse_jboot_stag_header(jboot_data: &[u8]) -> Result<JBOOTStagHea...
type JBOOTSchHeader (line 138) | pub struct JBOOTSchHeader {
function parse_jboot_sch2_header (line 147) | pub fn parse_jboot_sch2_header(jboot_data: &[u8]) -> Result<JBOOTSchHead...
function sch2_header_crc (line 198) | fn sch2_header_crc(sch2_header_bytes: &[u8]) -> usize {
FILE: src/structures/jffs2.rs
constant JFFS2_NODE_STRUCT_SIZE (line 5) | pub const JFFS2_NODE_STRUCT_SIZE: usize = 12;
type JFFS2Node (line 9) | pub struct JFFS2Node {
function parse_jffs2_node_header (line 16) | pub fn parse_jffs2_node_header(node_data: &[u8]) -> Result<JFFS2Node, St...
function jffs2_node_crc (line 70) | fn jffs2_node_crc(file_data: &[u8]) -> usize {
FILE: src/structures/linux.rs
type LinuxARM64BootHeader (line 5) | pub struct LinuxARM64BootHeader {
type LinuxARMzImageHeader (line 13) | pub struct LinuxARMzImageHeader {
function parse_linux_arm_zimage_header (line 18) | pub fn parse_linux_arm_zimage_header(
function parse_linux_arm64_boot_image_header (line 62) | pub fn parse_linux_arm64_boot_image_header(
FILE: src/structures/logfs.rs
constant LOGFS_MAGIC_OFFSET (line 4) | pub const LOGFS_MAGIC_OFFSET: usize = 0x18;
type LogFSSuperBlock (line 8) | pub struct LogFSSuperBlock {
function parse_logfs_super_block (line 13) | pub fn parse_logfs_super_block(logfs_data: &[u8]) -> Result<LogFSSuperBl...
FILE: src/structures/luks.rs
type LUKSHeader (line 6) | pub struct LUKSHeader {
function parse_luks_header (line 15) | pub fn parse_luks_header(luks_data: &[u8]) -> Result<LUKSHeader, Structu...
FILE: src/structures/lz4.rs
type LZ4FileHeader (line 6) | pub struct LZ4FileHeader {
function parse_lz4_file_header (line 13) | pub fn parse_lz4_file_header(lz4_data: &[u8]) -> Result<LZ4FileHeader, S...
type LZ4BlockHeader (line 89) | pub struct LZ4BlockHeader {
function parse_lz4_block_header (line 97) | pub fn parse_lz4_block_header(
FILE: src/structures/lzfse.rs
type LZFSEBlock (line 5) | pub struct LZFSEBlock {
function parse_lzfse_block_header (line 12) | pub fn parse_lzfse_block_header(lzfse_data: &[u8]) -> Result<LZFSEBlock,...
function parse_endofstream_block_header (line 45) | fn parse_endofstream_block_header(_lzfse_data: &[u8]) -> Result<LZFSEBlo...
function parse_uncompressed_block_header (line 55) | fn parse_uncompressed_block_header(lzfse_data: &[u8]) -> Result<LZFSEBlo...
function parse_compressedv1_block_header (line 72) | fn parse_compressedv1_block_header(lzfse_data: &[u8]) -> Result<LZFSEBlo...
function parse_compressedv2_block_header (line 104) | fn parse_compressedv2_block_header(lzfse_data: &[u8]) -> Result<LZFSEBlo...
function parse_compressedlzvn_block_header (line 136) | fn parse_compressedlzvn_block_header(lzfse_data: &[u8]) -> Result<LZFSEB...
FILE: src/structures/lzma.rs
type LZMAHeader (line 5) | pub struct LZMAHeader {
function parse_lzma_header (line 12) | pub fn parse_lzma_header(lzma_data: &[u8]) -> Result<LZMAHeader, Structu...
FILE: src/structures/lzop.rs
constant LZO_CHECKSUM_SIZE (line 4) | const LZO_CHECKSUM_SIZE: usize = 4;
type LZOPFileHeader (line 8) | pub struct LZOPFileHeader {
function parse_lzop_file_header (line 14) | pub fn parse_lzop_file_header(lzop_data: &[u8]) -> Result<LZOPFileHeader...
type LZOPBlockHeader (line 100) | pub struct LZOPBlockHeader {
function parse_lzop_block_header (line 108) | pub fn parse_lzop_block_header(
function parse_lzop_eof_marker (line 151) | pub fn parse_lzop_eof_marker(eof_data: &[u8]) -> Result<usize, Structure...
FILE: src/structures/matter_ota.rs
type MatterOTAHeader (line 8) | pub struct MatterOTAHeader {
type Value (line 20) | enum Value {
type Element (line 29) | struct Element {
function parse_matter_ota_header (line 35) | pub fn parse_matter_ota_header(ota_data: &[u8]) -> Result<MatterOTAHeade...
function parse_tlv_element (line 93) | fn parse_tlv_element(data: &[u8]) -> Result<(Element, usize), StructureE...
function parse_tlv_header (line 193) | fn parse_tlv_header(data: &[u8]) -> Result<HashMap<String, Value>, Struc...
FILE: src/structures/mbr.rs
type MBRPartition (line 6) | pub struct MBRPartition {
type MBRHeader (line 14) | pub struct MBRHeader {
function parse_mbr_image (line 20) | pub fn parse_mbr_image(mbr_data: &[u8]) -> Result<MBRHeader, StructureEr...
FILE: src/structures/mh01.rs
type MH01Header (line 6) | pub struct MH01Header {
function parse_mh01_header (line 18) | pub fn parse_mh01_header(mh01_data: &[u8]) -> Result<MH01Header, Structu...
FILE: src/structures/ntfs.rs
type NTFSPartition (line 5) | pub struct NTFSPartition {
function parse_ntfs_header (line 11) | pub fn parse_ntfs_header(ntfs_data: &[u8]) -> Result<NTFSPartition, Stru...
FILE: src/structures/openssl.rs
type OpenSSLCryptHeader (line 4) | pub struct OpenSSLCryptHeader {
function parse_openssl_crypt_header (line 9) | pub fn parse_openssl_crypt_header(ssl_data: &[u8]) -> Result<OpenSSLCryp...
FILE: src/structures/packimg.rs
type PackIMGHeader (line 4) | pub struct PackIMGHeader {
function parse_packimg_header (line 10) | pub fn parse_packimg_header(packimg_data: &[u8]) -> Result<PackIMGHeader...
FILE: src/structures/pcap.rs
type PcapBlock (line 6) | pub struct PcapBlock {
function parse_pcapng_block (line 12) | pub fn parse_pcapng_block(
type PcapSectionBlock (line 58) | pub struct PcapSectionBlock {
function parse_pcapng_section_block (line 64) | pub fn parse_pcapng_section_block(block_data: &[u8]) -> Result<PcapSecti...
FILE: src/structures/pchrom.rs
type PCHRomHeader (line 5) | pub struct PCHRomHeader {
function parse_pchrom_header (line 11) | pub fn parse_pchrom_header(pch_data: &[u8]) -> Result<PCHRomHeader, Stru...
function get_pch_regions_size (line 60) | fn get_pch_regions_size(
FILE: src/structures/pe.rs
type PEHeader (line 5) | pub struct PEHeader {
function parse_pe_header (line 10) | pub fn parse_pe_header(pe_data: &[u8]) -> Result<PEHeader, StructureErro...
FILE: src/structures/png.rs
type PNGChunkHeader (line 4) | pub struct PNGChunkHeader {
function parse_png_chunk_header (line 10) | pub fn parse_png_chunk_header(chunk_data: &[u8]) -> Result<PNGChunkHeade...
FILE: src/structures/qcow.rs
type QcowHeader (line 6) | pub struct QcowHeader {
function parse_qcow_header (line 13) | pub fn parse_qcow_header(qcow_data: &[u8]) -> Result<QcowHeader, Structu...
FILE: src/structures/qnx.rs
type IFSHeader (line 4) | pub struct IFSHeader {
function parse_ifs_header (line 9) | pub fn parse_ifs_header(ifs_data: &[u8]) -> Result<IFSHeader, StructureE...
FILE: src/structures/rar.rs
type RarArchiveHeader (line 6) | pub struct RarArchiveHeader {
function parse_rar_archive_header (line 11) | pub fn parse_rar_archive_header(rar_data: &[u8]) -> Result<RarArchiveHea...
FILE: src/structures/riff.rs
type RIFFHeader (line 4) | pub struct RIFFHeader {
function parse_riff_header (line 10) | pub fn parse_riff_header(riff_data: &[u8]) -> Result<RIFFHeader, Structu...
FILE: src/structures/romfs.rs
type RomFSHeader (line 6) | pub struct RomFSHeader {
function parse_romfs_header (line 13) | pub fn parse_romfs_header(romfs_data: &[u8]) -> Result<RomFSHeader, Stru...
type RomFSFileHeader (line 56) | pub struct RomFSFileHeader {
function parse_romfs_file_entry (line 77) | pub fn parse_romfs_file_entry(romfs_data: &[u8]) -> Result<RomFSFileHead...
function romfs_align (line 151) | fn romfs_align(x: usize) -> usize {
function romfs_crc_valid (line 165) | fn romfs_crc_valid(crc_data: &[u8]) -> bool {
FILE: src/structures/rtk.rs
type RTKHeader (line 5) | pub struct RTKHeader {
function parse_rtk_header (line 11) | pub fn parse_rtk_header(rtk_data: &[u8]) -> Result<RTKHeader, StructureE...
FILE: src/structures/seama.rs
type SeamaHeader (line 4) | pub struct SeamaHeader {
function parse_seama_header (line 10) | pub fn parse_seama_header(seama_data: &[u8]) -> Result<SeamaHeader, Stru...
FILE: src/structures/sevenzip.rs
type SevenZipHeader (line 6) | pub struct SevenZipHeader {
function parse_7z_header (line 16) | pub fn parse_7z_header(sevenzip_data: &[u8]) -> Result<SevenZipHeader, S...
FILE: src/structures/shrs.rs
type SHRSHeader (line 5) | pub struct SHRSHeader {
function parse_shrs_header (line 12) | pub fn parse_shrs_header(shrs_data: &[u8]) -> Result<SHRSHeader, Structu...
FILE: src/structures/squashfs.rs
type SquashFSHeader (line 6) | pub struct SquashFSHeader {
function parse_squashfs_header (line 20) | pub fn parse_squashfs_header(sqsh_data: &[u8]) -> Result<SquashFSHeader,...
function parse_squashfs_uid_entry (line 182) | pub fn parse_squashfs_uid_entry(
FILE: src/structures/svg.rs
constant SVG_OPEN_TAG (line 4) | const SVG_OPEN_TAG: &[u8] = b"<svg ";
constant SVG_CLOSE_TAG (line 5) | const SVG_CLOSE_TAG: &[u8] = b"</svg>";
constant SVG_HEAD_MAGIC (line 6) | const SVG_HEAD_MAGIC: &str = "xmlns=\"http://www.w3.org/2000/svg\"";
type SVGImage (line 10) | pub struct SVGImage {
function parse_svg_image (line 15) | pub fn parse_svg_image(svg_data: &[u8]) -> Result<SVGImage, StructureErr...
type SVGTag (line 65) | struct SVGTag {
function parse_svg_tag (line 72) | fn parse_svg_tag(tag_data: &[u8]) -> Result<SVGTag, StructureError> {
FILE: src/structures/tplink.rs
type TPLinkFirmwareHeader (line 5) | pub struct TPLinkFirmwareHeader {
function parse_tplink_header (line 12) | pub fn parse_tplink_header(tplink_data: &[u8]) -> Result<TPLinkFirmwareH...
type TPLinkRTOSFirmwareHeader (line 75) | pub struct TPLinkRTOSFirmwareHeader {
function parse_tplink_rtos_header (line 84) | pub fn parse_tplink_rtos_header(
FILE: src/structures/trx.rs
type TRXHeader (line 5) | pub struct TRXHeader {
function parse_trx_header (line 14) | pub fn parse_trx_header(header_data: &[u8]) -> Result<TRXHeader, Structu...
FILE: src/structures/ubi.rs
type UbiSuperBlockHeader (line 6) | pub struct UbiSuperBlockHeader {
function parse_ubi_superblock_header (line 12) | pub fn parse_ubi_superblock_header(ubi_data: &[u8]) -> Result<UbiSuperBl...
type UbiECHeader (line 84) | pub struct UbiECHeader {
function parse_ubi_ec_header (line 91) | pub fn parse_ubi_ec_header(ubi_data: &[u8]) -> Result<UbiECHeader, Struc...
type UbiVolumeHeader (line 134) | pub struct UbiVolumeHeader;
function parse_ubi_volume_header (line 137) | pub fn parse_ubi_volume_header(ubi_data: &[u8]) -> Result<UbiVolumeHeade...
function ubi_crc (line 182) | fn ubi_crc(data: &[u8]) -> usize {
FILE: src/structures/uefi.rs
type UEFIVolumeHeader (line 5) | pub struct UEFIVolumeHeader {
function parse_uefi_volume_header (line 12) | pub fn parse_uefi_volume_header(uefi_data: &[u8]) -> Result<UEFIVolumeHe...
type UEFICapsuleHeader (line 51) | pub struct UEFICapsuleHeader {
function parse_uefi_capsule_header (line 57) | pub fn parse_uefi_capsule_header(uefi_data: &[u8]) -> Result<UEFICapsule...
FILE: src/structures/uimage.rs
type UImageHeader (line 7) | pub struct UImageHeader {
function parse_uimage_header (line 23) | pub fn parse_uimage_header(uimage_data: &[u8]) -> Result<UImageHeader, S...
function calculate_uimage_header_checksum (line 193) | fn calculate_uimage_header_checksum(hdr: &[u8]) -> usize {
FILE: src/structures/vxworks.rs
type VxWorksSymbolTableEntry (line 7) | pub struct VxWorksSymbolTableEntry {
function parse_symtab_entry (line 15) | pub fn parse_symtab_entry(
function get_symtab_endianness (line 56) | pub fn get_symtab_endianness(symbol_data: &[u8]) -> Result<String, Struc...
FILE: src/structures/wince.rs
type WinCEHeader (line 5) | pub struct WinCEHeader {
function parse_wince_header (line 12) | pub fn parse_wince_header(wince_data: &[u8]) -> Result<WinCEHeader, Stru...
type WinCEBlock (line 34) | pub struct WinCEBlock {
function parse_wince_block_header (line 41) | pub fn parse_wince_block_header(block_data: &[u8]) -> Result<WinCEBlock,...
FILE: src/structures/xz.rs
function parse_xz_header (line 5) | pub fn parse_xz_header(xz_data: &[u8]) -> Result<usize, StructureError> {
FILE: src/structures/yaffs.rs
type YAFFSObject (line 5) | pub struct YAFFSObject {
function parse_yaffs_obj_header (line 11) | pub fn parse_yaffs_obj_header(
type YAFFSFileHeader (line 46) | pub struct YAFFSFileHeader {
function parse_yaffs_file_header (line 52) | pub fn parse_yaffs_file_header(
FILE: src/structures/zip.rs
type ZipFileHeader (line 4) | pub struct ZipFileHeader {
function parse_zip_header (line 13) | pub fn parse_zip_header(zip_data: &[u8]) -> Result<ZipFileHeader, Struct...
type ZipEOCDHeader (line 91) | pub struct ZipEOCDHeader {
function parse_eocd_header (line 97) | pub fn parse_eocd_header(eocd_data: &[u8]) -> Result<ZipEOCDHeader, Stru...
FILE: src/structures/zstd.rs
type ZSTDHeader (line 5) | pub struct ZSTDHeader {
function parse_zstd_header (line 14) | pub fn parse_zstd_header(zstd_data: &[u8]) -> Result<ZSTDHeader, Structu...
type ZSTDBlockHeader (line 60) | pub struct ZSTDBlockHeader {
function parse_block_header (line 68) | pub fn parse_block_header(block_data: &[u8]) -> Result<ZSTDBlockHeader, ...
FILE: tests/arcadyan.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/arj.rs
function integration_test_valid_arj (line 6) | fn integration_test_valid_arj() {
FILE: tests/bmp.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/bzip2.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/common/mod.rs
function integration_test (line 6) | pub fn integration_test(signature_filter: &str, file_name: &str) {
function assert_results_ok (line 22) | pub fn assert_results_ok(
function run_binwalk (line 41) | pub fn run_binwalk(signature_filter: &str, file_name: &str) -> AnalysisR...
FILE: tests/cramfs.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/gzip.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/jpeg.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/matter_ota.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/mbr.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/pdf.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/png.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/qcow.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/riff.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/romfs.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/sevenzip.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/squashfs.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/squashfs_v2.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/yaffs2.rs
function integration_test (line 4) | fn integration_test() {
FILE: tests/zip.rs
function integration_test_valid_zip (line 4) | fn integration_test_valid_zip() {
FILE: tests/zip_truncated.rs
function integration_test_truncated_zip (line 4) | fn integration_test_truncated_zip() {
Condensed preview — 270 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (896K chars).
[
{
"path": ".dockerignore",
"chars": 34,
"preview": "target\nDockerfile\nbuild_docker.sh\n"
},
{
"path": ".gitattributes",
"chars": 57,
"preview": "* text eol=lf\n*.md text eol=lf\n*.png binary\n*.bin binary\n"
},
{
"path": ".github/workflows/quality.yaml",
"chars": 1514,
"preview": "name: Quality Checks\non:\n pull_request:\n branches:\n - \"*\"\n\njobs:\n lychee-link-check:\n name: Link check\n "
},
{
"path": ".github/workflows/rust-release-binary.yml",
"chars": 2553,
"preview": "name: Release Build\n\non:\n push:\n tags:\n - 'v*'\njobs:\n release:\n name: Release - ${{ matrix.platform.os-name"
},
{
"path": ".gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": "CARGO_README.md",
"chars": 551,
"preview": "# binwalk\n\nA Rust implementation of the Binwalk firmware analysis tool.\n\n## System Requirements\n\nBuilding requires the f"
},
{
"path": "Cargo.toml",
"chars": 1277,
"preview": "[package]\nname = \"binwalk\"\nversion = \"3.1.1\"\nedition = \"2024\"\nauthors = [\"Craig Heffner <heffnercj@gmail.com>\"]\nlicense "
},
{
"path": "Dockerfile",
"chars": 4217,
"preview": "## Scratch build stage\nFROM ubuntu:25.04 AS build\n\nARG BUILD_DIR=\"/tmp\"\nARG BINWALK_BUILD_DIR=\"${BUILD_DIR}/binwalk\"\nARG"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2024 devttys0\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
},
{
"path": "README.md",
"chars": 1470,
"preview": "# Binwalk v3\n\nThis is an updated version of the Binwalk firmware analysis tool, re-written in Rust for speed and accurac"
},
{
"path": "build_docker.sh",
"chars": 84,
"preview": "#!/usr/bin/env bash\n\ndocker build --build-arg SCRIPT_DIRECTORY=$PWD -t binwalkv3 .\n\n"
},
{
"path": "dependencies/README.md",
"chars": 763,
"preview": "# Binwalk Dependencies\n\nThese scripts install the required Binwalk build and runtime system dependencies, except for the"
},
{
"path": "dependencies/pip.sh",
"chars": 318,
"preview": "#!/bin/bash\n# Install pip dependencies.\n# Requires that pip3 is already installed.\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_"
},
{
"path": "dependencies/requirements.txt",
"chars": 95,
"preview": "uefi_firmware\njefferson\nubi-reader\nlz4\nzstandard\ngit+https://github.com/marin-m/vmlinux-to-elf\n"
},
{
"path": "dependencies/src.sh",
"chars": 617,
"preview": "#!/bin/bash\n# Install dependencies from source.\n# Requires that git and build tools (make, gcc, etc) are already install"
},
{
"path": "dependencies/ubuntu.sh",
"chars": 1060,
"preview": "#!/bin/bash\n\n# Get the path to this script's directory, regardless of where it is run from\nSCRIPT_DIRECTORY=$(dirname --"
},
{
"path": "fuzzing/Cargo.toml",
"chars": 112,
"preview": "[package]\nname = \"fuzz\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\nafl = \"*\"\nbinwalk = { path = \"../\" }\n"
},
{
"path": "fuzzing/README.md",
"chars": 734,
"preview": "# Fuzzing Binwalk\n\nFuzz testing for Binwalk is done through [AFL++](https://aflplus.plus).\n\nAt the moment code coverage "
},
{
"path": "fuzzing/src/main.rs",
"chars": 289,
"preview": "use afl::fuzz;\nuse binwalk::Binwalk;\n\nfn main() {\n // AFL makes this real simple...\n fuzz!(|data: &[u8]| {\n "
},
{
"path": "scripts/binwalk-ui",
"chars": 1062,
"preview": "#! /bin/bash -\n# https://unix.stackexchange.com/questions/290696/display-stdout-and-stderr-in-two-separate-streams\n# \n# "
},
{
"path": "src/binwalk.rs",
"chars": 37739,
"preview": "//! Primary Binwalk interface.\n\nuse aho_corasick::AhoCorasick;\nuse log::{debug, error, info, warn};\nuse serde::{Deserial"
},
{
"path": "src/cliparser.rs",
"chars": 2139,
"preview": "use clap::{CommandFactory, Parser};\n\n#[derive(Debug, Parser)]\n#[command(author, version, about, long_about = None)]\npub "
},
{
"path": "src/common.rs",
"chars": 5961,
"preview": "//! Common Functions\nuse chrono::prelude::DateTime;\nuse log::{debug, error};\nuse std::fs::File;\nuse std::io::Read;\n\n/// "
},
{
"path": "src/display.rs",
"chars": 11586,
"preview": "use crate::binwalk::AnalysisResults;\nuse crate::extractors;\nuse crate::signatures;\nuse colored::ColoredString;\nuse color"
},
{
"path": "src/entropy.rs",
"chars": 2948,
"preview": "use crate::common::read_input;\nuse entropy::shannon_entropy;\nuse plotly::layout::{Axis, Layout};\nuse plotly::{ImageForma"
},
{
"path": "src/extractors/androidsparse.rs",
"chars": 5320,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/arcadyan.rs",
"chars": 3699,
"preview": "use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};\nuse crate::extractors::lzma::lzma_decompres"
},
{
"path": "src/extractors/autel.rs",
"chars": 8371,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::autel::parse"
},
{
"path": "src/extractors/bmp.rs",
"chars": 2616,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::bmp::{get_di"
},
{
"path": "src/extractors/bzip2.rs",
"chars": 4112,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/cab.rs",
"chars": 1229,
"preview": "use crate::extractors;\n\n/// Describes how to run the cabextract utility to extract MS CAB archives\n///\n/// ```\n/// use s"
},
{
"path": "src/extractors/common.rs",
"chars": 46226,
"preview": "use crate::signatures::common::SignatureResult;\nuse log::{debug, error, info, warn};\nuse serde::{Deserialize, Serialize}"
},
{
"path": "src/extractors/csman.rs",
"chars": 6277,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/dahua_zip.rs",
"chars": 2852,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::signatures::zip::find_zi"
},
{
"path": "src/extractors/dmg.rs",
"chars": 1375,
"preview": "use crate::extractors;\n\n/// Describes how to run the dmg2img utility to convert DMG images to MBR\n///\n/// ```\n/// use st"
},
{
"path": "src/extractors/dtb.rs",
"chars": 4083,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/dumpifs.rs",
"chars": 1312,
"preview": "use crate::extractors;\n\n/// Describes how to run the dumpifs utility to extract QNX IFS images\n///\n/// ```\n/// use std::"
},
{
"path": "src/extractors/dxbc.rs",
"chars": 1855,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::dxbc::parse_"
},
{
"path": "src/extractors/encfw.rs",
"chars": 1751,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n\n/// Defines the internal extractor"
},
{
"path": "src/extractors/gif.rs",
"chars": 4179,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/gpg.rs",
"chars": 1789,
"preview": "use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};\nuse crate::extractors::inflate;\n\n/// Define"
},
{
"path": "src/extractors/gzip.rs",
"chars": 2004,
"preview": "use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};\nuse crate::extractors::inflate;\nuse crate::"
},
{
"path": "src/extractors/inflate.rs",
"chars": 2887,
"preview": "use crate::extractors::common::Chroot;\nuse adler32::RollingAdler32;\nuse flate2::bufread::DeflateDecoder;\nuse std::io::Re"
},
{
"path": "src/extractors/iso9660.rs",
"chars": 1295,
"preview": "use crate::extractors;\nuse crate::extractors::sevenzip::sevenzip_extractor;\n\n/// Describes how to run the 7z utility to "
},
{
"path": "src/extractors/jboot.rs",
"chars": 2718,
"preview": "use crate::common::crc32;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate"
},
{
"path": "src/extractors/jffs2.rs",
"chars": 1457,
"preview": "use crate::extractors;\n\n/// Describes how to run the jefferson utility to extract JFFS file systems\n///\n/// ```\n/// use "
},
{
"path": "src/extractors/jpeg.rs",
"chars": 5999,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n\n/// Defines the internal extractor"
},
{
"path": "src/extractors/linux.rs",
"chars": 1402,
"preview": "use crate::extractors;\n\n/// Describes how to run the vmlinux-to-elf utility to convert raw kernel images to ELF files\n//"
},
{
"path": "src/extractors/lz4.rs",
"chars": 1452,
"preview": "use crate::extractors;\n\n/// Describes how to run the lz4 utility to extract LZ4 compressed files\n///\n/// ```\n/// use std"
},
{
"path": "src/extractors/lzfse.rs",
"chars": 1477,
"preview": "use crate::extractors::common::{Extractor, ExtractorType, SOURCE_FILE_PLACEHOLDER};\n\n/// Describes how to run the lzfse "
},
{
"path": "src/extractors/lzma.rs",
"chars": 5037,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse liblzma::stream::{Action, Statu"
},
{
"path": "src/extractors/lzop.rs",
"chars": 1431,
"preview": "use crate::extractors;\n\n/// Describes how to run the lzop utility to extract LZO compressed files\n///\n/// ```\n/// use st"
},
{
"path": "src/extractors/matter_ota.rs",
"chars": 2443,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::matter_ota::"
},
{
"path": "src/extractors/mbr.rs",
"chars": 3140,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::mbr::parse_m"
},
{
"path": "src/extractors/mh01.rs",
"chars": 3595,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::mh01::parse_"
},
{
"path": "src/extractors/pcap.rs",
"chars": 3518,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/pem.rs",
"chars": 4672,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse aho_corasick::AhoCorasick;\n\n///"
},
{
"path": "src/extractors/png.rs",
"chars": 3250,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/rar.rs",
"chars": 1474,
"preview": "use crate::extractors;\n\n/// Describes how to run the unrar utility to extract RAR archives\n///\n/// ```\n/// use std::io::"
},
{
"path": "src/extractors/riff.rs",
"chars": 2094,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::riff::parse_"
},
{
"path": "src/extractors/romfs.rs",
"chars": 11478,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{\n Chroot, ExtractionError, ExtractionResult, Extra"
},
{
"path": "src/extractors/sevenzip.rs",
"chars": 1666,
"preview": "use crate::extractors;\n\n/// Describes how to run the 7z utility, supports multiple file formats\n///\n/// ```\n/// use std:"
},
{
"path": "src/extractors/squashfs.rs",
"chars": 5527,
"preview": "use crate::extractors;\n\n/// Describes how to run the sasquatch utility to extract SquashFS images\n///\n/// ```\n/// use st"
},
{
"path": "src/extractors/srec.rs",
"chars": 1375,
"preview": "use crate::extractors;\n\n/// Describes how to run the srec_cat utility to convert Motorola S-records to binary\n///\n/// ``"
},
{
"path": "src/extractors/svg.rs",
"chars": 1899,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate::structures::svg::parse_s"
},
{
"path": "src/extractors/swapped.rs",
"chars": 3002,
"preview": "use crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n\n/// Defines the internal extractor"
},
{
"path": "src/extractors/tarball.rs",
"chars": 1399,
"preview": "use crate::extractors;\n\n/// Describes how to run the tar utility to extract tarball archives\n///\n/// ```\n/// use std::io"
},
{
"path": "src/extractors/trx.rs",
"chars": 3717,
"preview": "use crate::common::crc32;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate"
},
{
"path": "src/extractors/tsk.rs",
"chars": 700,
"preview": "use crate::extractors;\n\n/// Describes how to run the tsk_recover utility to extract various file systems\npub fn tsk_extr"
},
{
"path": "src/extractors/ubi.rs",
"chars": 1746,
"preview": "use crate::extractors;\n\n/// Describes how to run the ubireader_extract_images utility to extract UBI images\n///\n/// ```\n"
},
{
"path": "src/extractors/uefi.rs",
"chars": 1686,
"preview": "use crate::extractors;\n\n/// Describes how to run the uefi-firmware-parser utility to extract UEFI images\n///\n/// ```\n///"
},
{
"path": "src/extractors/uimage.rs",
"chars": 3384,
"preview": "use crate::common::crc32;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\nuse crate"
},
{
"path": "src/extractors/vxworks.rs",
"chars": 3733,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/wince.rs",
"chars": 5730,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::common::{Chroot, ExtractionResult, Extractor, ExtractorType};\n"
},
{
"path": "src/extractors/yaffs2.rs",
"chars": 1300,
"preview": "use crate::extractors;\n\n/// Describes how to run the unyaffs utility to extract YAFFS2 file systems\n///\n/// ```\n/// use "
},
{
"path": "src/extractors/zlib.rs",
"chars": 2458,
"preview": "use crate::extractors::common::{ExtractionResult, Extractor, ExtractorType};\nuse crate::extractors::inflate;\n\n/// Size o"
},
{
"path": "src/extractors/zstd.rs",
"chars": 1500,
"preview": "use crate::extractors;\n\n/// Describes how to run the zstd utility to extract ZSTD compressed files\n///\n/// ```\n/// use s"
},
{
"path": "src/extractors.rs",
"chars": 7653,
"preview": "//! # File Extractors\n//!\n//! File extractors may be internal (written in Rust, compiled into Binwalk), or external (com"
},
{
"path": "src/json.rs",
"chars": 2873,
"preview": "use log::error;\nuse serde::{Deserialize, Serialize};\nuse std::fs;\nuse std::io;\nuse std::io::Seek;\nuse std::io::Write;\n\nu"
},
{
"path": "src/lib.rs",
"chars": 680,
"preview": "//! Rust library for identifying, and optionally extracting, files embedded inside other files.\n//!\n//! ## Example\n//!\n/"
},
{
"path": "src/magic.rs",
"chars": 49557,
"preview": "use crate::extractors;\nuse crate::signatures;\n\n/// Returns a list of all supported signatures, including their \"magic\" b"
},
{
"path": "src/main.rs",
"chars": 13637,
"preview": "use binwalk::AnalysisResults;\nuse log::{debug, error, info};\nuse std::collections::VecDeque;\nuse std::panic;\nuse std::pr"
},
{
"path": "src/signatures/aes.rs",
"chars": 4431,
"preview": "use crate::signatures::common::{CONFIDENCE_LOW, SignatureError, SignatureResult};\n\n/// Human readable description\npub co"
},
{
"path": "src/signatures/android_bootimg.rs",
"chars": 1381,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\nuse crate::s"
},
{
"path": "src/signatures/androidsparse.rs",
"chars": 1780,
"preview": "use crate::extractors::androidsparse::extract_android_sparse;\nuse crate::signatures::common::{CONFIDENCE_HIGH, Signature"
},
{
"path": "src/signatures/apfs.rs",
"chars": 2134,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::apfs::{MAGIC"
},
{
"path": "src/signatures/arcadyan.rs",
"chars": 1411,
"preview": "use crate::extractors::arcadyan::extract_obfuscated_lzma;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureErro"
},
{
"path": "src/signatures/arj.rs",
"chars": 1779,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::arj::parse_a"
},
{
"path": "src/signatures/autel.rs",
"chars": 1083,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::autel::parse"
},
{
"path": "src/signatures/binhdr.rs",
"chars": 1288,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::binhdr::pars"
},
{
"path": "src/signatures/bmp.rs",
"chars": 1307,
"preview": "use crate::extractors::bmp::extract_bmp_image;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, Signat"
},
{
"path": "src/signatures/btrfs.rs",
"chars": 1684,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::btrfs::parse"
},
{
"path": "src/signatures/bzip2.rs",
"chars": 1361,
"preview": "use crate::extractors::bzip2::bzip2_decompressor;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Signa"
},
{
"path": "src/signatures/cab.rs",
"chars": 1447,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::cab::parse_c"
},
{
"path": "src/signatures/cfe.rs",
"chars": 1278,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\n\n/// Human r"
},
{
"path": "src/signatures/chk.rs",
"chars": 1723,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::chk::parse_c"
},
{
"path": "src/signatures/common.rs",
"chars": 3968,
"preview": "use crate::extractors;\nuse serde::{Deserialize, Serialize};\n\n/// Some pre-defined confidence levels for SignatureResult "
},
{
"path": "src/signatures/compressd.rs",
"chars": 855,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\n\n/// Human r"
},
{
"path": "src/signatures/copyright.rs",
"chars": 1303,
"preview": "use crate::common::get_cstring;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\n\n/// "
},
{
"path": "src/signatures/cpio.rs",
"chars": 3396,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures/cramfs.rs",
"chars": 2967,
"preview": "use crate::common;\nuse crate::signatures::common::{\n CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, SignatureError, SignatureRes"
},
{
"path": "src/signatures/csman.rs",
"chars": 1086,
"preview": "use crate::extractors::csman::extract_csman_dat;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Signat"
},
{
"path": "src/signatures/dahua_zip.rs",
"chars": 940,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\nuse crate::signatures::zip;\n\n/// Human readable descri"
},
{
"path": "src/signatures/deb.rs",
"chars": 1157,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::deb::parse_deb"
},
{
"path": "src/signatures/dkbs.rs",
"chars": 2243,
"preview": "use crate::signatures::common::{\n CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\nuse crate::"
},
{
"path": "src/signatures/dlink_tlv.rs",
"chars": 2605,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::signatures::openssl::opens"
},
{
"path": "src/signatures/dlke.rs",
"chars": 1975,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::jboot::parse_j"
},
{
"path": "src/signatures/dlob.rs",
"chars": 1362,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::dlob::parse_"
},
{
"path": "src/signatures/dmg.rs",
"chars": 3474,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::dmg::parse_dmg"
},
{
"path": "src/signatures/dms.rs",
"chars": 1517,
"preview": "use crate::extractors::swapped::byte_swap;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureR"
},
{
"path": "src/signatures/dpapi.rs",
"chars": 1762,
"preview": "use crate::{\n signatures::common::{SignatureError, SignatureResult},\n structures::dpapi::parse_dpapi_blob_header,\n"
},
{
"path": "src/signatures/dtb.rs",
"chars": 1514,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::dtb::parse_d"
},
{
"path": "src/signatures/dxbc.rs",
"chars": 1299,
"preview": "use crate::signatures::common::{\n CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\nuse crate::"
},
{
"path": "src/signatures/ecos.rs",
"chars": 1563,
"preview": "use crate::signatures::common::{CONFIDENCE_LOW, SignatureError, SignatureResult};\n\n/// Human readable description\npub co"
},
{
"path": "src/signatures/efigpt.rs",
"chars": 1833,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::efigpt::parse_"
},
{
"path": "src/signatures/elf.rs",
"chars": 1167,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::elf::parse_e"
},
{
"path": "src/signatures/encfw.rs",
"chars": 1896,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\nuse std::col"
},
{
"path": "src/signatures/encrpted_img.rs",
"chars": 834,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\n\n/// Human r"
},
{
"path": "src/signatures/ext.rs",
"chars": 2168,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::ext::parse_e"
},
{
"path": "src/signatures/fat.rs",
"chars": 1703,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::fat::parse_f"
},
{
"path": "src/signatures/gif.rs",
"chars": 1772,
"preview": "use crate::extractors::gif::extract_gif_image;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Signatur"
},
{
"path": "src/signatures/gpg.rs",
"chars": 1507,
"preview": "use crate::extractors::gpg::gpg_decompress;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureRe"
},
{
"path": "src/signatures/gzip.rs",
"chars": 2552,
"preview": "use crate::common;\nuse crate::extractors::gzip::gzip_decompress;\nuse crate::signatures::common::{CONFIDENCE_HIGH, Signat"
},
{
"path": "src/signatures/hashes.rs",
"chars": 3051,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\n\n/// All hash magics here are the same size\nconst HASH"
},
{
"path": "src/signatures/iso9660.rs",
"chars": 1358,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::iso9660::parse"
},
{
"path": "src/signatures/jboot.rs",
"chars": 4985,
"preview": "use crate::extractors::jboot::extract_jboot_sch2_kernel;\nuse crate::signatures::common::{\n CONFIDENCE_HIGH, CONFIDENC"
},
{
"path": "src/signatures/jffs2.rs",
"chars": 4591,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::jffs2::{JFFS2_"
},
{
"path": "src/signatures/jpeg.rs",
"chars": 1507,
"preview": "use crate::extractors::jpeg::extract_jpeg_image;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, Sign"
},
{
"path": "src/signatures/linux.rs",
"chars": 8275,
"preview": "use crate::common::get_cstring;\nuse crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, "
},
{
"path": "src/signatures/logfs.rs",
"chars": 1216,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::logfs::{LOGF"
},
{
"path": "src/signatures/luks.rs",
"chars": 1639,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::luks::parse_"
},
{
"path": "src/signatures/lz4.rs",
"chars": 3445,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\n"
},
{
"path": "src/signatures/lzfse.rs",
"chars": 1764,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures/lzma.rs",
"chars": 3000,
"preview": "use crate::extractors::lzma;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crat"
},
{
"path": "src/signatures/lzop.rs",
"chars": 3140,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures/matter_ota.rs",
"chars": 1471,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::matter_ota::pa"
},
{
"path": "src/signatures/mbr.rs",
"chars": 2522,
"preview": "use crate::extractors::mbr::extract_mbr_partitions;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, S"
},
{
"path": "src/signatures/mh01.rs",
"chars": 1570,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::signatures::openssl::opens"
},
{
"path": "src/signatures/ntfs.rs",
"chars": 1350,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::ntfs::parse_"
},
{
"path": "src/signatures/openssl.rs",
"chars": 1755,
"preview": "use crate::common::is_printable_ascii;\nuse crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, Signature"
},
{
"path": "src/signatures/packimg.rs",
"chars": 1215,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\nuse crate::structures::packimg::parse_packimg_header;\n"
},
{
"path": "src/signatures/pcap.rs",
"chars": 1444,
"preview": "use crate::extractors::pcap::pcapng_carver;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureRe"
},
{
"path": "src/signatures/pchrom.rs",
"chars": 1224,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\nuse crate::structures::pchrom::parse_pchrom_header;\n\n/"
},
{
"path": "src/signatures/pdf.rs",
"chars": 2364,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\n\n/// Human readable description\npub const DESCRIPTION:"
},
{
"path": "src/signatures/pe.rs",
"chars": 1272,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::pe::parse_pe"
},
{
"path": "src/signatures/pem.rs",
"chars": 5567,
"preview": "use crate::extractors::pem;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse base6"
},
{
"path": "src/signatures/pjl.rs",
"chars": 1360,
"preview": "use crate::common::get_cstring;\nuse crate::signatures::common::{CONFIDENCE_LOW, SignatureError, SignatureResult};\n\n/// H"
},
{
"path": "src/signatures/pkcs_der.rs",
"chars": 2064,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse std::collections::HashMap;\n\n///"
},
{
"path": "src/signatures/png.rs",
"chars": 1546,
"preview": "use crate::extractors::png::extract_png_image;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Signatur"
},
{
"path": "src/signatures/qcow.rs",
"chars": 1019,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::qcow::parse_"
},
{
"path": "src/signatures/qnx.rs",
"chars": 1281,
"preview": "use crate::signatures::common::{SignatureError, SignatureResult};\nuse crate::structures::qnx::parse_ifs_header;\n\n/// Hum"
},
{
"path": "src/signatures/rar.rs",
"chars": 2427,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::rar::parse_r"
},
{
"path": "src/signatures/riff.rs",
"chars": 1220,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::riff::parse_"
},
{
"path": "src/signatures/romfs.rs",
"chars": 1447,
"preview": "use crate::extractors::romfs::extract_romfs;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureR"
},
{
"path": "src/signatures/rsa.rs",
"chars": 9237,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::common;\nuse "
},
{
"path": "src/signatures/rtk.rs",
"chars": 1355,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::rtk::parse_r"
},
{
"path": "src/signatures/seama.rs",
"chars": 1356,
"preview": "use crate::signatures::common::{CONFIDENCE_LOW, SignatureError, SignatureResult};\nuse crate::structures::seama::parse_se"
},
{
"path": "src/signatures/sevenzip.rs",
"chars": 2070,
"preview": "use crate::common::crc32;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::"
},
{
"path": "src/signatures/shrs.rs",
"chars": 1265,
"preview": "use crate::signatures::common::{\n CONFIDENCE_LOW, CONFIDENCE_MEDIUM, SignatureError, SignatureResult,\n};\nuse crate::s"
},
{
"path": "src/signatures/squashfs.rs",
"chars": 6266,
"preview": "use crate::common::epoch_to_string;\nuse crate::extractors::squashfs::{\n squashfs_be_extractor, squashfs_le_extractor,"
},
{
"path": "src/signatures/srec.rs",
"chars": 3776,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures/svg.rs",
"chars": 1351,
"preview": "use crate::extractors::svg::extract_svg_image;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, Signat"
},
{
"path": "src/signatures/tarball.rs",
"chars": 5984,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{\n CONFIDENCE_HIGH, CONFIDENCE_MEDIUM, SignatureErr"
},
{
"path": "src/signatures/tplink.rs",
"chars": 2432,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::tplink::{par"
},
{
"path": "src/signatures/trx.rs",
"chars": 1550,
"preview": "use crate::extractors::trx::extract_trx_partitions;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Sig"
},
{
"path": "src/signatures/ubi.rs",
"chars": 4896,
"preview": "use crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nuse crate::structures::ubi::{\n par"
},
{
"path": "src/signatures/uboot.rs",
"chars": 1272,
"preview": "use crate::common::{get_cstring, is_ascii_number};\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, Si"
},
{
"path": "src/signatures/uefi.rs",
"chars": 3370,
"preview": "use crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\nuse crate::structures::uefi::{parse"
},
{
"path": "src/signatures/uimage.rs",
"chars": 3159,
"preview": "use crate::common::epoch_to_string;\nuse crate::extractors::uimage::extract_uimage;\nuse crate::signatures::common::{\n "
},
{
"path": "src/signatures/vxworks.rs",
"chars": 3483,
"preview": "use crate::common::get_cstring;\nuse crate::extractors::vxworks::extract_symbol_table;\nuse crate::signatures::common::{CO"
},
{
"path": "src/signatures/wince.rs",
"chars": 1471,
"preview": "use crate::extractors::wince::wince_dump;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResu"
},
{
"path": "src/signatures/xz.rs",
"chars": 2802,
"preview": "use crate::common::is_offset_safe;\nuse crate::extractors::lzma::lzma_decompress;\nuse crate::extractors::sevenzip::sevenz"
},
{
"path": "src/signatures/yaffs.rs",
"chars": 8243,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_MEDIUM, SignatureError, SignatureResult};\n"
},
{
"path": "src/signatures/zip.rs",
"chars": 4486,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures/zlib.rs",
"chars": 1267,
"preview": "use crate::extractors::zlib::zlib_decompress;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, Signature"
},
{
"path": "src/signatures/zstd.rs",
"chars": 5070,
"preview": "use crate::common::is_offset_safe;\nuse crate::signatures::common::{CONFIDENCE_HIGH, SignatureError, SignatureResult};\nus"
},
{
"path": "src/signatures.rs",
"chars": 6738,
"preview": "//! # File / Data Signatures\n//!\n//! Creating a signature to identify a particular file or data type is composed of two "
},
{
"path": "src/structures/android_bootimg.rs",
"chars": 1131,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store Android boot image header info\n#[derive(Debu"
},
{
"path": "src/structures/androidsparse.rs",
"chars": 3979,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Storage struct for AndroidSparse file header info\n#[derive(D"
},
{
"path": "src/structures/apfs.rs",
"chars": 3593,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Offset of the APFS magic bytes from the start of the APFS im"
},
{
"path": "src/structures/arj.rs",
"chars": 4682,
"preview": "use crate::common::{epoch_to_string, get_cstring};\nuse crate::structures::common;\nuse crate::structures::common::Structu"
},
{
"path": "src/structures/autel.rs",
"chars": 1683,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\n\n/// Struct to store Autel ECC he"
},
{
"path": "src/structures/binhdr.rs",
"chars": 2411,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\nuse std::collections::HashMap;\n\n/"
},
{
"path": "src/structures/bmp.rs",
"chars": 2170,
"preview": "use crate::structures::common::{self, StructureError};\n\n#[derive(Debug, Default, Clone)]\npub struct BMPFileHeader {\n "
},
{
"path": "src/structures/btrfs.rs",
"chars": 2420,
"preview": "use crate::structures::common::{self, StructureError};\nuse crc32c::crc32c;\n\n/// Struct to store BTRFS super block info\n#"
},
{
"path": "src/structures/cab.rs",
"chars": 4087,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Stores CAB header info\n#[derive(Debug, Default, Clone)]\npub "
},
{
"path": "src/structures/chk.rs",
"chars": 2219,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\n\n/// Storage struct for CHK heade"
},
{
"path": "src/structures/common.rs",
"chars": 6534,
"preview": "use log::error;\nuse std::collections::HashMap;\n\n/*\n * Note that all values returned by the parse() function are of type "
},
{
"path": "src/structures/cpio.rs",
"chars": 3377,
"preview": "use crate::structures::common::StructureError;\n\n/// Expected minimum size of a CPIO entry header\npub const CPIO_HEADER_S"
},
{
"path": "src/structures/cramfs.rs",
"chars": 2721,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store info about a CramFS header\n#[derive(Default,"
},
{
"path": "src/structures/csman.rs",
"chars": 3900,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store CSMAN header info\n#[derive(Debug, Default, C"
},
{
"path": "src/structures/deb.rs",
"chars": 2743,
"preview": "use crate::structures::common::StructureError;\n\n/// Struct to store DEB file info\n#[derive(Debug, Clone, Default)]\npub s"
},
{
"path": "src/structures/dkbs.rs",
"chars": 2601,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\n\n/// Struct to store DKBS header "
},
{
"path": "src/structures/dlink_tlv.rs",
"chars": 2204,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\n\n/// Struct to store DLink TLV fi"
},
{
"path": "src/structures/dlob.rs",
"chars": 2224,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store DLOB header info\n#[derive(Debug, Default, Cl"
},
{
"path": "src/structures/dmg.rs",
"chars": 4488,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store DMG footer info\n#[derive(Debug, Default, Clo"
},
{
"path": "src/structures/dms.rs",
"chars": 896,
"preview": "use crate::structures::common::{self, StructureError};\n\n/// Struct to store DMS header info\n#[derive(Debug, Default, Clo"
},
{
"path": "src/structures/dpapi.rs",
"chars": 4840,
"preview": "use crate::structures::common::{self, StructureError};\n\n/*\n Blob structure: from mimikatz repository.\n DWORD\tdwVersion"
},
{
"path": "src/structures/dtb.rs",
"chars": 6179,
"preview": "use crate::common::get_cstring;\nuse crate::structures::common::{self, StructureError};\n\n/// Struct to store DTB info\n#[d"
},
{
"path": "src/structures/dxbc.rs",
"chars": 1780,
"preview": "use crate::structures::common::{self, StructureError};\n\n#[derive(Debug, Default, Clone)]\npub struct DXBCHeader {\n pub"
}
]
// ... and 70 more files (download for full content)
About this extraction
This page contains the full source code of the ReFirmLabs/binwalk GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 270 files (828.3 KB), approximately 201.8k tokens, and a symbol index with 830 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.