QBDIPreload¶
The examples from the API sections demonstrated a use case where the instrumented code, QBDI and the instrumentation are compiled in the same binary. This, however, only works for a very limited amount of real-world scenarios. Other scenarios require some injection tool that can load QBDI and your instrumentation code in another binary.
QBDIPreload is a small utility library that provides code injection capabilities using dynamic
library injection. It currently only works under linux using the LD_PRELOAD
mechanism and
macOS using the DYLD_INSERT_LIBRARIES
mechanism. For other platforms please look into using
Frida/QBDI instead.
QBDIPreload exploits these library injection mechanisms to hijack the normal program startup.
During the hijacking process QBDIPreload will callback your code allowing you to setup and start
your instrumentation. The compilation should produce a dynamic library (.so
under linux,
.dylib
under macOS) which should then be added to the matching environment variable
(LD_PRELOAD
under linux and DYLD_INSERT_LIBRARIES
under macOS) when running the
target binary.
You can look at the QBDIPreload Template for a working example with build and usage instructions.
Note
QBDIPreload automatically takes care of blacklisting instrumentation of the C standard library and the OS loader as described in Limitations.
Note
Please note that QBDIPreload does not allow to instrument a binary before the main function (inside the loader and the library constructors / init) as explained in Limitations.
QBDIPreload API¶
Initialization¶
Initialization is performed using a constructor:
#include <QBDIPreload.h>
QBDIPRELOAD_INIT;
-
QBDIPRELOAD_INIT
¶ A C pre-processor macro declaring a constructor.
- Warning
QBDIPRELOAD_INIT
must be used once in any project using QBDIPreload. It declares a constructor, so it must be placed like a function declaration on a single line.
Return Codes¶
-
QBDIPRELOAD_NO_ERROR
¶ No error.
-
QBDIPRELOAD_NOT_HANDLED
¶ Startup step not handled by callback.
-
QBDIPRELOAD_ERR_STARTUP_FAILED
¶ Error in the startup (preload) process.
User callbacks¶
Each of these functions must be declared and will be called (in the same order than in this documentation) by QBDIPreload during the hijacking process.
-
int
QBDI::
qbdipreload_on_start
(void *main)¶ Function called when preload is on a program entry point (interposed start or an early constructor). It provides the main function address, that can be used to place a hook using the
qbdipreload_hook_main
API.- Return
- int QBDIPreload state
- Parameters
main
: Address of the main function
-
int
QBDI::
qbdipreload_on_premain
(void *gprCtx, void *fpuCtx)¶ Function called when preload hook on main function is triggered. It provides original (and platforms dependent) GPR and FPR contexts. They can be converted to QBDI states, using
qbdipreload_threadCtxToGPRState
andqbdipreload_floatCtxToFPRState
APIs.- Return
- int QBDIPreload state
- Parameters
gprCtx
: Original GPR contextfpuCtx
: Original FPU context
-
int
QBDI::
qbdipreload_on_main
(int argc, char **argv)¶ Function called when preload has successfully hijacked the main thread and we are in place of the original main function (with the same thread state).
- Return
- int QBDIPreload state
- Parameters
argc
: Original argcargv
: Original argv
-
int
QBDI::
qbdipreload_on_run
(VMInstanceRef vm, rword start, rword stop)¶ Function called when preload is done and we have a valid QBDI VM object on which we can call run (after some last initializations).
- Return
- int QBDIPreload state
- Parameters
vm
: VM instance.start
: Start address of the range (included).stop
: End address of the range (excluded).
-
int
QBDI::
qbdipreload_on_exit
(int status)¶ Function called when process is exiting (using
_exit
orexit
).- Return
- int QBDIPreload state
- Parameters
status
: exit status
Helpers¶
qbdipreload_hook_main()
can be used to hook any address
as main during the hijacking process.
-
int
QBDI::
qbdipreload_hook_main
(void *main)¶ Enable QBDIPreload hook on the main function (using its address)
- Warning
- It MUST be used in
qbdipreload_on_start
if you want to handle this step. The assumedmain
address is provided as a callback argument. - Parameters
main
: Pointer to the main function
Contexts related helpers allow to convert a platform dependent GPR or FPR state structure to a QBDI structure.
Under linux both functions should receive a ucontext_t*
and under macOS they should
receive a x86_thread_state64_t*
or a x86_float_state64_t*
. Please look into QBDIPreload
source code for more information.
-
void
QBDI::
qbdipreload_threadCtxToGPRState
(const void *gprCtx, GPRState *gprState)¶ Convert a QBDIPreload GPR context (platform dependent) to a QBDI GPR state.
- Parameters
gprCtx
: Platform GPRState pointergprState
: QBDI GPRState pointer
-
void
QBDI::
qbdipreload_floatCtxToFPRState
(const void *fprCtx, FPRState *fprState)¶ Convert a QBDIPreload FPR context (platform dependent) to a QBDI FPR state.
- Parameters
fprCtx
: Platform FPRState pointerfprState
: QBDI FPRState pointer
QBDIPreload Template¶
To get started with QBDIPreload you can follow those few simple steps:
$ mkdir QBDIPreload && cd QBDIPreload
$ qbdi-preload-template
$ mkdir build && cd build
$ cmake ..
$ make
This will simply build the default QBDIPreload template (which prints instruction address and disassembly) and can be executed doing the following under linux:
$ LD_PRELOAD=./libqbdi_tracer.so /bin/ls
Or the following under macOS:
$ cp /bin/ls ./ls
$ sudo DYLD_INSERT_LIBRARIES=./libqbdi_tracer.so ./ls
Note
Please note that, under macOS, the System Integrity Protection (SIP) will prevent you from instrumenting system binaries. You must either use a local copy of the binary or disable SIP.