Skip to content

Commit

Permalink
Merge pull request #5 from ohchase/v0.3
Browse files Browse the repository at this point in the history
V0.3
  • Loading branch information
ohchase authored Aug 11, 2024
2 parents feeed55 + 0d5b7b0 commit f7aeec6
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 179 deletions.
5 changes: 5 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
- package-ecosystem: cargo
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10
34 changes: 17 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "plt-rs"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["ohchase"]
license = "MIT"
Expand All @@ -9,7 +9,7 @@ documentation = "https://docs.rs/plt-rs"
readme = "README.md"
repository = "https://github.com/ohchase/plt-rs/"
homepage = "https://github.com/ohchase/plt-rs/"
keywords = ["plt", "elf", "bionic", "linker", "hooking"]
keywords = ["plt", "elf", "linker", "hook", "symbols"]
exclude = ["/examples"]

[lib]
Expand All @@ -18,8 +18,8 @@ crate-type = ["lib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libc = "0.2.149"
thiserror = "1.0.49"
libc = "0.2"
thiserror = "1.0"

[dev-dependencies]
anyhow = "1.0.75"
2 changes: 1 addition & 1 deletion Cross.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ image = "ghcr.io/cross-rs/aarch64-linux-android:main"
[target.i686-linux-android]
image = "ghcr.io/cross-rs/i686-linux-android:main"
[target.x86_64-linux-android]
image = "ghcr.io/cross-rs/x86_64-linux-android:main"
image = "ghcr.io/cross-rs/x86_64-linux-android:main"
81 changes: 19 additions & 62 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# PLT-RS
# Plt-rs

## Change Notes
### 0.1.0 initial release
### 0.2.0 total revamp
- removed hooking functionality
- reduced linux/android bloat
- documented and generally made more ergonomic

## Inspirations / Sources utilized
Projects I referenced while working on this.
### 0.3.0 usability
- promote finding function in dynamic library functionality to public
- added tests
- don't use patch version in dependencies
- ci/cd dependency updates

## Inspired
Projects I referenced and was heavily inspired by while working on this.
- [Plthook by Kubo] https://github.com/kubo/plthook
- [Bhook by bytedance] https://github.com/bytedance/bhook

Expand All @@ -18,26 +23,19 @@ Generally, PLT hooking is an ideal solution for hooking given you can guarantee
This library does not do any inline hooking, so there is no architecture (i686, arm, etc.) specific assembly magic going on,
making cross compatibility very easy. There IS architecture specific constants, but its very minimal.

## ... Unix Like?
Ye this library supports two target operating systems: Linux and Android;
So, android is unix like but has a separate linker implemention called Bionic.
At the ground floor, both are still linking shared objects in elf format; but
the library has to change how it crawls the dynamially loaded objects.

## Why
Video game modding, reverse engineering, etc
- Can hook networking calls: recv / send
- Rendering calls: eglSwapBuffers / video game overlays
- Rendering calls: eglSwapBuffers / video game mods and overlays
- Application hardening and monitoring
- Defensive and Offensive usages

## Supports and tests against many targets
highlighted builds
- ![i686-unknown-linux-gnu](https://github.com/ohchase/plt-rs/actions/workflows/i686-unknown-linux-gnu.yml/badge.svg)
- ![x86_64-unknown-linux-gnu](https://github.com/ohchase/plt-rs/actions/workflows/x86_64-unknown-linux-gnu.yml/badge.svg)
- ![aarch64-unknown-linux-gnu](https://github.com/ohchase/plt-rs/actions/workflows/aarch64-unknown-linux-gnu.yml/badge.svg)
- ![arm-unknown-linux-gnueabi](https://github.com/ohchase/plt-rs/actions/workflows/arm-unknown-linux-gnueabi.yml/badge.svg)
- ![i686-linux-android](https://github.com/ohchase/plt-rs/actions/workflows/i686-linux-android.yml/badge.svg)
- ![x86_64-linux-android](https://github.com/ohchase/plt-rs/actions/workflows/x86_64-linux-android.yml/badge.svg)
- ![aarch64-linux-android](https://github.com/ohchase/plt-rs/actions/workflows/aarch64-linux-android.yml/badge.svg)
- ![arm-linux-androideabi](https://github.com/ohchase/plt-rs/actions/workflows/arm-linux-androideabi.yml/badge.svg)
- ![armv7-linux-androideabi](https://github.com/ohchase/plt-rs/actions/workflows/armv7-linux-androideabi.yml/badge.svg)

## Show me da code
Expand All @@ -52,49 +50,6 @@ unsafe fn getpid() -> u32 {
999
}

/// Finding target function differs on 32 bit and 64 bit.
/// On 64 bit we want to check the addended relocations table only, opposed to the addendless relocations table.
/// Additionally, we will fall back to the plt given it is an addended relocation table.
#[cfg(target_pointer_width = "64")]
fn try_find_function<'a>(
dyn_lib: &'a DynamicLibrary,
dyn_symbols: &'a DynamicSymbols,
) -> Option<&'a plt_rs::elf64::DynRela> {
let string_table = dyn_lib.string_table();
if let Some(dyn_relas) = dyn_lib.addend_relocs() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}

if let Some(dyn_relas) = dyn_lib.plt_rela() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}
return None;
}

fn main() -> Result<()> {
let my_pid = unsafe { libc::getpid() };
println!("application pid is {my_pid}");
Expand All @@ -105,11 +60,8 @@ fn main() -> Result<()> {
let dyn_lib = DynamicLibrary::initialize(executable_entry)?;
println!("successfully initialied dynamic library for instrumentation");

let dyn_symbols = dyn_lib
.symbols()
.ok_or(anyhow!("dynamic lib should have symbols"))?;
let target_function =
try_find_function(&dyn_lib, &dyn_symbols).ok_or(anyhow!("unable to find getpid symbol"))?;
dyn_lib.try_find_function("getpid").ok_or(anyhow!("unable to find getpid symbol"))?;
println!(
"successfully identified libc getpid offset: {:#X?}",
target_function.r_offset
Expand Down Expand Up @@ -156,3 +108,8 @@ successfully identified libc getpid offset: 0x7E460
page start for function is 0x000061019c41b000
new pid is: 999
```

## References / Inspirations
Projects I referenced and was heavily inspired by while working on this.
- [Plthook by Kubo] https://github.com/kubo/plthook
- [Bhook by bytedance] https://github.com/bytedance/bhook
1 change: 1 addition & 0 deletions examples/dump_plt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use plt_rs::{collect_modules, DynamicLibrary, RelocationTable};

fn main() -> Result<()> {
let entries = collect_modules();
println!("collected modules");

for entry in entries.into_iter() {
println!("[{:?}] Addr: {:#X?}", entry.name(), entry.addr());
Expand Down
101 changes: 8 additions & 93 deletions examples/hook_getpid.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::anyhow;
use anyhow::Result;
use libc::c_void;
use plt_rs::{collect_modules, DynamicLibrary, DynamicSymbols};
use plt_rs::{collect_modules, DynamicLibrary};

unsafe fn getpid() -> u32 {
999
Expand All @@ -17,99 +17,16 @@ fn find_executable<'a>() -> Option<plt_rs::LoadedLibrary<'a>> {
/// Finding executable target differs on unix and android
#[cfg(target_os = "android")]
fn find_executable<'a>() -> Option<plt_rs::LoadedLibrary<'a>> {
let executable = std::env::current_exe().expect("current exe");
let file_stem = executable.file_stem()?;
let file_stem = file_stem.to_str()?;
let loaded_modules = collect_modules();
loaded_modules
.into_iter()
.filter(|lib| lib.name().contains("hook_getpid"))
.filter(|lib| lib.name().contains(file_stem))
.next()
}

/// Finding target function differs on 32 bit and 64 bit.
/// On 32 bit we want to check the relocations table only, opposed to the addend relocations table.
/// Additionally, we will fall back to the plt given it is an addendless relocation table.
#[cfg(target_pointer_width = "32")]
fn try_find_function<'a>(
dyn_lib: &'a DynamicLibrary,
dyn_symbols: &'a DynamicSymbols,
) -> Option<&'a plt_rs::elf32::DynRel> {
let string_table = dyn_lib.string_table();
if let Some(dyn_relas) = dyn_lib.relocs() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}

if let Some(dyn_relas) = dyn_lib.plt_rel() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}
return None;
}

/// Finding target function differs on 32 bit and 64 bit.
/// On 64 bit we want to check the addended relocations table only, opposed to the addendless relocations table.
/// Additionally, we will fall back to the plt given it is an addended relocation table.
#[cfg(target_pointer_width = "64")]
fn try_find_function<'a>(
dyn_lib: &'a DynamicLibrary,
dyn_symbols: &'a DynamicSymbols,
) -> Option<&'a plt_rs::elf64::DynRela> {
let string_table = dyn_lib.string_table();
if let Some(dyn_relas) = dyn_lib.addend_relocs() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}

if let Some(dyn_relas) = dyn_lib.plt_rela() {
let dyn_relas = dyn_relas.entries().iter();
if let Some(symbol) = dyn_relas
.flat_map(|e| {
dyn_symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq("getpid"))
.next()
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}
return None;
}

fn main() -> Result<()> {
let my_pid = unsafe { libc::getpid() };
println!("application pid is {my_pid}");
Expand All @@ -120,11 +37,9 @@ fn main() -> Result<()> {
let dyn_lib = DynamicLibrary::initialize(executable_entry)?;
println!("successfully initialied dynamic library for instrumentation");

let dyn_symbols = dyn_lib
.symbols()
.ok_or(anyhow!("dynamic lib should have symbols"))?;
let target_function =
try_find_function(&dyn_lib, &dyn_symbols).ok_or(anyhow!("unable to find getpid symbol"))?;
let target_function = dyn_lib
.try_find_function("getpid")
.ok_or(anyhow!("unable to find getpid symbol"))?;
println!(
"successfully identified libc getpid offset: {:#X?}",
target_function.r_offset
Expand Down
Loading

0 comments on commit f7aeec6

Please sign in to comment.