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
|
||
|---|---|---|
| .. | ||
| src/main/cpp | ||
| .gitignore | ||
| README.md | ||
| build.gradle.kts | ||
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:
- 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.
- liboat_hook.so (Hooker): A shared library injected into the
dex2oatprocess viaLD_PRELOADthat utilizes PLT hooking to sanitize the OAT header's command-line metadata.
Key Features
- Inlining Suppression: Appends
--inline-max-code-units=0to the compiler arguments, ensuring all methods remain discrete and hookable. - FD-Based Execution: Executes the original
dex2oatvia the system linker using/proc/self/fd/paths, avoiding direct execution of files on the disk. - Metadata Spoofing: Intercepts
art::OatHeader::ComputeChecksumorart::OatHeader::GetKeyValueStoreto remove traces of the wrapper and its injected flags from the final.oatfile. - 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
- connects to a predefined Unix socket (the stub name
5291374ceda0...will be replaced during installation ofVector); - identifies the target architecture (32-bit vs 64-bit) and debug status;
- receives File Descriptors (FDs) for both the original
dex2oatbinary and theoat_hooklibrary; - reconstructs the command line, replacing the wrapper path with the original binary path and appending the "no-inline" flags;
- clears
LD_LIBRARY_PATHand setsLD_PRELOADto the hooker library's FD; - 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:
- Scan the memory map to find the
dex2oatbinary. - Locate and hook internal ART functions:
- 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.