├── .gitignore ├── Cargo.toml ├── src ├── android_library.rs ├── android_loader.rs ├── hook_manager.rs ├── lib.rs └── relocation_types.rs └── sysv64 ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # This is where we put the Android libraries for testing, binaries should not be checked in 17 | lib/ 18 | 19 | # Valgrind output 20 | /vgcore.* 21 | 22 | # IntelliJ 23 | .idea/ 24 | 25 | # VSCode 26 | .vscode/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "android-loader" 3 | version = "0.2.0" 4 | edition = "2021" 5 | 6 | rust-version = "1.60" 7 | 8 | [dependencies] 9 | anyhow = "1.0" 10 | lazy_static = "1.4" 11 | libc = "0.2" 12 | memmap2 = "0.5" 13 | rand = "0.8" 14 | region = "3.0" 15 | sysv64 = { path = "./sysv64" } 16 | xmas-elf = "0.9" 17 | zero = "0.1" 18 | log = "*" 19 | -------------------------------------------------------------------------------- /src/android_library.rs: -------------------------------------------------------------------------------- 1 | use crate::sysv64; 2 | use anyhow::Result; 3 | use log::{debug, info, warn}; 4 | use memmap2::{MmapOptions, MmapMut}; 5 | use region::{page, Protection}; 6 | use std::cmp::max; 7 | use std::collections::HashMap; 8 | use std::error::Error; 9 | use std::ffi::CStr; 10 | use std::fmt::{Display, Formatter}; 11 | use std::{fs, slice}; 12 | use std::os::raw::{c_char, c_void}; 13 | use std::ptr::null_mut; 14 | use xmas_elf::ElfFile; 15 | use xmas_elf::program::{ProgramHeader, Type}; 16 | use xmas_elf::sections::{SectionData, ShType}; 17 | use xmas_elf::symbol_table::Entry; 18 | use zero::read_str; 19 | 20 | use crate::hook_manager::get_hooks; 21 | use crate::relocation_types::{RelocationType, RelocType}; 22 | 23 | #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] 24 | type DynEntry = xmas_elf::symbol_table::DynEntry64; 25 | #[cfg(any(target_arch = "x86", target_arch = "arm"))] 26 | type DynEntry = xmas_elf::symbol_table::DynEntry32; 27 | 28 | // GnuHashTable adapted from goblin code 29 | 30 | #[repr(C)] 31 | pub(crate) struct GnuHashTable<'a> { 32 | /// Index of the first symbol in the `.dynsym` table which is accessible with 33 | /// the hash table 34 | symindex: u32, 35 | /// Shift count used in the bloom filter 36 | shift2: u32, 37 | /// 2 bit bloom filter on `chains` 38 | // Either 32 or 64-bit depending on the class of object 39 | bloom_filter: &'a [usize], 40 | /// GNU hash table bucket array; indexes start at 0. This array holds symbol 41 | /// table indexes and contains the index of hashes in `chains` 42 | buckets: &'a [u32], 43 | /// Hash values; indexes start at 0. This array holds symbol table indexes. 44 | chains: &'a [u32], // => chains[dynsyms.len() - symindex] 45 | dynsyms: &'a [DynEntry], 46 | } 47 | 48 | impl<'a> GnuHashTable<'a> { 49 | unsafe fn new(hashtab: &'a [u8], dynsyms: &'a [DynEntry]) -> GnuHashTable<'a> { 50 | let [nbuckets, symindex, maskwords, shift2] = 51 | (hashtab.as_ptr() as *const u32 as *const [u32; 4]).read(); 52 | 53 | let hashtab = &hashtab[16..]; 54 | 55 | let bloom_filter_ptr = hashtab.as_ptr() as *const usize; 56 | let buckets_ptr = bloom_filter_ptr.add(maskwords as usize) as *const u32; 57 | let chains_ptr = buckets_ptr.add(nbuckets as usize); 58 | let bloom_filter = slice::from_raw_parts(bloom_filter_ptr, maskwords as usize); 59 | let buckets = slice::from_raw_parts(buckets_ptr, nbuckets as usize); 60 | let chains = slice::from_raw_parts(chains_ptr, dynsyms.len() - symindex as usize); 61 | Self { 62 | symindex, 63 | shift2, 64 | bloom_filter, 65 | buckets, 66 | chains, 67 | dynsyms, 68 | } 69 | } 70 | 71 | fn hash(symbol_name: &str) -> u32 { 72 | let mut h: u32 = 5381; 73 | 74 | for c in symbol_name.chars() { 75 | h = (h << 5).wrapping_add(h.wrapping_add(c as u32)); 76 | } 77 | 78 | h 79 | } 80 | 81 | pub unsafe fn lookup(&self, android_library: &AndroidLibrary, symbol: &str, dynstrtab: &[u8]) -> Option<*const ()> { 82 | let hash = Self::hash(symbol); 83 | 84 | const MASK_LOWEST_BIT: u32 = 0xffff_fffe; 85 | let bucket = self.buckets[hash as usize % self.buckets.len()]; 86 | 87 | // Empty hash chain, symbol not present 88 | if bucket < self.symindex { 89 | return None; 90 | } 91 | // Walk the chain until the symbol is found or the chain is exhausted. 92 | let chain_idx = bucket - self.symindex; 93 | let hash = hash & MASK_LOWEST_BIT; 94 | let chains = &self.chains.get((chain_idx as usize)..)?; 95 | let dynsyms = &self.dynsyms.get((bucket as usize)..)?; 96 | for (hash2, symb) in chains.iter().zip(dynsyms.iter()) { 97 | if (hash == (hash2 & MASK_LOWEST_BIT)) 98 | && (symbol == read_str(&dynstrtab[(symb.name() as usize)..])) 99 | { 100 | return Some(android_library.memory_map.as_ptr().offset((symb.value() as usize + android_library.alignment_offset) as isize) as *const ()); 101 | } 102 | // Chain ends with an element with the lowest bit set to 1. 103 | if hash2 & 1 == 1 { 104 | break; 105 | } 106 | } 107 | None 108 | } 109 | } 110 | 111 | pub struct AndroidLibrary<'a> { 112 | pub(crate) file: Box>, 113 | pub(crate) memory_map: MmapMut, 114 | pub(crate) dyn_symbols: &'a [DynEntry], 115 | pub(crate) dyn_strs: &'a [u8], 116 | pub(crate) alignment_offset: usize, 117 | pub(crate) gnu_hash_table: Option> 118 | } 119 | 120 | impl AndroidLibrary<'_> { 121 | pub fn get_symbol(&self, symbol_name: &str) -> Option<*const ()> { 122 | let elf_file = ElfFile::new(&self.file).unwrap(); 123 | match &self.gnu_hash_table { 124 | Some(hash_table) => { 125 | unsafe { 126 | hash_table.lookup(&self, symbol_name, self.dyn_strs) 127 | } 128 | } 129 | None => unsafe { self.dyn_symbols.iter().find(|sym| sym.get_name(&elf_file) == Ok(symbol_name)).map(|s| self.memory_map.as_ptr().offset((s.value() as usize + self.alignment_offset) as isize) as *const ()) } 130 | } 131 | } 132 | #[sysv64] 133 | fn pthread_stub() -> i32 { 134 | 0 135 | } 136 | 137 | #[sysv64] 138 | fn undefined_symbol_stub() { 139 | panic!("tried to call an undefined symbol"); 140 | } 141 | 142 | #[sysv64] 143 | unsafe fn dlopen(name: *const c_char) -> *mut c_void { 144 | use crate::hook_manager::get_hooks; 145 | let mut path_str = CStr::from_ptr(name).to_str().unwrap(); 146 | 147 | let _path: String; 148 | #[cfg(target_family = "windows")] 149 | { 150 | _path = path_str.chars() 151 | .map(|x| match x { 152 | '\\' => '/', 153 | c => c 154 | }).collect::(); 155 | 156 | path_str = _path.as_str(); 157 | } 158 | 159 | info!("Loading {}", path_str); 160 | match Self::load(path_str) { 161 | Ok(lib) => Box::into_raw(Box::new(lib)) as *mut c_void, 162 | Err(_) => null_mut(), 163 | } 164 | } 165 | 166 | #[sysv64] 167 | unsafe fn dlsym(library: *mut AndroidLibrary, symbol: *const c_char) -> *mut c_void { 168 | let symbol = CStr::from_ptr(symbol).to_str().unwrap(); 169 | debug!("Symbol requested: {}", symbol); 170 | match library.as_ref().and_then(|lib| lib.get_symbol(symbol)) { 171 | Some(func) => func as *mut c_void, 172 | None => null_mut(), 173 | } 174 | } 175 | 176 | #[sysv64] 177 | unsafe fn dlclose(library: *mut AndroidLibrary) { 178 | let _ = Box::from_raw(library); 179 | } 180 | 181 | fn symbol_finder(symbol_name: &str, hooks: &HashMap) -> *const () { 182 | // Check if this function is hooked for this library 183 | 184 | if let Some(func) = hooks.get(symbol_name) { 185 | *func as *const () 186 | // pthread functions are problematic, let's ignore them 187 | } else { 188 | Self::get_libc_symbol(symbol_name) 189 | } 190 | } 191 | 192 | fn get_libc_symbol(symbol_name: &str) -> *const () { 193 | if symbol_name.starts_with("pthread_") { 194 | Self::pthread_stub as *const () 195 | } else { 196 | match symbol_name { 197 | "dlopen" => Self::dlopen as *const (), 198 | "dlsym" => Self::dlsym as *const (), 199 | "dlclose" => Self::dlclose as *const (), 200 | _ => Self::undefined_symbol_stub as *const () 201 | } 202 | } 203 | } 204 | 205 | fn absolute_reloc(memory_map: &mut MmapMut, dynsym: &[T], dynstrings: &[u8], hooks: &HashMap, index: usize, offset: usize, addend: usize) { 206 | let name = 207 | read_str(&dynstrings[(dynsym[index].name() as usize)..]); 208 | let symbol = Self::symbol_finder(name, hooks); 209 | 210 | // addend is always 0, but we still add it to be safe 211 | // converted to an array in the systme endianess 212 | let relocated = addend.wrapping_add(symbol as usize).to_ne_bytes(); 213 | memory_map[offset..offset + relocated.len()].copy_from_slice(&relocated); 214 | } 215 | 216 | fn relative_reloc(memory_map: &mut MmapMut, offset: usize, addend: usize) { 217 | let relocated = addend 218 | .wrapping_add(memory_map.as_mut_ptr() as usize) 219 | .to_ne_bytes(); 220 | 221 | memory_map[offset..offset + relocated.len()].copy_from_slice(&relocated); 222 | } 223 | 224 | #[cfg(not(target_arch="aarch64"))] 225 | const MAX_PAGE_SIZE: usize = 4096; 226 | 227 | #[cfg(target_arch="aarch64")] 228 | const MAX_PAGE_SIZE: usize = 65536; 229 | 230 | pub fn load<'a>(path: &str) -> Result> { 231 | let file = Box::new(fs::read(path)?); 232 | let file_leak_ptr = Box::into_raw(file); 233 | let file_leak = unsafe { file_leak_ptr.as_ref().unwrap() }; 234 | let elf_file = ElfFile::new(&file_leak).map_err(|err| AndroidLoaderErr::ElfParsingError(err.to_string()))?; 235 | 236 | let mut minimum = usize::MAX; 237 | let mut maximum = usize::MIN; 238 | 239 | let mut is_incompatible = false; 240 | let mut offset = 0; 241 | let mut overall_protection = Protection::NONE; 242 | 243 | for header in elf_file.program_iter() { 244 | if header.get_type() == Ok(Type::Load) { 245 | let flags = header.flags(); 246 | 247 | let start = region::page::floor(header.virtual_addr() as *const ()) as usize; 248 | let end = region::page::ceil( 249 | (header.virtual_addr() as usize + header.mem_size() as usize) 250 | as *const (), 251 | ) as usize; 252 | 253 | if offset == 0 { 254 | let mut prot = Protection::NONE.bits(); 255 | if flags.is_read() { 256 | prot |= Protection::READ.bits(); 257 | } 258 | if flags.is_write() { 259 | prot |= Protection::WRITE.bits(); 260 | } 261 | if flags.is_execute() { 262 | prot |= Protection::EXECUTE.bits(); 263 | } 264 | overall_protection |= Protection::from_bits_truncate(prot); 265 | if overall_protection == Protection::READ_WRITE_EXECUTE { 266 | offset = page::ceil(start as *const ()) as usize - start; 267 | } 268 | } 269 | 270 | if !is_incompatible && page::ceil(maximum as *const ()) as usize > start { 271 | is_incompatible = true; 272 | warn!("library has not been made for this CPU. It may crash!"); 273 | } 274 | 275 | if start < minimum { 276 | minimum = start; 277 | } 278 | 279 | if end > maximum { 280 | maximum = end; 281 | } 282 | } 283 | } 284 | 285 | let alloc_start = region::page::floor(minimum as *const ()) as usize; 286 | let alloc_end = region::page::ceil(maximum as *const ()) as usize; 287 | 288 | if !is_incompatible { 289 | offset = 0; 290 | } 291 | 292 | let mut memory_map = MmapOptions::new().len(alloc_end - alloc_start).map_anon()?; 293 | let addr = memory_map.as_ptr() as usize + offset; 294 | 295 | for program_header in elf_file.program_iter() { 296 | if program_header.get_type() == Ok(Type::Load) { 297 | let data = match program_header { 298 | ProgramHeader::Ph32(inner) => inner.raw_data(&elf_file), 299 | ProgramHeader::Ph64(inner) => inner.raw_data(&elf_file), 300 | }; 301 | 302 | let virtual_addr = program_header.virtual_addr() as usize; 303 | let mem_size = program_header.mem_size() as usize; 304 | let file_size = program_header.file_size() as usize; 305 | 306 | let start_addr = region::page::floor((addr + virtual_addr) as *const c_void) as *mut c_void; 307 | let end_addr = region::page::ceil((addr + virtual_addr + mem_size) as *const c_void); 308 | let mut header_debug = format!( 309 | "{:x} - {:x} (mem_sz: {}, file_sz: {}) [", 310 | start_addr as usize, end_addr as usize, mem_size, file_size 311 | ); 312 | 313 | let is_standard_page = region::page::size() <= Self::MAX_PAGE_SIZE; 314 | 315 | let flags = program_header.flags(); 316 | let mut prot = Protection::NONE.bits(); 317 | if flags.is_read() || !is_standard_page { 318 | header_debug += "R"; 319 | prot |= Protection::READ.bits(); 320 | } else { 321 | header_debug += "-"; 322 | } 323 | if flags.is_write() || !is_standard_page { 324 | header_debug += "W"; 325 | prot |= Protection::WRITE.bits(); 326 | } else { 327 | header_debug += "-"; 328 | } 329 | if flags.is_execute() || !is_standard_page { 330 | header_debug += "X]"; 331 | prot |= Protection::EXECUTE.bits(); 332 | } else { 333 | header_debug += "-]"; 334 | } 335 | debug!("{header_debug}"); 336 | 337 | unsafe { 338 | region::protect( 339 | start_addr, 340 | end_addr as usize - start_addr as usize, 341 | Protection::READ_WRITE, 342 | )?; 343 | } 344 | 345 | memory_map[virtual_addr..virtual_addr + file_size].copy_from_slice(data); 346 | 347 | unsafe { 348 | region::protect( 349 | start_addr, 350 | end_addr as usize - start_addr as usize, 351 | Protection::from_bits_truncate(prot) | Protection::READ, 352 | )?; 353 | } 354 | } 355 | } 356 | 357 | let hooks = get_hooks(); 358 | let mut dyn_symbols: &[DynEntry] = &[]; 359 | let mut dyn_strings: &[u8] = &[]; 360 | let mut gnu_hash_table = None; 361 | 362 | for section in elf_file.section_iter() { 363 | match section.get_type() { 364 | Ok(ShType::OsSpecific(0x6FFFFFF6)) => unsafe { 365 | gnu_hash_table = Some(GnuHashTable::new(section.raw_data(&elf_file), dyn_symbols)); 366 | } 367 | Ok(ShType::StrTab) => { 368 | if section.get_name(&elf_file) == Ok(".dynstr") { 369 | dyn_strings = section.raw_data(&elf_file); 370 | } 371 | } 372 | Ok(ShType::DynSym) => { 373 | dyn_symbols = match section.get_data(&elf_file).map_err(|err| AndroidLoaderErr::ElfParsingError(err.to_string()))? { // FIXME expensive 374 | #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] 375 | SectionData::DynSymbolTable64(entries) => entries, 376 | #[cfg(any(target_arch = "x86", target_arch = "arm"))] 377 | SectionData::DynSymbolTable32(entries) => entries, 378 | _ => return Err(AndroidLoaderErr::ElfParsingError("Unsupported Dynamic symbol table data".to_string()).into()) 379 | }; 380 | } 381 | Ok(ShType::Rel) | Ok(ShType::Rela) => { 382 | match section.get_data(&elf_file) { 383 | #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] 384 | Ok(SectionData::Rela64(relocations)) => { 385 | for relocation in relocations { 386 | match RelocationType::from(relocation.get_type()) { 387 | RelocationType::Absolute | RelocationType::GlobalData | RelocationType::JumpSlot => { 388 | Self::absolute_reloc(&mut memory_map, dyn_symbols, dyn_strings, &hooks, relocation.get_symbol_table_index() as usize, relocation.get_offset() as usize + offset, relocation.get_addend() as usize); 389 | } 390 | RelocationType::Relative => { 391 | Self::relative_reloc(&mut memory_map, relocation.get_offset() as usize + offset, relocation.get_addend() as usize); 392 | } 393 | RelocationType::Unknown(reloc_number) => { 394 | return Err(AndroidLoaderErr::UnsupportedRelocation(reloc_number).into()); 395 | } 396 | } 397 | } 398 | } 399 | #[cfg(any(target_arch = "x86", target_arch = "arm"))] 400 | Ok(SectionData::Rel32(relocations)) => { 401 | for relocation in relocations { 402 | let offset = relocation.get_offset() as usize + offset; 403 | let addend = usize::from_ne_bytes( 404 | memory_map[offset 405 | ..offset + std::mem::size_of::()] 406 | .try_into() 407 | .unwrap(), 408 | ); 409 | match RelocationType::from(relocation.get_type()) { 410 | RelocationType::Absolute => { 411 | Self::absolute_reloc(&mut memory_map, dyn_symbols, dyn_strings, &hooks, relocation.get_symbol_table_index() as usize, offset, 0); 412 | } 413 | RelocationType::GlobalData | RelocationType::JumpSlot => { 414 | Self::absolute_reloc(&mut memory_map, dyn_symbols, dyn_strings, &hooks, relocation.get_symbol_table_index() as usize, offset, addend); 415 | } 416 | RelocationType::Relative => { 417 | Self::relative_reloc(&mut memory_map, offset, addend); 418 | } 419 | RelocationType::Unknown(reloc_number) => { 420 | return Err(AndroidLoaderErr::UnsupportedRelocation(reloc_number).into()); 421 | } 422 | } 423 | } 424 | } 425 | _ => {} 426 | } 427 | } 428 | _ => {} 429 | } 430 | } 431 | 432 | let android_library = AndroidLibrary { 433 | file: unsafe { Box::from_raw(file_leak_ptr) }, 434 | memory_map, 435 | gnu_hash_table, 436 | dyn_symbols, 437 | alignment_offset: offset, 438 | dyn_strs: dyn_strings 439 | }; 440 | 441 | Ok(android_library) 442 | } 443 | } 444 | 445 | #[derive(Debug)] 446 | enum AndroidLoaderErr { 447 | ElfParsingError(String), 448 | UnsupportedRelocation(RelocType) 449 | } 450 | 451 | impl Display for AndroidLoaderErr { 452 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 453 | write!(f, "AndroidLoaderErr::{self:?}") 454 | } 455 | } 456 | 457 | impl Error for AndroidLoaderErr {} 458 | 459 | #[cfg(test)] 460 | mod tests { 461 | use crate::android_library::GnuHashTable; 462 | 463 | #[test] 464 | fn gnu_hash_tests() { 465 | assert_eq!(GnuHashTable::hash(""), 0x00001505); 466 | assert_eq!(GnuHashTable::hash("printf"), 0x156b2bb8); 467 | assert_eq!(GnuHashTable::hash("exit"), 0x7c967e3f); 468 | } 469 | } 470 | -------------------------------------------------------------------------------- /src/android_loader.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dadoum/android-loader/a59a1bef69f3e894600fc4afd8e51d799d20932c/src/android_loader.rs -------------------------------------------------------------------------------- /src/hook_manager.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::lazy_static; 2 | use std::{collections::HashMap, sync::Mutex}; 3 | use std::sync::MutexGuard; 4 | 5 | lazy_static! { 6 | static ref HOOKS: Mutex> = Mutex::new(HashMap::new()); 7 | } 8 | 9 | /// Get the list of hooks 10 | pub fn get_hooks<'a>() -> MutexGuard<'a, HashMap> { 11 | let hooks = HOOKS.lock().unwrap(); 12 | hooks 13 | } 14 | 15 | /// Add a list of hooks to the global list 16 | pub fn add_hooks(hooks: HashMap) { 17 | let mut global_hooks = HOOKS.lock().unwrap(); 18 | for (key, value) in hooks.iter() { 19 | global_hooks.insert(key.clone(), *value); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate core; 2 | 3 | pub mod android_library; 4 | pub mod android_loader; 5 | pub mod hook_manager; 6 | mod relocation_types; 7 | 8 | pub use sysv64::sysv64; 9 | 10 | #[cfg(all(target_family = "windows", target_arch = "x86_64"))] 11 | pub use sysv64::sysved64_type as sysv64_type; 12 | 13 | #[cfg(not(all(target_family = "windows", target_arch = "x86_64")))] 14 | pub use sysv64::sysvno64_type as sysv64_type; 15 | 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use rand::Rng; 20 | use std::collections::HashMap; 21 | use std::ffi::CString; 22 | use std::os::raw::c_char; 23 | use libc::{chmod, close, free, fstat, ftruncate, gettimeofday, lstat, malloc, mkdir, open, read, strncpy, umask, write}; 24 | 25 | use crate::android_library::AndroidLibrary; 26 | use crate::{sysv64, sysv64_type}; 27 | 28 | #[sysv64] 29 | fn arc4random() -> u32 { 30 | rand::thread_rng().gen() 31 | } 32 | 33 | #[test] 34 | fn load_android_libraries() { 35 | let mut hooks = HashMap::new(); 36 | hooks.insert("arc4random".to_owned(), arc4random as usize); 37 | hooks.insert("chmod".to_owned(), chmod as usize); 38 | hooks.insert("close".to_owned(), close as usize); 39 | hooks.insert("free".to_owned(), free as usize); 40 | hooks.insert("fstat".to_owned(), fstat as usize); 41 | hooks.insert("ftruncate".to_owned(), ftruncate as usize); 42 | hooks.insert("gettimeofday".to_owned(), gettimeofday as usize); 43 | hooks.insert("lstat".to_owned(), lstat as usize); 44 | hooks.insert("malloc".to_owned(), malloc as usize); 45 | hooks.insert("mkdir".to_owned(), mkdir as usize); 46 | hooks.insert("open".to_owned(), open as usize); 47 | hooks.insert("read".to_owned(), read as usize); 48 | hooks.insert("strncpy".to_owned(), strncpy as usize); 49 | hooks.insert("umask".to_owned(), umask as usize); 50 | hooks.insert("write".to_owned(), write as usize); 51 | crate::hook_manager::add_hooks(hooks); 52 | 53 | let store_services_core = 54 | AndroidLibrary::load("lib/x86_64/libstoreservicescore.so") 55 | .expect("Cannot load StoreServicesCore"); 56 | 57 | println!("Library loaded. Let's start."); 58 | let load_library_with_path: sysv64_type!(fn(*const c_char) -> i32) = 59 | unsafe { std::mem::transmute(store_services_core.get_symbol("kq56gsgHG6").unwrap()) }; // Sph98paBcz abort 60 | let library_path = CString::new("lib/x86_64/").unwrap(); 61 | let ret = load_library_with_path(library_path.as_ptr() as *const c_char); 62 | println!("provisioning path, ADI returned {}", ret); 63 | 64 | let set_android_identifier: sysv64_type!(fn(*const c_char, u32) -> i32) = 65 | unsafe { std::mem::transmute(store_services_core.get_symbol("Sph98paBcz").unwrap()) }; // Sph98paBcz abort 66 | // println!("{:p}", set_android_identifier as *const ()); 67 | let identifier = "f213456789abcde0"; 68 | let str = CString::new(identifier).unwrap(); 69 | let len = identifier.len() as u32; 70 | let ret = set_android_identifier(str.as_ptr() as *const c_char, len); 71 | println!("Fin ? ADI returned {}", ret); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/relocation_types.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] 2 | pub type RelocType = u32; 3 | 4 | #[cfg(any(target_arch = "x86", target_arch = "arm"))] 5 | pub type RelocType = u8; 6 | 7 | pub enum RelocationType { 8 | Absolute, 9 | GlobalData, 10 | JumpSlot, 11 | Relative, 12 | Unknown(RelocType) 13 | } 14 | 15 | impl From for RelocationType { 16 | #[cfg(target_arch = "x86_64")] 17 | fn from(reloc: RelocType) -> RelocationType { 18 | match reloc { 19 | 1 => RelocationType::Absolute, 20 | 6 => RelocationType::GlobalData, 21 | 7 => RelocationType::JumpSlot, 22 | 8 => RelocationType::Relative, 23 | _ => RelocationType::Unknown(reloc) 24 | } 25 | } 26 | 27 | #[cfg(target_arch = "x86")] 28 | fn from(reloc: RelocType) -> RelocationType { 29 | match reloc { 30 | 1 => RelocationType::Absolute, 31 | 6 => RelocationType::GlobalData, 32 | 7 => RelocationType::JumpSlot, 33 | 8 => RelocationType::Relative, 34 | _ => RelocationType::Unknown(reloc) 35 | } 36 | } 37 | 38 | #[cfg(target_arch = "aarch64")] 39 | fn from(reloc: RelocType) -> RelocationType { 40 | match reloc { 41 | 257 => RelocationType::Absolute, 42 | 1025 => RelocationType::GlobalData, 43 | 1026 => RelocationType::JumpSlot, 44 | 1027 => RelocationType::Relative, 45 | _ => RelocationType::Unknown(reloc) 46 | } 47 | } 48 | 49 | #[cfg(target_arch = "arm")] 50 | fn from(reloc: RelocType) -> RelocationType { 51 | match reloc { 52 | 2 => RelocationType::Absolute, 53 | 21 => RelocationType::GlobalData, 54 | 22 => RelocationType::JumpSlot, 55 | 23 => RelocationType::Relative, 56 | _ => RelocationType::Unknown(reloc) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sysv64/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sysv64" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc-macro = true 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /sysv64/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | use proc_macro::TokenStream; 3 | 4 | #[proc_macro_attribute] 5 | pub fn sysv64(_args: TokenStream, input: TokenStream) -> TokenStream { 6 | let mut output = String::new(); 7 | let input = input.to_string(); 8 | 9 | let input_splitted: Vec<&str> = input.split("fn").collect(); 10 | 11 | output.push_str("#[cfg(target_arch = \"x86_64\")]\n"); 12 | output.push_str(&input_splitted[0]); 13 | output.push_str("extern \"sysv64\" fn"); 14 | output.push_str(&input_splitted[1]); 15 | output.push_str("#[cfg(not(target_arch = \"x86_64\"))]\n"); 16 | output.push_str(&input_splitted[0]); 17 | output.push_str("extern \"C\" fn"); 18 | output.push_str(&input_splitted[1]); 19 | 20 | output.parse().unwrap() 21 | } 22 | 23 | #[proc_macro] 24 | pub fn sysved64_type(input: TokenStream) -> TokenStream { 25 | let mut output = String::new(); 26 | let input = input.to_string(); 27 | 28 | output.push_str("extern \"sysv64\" "); 29 | output.push_str(&input); 30 | 31 | output.parse().unwrap() 32 | } 33 | 34 | #[proc_macro] 35 | pub fn sysvno64_type(input: TokenStream) -> TokenStream { 36 | let mut output = String::new(); 37 | let input = input.to_string(); 38 | 39 | output.push_str("extern \"C\" "); 40 | output.push_str(&input); 41 | 42 | output.parse().unwrap() 43 | } 44 | --------------------------------------------------------------------------------