From a7823c2f3721de8ed5949d535da03bc450048282 Mon Sep 17 00:00:00 2001 From: erfur Date: Sat, 16 Mar 2024 08:51:31 +0100 Subject: [PATCH 1/6] wait for map address --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index aff131b..312e632 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -223,6 +223,7 @@ impl Injector { info!("wait for shellcode to trigger"); let mut new_map: u64; loop { + std::thread::sleep(std::time::Duration::from_millis(10)); let data = proc.mem.read(self.target_var_sym_addr, 0x8).unwrap(); // u64 from val new_map = u64::from_le_bytes(data[0..8].try_into().unwrap()); From 864695989a55722595b4442ce1f763cd935e1b1f Mon Sep 17 00:00:00 2001 From: Windy Date: Tue, 19 Mar 2024 20:33:48 +0800 Subject: [PATCH 2/6] Add new feature: support injecting specified app package name (#1) * Add new feature: support injecting specified app package name, optimize the size of executed elf file. * Update Cargo.toml Co-authored-by: erfur <93070510+erfur@users.noreply.github.com> * Update README.md Co-authored-by: erfur <93070510+erfur@users.noreply.github.com> * fix package name mistake --------- Co-authored-by: erfur <93070510+erfur@users.noreply.github.com> --- Cargo.toml | 10 +++++++ README.md | 3 +- bin/cli.rs | 23 ++++++++++++-- src/lib.rs | 10 +++++++ src/utils.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 475bff0..0c99b55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,13 @@ nix = { version = "0.27.1", features = ["uio"] } pretty-hex = "0.4.0" proc-maps = "0.3.2" simple_logger = "4.3.3" +glob = "0.3.1" + +[profile.release] +opt-level = "z" +debug = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = "abort" +strip = true \ No newline at end of file diff --git a/README.md b/README.md index 492c248..a8b7a04 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Usage: linjector-cli [OPTIONS] --pid --file Options: -p, --pid pid of the target process - + -a, --app-package-name + target application's package name, (re)start the application and do injection -f, --file path of the library/shellcode to inject diff --git a/bin/cli.rs b/bin/cli.rs index ed6236d..8cc1b04 100644 --- a/bin/cli.rs +++ b/bin/cli.rs @@ -9,7 +9,11 @@ use simple_logger::SimpleLogger; struct Args { /// pid of the target process #[arg(short, long)] - pid: i32, + pid: Option, + + /// target application's package name, restart the application and do injection + #[arg(short, long)] + app_package_name: Option, /// path of the library/shellcode to inject #[arg(short, long)] @@ -71,7 +75,22 @@ fn main() { } } - let mut injector = match linjector_rs::Injector::new(args.pid) { + let mut target_pid = args.pid.unwrap_or(0); + if target_pid <= 0 { + let Some(name) = args.app_package_name else { + error!("No pid or app_package_name is specified"); + return; + }; + let Ok(app_pid) = linjector_rs::Injector::restart_app_and_get_pid(&name) else { + error!("Can't restart package: {}, or cannot found pid", name); + return; + }; + target_pid = app_pid as i32; + } + + info!("target process pid: {}", target_pid); + + let mut injector = match linjector_rs::Injector::new(target_pid) { Ok(injector) => injector, Err(e) => { error!("Error creating injector: {:?}", e); diff --git a/src/lib.rs b/src/lib.rs index 312e632..d31289a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,7 @@ pub enum InjectionError { FileError, CommandError, ShellcodeError, + PidNotFound } pub struct Injector { @@ -167,6 +168,15 @@ impl Injector { Ok(self) } + pub fn restart_app_and_get_pid(package_name: &str) -> Result { + let pid = utils::restart_app_and_get_pid(package_name); + if pid > 0 { + Ok(pid) + } else { + Err(InjectionError::PidNotFound) + } + } + pub fn inject(&mut self) -> Result<(), InjectionError> { let file_path = self.prepare_file()?; let proc = remote_proc::RemoteProc::new(self.pid)?; diff --git a/src/utils.rs b/src/utils.rs index a798a60..ef75217 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,14 @@ use hxdmp::hexdump; use std::io::{ErrorKind, Read}; +use std::process::Output; +use std::str::from_utf8; use crate::InjectionError; +use glob::glob; +use std::time::Duration; +use std::thread; + const HEXDUMP_BUFFER_SIZE: usize = 0x200; const TMP_DIR_PATH: &str = "/data/local/tmp"; @@ -162,3 +168,82 @@ pub fn fix_file_permissions(file_path: &str) -> Result<(), InjectionError> { } } } + +pub fn execute_command(program: &str, args: &Vec<&str>) -> Result { + match std::process::Command::new(program) + .args(args) + .output() + { + Ok(output) => { + if !output.status.success() { + error!( + "Error running cmd {} {:?} err: {}", program, args, + String::from_utf8_lossy(&output.stderr) + ); + Err(InjectionError::CommandError) + } else { + info!("Running cmd successfully: {} {:?}", program, args); + Ok(output) + } + } + Err(e) => { + error!("Error running cmd {} {:?} err: {}", program, args, e); + Err(InjectionError::CommandError) + } + } +} + +pub fn get_pid_by_package(pkg_name: &str) -> std::io::Result { + for entry in glob("/proc/*/cmdline").unwrap() { + match entry { + Ok(path) => { + let mut file = std::fs::File::open(&path)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + let tmp_name = contents.trim_end_matches('\0'); + if tmp_name == pkg_name { + let path_str = path.to_str().unwrap(); + let pid_str = path_str.split("/").nth(2).unwrap(); + let pid = pid_str.parse::().unwrap(); + return Ok(pid); + } + }, + Err(err) => println!("{:?}", err), + } + } + Ok(0) +} + +pub fn get_pid_by_package_with_polling(pkg_name: &str) -> u32{ + let mut _pid: u32 = 0; + + let count = 100; + for _i in 0..count { + _pid = get_pid_by_package(pkg_name).unwrap(); + if _pid > 0 { + break; + } + thread::sleep(Duration::from_micros(500)); + } + + return _pid; +} + +pub fn restart_app_and_get_pid(pkg_name: &str) -> u32 { + let _ = execute_command("am", &vec!["force-stop", pkg_name]); + // check if this command can start the application + let _ = execute_command("monkey", &vec!["-p", pkg_name, "-c", "android.intent.category.LAUNCHER", "1"]); + + let mut _pid = get_pid_by_package_with_polling(pkg_name); + + if _pid <= 0 { + // try another way to start the application + let get_main_activity_result = execute_command("cmd", &vec!["package", "resolve-activity", "--brief", pkg_name, "|", "tail", "-n", "1"]).unwrap(); + let result_str = from_utf8(&get_main_activity_result.stdout).unwrap(); + let last_line = result_str.lines().last().unwrap(); + let _ = execute_command("am", &vec!["start", last_line]); + _pid = get_pid_by_package_with_polling(pkg_name); + } + return _pid; +} + From 2cef368856155e91f359bc80af15c89b2f8573d7 Mon Sep 17 00:00:00 2001 From: erfur Date: Wed, 20 Mar 2024 11:41:49 +0100 Subject: [PATCH 3/6] refactor --- bin/cli.rs | 22 +++++++++----------- src/lib.rs | 41 +++++++++++++++--------------------- src/remote_module.rs | 2 +- src/utils.rs | 49 +++++++++++++++++++++++++++++++------------- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/bin/cli.rs b/bin/cli.rs index 8cc1b04..3763586 100644 --- a/bin/cli.rs +++ b/bin/cli.rs @@ -11,7 +11,7 @@ struct Args { #[arg(short, long)] pid: Option, - /// target application's package name, restart the application and do injection + /// target application's package name, (re)start the application and do injection #[arg(short, long)] app_package_name: Option, @@ -61,18 +61,16 @@ fn main() { } else { android_logger::init_once(Config::default().with_max_level(LevelFilter::Info)); } + } else if args.debug { + SimpleLogger::new() + .with_level(LevelFilter::Debug) + .init() + .unwrap(); } else { - if args.debug { - SimpleLogger::new() - .with_level(LevelFilter::Debug) - .init() - .unwrap(); - } else { - SimpleLogger::new() - .with_level(LevelFilter::Info) - .init() - .unwrap(); - } + SimpleLogger::new() + .with_level(LevelFilter::Info) + .init() + .unwrap(); } let mut target_pid = args.pid.unwrap_or(0); diff --git a/src/lib.rs b/src/lib.rs index d31289a..cc05ae9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ pub enum InjectionError { FileError, CommandError, ShellcodeError, - PidNotFound + PidNotFound, } pub struct Injector { @@ -187,29 +187,22 @@ impl Injector { } info!("build second stage shellcode"); - let second_stage: Vec; - match self.injection_type { - InjectionType::RawDlopen => { - second_stage = shellcode::raw_dlopen_shellcode( - *self.sym_cache.get("dlopen").unwrap(), - file_path, - *self.sym_cache.get("malloc").unwrap(), - ) - .unwrap(); - } - InjectionType::MemFdDlopen => { - second_stage = shellcode::memfd_dlopen_shellcode( - *self.sym_cache.get("dlopen").unwrap(), - *self.sym_cache.get("malloc").unwrap(), - &std::fs::read(file_path.as_str()).unwrap(), - *self.sym_cache.get("sprintf").unwrap(), - ) - .unwrap(); - } - InjectionType::RawShellcode => { - second_stage = shellcode::raw_shellcode().unwrap(); - } - } + let second_stage = match self.injection_type { + InjectionType::RawDlopen => shellcode::raw_dlopen_shellcode( + *self.sym_cache.get("dlopen").unwrap(), + file_path, + *self.sym_cache.get("malloc").unwrap(), + ) + .unwrap(), + InjectionType::MemFdDlopen => shellcode::memfd_dlopen_shellcode( + *self.sym_cache.get("dlopen").unwrap(), + *self.sym_cache.get("malloc").unwrap(), + &std::fs::read(file_path.as_str()).unwrap(), + *self.sym_cache.get("sprintf").unwrap(), + ) + .unwrap(), + InjectionType::RawShellcode => shellcode::raw_shellcode().unwrap(), + }; info!("build first stage shellcode"); let first_stage = diff --git a/src/remote_module.rs b/src/remote_module.rs index 3ad6c31..698e976 100644 --- a/src/remote_module.rs +++ b/src/remote_module.rs @@ -25,7 +25,7 @@ impl RemoteModule { .iter() .find(|sym| symbol_name == elf.strtab.get_at(sym.st_name).unwrap()); - if !result.is_none() { + if result.is_some() { let offset = result.unwrap().st_value as usize; return Ok(offset + self.vm_addr); } diff --git a/src/utils.rs b/src/utils.rs index ef75217..e941fcd 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,8 +6,8 @@ use std::str::from_utf8; use crate::InjectionError; use glob::glob; -use std::time::Duration; use std::thread; +use std::time::Duration; const HEXDUMP_BUFFER_SIZE: usize = 0x200; const TMP_DIR_PATH: &str = "/data/local/tmp"; @@ -170,14 +170,13 @@ pub fn fix_file_permissions(file_path: &str) -> Result<(), InjectionError> { } pub fn execute_command(program: &str, args: &Vec<&str>) -> Result { - match std::process::Command::new(program) - .args(args) - .output() - { + match std::process::Command::new(program).args(args).output() { Ok(output) => { if !output.status.success() { error!( - "Error running cmd {} {:?} err: {}", program, args, + "Error running cmd {} {:?} err: {}", + program, + args, String::from_utf8_lossy(&output.stderr) ); Err(InjectionError::CommandError) @@ -207,14 +206,14 @@ pub fn get_pid_by_package(pkg_name: &str) -> std::io::Result { let pid = pid_str.parse::().unwrap(); return Ok(pid); } - }, + } Err(err) => println!("{:?}", err), } } Ok(0) } -pub fn get_pid_by_package_with_polling(pkg_name: &str) -> u32{ +pub fn get_pid_by_package_with_polling(pkg_name: &str) -> u32 { let mut _pid: u32 = 0; let count = 100; @@ -226,24 +225,46 @@ pub fn get_pid_by_package_with_polling(pkg_name: &str) -> u32{ thread::sleep(Duration::from_micros(500)); } - return _pid; + _pid } pub fn restart_app_and_get_pid(pkg_name: &str) -> u32 { let _ = execute_command("am", &vec!["force-stop", pkg_name]); // check if this command can start the application - let _ = execute_command("monkey", &vec!["-p", pkg_name, "-c", "android.intent.category.LAUNCHER", "1"]); + let _ = execute_command( + "monkey", + &vec![ + "-p", + pkg_name, + "-c", + "android.intent.category.LAUNCHER", + "1", + ], + ); let mut _pid = get_pid_by_package_with_polling(pkg_name); - if _pid <= 0 { + if _pid == 0 { // try another way to start the application - let get_main_activity_result = execute_command("cmd", &vec!["package", "resolve-activity", "--brief", pkg_name, "|", "tail", "-n", "1"]).unwrap(); + let get_main_activity_result = execute_command( + "cmd", + &vec![ + "package", + "resolve-activity", + "--brief", + pkg_name, + "|", + "tail", + "-n", + "1", + ], + ) + .unwrap(); let result_str = from_utf8(&get_main_activity_result.stdout).unwrap(); let last_line = result_str.lines().last().unwrap(); let _ = execute_command("am", &vec!["start", last_line]); _pid = get_pid_by_package_with_polling(pkg_name); } - return _pid; -} + _pid +} From 31243bcdbd5ba137e8dc9cbb8dd7744a398f1817 Mon Sep 17 00:00:00 2001 From: erfur Date: Wed, 20 Mar 2024 11:42:03 +0100 Subject: [PATCH 4/6] refactor pt2 --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0c99b55..93ca9b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,15 @@ path = "src/lib.rs" name = "linjector-cli" path = "bin/cli.rs" +[profile.release] +opt-level = "z" +debug = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = "abort" +strip = true + [dependencies] android_logger = "0.13.3" backtrace = "0.3.69" @@ -25,13 +34,4 @@ nix = { version = "0.27.1", features = ["uio"] } pretty-hex = "0.4.0" proc-maps = "0.3.2" simple_logger = "4.3.3" -glob = "0.3.1" - -[profile.release] -opt-level = "z" -debug = false -lto = true -debug-assertions = false -codegen-units = 1 -panic = "abort" -strip = true \ No newline at end of file +glob = "0.3.1" \ No newline at end of file From b99fb5766005caae99eeb55885931940e24b53e1 Mon Sep 17 00:00:00 2001 From: erfur Date: Wed, 20 Mar 2024 11:42:13 +0100 Subject: [PATCH 5/6] update usage --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a8b7a04..eff9ce1 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,21 @@ To get an idea of how it works, you can read the [blog post](https://erfur.githu ``` Inject code into a running process using /proc/mem -Usage: linjector-cli [OPTIONS] --pid --file +Usage: linjector-cli [OPTIONS] --file Options: -p, --pid pid of the target process + -a, --app-package-name target application's package name, (re)start the application and do injection + -f, --file path of the library/shellcode to inject -i, --injection-type type of injection - + [default: raw-dlopen] Possible values: From 9d446e1e22a828fb64396607e6311686674edaba Mon Sep 17 00:00:00 2001 From: erfur Date: Wed, 20 Mar 2024 11:42:34 +0100 Subject: [PATCH 6/6] update CI, remove compressed artifact --- .github/workflows/build.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e14c44b..e9602c7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,28 +1,21 @@ -# This is a basic workflow to help you get started with Actions - name: CI -# Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the "main" branch push: branches: ["main"] tags: - - "*" + - "v*" pull_request: branches: ["main"] - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: permissions: contents: write -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: build: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4 @@ -35,27 +28,21 @@ jobs: - name: build run: cargo ndk --target aarch64-linux-android build --release - # - name: Setup tmate session - # uses: mxschmitt/action-tmate@v3 - - name: move files run: | mv target/aarch64-linux-android/release/linjector-cli ./linjector-cli - xz -9 -k ./linjector-cli - name: save hashes in env run: | echo '```' > hashes.txt echo "SHA256 hashes:" >> hashes.txt sha256sum linjector-cli >> hashes.txt - sha256sum linjector-cli.xz >> hashes.txt echo '```' >> hashes.txt - name: release uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + if: startsWith(github.ref, 'refs/tags/v') with: body_path: hashes.txt files: | ./linjector-cli - ./linjector-cli.xz