diff --git a/rust/backend/daemon/src/bin/cli.rs b/rust/backend/daemon/src/bin/cli.rs index 80e8d8c..5d67162 100644 --- a/rust/backend/daemon/src/bin/cli.rs +++ b/rust/backend/daemon/src/bin/cli.rs @@ -5,15 +5,11 @@ // SPDX-License-Identifier: MIT use clap::Parser; -use shared::ziofa::{ - GetAddressOfSymbolRequest, GetSymbolsOfProcessRequest, -}; use shared::{ config::{Configuration, SysSendmsgConfig, VfsWriteConfig}, ziofa::ziofa_client::ZiofaClient, }; use tonic::transport::Channel; -use tonic::Request; #[derive(Parser, Debug)] struct Args { @@ -30,59 +26,49 @@ struct Args { pid: i32, } -async fn test_get_symbols_of_process( - client: &mut ZiofaClient, - pid: i32, - package_name: String, - verbose: bool, +async fn test_get_symbols( + _client: &mut ZiofaClient, + _pid: i32, + _package_name: String, + _verbose: bool, ) { - println!("TEST get_symbols_of_process"); - - match client - .get_symbols_of_process(Request::new(GetSymbolsOfProcessRequest { - pid, - package_name, - })) - .await - { - Ok(res) => { - let names = res.into_inner().names; - println!("SUCCESS"); - if verbose { - for (i, s) in names.iter().enumerate() { - println!("Symbol {}: {}", i, s); - } - } - } - Err(e) => println!("ERROR: {:?}", e), - }; - println!(); + todo!("implement"); + // println!("TEST get_symbols_of_process"); + // + // match () { + // Ok(res) => { + // let names = res.into_inner().names; + // println!("SUCCESS"); + // if verbose { + // for (i, s) in names.iter().enumerate() { + // println!("Symbol {}: {}", i, s); + // } + // } + // } + // Err(e) => println!("ERROR: {:?}", e), + // }; + // println!(); } async fn test_get_address_of_symbol( - client: &mut ZiofaClient, - name: String, - pid: i32, - package_name: String, + _client: &mut ZiofaClient, + _name: String, + _pid: i32, + _package_name: String, ) { - println!("TEST get_address_of_symbol"); - - match client - .get_address_of_symbol(Request::new(GetAddressOfSymbolRequest { - name, - pid, - package_name, - })) - .await - { - Ok(res) => { - let offset = res.into_inner().offset; - println!("SUCCESS: {}", offset); - } - Err(e) => println!("ERROR: {:?}", e), - }; - - println!(); + todo!("implement"); + // println!("TEST get_address_of_symbol"); + // + // match Ok(()) + // { + // Ok(res) => { + // let offset = res.into_inner().offset; + // println!("SUCCESS: {}", offset); + // } + // Err(e) => println!("ERROR: {:?}", e), + // }; + // + // println!(); } async fn test_check_server(client: &mut ZiofaClient) { @@ -168,7 +154,7 @@ async fn main() { let config = test_get_configuration(&mut client, args.verbose).await; test_set_configuration(&mut client, config).await; test_list_processes(&mut client, args.verbose).await; - test_get_symbols_of_process( + test_get_symbols( &mut client, args.pid, "de.amosproj3.ziofa".to_string(), diff --git a/rust/backend/daemon/src/lib.rs b/rust/backend/daemon/src/lib.rs index 3e2e0f1..bcd17ba 100644 --- a/rust/backend/daemon/src/lib.rs +++ b/rust/backend/daemon/src/lib.rs @@ -12,11 +12,9 @@ mod helpers; mod procfs_utils; mod server; mod features; - mod symbols; mod collector; -mod symbols_helpers; pub async fn run_server() { helpers::bump_rlimit(); diff --git a/rust/backend/daemon/src/main.rs b/rust/backend/daemon/src/main.rs index 812a658..4a059ee 100644 --- a/rust/backend/daemon/src/main.rs +++ b/rust/backend/daemon/src/main.rs @@ -14,7 +14,6 @@ mod server; mod features; mod collector; mod symbols; -mod symbols_helpers; #[tokio::main] async fn main() { diff --git a/rust/backend/daemon/src/server.rs b/rust/backend/daemon/src/server.rs index 31f1dd7..8fa0d58 100644 --- a/rust/backend/daemon/src/server.rs +++ b/rust/backend/daemon/src/server.rs @@ -6,7 +6,7 @@ // SPDX-License-Identifier: MIT use crate::collector::MultiCollector; -use crate::symbols::{self, get_symbol_offset_for_function_of_process, SymbolHandler}; +use crate::symbols::SymbolHandler; use crate::{ configuration, constants, counter::Counter, @@ -17,7 +17,7 @@ use async_broadcast::{broadcast, Receiver, Sender}; use aya::Ebpf; use aya_log::EbpfLogger; use shared::ziofa::{ - Event, GetAddressOfSymbolRequest, GetAddressOfSymbolResponse, GetSymbolsOfProcessRequest, + Event, GetSymbolsRequest, PidMessage, StringResponse, Symbol, }; use shared::{ @@ -28,6 +28,7 @@ use shared::{ CheckServerResponse, ProcessList, SetConfigurationResponse, }, }; +use std::path::PathBuf; use std::{ops::DerefMut, sync::Arc}; use tokio::join; use tokio::sync::{mpsc, Mutex}; @@ -121,55 +122,83 @@ impl Ziofa for ZiofaImpl { } type GetOdexFilesStream = ReceiverStream>; + + // TODO: What is this function for? async fn get_odex_files( &self, request: Request, ) -> Result, Status> { let pid = request.into_inner().pid; - let symbol_handler_guard = self.symbol_handler.lock().await; - let odex_paths = symbol_handler_guard.get_odex_paths(pid)?.clone(); let (tx, rx) = mpsc::channel(4); + let symbol_handler_clone = self.symbol_handler.clone(); + + // let symbol_handler_guard = self.symbol_handler.lock().await; + // let odex_paths = symbol_handler_guard.get_odex_paths(pid)?; + tokio::spawn(async move { + let mut symbol_handler_guard = symbol_handler_clone.lock().await; + // TODO Error Handling + let odex_paths = match symbol_handler_guard.get_odex_paths(pid) { + Ok(paths) => paths, + Err(e) => { + tx.send(Err(Status::from(e))) + .await + .expect("Error sending Error to client ._."); + return; + } + }; + for path in odex_paths { tx.send(Ok(StringResponse { name: path.to_str().unwrap().to_string(), - })); + })) + .await + .expect("Error sending odex file to client"); } }); Ok(Response::new(ReceiverStream::new(rx))) } - type GetSymbolsOfProcessStream = ReceiverStream>; + type GetSymbolsStream = ReceiverStream>; - async fn get_symbols_of_process( + async fn get_symbols( &self, - request: Request, - ) -> Result, Status> { - let process = request.into_inner(); - let pid = process.pid; - let package_name = process.package_name; - - Ok(Response::new(SymbolList { - names: symbols::get_symbols_of_pid(pid, &package_name).await?, - })) - } + request: Request, + ) -> Result, Status> { + let process_request = request.into_inner(); + let pid = process_request.pid; + let odex_file_path_string = process_request.odex_file_path; + let odex_file_path = PathBuf::from(odex_file_path_string); - async fn get_address_of_symbol( - &self, - request: Request, - ) -> Result, Status> { - let request_inner = request.into_inner(); - let symbol_name = request_inner.name; - let pid = request_inner.pid; - let package_name = request_inner.package_name; + let symbol_handler_clone = self.symbol_handler.clone(); - let offset = - get_symbol_offset_for_function_of_process(pid, &package_name, &symbol_name).await?; + let (tx, rx) = mpsc::channel(4); + tokio::spawn(async move { + let mut symbol_handler_guard = symbol_handler_clone.lock().await; + + let symbol = match symbol_handler_guard.get_symbols(pid, &odex_file_path).await{ + Ok(symbol) => symbol, + Err(e) => { + tx.send(Err(Status::from(e))) + .await + .expect("Error sending Error to client ._."); + return; + } + }; + for (symbol, offset) in symbol.iter() { + tx.send(Ok(Symbol{ + method: symbol.to_string(), + offset: *offset, + })) + .await + .expect("Error sending odex file to client"); + } + }); - Ok(Response::new(GetAddressOfSymbolResponse { offset })) + Ok(Response::new(ReceiverStream::new(rx))) } } @@ -191,7 +220,7 @@ pub async fn serve_forever() { let mut state = State::new(); state.init(&mut ebpf).expect("should work"); - let mut symbol_handler = Arc::new(Mutex::new(SymbolHandler::new())); + let symbol_handler = Arc::new(Mutex::new(SymbolHandler::new())); let ebpf = Arc::new(Mutex::new(ebpf)); let state = Arc::new(Mutex::new(state)); diff --git a/rust/backend/daemon/src/symbols.rs b/rust/backend/daemon/src/symbols.rs index 9ec01ed..9c27935 100644 --- a/rust/backend/daemon/src/symbols.rs +++ b/rust/backend/daemon/src/symbols.rs @@ -4,21 +4,39 @@ // SPDX-License-Identifier: MIT use crate::constants::OATDUMP_PATH; -use crate::symbols_helpers::{self, get_odex_files_for_pid}; -use object::Symbol; +use object::{Object, ObjectSymbol, ReadCache}; use procfs::process::{MMapPath, Process}; use procfs::ProcError; use serde::{Deserialize, Serialize}; -use serde_json::de::IoRead; -use serde_json::StreamDeserializer; use std::collections::{HashMap, HashSet}; use std::fs::File; -use std::hash::Hash; -use std::io::BufReader; -use std::path::{Path, PathBuf}; -use symbols_helpers::{generate_json_oatdump, get_section_address}; +use std::io::Error; +use std::path::PathBuf; use thiserror::Error; use tokio::io::AsyncBufReadExt; +use tokio::process::Command; + +#[derive(Debug, Error)] +pub enum SymbolError { + #[error(transparent)] + IOError(#[from] std::io::Error), + #[error(transparent)] + JoinError(#[from] tokio::task::JoinError), + #[error(transparent)] + ProcError(#[from] ProcError), + #[error("Odex paths are not loaded for specified pid")] + OdexPathsNotLoaded { pid: i32 }, + #[error("Symbols are not loaded for specified pid and odex path")] + SymbolsNotLoaded { pid: i32, odex_path: PathBuf }, + #[error(transparent)] + SerdeError(#[from] serde_json::Error), +} + +impl From for tonic::Status { + fn from(err: SymbolError) -> Self { + Self::from_error(Box::new(err)) + } +} #[derive(Serialize, Deserialize, Debug)] struct JsonSymbol { @@ -27,8 +45,6 @@ struct JsonSymbol { } pub struct SymbolHandler { - /// maps pid to odex paths supplied by /proc/pid/maps - odex_paths: HashMap>, /// maps pid, odex file path and symbol name to offset symbols: HashMap>>, } @@ -36,45 +52,56 @@ pub struct SymbolHandler { impl SymbolHandler { pub fn new() -> Self { SymbolHandler { - odex_paths: HashMap::new(), symbols: HashMap::new(), } } /// loads the paths to all odex files // TODO: blocking? - pub fn load_odex_paths(&mut self, pid: i32) -> Result<(), ProcError> { + fn load_odex_paths(&mut self, pid: i32) -> Result<(), ProcError> { let process = Process::new(pid)?; let maps = process.maps()?; // TODO: Check for old/potentially outdated entries and reload them - if self.odex_paths.contains_key(&pid) { + if self.symbols.contains_key(&pid) { return Ok(()); } - self.odex_paths.insert( - pid, - maps.iter() - .filter_map(|mm_map| match mm_map.clone().pathname { - MMapPath::Path(path) => Some(path), - _ => None, - }) - .filter(|path: &PathBuf| path.to_str().unwrap().contains(".odex")) - .collect(), - ); + for _maps in maps.iter() + .filter_map(|mm_map| match mm_map.clone().pathname { + MMapPath::Path(path) => Some(path), + _ => None, + }) + .filter(|path: &PathBuf| path.to_str().unwrap().contains(".odex")) + { + self.symbols.insert(pid, HashMap::new()); + } Ok(()) // TODO: Remove old/long unused paths from cache } - pub fn get_odex_paths(&self, pid: i32) -> Result<&HashSet, SymbolError> { - self.odex_paths + pub fn get_odex_paths(&mut self, pid: i32) -> Result, SymbolError> { + if !self.symbols.contains_key(&pid) { + self.load_odex_paths(pid)?; + } + + Ok(self.symbols .get(&pid) - .ok_or(SymbolError::OdexPathsNotLoaded { pid }) + .ok_or(SymbolError::OdexPathsNotLoaded { pid })? + .keys() + .collect() + ) } - pub async fn load_symbols(&mut self, pid: i32, odex_path: &PathBuf) -> Result<(), SymbolError> { - generate_json_oatdump(odex_path).await; + async fn load_symbols(&mut self, pid: i32, odex_path: &PathBuf) -> Result<(), SymbolError> { + // TODO: Check cache before re-generating oatdump... + + self.generate_json_oatdump(odex_path) + .await + .expect("fn 'load_symbols' Failed to generate oatdump"); + + let oatdata_section_offset = self.get_oatsection_address(odex_path).await?; let json_file = tokio::fs::File::open(OATDUMP_PATH).await?; let json_reader = tokio::io::BufReader::new(json_file); @@ -98,12 +125,10 @@ impl SymbolHandler { let symbol_to_offset = odex_to_symbol_map.get_mut(odex_path).unwrap(); // store all symbols with their offsets in the map - for line in lines.next_line().await { - if line.is_none() { - break; - } - let symbol: JsonSymbol = serde_json::from_str(&line.unwrap())?; - let offset = + while let Some(line) = lines.next_line().await? { + let symbol: JsonSymbol = serde_json::from_str(&line)?; + // the actual symbol offset is build from section offset and relative offset + let offset = oatdata_section_offset + u64::from_str_radix(symbol.offset.strip_prefix("0x").unwrap(), 16).unwrap(); symbol_to_offset.insert(symbol.method, offset); } @@ -111,11 +136,20 @@ impl SymbolHandler { Ok(()) } - pub fn get_symbols( - &self, + pub async fn get_symbols( + &mut self, pid: i32, odex_path: &PathBuf, ) -> Result<&HashMap, SymbolError> { + // TODO: check if was already generated, if not exec. load symbol... + if !self.symbols.contains_key(&pid) { + self.load_odex_paths(pid)?; + } + + if !self.symbols.get(&pid).unwrap().contains_key(odex_path) { + self.load_symbols(pid, odex_path).await?; + } + self.symbols .get(&pid) .ok_or(SymbolError::OdexPathsNotLoaded { pid })? @@ -125,136 +159,36 @@ impl SymbolHandler { odex_path: odex_path.to_path_buf(), }) } -} -#[derive(Debug, Error)] -pub enum SymbolError { - #[error("Symbol doesn't exist")] - SymbolDoesNotExist { symbol: String }, - #[error("Symbol is not compiled")] - SymbolIsNotCompiled { symbol: String }, - #[error(transparent)] - IOError(#[from] std::io::Error), - #[error(transparent)] - JoinError(#[from] tokio::task::JoinError), - #[error("Other")] - Other { text: String }, - #[error(transparent)] - ProcError(#[from] ProcError), - #[error("Odex paths are not loaded for specified pid")] - OdexPathsNotLoaded { pid: i32 }, - #[error("Symbols are not loaded for specified pid and odex path")] - SymbolsNotLoaded { pid: i32, odex_path: PathBuf }, - #[error(transparent)] - SerdeError(#[from] serde_json::Error), -} - -impl From for tonic::Status { - fn from(err: SymbolError) -> Self { - Self::from_error(Box::new(err)) - } -} - -pub async fn get_symbol_offset_for_function_of_process( - pid: i32, - package_name: &str, - symbol_name: &str, -) -> Result { - let odex_file_paths = get_odex_files_for_pid(pid)?; - parse_odex_files_for_process(&odex_file_paths, symbol_name, package_name).await -} - -async fn parse_odex_files_for_process( - odex_file_paths: &Vec, - symbol_name: &str, - package_name: &str, -) -> Result { - for odex_file_path in odex_file_paths { - // TODO: is this really the way... i doubt it - if !odex_file_path.to_str().unwrap().contains(package_name) { - continue; - } - - return Ok(parse_odex_file(odex_file_path, symbol_name).await?); + async fn generate_json_oatdump(&self, path: &PathBuf) -> Result<(), SymbolError> { + Command::new("oatdump") + .args(vec![ + format!("--output={}", OATDUMP_PATH).as_str(), + "--dump-method-and-offset-as-json", + format!("--oat-file={}", path.to_str().unwrap()).as_str(), + ]) + .spawn()? + .wait() + .await?; + Ok(()) } - Err(SymbolError::Other { - text: format!("no oat file found for package-name: {}", package_name), - }) -} -pub async fn get_symbols_of_pid(pid: i32, package_name: &str) -> Result, SymbolError> { - let odex_file_paths = get_odex_files_for_pid(pid)?; - for odex_file_path in odex_file_paths { - // TODO: is this really the way... i doubt it - if !odex_file_path.to_str().unwrap().contains(package_name) { - continue; - } - generate_json_oatdump(&odex_file_path).await?; - let outdump_contents = get_oatdump_contents()?; - return Ok(get_symbols_from_json(outdump_contents)); - } + async fn get_oatsection_address(&self, oat_path: &PathBuf) -> Result { + tokio::task::spawn_blocking({ + let path = oat_path.clone(); + move || { + let file = File::open(path)?; + let file_cache = ReadCache::new(file); + let obj = object::File::parse(&file_cache).unwrap(); - Err(SymbolError::Other { - text: format!("no oat file found for package-name: {}", package_name), - }) -} + let section = obj + .dynamic_symbols() + .find(|s| s.name() == Ok("oatdata")) + .unwrap(); -async fn parse_odex_file(odex_file_path: &PathBuf, symbol_name: &str) -> Result { - let section_address = get_section_address(odex_file_path).await?; - generate_json_oatdump(odex_file_path).await?; - get_symbol_address_from_json(symbol_name, section_address) -} - -fn get_symbols_from_json( - outdump_contents: StreamDeserializer<'_, IoRead>, JsonSymbol>, -) -> Vec { - outdump_contents - .filter_map(|c| match c { - Ok(symbol) => Some(symbol.method), - Err(_) => None, - }) - .collect() -} - -fn get_symbol_address_from_json( - symbol_name: &str, - section_address: u64, -) -> Result { - for res in get_oatdump_contents()? { - if let Ok(symbol) = res { - if symbol.method == symbol_name { - return get_symbol_address(section_address, symbol); + Ok(section.address()) } - } - } - Err(SymbolError::SymbolDoesNotExist { - symbol: symbol_name.to_string(), - }) -} - -pub async fn stream_symbols( -) -> Result>, JsonSymbol>, SymbolError> { - let json_file = tokio::fs::File::open(OATDUMP_PATH).await?; - let json_reader = tokio::io::BufReader::new(json_file); - let lines = json_reader.lines(); - lines.Ok(serde_json::Deserializer::from_reader(json_reader).into_iter::()) -} - -fn get_oatdump_contents( -) -> Result>, JsonSymbol>, SymbolError> { - let json_file = File::open(OATDUMP_PATH)?; - let json_reader = BufReader::new(json_file); - Ok(serde_json::Deserializer::from_reader(json_reader).into_iter::()) -} - -fn get_symbol_address(section_address: u64, symbol: JsonSymbol) -> Result { - let symbol_address = - u64::from_str_radix(symbol.offset.strip_prefix("0x").unwrap(), 16).unwrap(); - if symbol_address == 0 { - return Err(SymbolError::SymbolIsNotCompiled { - symbol: symbol.method.to_string(), - }); + }) + .await? } - - Ok(symbol_address + section_address) -} +} \ No newline at end of file diff --git a/rust/backend/daemon/src/symbols_helpers.rs b/rust/backend/daemon/src/symbols_helpers.rs deleted file mode 100644 index bda3f45..0000000 --- a/rust/backend/daemon/src/symbols_helpers.rs +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Benedikt Zinn -// SPDX-FileCopyrightText: 2024 Robin Seidl -// -// SPDX-License-Identifier: MIT - -use std::path::PathBuf; -use procfs::process::{MMapPath, Process}; -use tokio::process::Command; -use std::fs::File; -use object::{Object, ObjectSymbol, ReadCache}; -use std::io::Error; -use crate::constants::OATDUMP_PATH; -use crate::symbols::SymbolError; - -pub async fn generate_json_oatdump(path: &PathBuf) -> Result<(), SymbolError> { - let _oatdump_status = Command::new("oatdump") - .args(vec![ - format!("--output={}", OATDUMP_PATH).as_str(), - "--dump-method-and-offset-as-json", - format!("--oat-file={}", path.to_str().unwrap()).as_str(), - ]) - .spawn()? - .wait() - .await?; - // TODO: Check for status [robin] - // do we even need the status -> if yes for what? [benedikt] - Ok(()) -} - -pub async fn get_section_address(oat_path: &PathBuf) -> Result { - tokio::task::spawn_blocking({ - let path = oat_path.clone(); - move || get_symbol_address_from_oat(&path, "oatdata") - }) - .await? -} - -pub fn get_odex_files_for_pid(pid: i32) -> Result, SymbolError> { - // get from : /proc/pid/maps -> oat directory (directory with all the odex files) - - let process = Process::new(pid)?; - let maps = process.maps()?; - let all_files: Vec = maps - .iter() - .filter_map(|mm_map| match mm_map.clone().pathname { - MMapPath::Path(path) => Some(path), - _ => None, - }) - .filter(|path: &PathBuf| path.to_str().unwrap().contains(".odex")) - .collect(); - match !all_files.is_empty() { - true => Ok(all_files), - false => Err(SymbolError::Other { - text: format!("Could not find oat file for process with pid: {}", pid), - }), - } -} - -fn get_symbol_address_from_oat(path: &PathBuf, symbol_name: &str) -> Result { - let file = File::open(path)?; - let file_cache = ReadCache::new(file); - let obj = object::File::parse(&file_cache).unwrap(); - - let section = obj - .dynamic_symbols() - .find(|s| s.name() == Ok(symbol_name)) - .unwrap(); - - Ok(section.address()) -} \ No newline at end of file diff --git a/rust/shared/proto/ziofa.proto b/rust/shared/proto/ziofa.proto index 16402dc..c5051c5 100644 --- a/rust/shared/proto/ziofa.proto +++ b/rust/shared/proto/ziofa.proto @@ -21,18 +21,16 @@ service Ziofa { rpc InitStream(google.protobuf.Empty) returns (stream Event) {} // all Responses genereated by the ebpf-programms are send via this stream rpc GetOdexFiles(PidMessage) returns (stream StringResponse) {} - rpc GetSymbolsOfProcess(GetSymbolsOfProcessRequest) returns (stream Symbol){} - rpc GetAddressOfSymbol(GetAddressOfSymbolRequest) returns (GetAddressOfSymbolResponse){} - + rpc GetSymbols(GetSymbolsRequest) returns (stream Symbol) {} } message StringResponse { string name = 1; } -message GetSymbolsOfProcessRequest { +message GetSymbolsRequest { int32 pid = 1; - string package_name = 2; + string odex_file_path = 2; } message Symbol { @@ -40,16 +38,6 @@ message Symbol { uint64 offset = 2; } -message GetAddressOfSymbolRequest { - string name = 1; - int32 pid = 2; - string package_name = 3; -} - -message GetAddressOfSymbolResponse { - uint64 offset = 1; -} - message PidMessage{ int32 pid = 1; }