Skip to content
This repository has been archived by the owner on Aug 4, 2024. It is now read-only.

Commit

Permalink
properly handle errors when getting a CapstoneContext instance from Java
Browse files Browse the repository at this point in the history
  • Loading branch information
jus7n committed Aug 13, 2023
1 parent 4404f21 commit b3a7bed
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 99 deletions.
6 changes: 3 additions & 3 deletions lib/src/capstone/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ impl CapstoneContext {
}

/// Clones an Arc out of the handle field of the given object.
pub fn get(env: &mut JNIEnv, object: &JObject) -> Arc<CapstoneContext> {
pub fn get(env: &mut JNIEnv, object: &JObject) -> JResult<Arc<CapstoneContext>> {
let guard: MutexGuard<Arc<CapstoneContext>> =
unsafe { env.get_rust_field(object, HANDLE_FIELD).unwrap() };
guard.clone()
unsafe { env.get_rust_field(object, HANDLE_FIELD)? };
Ok(guard.clone())
}

/// Surrenders ownership of the context instance to Java.
Expand Down
104 changes: 104 additions & 0 deletions lib/src/capstone/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,110 @@
*
* Use of this source code is governed by the MIT license found in the LICENSE file.
*/
use crate::capstone::context::CapstoneContext;
use crate::capstone::mode::CapstoneMode;
use crate::capstone::output::CapstoneOutput;
use capstone::arch::BuildsCapstone;
use capstone::{arch, Capstone, InsnGroupId, InsnGroupIdInt, InsnId, InsnIdInt, RegId, RegIdInt};
use jni::objects::{JByteArray, JObject, ReleaseMode};
use jni::sys::{jint, jlong, jshort};
use jni::JNIEnv;
use std::ops::BitAnd;

pub mod context;
pub mod mode;
pub mod output;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

pub fn init<'local>(
env: &mut JNIEnv<'local>,
this: JObject<'local>,
mode: JObject<'local>,
) -> Result<()> {
let mode = CapstoneMode::from(env, &mode);

let capstone = match mode {
Some(CapstoneMode::ARM32) => Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.detail(true)
.build(),
Some(CapstoneMode::ARM64) => Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.detail(true)
.build(),
_ => return Err("invalid argument 'mode'".into()),
}
.map_err(|e| e.to_string())?;

let instance = CapstoneContext::new(capstone, mode.unwrap());

CapstoneContext::surrender_instance(instance, env, &this)?;
Ok(())
}

pub fn disassemble<'local>(
env: &mut JNIEnv<'local>,
this: JObject<'local>,
result_object: JObject<'local>,
data: JByteArray<'local>,
count: jint,
address: jlong,
) -> Result<()> {
let code: Vec<u8> = {
let result = unsafe { env.get_array_elements(&data, ReleaseMode::NoCopyBack)? };
result.iter().map(|b| (*b as u8).bitand(0xff)).collect()
};

let ctx = CapstoneContext::get(env, &this)?;
let capstone = ctx.capstone.lock().unwrap();

let instructions = {
if count == 0 {
capstone.disasm_all(&code, address as u64)
} else {
capstone.disasm_count(&code, address as u64, count as usize)
}
.map_err(|e| e.to_string())?
};

let mut output = CapstoneOutput::new(env, &ctx.mode, &capstone, &result_object);
output.copy_instructions(instructions)
}

pub fn get_insn_name<'local>(
env: &mut JNIEnv<'local>,
this: JObject<'local>,
insn_id: jint,
) -> Result<Option<String>> {
let ctx = CapstoneContext::get(env, &this)?;
let capstone = ctx.capstone.lock().unwrap();
Ok(capstone.insn_name(InsnId(insn_id as InsnIdInt)))
}

pub fn get_reg_name<'local>(
env: &mut JNIEnv<'local>,
this: JObject<'local>,
reg_id: jint,
) -> Result<Option<String>> {
let ctx = CapstoneContext::get(env, &this)?;
let capstone = ctx.capstone.lock().unwrap();
Ok(capstone.reg_name(RegId(reg_id as RegIdInt)))
}

pub fn get_group_name<'local>(
env: &mut JNIEnv<'local>,
this: JObject<'local>,
group_id: jshort,
) -> Result<Option<String>> {
let ctx = CapstoneContext::get(env, &this)?;
let capstone = ctx.capstone.lock().unwrap();
Ok(capstone.group_name(InsnGroupId(group_id as InsnGroupIdInt)))
}

pub fn throw(env: &mut JNIEnv, message: &str) {
env.throw_new("org/native4j/capstone/exception/CapstoneException", message)
.unwrap();
}
101 changes: 28 additions & 73 deletions lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,25 @@
*
* Use of this source code is governed by the MIT license found in the LICENSE file.
*/
use std::ops::BitAnd;

use ::capstone::arch::BuildsCapstone;
use ::capstone::{arch, Capstone, InsnGroupId, InsnGroupIdInt, InsnId, InsnIdInt, RegId, RegIdInt};
use jni::objects::{JByteArray, JObject, ReleaseMode};
use jni::objects::{JByteArray, JObject};
use jni::sys::{jint, jlong, jshort, jstring};
use jni::JNIEnv;

use crate::capstone::context::CapstoneContext;
use crate::capstone::mode::CapstoneMode;
use crate::capstone::output::CapstoneOutput;

mod capstone;
mod obj;
mod util;
mod writer;

#[no_mangle]
pub extern "system" fn Java_org_native4j_capstone_Capstone_init<'local>(
mut env: JNIEnv<'local>,
this: JObject<'local>,
mode: JObject<'local>,
) -> jstring {
let mode = CapstoneMode::from(&mut env, &mode);

let capstone = match mode {
Some(CapstoneMode::ARM32) => Capstone::new()
.arm()
.mode(arch::arm::ArchMode::Arm)
.detail(true)
.build(),
Some(CapstoneMode::ARM64) => Capstone::new()
.arm64()
.mode(arch::arm64::ArchMode::Arm)
.detail(true)
.build(),
_ => return make_error!(env, "invalid argument 'mode'"),
};
check_result!(env, capstone);

let instance = CapstoneContext::new(capstone.unwrap(), mode.unwrap());

let result = CapstoneContext::surrender_instance(instance, &mut env, &this);
let result = capstone::init(&mut env, this, mode);
check_result!(env, result);

0 as jstring /* null */
}

Expand All @@ -57,7 +32,6 @@ pub extern "system" fn Java_org_native4j_capstone_Capstone_shutdown<'local>(
) -> jstring {
let result = CapstoneContext::drop_instance(&mut env, &this);
check_result!(env, result);

0 as jstring /* null */
}

Expand All @@ -70,36 +44,8 @@ pub extern "system" fn Java_org_native4j_capstone_Capstone_disassemble<'local>(
count: jint,
address: jlong,
) -> jstring {
let code: Vec<u8> = {
let result = unsafe { env.get_array_elements(&data, ReleaseMode::NoCopyBack) };
check_result!(env, result);
result
.unwrap()
.iter()
.map(|b| (*b as u8).bitand(0xff))
.collect()
};

let ctx = CapstoneContext::get(&mut env, &this);
let capstone = ctx.capstone.lock().unwrap();

let instructions = {
let result = if count == 0 {
capstone.disasm_all(&code, address as u64)
} else {
capstone.disasm_count(&code, address as u64, count as usize)
};
if let Err(e) = result {
return make_error!(env, e.to_string());
}
result.unwrap()
};

let mut output = CapstoneOutput::new(&mut env, &ctx.mode, &capstone, &result_object);

let result = output.copy_instructions(instructions);
let result = capstone::disassemble(&mut env, this, result_object, data, count, address);
check_result!(env, result);

0 as jstring /* null */
}

Expand All @@ -109,12 +55,15 @@ pub extern "system" fn Java_org_native4j_capstone_Capstone_getInsnName<'local>(
this: JObject<'local>,
insn_id: jint,
) -> jstring {
let ctx = CapstoneContext::get(&mut env, &this);
let capstone = ctx.capstone.lock().unwrap();
match capstone.insn_name(InsnId(insn_id as InsnIdInt)) {
Some(str) => make_jstring!(env, str),
None => 0 as jstring, /* null */
let result = capstone::get_insn_name(&mut env, this, insn_id);
if let Err(e) = result {
capstone::throw(&mut env, &e.to_string());
return 0 as jstring /* null */;
}
result
.unwrap()
.map(|str| make_jstring!(env, str))
.unwrap_or(0 as jstring /* null */)
}

#[no_mangle]
Expand All @@ -123,12 +72,15 @@ pub extern "system" fn Java_org_native4j_capstone_Capstone_getRegName<'local>(
this: JObject<'local>,
reg_id: jint,
) -> jstring {
let ctx = CapstoneContext::get(&mut env, &this);
let capstone = ctx.capstone.lock().unwrap();
match capstone.reg_name(RegId(reg_id as RegIdInt)) {
Some(str) => make_jstring!(env, str),
None => 0 as jstring, /* null */
let result = capstone::get_reg_name(&mut env, this, reg_id);
if let Err(e) = result {
capstone::throw(&mut env, &e.to_string());
return 0 as jstring /* null */;
}
result
.unwrap()
.map(|str| make_jstring!(env, str))
.unwrap_or(0 as jstring /* null */)
}

#[no_mangle]
Expand All @@ -137,10 +89,13 @@ pub extern "system" fn Java_org_native4j_capstone_Capstone_getGroupName<'local>(
this: JObject<'local>,
group_id: jshort,
) -> jstring {
let ctx = CapstoneContext::get(&mut env, &this);
let capstone = ctx.capstone.lock().unwrap();
match capstone.group_name(InsnGroupId(group_id as InsnGroupIdInt)) {
Some(str) => make_jstring!(env, str),
None => 0 as jstring, /* null */
let result = capstone::get_group_name(&mut env, this, group_id);
if let Err(e) = result {
capstone::throw(&mut env, &e.to_string());
return 0 as jstring /* null */;
}
result
.unwrap()
.map(|str| make_jstring!(env, str))
.unwrap_or(0 as jstring /* null */)
}
Loading

0 comments on commit b3a7bed

Please sign in to comment.