Skip to content

Commit

Permalink
prepare version-0.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
ohchase committed Aug 11, 2024
1 parent cc52a6f commit 381ba6b
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 37 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"
22 changes: 17 additions & 5 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 @@ -27,7 +32,9 @@ 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
- ![i686-unknown-linux-gnu](https://github.com/ohchase/plt-rs/actions/workflows/i686-unknown-linux-gnu.yml/badge.svg)
Expand Down Expand Up @@ -156,3 +163,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
12 changes: 4 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ impl<'a> DynamicLibrary<'a> {
return Some(symbol);
}
}
return None;
None
}

/// Finding target function differs on 32 bit and 64 bit.
Expand All @@ -505,9 +505,7 @@ impl<'a> DynamicLibrary<'a> {
symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq(symbol_name))
.next()
}).find(|(_, s)| s.eq(symbol_name))
.map(|(target_function, _)| target_function)
{
return Some(symbol);
Expand All @@ -521,15 +519,13 @@ impl<'a> DynamicLibrary<'a> {
symbols
.resolve_name(e.symbol_index() as usize, string_table)
.map(|s| (e, s))
})
.filter(|(_, s)| s.eq(symbol_name))
.next()
}).find(|(_, s)| s.eq(symbol_name))
.map(|(target_function, _)| target_function)
{
return Some(symbol);
}
}
return None;
None
}
/// Access the plt as a dynamic relocation table if possible
/// can fail if the plt is not available or the plt is with addend
Expand Down
133 changes: 131 additions & 2 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,134 @@
use libc::c_void;
use plt_rs::{collect_modules, DynamicLibrary, RelocationTable};

/// Make sure we can load all the modules we load ourselves
/// A simple sanity check, we are not checking the modules contents in any meaningful way.
/// But this works great to catch issues, because realistically we should never run into a issue parsing libraries.
#[test]
fn can_load_own_link_map() {}
fn can_load_own_link_map() {
let entries = collect_modules();

for entry in entries.into_iter() {
if let Ok(dynamic_lib) = DynamicLibrary::initialize(entry) {
let dynamic_symbols = dynamic_lib.symbols().expect("symbols...");
let string_table = dynamic_lib.string_table();
if let Some(dyn_relas) = dynamic_lib.addend_relocs() {
dyn_relas
.entries()
.iter()
.flat_map(|e| {
dynamic_symbols.resolve_name(e.symbol_index() as usize, string_table)
})
.filter(|s| !s.is_empty())
.for_each(|s| println!("\t{}", s));
}

if let Some(dyn_relocs) = dynamic_lib.relocs() {
dyn_relocs
.entries()
.iter()
.flat_map(|e| {
dynamic_symbols.resolve_name(e.symbol_index() as usize, string_table)
})
.filter(|s| !s.is_empty())
.for_each(|s| println!("\t{}", s));
}

if let Some(plt) = dynamic_lib.plt() {
match plt {
RelocationTable::WithAddend(rel) => {
rel.entries()
.iter()
.flat_map(|e| {
dynamic_symbols
.resolve_name(e.symbol_index() as usize, string_table)
})
.filter(|s| !s.is_empty())
.for_each(|s| println!("\t{}", s));
}
RelocationTable::WithoutAddend(rel) => {
rel.entries()
.iter()
.flat_map(|e| {
dynamic_symbols
.resolve_name(e.symbol_index() as usize, string_table)
})
.filter(|s| !s.is_empty())
.for_each(|s| println!("\t{}", s));
}
}
}
}
}
}

unsafe fn getpid() -> u32 {
999
}

/// Finding executable target differs on unix and android
#[cfg(target_os = "linux")]
fn find_executable<'a>() -> Option<plt_rs::LoadedLibrary<'a>> {
let loaded_modules = collect_modules();
loaded_modules.into_iter().next()
}

/// 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(file_stem))
.next()
}
#[test]
fn can_hook_getpid() {}
fn can_hook_getpid() {
let my_pid = unsafe { libc::getpid() };
println!("application pid is {my_pid}");

let executable_entry = find_executable().expect("can find executable");
println!("successfully identified executable");

let dyn_lib = DynamicLibrary::initialize(executable_entry).expect("can load");
println!("successfully initialied dynamic library for instrumentation");

let target_function = dyn_lib
.try_find_function("getpid")
.expect("executable should link getpid");
println!(
"successfully identified libc getpid offset: {:#X?}",
target_function.r_offset
);

let base_addr = dyn_lib.library().addr();
let plt_fn_ptr = (base_addr + target_function.r_offset as usize) as *mut *mut libc::c_void;
let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) as usize };
let plt_page = ((plt_fn_ptr as usize / page_size) * page_size) as *mut libc::c_void;
println!("page start for function is {plt_page:#X?}");

let _stored_address = unsafe {
// Set the memory page to read, write
let prot_res = libc::mprotect(plt_page, page_size, libc::PROT_WRITE | libc::PROT_READ);
if prot_res != 0 {
panic!("failed to set prot res");
}

// Replace the function address
let previous_address = std::ptr::replace(plt_fn_ptr, getpid as *mut _);

// Set the memory page protection back to read only
let prot_res = libc::mprotect(plt_page, page_size, libc::PROT_READ);
if prot_res != 0 {
panic!("failed to set prot res");
}

previous_address as *const c_void
};

let get_pid = unsafe { libc::getpid() };
assert_eq!(get_pid, 999)
}

0 comments on commit 381ba6b

Please sign in to comment.