xHook Documentation

repository·master·Indexed 26 days ago

https://github.com/iqiyi/xhook

A PLT (Procedure Linkage Table) hook library for Android native ELF libraries that allows developers to intercept and redirect function calls in native code without root access. It supports Android 4.0 - 10 (API 14 - 29) across armeabi, armeabi-v7a, arm64-v8a, x86, and x86_64 architectures. The library handles ELF HASH and GNU HASH indexed symbols, supports POSIX BRE for hook registration, and features Segmentation Fault Protection (SFP) to prevent crashes during pointer calculations.

Tokens
5.6K
Snippets
13
Records
27
Agent score
86%

What's inside xHook

  1. Overview of xHook

    master

    xHook is a PLT (Procedure Linkage Table) hook library designed for Android native ELF (executable and shared libraries). It allows developers to intercept function calls within loaded libraries by replacing PLT entries with custom functions.

    Key Features:

    • Compatibility: Supports Android 4.0 - 10 (API level 14 - 29).
    • Architecture Support: armeabi, armeabi-v7a, arm64-v8a, x86, and x86_64.
    • ELF Support: Handles both ELF HASH and GNU HASH indexed symbols, as well as SLEB128 encoded relocation info.
    • Flexibility: Supports setting hook info via POSIX BRE (Basic Regular Expression) and requires no root or special system permissions.
    • Zero Dependencies: Does not depend on any third-party shared libraries.
  2. Understand ELF structure for Android PLT hooking

    master

    xHook's Android PLT hooking mechanism relies on understanding the ELF (Executable and Linkable Format) structure. When a dynamic library is loaded into memory, it transitions from a Linking View (organized by sections) to an Execution View (organized by segments).

    To perform hooking, you must focus on the Execution View, which describes how data is organized in memory after the dynamic linker has mapped the file. The key components are:

    1. ELF Header: Contains the magic number 0x7F 0x45 0x4C 0x46 and provides the starting offsets and sizes for the SHT and PHT.
    2. SHT (Section Header Table): Records metadata for sections (type, offset, size, virtual address, alignment).
    3. PHT (Program Header Table): Records metadata for segments. Segments of type PT_LOAD are mapped into memory by the dynamic linker.
    4. PT_DYNAMIC Segment: A special segment containing the .dynamic section, which is critical for finding the memory locations of other sections and performing dynamic linking or hooking.
  3. Handle intermittent segmentation faults using SFP

    master

    Intermittent segmentation faults (SIGSEGV) may occur during hooking due to multi-threaded memory access (e.g., dlclose() or mprotect() being called by other threads) or Android ROM-specific memory protections.

    To prevent these from crashing the app, use SFP (Segmentation Fault Protection):

    1. Mark Danger Zones: Set a global flag before entering code that performs direct memory address calculations/writes (the "danger zone") and reset it immediately after.
    2. Register a Signal Handler: Implement a custom SIGSEGV handler.
    3. Use siglongjmp: Inside the handler, check the global flag. If the flag is set (meaning the crash happened in a danger zone), use siglongjmp to jump to a safe location outside the danger zone. If the flag is not set, restore the original system signal handler and return to allow the system to handle the crash normally.

    Best Practice:

    • Development/Debugging: Keep SFP disabled to catch actual coding errors.
    • Production: Keep SFP enabled to ensure app stability.
  4. Calculate the ELF Base Address on Android

    master

    Because ELF loading addresses are randomized at runtime, you must calculate the absolute address by adding the ELF's base address to the relative offset.

    On Android versions 5.0 (API 21) and above, you can use dl_iterate_phdr. For older versions (Android 4.0+), you should parse /proc/self/maps to find the base address. In /proc/self/maps, the starting address of the first entry for a specific library (where the offset is 00000000) is typically the base address.

  5. Build and Install xHook native libraries

    master

    To build the native libraries, you must use Android NDK r16b and ensure it is in your environment PATH. Note that support for armeabi was removed in NDK r17.

    Run the following commands to build and install:

    ./build_libs.sh
    ./install_libs.sh
    #!/bin/bash
    ./build_libs.sh
    ./install_libs.sh
  6. Prepare a test dynamic library (libtest.so) for PLT hooking

    master

    To demonstrate or test PLT hooking, you can create a sample dynamic library libtest.so that contains a function with a memory leak. This simulates a real-world scenario where a third-party or system library needs to be monitored or fixed via hooking.

    1. Create test.h with the function declaration.
    2. Create test.c implementing the function (e.g., a function that leaks memory using malloc).
    3. Create a main.c to call the function.
    4. Compile the files into libtest.so and a main executable.
    // test.h
    #ifndef TEST_H
    #define TEST_H 1
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void say_hello();
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    // test.c
    #include <stdlib.h>
    #include <stdio.h>
    
    void say_hello()
    {
        char *buf = malloc(1024);
        if(NULL != buf)
        {
            snprintf(buf, 1024, "%s", "hello\n");
            printf("%s", buf);
        }
    }
    // main.c
    #include <test.h>
    
    int main()
    {
        say_hello();
        return 0;
    }
  7. Understand the impact of compilation options on PLT hooking

    master

    The ability to hook external function calls via PLT depends on how the ELF was compiled. There are three main scenarios:

    1. Direct Calls: Can always be hooked regardless of compilation options. External function addresses are stored in .got.
    2. Global Function Pointer Calls: Can always be hooked regardless of compilation options. External function addresses are stored in .data.
    3. Local Function Pointer Calls:
      • With -O2 (default): The call is optimized into a direct call (Scenario 1), so it can be hooked.
      • With -O0: If the external function pointer was assigned to a local variable before the hook was executed, it cannot be hooked via PLT. If it is assigned after the hook, it can be hooked.

    Tip for protection: To make an ELF harder to PLT hook, compile with -O0 and assign external function pointers to local variables as early as possible.

  8. Understand the Android PLT Hook mechanism

    master
    Android PLT (Procedure Linkage Table) hooking works by intercepting calls to external symbols. When a library (e.g., libtest.so) calls an external function like malloc, it typically jumps to a .plt section. This section performs address calculations that eventually lead to a function pointer stored in the .got (Global Offset Table) or .data section. By overwriting the function pointer in the .got with the address of a custom function, you can redirect the execution flow to your own implementation.
  9. Calculate the precise base address of an ELF in memory

    master

    To perform accurate hooking, you must calculate the ELF's current base address in memory using the following steps:

    1. Locate the line in /proc/self/maps where the offset is 0 and the pathname matches your target ELF. Save the start address of this line as p0.
    2. Find the first segment in the ELF's Program Header Table (PHT) that has a type of PT_LOAD and an offset of 0. Save this segment's virtual memory relative address (p_vaddr) as p1.
    3. The base address is calculated as: p0 - p1.

    Note: In most ELFs, the p_vaddr of the first PT_LOAD segment is 0.

  10. Build and Install xHook

    master

    To build the native libraries, you must use Android NDK r16b and ensure it is in your PATH environment variable (support for armeabi was removed in r17).

    Run the following commands to compile and install the libraries:

    ./build_libs.sh
    ./install_libs.sh

    To run the provided Demo:

    cd ./xhookwrapper/
    ./gradlew assembleDebug
    adb install ./app/build/outputs/apk/debug/app-debug.apk
    #!/bin/bash
    ./build_libs.sh
    ./install_libs.sh
    
    # Demo steps
    cd ./xhookwrapper/
    ./gradlew assembleDebug
    adb install ./app/build/outputs/apk/debug/app-debug.apk
  11. Use xhook for PLT Hooking

    master

    Use the xhook library to perform PLT hooks more elegantly and avoid hardcoding memory offsets. xhook handles the complexity of finding symbol addresses and managing memory permissions automatically.

    Supported Architectures: armeabi, armeabi-v7a, and arm64-v8a. Supported Android Versions: Android 4.0+ (API level >= 14).

    #include <stdlib.h>
    #include <stdio.h>
    #include <test.h>
    #include <xhook.h>
    
    void *my_malloc(size_t size)
    {
        printf("%zu bytes memory are allocated by libtest.so\n", size);
        return malloc(size);
    }
    
    int main()
    {
        xhook_register(".*/libtest\.so$", "malloc", my_malloc, NULL);
        xhook_refresh(0);
        
        say_hello();
        return 0;
    }