Skip to content

Commit 20eba12

Browse files
james-a-johnsonemesare
authored andcommitted
[Rust] Improvements to LLIL flag and intrinsic operations
1 parent 9d2e83d commit 20eba12

File tree

4 files changed

+149
-9
lines changed

4 files changed

+149
-9
lines changed

rust/src/architecture.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,7 @@ impl FlagGroup for CoreFlagGroup {
13101310
}
13111311
}
13121312

1313-
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1313+
#[derive(Copy, Clone, Eq, PartialEq)]
13141314
pub struct CoreIntrinsic {
13151315
pub arch: CoreArchitecture,
13161316
pub id: IntrinsicId,
@@ -1396,6 +1396,18 @@ impl Intrinsic for CoreIntrinsic {
13961396
}
13971397
}
13981398

1399+
impl Debug for CoreIntrinsic {
1400+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1401+
f.debug_struct("CoreIntrinsic")
1402+
.field("id", &self.id)
1403+
.field("name", &self.name())
1404+
.field("class", &self.class())
1405+
.field("inputs", &self.inputs())
1406+
.field("outputs", &self.outputs())
1407+
.finish()
1408+
}
1409+
}
1410+
13991411
// TODO: WTF?!?!?!?
14001412
pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize);
14011413

rust/src/low_level_il/expression.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,7 @@ where
9898
F: FunctionForm,
9999
R: ExpressionResultType,
100100
{
101-
pub(crate) fn new(
102-
function: &'func LowLevelILFunction<M, F>,
103-
index: LowLevelExpressionIndex,
104-
) -> Self {
101+
pub fn new(function: &'func LowLevelILFunction<M, F>, index: LowLevelExpressionIndex) -> Self {
105102
// TODO: Validate expression here?
106103
Self {
107104
function,

rust/src/low_level_il/operation.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ where
8888
};
8989
PossibleValueSet::from_owned_core_raw(raw_pvs)
9090
}
91+
92+
/// Get the raw operand from the operand list.
93+
///
94+
/// This has no type information associated with it. It's up to the caller to know what the correct type of the
95+
/// underlying u64 should be.
96+
///
97+
/// # Panic
98+
/// `idx` must be less than 4. This is to protect against an out of bounds access.
99+
///
100+
/// # Safety
101+
/// Even if `idx` is valid, it may index to an unitialized or unused value. Make sure you index into an operand that
102+
/// you know should be initialized properly.
103+
pub unsafe fn get_operand(&self, idx: usize) -> u64 {
104+
assert!(idx < 4);
105+
self.op.operands[idx]
106+
}
91107
}
92108

93109
impl<M, O> Operation<'_, M, NonSSA, O>
@@ -214,16 +230,69 @@ where
214230
// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
215231
pub struct Intrinsic;
216232

233+
#[derive(Debug, Clone, Copy, PartialEq)]
234+
pub enum IntrinsicOutput {
235+
Reg(CoreRegister),
236+
Flag(CoreFlag),
237+
}
238+
239+
impl From<CoreRegister> for IntrinsicOutput {
240+
fn from(value: CoreRegister) -> Self {
241+
Self::Reg(value)
242+
}
243+
}
244+
245+
impl From<CoreFlag> for IntrinsicOutput {
246+
fn from(value: CoreFlag) -> Self {
247+
Self::Flag(value)
248+
}
249+
}
250+
217251
impl<M, F> Operation<'_, M, F, Intrinsic>
218252
where
219253
M: FunctionMutability,
220254
F: FunctionForm,
221255
{
222-
// TODO: Support register and expression lists
223256
pub fn intrinsic(&self) -> Option<CoreIntrinsic> {
224257
let raw_id = self.op.operands[2] as u32;
225258
self.function.arch().intrinsic_from_id(IntrinsicId(raw_id))
226259
}
260+
261+
/// Get the output list.
262+
pub fn outputs(&self) -> Vec<IntrinsicOutput> {
263+
// Convert the operand to either a register or flag id.
264+
let operand_to_output = |o: u64| {
265+
if o & (1 << 32) != 0 {
266+
self.function
267+
.arch()
268+
.flag_from_id(FlagId((o & 0xffffffff) as u32))
269+
.expect("Invalid core flag ID")
270+
.into()
271+
} else {
272+
self.function
273+
.arch()
274+
.register_from_id(RegisterId((o & 0xffffffff) as u32))
275+
.expect("Invalid register ID")
276+
.into()
277+
}
278+
};
279+
280+
self.get_operand_list(0)
281+
.into_iter()
282+
.map(operand_to_output)
283+
.collect::<Vec<_>>()
284+
}
285+
286+
/// Get the input list for the intrinsic.
287+
///
288+
/// This will just be a CallParamSsa expression.
289+
#[inline]
290+
pub fn inputs(&self) -> LowLevelILExpression<'_, M, F, ValueExpr> {
291+
LowLevelILExpression::new(
292+
self.function,
293+
LowLevelExpressionIndex(self.op.operands[3] as usize),
294+
)
295+
}
227296
}
228297

229298
impl<M, F> Debug for Operation<'_, M, F, Intrinsic>
@@ -232,9 +301,15 @@ where
232301
F: FunctionForm,
233302
{
234303
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
304+
use crate::architecture::Intrinsic;
235305
f.debug_struct("Intrinsic")
236306
.field("address", &self.address())
237-
.field("size", &self.intrinsic())
307+
.field(
308+
"intrinsic",
309+
&self.intrinsic().expect("Valid intrinsic").name(),
310+
)
311+
.field("outputs", &self.outputs())
312+
.field("inputs", &self.inputs())
238313
.finish()
239314
}
240315
}
@@ -1013,13 +1088,28 @@ where
10131088
// LLIL_FLAG, LLIL_FLAG_SSA
10141089
pub struct Flag;
10151090

1091+
impl<M, F> Operation<'_, M, F, Flag>
1092+
where
1093+
M: FunctionMutability,
1094+
F: FunctionForm,
1095+
{
1096+
pub fn source_flag(&self) -> CoreFlag {
1097+
self.function
1098+
.arch()
1099+
.flag_from_id(FlagId(self.op.operands[0] as u32))
1100+
.expect("Bad flag ID")
1101+
}
1102+
}
1103+
10161104
impl<M, F> Debug for Operation<'_, M, F, Flag>
10171105
where
10181106
M: FunctionMutability,
10191107
F: FunctionForm,
10201108
{
10211109
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1022-
f.debug_struct("Flag").finish()
1110+
f.debug_struct("Flag")
1111+
.field("source_flag", &self.source_flag())
1112+
.finish()
10231113
}
10241114
}
10251115

rust/tests/low_level_il.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use binaryninja::architecture::Register;
1+
use binaryninja::architecture::{ArchitectureExt, Intrinsic, Register};
22
use binaryninja::binary_view::BinaryViewExt;
33
use binaryninja::headless::Session;
44
use binaryninja::low_level_il::expression::{
@@ -7,6 +7,7 @@ use binaryninja::low_level_il::expression::{
77
use binaryninja::low_level_il::instruction::{
88
InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex,
99
};
10+
use binaryninja::low_level_il::operation::IntrinsicOutput;
1011
use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction};
1112
use std::path::PathBuf;
1213

@@ -308,3 +309,43 @@ fn test_llil_ssa() {
308309
_ => panic!("Expected CallSsa"),
309310
}
310311
}
312+
313+
#[test]
314+
fn test_llil_intrinsic() {
315+
let _session = Session::new().expect("Failed to initialize session");
316+
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
317+
let view = binaryninja::load(out_dir.join("atof.obj")).expect("Failed to create view");
318+
let image_base = view.original_image_base();
319+
let platform = view.default_platform().unwrap();
320+
let arch = platform.arch();
321+
322+
// Sample function: __crt_strtox::bit_scan_reverse
323+
let sample_function = view
324+
.function_at(&platform, image_base + 0x00037310)
325+
.unwrap();
326+
let llil_function = sample_function.low_level_il().unwrap();
327+
328+
// 5 @ 0004731d (LLIL_INTRINSIC eax, eflags = __bsr_gprv_memv((LLIL_LOAD.d [(LLIL_ADD.d (LLIL_REG.d ebp) + (LLIL_CONST.d 8)) {arg1}].d)))
329+
let instr_5 = llil_function
330+
.instruction_from_index(LowLevelInstructionIndex(5))
331+
.expect("Valid instruction");
332+
assert_eq!(instr_5.address(), image_base + 0x0003731d);
333+
println!("{:?}", instr_5);
334+
println!("{:#?}", instr_5.kind());
335+
match instr_5.kind() {
336+
LowLevelILInstructionKind::Intrinsic(op) => {
337+
assert_eq!(op.intrinsic().unwrap().name(), "__bsr_gprv_memv");
338+
assert_eq!(op.outputs().len(), 2);
339+
let reg_out_0 = arch.register_by_name("eax").unwrap();
340+
let reg_out_1 = arch.register_by_name("eflags").unwrap();
341+
assert_eq!(
342+
op.outputs(),
343+
vec![
344+
IntrinsicOutput::Reg(reg_out_0),
345+
IntrinsicOutput::Reg(reg_out_1),
346+
]
347+
);
348+
}
349+
_ => panic!("Expected Intrinsic"),
350+
}
351+
}

0 commit comments

Comments
 (0)