LSPosed/dex2oat
JingMatrix 1436f692fb
Refactor dex2oat wrapper to solve long-standing issues (#515)
Manual library path injection via LD_LIBRARY_PATH has become unreliable due to symbol mismatches in core libraries (e.g., `libc++`) between the system and APEX partitions. Recent updates to `liblog` and `libbase` (in Android 16) have resulted in missing symbols like `__hash_memory` or `fmt` when the ART APEX binaries are forced to load system-partition shims.

This commit switches the wrapper to execute the runtime APEX linker directly (e.g., `/apex/com.android.runtime/bin/linker64`). By passing the dex2oat binary to the linker via `/proc/self/fd/`, the linker can properly initialize internal namespaces and resolve dependencies from the correct APEX and bootstrap locations.

Moreover, for the OatHeader hook, a bug introduced in 6703b45350 is now fixed, where the target functions of PLT hooks are overwritten by the our helper functions.

Details of the refactored project are explained in README.
2026-01-25 10:13:57 +01:00
..
src/main/cpp Refactor dex2oat wrapper to solve long-standing issues (#515) 2026-01-25 10:13:57 +01:00
.gitignore Resolve dex2oat parameters for Android 10+ (#1803) 2022-04-02 13:13:22 +08:00
README.md Refactor dex2oat wrapper to solve long-standing issues (#515) 2026-01-25 10:13:57 +01:00
build.gradle.kts Refactor dex2oat wrapper to solve long-standing issues (#515) 2026-01-25 10:13:57 +01:00

README.md

VectorDex2Oat

VectorDex2Oat is a specialized wrapper and instrumentation suite for the Android dex2oat (Ahead-of-Time compiler) binary. It is designed to intercept the compilation process, force specific compiler behaviors (specifically disabling method inlining), and transparently spoof the resulting OAT metadata to hide the presence of the wrapper.

Overview

In the Android Runtime (ART), dex2oat compiles DEX files into OAT files. Modern ART optimizations often inline methods, making it difficult for instrumentation tools to hook specific function calls.

This project consists of two primary components:

  1. dex2oat (Wrapper): A replacement binary that intercepts the execution, communicates via Unix Domain Sockets to obtain the original compiler binary, and executes it with forced flags.
  2. liboat_hook.so (Hooker): A shared library injected into the dex2oat process via LD_PRELOAD that utilizes PLT hooking to sanitize the OAT header's command-line metadata.

Key Features

  • Inlining Suppression: Appends --inline-max-code-units=0 to the compiler arguments, ensuring all methods remain discrete and hookable.
  • FD-Based Execution: Executes the original dex2oat via the system linker using /proc/self/fd/ paths, avoiding direct execution of files on the disk.
  • Metadata Spoofing: Intercepts art::OatHeader::ComputeChecksum or art::OatHeader::GetKeyValueStore to remove traces of the wrapper and its injected flags from the final .oat file.
  • Abstract Socket Communication: Uses the Linux Abstract Namespace for Unix sockets to coordinate file descriptor passing between the controller and the wrapper.

Architecture

The Wrapper dex2oat.cpp

The wrapper acts as a "man-in-the-middle" for the compiler. When called by the system, it

  1. connects to a predefined Unix socket (the stub name 5291374ceda0... will be replaced during installation of Vector);
  2. identifies the target architecture (32-bit vs 64-bit) and debug status;
  3. receives File Descriptors (FDs) for both the original dex2oat binary and the oat_hook library;
  4. reconstructs the command line, replacing the wrapper path with the original binary path and appending the "no-inline" flags;
  5. clears LD_LIBRARY_PATH and sets LD_PRELOAD to the hooker library's FD;
  6. invokes the dynamic linker (linker64) to execute the compiler.

The Hooker oat_hook.cpp

The hooker library is preloaded into the compiler's address space. It uses the LSPlt library to:

  1. Scan the memory map to find the dex2oat binary.
  2. Locate and hook internal ART functions:
  3. When the compiler attempts to write the "dex2oat-cmdline" key into the OAT header, the hooker intercepts the call, parses the key-value store, and removes the wrapper-specific flags and paths.