Crate hyperpom

source ·
Expand description

Apple Silicon hypervisor-based fuzzer for ARM64 binaries.

Disclaimer

The idea behind this project was to create an efficient and fast fuzzer that would leverage Apple Silicon’s features. However, at this stage, while the fuzzer works, it is still mostly a proof of concept and requires tons of enhancement to provide better features, usability and performances.

It might be enough for your use cases, but keep in mind that you might encounter limitations that weren’t factored in while designing the project. In any case, feel free to open an issue and we’ll try to address your problem.

Hyperpom Internals & Usage

If you want an in-depth guide on how to use this fuzzer, you can directly jump to the chapter about the Loader, which provides different examples.

Otherwise, if you want a better understanding of the fuzzer’s implementation and the interactions between its components, it is recommended to read the documentation in the following order.

  1. Memory Management
    1. Physical Memory Allocator
    2. Slab Allocator
    3. Page Table Manager
    4. Virtual Memory Allocator
  2. Exception Handling
  3. Cache Maintenance
  4. Hooks
  5. Coverage
  6. Tracing
  7. Corpus
  8. Mutator
  9. Fuzzer’s Core
    1. HyperPom
    2. Worker
    3. Executor
  10. Config
  11. Loader

Getting Started

Prerequisites

  1. Install Rust and rustup using the official guide.
  2. Install the nightly channel.
rustup toolchain install nightly
  1. To use this channel when compiling you can either:

    • set it as default using rustup default nightly;
    • or add +nightly everytime you compile a binary with cargo.
  2. Install Cmake, using brew for example:

brew install cmake

Self-Signed Binaries and Hypervisor Entitlement

To be able to reach the Hypervisor Framework, a binary executable has to have been granted the hypervisor entitlement.

Certificate Chain

To add this entitlement to your project, you’ll first need a certificate chain to sign your binaries, which can be created by following the instructions below.

  • Open the Keychain Access application.
  • Go to Keychain Access > Certificate Assistant > Create a Certificate.
  • Fill out the Name field, this value will be used later on to identify the certificate we want to sign with and will be referred to as ${CERT_NAME}.
  • Set Identity Type to Self-Signed Root.
  • Set Certificate Type to Code Signing.
  • Click on Create.

You can now sign binaries and add entitlements using the following command:

codesign --entitlements entitlements.xml -s ${CERT_NAME} /path/to/binary

Note: The entitlements.xml file is available at the root of the Hyperpom repository.

Compilation Workflow

Create a Rust project and add Hyperpom as a dependency in Cargo.toml. You can either pull it from crates.io

hyperpom = "0.1.0"

… or directly from the GitHub repository.

hyperpom = { git="https://github.com/impalabs/hyperpom", branch="master" }

Create a file called entitlements.txt in the project’s root directory and add the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.hypervisor</key>
    <true/>
</dict>
</plist>

Write code and then build the project.

cargo build --release

Sign the binary and grant the hypervisor entitlement.

codesign --entitlements entitlements.xml -s ${CERT_NAME} target/release/${PROJECT_NAME}

Run the binary.

target/release/${PROJECT_NAME}

Examples

Four examples are provided to give you a better understanding of how the framework operates and get you started:

You can also have a look at the tests.

Re-exports

pub extern crate applevisor;

Modules

Handles everything related to backtrace generation.
Handles everything related to ARMv8 cache and TLB maintenance.
Implements the fuzzer configuration builder.
The core components that setup and manage the fuzzer.
Handles the corpus and testcases sent to the fuzzed targets.
Handles everything related to coverage gathering at runtime.
Methods to create crash files after a crash or a timeout occured.
Contains all error types that can be returned by this crate.
Handles everything related to ARMv8 exceptions, from setting up the exception vector table, to handling exceptions raised by the guest VMs.
Handles instruction hooking and emulation of branch instructions.
Interface used to implement user-defined behaviour. It is reponsible for loading the binary, placing hooks, generating a symbols list, etc.
Module responsible for everything related to memory management. It handles memory allocations, slab allocations, page table management and virtual memory allocations.
Mutation engine implementation.
Handles everything related to instruction tracing.
Miscellaneous functions used by different modules of the fuzzer.

Macros

Truncates an address to a multiple of applevisor::PAGE_SIZE.
Truncates an address to a multiple of VIRT_PAGE_SIZE.
Counts the number of arguments passed to a macro.
Calls an arbitrary function using its symbol name.
Calls an arbitrary function using its address.
Rounds up an address to the next multiple of applevisor::PAGE_SIZE.
Rounds up an address to the next multiple of VIRT_PAGE_SIZE.