[
  {
    "path": ".gitignore",
    "content": ".idea/\ntarget\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at cao1988228@163.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"asm-cli-rust\"\nversion = \"0.1.0\"\nauthors = [\"Xargin <cao1988228@163.com>\"]\nedition = \"2018\"\n\n[dependencies]\ncapstone = \"^0.9.0\"\nkeystone = {git = \"https://github.com/keystone-engine/keystone/\", rev = \"18569351000cf1b8bd1ea2cc8a02c2e17b76391f\"}\nunicorn-engine = {git = \"https://github.com/unicorn-engine/unicorn/\", rev = \"8fb4b45f57557eb82a165ec24ed8a2aa2f9f201b\"}\nhex = \"0.3.2\"\nrustyline = \"9.1.0\"\nansi_term = \"0.11.0\"\nmaplit = \"1\"\nlazy_static = \"1.4.0\"\nclap = { version = \"4.0.26\", features = [\"derive\"] }\nproc-macro2 = { version = \"=1.0.93\", features=[\"default\", \"proc-macro\"] }\n"
  },
  {
    "path": "LICENSE",
    "content": "The Star And Thank Author License (SATA)\n\nCopyright © 2018 Xargin(cao1988228@163.com)\n\nProject Url: https://github.com/cch123/asm-cli-rust\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software. \n\nAnd wait, the most important, you shall star/+1/like the project(s) in project url \nsection above first, and then thank the author(s) in Copyright section. \n\nHere are some suggested ways:\n\n - Email the authors a thank-you letter, and make friends with him/her/them.\n - Report bugs or issues.\n - Tell friends what a wonderful project this is.\n - And, sure, you can just express thanks in your mind without telling the world.\n\nContributors of this project by forking have the option to add his/her name and \nforked project url at copyright and project url sections, but shall not delete \nor modify anything else in these two sections.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "readme.md",
    "content": "# Overview\n\nthis project is inspired by https://github.com/poppycompass/asmshell\n\n## Preview\n\n![x64](images/x64.png)\n\n## Usage\n```\nshell> asm-cli-rust [x86/x64]\n\ndefault : x64\noptional: x86\n```\n\nkey_up/key_down: history\n\n## Build from source\n\n1. `cargo b`\n\n## Go version\n\nhttps://github.com/cch123/asm-cli\n"
  },
  {
    "path": "src/engine/cpu.rs",
    "content": "use std::{collections::HashMap, convert::TryFrom};\n\nuse maplit::hashmap;\nuse unicorn_engine::{unicorn_const, RegisterX86, Unicorn};\n\n#[derive(Clone, Copy, Debug)]\npub enum Mode {\n    Mode32,\n    Mode64,\n}\n\n#[allow(clippy::from_over_into)]\nimpl Into<unicorn_const::Mode> for Mode {\n    fn into(self) -> unicorn_const::Mode {\n        match self {\n            Self::Mode32 => unicorn_const::Mode::MODE_32,\n            Self::Mode64 => unicorn_const::Mode::MODE_64,\n        }\n    }\n}\n\n#[allow(clippy::from_over_into)]\nimpl Into<keystone::Mode> for Mode {\n    fn into(self) -> keystone::Mode {\n        match self {\n            Self::Mode32 => keystone::Mode::MODE_32,\n            Self::Mode64 => keystone::Mode::MODE_64,\n        }\n    }\n}\n\nimpl TryFrom<unicorn_const::Mode> for Mode {\n    type Error = &'static str;\n    fn try_from(value: unicorn_const::Mode) -> Result<Self, Self::Error> {\n        match value {\n            unicorn_const::Mode::MODE_32 => Ok(Self::Mode32),\n            unicorn_const::Mode::MODE_64 => Ok(Self::Mode64),\n            _ => Err(\"unsupported mode\"),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub enum Arch {\n    X86,\n}\n\n#[allow(clippy::from_over_into)]\nimpl Into<unicorn_const::Arch> for Arch {\n    fn into(self) -> unicorn_const::Arch {\n        match self {\n            Self::X86 => unicorn_const::Arch::X86,\n        }\n    }\n}\n\n#[allow(clippy::from_over_into)]\nimpl Into<keystone::Arch> for Arch {\n    fn into(self) -> keystone::Arch {\n        match self {\n            Self::X86 => keystone::Arch::X86,\n        }\n    }\n}\n\nimpl TryFrom<unicorn_const::Arch> for Arch {\n    type Error = &'static str;\n    fn try_from(value: unicorn_const::Arch) -> Result<Self, Self::Error> {\n        match value {\n            unicorn_const::Arch::X86 => Ok(Self::X86),\n            _ => Err(\"unsupported arch\"),\n        }\n    }\n}\n\nimpl Arch {\n    fn dump_registers(\n        &self,\n        emu: &Unicorn<'static, ()>,\n        registers: HashMap<&'static str, i32>,\n    ) -> HashMap<&'static str, u64> {\n        registers\n            .keys()\n            .filter(|&&x| x != \"end\") // \"end\" is pseudo-register (to add new row)\n            .map(|&reg_name| {\n                (\n                    reg_name,\n                    emu.reg_read(*registers.get(reg_name).unwrap()).unwrap(),\n                )\n            })\n            .collect::<HashMap<_, _>>()\n    }\n}\n\npub trait ArchMeta {\n    fn cpu(&self) -> (Arch, Mode);\n    fn sp_reg(&self) -> i32;\n    fn fp_reg(&self) -> i32;\n    fn int_size(&self) -> usize;\n    fn sorted_reg_names(&self) -> Vec<&'static str>;\n    fn register_map(&self) -> HashMap<&'static str, i32>;\n    fn dump_registers(&self, emu: &Unicorn<'static, ()>) -> HashMap<&'static str, u64>;\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct X32 {\n    inner: Arch,\n}\nimpl X32 {\n    pub fn new(arch: Arch) -> Self {\n        Self { inner: arch }\n    }\n}\nimpl ArchMeta for X32 {\n    fn cpu(&self) -> (Arch, Mode) {\n        (self.inner, Mode::Mode32)\n    }\n\n    fn sp_reg(&self) -> i32 {\n        i32::from(RegisterX86::ESP)\n    }\n    fn fp_reg(&self) -> i32 {\n        i32::from(RegisterX86::EBP)\n    }\n\n    fn int_size(&self) -> usize {\n        32 / 8\n    }\n\n    fn sorted_reg_names(&self) -> Vec<&'static str> {\n        vec![\n            \"eax\", \"ebx\", \"ecx\", \"edx\", \"end\", //\n            \"esi\", \"edi\", \"end\", //\n            \"eip\", \"ebp\", \"esp\", \"end\", //\n            \"flags\", \"end\", //\n            \"cs\", \"ss\", \"ds\", \"es\", \"end\", //\n            \"fs\", \"gs\", \"end\", //\n        ]\n    }\n\n    fn register_map(&self) -> HashMap<&'static str, i32> {\n        // register to trace, display, etc.\n        hashmap! {\n            \"eax\"   => i32::from(RegisterX86::EAX),\n            \"ebx\"   => i32::from(RegisterX86::EBX),\n            \"ecx\"   => i32::from(RegisterX86::ECX),\n            \"edx\"   => i32::from(RegisterX86::EDX),\n            \"esi\"   => i32::from(RegisterX86::ESI),\n            \"edi\"   => i32::from(RegisterX86::EDI),\n            \"eip\"   => i32::from(RegisterX86::EIP),\n            \"ebp\"   => i32::from(RegisterX86::EBP),\n            \"esp\"   => i32::from(RegisterX86::ESP),\n            \"flags\" => i32::from(RegisterX86::EFLAGS),\n            \"cs\"    => i32::from(RegisterX86::CS),\n            \"ss\"    => i32::from(RegisterX86::SS),\n            \"ds\"    => i32::from(RegisterX86::DS),\n            \"es\"    => i32::from(RegisterX86::ES),\n            \"fs\"    => i32::from(RegisterX86::FS),\n            \"gs\"    => i32::from(RegisterX86::GS),\n        }\n    }\n\n    fn dump_registers(&self, emu: &Unicorn<'static, ()>) -> HashMap<&'static str, u64> {\n        self.inner.dump_registers(emu, self.register_map())\n    }\n}\n\n#[derive(Clone, Copy, Debug)]\npub struct X64 {\n    inner: Arch,\n}\nimpl X64 {\n    pub fn new(arch: Arch) -> X64 {\n        X64 { inner: arch }\n    }\n}\nimpl ArchMeta for X64 {\n    fn cpu(&self) -> (Arch, Mode) {\n        (self.inner, Mode::Mode64)\n    }\n\n    fn sp_reg(&self) -> i32 {\n        i32::from(RegisterX86::RSP)\n    }\n    fn fp_reg(&self) -> i32 {\n        i32::from(RegisterX86::RBP)\n    }\n\n    fn int_size(&self) -> usize {\n        64 / 8\n    }\n\n    fn sorted_reg_names(&self) -> Vec<&'static str> {\n        vec![\n            \"rax\", \"rbx\", \"rcx\", \"rdx\", \"end\", //\n            \"rsi\", \"rdi\", \"r8\", \"r9\", \"end\", //\n            \"r10\", \"r11\", \"r12\", \"r13\", \"end\", //\n            \"r14\", \"r15\", \"end\", //\n            \"rip\", \"rbp\", \"rsp\", \"end\", //\n            \"cs\", \"ss\", \"ds\", \"es\", \"end\", //\n            \"fs\", \"gs\", \"end\", \"flags\", \"end\", //\n        ]\n    }\n\n    fn register_map(&self) -> HashMap<&'static str, i32> {\n        // register to trace, display, etc.\n        hashmap! {\n            \"rax\"   => i32::from(RegisterX86::RAX),\n            \"rbx\"   => i32::from(RegisterX86::RBX),\n            \"rcx\"   => i32::from(RegisterX86::RCX),\n            \"rdx\"   => i32::from(RegisterX86::RDX),\n            \"rsi\"   => i32::from(RegisterX86::RSI),\n            \"rdi\"   => i32::from(RegisterX86::RDI),\n            \"r8\"    => i32::from(RegisterX86::R8),\n            \"r9\"    => i32::from(RegisterX86::R9),\n            \"r10\"   => i32::from(RegisterX86::R10),\n            \"r11\"   => i32::from(RegisterX86::R11),\n            \"r12\"   => i32::from(RegisterX86::R12),\n            \"r13\"   => i32::from(RegisterX86::R13),\n            \"r14\"   => i32::from(RegisterX86::R14),\n            \"r15\"   => i32::from(RegisterX86::R15),\n            \"rip\"   => i32::from(RegisterX86::RIP),\n            \"rbp\"   => i32::from(RegisterX86::RBP),\n            \"rsp\"   => i32::from(RegisterX86::RSP),\n            \"flags\" => i32::from(RegisterX86::EFLAGS),\n            \"cs\"    => i32::from(RegisterX86::CS),\n            \"ss\"    => i32::from(RegisterX86::SS),\n            \"ds\"    => i32::from(RegisterX86::DS),\n            \"es\"    => i32::from(RegisterX86::ES),\n            \"fs\"    => i32::from(RegisterX86::FS),\n            \"gs\"    => i32::from(RegisterX86::GS),\n        }\n    }\n\n    fn dump_registers(&self, emu: &Unicorn<'static, ()>) -> HashMap<&'static str, u64> {\n        self.inner.dump_registers(emu, self.register_map())\n    }\n}\n"
  },
  {
    "path": "src/engine/machine.rs",
    "content": "use ansi_term::Colour::{Blue, Purple, Yellow};\nuse keystone::{AsmResult, Error};\nuse std::collections::HashMap;\nuse unicorn_engine::unicorn_const::uc_error;\nuse unicorn_engine::unicorn_const::SECOND_SCALE;\nuse unicorn_engine::Unicorn;\n\nuse super::cpu;\n\n#[derive(Debug)]\npub enum MachineError {\n    Unsupported,\n    Keystone(keystone::Error),\n    Unicorn(uc_error),\n}\n\npub struct Machine<'a> {\n    pub register_map: HashMap<&'a str, i32>,\n    pub assembler: keystone::Keystone,\n    pub emu: Unicorn<'static, ()>,\n    pub sorted_reg_names: Vec<&'a str>,\n    pub byte_size: usize,\n    pub previous_reg_value: HashMap<&'a str, u64>,\n    pub cpu: (cpu::Arch, cpu::Mode),\n    pub sp: i32, // stack pointer\n    pub fp: i32, // stack frame\n}\n\nimpl<'a> Machine<'a> {\n    pub fn new(arch: cpu::Arch, mode: cpu::Mode) -> Result<Self, MachineError> {\n        let emu = Self::init_unicorn(arch, mode)?;\n        let assembler = Self::init_keystone(arch, mode)?;\n        let arch_meta = Self::get_arch_meta(arch, mode)?;\n\n        let register_map = arch_meta.register_map();\n        let prev_reg_value = arch_meta.dump_registers(&emu);\n\n        Ok(Self {\n            emu,\n            assembler,\n\n            register_map,\n            sorted_reg_names: arch_meta.sorted_reg_names(),\n            byte_size: arch_meta.int_size(),\n            previous_reg_value: prev_reg_value,\n            cpu: arch_meta.cpu(),\n\n            sp: arch_meta.sp_reg(),\n            fp: arch_meta.fp_reg(),\n        })\n    }\n\n    pub fn new_from_arch(arch_name: &str) -> Result<Self, MachineError> {\n        let arch_meta = Self::get_arch_meta_from_name(arch_name)?;\n        let cpu = arch_meta.cpu();\n        Self::new(cpu.0, cpu.1)\n    }\n\n    pub(crate) fn init_unicorn(\n        arch: cpu::Arch,\n        mode: cpu::Mode,\n    ) -> Result<Unicorn<'static, ()>, MachineError> {\n        Unicorn::new(arch.into(), mode.into()).map_err(MachineError::Unicorn)\n    }\n\n    pub(crate) fn init_keystone(\n        arch: cpu::Arch,\n        mode: cpu::Mode,\n    ) -> Result<keystone::Keystone, MachineError> {\n        keystone::Keystone::new(arch.into(), mode.into()).map_err(MachineError::Keystone)\n    }\n\n    pub(crate) fn get_arch_name(\n        arch: cpu::Arch,\n        mode: cpu::Mode,\n    ) -> Result<&'static str, MachineError> {\n        match arch {\n            cpu::Arch::X86 => match mode {\n                cpu::Mode::Mode32 => Ok(\"x32\"),\n                cpu::Mode::Mode64 => Ok(\"x64\"),\n                // _ => Err(MachineError::Unsupported),\n            },\n            // _ => Err(MachineError::Unsupported),\n        }\n    }\n\n    pub(crate) fn get_arch_meta(\n        arch: cpu::Arch,\n        mode: cpu::Mode,\n    ) -> Result<Box<dyn cpu::ArchMeta>, MachineError> {\n        let arch_name = Self::get_arch_name(arch, mode)?;\n        Self::get_arch_meta_from_name(arch_name)\n    }\n\n    pub(crate) fn get_arch_meta_from_name(\n        arch_name: &str,\n    ) -> Result<Box<dyn cpu::ArchMeta>, MachineError> {\n        match arch_name {\n            \"x32\" => Ok(Box::new(cpu::X32::new(cpu::Arch::X86))),\n            \"x64\" => Ok(Box::new(cpu::X64::new(cpu::Arch::X86))),\n            _ => Err(MachineError::Unsupported),\n        }\n    }\n\n    pub fn set_sp(&mut self, value: u64) -> Result<(), MachineError> {\n        self.emu\n            .reg_write(self.sp, value)\n            .map_err(MachineError::Unicorn)\n    }\n    pub fn set_fp(&mut self, value: u64) -> Result<(), MachineError> {\n        self.emu\n            .reg_write(self.fp, value)\n            .map_err(MachineError::Unicorn)\n    }\n}\n\nimpl<'a> Machine<'a> {\n    pub fn print_machine(&self) {\n        println!(\"arch: {:?} mode: {:?}\", self.cpu.0, self.cpu.1);\n    }\n    pub fn print_register(&mut self) {\n        println!(\n            \"{}\",\n            Yellow.paint(\"----------------- cpu context -----------------\")\n        );\n\n        let mut current_reg_val_map = HashMap::new();\n        for &reg_name in &self.sorted_reg_names {\n            if reg_name == \"end\" {\n                println!();\n                continue;\n            }\n\n            let &uc_reg = self.register_map.get(reg_name).unwrap();\n\n            // pad reg_name to 3 bytes\n            let mut padded_reg_name = reg_name.to_string();\n            while padded_reg_name.len() < 3 {\n                padded_reg_name.push(' ');\n            }\n\n            let reg_val = self.emu.reg_read(uc_reg).unwrap();\n            let previous_reg_val = *self.previous_reg_value.get(reg_name).unwrap();\n\n            let reg_val_str = match self.byte_size {\n                4 => format!(\"0x{:08x}\", reg_val),\n                8 => format!(\"0x{:016x}\", reg_val),\n                _ => unreachable!(),\n            };\n\n            if previous_reg_val != reg_val {\n                print!(\"{} : {} \", padded_reg_name, Blue.paint(reg_val_str));\n            } else {\n                print!(\"{} : {} \", padded_reg_name, reg_val_str);\n            }\n            current_reg_val_map.insert(reg_name, reg_val);\n            if reg_name == \"flags\" {\n                self.print_flags(reg_val);\n            }\n        }\n        self.previous_reg_value = current_reg_val_map;\n    }\n\n    pub fn asm(&self, str: String, address: u64) -> Result<AsmResult, Error> {\n        self.assembler.asm(str, address)\n    }\n\n    pub fn write_instruction(&mut self, byte_arr: Vec<u8>) {\n        let address = 0x0000;\n        let _ = self.emu.mem_write(address, &byte_arr);\n        let _ = self.emu.emu_start(\n            address,\n            address + byte_arr.len() as u64,\n            10 * SECOND_SCALE,\n            1000,\n        );\n    }\n\n    pub fn print_stack(&self) {\n        println!(\n            \"{}\",\n            Purple.paint(\"----------------- stack context -----------------\")\n        );\n        let cur_sp_val = self.emu.reg_read(self.sp).unwrap();\n\n        //let start_address = (0x1300000 - 8 * self.byte_size) as u64;\n        let mut start_address: u64 = 0x1300000;\n        while cur_sp_val < start_address - 4 * self.byte_size as u64 {\n            start_address -= 4 * self.byte_size as u64;\n        }\n        start_address -= 8 * self.byte_size as u64;\n        let mem_data = self\n            .emu\n            .mem_read_as_vec(start_address, self.byte_size * 4 * 5)\n            .unwrap();\n\n        // 8 个字节打印一次\n        (0..mem_data.len())\n            .step_by(4 * self.byte_size)\n            .for_each(|idx| {\n                match self.byte_size {\n                    4 => print!(\"{:08x} : \", start_address + idx as u64),\n                    8 => print!(\"{:016x} : \", start_address + idx as u64),\n                    _ => unreachable!(),\n                }\n\n                (0..4).for_each(|offset| {\n                    let (start_pos, end_pos) = (\n                        idx + offset * self.byte_size,\n                        idx + offset * self.byte_size + self.byte_size,\n                    );\n                    let mut cur = mem_data[start_pos..end_pos].to_vec();\n                    cur.reverse();\n                    if (start_address + start_pos as u64) == cur_sp_val {\n                        print!(\"{} \", Blue.paint(hex::encode(cur)));\n                    } else {\n                        print!(\"{} \", hex::encode(cur));\n                    }\n                });\n                println!();\n            });\n        println!();\n    }\n\n    fn print_flags(&self, flag_val: u64) {\n        let flag_names = vec![\"cf\", \"zf\", \"of\", \"sf\", \"pf\", \"af\", \"df\"];\n        let name_to_bit = vec![\n            (\"cf\", 0),\n            (\"pf\", 2),\n            (\"af\", 4),\n            (\"zf\", 6),\n            (\"sf\", 7),\n            (\"df\", 10),\n            (\"of\", 11),\n        ]\n        .into_iter()\n        .collect::<HashMap<_, _>>();\n\n        for flag_name in flag_names {\n            let bit_pos = name_to_bit.get(flag_name).unwrap();\n            let flag_val = flag_val >> (*bit_pos as u64) & 1;\n            match flag_val {\n                0 => print!(\"{}({}) \", flag_name, flag_val),\n                1 => print!(\"{} \", Blue.paint(format!(\"{}({})\", flag_name, flag_val))),\n                _ => unreachable!(),\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/engine/mod.rs",
    "content": "pub mod cpu;\npub mod machine;\n"
  },
  {
    "path": "src/main.rs",
    "content": "use ansi_term::Colour::Red;\nuse clap::Parser;\nuse keystone::{OptionType, OptionValue, Error};\nuse crate::engine::machine::MachineError;\nuse rustyline::error::ReadlineError;\nuse rustyline::{Cmd, Editor, KeyCode, KeyEvent, Modifiers};\nuse unicorn_engine::unicorn_const::Permission;\n\npub mod engine;\n\nuse crate::engine::machine::Machine;\n\n#[derive(clap::ValueEnum, Clone, Debug, PartialEq)]\n#[allow(clippy::upper_case_acronyms)]\n\nenum AssemblerSyntax {\n    INTEL,\n    ATT,\n    GAS,\n    MASM,\n    NASM,\n}\n\n#[derive(clap::Parser, Debug)]\nstruct Args {\n    #[arg(short, long)]\n    arch: Option<String>,\n\n    #[arg(short, long, value_enum)]\n    syntax: Option<AssemblerSyntax>,\n\n    #[arg(long, default_value_t = 0x01300000)]\n    initial_sp: u64,\n    #[arg(long, default_value_t = 0x10000000)]\n    initial_fp: u64,\n\n    #[arg(long, default_value_t = 0)]\n    initial_mem_begin: u64,\n    #[arg(long, default_value_t = 0x20000000)]\n    initial_mem_size: usize,\n\n    #[arg(long, default_value_t = String::from(\"ALL\"))]\n    initial_mem_prot: String,\n}\n\nfn get_machine(arch_name: String) -> Machine<'static> {\n    Machine::new_from_arch(arch_name.as_str()).unwrap()\n}\n\nfn parse_permission(prot: &str) -> Permission {\n    match prot {\n        \"READ\" => Permission::READ,\n        \"WRITE\" => Permission::WRITE,\n        \"EXEC\" => Permission::EXEC,\n        \"NONE\" => Permission::NONE,\n        _ => Permission::ALL,\n    }\n}\n\nfn main() {\n    // let args: Vec<String> = env::args().collect();\n    let args = Args::parse();\n\n    let arch_name = match args.arch {\n        Some(r) => r,\n        None => \"x64\".to_string(),\n    };\n    let ass_syntax = match args.syntax {\n        Some(syntax) => match syntax {\n            AssemblerSyntax::INTEL => OptionValue::SYNTAX_INTEL,\n            AssemblerSyntax::ATT => OptionValue::SYNTAX_ATT,\n            AssemblerSyntax::GAS => OptionValue::SYNTAX_GAS,\n            AssemblerSyntax::MASM => OptionValue::SYNTAX_MASM,\n            AssemblerSyntax::NASM => OptionValue::SYNTAX_NASM,\n        },\n        None => OptionValue::SYNTAX_INTEL,\n    };\n\n    let mut m: Machine = get_machine(arch_name);\n\n    // machine init\n    println!(\"initial: sp={:?} fp={:?}\", args.initial_sp, args.initial_fp);\n    println!(\"ass_syntax: {:?}\", ass_syntax);\n    \n    m.set_sp(args.initial_sp)\n        .expect(\"failed to write stack pointer\");\n    m.set_fp(args.initial_fp)\n        .expect(\"failed to write stack frame\");\n\n    let mem_prot = parse_permission(&args.initial_mem_prot);\n    println!(\"permission: {:?}\", mem_prot);\n    m.emu\n        .mem_map(\n            args.initial_mem_begin,\n            args.initial_mem_size,\n            mem_prot,\n        )\n        .expect(\"failed to mem map\");\n    m.assembler\n        .option(OptionType::SYNTAX, ass_syntax)\n        .expect(\"failed to change assembler syntax\");\n\n    m.print_machine();\n    m.print_register();\n    m.print_stack();\n\n    let mut rl = Editor::<()>::new();\n    rl.bind_sequence(KeyEvent(KeyCode::Down, Modifiers::NONE), Cmd::NextHistory);\n    rl.bind_sequence(KeyEvent(KeyCode::Up, Modifiers::NONE), Cmd::PreviousHistory);\n\n    loop {\n        let input = rl.readline(Red.paint(\">> \").to_string().as_str());\n        match input {\n            Ok(line) => {\n                let result = m.asm(line.to_string(), 0);\n                if line.is_empty() {\n                    println!(\"failed to assemble, err: {:?}\", Err::<Error, MachineError>(MachineError::Unsupported));\n                }\n                match result {\n                    Ok(r) => {\n                        rl.add_history_entry(line.as_str());\n                        println!(\n                            \"{} : {} {} : {}\",\n                            Red.paint(\"mnemonic\"),\n                            line.trim(),\n                            Red.paint(\"hex\"),\n                            r\n                        );\n                        m.write_instruction(r.bytes);\n                        m.print_register();\n                        m.print_stack();\n                    }\n                    Err(e) => println!(\"failed to assemble, err: {:?}\", e),\n                }\n            }\n            Err(ReadlineError::Interrupted) => {\n                println!(\"CTRL-C\");\n                break;\n            }\n            Err(ReadlineError::Eof) => {\n                println!(\"CTRL-D\");\n                break;\n            }\n            Err(err) => {\n                println!(\"Error: {:?}\", err);\n                break;\n            }\n        }\n    }\n}\n"
  }
]