A Rust crate to load a shared library into a target process without using ptrace
. This is a portable rewrite of dlinject.
It should work for x86
, x86-64
, arm
and aarch64
, for both Linux and Android.
# Build binary
cargo build --example intruducer
# Build victim
cargo build --example victim
# Build library
rustc ./examples/evil.rs --crate-type cdylib --out-dir ./target/debug/examples
# Execute the victim
cd ./target/debug/examples
./victim
# Within a new shell
cd ./target/debug/examples
./intruducer -l ./libevil.so `pidof victim`
- Retrieve the instruction pointer (
ip
) of the target process reading/proc/<pid>/syscall
; - Open
/proc/<pid>/mem
and backs up the content atip
; - Generate the two shellcodes, and saves the last one to a file.
- Write the first shellcode to the target process memory at
ip
- the execution flow is now altered. - The first shellcode loads and executes the second shellcode.
- The second shellcode restores the original code, calls
dlopen
and branches toip
- the original execution flow is resumed.
- It makes large applications crash when a lot of computing is going on - this happens when a thread is executing the first shellcode and another one is executing the second shellcode, which restores the original code. A possible solution consists in freezing every thread but one using
/sys/fs/cgroup/freezer
, let this one perform the whole task and then thawing all the others. However, this only seemed to reduce the chance of crashes. - A register (
x28
) will be clobbered onaarch64
- I found no way to branch to an absolute virtual address without using a register. - When targeting an Android application, both library and second shellcode binary blob will be copied to its native library directory - changing the security context to
u:object_r:apk_data_file:s0
is not enough for the library file.