Inject shared libraries inside running processes on AMD64 Linux.
- The injector uses ptrace to get control over the program to inject.
- It pauses its execution, injects a shellcode (a sequence of opcodes) into an executable memory zone of the target then modifies the target's instruction pointer to point to the beginning of the shellcode.
- The injector orders the target to resume its execution. The target executes the shellcode, which contains a call to
dlopen(injection) /dlclose(ejection) to load/unload the desired shared library. - The dynamic loader loads/unloads the shared library and looks for a constructor (injection) / destructor (ejection). If it is present, it executes it.
- The injector detects when the shellcode's execution is finished and restores the target's initial state so that its execution can continue normally.
-
Download and install rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
-
Clone this repository:
git clone https://github.com/GuillaumeMZ/linux-x64-code-injection.git
-
Navigate inside the cloned folder
cd linux-x64-code-injection -
Build the project:
cargo build --release
The generated executable (
injector) is located in thetarget/releasefolder.
Run:
./injector --helpto see the help.
Run as admin the following command:
sudo ./injector cli --action inject --pid pid --dl dl_pathwhere:
pidis the PID of the process to injectdl_pathis the absolute path of the shared library to inject
You can also customize more advanced options:
--libdl <dl_name>wheredl_nameis the name of the library providing thedlopenfunction that will be used by the injector.--dlopen-name <dlopen_name>wheredlopen_nameis the name of the dlopen-like function that will be used by the injector to open the desired shared library.
Run as admin the following command:
sudo ./injector cli --action eject --pid pid --dl dl_pathwhere:
pidis the PID of the process to injectdl_pathis the absolute path of the shared library to eject
You can also customize more advanced options:
--libdl <dl_name>wheredl_nameis the name of the library providing thedlclosefunction that will be used by the ejector.--dlclose-name <dlclose_name>wheredlclose_nameis the name of the dlclose-like function that will be used by the ejector to close the desired shared library.
Create one function that will be called on injection (optional) and another one that will be called on ejection (optional). Each function's prototype must be void(void). Annotate the injection function with GCC's __attribute__((constructor)) construct, and the ejection function with __attribute__((destructor)).
main.c:
#include <stdio.h>
__attribute__((constructor))
void on_inject(void) {
printf("Hello from injected code !\n");
}
__attribute__((destructor))
void on_eject(void) {
printf("Goodbye from injected code !\n");
}Compile with:
gcc main.c -shared -fPIC -o libname.soThere is a crate named ctor that provides an equivalent to GCC's __attribute__((constructor)) and __attribute__((destructor))
Cargo.toml:
[package]
name = "name"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
ctor = "0.4.2"src/lib.rs:
use ctor::{ctor, dtor};
#[ctor]
pub fn on_inject() {
println!("Hello from injected code !")
}
#[dtor]
pub fn on_eject() {
println!("Goodbye from injected code !")
}Build with:
cargo build --releaseThe result (libname.so) is located in the target/release folder.
- Library ejection is not implemented yet.
- The TUI (terminal user interface) is not implemented yet.