Skip to content

Commit 6edeedb

Browse files
committed
[idb_import] Display enum operands against their enumeration
Recover operands IDA displays as enumeration members, resolve each to its enumeration via idb-rs (op_enum_type, which maps the operand's member tid to the owning enumeration by tid range), and apply it to the disassembly with EnumerationDisplayType. Like the number-format pass it disassembles each operand to recover the immediate value and is gated behind the same "Apply IDB Operand Formats" setting.
1 parent a61a757 commit 6edeedb

2 files changed

Lines changed: 116 additions & 1 deletion

File tree

plugins/idb_import/src/mapper.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
33
use crate::parse::{
44
BaseAddressInfo, CommentInfo, DataInfo, DataKind, ExportInfo, FunctionFolderEntry,
5-
FunctionInfo, IDBInfo, LabelInfo, NameInfo, OperandFormat, OperandFormatInfo, SegmentInfo,
5+
FunctionInfo, IDBInfo, LabelInfo, NameInfo, OperandEnumInfo, OperandFormat, OperandFormatInfo,
6+
SegmentInfo,
67
};
78
use crate::translate::TILTranslator;
89
use binaryninja::architecture::{Architecture, ArchitectureExt, Register, RegisterInfo};
@@ -229,9 +230,65 @@ impl IDBMapper {
229230
}
230231
}
231232

233+
// Apply enum-displayed operands (same gating, since it also disassembles each operand).
234+
if self.apply_operand_formats && !self.info.operand_enums.is_empty() {
235+
tracing::info!(
236+
"Applying {} enum-displayed operands",
237+
self.info.operand_enums.len()
238+
);
239+
for operand_enum in &self.info.operand_enums {
240+
let mut rebased = operand_enum.clone();
241+
rebased.address = rebase(operand_enum.address);
242+
self.map_operand_enum_to_view(view, &rebased);
243+
}
244+
}
245+
232246
// self.map_used_types_to_view(view, &til_translator);
233247
}
234248

249+
/// Display an operand against its enumeration, as IDA does.
250+
///
251+
/// Resolves the enumeration's Binary Ninja type id and sets it on the operand for every
252+
/// immediate value in the instruction; like the number-format pass, an entry only takes
253+
/// effect for the exact (value, operand) Binary Ninja renders.
254+
fn map_operand_enum_to_view(&self, view: &BinaryView, operand_enum: &OperandEnumInfo) {
255+
let Some(type_id) = view.type_id_by_name(operand_enum.enum_name.as_str()) else {
256+
tracing::debug!(
257+
"No Binary Ninja type for enum '{}', skipping operand at {:0x}",
258+
operand_enum.enum_name,
259+
operand_enum.address
260+
);
261+
return;
262+
};
263+
264+
let functions = view.functions_containing(operand_enum.address);
265+
let Some(func) = functions.iter().next() else {
266+
return;
267+
};
268+
let arch = func.arch();
269+
270+
let bytes = view.read_vec(operand_enum.address, 16);
271+
if bytes.is_empty() {
272+
return;
273+
}
274+
let Some((_consumed, tokens)) = arch.instruction_text(&bytes, operand_enum.address) else {
275+
return;
276+
};
277+
278+
for token in &tokens {
279+
if let Some(value) = integer_token_value(&token.kind) {
280+
func.set_int_display_type(
281+
operand_enum.address,
282+
value,
283+
operand_enum.operand as usize,
284+
IntegerDisplayType::EnumerationDisplayType,
285+
Some(arch),
286+
Some(type_id.as_str()),
287+
);
288+
}
289+
}
290+
}
291+
235292
/// Apply IDA's per-operand number formats to the instruction at an address.
236293
///
237294
/// `set_int_display_type` only takes effect when Binary Ninja renders a token with the exact

plugins/idb_import/src/parse.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ pub enum OperandFormat {
116116
Offset,
117117
}
118118

119+
/// An instruction operand IDA displays as a member of an enumeration.
120+
#[derive(Debug, Clone, Serialize)]
121+
pub struct OperandEnumInfo {
122+
pub address: u64,
123+
pub operand: u8,
124+
/// The name of the enumeration the operand is displayed against.
125+
pub enum_name: String,
126+
}
127+
119128
#[derive(Debug, Clone, Serialize)]
120129
pub struct CommentInfo {
121130
pub address: u64,
@@ -194,6 +203,8 @@ pub struct IDBInfo {
194203
pub dir_tree: Option<DirTreeInfo>,
195204
/// Typed data items recovered from the byte flags (id1).
196205
pub data_items: Vec<DataInfo>,
206+
/// Operands IDA displays as enumeration members.
207+
pub operand_enums: Vec<OperandEnumInfo>,
197208
/// Per-operand number formats recovered from the byte flags (id1).
198209
pub operand_formats: Vec<OperandFormatInfo>,
199210
}
@@ -400,16 +411,63 @@ impl IDBFileParser {
400411
.map(|id1| self.parse_operand_formats(id1))
401412
.unwrap_or_default();
402413

414+
// Recover operands displayed as enumeration members, resolved to their enumeration.
415+
let operand_enums = match (id0.as_ref(), id1.as_ref(), til.as_ref()) {
416+
(Some(id0), Some(id1), Some(til)) => {
417+
self.parse_operand_enums(id0, id1, id2.as_ref(), til)?
418+
}
419+
_ => Vec::new(),
420+
};
421+
403422
Ok(IDBInfo {
404423
sha256,
405424
id0: id0_info,
406425
til,
407426
dir_tree: dir_tree_info,
408427
data_items,
428+
operand_enums,
409429
operand_formats,
410430
})
411431
}
412432

433+
/// Walk the byte flags and recover operands IDA displays as enumeration members, resolving
434+
/// each to the enumeration it belongs to.
435+
pub fn parse_operand_enums<K: IDAKind>(
436+
&self,
437+
id0: &ID0Section<K>,
438+
id1: &ID1Section<K>,
439+
id2: Option<&ID2Section<K>>,
440+
til: &TILSection,
441+
) -> anyhow::Result<Vec<OperandEnumInfo>> {
442+
use idb_rs::id1::{ByteOp, ByteType};
443+
444+
let root_info = id0.ida_info(id0.root_node()?)?;
445+
let netdelta = root_info.netdelta();
446+
447+
let mut operand_enums = Vec::new();
448+
for (address, byte_info, _size) in id1.all_bytes_no_tails() {
449+
let ByteType::Code(code) = byte_info.byte_type() else {
450+
continue;
451+
};
452+
for (operand, op) in [(0u8, code.operand0()), (1u8, code.operand1())] {
453+
if !matches!(op, Ok(Some(ByteOp::Enum))) {
454+
continue;
455+
}
456+
let Some(info) = AddressInfo::new(id0, id1, id2, netdelta, address) else {
457+
continue;
458+
};
459+
if let Some(enum_ty) = info.op_enum_type(operand, til) {
460+
operand_enums.push(OperandEnumInfo {
461+
address: address.into_raw().into_u64(),
462+
operand,
463+
enum_name: enum_ty.name.to_string(),
464+
});
465+
}
466+
}
467+
}
468+
Ok(operand_enums)
469+
}
470+
413471
/// Walk the byte flags and recover the per-operand number formats IDA assigned to code.
414472
pub fn parse_operand_formats<K: IDAKind>(
415473
&self,

0 commit comments

Comments
 (0)