[
  {
    "path": ".gitignore",
    "content": "/target\n/.peekieboi\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"mempeek\"\nversion = \"0.1.5\"\nedition = \"2021\"\nlicense = \"BSD-2-Clause\"\ndescription = \"A command line tool that resembles a debugger as well as Cheat Engine, to search for values in memory\"\ndocumentation = \"https://docs.rs/mempeek\"\nrepository = \"https://github.com/gamozolabs/mempeek\"\nkeywords = [\"proc\", \"mem\", \"maps\"]\ncategories = [\"os::linux-apis\", \"command-line-utilities\"]\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nlibprocmem = \"0.1\"\nquoted_strings = \"0.1.0\"\nrustyline = \"9.1\"\n\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 2-Clause License\n\nCopyright (c) 2022, gamozolabs\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "# Summary\n\nThis is a small command-line tool designed to peek around memory of a running\nLinux process. It also provides filtering mechanisms similar to Cheat Engine\nto scan for memory of certain values.\n\nIt uses `rustyline` to maintain a history of command line arguments which is\npersisted in the `.peekieboi` file. Allowing \"up-arrow\" to work across\ndifferent runs of the tool!\n\n# Installing\n\nSimply run `cargo install mempeek` to install this tool! Then invoke it by\nrunning `mempeek <pid of process to introspect>`\n\n# Commands\n\n## Expression support\n\nI've added extremely basic support for expressions of various radix as well as\nadd, subtract, multiply, and divide. No support for parenthesis (yet).\n\nThis allows you to use an expression like `0x13370000+0o100*4` in any argument\nto a command which expects a constant value. _The default radix for numbers\nis 16, thus, hex unless you use an `0b`, `0o`, or `0d` prefix_\n\n## Types\n\nTypes may be one of the following:\n\n- `b` - `u8`\n- `w` - `u16`\n- `d` - `u32`\n- `q` - `u64`\n- `B` - `i8`\n- `W` - `i16`\n- `D` - `i32`\n- `Q` - `i64`\n- `f` - `f32`\n- `F` - `f64`\n\n## Constraints\n\nConstraints may be any one of the following:\n\n- `=[val]`  - Equal to `[val]`\n- `![val]`  - Not equal to `[val]`\n- `>[val]`  - Greater than `[val]`\n- `>=[val]` - Greater than or equal to `[val]`\n- `<[val]`  - Less than `[val]`\n- `<=[val]` - Less than or equal to `[val]`\n\nCurrently this only supports a few commands\n\n## `q` | `exit` | `quit`\n\nExit the program\n\n## `h <query index | l>`\n\nGet the results from a previous memory scan. Takes the query index of the query\nto retrieve. Optionally, you can use `l` in place of the query index to get the\nmost recent query results\n\n## `s[bwdqBWDQfF] <addr> <length> [constraints]`\n\nScan memory for a value of a given type starting at `addr` for `length` bytes\nusing `constraints`\n\n## `u[bwdqBWDQfFo] <query #> [constraints]`\n\nUsing the address list from a previous query, interpret the pointed-to-value as\nthe specified type `o` implies that the update should use the type of the\noriginal query.\n\n## `d[bwdqBWDQfF] <addr> [<number of bytes>]`\n\nDump memory interpreted as a given type for a given number of bytes\n\n## `ss <addr> <length> <string>`\n\nSearch for a `string` in a region of memory specified by `addr` and `length`\n(in bytes)\n\n## `m`\n\nDump memory regions and their permissions.\n\n# Example\n\n![Example of mempeek](/screenshot.png)\n\n_Green values in the dump output indicate that the value is a valid pointer\nwhen cast to a `u64`_\n\n"
  },
  {
    "path": "src/constraint.rs",
    "content": "///! Basic constraints\n\nuse crate::{Error, Result, Value};\n\n/// Different constraints we can apply on a value\n#[derive(Debug, Clone, Copy)]\npub enum Constraint {\n    /// `=`\n    Equal(Value),\n    \n    /// `!`\n    NotEqual(Value),\n\n    /// `>`\n    Greater(Value),\n\n    /// `>=`\n    GreaterEqual(Value),\n\n    /// `<`\n    Less(Value),\n\n    /// `<=`\n    LessEqual(Value),\n}\n\nimpl Constraint {\n    /// Replace the value with a new one\n    pub fn update_val(&mut self, val: Value) {\n        match self {\n            Self::Greater(ref mut rhs) |\n            Self::GreaterEqual(ref mut rhs) |\n            Self::Less(ref mut rhs) |\n            Self::LessEqual(ref mut rhs) |\n            Self::Equal(ref mut rhs) => *rhs = val,\n            Self::NotEqual(ref mut rhs) => *rhs = val,\n        }\n    }\n\n    /// Check a condition\n    pub fn check(&self, lhs: Value) -> bool {\n        match *self {\n            Self::Greater(rhs)      => lhs >  rhs,\n            Self::GreaterEqual(rhs) => lhs >= rhs,\n            Self::Less(rhs)         => lhs <  rhs,\n            Self::LessEqual(rhs)    => lhs <= rhs,\n            Self::Equal(rhs)        => lhs == rhs,\n            Self::NotEqual(rhs)     => lhs != rhs,\n        }\n    }\n\n    /// Create a new constraint from `s` using `val` as the type for the value\n    pub fn from_str_value(s: &str, val: Option<Value>) -> Result<Self> {\n        if s.starts_with(\"=\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[1..])?;\n                Ok(Constraint::Equal(val))\n            } else {\n                Ok(Constraint::Equal(Value::U8(0)))\n            }\n        } else if s.starts_with(\"!\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[1..])?;\n                Ok(Constraint::NotEqual(val))\n            } else {\n                Ok(Constraint::NotEqual(Value::U8(0)))\n            }\n        } else if s.starts_with(\">=\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[2..])?;\n                Ok(Constraint::GreaterEqual(val))\n            } else {\n                Ok(Constraint::GreaterEqual(Value::U8(0)))\n            }\n        } else if s.starts_with(\">\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[1..])?;\n                Ok(Constraint::Greater(val))\n            } else {\n                Ok(Constraint::Greater(Value::U8(0)))\n            }\n        } else if s.starts_with(\"<=\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[2..])?;\n                Ok(Constraint::LessEqual(val))\n            } else {\n                Ok(Constraint::LessEqual(Value::U8(0)))\n            }\n        } else if s.starts_with(\"<\") {\n            if let Some(mut val) = val {\n                val.update_str(&s[1..])?;\n                Ok(Constraint::Less(val))\n            } else {\n                Ok(Constraint::Less(Value::U8(0)))\n            }\n        } else {\n            Err(Error::InvalidConstraint)\n        }\n    }\n}\n\n"
  },
  {
    "path": "src/int.rs",
    "content": "//! Integer parsing helper libraries\n\nuse crate::Error;\n\nmacro_rules! impl_expr {\n    ($name:ident, $name_int:ident, $ty:ty) => {\n        pub fn $name(s: &str) -> crate::Result<$ty> {\n            #[derive(Clone, Copy, Debug)]\n            enum Expr {\n                Add,\n                Sub,\n                Mul,\n                Div,\n                Val($ty),\n            }\n\n            // Split expression into components\n            let mut components = Vec::new();\n\n            let mut cur = String::new();\n            for chr in s.chars() {\n                if matches!(chr, '+' | '-' | '*' | '/') {\n                    // Push the current component\n                    components.push(Expr::Val($name_int(&cur)?));\n                    match chr {\n                        '+' => components.push(Expr::Add),\n                        '-' => components.push(Expr::Sub),\n                        '*' => components.push(Expr::Mul),\n                        '/' => components.push(Expr::Div),\n                        _   => unreachable!(),\n                    }\n                    cur.clear();\n                    continue;\n                }\n\n                cur.push(chr);\n            }\n\n            // Flush remaining data\n            if !cur.is_empty() {\n                components.push(Expr::Val($name_int(&cur)?));\n            }\n\n            let mut ii = 0;\n            while ii < components.len().saturating_sub(2) {\n                let (left, op, right) = (\n                    components[ii + 0],\n                    components[ii + 1],\n                    components[ii + 2],\n                );\n\n                let res = match (left, op, right) {\n                    (Expr::Val(l), Expr::Mul, Expr::Val(r)) =>\n                        l.wrapping_mul(r),\n                    (Expr::Val(l), Expr::Div, Expr::Val(r)) =>\n                        l.wrapping_div(r),\n                    _ => {\n                        ii += 1;\n                        continue;\n                    }\n                };\n\n                // Replace the expression with the result\n                components[ii] = Expr::Val(res);\n\n                // Remove the operation and right side as we've \"consumed\" them\n                components.drain(ii + 1..ii + 3);\n            }\n            \n            let mut ii = 0;\n            while ii < components.len().saturating_sub(2) {\n                let (left, op, right) = (\n                    components[ii + 0],\n                    components[ii + 1],\n                    components[ii + 2],\n                );\n\n                let res = match (left, op, right) {\n                    (Expr::Val(l), Expr::Add, Expr::Val(r)) =>\n                        l.wrapping_add(r),\n                    (Expr::Val(l), Expr::Sub, Expr::Val(r)) =>\n                        l.wrapping_sub(r),\n                    _ => {\n                        ii += 1;\n                        continue;\n                    }\n                };\n\n                // Replace the expression with the result\n                components[ii] = Expr::Val(res);\n\n                // Remove the operation and right side as we've \"consumed\" them\n                components.drain(ii + 1..ii + 3);\n            }\n\n            match components.as_slice() {\n                [Expr::Val(ret)] => Ok(*ret),\n                _ => Err(Error::InvalidExpression),\n            }\n        }\n    }\n}\n\nmacro_rules! int_parse {\n    ($name:ident, $name_int:ident, $ty:ty) => {\n        /// Parse an integer with optional base override prefix\n        pub fn $name_int(mut s: &str) -> crate::Result<$ty> {\n            // Default base\n            let mut base = 16;\n\n            // Invert sign\n            let mut inv = false;\n\n            // Check for a prefix\n            match s.get(0..3) {\n                Some(\"-0x\" | \"-0X\") => { base = 16; s = &s[3..]; inv = true; }\n                Some(\"-0d\" | \"-0D\") => { base = 10; s = &s[3..]; inv = true; }\n                Some(\"-0o\" | \"-0O\") => { base =  8; s = &s[3..]; inv = true; }\n                Some(\"-0b\" | \"-0B\") => { base =  2; s = &s[3..]; inv = true; }\n                _ => {\n                    // Check for a prefix\n                    match s.get(0..2) {\n                        Some(\"0x\" | \"0X\") => { base = 16; s = &s[2..]; }\n                        Some(\"0d\" | \"0D\") => { base = 10; s = &s[2..]; }\n                        Some(\"0o\" | \"0O\") => { base =  8; s = &s[2..]; }\n                        Some(\"0b\" | \"0B\") => { base =  2; s = &s[2..]; }\n                        _ => {}\n                    }\n                }\n            }\n\n            // Parse the integer\n            <$ty>::from_str_radix(s, base).map_err(crate::Error::ParseSigned)\n                .map(|x| if inv { -x } else { x })\n        }\n\n        impl_expr!($name, $name_int, $ty);\n    }\n}\n\nmacro_rules! uint_parse {\n    ($name:ident, $name_int:ident, $ty:ty) => {\n        /// Parse an integer with optional base override prefix\n        pub fn $name_int(mut s: &str) -> crate::Result<$ty> {\n            // Default base\n            let mut base = 16;\n\n            // Check for a prefix\n            match s.get(0..2) {\n                Some(\"0x\" | \"0X\") => { base = 16; s = &s[2..]; }\n                Some(\"0d\" | \"0D\") => { base = 10; s = &s[2..]; }\n                Some(\"0o\" | \"0O\") => { base =  8; s = &s[2..]; }\n                Some(\"0b\" | \"0B\") => { base =  2; s = &s[2..]; }\n                _ => {}\n            }\n\n            // Parse the integer\n            <$ty>::from_str_radix(s, base).map_err(crate::Error::ParseUnsigned)\n        }\n        \n        impl_expr!($name, $name_int, $ty);\n    }\n}\n\nuint_parse!(parse_u8,    parse_u8_int,    u8);\nuint_parse!(parse_u16,   parse_u16_int,   u16);\nuint_parse!(parse_u32,   parse_u32_int,   u32);\nuint_parse!(parse_u64,   parse_u64_int,   u64);\nuint_parse!(parse_usize, parse_usize_int, usize);\nint_parse!(parse_i8,     parse_i8_int,    i8);\nint_parse!(parse_i16,    parse_i16_int,   i16);\nint_parse!(parse_i32,    parse_i32_int,   i32);\nint_parse!(parse_i64,    parse_i64_int,   i64);\nint_parse!(parse_isize,  parse_isize_int, isize);\n\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![feature(array_chunks)]\n\npub mod int;\npub mod constraint;\n\nuse std::str::FromStr;\nuse crate::int::*;\nuse crate::constraint::*;\nuse libprocmem::Memory;\nuse rustyline::Editor;\nuse rustyline::error::ReadlineError;\nuse quoted_strings::QuotedParts;\n\n/// Wrapper for `Result`\ntype Result<T> = std::result::Result<T, Error>;\n\n/// Error type\n#[derive(Debug)]\npub enum Error {\n    /// Error interacting with libprocmem\n    Memory(libprocmem::Error),\n\n    /// Failed to parse a signed value\n    ParseSigned(std::num::ParseIntError),\n\n    /// Failed to parse an unsigned value\n    ParseUnsigned(std::num::ParseIntError),\n\n    /// Failed to read command\n    Readline(ReadlineError),\n\n    /// Integer truncation happened when converting a `u64` to a `usize`\n    TooBig,\n\n    /// Failed to parse a floating point value\n    ParseFloat(std::num::ParseFloatError),\n\n    /// Invalid constraint\n    InvalidConstraint,\n\n    /// Got an invalid PID on the command line\n    InvalidPid(std::num::ParseIntError),\n\n    /// An invalid expression was used\n    ///\n    /// Currently we just support add, sub, mul, and div. No spaces. Numbers\n    /// can be any base (with the correct override)\n    InvalidExpression,\n}\n\n/// Different values\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]\npub enum Value {\n    F32(f32),\n    F64(f64),\n    U8(u8),\n    U16(u16),\n    U32(u32),\n    U64(u64),\n    I8(i8),\n    I16(i16),\n    I32(i32),\n    I64(i64),\n}\n\nimpl Value {\n    /// Create a new (zero) value with the format specified by `chr`\n    pub fn default_from_letter(chr: char) -> Value {\n        // Get the value\n        match chr {\n            'f' => Value::F32(0.),\n            'F' => Value::F64(0.),\n            'b' => Value::U8(0),\n            'w' => Value::U16(0),\n            'd' => Value::U32(0),\n            'q' => Value::U64(0),\n            'B' => Value::I8(0),\n            'W' => Value::I16(0),\n            'D' => Value::I32(0),\n            'Q' => Value::I64(0),\n            _ => unreachable!(),\n        }\n    }\n\n    /// Interpret the value as a `u64` through casting\n    pub fn as_u64(&self) -> u64 {\n        match self {\n            Self::F32(x) => x.to_bits() as u64,\n            Self::F64(x) => x.to_bits(),\n            Self::U8 (x) => *x as u64,\n            Self::U16(x) => *x as u64,\n            Self::U32(x) => *x as u64,\n            Self::U64(x) => *x,\n            Self::I8 (x) => *x as u64,\n            Self::I16(x) => *x as u64,\n            Self::I32(x) => *x as u64,\n            Self::I64(x) => *x as u64,\n        }\n    }\n\n    /// Get number of bytes per `self`\n    pub fn bytes(&self) -> usize {\n        match self {\n            Self::F32(_) => 4,\n            Self::F64(_) => 8,\n            Self::U8 (_) => 1,\n            Self::U16(_) => 2,\n            Self::U32(_) => 4,\n            Self::U64(_) => 8,\n            Self::I8 (_) => 1,\n            Self::I16(_) => 2,\n            Self::I32(_) => 4,\n            Self::I64(_) => 8,\n        }\n    }\n\n    /// Get number of bytes per `self` when `Display`ed\n    pub fn display(&self) -> usize {\n        match self {\n            Self::F32(_) => 25,\n            Self::F64(_) => 25,\n            Self::U8 (_) => 2,\n            Self::U16(_) => 4,\n            Self::U32(_) => 8,\n            Self::U64(_) => 16,\n            Self::I8 (_) => 4,\n            Self::I16(_) => 6,\n            Self::I32(_) => 11,\n            Self::I64(_) => 21,\n        }\n    }\n\n    /// Update value from little-endian bytes\n    pub fn from_le_bytes(&mut self, bytes: &[u8]) {\n        match self {\n            Self::F32(ref mut val) =>\n                *val = f32::from_le_bytes(bytes.try_into().unwrap()),\n            Self::F64(ref mut val) =>\n                *val = f64::from_le_bytes(bytes.try_into().unwrap()),\n            Self::U8 (ref mut val) =>\n                *val = u8::from_le_bytes(bytes.try_into().unwrap()),\n            Self::U16(ref mut val) =>\n                *val = u16::from_le_bytes(bytes.try_into().unwrap()),\n            Self::U32(ref mut val) =>\n                *val = u32::from_le_bytes(bytes.try_into().unwrap()),\n            Self::U64(ref mut val) =>\n                *val = u64::from_le_bytes(bytes.try_into().unwrap()),\n            Self::I8 (ref mut val) =>\n                *val = i8::from_le_bytes(bytes.try_into().unwrap()),\n            Self::I16(ref mut val) =>\n                *val = i16::from_le_bytes(bytes.try_into().unwrap()),\n            Self::I32(ref mut val) =>\n                *val = i32::from_le_bytes(bytes.try_into().unwrap()),\n            Self::I64(ref mut val) =>\n                *val = i64::from_le_bytes(bytes.try_into().unwrap()),\n        }\n    }\n\n    /// Update `self` to a new value of the same type from `s`\n    pub fn update_str(&mut self, s: &str) -> Result<()> {\n        match self {\n            Self::F32(ref mut val) => {\n                *val = f32::from_str(s).map_err(Error::ParseFloat)?;\n            }\n            Self::F64(ref mut val) => {\n                *val = f64::from_str(s).map_err(Error::ParseFloat)?;\n            }\n            Self::U8 (ref mut val) => *val = parse_u8 (s)?,\n            Self::U16(ref mut val) => *val = parse_u16(s)?,\n            Self::U32(ref mut val) => *val = parse_u32(s)?,\n            Self::U64(ref mut val) => *val = parse_u64(s)?,\n            Self::I8 (ref mut val) => *val = parse_i8 (s)?,\n            Self::I16(ref mut val) => *val = parse_i16(s)?,\n            Self::I32(ref mut val) => *val = parse_i32(s)?,\n            Self::I64(ref mut val) => *val = parse_i64(s)?,\n        }\n\n        Ok(())\n    }\n}\n\nimpl std::fmt::Display for Value {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::F32(val) =>\n                f.write_fmt(format_args!(\"{:25.6}\", val)),\n            Self::F64(val) =>\n                f.write_fmt(format_args!(\"{:25.6}\", val)),\n            Self::U8 (val) => f.write_fmt(format_args!(\"{:02x}\", val)),\n            Self::U16(val) => f.write_fmt(format_args!(\"{:04x}\", val)),\n            Self::U32(val) => f.write_fmt(format_args!(\"{:08x}\", val)),\n            Self::U64(val) => f.write_fmt(format_args!(\"{:016x}\", val)),\n            Self::I8 (val) => f.write_fmt(format_args!(\"{:4}\", val)),\n            Self::I16(val) => f.write_fmt(format_args!(\"{:6}\", val)),\n            Self::I32(val) => f.write_fmt(format_args!(\"{:11}\", val)),\n            Self::I64(val) => f.write_fmt(format_args!(\"{:21}\", val)),\n        }\n    }\n}\n\nimpl std::fmt::LowerHex for Value {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::F32(val) =>\n                f.write_fmt(format_args!(\"{:08x}\", val.to_bits())),\n            Self::F64(val) =>\n                f.write_fmt(format_args!(\"{:016x}\", val.to_bits())),\n            Self::U8 (val) => f.write_fmt(format_args!(\"{:02x}\", val)),\n            Self::U16(val) => f.write_fmt(format_args!(\"{:04x}\", val)),\n            Self::U32(val) => f.write_fmt(format_args!(\"{:08x}\", val)),\n            Self::U64(val) => f.write_fmt(format_args!(\"{:016x}\", val)),\n            Self::I8 (val) => f.write_fmt(format_args!(\"{:02x}\", val)),\n            Self::I16(val) => f.write_fmt(format_args!(\"{:04x}\", val)),\n            Self::I32(val) => f.write_fmt(format_args!(\"{:08x}\", val)),\n            Self::I64(val) => f.write_fmt(format_args!(\"{:016x}\", val)),\n        }\n    }\n}\n\n/// A memory scanner!\nstruct Scan {\n    /// Memory\n    memory: Memory,\n\n    /// Searches which yielded results\n    matches: Vec<(Vec<Constraint>, Vec<(u64, Value)>)>,\n}\n\nimpl Scan {\n    /// Handle a command to the scanner\n    fn handle_command(&mut self, command: &[String]) -> Result<()> {\n        // Nothing to do\n        if command.len() == 0 { return Ok(()); }\n\n        match command[0].as_str() {\n            \"exit\" | \"q\" | \"quit\" => {\n                // Exit the program\n                std::process::exit(0);\n            }\n            \"h\" => {\n                // History, displays old query results\n                if command.len() != 2 {\n                    println!(\"h <query #>\");\n                    return Ok(());\n                }\n\n                // Get the query number\n                let query = if command[1] == \"l\" {\n                    // 'l' is a shortcut for \"last\" command\n                    self.matches.len() - 1\n                } else {\n                    parse_usize(&command[1])?\n                };\n                if let Some((constraints, matches)) = self.matches.get(query) {\n                    for &(addr, old) in matches.iter() {\n                        // Read the new value\n                        let tmp =\n                            self.memory.read_slice::<u8>(addr, old.bytes())\n                            .map_err(Error::Memory)?;\n\n                        // Get the new value with the same typing as the old\n                        // value\n                        let mut new = old;\n                        new.from_le_bytes(&tmp);\n\n                        if old != new {\n                            println!(\"{:016x} query: {} -> {}\",\n                                addr, old, new);\n                        } else {\n                            println!(\"{:016x} query: {}\", addr, old);\n                        }\n                    }\n\n                    println!(\n                        \"Query #{} had {} matches with constraints:\\n{:#?}\",\n                        query, matches.len(), constraints);\n                } else {\n                    println!(\"No matching query\");\n                }\n            }\n            \"m\" => {\n                // Print address maps\n                if let Ok(maps) = self.memory.query_address_space() {\n                    for region in maps {\n                        println!(\"{:016x}-{:016x} {}{}{}\",\n                            region.base, region.end,\n                            if region.r { \"r\" } else { \" \" },\n                            if region.w { \"w\" } else { \" \" },\n                            if region.x { \"x\" } else { \" \" });\n                    }\n                } else {\n                    println!(\"Failed to query address map.\");\n                }\n            }\n            \"uo\" |\n            \"ub\" | \"uw\" | \"ud\" | \"uq\" |\n            \"uB\" | \"uW\" | \"uD\" | \"uQ\" | \"uf\" | \"uF\" => {\n                // Re-query only the addresses of a previous query with new\n                // constraints\n                if command.len() < 3 {\n                    println!(\"u[bwdqBWDQfF] <query #> [constraints]\");\n                    return Ok(());\n                }\n\n                // Create value associated with type\n                let value = if command[0] == \"uo\" {\n                    None\n                } else {\n                    Some(Value::default_from_letter(\n                        command[0].as_bytes()[1] as char))\n                };\n\n                // Create list of constraints\n                let mut constraints = Vec::new();\n                for constraint in &command[2..] {\n                    constraints.push(\n                        Constraint::from_str_value(constraint, value)?);\n                }\n\n                // Get query ID\n                let query = if command[1] == \"l\" {\n                    self.matches.len() - 1\n                } else {\n                    parse_usize(&command[1])?\n                };\n                if let Some((_, old_matches)) = self.matches.get(query) {\n                    // Search through old query matches\n                    let mut matches = Vec::new();\n                    for &(addr, old_value) in old_matches {\n                        // Check if we have a concrete value, if not, compare\n                        // against the last observed value\n                        let mut value = if let Some(value) = value {\n                            value\n                        } else {\n                            for constraint in constraints.iter_mut() {\n                                constraint.update_val(old_value);\n                            }\n\n                            old_value\n                        };\n\n                        // Read the new value\n                        let tmp = self.memory.read_slice::<u8>(\n                            addr, value.bytes())\n                            .map_err(Error::Memory)?;\n                        value.from_le_bytes(&tmp);\n\n                        // Check constraints\n                        if constraints.iter().all(|x| x.check(value)) {\n                            matches.push((addr, value));\n                        }\n                    }\n\n                    if !matches.is_empty() {\n                        println!(\"Got {} matches, saving as query #{:x}\",\n                            matches.len(), self.matches.len());\n                        if matches.len() < 100 {\n                            for (addr, value) in &matches {\n                                println!(\"{:016x} {}\", addr, value);\n                            }\n                        }\n                        self.matches.push((constraints, matches));\n                    } else {\n                        println!(\"No matches.\");\n                    }\n                } else {\n                    println!(\"No matching query\");\n                }\n            }\n            \"ss\" => {\n                // Search for a string\n                if command.len() != 4 {\n                    println!(\"ss <addr> <length> <string>\");\n                    return Ok(());\n                }\n\n                // Get the address and size\n                let addr = parse_u64(&command[1])?;\n                let size = parse_usize(&command[2])?;\n\n                // Read the memory\n                let tmp = self.memory.read_slice::<u8>(addr, size)\n                    .map_err(Error::Memory)?;\n\n                // String search\n                tmp.windows(command[3].len()).enumerate()\n                        .for_each(|(ii, window)| {\n                    if window == command[3].as_bytes() {\n                        println!(\"{:016x} {}\", addr + ii as u64, command[3]);\n                    }\n                });\n            }\n            \"sb\" | \"sw\" | \"sd\" | \"sq\" |\n            \"sB\" | \"sW\" | \"sD\" | \"sQ\" | \"sf\" | \"sF\" => {\n                // Search for a value\n                if command.len() <= 2 {\n                    println!(\"s[bwdqBWDQfF] <addr> <length> [constraints]\");\n                    return Ok(());\n                }\n\n                // Create value associated with type\n                let value =\n                    Value::default_from_letter(\n                        command[0].as_bytes()[1] as char);\n                \n                // Create list of constraints\n                let mut constraints = Vec::new();\n                for constraint in &command[3..] {\n                    constraints.push(\n                        Constraint::from_str_value(constraint, Some(value))?);\n                }\n\n                // Get the address and size\n                let addr = parse_u64(&command[1])?;\n                let size = parse_usize(&command[2])?;\n                let end  = addr + size as u64;\n\n                let mut matches = Vec::new();\n                for mapping in self.memory.query_address_space()\n                        .map_err(Error::Memory)? {\n                    // Skip ranges not in bounds\n                    if mapping.end <= addr || mapping.base >= end {\n                        continue;\n                    }\n\n                    let start = addr.max(mapping.base);\n                    let end   = end.min(mapping.end);\n                    self.search_memory(value, start, (end - start) as usize,\n                        &constraints, &mut matches)?;\n                }\n\n                // If we got matches, save them off\n                if !matches.is_empty() {\n                    println!(\"Got {} matches, saving as query #{:x}\",\n                        matches.len(), self.matches.len());\n                    if matches.len() < 100 {\n                        for (addr, value) in &matches {\n                            println!(\"{:016x} {}\", addr, value);\n                        }\n                    }\n                    self.matches.push((constraints, matches));\n                } else {\n                    println!(\"No matches.\");\n                }\n            }\n            \"db\" | \"dw\" | \"dd\" | \"dq\" |\n            \"dB\" | \"dW\" | \"dD\" | \"dQ\" | \"df\" | \"dF\" =>{\n                // Display memory\n                if !matches!(command.len(), 2 | 3) {\n                    println!(\"d[bwdqBWDQfF] <addr> [<number of bytes>]\");\n                    return Ok(());\n                }\n\n                // Get the letter used with this command and use it to create a\n                // dummy expected value\n                let mut value =\n                    Value::default_from_letter(\n                        command[0].as_bytes()[1] as char);\n\n                // Parse integer address\n                let addr = parse_u64(&command[1])?;\n\n                // Determine number of bytes to read\n                let bytes = if let Some(x) = command.get(2) {\n                    parse_usize(x)?\n                } else {\n                    64\n                };\n\n                // Read memory\n                let tmp = self.memory.read_slice::<u8>(addr, bytes)\n                    .map_err(Error::Memory)?;\n\n                // Print the new line header\n                print!(\"\\x1b[0;34m{:016x}\\x1b[0m: \", addr);\n\n                // Display all the values\n                let mut output_used = 0;\n                let mut iter = tmp.chunks_exact(\n                    value.bytes()).map(|x| Some(x)).chain(\n                    std::iter::repeat(None)).enumerate().peekable();\n                while let Some((ii, val)) = iter.next() {\n                    if let Some(val) = val {\n                        // Print the value\n                        value.from_le_bytes(val);\n\n                        // Convert the value to a `u64` and try to use it as\n                        // an address to see if it is a pointer\n                        let maybe_addr = value.as_u64();\n                        let valid_ptr =\n                            self.memory.read::<u8>(maybe_addr).is_ok();\n\n                        if valid_ptr {\n                            print!(\"\\x1b[0;32m{}\\x1b[0m \", value);\n                        } else {\n                            print!(\"{} \", value);\n                        }\n                    } else {\n                        // Clean\n                        for _ in 0..value.display() {\n                            print!(\"?\");\n                        }\n                        print!(\" \");\n                    }\n\n                    // Update output used for this line\n                    output_used += 1;\n                    let vals_per_line = 16 / value.bytes();\n                    if output_used == vals_per_line {\n                        // Before we make the newline, print the ASCII\n                        let ascii = ii / vals_per_line * vals_per_line *\n                            value.bytes();\n                        for byte in tmp[ascii..].iter().take(16) {\n                            if byte.is_ascii_graphic() {\n                                print!(\"{}\", *byte as char);\n                            } else {\n                                print!(\".\");\n                            }\n                        }\n                        println!();\n\n                        // If we have nothing more to print after this, we're\n                        // done\n                        if matches!(iter.peek(), Some((_, None))) {\n                            return Ok(());\n                        }\n\n                        // Print the new line header\n                        print!(\"\\x1b[34m{:016x}\\x1b[0m: \",\n                            addr + (ii as u64 + 1) * value.bytes() as u64);\n\n                        // Update state\n                        output_used = 0;\n                    }\n                }\n            }\n            _ => {\n                println!(\"Unknown command: {:?}\", command);\n            }\n        }\n\n        Ok(())\n    }\n\n    fn search_memory(&mut self, mut value: Value, addr: u64, size: usize,\n            constraints: &[Constraint],\n            matches: &mut Vec<(u64, Value)>) -> Result<()> {\n        // Read the memory\n        let tmp = self.memory.read_slice::<u8>(addr, size)\n            .map_err(Error::Memory)?;\n        // Go through memory\n        for (ii, chunk) in tmp.chunks_exact(value.bytes()).enumerate(){\n            // Update value\n            value.from_le_bytes(chunk);\n\n            // Check constraints\n            if constraints.iter().all(|x| x.check(value)) {\n                let addr = addr + ii as u64 * value.bytes() as u64;\n                matches.push((addr, value));\n            }\n        }\n\n        Ok(())\n    }\n}\n\nfn main() -> Result<()> {\n    // Get the arguments\n    let args = std::env::args().collect::<Vec<_>>();\n    if args.len() != 2 {\n        println!(\"Usage: peek <pid>\");\n        return Ok(());\n    }\n\n    // Get the PID\n    let pid = usize::from_str_radix(&args[1], 10)\n        .map_err(Error::InvalidPid)?;\n\n    // Create a readline handler\n    let mut rl = Editor::<()>::new();\n    let _ = rl.load_history(\".peekieboi\");\n\n    // Create a memory scanner\n    let mut scan = Scan {\n        memory:  Memory::pid(pid).map_err(Error::Memory)?,\n        matches: Vec::new(),\n    };\n\n    // Wait for commands\n    loop {\n        // Get command\n        let command = match rl.readline(\">> \") {\n            Ok(x) => x,\n            Err(ReadlineError::Interrupted) => {\n                // Ctrl+c\n                break;\n            }\n            Err(x) => return Err(Error::Readline(x)),\n        };\n        rl.add_history_entry(command.as_str());\n        let _ = rl.save_history(\".peekieboi\");\n\n        // Split command\n        let command = QuotedParts::from(command.trim()).collect::<Vec<_>>();\n        if let Err(err) = scan.handle_command(&command) {\n            println!(\"Failed to execute command: {:?}\", err);\n        }\n    }\n\n    Ok(())\n}\n\n"
  }
]