Skip to content

Instantly share code, notes, and snippets.

@win3zz
Last active June 15, 2025 16:13
Show Gist options
  • Save win3zz/f78eca67036b578a7b40736ac4c4bc47 to your computer and use it in GitHub Desktop.
Save win3zz/f78eca67036b578a7b40736ac4c4bc47 to your computer and use it in GitHub Desktop.

Getting started with Firmware Analysis on Meta Quest

Summary of the concepts and techniques discussed in a firmware analysis series, along with technical notes and commands

This video, Part 1 of a three-part firmware analysis series by Tom Heb of Meta Red Team X, introduces what firmware is, why it's security-critical, and the initial two phases of firmware analysis: enumerate (figuring out what firmware exists) and obtain (getting a copy of the firmware).

Key Technical Details and Commands:

  • What is Firmware?
    • Proprietary code running outside the main operating system (e.g., Android, Windows, Linux) on a dedicated processor or in a non-OS environment on the same processor.
    • Focuses on proprietary firmware because open-source or well-documented firmware (like UEFI) has readily available source code.
    • Runs "bare metal" or on very low-level embedded OSes, lacking the robust security mitigations (ASLR, permissions, users) common in full operating systems.
  • Examples of Firmware:
    • Dedicated Device Peripherals: Keyboards, USB hubs, SIM cards (often the only code running).
    • Firmware Alongside OS:
      • Wi-Fi Radio: Handles complex real-time modulation/demodulation. Often has direct access to main system memory, making it a high-impact attack vector.
      • GPU: For video decoding, rendering.
      • Flash Drives/SSDs: Manages the complex, unforgiving nature of flash memory to present a reliable disk interface.
      • Power Management IC (PMIC): Manages power, charging, etc.
      • Microcontrollers: E.g., in Oculus Quest 2 for camera timings, sensor data (IMU, accelerometers, gyroscope).
  • Why Analyze Firmware for Security?
    • Lack of Protections: Often written to lower security standards, lacking modern OS mitigations.
    • High Access on Compromise:
      • Remote Exploitation: Wi-Fi firmware can be exploited remotely via malicious packets, potentially leading to main OS compromise (due to direct memory access).
      • Hardware Damage: Compromising PMIC or flash storage firmware can lead to bricking the device (e.g., frying chips by increasing voltage, exhausting flash write cycles).
      • Secure Boot Bypass: Firmware often forms the chain of trust for secure boot, making it a target for bypassing OS integrity checks.
      • Sensitive Data Access: Microcontroller firmware can expose direct access to system-level sensor data.
    • Bug Bounties: Meta pays for vulnerabilities in firmware on its Reality Lab devices (Quest, Portal, smart glasses), especially for secure boot bypasses and direct system-level access.
  • Firmware Analysis Process (Four Rough Steps):
    1. Enumerate: Figure out what firmware exists on a device.
    2. Obtain: Get a copy of the firmware file.
    3. Extract: Parse the unknown firmware blob into recognizable code and data.
    4. Analyze: Load into static analysis tools (like Ghidra) and begin reading code. (Parts 3 and 4 (Extract & Analyze) will be covered in the next video.)

Phase 1: Enumerate Firmware

  • Goal: List all firmware, know where it runs (pre-boot, separate chip), and understand how it's loaded.
  • Methods:
    1. Physical Inspection / Hardware Teardown:
      • Open the device, take photos of the mainboard and chips.

      • Screenshot 2025-06-15 213735

      • Look for part numbers/markings on chips (e.g., in Oculus Quest 2).

      • Screenshot 2025-06-15 213753

      • Data Sheets / Reference Manuals: Search online for documentation of identified chips.

        • Provides: CPU architecture, communication bus (e.g., PCI, SDIO for Wi-Fi), and sometimes firmware update methods or read locations.
        • Full reference manuals (rarely public) detail register maps.
    2. Operating System Source Code / Binaries (e.g., Linux Kernel):
      • Applicability: Most ARM/embedded devices, including Meta hardware, run Android/Linux.
      • Kernel Source (e.g., Oculus Quest 2 public kernel source):
        • Device Tree (arch/arm64/boot/dts/<vendor>/<chip>.dtsi):
          • Declarative text files describing hardware components and their connections.
          • Example: Wi-Fi chip entry (WLAN) shows compatible string (e.g., qcom,cnss).
        • Drivers Directory:
          • Search the drivers directory for the compatible string found in the device tree.
          • Identify the specific driver file (e.g., net/wireless/cnss2/main.c).
        • request_firmware function:
          • A core Linux kernel function used by drivers to load firmware from disk onto a chip.
          • Indicates firmware is not built into the chip's ROM/flash, but loaded into RAM at boot.
          • This function avoids embedding large, often non-GPL firmware directly into the kernel image.
          • The second argument to request_firmware is the firmware filename.
          • Example Filename: QCA6390/M3.bin (for a Qualcomm Wi-Fi chip).
  • Common Firmware Paths (on Android/Linux filesystems):
    • /vendor/firmware
    • /etc/firmware
    • /lib/firmware
    • /system/etc/firmware
    • vendor firmware mount/image (e.g., on Quest 2 for Wi-Fi)
  • Information to Compile (Mental or Physical Table):
    • Chip Name/Function (e.g., Flash Storage, Wi-Fi, Audio DSP).
    • Interface to Main Chipset (e.g., UFS, eMMC, NVMe, PCI, SDIO, I2S).
    • Public Documentation Found (Data Sheet, Reference Manual, Press Release).
    • Confirmation of Firmware Presence/Rewritability (e.g., "supports field firmware upgrades").
    • CPU Core Architecture (if known).
    • Firmware Update Source/Method (if known).

Phase 2: Obtain Firmware

  • Goal: Get a copy of the firmware file.
  • Methods:
    1. Directly from Filesystem (Easiest):
      • If the driver loads firmware at every boot (identified via request_firmware calls in kernel source), you can simply read the firmware file from the listed paths on the device's filesystem.
    2. Reverse Engineer Firmware Update Mechanisms:
      • If the device has firmware update functionality, reverse engineer the update binary (which runs on a high-level OS).
      • This binary might contain the firmware update image embedded within it or know a URL to download it.
      • Traditional reversing tools (for high-level OS binaries) can be used to extract the firmware from these updates.
    3. Direct Chip Extraction (Hardest):
      • If no other methods work (e.g., firmware built into internal flash/ROM, no updates found).
      • Reference Manual: Consult for instructions on how to update or read firmware, or how to run code on the chip.
      • Debug Interfaces: Use hardware debug interfaces like JTAG (Joint Test Action Group) to directly read memory from the chip.
      • Standardized Protocols: Some external peripherals support standardized update protocols like DFU (Device Firmware Upgrade) over USB.

The video concludes, stating that the next part will cover how to extract (parse unknown binary formats) and analyze (load into static analysis tools) the obtained firmware.

This video, Part 2 of a firmware analysis series by Tom Heb of Meta Red Team X, focuses on the static analysis phase, particularly understanding unknown firmware file formats and preparing them for further analysis in tools like Ghidra.

Key Technical Details and Commands:

  • File Type Identification:
    • file <firmware.bin>: The primary command to identify a file's type.
      • Lucky Case (ELF): Sometimes, firmware is a standard ELF executable (e.g., m3.bin identified as ELF 32-bit LSB executable, Intel 80386...). Even if the architecture reported (e.g., 386) seems incorrect (e.g., for an ARM Cortex M3), the file is mostly conformant and standard ELF tools can be used.
      • Common Case (Data): More often, file will output data, indicating it doesn't recognize the format in its internal database. This means it's a custom or unknown binary format.
  • Limitations of Basic Tools for Unknown Formats:
    • Hex Editors: While showing raw bytes, they are generally ineffective for understanding complex binary structures due to nested data and length-prefixed fields, which are difficult for humans to parse manually.
    • Pattern Matching Tools:
      • strings <firmware.bin>: Extracts sequences of printable ASCII characters, which can sometimes reveal log messages or user-facing text, offering clues.
      • binwalk <firmware.bin>: A more advanced pattern matcher that scans a file byte-by-byte for known signatures (e.g., JPEG, PNG headers, file systems). Useful for a high-level overview or extracting embedded files, but doesn't understand the enclosing custom format.
  • The Need for a Parser:
    • Standard tools like objdump or readelf are parsers for well-known formats like ELF.
    • For custom firmware formats, you often need to write your own parser.
    • Traditional manual parsing with C/Python is cumbersome due to boilerplate code, offset management, and refactoring needs.
  • Kaitai Struct (Recommended Tool for Parsing Unknown Binary Formats):
    • Purpose: A declarative language for specifying binary file formats, enabling clean and efficient parsing.
    • Web IDE: A key feature for reverse engineers. It provides:
      • A hex editor.
      • A pane for writing the declarative specification (.ksy file).
      • Automatic highlighting in the hex editor based on parsed fields.
      • A hierarchical view of parsed values with type information.
    • Advantages: Iterative analysis (add/delete fields, live updates), no boilerplate code, open-source, easy sharing of specifications.
    • Analogy: Described as "Ghidra for data formats"
  • Key Information to Extract from Firmware (using Kaitai Struct or similar):
    1. Pure Binary Code: Identify the exact sections of the file that contain executable machine code, free from headers or surrounding data, to ensure correct alignment and offsets for disassemblers.
    2. Instruction Set/Architecture: Determine the CPU architecture the code is compiled for (e.g., ARM, MIPS, Xtensa).
      • Hints can come from file names (e.g., M3 for Cortex-M3/ARM), datasheets, or reference manuals for the chip.
      • Firmware files themselves generally don't explicitly state the architecture.
    3. Memory Map: Crucial for understanding how the firmware is loaded and executed in RAM.
      • Unlike OS executables (ELF), firmware often lacks standard headers indicating load addresses and sections.
      • You need to find:
        • Start Address: The specific RAM address where code and data segments are loaded.
        • Segment Attributes: Read, Write, Execute permissions for each memory region.
      • Ghidra/Binary Ninja Configuration: These tools allow you to configure memory maps, mapping portions of your firmware file to specific addresses with defined permissions.
        • Marking executable code as readable and executable, but not writable enables optimizations in decompilers, assuming modern compilers don't emit self-modifying code.
  • Memory-Mapped I/O (Peripherals):
    • Concept: On bare-metal firmware (without an OS or syscalls), hardware operations (e.g., printing to serial, controlling GPIO pins) are performed by reading from and writing to specific memory addresses that correspond to hardware registers.
    • Example: A function writing a value to a fixed address, waiting, and then reading from the same address is likely interacting with a peripheral register, not RAM.
    • Importance in Ghidra: Mark peripheral memory regions as volatile.
      • This prevents Ghidra from making optimizations based on the assumption that writing a value to an address means reading the same value back later (as would be true for RAM). Volatile memory indicates that its contents can change unexpectedly (due to hardware interaction).
      • Peripheral regions often start at nice, round memory addresses.

Dynamic Analysis (Brief Introduction):

  • Goal: Observing firmware behavior during execution (like a debugger).
  • Challenges: Highly device-specific.
  • Techniques Mentioned:
    • Monitoring Buses: Using a logic analyzer on communication buses (e.g., eMMC) to observe data flow (requires physical access).
    • Kernel Driver Debugging: Leveraging existing debug functionality in kernel drivers (requires reading kernel source).
    • Special USB Modes: Some devices offer serial or log output over USB in special debug modes.
    • Modifying Kernel/Host Data: Changing input data from the host (if rooted) to trigger different firmware code paths.
    • Hardware Debuggers (JTAG/SWD): Physical interfaces for direct memory reading/writing and code execution, often bypassing software security measures like signing (useful for research, but not typically a valid bug bounty submission if it requires physical access).
    • Modifying Firmware Image:
      • Kernel-Loaded Firmware: Replace the file on disk (after repacking if custom format).
      • Internal Firmware: Use firmware update protocols (if unsigned).
      • Security Considerations: Be aware of code signing (e.g., chain of trust bootloaders) which prevents running unsigned or modified firmware. Encryption is another hurdle. Signature blobs in the extracted firmware can indicate signing. JTAG/SWD can sometimes bypass signing.

This video, part three of a firmware analysis series by Tom Heb of Red Team X at Meta, demonstrates a real-world analysis of the C-Media CM7104 audio DSP chip's firmware found in the Oculus Quest 2.

Key Technical Details and Commands:

  • Target Chip: C-Media CM7104 Digital Signal Processor (DSP)

    • Handles all audio processing (echo cancellation, noise reduction, spatial audio, microphone/speaker handling) on the Quest 2.
    • Connects to the main chip via I²S audio bus.
    • Firmware is loaded from the kernel each time the kernel boots.
  • Firmware Acquisition (from Quest 2 via ADB):

    • The firmware is located at /vendor/firmware/CM710xtop.bin.
    • SELinux Restriction Bypass: Due to Android's SELinux restrictions, directly reading the file as a non-root user results in a "file not found" error, even if it's listed. The workaround is to use the vendor shell.
    • Command to extract firmware:
      adb shell "cd /vendor/bin && ./sh -c 'cat /vendor/firmware/CM710xtop.bin'" > cm710x.bin
      • This command spawns two shells and pipes the file's content out, saving it locally.
  • Firmware Enumeration (Finding the Firmware and Driver):

    • Physical Inspection: The chip can be identified in photos of the Quest 2 mainboard (as shown in Part 1).
    • Device Tree: The chip's presence is also noted in the device tree.
      • Code Name: The Quest 2's kernel code name is "Hollywood"
      • Search Oculus Quest kernel source for "Hollywood audio"
      • Locate "CM710X" in the kernel source, which indicates its connection over an SPI bus for control.
      • Compatible String: The compatible string within the device tree entry (e.g., cm710x) is crucial for finding the corresponding driver.
    • Driver Identification:
      • Search the kernel's drivers directory for the compatible string.
      • The main driver file is CM710X.c.
      • Firmware Loading Function: Search CM710X.c for request_firmware. This function is commonly used in Linux drivers to load firmware.
      • The firmware name (CM710X_firmware) found in the request_firmware call within the driver matches the extracted binary file name.
  • Firmware Analysis (Using Kaitai Struct):

    • Tool: Kaitai Struct (KSY format).
    • Initial Observation: The first three bytes of the CM710xtop.bin file contain the ASCII string "CMR" – likely a magic number.
      • Kaitai Struct Specification Example (Initial Guess):
        - id: magic
          contents: 'CMR'
    • Refining the Header (with Kernel Source Hints):
      • Analyzing the CM710X_firmware_parsing function in CM710X.c reveals the actual header structure:
        • First two bytes: "CM" (magic number).
        • Third byte: count1 (a count of something).
        • Fourth byte: count2 (another count).
        • Bytes 4 and 5: A two-byte address.
        • Bytes 6 and 7: More single-byte values.
        • A checksum is also calculated.
      • Kaitai Struct Specification Example (Refined):
        - id: magic
          contents: 'CM'
        - id: count1
          type: u1
        - id: count2
          type: u1
        # ... and so on for other header fields
    • Dynamic Data Loading: The KSY format allows defining a subtype that starts at an address specified within the file itself (e.g., data_address). This enables parsing non-linear file formats.
    • DSP Blocks: The firmware consists of "DSP blocks," each having:
      • address: The memory address where the firmware segment is loaded.
      • length: The size of the firmware data.
      • data: The raw machine code.
      • Base Address Guess: The address is often a large, round 32-bit number, indicating a base address for the code that follows.
  • Static Analysis (Using Ghidra):

    • Importing Firmware: Import the extracted DSP block data into Ghidra as a "raw binary"
    • Architecture Identification: The raw binary does not contain architecture information.
      • Product Briefs: Initial search of the CM7104 product brief is unhelpful for architecture.
      • Related Chip (CM7120): The CM7120 product page (a similar chip) reveals it has a "Tensilica Hi-Fi 3 DSP core"
      • Tensilica Extensa: Tensilica's DSP cores use the Extensa instruction set.
      • Ghidra Plugin: Ghidra does not support Extensa by default; an external plugin is required and easily installed.
    • Setting Base Address: Crucially, set the base address in Ghidra to the address identified in the Kaitai Struct analysis (e.g., 0x500000). This ensures correct referencing and decompilation.
    • Disassembly: Raw binaries don't have entry point information. Start disassembling from the very beginning of the loaded segment.
    • Signs of Correct Architecture:
      • Valid instructions (not random nonsense).
      • Presence of jump instructions at the beginning (common to skip vector tables).
      • Plausible instruction patterns (loads, stores, complex structures like loops).
      • HALT_BAD_DATA / Unknown Instructions: Extensa's extensibility means the Ghidra plugin might not recognize all custom DSP instructions. This is often acceptable, as subsequent code can still be valid.
      • Infinite Loop: Firmware often ends with a "do-while true" loop to prevent execution from running off memory.
    • Advanced Ghidra Features:
      • Memory Map: Mark segments as non-writable (e.g., executable code) for better decompilation optimizations.
      • Segment Mapping: Map segments to files on disk.
      • Function Identification and Cross-References: Ghidra automatically identifies functions and their calls.
      • Symbol Renaming & Type Creation: Renaming generic functions (e.g., FUN_00500010) to descriptive names (e.g., verbose_terminate_handler) based on recognized patterns or external library source code (like GNU libstdc++). This allows building a more meaningful call graph and understanding the firmware's functionality, enabling the search for bugs by examining peripheral communication and host interactions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment