Injector
Macros | Typedefs | Functions
injector.h File Reference

Library for injecting a shared library into a Linux, Windows and macOS process. More...

#include <sys/types.h>
#include <stdarg.h>
#include <stdint.h>
Include dependency graph for injector.h:

Go to the source code of this file.

Macros

#define INJERR_SUCCESS   0 /* linux, windows, macos */
 
#define INJERR_OTHER   -1 /* linux, windows, macos */
 
#define INJERR_NO_MEMORY   -2 /* linux, windows, macos */
 
#define INJERR_NO_PROCESS   -3 /* linux, windows, macos */
 
#define INJERR_NO_LIBRARY   -4 /* linux */
 
#define INJERR_NO_FUNCTION   -4 /* linux */
 
#define INJERR_ERROR_IN_TARGET   -5 /* linux, windows, macos */
 
#define INJERR_FILE_NOT_FOUND   -6 /* linux, windows, macos */
 
#define INJERR_INVALID_MEMORY_AREA   -7 /* linux, macos */
 
#define INJERR_PERMISSION   -8 /* linux, windows, macos */
 
#define INJERR_UNSUPPORTED_TARGET   -9 /* linux, windows, macos */
 
#define INJERR_INVALID_ELF_FORMAT   -10 /* linux */
 
#define INJERR_WAIT_TRACEE   -11 /* linux */
 
#define INJERR_FUNCTION_MISSING   -12 /* linux, windows, macos */
 
#define INJECTOR_HAS_REMOTE_CALL_FUNCS   1
 
#define INJECTOR_HAS_INJECT_IN_CLONED_THREAD   1
 

Typedefs

typedef pid_t injector_pid_t
 Platform-dependent process id type (pid_t on Unix. DWORD on Windows)
 
typedef struct injector injector_t
 

Functions

int injector_attach (injector_t **injector, injector_pid_t pid)
 Attach to the specified process. More...
 
int injector_detach (injector_t *injector)
 Detach from the attached process and destroy the specified handle. More...
 
int injector_inject (injector_t *injector, const char *path, void **handle)
 Inject the specified shared library into the target process. More...
 
int injector_uninject (injector_t *injector, void *handle)
 Uninject the shared library specified by handle. More...
 
int injector_call (injector_t *injector, void *handle, const char *name)
 Call the specified function taking no arguments in the target process (Linux and macOS only) More...
 
const char * injector_error (void)
 Get the message of the last error. More...
 
int injector_remote_func_addr (injector_t *injector, void *handle, const char *name, size_t *func_addr_out)
 Get the function address in the target process (Linux and Windows only) More...
 
int injector_remote_call (injector_t *injector, intptr_t *retval, size_t func_addr,...)
 Call the function in the target process (Linux and Windows only) More...
 
int injector_remote_vcall (injector_t *injector, intptr_t *retval, size_t func_addr, va_list ap)
 Call the function in the target process (Linux and Windows only) More...
 
int injector_inject_w (injector_t *injector, const wchar_t *path, void **handle)
 Same with injector_inject except the type of the path argument. (Windows only) More...
 
int injector_inject_in_cloned_thread (injector_t *injector, const char *path, void **handle)
 Inject the specified shared library into the target process by the clone system call. (Linux x86_64 only) More...
 

Detailed Description

Library for injecting a shared library into a Linux, Windows and macOS process.

Function Documentation

◆ injector_attach()

int injector_attach ( injector_t **  injector,
injector_pid_t  pid 
)

Attach to the specified process.

Parameters
[out]injectorthe address where the newly created injector handle will be stored
[in]pidthe process id to be attached
Returns
zero on success. Otherwise, error code

◆ injector_call()

int injector_call ( injector_t *  injector,
void *  handle,
const char *  name 
)

Call the specified function taking no arguments in the target process (Linux and macOS only)

Parameters
[in]injectorthe injector handle specifying the target process
[in]handlethe module handle created by injector_inject or special-handles such as RTLD_DEFAULT
[in]namethe function name

The handle and name arguments are passed to dlsym (Linux, macOS) and then the return value of dlsym is called without arguments in the target process.

This is same with the combination of injector_remote_func_addr() and injector_remote_call() without extra arguments.

Note
(Linux only) If the function in the target process internally calls non-async-signal-safe functions, it may stop the target process or cause unexpected behaviour.
See also
injector_remote_func_addr(), injector_remote_call(), injector_remote_vcall()

◆ injector_detach()

int injector_detach ( injector_t *  injector)

Detach from the attached process and destroy the specified handle.

Parameters
[in]injectorthe injector handle to destroy
Returns
zero on success. Otherwise, error code

◆ injector_error()

const char* injector_error ( void  )

Get the message of the last error.

Remarks
The message is updated only when injector functions return non-zero.

◆ injector_inject()

int injector_inject ( injector_t *  injector,
const char *  path,
void **  handle 
)

Inject the specified shared library into the target process.

Parameters
[in]injectorthe injector handle specifying the target process
[in]paththe path name of the shared library
[out]handlethe address where the newly created module handle will be stored
Returns
zero on success. Otherwise, error code

Note on Linux: This calls functions inside of the target process interrupted by ptrace(). If the target process is interrupted while holding a non-reentrant lock and injector calls a function requiring the same lock, the process stops forever. If the lock type is reentrant, the status guarded by the lock may become inconsistent. As far as I checked, dlopen() internally calls malloc() requiring non-reentrant locks. dlopen() also uses a reentrant lock to guard information about loaded files.

◆ injector_inject_in_cloned_thread()

int injector_inject_in_cloned_thread ( injector_t *  injector,
const char *  path,
void **  handle 
)

Inject the specified shared library into the target process by the clone system call. (Linux x86_64 only)

Parameters
[in]injectorthe injector handle specifying the target process
[in]paththe path name of the shared library
[out]handlethe address where the newly created module handle will be stored
Returns
zero on success. Otherwise, error code

This calls dlopen() in a thread created by clone(). Note that no wonder there are unexpected pitfalls because some resources allocated in pthread_create() lack in the clone()-ed thread. Use it at your own risk.

◆ injector_inject_w()

int injector_inject_w ( injector_t *  injector,
const wchar_t *  path,
void **  handle 
)

Same with injector_inject except the type of the path argument. (Windows only)

Parameters
[in]injectorthe injector handle specifying the target process
[in]paththe path name of the shared library
[out]handlethe address where the newly created module handle will be stored
Returns
zero on success. Otherwise, error code

◆ injector_remote_call()

int injector_remote_call ( injector_t *  injector,
intptr_t *  retval,
size_t  func_addr,
  ... 
)

Call the function in the target process (Linux and Windows only)

Parameters
[in]injectorthe injector handle specifying the target process
[out]retvalNULL or the address where the return value of the function call will be stored
[in]func_addrthe function address in the target process
[in]...arguments passed to the function
Returns
zero on success. Otherwise, error code
Remarks
The types of the arguments must be integer or pointer. If it is a pointer, it must point to a valid address in the target process. The number of arguments must be less than or equal to six.
Note
If the function in the target process internally calls non-async-signal-safe functions, it may stop the target process or cause unexpected behaviour.
See also
injector_remote_func_addr(), injector_remote_vcall()

◆ injector_remote_func_addr()

int injector_remote_func_addr ( injector_t *  injector,
void *  handle,
const char *  name,
size_t *  func_addr_out 
)

Get the function address in the target process (Linux and Windows only)

Parameters
[in]injectorthe injector handle specifying the target process
[in]handlethe module handle created by injector_inject or special-handles such as RTLD_DEFAULT
[in]namethe function name
[out]func_addr_outthe address where the function address in the target process will be stored
Returns
zero on success. Otherwise, error code

Example

Inject libfoo.so and then call foo_func(1, 2, 3) in it.

void *handle;
// inject libfoo.so and get the handle
if (injector_inject(injector, "libfoo.so", &handle) != 0) {
return;
}
size_t func_addr;
// get the address of foo_func in the handle
if (injector_remote_func_addr(injector, handle, "foo_func", &func_addr) != 0) {
return;
}
intptr_t retval;
// call foo_func
if (injector_remote_call(injector, &retval, func_addr, 1, 2, 3) != 0) {
return;
}
printf("The return value of foo_func(1, 2, 3) is %ld.\n", retval);
int injector_remote_call(injector_t *injector, intptr_t *retval, size_t func_addr,...)
Call the function in the target process (Linux and Windows only)
int injector_remote_func_addr(injector_t *injector, void *handle, const char *name, size_t *func_addr_out)
Get the function address in the target process (Linux and Windows only)
int injector_inject(injector_t *injector, const char *path, void **handle)
Inject the specified shared library into the target process.

◆ injector_remote_vcall()

int injector_remote_vcall ( injector_t *  injector,
intptr_t *  retval,
size_t  func_addr,
va_list  ap 
)

Call the function in the target process (Linux and Windows only)

Parameters
[in]injectorthe injector handle specifying the target process
[out]retvalNULL or the address where the return value of the function call will be stored
[in]func_addrthe function address in the target process
[in]aparguments passed to the function
Returns
zero on success. Otherwise, error code
Remarks
The types of the arguments must be integer or pointer. If it is a pointer, it must point to a valid address in the target process. The number of arguments must be less than or equal to six.
Note
If the function in the target process internally calls non-async-signal-safe functions, it may stop the target process or cause unexpected behaviour.
See also
injector_remote_func_addr(), injector_remote_call()

◆ injector_uninject()

int injector_uninject ( injector_t *  injector,
void *  handle 
)

Uninject the shared library specified by handle.

Parameters
[in]injectorthe injector handle specifying the target process
[in]handlethe module handle created by injector_inject
Returns
zero on success. Otherwise, error code
Remarks
This fearute isn't supported for musl-libc processes. See Functional differences from glibc.