ELF PLT Hook: Principles, Usage, and Implementation on Android
ELF PLT Hook on Android intercepts shared‑library calls by overwriting GOT/PLT entries, providing a stable production‑ready method for tasks such as APM monitoring, APK unpacking and performance profiling, with a clear workflow, dynamic‑linking background, and a practical implementation using the plthook library.
ELF PLT Hook is a technique used on Android (a Linux‑based OS) to intercept function calls in shared libraries by modifying the Global Offset Table (GOT) or Procedure Linkage Table (PLT). It is stable and widely adopted in production environments.
Usage scenarios include APM monitoring (hooking low‑level I/O APIs), APK unpacking (hooking dex loading functions), and performance profiling (e.g., Facebook’s profilo library uses PLT Hook to enable atrace in release builds).
ELF file structure – an ELF file consists of an ELF header, program header table, section header table, and associated data (program segments, sections, etc.). The format is used for executables, shared objects (.so), object files (.o) and core dumps.
Dynamic linking and loading – at program start the kernel loads the ELF image, then the dynamic linker reads the .dynamic section, loads required shared libraries, and resolves symbols via the GOT and PLT. The first call to an external function goes through a PLT stub that jumps to a GOT entry; the GOT entry is initially pointing to the runtime resolver ( dl_runtime_resolve ) and is patched with the real address after the first call (lazy binding).
PLT Hook core mechanism – by replacing the function address stored in the PLT (or the corresponding GOT entry) with a custom implementation, calls to the original function are redirected.
Example workflow to build a test library and executable:
#include void say_hello();
#include "testa.h" #include void say_hello(){ printf("Hello,World! \n"); }
gcc ./testa.c -fPIC -shared -o libtesta.so
#include "testa.h" #include int main(){ say_hello(); return 0; }
gcc main.c -L. -ltesta -o main
Running ./main prints Hello,World . The generated binary can be inspected with:
objdump -d -M intel -S main
Relevant excerpt of the PLT section:
.plt: 5d0: ff 35 ea 09 20 00 push QWORD PTR [rip+0x2009ea] # _GLOBAL_OFFSET_TABLE_+0x8 5d6: ff 25 ec 09 20 00 jmp QWORD PTR [rip+0x2009ec] # _GLOBAL_OFFSET_TABLE_+0x10 say_hello@plt: 5e0: ff 25 ea 09 20 00 jmp QWORD PTR [rip+0x2009ea] # 5e6: 68 00 00 00 00 push 0x0 5eb: e9 e0 ff ff ff jmp 5d0 <.plt>
To locate a symbol at runtime, the typical steps are:
void *hndl = dlopen(filename, RTLD_LAZY | RTLD_NOLOAD); char *addr = dlsym(hndl, symbols[i]);
Finding the base address of a loaded library can be done with dl_iterate_phdr :
struct dl_iterate_data data = {0}; data.addr = address; dl_iterate_phdr(dl_iterate_cb, &data); static int dl_iterate_cb(struct dl_phdr_info *info, size_t size, void *cb_data) { struct dl_iterate_data *data = (struct dl_iterate_data*)cb_data; for (unsigned idx = 0; idx < info->dlpi_phnum; ++idx) { const Elf_Phdr *phdr = &info->dlpi_phdr[idx]; char *base = (char*)info->dlpi_addr + phdr->p_vaddr; if (base <= data->addr && data->addr < base + phdr->p_memsz) { break; } } // locate PT_DYNAMIC and store l_addr, l_ld … return 0; }
Replacing a PLT entry with a custom function using the plthook library:
static int check_rel(const plthook_t *plthook, const Elf_Plt_Rel *plt, Elf_Xword r_type, const char **name_out, void ***addr_out) { if (ELF_R_TYPE(plt->r_info) == r_type) { size_t idx = ELF_R_SYM(plt->r_info); *name_out = plthook->dynstr + plthook->dynsym[idx].st_name; *addr_out = (void**)(plthook->plt_addr_base + plt->r_offset); return 0; } return -1; } int plthook_replace(plthook_t *plthook, const char *funcname, void *funcaddr, void **oldfunc) { unsigned int pos = 0; const char *name; void **addr; while (plthook_enum(plthook, &pos, &name, &addr) == 0) { if (strncmp(name, funcname, strlen(funcname)) == 0) { int prot = get_memory_permission(addr); if (!(prot & PROT_WRITE)) mprotect(ALIGN_ADDR(addr), page_size, PROT_READ|PROT_WRITE); if (oldfunc) *oldfunc = *addr; *addr = funcaddr; if (!(prot & PROT_WRITE)) mprotect(ALIGN_ADDR(addr), page_size, prot); return 0; } } return PLTHOOK_FUNCTION_NOT_FOUND; }
Other native hooking methods (InlineHook, TrapHook) exist but are less common in production; GOT/PLT hooking remains the preferred approach, especially on Android where libraries are often loaded non‑lazily.
DeWu Technology
A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.