From 1141459ef6ec38b1309208b8ebdf98c8eb911e57 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 18 Sep 2024 01:29:52 +0700 Subject: [PATCH 1/7] migrate 2021 --- bindings/rust/Cargo.toml | 3 ++- bindings/rust/examples/asm.rs | 1 - bindings/rust/keystone-sys/Cargo.toml | 1 + bindings/rust/keystone-sys/build.rs | 28 ++++++++++++--------------- bindings/rust/keystone-sys/src/lib.rs | 3 +-- bindings/rust/src/lib.rs | 6 ++---- bindings/rust/tests/keystone.rs | 2 -- 7 files changed, 18 insertions(+), 26 deletions(-) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 3342e5a1..de06eef7 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -6,6 +6,7 @@ authors = [ "Tasuku SUENAGA a.k.a. gunyarakun " ] description = "Rust bindings for the keystone-engine" +edition = "2021" license = "GPL-2.0" readme = "README.md" repository = "https://github.com/keystone-engine/keystone" @@ -15,7 +16,7 @@ include = [ [dependencies] libc = "0.2" -keystone-sys = { path = "keystone-sys", version = "0.9.2" } +keystone-sys = { path = "keystone-sys", version = "0.9.2", default-features = false } [features] default = [] diff --git a/bindings/rust/examples/asm.rs b/bindings/rust/examples/asm.rs index 86f32620..4922da25 100644 --- a/bindings/rust/examples/asm.rs +++ b/bindings/rust/examples/asm.rs @@ -1,4 +1,3 @@ -extern crate keystone; use keystone::*; fn main() { diff --git a/bindings/rust/keystone-sys/Cargo.toml b/bindings/rust/keystone-sys/Cargo.toml index a836c305..18acd641 100644 --- a/bindings/rust/keystone-sys/Cargo.toml +++ b/bindings/rust/keystone-sys/Cargo.toml @@ -5,6 +5,7 @@ authors = [ "Remco Verhoef ", "Tasuku SUENAGA a.k.a. gunyarakun " ] +edition = "2021" description = "Rust bindings for the keystone assembler" repository = "https://github.com/keystone-engine/keystone" documentation = "https://docs.rs/keystone/" diff --git a/bindings/rust/keystone-sys/build.rs b/bindings/rust/keystone-sys/build.rs index 9670fcb3..dc326066 100644 --- a/bindings/rust/keystone-sys/build.rs +++ b/bindings/rust/keystone-sys/build.rs @@ -1,18 +1,13 @@ -#[cfg(feature = "build_keystone_cmake")] -extern crate cmake; -#[cfg(feature = "use_system_keystone")] -extern crate pkg_config; - -#[cfg(all(not(windows), feature = "build_keystone_cmake"))] -use std::os::unix::fs::symlink; -#[cfg(all(windows, feature = "build_keystone_cmake"))] -use std::os::windows::fs::symlink_dir as symlink; - -#[cfg(feature = "build_keystone_cmake")] -use std::path::Path; +#[cfg(all(feature = "use_system_keystone", feature = "build_keystone_cmake"))] +compile_error!("mutual exclusive features: use_system_keystone & build_with_cmake"); #[cfg(feature = "build_keystone_cmake")] fn build_with_cmake() { + #[cfg(not(windows))] + use std::os::unix::fs::symlink; + #[cfg(windows)] + use std::os::windows::fs::symlink_dir as symlink; + use std::path::Path; if !Path::new("keystone").exists() { // This only happens when using the crate via a `git` reference as the // published version already embeds keystone's source. @@ -44,11 +39,12 @@ fn build_with_cmake() { } fn main() { - if cfg!(feature = "use_system_keystone") { - #[cfg(feature = "use_system_keystone")] + #[cfg(feature = "use_system_keystone")] { pkg_config::find_library("keystone").expect("Could not find system keystone"); - } else { - #[cfg(feature = "build_keystone_cmake")] + return; + } + #[cfg(feature = "build_keystone_cmake")] { build_with_cmake(); + return; } } diff --git a/bindings/rust/keystone-sys/src/lib.rs b/bindings/rust/keystone-sys/src/lib.rs index 6eb282a6..9f7ca931 100644 --- a/bindings/rust/keystone-sys/src/lib.rs +++ b/bindings/rust/keystone-sys/src/lib.rs @@ -6,11 +6,10 @@ #[macro_use] extern crate bitflags; -extern crate libc; pub mod keystone_const; -use keystone_const::{Arch, Error, Mode, OptionType, OptionValue}; +use crate::keystone_const::{Arch, Error, Mode, OptionType, OptionValue}; use ::std::{ ffi::CStr, fmt, diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index dd5bd042..9fddc217 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -3,7 +3,6 @@ //! Rust bindings by Remco Verhoef , 2016 //! //! ```rust -//! extern crate keystone; //! use keystone::{Keystone, Arch, Mode, OptionType, OptionValue}; //! //! fn main() { @@ -17,7 +16,6 @@ //! ``` extern crate keystone_sys as ffi; -extern crate libc; use std::{ convert::TryInto, @@ -26,8 +24,8 @@ use std::{ ops::Not, }; -pub use ffi::keystone_const::*; -pub use ffi::ks_handle; +pub use crate::ffi::keystone_const::*; +pub use crate::ffi::ks_handle; #[derive(Debug, PartialEq)] pub struct AsmResult { diff --git a/bindings/rust/tests/keystone.rs b/bindings/rust/tests/keystone.rs index f5d3e39d..1ea55629 100644 --- a/bindings/rust/tests/keystone.rs +++ b/bindings/rust/tests/keystone.rs @@ -1,5 +1,3 @@ -extern crate keystone; - use keystone::{Arch, Error, Keystone, Mode, OptionType, OptionValue}; #[test] From 4d0ec32a746c584c75fcb7bc66fb2dd0fdddb0ff Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 18 Sep 2024 21:33:30 +0700 Subject: [PATCH 2/7] fmt --- bindings/rust/Cargo.toml | 2 +- bindings/rust/keystone-sys/build.rs | 6 ++++-- bindings/rust/keystone-sys/src/lib.rs | 14 ++------------ bindings/rust/src/lib.rs | 2 +- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index de06eef7..21e0575d 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -19,7 +19,7 @@ libc = "0.2" keystone-sys = { path = "keystone-sys", version = "0.9.2", default-features = false } [features] -default = [] +default = ["use_system_keystone"] use_system_keystone = ["keystone-sys/use_system_keystone"] build_keystone_cmake = ["keystone-sys/build_keystone_cmake"] diff --git a/bindings/rust/keystone-sys/build.rs b/bindings/rust/keystone-sys/build.rs index dc326066..5021f8b7 100644 --- a/bindings/rust/keystone-sys/build.rs +++ b/bindings/rust/keystone-sys/build.rs @@ -39,11 +39,13 @@ fn build_with_cmake() { } fn main() { - #[cfg(feature = "use_system_keystone")] { + #[cfg(feature = "use_system_keystone")] + { pkg_config::find_library("keystone").expect("Could not find system keystone"); return; } - #[cfg(feature = "build_keystone_cmake")] { + #[cfg(feature = "build_keystone_cmake")] + { build_with_cmake(); return; } diff --git a/bindings/rust/keystone-sys/src/lib.rs b/bindings/rust/keystone-sys/src/lib.rs index 9f7ca931..af28dd1a 100644 --- a/bindings/rust/keystone-sys/src/lib.rs +++ b/bindings/rust/keystone-sys/src/lib.rs @@ -10,18 +10,8 @@ extern crate bitflags; pub mod keystone_const; use crate::keystone_const::{Arch, Error, Mode, OptionType, OptionValue}; -use ::std::{ - ffi::CStr, - fmt, - ptr, -}; -use ::libc::{ - c_char, - c_uchar, - c_int, - c_uint, - size_t, -}; +use ::libc::{c_char, c_int, c_uchar, c_uint, size_t}; +use ::std::{ffi::CStr, fmt, ptr}; /// Opaque type representing the Keystone engine #[repr(C)] diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 9fddc217..f211b1a7 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -88,7 +88,7 @@ impl Keystone { let err = unsafe { ffi::ks_open(arch, mode, &mut handle) }; if err == Error::OK { Ok(Keystone { - handle: handle.expect("Got NULL engine from ks_open()") + handle: handle.expect("Got NULL engine from ks_open()"), }) } else { Err(err) From 57fda904fbb21a384ba750f0d9253964f9a92aea Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 18 Sep 2024 22:22:18 +0700 Subject: [PATCH 3/7] kstool in Rust --- bindings/rust/Cargo.toml | 3 + bindings/rust/examples/kstool.rs | 133 +++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 bindings/rust/examples/kstool.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 21e0575d..62bec547 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -18,6 +18,9 @@ include = [ libc = "0.2" keystone-sys = { path = "keystone-sys", version = "0.9.2", default-features = false } +[dev-dependencies] +argh = "0.1" + [features] default = ["use_system_keystone"] diff --git a/bindings/rust/examples/kstool.rs b/bindings/rust/examples/kstool.rs new file mode 100644 index 00000000..4fc1ecb4 --- /dev/null +++ b/bindings/rust/examples/kstool.rs @@ -0,0 +1,133 @@ +use argh::FromArgs; +use std::fmt; +use std::io; +use std::str::FromStr; + +use keystone::{Arch, Keystone, Mode}; + +struct InvalidFromat; + +impl fmt::Display for InvalidFromat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "one of (raw, hex)") + } +} + +impl FromStr for FormatOuput { + type Err = InvalidFromat; + fn from_str(s: &str) -> Result { + let this = match s { + "raw" => Self::Raw, + "hex" => Self::Hex, + _ => return Err(InvalidFromat), + }; + Ok(this) + } +} + +#[derive(Clone, Copy, Default)] +enum FormatOuput { + #[default] + Hex, + Raw, + // String, // as python str + // Elf, +} + +#[derive(FromArgs)] +/// Assemble shellcode into bytes +struct Args { + // print version + //#[argh(switch, short = 'v')] + //version: bool, + /// output format (defaults to hex for ttys, otherwise raw) + #[argh(option, short = 'f')] + format: Option, + // output filename (defaults to stdout) + //#[argh(option, short = 'o')] + //output: Option, + /// arch/mode (defaults to x64) + #[argh(option, short = 'c')] + mode: Option, + /// input filename + #[argh(switch, short = 'i')] + infile: bool, + /// assembly strings or infile with -i + #[argh(positional)] + asm: String, +} + +fn main() -> Result<(), i32> { + let args: Args = argh::from_env(); + let mode = args.mode.as_deref().unwrap_or("x64"); + let (arch, mode) = match mode { + "x16" => (Arch::X86, Mode::MODE_16), + "x32" => (Arch::X86, Mode::MODE_32), + "x64" => (Arch::X86, Mode::MODE_64), + "arm" => (Arch::ARM, Mode::ARM | Mode::LITTLE_ENDIAN), + "armbe" => (Arch::ARM, Mode::ARM | Mode::BIG_ENDIAN), + "thumb" => (Arch::ARM, Mode::THUMB | Mode::LITTLE_ENDIAN), + "thumbbe" => (Arch::ARM, Mode::THUMB | Mode::LITTLE_ENDIAN), + "armv8" => (Arch::ARM, Mode::ARM | Mode::LITTLE_ENDIAN | Mode::V8), + "armv8be" => (Arch::ARM, Mode::ARM | Mode::BIG_ENDIAN | Mode::V8), + "thumbv8" => (Arch::ARM, Mode::THUMB | Mode::LITTLE_ENDIAN | Mode::V8), + "thumbv8be" => (Arch::ARM, Mode::THUMB | Mode::BIG_ENDIAN | Mode::V8), + "arm64" => (Arch::ARM64, Mode::LITTLE_ENDIAN), + "hexagon" => (Arch::HEXAGON, Mode::BIG_ENDIAN), + "mips" => (Arch::MIPS, Mode::MIPS32 | Mode::LITTLE_ENDIAN), + "mipsbe" => (Arch::MIPS, Mode::MIPS32 | Mode::BIG_ENDIAN), + "mips64" => (Arch::MIPS, Mode::LITTLE_ENDIAN), + "mips64be" => (Arch::MIPS, Mode::BIG_ENDIAN), + "ppc32be" => (Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN), + "ppc64" => (Arch::PPC, Mode::PPC64 | Mode::LITTLE_ENDIAN), + "ppc64be" => (Arch::PPC, Mode::PPC64 | Mode::BIG_ENDIAN), + "sparc" => (Arch::SPARC, Mode::SPARC32 | Mode::LITTLE_ENDIAN), + "sparcbe" => (Arch::SPARC, Mode::SPARC32 | Mode::BIG_ENDIAN), + "sparc64be" => (Arch::SPARC, Mode::SPARC64 | Mode::BIG_ENDIAN), + "systemz" => (Arch::SYSTEMZ, Mode::BIG_ENDIAN), + "evm" => (Arch::EVM, Mode::LITTLE_ENDIAN), + "riscv32" => (Arch::RISCV, Mode::RISCV32 | Mode::LITTLE_ENDIAN), + "riscv64" => (Arch::RISCV, Mode::RISCV64 | Mode::LITTLE_ENDIAN), + _ => { + eprintln!("invalid arch/mode: {mode}"); + return Err(1); + } + }; + let engine = Keystone::new(arch, mode).expect("could not initialize Keystone engine"); + + let asm = if args.infile { + let Ok(data) = std::fs::read(&args.asm) else { + panic!("cannot read filename {path}", path = &args.asm); + }; + String::from_utf8(data).expect("cannot decode utf-8 file") + } else { + args.asm + }; + + let result = engine.asm(asm, 0).expect("could not assemble"); + let output_format = args.format.unwrap_or(FormatOuput::default()); + match output_format { + FormatOuput::Hex => { + println!("{}", enhex(&result.bytes)); + } + FormatOuput::Raw => { + use io::Write; + let stdout = io::stdout(); + let mut handle = stdout.lock(); + handle + .write_all(&result.bytes) + .expect("cannot write to stdout"); + } + } + + Ok(()) +} + +fn enhex(s: &[u8]) -> String { + use fmt::Write; + let mut out = String::with_capacity(s.len() * 2); + for b in s { + let _e = write!(&mut out, "{b:02x}"); + } + out +} From 3503f39664c4d03c5b5b1fb0def145355c78d815 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Wed, 18 Sep 2024 22:44:29 +0700 Subject: [PATCH 4/7] new crate --- bindings/rust/Cargo.toml | 4 +--- bindings/rust/kstool/Cargo.toml | 14 ++++++++++++++ .../{examples/kstool.rs => kstool/src/main.rs} | 0 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 bindings/rust/kstool/Cargo.toml rename bindings/rust/{examples/kstool.rs => kstool/src/main.rs} (100%) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 62bec547..b51e804f 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -18,9 +18,6 @@ include = [ libc = "0.2" keystone-sys = { path = "keystone-sys", version = "0.9.2", default-features = false } -[dev-dependencies] -argh = "0.1" - [features] default = ["use_system_keystone"] @@ -28,3 +25,4 @@ use_system_keystone = ["keystone-sys/use_system_keystone"] build_keystone_cmake = ["keystone-sys/build_keystone_cmake"] [workspace] +members = ["kstool"] diff --git a/bindings/rust/kstool/Cargo.toml b/bindings/rust/kstool/Cargo.toml new file mode 100644 index 00000000..d194046e --- /dev/null +++ b/bindings/rust/kstool/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "kstool" +version = "0.1.0" +edition = "2021" +description = "Quick assembler using keystone-engine for CTF" + +[dependencies] +keystone = { version = "0.9", path = "../", default-features = false } +argh = "0.1" + +[features] +default = ["use_system"] +use_system = ["keystone/use_system_keystone"] +use_cmake = ["keystone/build_keystone_cmake"] diff --git a/bindings/rust/examples/kstool.rs b/bindings/rust/kstool/src/main.rs similarity index 100% rename from bindings/rust/examples/kstool.rs rename to bindings/rust/kstool/src/main.rs From 1e1c9a15b47da512ce1cd6b1c4398b4e33cc2fcd Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 19 Sep 2024 07:36:37 +0700 Subject: [PATCH 5/7] rust: Renew api of Ks::asm * reuse existing returned allocation from ks_asm * return Err instead of panic in lib --- bindings/rust/Cargo.toml | 2 +- bindings/rust/examples/asm.rs | 6 +- .../rust/keystone-sys/src/keystone_const.rs | 3 +- bindings/rust/keystone-sys/src/lib.rs | 6 +- bindings/rust/kstool/Cargo.toml | 9 +- bindings/rust/kstool/src/main.rs | 20 ++--- bindings/rust/src/lib.rs | 85 ++++++++++++------- bindings/rust/tests/keystone.rs | 15 ++-- 8 files changed, 82 insertions(+), 64 deletions(-) diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index b51e804f..31eea8ea 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "keystone" -version = "0.9.2" +version = "0.10.0" authors = [ "Remco Verhoef ", "Tasuku SUENAGA a.k.a. gunyarakun " diff --git a/bindings/rust/examples/asm.rs b/bindings/rust/examples/asm.rs index 4922da25..c13d4ccd 100644 --- a/bindings/rust/examples/asm.rs +++ b/bindings/rust/examples/asm.rs @@ -8,13 +8,11 @@ fn main() { .option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM) .expect("Could not set option to nasm syntax"); - let result = engine - .asm("mov ah, 0x80".to_string(), 0) - .expect("Could not assemble"); + let result = engine.asm(c"mov ah, 0x80", 0).expect("Could not assemble"); println!("ASM result: {}", result); - if let Err(err) = engine.asm("INVALID".to_string(), 0) { + if let Err(err) = engine.asm(c"INVALID", 0) { println!("Error: {}", err); } } diff --git a/bindings/rust/keystone-sys/src/keystone_const.rs b/bindings/rust/keystone-sys/src/keystone_const.rs index c2e977e5..a2e196ba 100644 --- a/bindings/rust/keystone-sys/src/keystone_const.rs +++ b/bindings/rust/keystone-sys/src/keystone_const.rs @@ -1,6 +1,7 @@ -#![allow(non_camel_case_types)] +#![expect(non_camel_case_types)] // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.rs] use ::libc::*; +use bitflags::bitflags; pub const API_MAJOR: c_uint = 0; pub const API_MINOR: c_uint = 9; diff --git a/bindings/rust/keystone-sys/src/lib.rs b/bindings/rust/keystone-sys/src/lib.rs index af28dd1a..075246f6 100644 --- a/bindings/rust/keystone-sys/src/lib.rs +++ b/bindings/rust/keystone-sys/src/lib.rs @@ -2,10 +2,6 @@ //! By Nguyen Anh Quynh , 2016 */ //! Rust bindings by Remco Verhoef , 2016 */ //! -#![allow(bad_style)] - -#[macro_use] -extern crate bitflags; pub mod keystone_const; @@ -18,6 +14,8 @@ use ::std::{ffi::CStr, fmt, ptr}; pub struct ks_engine { _private: [u8; 0], } + +#[expect(non_camel_case_types)] pub type ks_handle = ptr::NonNull; extern "C" { diff --git a/bindings/rust/kstool/Cargo.toml b/bindings/rust/kstool/Cargo.toml index d194046e..dfb9b883 100644 --- a/bindings/rust/kstool/Cargo.toml +++ b/bindings/rust/kstool/Cargo.toml @@ -1,11 +1,16 @@ [package] -name = "kstool" +name = "kstool-cli" version = "0.1.0" edition = "2021" description = "Quick assembler using keystone-engine for CTF" +license = "MIT" + +[[bin]] +name = "kstool" +path = "src/main.rs" [dependencies] -keystone = { version = "0.9", path = "../", default-features = false } +keystone = { version = "0.10", path = "../", default-features = false } argh = "0.1" [features] diff --git a/bindings/rust/kstool/src/main.rs b/bindings/rust/kstool/src/main.rs index 4fc1ecb4..46f6026d 100644 --- a/bindings/rust/kstool/src/main.rs +++ b/bindings/rust/kstool/src/main.rs @@ -1,4 +1,5 @@ use argh::FromArgs; +use std::ffi::CString; use std::fmt; use std::io; use std::str::FromStr; @@ -99,35 +100,26 @@ fn main() -> Result<(), i32> { let Ok(data) = std::fs::read(&args.asm) else { panic!("cannot read filename {path}", path = &args.asm); }; - String::from_utf8(data).expect("cannot decode utf-8 file") + CString::from_vec_with_nul(data).expect("file shouldn't contains NUL bytes") } else { - args.asm + CString::new(args.asm).expect("assembly contains NUL bytes") }; - let result = engine.asm(asm, 0).expect("could not assemble"); + let result = engine.asm(&asm, 0).expect("could not assemble"); let output_format = args.format.unwrap_or(FormatOuput::default()); match output_format { FormatOuput::Hex => { - println!("{}", enhex(&result.bytes)); + println!("{result}"); } FormatOuput::Raw => { use io::Write; let stdout = io::stdout(); let mut handle = stdout.lock(); handle - .write_all(&result.bytes) + .write_all(&result.as_bytes()) .expect("cannot write to stdout"); } } Ok(()) } - -fn enhex(s: &[u8]) -> String { - use fmt::Write; - let mut out = String::with_capacity(s.len() * 2); - for b in s { - let _e = write!(&mut out, "{b:02x}"); - } - out -} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index f211b1a7..34ade4c9 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -17,26 +17,36 @@ extern crate keystone_sys as ffi; -use std::{ - convert::TryInto, - ffi::{CStr, CString}, - fmt, - ops::Not, -}; +use std::{convert::TryInto, ffi::CStr, fmt}; pub use crate::ffi::keystone_const::*; pub use crate::ffi::ks_handle; #[derive(Debug, PartialEq)] pub struct AsmResult { - pub size: u32, pub stat_count: u32, - pub bytes: Vec, + size: u32, + ptr: *mut libc::c_uchar, +} + +impl AsmResult { + pub fn as_bytes(&self) -> &[u8] { + let bytes = unsafe { core::slice::from_raw_parts(self.ptr, self.size as _) }; + bytes + } +} + +impl Drop for AsmResult { + fn drop(&mut self) { + unsafe { + ffi::ks_free(self.ptr); + }; + } } impl fmt::Display for AsmResult { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for &byte in &self.bytes { + for byte in self.as_bytes() { f.write_fmt(format_args!("{:02x}", byte))?; } @@ -44,6 +54,24 @@ impl fmt::Display for AsmResult { } } +#[derive(Debug)] +pub enum AsmError { + NullPtr, + SizeOverflow, + Raw(Error), +} + +impl fmt::Display for AsmError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let msg = match self { + Self::NullPtr => "got NULL ptr in allocation", + Self::SizeOverflow => "u32 bigger than size_t", + Self::Raw(err) => return fmt::Display::fmt(err, f), + }; + f.write_str(msg) + } +} + pub fn bindings_version() -> (u32, u32) { (API_MAJOR, API_MINOR) } @@ -88,6 +116,7 @@ impl Keystone { let err = unsafe { ffi::ks_open(arch, mode, &mut handle) }; if err == Error::OK { Ok(Keystone { + // fixme: return err handle: handle.expect("Got NULL engine from ks_open()"), }) } else { @@ -120,42 +149,36 @@ impl Keystone { /// /// This API dynamically allocate memory to contain assembled instruction. /// Resulted array of bytes containing the machine code is put into @*encoding - pub fn asm(&self, str: String, address: u64) -> Result { + pub fn asm(&self, str: &CStr, address: u64) -> Result { let mut size: libc::size_t = 0; let mut stat_count: libc::size_t = 0; - let s = CString::new(str).unwrap(); let mut ptr: *mut libc::c_uchar = std::ptr::null_mut(); - let err = Error::from_bits_truncate(unsafe { + let err = unsafe { ffi::ks_asm( self.handle, - s.as_ptr(), + str.as_ptr(), address, &mut ptr, &mut size, &mut stat_count, ) - }); + }; - if err == Error::OK { - debug_assert!(ptr.is_null().not()); - let bytes_slice = unsafe { std::slice::from_raw_parts(ptr, size) }; - let bytes = bytes_slice.to_vec(); - - unsafe { - ffi::ks_free(ptr); - }; - - Ok(AsmResult { - size: size.try_into().expect("size_t overflowed u32"), - stat_count: stat_count.try_into().expect("size_t overflowed u32"), - bytes, - }) - } else { - let err = self.error().unwrap_or(err); - Err(err) + if err != 0 { + let err = unsafe { ffi::ks_errno(self.handle) }; + return Err(AsmError::Raw(err)); } + if ptr.is_null() { + return Err(AsmError::NullPtr); + } + + Ok(AsmResult { + stat_count: stat_count.try_into().map_err(|_| AsmError::SizeOverflow)?, + size: size.try_into().map_err(|_| AsmError::SizeOverflow)?, + ptr, + }) } } diff --git a/bindings/rust/tests/keystone.rs b/bindings/rust/tests/keystone.rs index 1ea55629..305220a9 100644 --- a/bindings/rust/tests/keystone.rs +++ b/bindings/rust/tests/keystone.rs @@ -1,4 +1,4 @@ -use keystone::{Arch, Error, Keystone, Mode, OptionType, OptionValue}; +use keystone::{Arch, AsmError, Error, Keystone, Mode, OptionType, OptionValue}; #[test] fn version() { @@ -14,7 +14,7 @@ fn arch_supported() { #[test] fn asm() { - let asm = String::from("mov ah, 0x80\n nop\n mov al, 0x81\n"); + let asm = c"mov ah, 0x80\n nop\n mov al, 0x81\n"; let engine = Keystone::new(Arch::X86, Mode::LITTLE_ENDIAN | Mode::MODE_32) .expect("Could not initialize Keystone engine"); @@ -25,13 +25,12 @@ fn asm() { let result = engine.asm(asm, 0).expect("Could not assemble"); - print!("{0:?}", result.bytes); - assert_eq!(result.bytes, [0xb4, 0x80, 0x90, 0xb0, 0x81]); + println!("{0}", result); } #[test] fn invalid_asm() { - let asm = String::from("invalid asm"); + let asm = c"invalid asm"; let engine = Keystone::new(Arch::X86, Mode::MODE_32).expect("Could not initialize Keystone engine"); @@ -39,10 +38,12 @@ fn invalid_asm() { let result = engine.asm(asm, 0); let err = result.unwrap_err(); + let AsmError::Raw(err) = err else { + panic!("should be an invaid asm error"); + }; assert_eq!(err, Error::ASM_MNEMONICFAIL); - assert_eq!(err.msg(), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)"); assert_eq!( - format!("{}", err), + format!("{err}"), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)" ); } From 6d7f6d49b998f5a251e06e60bbb0ba70f89e25a3 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 19 Sep 2024 20:28:03 +0700 Subject: [PATCH 6/7] migrate crate to keystone-engine crate keystone on crates.io is old --- bindings/rust/kstool/Cargo.toml | 9 +++++---- bindings/rust/kstool/src/main.rs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bindings/rust/kstool/Cargo.toml b/bindings/rust/kstool/Cargo.toml index dfb9b883..b5fc187c 100644 --- a/bindings/rust/kstool/Cargo.toml +++ b/bindings/rust/kstool/Cargo.toml @@ -1,19 +1,20 @@ [package] -name = "kstool-cli" +name = "keystone-cli" version = "0.1.0" edition = "2021" description = "Quick assembler using keystone-engine for CTF" license = "MIT" +resolver = "2" [[bin]] name = "kstool" path = "src/main.rs" [dependencies] -keystone = { version = "0.10", path = "../", default-features = false } +keystone = { version = "0.1", package = "keystone-engine", default-features = false } argh = "0.1" [features] default = ["use_system"] -use_system = ["keystone/use_system_keystone"] -use_cmake = ["keystone/build_keystone_cmake"] +use_system = ["keystone/use-system-lib"] +use_cmake = ["keystone/cmake"] diff --git a/bindings/rust/kstool/src/main.rs b/bindings/rust/kstool/src/main.rs index 46f6026d..3023abc6 100644 --- a/bindings/rust/kstool/src/main.rs +++ b/bindings/rust/kstool/src/main.rs @@ -87,8 +87,8 @@ fn main() -> Result<(), i32> { "sparc64be" => (Arch::SPARC, Mode::SPARC64 | Mode::BIG_ENDIAN), "systemz" => (Arch::SYSTEMZ, Mode::BIG_ENDIAN), "evm" => (Arch::EVM, Mode::LITTLE_ENDIAN), - "riscv32" => (Arch::RISCV, Mode::RISCV32 | Mode::LITTLE_ENDIAN), - "riscv64" => (Arch::RISCV, Mode::RISCV64 | Mode::LITTLE_ENDIAN), + //"riscv32" => (Arch::RISCV, Mode::RISCV32 | Mode::LITTLE_ENDIAN), + //"riscv64" => (Arch::RISCV, Mode::RISCV64 | Mode::LITTLE_ENDIAN), _ => { eprintln!("invalid arch/mode: {mode}"); return Err(1); From e73fccac91974477d67ca07642aadff1da305421 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Mon, 9 Dec 2024 19:59:58 +0700 Subject: [PATCH 7/7] kstool: normalize 'word' -> 'word ptr' --- bindings/rust/kstool/Cargo.toml | 8 +++++++- bindings/rust/kstool/src/main.rs | 9 +++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/bindings/rust/kstool/Cargo.toml b/bindings/rust/kstool/Cargo.toml index b5fc187c..a52230a2 100644 --- a/bindings/rust/kstool/Cargo.toml +++ b/bindings/rust/kstool/Cargo.toml @@ -11,9 +11,15 @@ name = "kstool" path = "src/main.rs" [dependencies] -keystone = { version = "0.1", package = "keystone-engine", default-features = false } +# keystone = { version = "0.1", package = "keystone-engine", default-features = false } argh = "0.1" +[dependencies.keystone] +version = "0.2" +# in my forked +package = "keystone-engine" +default-features = false + [features] default = ["use_system"] use_system = ["keystone/use-system-lib"] diff --git a/bindings/rust/kstool/src/main.rs b/bindings/rust/kstool/src/main.rs index 3023abc6..e8d7ebbf 100644 --- a/bindings/rust/kstool/src/main.rs +++ b/bindings/rust/kstool/src/main.rs @@ -100,9 +100,14 @@ fn main() -> Result<(), i32> { let Ok(data) = std::fs::read(&args.asm) else { panic!("cannot read filename {path}", path = &args.asm); }; - CString::from_vec_with_nul(data).expect("file shouldn't contains NUL bytes") + CString::from_vec_with_nul(data).expect("file shouldn't contain NUL bytes") } else { - CString::new(args.asm).expect("assembly contains NUL bytes") + let norm = args + .asm + .replace("word [", "word ptr [") + .replace("dword [", "dword ptr [") + .replace("qword [", "qword ptr ["); + CString::new(norm).expect("assembly shouldn't contain NUL bytes") }; let result = engine.asm(&asm, 0).expect("could not assemble");