lurk_cli/arch/
mod.rs

1use crate::syscall_info::{SyscallArg, SyscallArgs};
2use byteorder::{LittleEndian, WriteBytesExt};
3use libc::{c_long, c_ulonglong, user_regs_struct};
4use nix::sys::ptrace;
5use nix::sys::ptrace::Options;
6use nix::unistd::Pid;
7use std::ffi::c_void;
8use syscalls::Sysno;
9
10#[cfg(any(target_arch = "aarch64", feature = "aarch64"))]
11pub mod aarch64;
12// #[cfg(any(target_arch = "arm", feature = "arm"))]
13// pub mod arm;
14// #[cfg(any(target_arch = "mips", feature = "mips"))]
15// pub mod mips;
16// #[cfg(any(target_arch = "mips64", feature = "mips64"))]
17// pub mod mips64;
18// #[cfg(any(target_arch = "powerpc", feature = "powerpc"))]
19// pub mod powerpc;
20// #[cfg(any(target_arch = "powerpc64", feature = "powerpc64"))]
21// pub mod powerpc64;
22#[cfg(any(target_arch = "riscv64", feature = "riscv64"))]
23pub mod riscv64;
24// #[cfg(any(target_arch = "s390x", feature = "s390x"))]
25// pub mod s390x;
26// #[cfg(any(target_arch = "sparc", feature = "sparc"))]
27// pub mod sparc;
28// #[cfg(any(target_arch = "sparc64", feature = "sparc64"))]
29// pub mod sparc64;
30// #[cfg(any(target_arch = "x86", feature = "x86"))]
31// pub mod x86;
32#[cfg(any(target_arch = "x86_64", feature = "x86_64"))]
33pub mod x86_64;
34
35#[cfg(target_arch = "aarch64")]
36pub use aarch64::*;
37// #[cfg(target_arch = "arm")]
38// pub use arm::*;
39// #[cfg(target_arch = "mips")]
40// pub use mips::*;
41// #[cfg(target_arch = "mips64")]
42// pub use mips64::*;
43// #[cfg(target_arch = "powerpc")]
44// pub use powerpc::*;
45// #[cfg(target_arch = "powerpc64")]
46// pub use powerpc64::*;
47#[cfg(target_arch = "riscv64")]
48pub use riscv64::*;
49// #[cfg(target_arch = "s390x")]
50// pub use s390x::*;
51// #[cfg(target_arch = "sparc")]
52// pub use sparc::*;
53// #[cfg(target_arch = "sparc64")]
54// pub use sparc64::*;
55// #[cfg(target_arch = "x86")]
56// pub use x86::*;
57#[cfg(target_arch = "x86_64")]
58pub use x86_64::*;
59
60#[derive(Debug, Copy, Clone)]
61pub enum SyscallArgType {
62    // Integer can be used to represent int, fd and size_t
63    Int,
64    // String can be used to represent *buf
65    Str,
66    // Address can be used to represent *statbuf
67    Addr,
68}
69
70pub fn read_string(pid: Pid, address: c_ulonglong) -> String {
71    let mut string = String::new();
72    // Move 8 bytes up each time for next read.
73    let mut count = 0;
74    let word_size = 8;
75
76    'done: loop {
77        let address = unsafe { (address as *mut c_void).offset(count) };
78
79        let res: c_long = match ptrace::read(pid, address) {
80            Ok(c_long) => c_long,
81            Err(_) => break 'done,
82        };
83
84        let mut bytes: Vec<u8> = vec![];
85        bytes.write_i64::<LittleEndian>(res).unwrap_or_else(|err| {
86            panic!("Failed to write {res} as i64 LittleEndian: {err}");
87        });
88        for b in bytes {
89            if b == 0 {
90                break 'done;
91            }
92            string.push(b as char);
93        }
94
95        count += word_size;
96    }
97
98    string
99}
100
101pub fn ptrace_init_options(pid: Pid) -> nix::Result<()> {
102    ptrace::setoptions(
103        pid,
104        Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT | Options::PTRACE_O_TRACEEXEC,
105    )
106}
107
108pub fn ptrace_init_options_fork(pid: Pid) -> nix::Result<()> {
109    ptrace::setoptions(
110        pid,
111        Options::PTRACE_O_TRACESYSGOOD
112            | Options::PTRACE_O_TRACEEXIT
113            | Options::PTRACE_O_TRACEEXEC
114            | Options::PTRACE_O_TRACEFORK
115            | Options::PTRACE_O_TRACEVFORK
116            | Options::PTRACE_O_TRACECLONE,
117    )
118}
119
120#[allow(clippy::cast_sign_loss)]
121#[must_use]
122// SAFTEY: In get_register_data we make sure that the syscall number will never be negative.
123pub fn parse_args(pid: Pid, syscall: Sysno, registers: user_regs_struct) -> SyscallArgs {
124    SYSCALLS
125        .get(syscall.id() as usize)
126        .and_then(|option| option.as_ref())
127        .map_or_else(
128            || SyscallArgs(vec![]),
129            |(_, args)| {
130                SyscallArgs(
131                    args.iter()
132                        .filter_map(Option::as_ref)
133                        .enumerate()
134                        .map(|(idx, arg_type)| map_arg(pid, registers, idx, *arg_type))
135                        .collect(),
136                )
137            },
138        )
139}
140
141fn map_arg(pid: Pid, registers: user_regs_struct, idx: usize, arg: SyscallArgType) -> SyscallArg {
142    let value = get_arg_value(registers, idx);
143    match arg {
144        SyscallArgType::Int => SyscallArg::Int(value as i64),
145        SyscallArgType::Str => SyscallArg::Str(read_string(pid, value)),
146        SyscallArgType::Addr => SyscallArg::Addr(value as usize),
147    }
148}
149
150pub fn escape_to_string(buf: &Vec<u8>) -> String {
151    let mut string = String::new();
152    for c in buf {
153        let code = *c;
154        if (0x20..=0x7f).contains(&code) {
155            if code == b'\\' {
156                string.push_str("\\\\");
157            } else {
158                string.push(char::from(code));
159            }
160        } else {
161            string.push_str(format!("\\{c:x}").as_str());
162        }
163    }
164    string
165}