Skip to content

Instantly share code, notes, and snippets.

@mooware
Last active November 29, 2023 16:59
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save mooware/1174572 to your computer and use it in GitHub Desktop.
Save mooware/1174572 to your computer and use it in GitHub Desktop.
Intercept C++ methods with LD_PRELOAD
// this file is an example of how to intercept a C++ method by using the
// LD_PRELOAD environment variable of the GNU dynamic linker.
//
// it works like this:
//
// 1) define a method that will have the same symbol as the intercepted
// method when compiled. For example, the method Foo::getValue()
// defined here has the mangled symbol "_ZNK3Foo8getValueEv".
// tools like nm, objdump or readelf can display the symbols of
// binaries. note that depending on compiler and linker options,
// some functions might not be replaceable with LD_PRELOAD.
//
// 2) if you want to call the original method from your new method,
// you can get a pointer to it with dlsym() and the mangled name.
//
// 3) compile your intercept code into its own shared library,
// for example with "g++ -g -Wall -shared -fPIC -ldl foo.cpp -o foo.so".
// "-ldl" should only be necessary if you use dlsym().
//
// 4) set LD_PRELOAD to the path of your new shared library and call the
// program in which you want to intercept something. in bash, you can
// do it like this: "LD_PRELOAD=./foo.so /usr/bin/some_program".
#include <string.h>
// necessary for RTLD_NEXT in dlfcn.h
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <dlfcn.h>
class Foo
{
public:
unsigned int getValue() const;
};
unsigned int Foo::getValue() const
{
// define the pointer-to-member type
typedef unsigned int (Foo::*methodType)() const;
static methodType origMethod = 0;
if (origMethod == 0)
{
// use the mangled method name here. RTLD_NEXT means something like
// "search this symbol in any libraries loaded after the current one".
void *tmpPtr = dlsym(RTLD_NEXT, "_ZNK3Foo8getValueEv");
// not even reinterpret_cast can convert between void* and a method ptr,
// so i'm doing the worst hack i've ever seen.
memcpy(&origMethod, &tmpPtr, sizeof(&tmpPtr));
}
// here we call the original method
unsigned int origValue = (this->*origMethod)();
return 1000 + origValue;
}
@charmoniumQ
Copy link

Mm. I was thinking of casting to methodType* not messageType. But I guess that would only work with virtual functions?

@mooware
Copy link
Author

mooware commented Feb 11, 2020

methodType already is a pointer. but it's a pointer-to-member-function, not a data pointer, which makes them incompatible.

See also here:
https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr

Technical details: pointers to member functions and pointers to data are not necessarily represented in the same way. A pointer to a member function might be a data structure rather than a single pointer. Think about it: if it’s pointing at a virtual function, it might not actually be pointing at a statically resolvable pile of code, so it might not even be a normal address — it might be a different data structure of some sort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment