Giter Club home page Giter Club logo

plthook's Introduction

PLTHook

tests

What is plthook.

A utility library to hook library function calls issued by specified object files (executable and libraries). This modifies PLT (Procedure Linkage Table) entries in ELF format used on most Unixes or IAT (Import Address Table) entries in PE format used on Windows.

What is PLT (or IAT)

Note: This isn't precise explanation. Some details are omitted.

When a function calls another function in another file, it is called via PLT (on Unix using ELF) or IAT (on Windows).

figure1

In order to call foo_func() in libfoo.so, the address of the callee must be known. When callers are in the same file, the relative address to the callee is known at compile time regardless of the absolute address at run time. So some_func() calls foo_func() using relative addressing.

When callers are in other files, the address of the callee cannot be known at compile time. To resolve it, each file has a mapping from external function names to addresses. The callers directly look at the address in the PLT entry for foo_func() and jump to the address.

The addresses in PLT entries are resolved (1) at process startup or (2) at first function call (lazy binding). It depends on OSes or on settings.

What plthook does.

figure2

Plthook changes the address in PLT entries as above. When foo_func() is called from program, hook_foo_func() is called instead. It doesn't change function calls from libfoo.so and libbar.so.

How to call original functions from hook functions.

When hook functions are outside of modified files

figure3

When the hook function hook_foo_func() is in libbar.so, just call the original function foo_func(). It looks the PLT entry in libbar.so and jumps to the original.

When hook functions are inside of modified files

figure4

When the hook function hook_foo_func() is in program, do not call the original function foo_func() because it jumps to hook_foo_func() repeatedly and crashes the process after memory for stack is exhausted. You need to get the address of the original function and set it to the function pointer variable foo_func_addr. Use the fourth argument of plthook_replace() to get the address on Windows. Use the return value of dlsym(RTLD_DEFAULT, "foo_func") on Unixes. The fourth argument of plthook_replace() isn't available on Unixes because it doesn't set the address of the original before the address in the PLT entry is resolved.

Changes

2019-02-17: Support plthook_open_by_address() and change internal logic of plthook_open() on Android.

2019-02-17: Stop checking RELRO and check memory protection at runtime instead.

2019-02-03: Fix crash when programs are compiled with compiler options -Wl,-z,relro and -fno-plt with the help of JC Liang. (#10)

2018-02-06: Android support was contributed by Daniel Deptford.

2017-10-01: plthook_elf.c was rewritten. Plthook had needed to read files on filesystem to get various information about target object files. It now do it only for full RELRO object files. Note that plthook before 2017-10-01 gets segmentation fault while hooking a prelinked file on Linux.

2017-09-18: Fixed for processes on valgrind on Linux.

Usage

If you have a library libfoo.so.1 and want to intercept a function call recv() without modifying the library, put plthook.h and plthook_elf.c, plthook_win32.c or plthook_osx.c in your source tree and add the following code.

#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;
}

The above code doesn't work when my_recv() is in the file opened by plthook_open() as described here. Use the following code instead in the case.

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;
}

Note that built-in functions cannot be hooked. For example the C compiler in macOS Sierra compiles ceil() as inline assembly code, not as function call of ceil in the system library.

When a functions is imported by ordinal on Windows, the function name is specified by export_dll_name:@ordinal. For example api-ms-win-shcore-path-l1-1-0.dll:@170.

Another Usage

PLTHook provides a function enumerating PLT/IAT entries.

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;
}

Supported Platforms

Platform source file status
Linux i386 and x86_64 plthook_elf.c tested using github actions
Linux arm, aarch64, powerpc and powerpc64le plthook_elf.c tested on QEMU using github actions
Windows 32-bit and x64 (MSVC) plthook_win32.c tested using github actions
macOS (intel) (*5) plthook_osx.c tested using github actions
macOS (arm) plthook_osx.c probably(*4)
Windows 32-bit and x64 (Mingw32 and Cygwin) plthook_win32.c perhaps(*2)
Solaris x86_64 plthook_elf.c perhaps(*1)
FreeBSD i386 and x86_64 except i386 program on x86_64 OS plthook_elf.c perhaps(*1)
Android(*3) plthook_elf.c perhaps(*2)

*1 Tested on a local VM before.
*2 Tested on travis-ci.org before.
*3 Contributed by Daniel Deptford.
*4 Tested on bitrise M1 stacks. (2022-09-19)
*5 10.14 Mojave support was dropped on 2022-09-19.

License

2-clause BSD-style license.

plthook's People

Contributors

baldurk avatar joromaser avatar jwilk avatar kubo avatar luzpaz avatar michalbiesek avatar milabs avatar rumblehhh avatar tntljc avatar wnxd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plthook's Issues

plthook_elf.c: plthook_open() fails on executables with local hint symbols (__INIT_ARRAY, _end)

We came across an executable in which the plthook_open_by_handle() fails as
dlsym() can't find the __INIT_ARRAY__ and _end symbols. The _end symbol
seems to be local in the file:

micha ~/temp/plthook $ nm -CD example | egrep -w "__INIT_ARRAY__|_end"
0111ab6c b _end

It might be because this is executable was extracted from a UPX packed
executable. Unlike _end the _start symbol is exported:

001b3d48 T _start

We currently added a call to dlsym(hndl, "_start") in order to make it
work.

A minor typo issue.

Hi,

Thanks for your great efforts in developing plthook. It is a very useful tool.

While I am going through the code, I found a minor typo in code which in most cases does not affect the correctness but may be wrong sometimes.

I feel both below statements should use dyn->d_un.d_val instead of dyn->d_un.d_ptr.

total_size = dyn->d_un.d_ptr;

elem_size = dyn->d_un.d_ptr;

It is quite trivial, so I hope I would not bother you too much.

Thanks.

Hooking all loaded libraries

I'm writing a library and want to intercept and take some action on any call to fork. This requires overriding the function in all loaded shared libraries. Users install the interceptor after process start so LD_PRELOAD is out of the question, and instead I'm using plthook.

Currently, my approach is:

  1. Get all mapped ELF headers (i.e. that start with "\x7fELF") in /proc/self/maps
  2. Convert them to a valid address to use with plthook_open_by_address(&hook, (void *) address)
  3. Call plthook_replace(hook, "fork", (void *) my_fork, NULL) to actually do work
  4. Call plthook_replace(hook, "dlopen", (void *) my_dlopen, NULL) to intercept and hook any future loaded libraries

Is this the most straightforward use of plthook to accomplish the stated goal?

undefined reference to `plthook_open`

#include <iostream>
#include <thread>

#include <string>


#include "plthook/plthook.h"


 int mop(void);
ssize_t recv(){
   
ssize_t rv = mop();
return rv;
};

int main(){
    plthook_t *hook;
    plthook_open(&hook, "vulkan.elf");
   recv();

};

where i get

/usr/bin/ld: /tmp/ccjaXJTy.o: in function `recv()':
main.cpp:(.text+0x9): undefined reference to `mop()'
/usr/bin/ld: /tmp/ccjaXJTy.o: in function `main':
main.cpp:(.text+0x42): undefined reference to `plthook_open'
collect2: error: ld returned 1 exit status

the same with plthook_open_by_address
i tried compiling it including the plthook_elf.c
and got

plthook_elf.c: In function ‘int plthook_open_real(plthook_t**, link_map*)’:
plthook_elf.c:655:26: error: invalid conversion from ‘void*’ to ‘plthook_t*’ {aka ‘plthook*’} [-fpermissive]
  655 |     *plthook_out = malloc(sizeof(plthook_t));
      |                    ~~~~~~^~~~~~~~~~~~~~~~~~~
      |                          |
      |                          void*

any help?
compiling with g++

Conversion from function pointer to void* isn't portable

Hi :)

Thank you for your library, one minor nitpick though: the plthook_replace function takes the function pointer as a void*, and that conversion isn't guaranteed to work per C11 standard, 6.3.2.3 §1 :

"A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer."

So the conversion from/to void* is only guaranteed to work for object pointers, it says nothing about function pointers. However, (C11 standard, 6.3.2.3 §8) :

"A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer."

So an easy fix could be to change void* to void (*)(void), which could be typedef'd to something like plthook_func or similar.

Thanks!

plthook failed to hook function calls of system library on macOS platform

When i hook read/write of socket function on macOS Platform as follows, it reported "segmentation fault".

ssize_t hook_read(int fildes, void *buf, size_t nbyte) {
    ssize_t rv;
    rv = read(fildes, buf, nbyte);
    printf("Hook read end\n");
    return rv;
}

void install_hook() {
    plthook_t *plthook;
    void *handle;
    // const char *filename = "/usr/lib/libc.dylib";  // this also not work
    const char *filename = "/usr/lib/libSystem.B.dylib";
	if (plthook_open(&plthook, filename) != 0) {
        printf("plthook_open error: %s\n", plthook_error());
        return;
    }
    if (plthook_replace(plthook, "read", (void*)hook_read, NULL) != 0) {
        printf("plthook_replace error: %s\n", plthook_error());
        plthook_close(plthook);
        return;
    }
    plthook_close(plthook);

}

How to hook a member function of some class in C++ ?

I want to hook a member function of some class in C++, for example, Helper::func()

class Helper {
public:
    void func(){
        // do something here...
    }
}

But I have not found related document here. Is this possible with plthook? If I want to achieve this, what efforts do I need to make? Thanks in advance.

Can I hook the function called by a referenced library?

@kubo Hello, kubo
Thanks for your great tools, I have a question about usage.
If I wirte a program a. The program a use a function b in LIBRARY B, function b calls the function c in LIBRARY C. I cant edit the LIBRARY B and LIBRARY C, LIBRARY B is the further wrapper of LIBRARY C。I use the API provided by LIBRARY B. Can I use the methods in Readme to hook the function c in program a?

If not, can u tell me how to implement this feature?

plthook_replace error: no such function: malloc

I tested codes like below but get no such function error. Is there something wrong and how to know which function could be hooked? BTW, I used same way to hook puts function is ok. thanks!

static void* (*malloc_hook_func)(size_t size);
static void* malloc_hook(size_t size)
{
        char* buffer = (char*) (*malloc_hook_func)(size); 
        return buffer;
}

// install hook
if (plthook_open_by_address(&plthook, &malloc_hook_func) != 0) {
         printf("plthook_open error: %s\n", plthook_error());
         return -1;
}
if (plthook_replace(plthook, "malloc", (void*)malloc_hook, (void**)&malloc_hook_func) != 0) {
         printf("plthook_replace error: %s\n", plthook_error());
         plthook_close(plthook);
         return -1;
}
malloc_hook_func = (void* (*)(size_t size))dlsym(RTLD_DEFAULT, "malloc");

hook function from vtable

hello, how to hook function from vtable with plthook on android armv7-a?
i tried this
if(plthook_replace(plthook, "_ZN25StartMenuScreenController4tickEv", (void*)my_startMenuTick, (void**)&startMenuTick) != 0) return 1;



static unsigned int (*startMenuTick)(void *thiz);
static unsigned int my_startMenuTick(void *thiz) { __android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "%s\n", "my_startMenuTick"); }
but "my_startMenuTick" message is not shown via logcat, although messages from other functions are displayed
and there were no errors while applying the hook

ssize_t rv was not decleared

#include "plthook.h"

ssize_t recv(){
ssize_t rv = main();
return rv;
};

int main(){
    plthook_t *hook;
    plthook_open(&hook, "vulkan.elf");
   recv();

};

i compile it with g++ main.cpp
and on the vulkan.elf i have everything in extern "C"{}
all plhook stuff is in the same directory as main.cpp
how do i solve this?
compiler error =

main.cpp: In function ‘ssize_t recv()’:
main.cpp:12:14: error: ‘main’ was not declared in this scope
   12 | ssize_t rv = main();
      |              ^~~~

[Android] Hook dlopen

As root user is it possible to hook dlopen to get reading & writing access on /proc/$pid/mem ?

`plthook_win32.c` bug

GetModuleHandleEx will increments the module's reference count.
Please add flag GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT

A fatal error about the handle is invalid

dlclose(hndl);

I am working on 64-bit ubuntu20.04.
I was learning ELF and hook recently, so I found pltkook.
plthook is so cool,plthook is a very useful tool,
I learned how to hook a dynamic library function, thanks.

but I found a fatal error,
I think ... lmap is equal to hndl,
so, if hndl is invalid after dlclose, lmap is invalid too.

I add memset into here:

if (dlinfo(hndl, RTLD_DI_LINKMAP, &lmap) != 0) {
        set_errmsg("dlinfo error");
        dlclose(hndl);
        return PLTHOOK_FILE_NOT_FOUND;
    }
    dlclose(hndl);
    memset(hndl, 0x00, 16+1);
    return plthook_open_real(plthook_out, lmap);

lmap's l_addr and l_ld are invalid,then the program crashed

how about adding the hndl into struct plthook:

struct plthook {
   void* hndl;
   const Elf_Sym *dynsym;
   const char *dynstr;
   size_t dynstr_size;
   const char *plt_addr_base;
   const Elf_Plt_Rel *rela_plt;
   size_t rela_plt_cnt;
#ifdef R_GLOBAL_DATA
   const Elf_Plt_Rel *rela_dyn;
   size_t rela_dyn_cnt;
#endif
};

Can you reply me?
thanks

Document `plthook_open_by_address`

It's not specified where address can come from - on Linux it would be anything that would be passed to dladdr1 i.e. a pointer to some location in a loaded program header segment.

Is it possible to access VR service and hook custom code on Android?

I want to apply custom distortion to any android VR app. Can I use this hook to access its GPU context and hook my custom code to create distortion ?

https://stackoverflow.com/questions/50753051/accessing-android-app-gpu-context

Is it possible to access any VR Android app's GPU context i.e. what images/graphics the other VR app is rendering on screen from our custom app. I want to get the images rendered by other VR app and apply custom distortion. If yes, how can it be achieved?

My idea was to hook a service the the buffer stream, i.e. read whatever is going to be displayed on screen, apply custom distortion/filter and display it back. Will SwapChain from gvr-android-sdk work in this scenario? Reading GPU buffer from an android service and displaying it back. (For now we don't worry about the DRM protection etc. later we'll be askig for permissions)

https://stackoverflow.com/questions/50920427/android-access-vr-apps-gpu-context

plthook_open error: dlopen error: library "data/user/0/com.kyhsgeekcode.disassembler/files/tmp.so" wasn't loaded and RTLD_NOLOAD prevented it

I really thank you for this library! However,

I got this error using this code:

                plthook_t *plthook;
		unsigned int pos = 0; /* This must be initialized with zero. */
		const char *name;
		void **addr;
		if (plthook_open(&plthook, filename) != 0)
		{
			__android_log_print(ANDROID_LOG_ERROR, "Disassembler","plthook_open error: %s\n", plthook_error());
			return NULL;
		}

So I tried modifying to

static int plthook_open_shared_library(plthook_t **plthook_out, const char *filename)
{
    void *hndl = dlopen(filename, RTLD_LAZY /*| RTLD_NOLOAD*/);
    struct link_map *lmap = NULL;

    if (hndl == NULL) {
        set_errmsg("dlopen error: %s", dlerror());
        return PLTHOOK_FILE_NOT_FOUND;
    }

Then I get this error:

12-25 19:36:12.580 E/linker (954): library "/storage/emulated/0/adaTest/libhello-jni.so" ("/storage/emulated/0/adaTest/libhello-jni.so") needed or dlopened by "/data/app/com.kyhsgeekcode.disassembler-2/lib/arm/libhello-jni.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/com.kyhsgeekcode.disassembler-2/lib/arm:/data/app/com.kyhsgeekcode.disassembler-2/base.apk!/lib/armeabi-v7a", permitted_paths="/data:/mnt/expand:/data/data/com.kyhsgeekcode.disassembler"]
12-25 19:36:12.580 E/Disassembler(954): plthook_open error: dlopen error: dlopen failed: library "/storage/emulated/0/adaTest/libhello-jni.so" needed or dlopened by "/data/app/com.kyhsgeekcode.disassembler-2/lib/arm/libhello-jni.so" is not accessible for the namespace "classloader-namespace"

Do you know how to use it well on android?

Library doesn't support the new LC_DYLD_CHAINED_FIXUPS and LC_DYLD_EXPORTS_TRIE macos linker commands

There are two new load commands added in 10.15 / iOS 13 (http://newosxbook.com/articles/13-10.15.html and can be seen in /Applications.Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/mach-o/loader.h:324

#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD) /* used with linkedit_data_command, payload is trie */

#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) /* used with linkedit_data_command */

Without handling these linker commands, the functionality of the library is restricted to macOS < 10.15

Failed to find symbol when using C# runtime.

We have existing DLL that works on C++, C# both runtime.
and we wanted to hook on WSASocketW, WSASocketA but it works fine with C++ base but not working on C# base. (same DLL implementation, but loaded from another executable)
I can see there is WSASocket functions on C++ base using plthook_enum but C# one does not.

maybe there is difference between IAT mapping on .NET runtime?

Not found function "recv" and any functions from Socket API in libc

@kubo Hello

Thanks for your project, I have a usage question, is it possible to intercept a function with STB_LOCAL binding, because it seems that these functions are not in PLT

Some code:

 plthook_t *plthook;
  if (plthook_open(&plthook, "libc.so.6") != 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;
}

Why mprotect not needed for osx?

HI,

Thank you for a wonderful tool. I was curious why mprotect(PROT_WRITE) was not needed before modifying the PLT on osx? I thought that __TEXT and __LINKEDIT segments did not have write permission?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.