PLTHook

repository·master·Indexed 21 days ago

https://github.com/kubo/plthook

A utility library for intercepting library function calls by modifying Procedure Linkage Table (PLT) entries in ELF format (Unix/Linux) or Import Address Table (IAT) entries in PE format (Windows). It allows developers to inject custom logic into existing library calls without modifying the target library's source code. Supports Linux, Windows, macOS, and other platforms via specific source implementations like plthook_elf.c, plthook_win32.c, and plthook_osx.c.

Tokens
1.9K
Snippets
3
Records
5
Agent score
25%

What's inside PLTHook

  1. What is PLTHook

    master

    PLTHook is a utility library used to intercept (hook) library function calls issued by specific object files (executables or libraries). It works by modifying the Procedure Linkage Table (PLT) entries in ELF format (Unix/Linux) or the Import Address Table (IAT) entries in PE format (Windows).

    When a function in a target file (e.g., program) calls an external function (e.g., foo_func() in libfoo.so), PLTHook redirects that call to a user-defined hook function instead of the original implementation. It does not affect calls made from within the library being hooked itself.

  2. How to hook a function from an external library

    master

    If your hook function is located in a file that is not the one being modified by plthook_open(), you can call the original function directly by its name. The system will use the PLT entry in your current library to jump to the original implementation.

    To use this, include plthook.h and the appropriate platform source file (plthook_elf.c, plthook_win32.c, or plthook_osx.c) in your project.

    #include "plthook.h"
    
    /* This function is called instead of recv() called by libfoo.so.1  */
    static ssize_t my_recv(int sockfd, void *buf, size_t len, int flags)
    {
        ssize_t rv;
        
        ... do your task: logging, etc. ...
        rv = recv(sockfd, buf, len, flags); /* call real recv(). */
        ... do your task: logging, check received data, etc. ...
        return rv;
    }
        
    int install_hook_function()
    {
        plthook_t *plthook;
        
        if (plthook_open(&plthook, "libfoo.so.1") != 0) {
            printf("plthook_open error: %s\n", plthook_error());
            return -1;
        }
        if (plthook_replace(plthook, "recv", (void*)my_recv, NULL) != 0) {
            printf("plthook_replace error: %s\n", plthook_error());
            plthook_close(plthook);
            return -1;
        }
        plthook_close(plthook);
        return 0;
    }
  3. How to hook a function when the hook is inside the modified file

    master

    If your hook function is located within the same file being modified by plthook_open(), calling the original function by name will cause an infinite recursion (the hook calls itself), leading to a stack overflow and process crash.

    To avoid this, you must capture the address of the original function and call it via a function pointer.

    Platform Differences:

    • Windows: The fourth argument of plthook_replace() provides the address of the original function.
    • Unix/Linux: The fourth argument of plthook_replace() does not set the address. You must use dlsym(RTLD_DEFAULT, "function_name") to retrieve the original function's address.
    static ssize_t (*recv_func)(int sockfd, void *buf, size_t len, int flags);
    
    /* This function is called instead of recv() called by libfoo.so.1  */
    static ssize_t my_recv(int sockfd, void *buf, size_t len, int flags)
    {
        ssize_t rv;
        
        ... do your task: logging, etc. ...
        rv = (*recv_func)(sockfd, buf, len, flags); /* call real recv(). */
        ... do your task: logging, check received data, etc. ...
        return rv;
    }
        
    int install_hook_function()
    {
        plthook_t *plthook;
        
        if (plthook_open_by_address(&plthook, &recv_func) != 0) {
            printf("plthook_open error: %s\n", plthook_error());
            return -1;
        }
        if (plthook_replace(plthook, "recv", (void*)my_recv, (void**)&recv_func) != 0) {
            printf("plthook_replace error: %s\n", plthook_error());
            plthook_close(plthook);
            return -1;
        }
    #ifndef WIN32
        // The address passed to the fourth argument of plthook_replace() is
        // available on Windows. But not on Unixes. Get the real address by dlsym().
        recv_func = (ssize_t (*)(int, void *, size_t, int))dlsym(RTLD_DEFAULT, "recv");
    #endif
        plthook_close(plthook);
        return 0;
    }
  4. Enumerate PLT/IAT entries

    master

    PLTHook provides a mechanism to iterate through all entries in the PLT/IAT of a specified file using plthook_enum(). This is useful for inspecting which functions are being imported by a library.

    void print_plt_entries(const char *filename)
    {
        plthook_t *plthook;
        unsigned int pos = 0; /* This must be initialized with zero. */
        const char *name;
        void **addr;
    
        if (plthook_open(&plthook, filename) != 0) {
            printf("plthook_open error: %s\n", plthook_error());
            return -1;
        }
        while (plthook_enum(plthook, &pos, &name, &addr) == 0) {
            printf("%p(%p) %s\n", addr, *addr, name);
        }
        plthook_close(plthook);
        return 0;
    }
  5. Supported Platforms

    master

    PLTHook supports various platforms depending on the source file implementation used:

    Platformsource filestatus
    Linux i386 and x86_64plthook_elf.ctested
    Linux arm, aarch64, powerpc and powerpc64leplthook_elf.ctested on QEMU
    Windows 32-bit and x64 (MSVC)plthook_win32.ctested
    macOS (intel)plthook_osx.ctested
    macOS (arm)plthook_osx.ctested
    Windows 32-bit and x64 (Mingw32 and Cygwin)plthook_win32.cperhaps
    Solaris x86_64plthook_elf.cperhaps
    FreeBSD i386 and x86_64plthook_elf.cperhaps
    Androidplthook_elf.cperhaps