packrat-tui 0.3.1

A Wireshark-style terminal packet analyzer, reverse engineering, and security research tool with live capture, IDS, port scanner, packet crafter, and PCAP replay
use rand::Rng;

#[derive(Debug, Clone)]
pub enum EntryKind { Syscall, Signal, Network }

#[derive(Debug, Clone)]
pub struct DynEntry {
    pub ts: f64,
    pub kind: EntryKind,
    pub name: String,
    pub args: String,
    pub retval: String,
}

const SYSCALLS: &[&str] = &[
    "read","write","open","close","stat","fstat","poll","mmap","mprotect","munmap",
    "brk","ioctl","socket","connect","accept","sendto","recvfrom","sendmsg","recvmsg",
    "bind","listen","getsockname","setsockopt","fork","execve","exit","kill",
    "fcntl","flock","fsync","getcwd","chdir","rename","mkdir","unlink","lseek",
    "select","dup2","nanosleep","getpid","getuid","clone","wait4","access",
];
const SIGNALS: &[&str] = &[
    "SIGSEGV","SIGABRT","SIGFPE","SIGBUS","SIGTERM","SIGKILL","SIGUSR1","SIGCHLD","SIGPIPE",
];
const LOCAL_IPS: &[&str] = &["192.168.1.1","192.168.1.42","10.0.0.1"];
const REMOTE_IPS: &[&str] = &["8.8.8.8","1.1.1.1","142.250.80.46"];

static START: std::sync::OnceLock<std::time::Instant> = std::sync::OnceLock::new();

pub fn generate_entry(_tick: u32) -> DynEntry {
    let start = START.get_or_init(std::time::Instant::now);
    let ts = start.elapsed().as_secs_f64();
    let mut rng = rand::thread_rng();

    let r: u8 = rng.gen_range(0..10);
    if r == 0 {
        let sig = SIGNALS[rng.gen_range(0..SIGNALS.len())];
        DynEntry {
            ts,
            kind: EntryKind::Signal,
            name: sig.to_string(),
            args: format!("→ received by PID {}", rng.gen_range(1000..9999)),
            retval: String::new(),
        }
    } else if r == 1 {
        let src = LOCAL_IPS[rng.gen_range(0..LOCAL_IPS.len())];
        let dst = REMOTE_IPS[rng.gen_range(0..REMOTE_IPS.len())];
        let protos = ["TCP","UDP","DNS","TLS"];
        let proto = protos[rng.gen_range(0..protos.len())];
        DynEntry {
            ts,
            kind: EntryKind::Network,
            name: "NETPKT".into(),
            args: format!("{} {}{} len={}", proto, src, dst, rng.gen_range(64..=1460)),
            retval: String::new(),
        }
    } else {
        let sc = SYSCALLS[rng.gen_range(0..SYSCALLS.len())];
        let args = gen_args(sc, &mut rng);
        let ret_neg = rng.gen_bool(0.08);
        let retval = if ret_neg {
            format!("-{}", rng.gen_range(1..=22))
        } else {
            rng.gen_range(0..=1000u32).to_string()
        };
        DynEntry {
            ts,
            kind: EntryKind::Syscall,
            name: sc.to_string(),
            args,
            retval,
        }
    }
}

fn gen_args(sc: &str, rng: &mut impl Rng) -> String {
    match sc {
        "read"    => format!("{}, 0x{:06x}, {}", rng.gen_range(0..20u8), rng.r#gen::<u32>() & 0xffffff, rng.gen_range(1..4096u16)),
        "write"   => format!("{}, 0x{:06x}, {}", rng.gen_range(0..20u8), rng.r#gen::<u32>() & 0xffffff, rng.gen_range(1..1024u16)),
        "open"    => {
            let files = ["/etc/resolv.conf","/var/log/syslog","/proc/net/tcp","/dev/urandom","/etc/passwd"];
            format!("\"{}\", O_RDONLY, 0644", files[rng.gen_range(0..files.len())])
        }
        "socket"  => {
            let fam = ["AF_INET","AF_INET6","AF_UNIX"][rng.gen_range(0..3)];
            let typ = ["SOCK_STREAM","SOCK_DGRAM"][rng.gen_range(0..2)];
            format!("{}, {}, 0", fam, typ)
        }
        "connect" => {
            let ip = REMOTE_IPS[rng.gen_range(0..REMOTE_IPS.len())];
            format!("{}, {{AF_INET, htons({}), \"{}\"}}, 16", rng.gen_range(3..20u8), rng.gen_range(1..65535u16), ip)
        }
        "sendto"  => format!("{}, 0x{:06x}, {}, 0", rng.gen_range(3..20u8), rng.r#gen::<u32>() & 0xffffff, rng.gen_range(1..4096u16)),
        "recvfrom"=> format!("{}, 0x{:06x}, {}, 0", rng.gen_range(3..20u8), rng.r#gen::<u32>() & 0xffffff, rng.gen_range(1..4096u16)),
        "mmap"    => format!("NULL, {}, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0", rng.gen_range(4096..65536u32)),
        "execve"  => {
            let bins = ["\"/bin/bash\"","\"/usr/bin/python3\"","\"/bin/sh\"","\"/usr/bin/curl\""];
            format!("{}, [...], [...]", bins[rng.gen_range(0..bins.len())])
        }
        "kill"    => format!("{}, SIG{}", rng.gen_range(1..9999u16), ["TERM","KILL","HUP","USR1"][rng.gen_range(0..4)]),
        _         => format!("{}, 0x{:06x}", rng.gen_range(0..10u8), rng.r#gen::<u32>() & 0xffffff),
    }
}