├── .gitignore ├── .gitmodules ├── Cargo.toml ├── README.md ├── demo ├── Cargo.toml └── src │ └── main.rs ├── loader ├── Cargo.toml └── src │ └── main.rs ├── pic ├── 1.png ├── 2.png └── 3.gif └── tracer ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | .vscode/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tracer/frida-rust"] 2 | path = tracer/frida-rust 3 | url = https://github.com/Mrack/frida-rust.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "tracer", 4 | "loader", 5 | "demo", 6 | ] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tracer-rs 2 | 3 | Rust写的一个x86_64,aarch64 trace工具 4 | 使用 frida stalker 实现的 trace 5 | 记录执行指令以及统计寄存器变化 6 | ### x86_64 7 | ![Image text](https://raw.githubusercontent.com/Mrack/tracer-rs/master/pic/1.png) 8 | 9 | ### aarch64 10 | ![Image text](https://raw.githubusercontent.com/Mrack/tracer-rs/master/pic/2.png) 11 | 12 | 13 | ## tracer 14 | fun_addr fun_size 为 trace 函数地址 以及大小 15 | ··· 16 | static ref RANGE: TraceRange = 17 | TraceRange { 18 | begin: {fun_addr} as u64, 19 | size: {fun_size}, 20 | }; 21 | ··· 22 | 23 | ## loader(windows) 24 | 注入dll工具 25 | 26 | ## loader(Android) 27 | todo. 28 | 29 | ## 用法 30 | 修改 fun_addr fun_size 指定函数地址编译 31 | 使用loader注入生成的tracer.dll (libtracer.so) 32 | 33 | ## 样例 34 | ![Image text](https://raw.githubusercontent.com/Mrack/tracer-rs/master/pic/3.gif) 35 | -------------------------------------------------------------------------------- /demo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "demo" 3 | version = "0.1.0" 4 | authors = ["mrack "] 5 | edition = "2018" 6 | license = "https://github.com/Mrack/tracer-rs" 7 | publish = false 8 | -------------------------------------------------------------------------------- /demo/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020-2022 Mrack 3 | * Email: Mrack@qq.com 4 | */ 5 | 6 | #[inline(never)] 7 | fn verify(input: &String) -> bool { 8 | let key = vec!['m', 'r', 'a', 'c', 'k']; 9 | let input_arr = input.as_bytes(); 10 | if key.len() != input_arr.len() { 11 | return false; 12 | } 13 | for (i, c) in input_arr.iter().enumerate() { 14 | if key[i] as u8 != *c { 15 | return false; 16 | } 17 | } 18 | true 19 | } 20 | 21 | 22 | fn main() { 23 | loop { 24 | let mut input = String::new(); 25 | std::io::stdin() 26 | .read_line(&mut input) 27 | .expect("Failed to read line"); 28 | 29 | input = input.trim().to_string(); 30 | 31 | if verify(&input.to_ascii_lowercase()) { 32 | println!("Thank you for your purchase! key is {}", input); 33 | break; 34 | } else { 35 | println!("This unauthorized key.") 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /loader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loader" 3 | version = "0.1.0" 4 | authors = ["mrack "] 5 | edition = "2018" 6 | license = "https://github.com/Mrack/tracer-rs" 7 | publish = false 8 | 9 | [dependencies] 10 | winapi = { version = "0.3.9", features = [ 11 | "psapi", 12 | "libloaderapi", 13 | "processthreadsapi", 14 | "handleapi", 15 | "memoryapi", 16 | "tlhelp32", 17 | ] } 18 | -------------------------------------------------------------------------------- /loader/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020-2022 Mrack 3 | * Email: Mrack@qq.com 4 | */ 5 | 6 | use std::ffi::CString; 7 | use winapi as win; 8 | 9 | #[cfg(target_os = "windows")] 10 | fn main() { 11 | println!("input inject dll path:"); 12 | let mut input = String::new(); 13 | std::io::stdin() 14 | .read_line(&mut input) 15 | .expect("Failed to read line"); 16 | let dll_path = input.trim().to_string(); 17 | 18 | println!("input inject process id:"); 19 | let mut input = String::new(); 20 | std::io::stdin() 21 | .read_line(&mut input) 22 | .expect("Failed to read line"); 23 | let pid = input.trim().to_string().parse::().unwrap(); 24 | 25 | let dll_len = dll_path.len(); 26 | let dll_path = std::ffi::CString::new(dll_path).unwrap(); 27 | let dll_path = dll_path.as_ptr(); 28 | let h_process = unsafe { 29 | win::um::processthreadsapi::OpenProcess(win::um::winnt::PROCESS_ALL_ACCESS, 0, pid) 30 | }; 31 | if h_process.is_null() { 32 | println!("OpenProcess failed"); 33 | return; 34 | } 35 | 36 | let alloc_addr = unsafe { 37 | win::um::memoryapi::VirtualAllocEx( 38 | h_process, 39 | std::ptr::null_mut(), 40 | dll_len, 41 | win::um::winnt::MEM_COMMIT, 42 | win::um::winnt::PAGE_READWRITE, 43 | ) 44 | }; 45 | 46 | if alloc_addr.is_null() { 47 | println!("VirtualAllocEx failed"); 48 | return; 49 | } 50 | 51 | let mut bytes_write = 0; 52 | 53 | let fn_lla_addr = unsafe { 54 | std::mem::transmute(match get_fn_addr("Kernel32.dll", "LoadLibraryA") { 55 | Ok(addr) => addr, 56 | Err(_e) => 0, 57 | }) 58 | }; 59 | 60 | unsafe { 61 | win::um::memoryapi::WriteProcessMemory( 62 | h_process, 63 | alloc_addr, 64 | dll_path as *const win::ctypes::c_void, 65 | dll_len, 66 | &mut bytes_write, 67 | ) 68 | }; 69 | let h_thread = unsafe { 70 | win::um::processthreadsapi::CreateRemoteThread( 71 | h_process, 72 | std::ptr::null_mut(), 73 | 0, 74 | Some(fn_lla_addr), 75 | alloc_addr, 76 | 0, 77 | std::ptr::null_mut(), 78 | ) 79 | }; 80 | unsafe { win::um::handleapi::CloseHandle(h_thread) }; 81 | unsafe { win::um::handleapi::CloseHandle(h_process) }; 82 | } 83 | 84 | fn get_fn_addr<'a>(mod_name: &str, fn_name: &str) -> Result { 85 | let mod_str = CString::new(mod_name).unwrap(); 86 | let fn_str = CString::new(fn_name).unwrap(); 87 | 88 | let mod_handle = unsafe { winapi::um::libloaderapi::GetModuleHandleA(mod_str.as_ptr()) }; 89 | 90 | if mod_handle == core::ptr::null_mut() { 91 | return Err("GetModuleHandleA"); 92 | } 93 | 94 | let fn_addr = unsafe { winapi::um::libloaderapi::GetProcAddress(mod_handle, fn_str.as_ptr()) }; 95 | 96 | if fn_addr == core::ptr::null_mut() { 97 | return Err("GetProcAddress"); 98 | } 99 | Ok(fn_addr as u64) 100 | } 101 | -------------------------------------------------------------------------------- /pic/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrack/tracer-rs/94b5df9ac0fd7ea1359af311e08e99c5f838021b/pic/1.png -------------------------------------------------------------------------------- /pic/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrack/tracer-rs/94b5df9ac0fd7ea1359af311e08e99c5f838021b/pic/2.png -------------------------------------------------------------------------------- /pic/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mrack/tracer-rs/94b5df9ac0fd7ea1359af311e08e99c5f838021b/pic/3.gif -------------------------------------------------------------------------------- /tracer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tracer-rs" 3 | version = "0.1.0" 4 | authors = ["mrack "] 5 | edition = "2018" 6 | license = "https://github.com/Mrack/tracer-rs" 7 | publish = false 8 | 9 | 10 | [lib] 11 | name = "tracer" 12 | crate-type = ["dylib"] 13 | 14 | [dependencies] 15 | frida-gum = { path = "frida-rust/frida-gum", features = [ 16 | "invocation-listener", 17 | "event-sink", 18 | ] } 19 | frida-gum-sys = { path = "frida-rust/frida-gum-sys", features = [ 20 | "auto-download", 21 | ] } 22 | lazy_static = "1.4" 23 | 24 | 25 | winapi = { version = "0.3.9", features = [ 26 | "winuser", 27 | ] } 28 | -------------------------------------------------------------------------------- /tracer/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020-2022 Mrack 3 | * Email: Mrack@qq.com 4 | */ 5 | 6 | use frida_gum as gum; 7 | use frida_gum::stalker::{Stalker, Transformer}; 8 | use gum::interceptor::{Interceptor, InvocationListener}; 9 | use gum::stalker::NoneEventSink; 10 | use lazy_static::lazy_static; 11 | use winapi::shared::windef::HWND; 12 | use std::collections::HashMap; 13 | use std::os::raw::c_void; 14 | use std::sync::Mutex; 15 | use winapi::um::winnt::{DLL_PROCESS_ATTACH, LPCSTR}; 16 | use winapi::shared::minwindef::*; 17 | use std::io::Write; 18 | use std::ffi::CString; 19 | 20 | #[cfg(target_os = "windows")] 21 | #[no_mangle] 22 | extern "stdcall" fn DllMain(_: HINSTANCE, fdw_reason: DWORD, _: LPVOID){ 23 | if fdw_reason == DLL_PROCESS_ATTACH { 24 | let msg = CString::new("injected").unwrap(); 25 | let title = CString::new("mrack").unwrap(); 26 | unsafe { 27 | winapi::um::winuser::MessageBoxA(0 as HWND, msg.as_ptr() as LPCSTR, title.as_ptr() as LPCSTR, 0); 28 | } 29 | start_stalker() 30 | } 31 | } 32 | 33 | fn write_to_file(path: &str, content: &str) { 34 | let mut file = std::fs::OpenOptions::new() 35 | .append(true) 36 | .create(true) 37 | .open(path) 38 | .unwrap(); 39 | file.write_all((content.to_string() + "\n").as_bytes()).unwrap(); 40 | } 41 | 42 | lazy_static! { 43 | static ref GUM: gum::Gum = unsafe { gum::Gum::obtain() }; 44 | 45 | static ref RANGE: TraceRange = 46 | TraceRange { 47 | begin: 0x0007FF6C15E13F0 as u64, 48 | size: 800, 49 | }; 50 | 51 | static ref MAP: Mutex> = Mutex::new(HashMap::new()); 52 | 53 | static ref INS_INFO: Mutex> = Mutex::new(HashMap::new()); 54 | } 55 | 56 | static mut STALKER: *mut c_void = 0 as *mut c_void; 57 | 58 | struct TraceRange { 59 | begin: u64, 60 | size: u64, 61 | } 62 | struct AttachListener; 63 | 64 | macro_rules! save_context { 65 | ($map:ident,$ct:expr ,$($name:ident),*) => { 66 | $( 67 | $map.insert(stringify!($name).to_string(), $ct.$name()); 68 | )* 69 | }; 70 | 71 | 72 | ($map:ident,$ct:expr) => { 73 | save_context!($map,$ct, sp, fp, lr); 74 | for i in 0..29{ 75 | $map.insert(format!("x{}", i), $ct.reg(i)); 76 | } 77 | }; 78 | } 79 | 80 | 81 | impl InvocationListener for AttachListener { 82 | fn on_enter(&mut self, _: gum::interceptor::InvocationContext) { 83 | 84 | let transformer = Transformer::from_callback(&GUM, |basic_block, _output| { 85 | 86 | for instr in basic_block { 87 | 88 | if instr.instr().address() >= RANGE.begin 89 | && instr.instr().address() <= RANGE.begin + RANGE.size 90 | { 91 | 92 | let ins = format!( 93 | "{:x} {} {}", 94 | instr.instr().address(), 95 | instr.instr().mnemonic().unwrap(), 96 | instr.instr().op_str().unwrap() 97 | ); 98 | 99 | INS_INFO.lock().unwrap().insert(instr.instr().address(), ins); 100 | instr.put_callout(|_cpu_context| { 101 | let mut ct = MAP.lock().unwrap(); 102 | if !ct.is_empty() { 103 | let mut cur = HashMap::new(); 104 | 105 | #[cfg(target_arch = "x86_64")] 106 | save_context!(cur,&_cpu_context,r15,r14,r13,r12,r11,r10,r9,r8,rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax); 107 | 108 | #[cfg(target_arch = "aarch64")] 109 | save_context!(cur,&_cpu_context); 110 | 111 | for (k,v) in cur.iter(){ 112 | if ct[k]!= *v { 113 | write_to_file("log.txt", &format!("\t{}: {:x} -> {:x}",k,ct[k],v)); 114 | } 115 | } 116 | } 117 | #[cfg(target_arch = "x86_64")] 118 | save_context!(ct,&_cpu_context,r15,r14,r13,r12,r11,r10,r9,r8,rdi,rsi,rbp,rsp,rbx,rdx,rcx,rax); 119 | #[cfg(target_arch = "aarch64")] 120 | save_context!(ct,&_cpu_context); 121 | 122 | #[cfg(target_arch = "x86_64")] 123 | write_to_file("log.txt", INS_INFO.lock().unwrap()[&_cpu_context.rip()].as_str()); 124 | #[cfg(target_arch = "aarch64")] 125 | write_to_file("log.txt", INS_INFO.lock().unwrap()[&_cpu_context.pc()].as_str()); 126 | }); 127 | } 128 | 129 | instr.keep(); 130 | } 131 | 132 | }); 133 | unsafe { 134 | let mut s = Stalker::new(&GUM); 135 | s.follow_me::(&transformer, None); 136 | STALKER = Box::leak(Box::new(s)) as *mut _ as *mut c_void; 137 | } 138 | } 139 | 140 | fn on_leave(&mut self, _: gum::interceptor::InvocationContext) { 141 | unsafe { 142 | let s = STALKER as *mut Stalker; 143 | (*s).unfollow_me(); 144 | } 145 | } 146 | } 147 | 148 | 149 | #[cfg(any( 150 | target_arch = "x86_64", 151 | target_arch = "aarch64" 152 | ))] 153 | extern "C" fn start_stalker() { 154 | let mut interceptor = Interceptor::obtain(&GUM); 155 | 156 | let mut listener = AttachListener {}; 157 | 158 | interceptor.attach( 159 | gum::NativePointer(RANGE.begin as *mut c_void), 160 | &mut listener, 161 | ); 162 | } 163 | --------------------------------------------------------------------------------