capstone-rust / capstone-rs

high-level Capstone system bindings for Rust
217 stars 76 forks source link

Unknown Capstone Return Error #21

Closed Talanor closed 6 years ago

Talanor commented 6 years ago

Hello,

I have been playing a bit with capstone-rs and I stumbled upon this error: "Error: Encountered Unknown Capstone Return Error", which looks like an internal error from the library.

Below is the code I used (please excuse any dumb stuff I might have done as I am relatively new to rust and capstone):

extern crate byteorder;
extern crate capstone;
extern crate elf;
#[macro_use]
extern crate error_chain;
extern crate libc;
extern crate nix;
extern crate potential_couscous;

use std::env;
use std::ffi;
use byteorder::WriteBytesExt;
use capstone::prelude::*;

error_chain! {
    foreign_links {
        UnixError(nix::Error);
        CapstoneError(capstone::Error);
    }
}

fn run(binpath: &str) -> Result<()> {
    match nix::unistd::fork()? {
        nix::unistd::ForkResult::Parent { child, .. } => {
            let status = nix::sys::wait::waitpid(child, None)?;
            println!("parent: status: {:#?}", status);

            let ret = nix::sys::ptrace::ptrace(
                nix::sys::ptrace::ptrace::PTRACE_PEEKUSER,
                child,
                (libc::ORIG_RAX * 8) as *mut nix::libc::c_void,
                std::ptr::null_mut(),
            )?;
            println!("parent: ptrace return: {:#?}", ret);

            let cs = Capstone::new()
                .x86()
                .mode(arch::x86::ArchMode::Mode64)
                .syntax(arch::x86::ArchSyntax::Intel)
                .detail(true)
                .build()?;

            loop {
                let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
                let p_regs: *mut libc::c_void = &mut regs as *mut _ as *mut libc::c_void;

                nix::sys::ptrace::ptrace(
                    nix::sys::ptrace::ptrace::PTRACE_GETREGS,
                    child,
                    std::ptr::null_mut(),
                    p_regs
                )?;

                //println!("parent: ptrace return: {}", ret);
                println!("parent: RIP: 0x{:X}", regs.rip);

                let instr = nix::sys::ptrace::ptrace(
                    nix::sys::ptrace::ptrace::PTRACE_PEEKTEXT,
                    child,
                    regs.rip as *mut nix::libc::c_void,
                    std::ptr::null_mut()
                )?;

                let mut wtr = vec![];
                wtr.write_i64::<byteorder::LittleEndian>(instr).unwrap();

                //println!("parent: INSTR: {:#?}", instr);
                println!("parent: Bytes: {:#?}", wtr);

                let instructions = cs.disasm_count(wtr.as_slice(), regs.rip, 1)?;

                //println!("parent: found {} instructions", instructions.len());

                if instructions.len() > 0 {
                    let insn = instructions.iter().next().unwrap();
                    println!("{} {}", insn.mnemonic().unwrap(), insn.op_str().unwrap());
                }

                nix::sys::ptrace::ptrace(
                    nix::sys::ptrace::ptrace::PTRACE_SINGLESTEP,
                    child,
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                )?;
                //println!("parent: ptrace return: {:#?}", ret);

                nix::sys::wait::waitpid(child, None)?;
                //println!("parent: status: {:#?}", status);
            }
        }

        nix::unistd::ForkResult::Child => {
            let ret = nix::sys::ptrace::ptrace(
                nix::sys::ptrace::ptrace::PTRACE_TRACEME,
                nix::unistd::Pid::from_raw(0),
                std::ptr::null_mut(),
                std::ptr::null_mut(),
            )?;
            println!("child: TRACEME worked: {:#?}", ret);
            nix::unistd::execvp(&ffi::CString::new(binpath).unwrap(), &[])?;
        }
    };
    Ok(())
}

fn _main() -> Result<()> {
    let args: Vec<String> = env::args().collect();

    if args.len() == 2 {
        run(&args[1])?;
    } else {
        println!("Parameters NOK");
    }
    Ok(())
}

quick_main!(_main);

From my understanding, the interesting debug info would be this:

parent: RIP: 0x7F26E403BBCC
parent: Bytes: [
    73,
    185,
    208,
    3,
    0,
    128,
    3,
    0
]
Error: Encountered Unknown Capstone Return Error

Please let me know if I can provide any more information.

Thanks for your time.

tmfink commented 6 years ago

It looks like the vector of bytes (wtr) does not contain a valid amd64 instructions (at least according to Capstone). Currently, capstone-rs assumes that getting 0 instructions back from a disasm() call indicates an error. wtr has no valid instructions, so disasm_count() returns an Error.

This is probably not the behavior we want. The official Python bindings check the errno if 0 instructions are returned, so we should do the same.

I did not use this logic originally because it seemed like Capstone did not always reset the errno when appropriate. However, even in the version of Capstone bundled with capstone-sys, the errno is properly reset.

Thanks for filing the bug! This is one of the pain points of the library that I have been meaning to address.

tmfink commented 6 years ago

Fixed with #22.

@Talanor, please comment on this issue if the latest master branch does not fix your bug. Please refer to Specifying dependencies from git repositories for details.