121 lines
3.7 KiB
Rust
121 lines
3.7 KiB
Rust
|
|
extern crate libc;
|
|
|
|
use jni::sys::{jboolean, JNI_FALSE, JNI_TRUE};
|
|
use goldberg::goldberg_string;
|
|
use std::{thread, time};
|
|
use log::warn;
|
|
use std::fs;
|
|
use std::process;
|
|
use crate::CHECKSUMS;
|
|
use std::ffi::CString;
|
|
use std::str;
|
|
|
|
fn check_for_debugger() {
|
|
loop {
|
|
let status = fs::read_to_string("/proc/self/status").unwrap_or_default();
|
|
if !status.contains("TracerPid:\t0") {
|
|
if should_abort_on_tamper() {
|
|
process::abort();
|
|
} else {
|
|
warn!("TracerPid detected; skipping abort under hooked environment");
|
|
return;
|
|
}
|
|
}
|
|
|
|
let maps = fs::read_to_string("/proc/self/maps").unwrap_or_default();
|
|
if maps.contains("frida") || maps.contains("gumjs") || maps.contains("gdb") {
|
|
if should_abort_on_tamper() {
|
|
process::abort();
|
|
} else {
|
|
warn!("Debugger markers detected; skipping abort under hooked environment");
|
|
return;
|
|
}
|
|
}
|
|
|
|
thread::sleep(time::Duration::from_secs(5));
|
|
}
|
|
}
|
|
|
|
pub fn start_anti_debug_thread() {
|
|
thread::spawn(move || {
|
|
check_for_debugger();
|
|
});
|
|
thread::spawn(move || {
|
|
verify_library_checksum();
|
|
});
|
|
}
|
|
|
|
fn get_abi() -> Option<String> {
|
|
let key = CString::new("ro.product.cpu.abi").unwrap();
|
|
let mut value = [0 as libc::c_char; 92];
|
|
let len = unsafe { libc::__system_property_get(key.as_ptr(), value.as_mut_ptr()) };
|
|
if len > 0 {
|
|
let value_slice = unsafe { std::slice::from_raw_parts(value.as_ptr() as *const u8, len as usize) };
|
|
let value_str = str::from_utf8(value_slice).unwrap();
|
|
Some(value_str.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn verify_library_checksum() {
|
|
loop {
|
|
if let Some(path) = get_library_path() {
|
|
if let Ok(data) = fs::read(path) {
|
|
let checksum = crc32fast::hash(&data);
|
|
if let Some(abi) = get_abi() {
|
|
if let Some(expected_checksum) = CHECKSUMS.lock().unwrap().get(&abi) {
|
|
if checksum != *expected_checksum as u32 {
|
|
if should_abort_on_tamper() {
|
|
process::abort();
|
|
} else {
|
|
warn!("Checksum mismatch; skipping abort under hooked environment");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
thread::sleep(time::Duration::from_secs(60));
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn verify_key(key: &str) -> bool {
|
|
// Transformed key verification
|
|
let transformed_key = key.chars().rev().collect::<String>();
|
|
let expected_key = goldberg_string!("qd84R1hT6p7Mk").chars().rev().collect::<String>();
|
|
transformed_key == expected_key
|
|
}
|
|
|
|
// this function will be called from the JNI, it will verify the key and if it's correct, it will return true
|
|
// otherwise it will return false
|
|
#[allow(dead_code)]
|
|
pub fn jni_verify_key(key: &str) -> jboolean {
|
|
if verify_key(key) {
|
|
JNI_TRUE
|
|
} else {
|
|
JNI_FALSE
|
|
}
|
|
}
|
|
fn get_library_path() -> Option<String> {
|
|
let maps = std::fs::read_to_string("/proc/self/maps").ok()?;
|
|
for line in maps.lines() {
|
|
if line.ends_with("libpurrfectsnap.so") {
|
|
let parts: Vec<&str> = line.split_whitespace().collect();
|
|
if parts.len() > 5 {
|
|
return Some(parts[5].to_string());
|
|
}
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn should_abort_on_tamper() -> bool {
|
|
let maps = fs::read_to_string("/proc/self/maps").unwrap_or_default();
|
|
let markers = ["lspatch", "lsposed", "xposed", "zygisk", "riru"];
|
|
!markers.iter().any(|marker| maps.contains(marker))
|
|
}
|