Skip to content

Commit 05f140e

Browse files
committed
add rust language representation tests
1 parent 1698b2e commit 05f140e

File tree

2 files changed

+376
-5
lines changed

2 files changed

+376
-5
lines changed

rust/src/language_representation.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -677,10 +677,7 @@ unsafe extern "C" fn line_formatter_format_lines_ffi<C: CustomLineFormater>(
677677
// NOTE dropped by line_formatter_free_lines_ffi
678678
let ctxt = ctxt as *mut C;
679679
let lines = core::slice::from_raw_parts(in_lines, in_count);
680-
let lines: Vec<_> = lines
681-
.into_iter()
682-
.map(DisassemblyTextLine::from_raw)
683-
.collect();
680+
let lines: Vec<_> = lines.iter().map(DisassemblyTextLine::from_raw).collect();
684681
let result = (*ctxt).format_lines(&lines, &*settings);
685682
*out_count = result.len();
686683
let result: Box<[BNDisassemblyTextLine]> = result
@@ -833,7 +830,7 @@ impl HighLevelILTokenEmitter {
833830
unsafe {
834831
BNAddHighLevelILVarTextToken(
835832
func.handle,
836-
&mut BNVariable::from(var),
833+
&BNVariable::from(var),
837834
self.as_raw(),
838835
expr_index,
839836
size,
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
use std::path::PathBuf;
2+
3+
use binaryninja::architecture::CoreArchitecture;
4+
use binaryninja::binary_view::{BinaryView, BinaryViewExt};
5+
use binaryninja::disassembly::{
6+
DisassemblySettings, DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind,
7+
};
8+
use binaryninja::function::Function;
9+
use binaryninja::headless::Session;
10+
use binaryninja::high_level_il::{HighLevelILFunction, HighLevelInstructionIndex};
11+
use binaryninja::language_representation::{
12+
create_language_representation_function, register_language_representation_function_type,
13+
register_line_formatter, CoreLanguageRepresentationFunction,
14+
CoreLanguageRepresentationFunctionType, CoreLineFormatter,
15+
CustomLanguageRepresentationFunction, CustomLanguageRepresentationFunctionType,
16+
CustomLineFormater, HighLevelILTokenEmitter, LineFormatterSettings, OperatorPrecedence,
17+
};
18+
use binaryninja::platform::Platform;
19+
use binaryninja::rc::Ref;
20+
use binaryninja::type_container::TypeContainer;
21+
use binaryninja::type_parser::{
22+
register_type_parser, CoreTypeParser, TypeParser, TypeParserError, TypeParserOption,
23+
};
24+
use binaryninja::type_printer::{
25+
register_type_printer, CoreTypePrinter, TokenEscapingType, TypeDefinitionLine, TypePrinter,
26+
};
27+
use binaryninja::types::{QualifiedName, QualifiedNameAndType, Type};
28+
29+
struct MyLangRepr {}
30+
struct MyLangReprType {
31+
core: CoreLanguageRepresentationFunctionType,
32+
printer: CoreTypePrinter,
33+
parser: CoreTypeParser,
34+
line_formatter: CoreLineFormatter,
35+
}
36+
struct MyTypePrinter {}
37+
struct MyTypeParser {}
38+
struct MyLineFormatter {}
39+
40+
impl CustomLanguageRepresentationFunction for MyLangRepr {
41+
fn init_token_emitter(&self, _tokens: &HighLevelILTokenEmitter) {}
42+
43+
fn expr_text(
44+
&self,
45+
il: &HighLevelILFunction,
46+
expr_index: HighLevelInstructionIndex,
47+
tokens: &HighLevelILTokenEmitter,
48+
_settings: &DisassemblySettings,
49+
_as_full_ast: bool,
50+
_precedence: OperatorPrecedence,
51+
_statement: bool,
52+
) {
53+
let instr = il.instruction_from_expr_index(expr_index).unwrap();
54+
let instr = instr.lift();
55+
use binaryninja::high_level_il::HighLevelILLiftedInstructionKind::*;
56+
match &instr.kind {
57+
Block(block) => {
58+
tokens.append(InstructionTextToken::new(
59+
format!("block {}\n", block.body.len()),
60+
InstructionTextTokenKind::Text,
61+
));
62+
for block_inst in &block.body {
63+
self.expr_text(
64+
il,
65+
block_inst.expr_index,
66+
tokens,
67+
_settings,
68+
_as_full_ast,
69+
_precedence,
70+
_statement,
71+
);
72+
}
73+
}
74+
Unimpl | Unreachable | Undef => panic!(),
75+
_kind => {
76+
tokens.append(InstructionTextToken::new(
77+
format!("other instr {:x}\n", instr.address),
78+
InstructionTextTokenKind::Text,
79+
));
80+
}
81+
}
82+
}
83+
84+
fn begin_lines(
85+
&self,
86+
_il: &HighLevelILFunction,
87+
_expr_index: HighLevelInstructionIndex,
88+
_tokens: &HighLevelILTokenEmitter,
89+
) {
90+
}
91+
92+
fn end_lines(
93+
&self,
94+
_il: &HighLevelILFunction,
95+
_expr_index: HighLevelInstructionIndex,
96+
_tokens: &HighLevelILTokenEmitter,
97+
) {
98+
}
99+
100+
fn comment_start_string(&self) -> &str {
101+
"/* "
102+
}
103+
104+
fn comment_end_string(&self) -> &str {
105+
" */"
106+
}
107+
108+
fn annotation_start_string(&self) -> &str {
109+
"{"
110+
}
111+
112+
fn annotation_end_string(&self) -> &str {
113+
"}"
114+
}
115+
}
116+
117+
impl CustomLanguageRepresentationFunctionType for MyLangReprType {
118+
fn create(
119+
&self,
120+
arch: &CoreArchitecture,
121+
func: &Function,
122+
high_level_il: &HighLevelILFunction,
123+
) -> CoreLanguageRepresentationFunction {
124+
create_language_representation_function(
125+
MyLangRepr {},
126+
&self.core,
127+
arch,
128+
func,
129+
high_level_il,
130+
)
131+
}
132+
133+
fn is_valid(&self, _view: &BinaryView) -> bool {
134+
true
135+
}
136+
137+
fn type_printer(&self) -> &CoreTypePrinter {
138+
&self.printer
139+
}
140+
141+
fn type_parser(&self) -> &CoreTypeParser {
142+
&self.parser
143+
}
144+
145+
fn line_formatter(&self) -> &CoreLineFormatter {
146+
&self.line_formatter
147+
}
148+
149+
fn function_type_tokens(
150+
&self,
151+
_func: &Function,
152+
_settings: &DisassemblySettings,
153+
) -> Vec<DisassemblyTextLine> {
154+
todo!()
155+
}
156+
}
157+
158+
impl TypePrinter for MyTypePrinter {
159+
fn get_type_tokens<T: Into<QualifiedName>>(
160+
&self,
161+
_type_: Ref<Type>,
162+
_platform: Option<Ref<Platform>>,
163+
_name: T,
164+
_base_confidence: u8,
165+
_escaping: TokenEscapingType,
166+
) -> Option<Vec<InstructionTextToken>> {
167+
Some(vec![InstructionTextToken::new(
168+
"SomeType",
169+
InstructionTextTokenKind::Text,
170+
)])
171+
}
172+
173+
fn get_type_tokens_before_name(
174+
&self,
175+
_type_: Ref<Type>,
176+
_platform: Option<Ref<Platform>>,
177+
_base_confidence: u8,
178+
_parent_type: Option<Ref<Type>>,
179+
_escaping: TokenEscapingType,
180+
) -> Option<Vec<InstructionTextToken>> {
181+
Some(vec![InstructionTextToken::new(
182+
"<name>",
183+
InstructionTextTokenKind::Text,
184+
)])
185+
}
186+
187+
fn get_type_tokens_after_name(
188+
&self,
189+
_type_: Ref<Type>,
190+
_platform: Option<Ref<Platform>>,
191+
_base_confidence: u8,
192+
_parent_type: Option<Ref<Type>>,
193+
_escaping: TokenEscapingType,
194+
) -> Option<Vec<binaryninja::disassembly::InstructionTextToken>> {
195+
Some(vec![InstructionTextToken::new(
196+
"</name>",
197+
InstructionTextTokenKind::Text,
198+
)])
199+
}
200+
201+
fn get_type_string<T: Into<QualifiedName>>(
202+
&self,
203+
_type_: Ref<Type>,
204+
_platform: Option<Ref<Platform>>,
205+
_name: T,
206+
_escaping: TokenEscapingType,
207+
) -> Option<String> {
208+
None
209+
}
210+
211+
fn get_type_string_before_name(
212+
&self,
213+
_type_: Ref<Type>,
214+
_platform: Option<Ref<Platform>>,
215+
_escaping: TokenEscapingType,
216+
) -> Option<String> {
217+
None
218+
}
219+
220+
fn get_type_string_after_name(
221+
&self,
222+
_type_: Ref<Type>,
223+
_platform: Option<Ref<Platform>>,
224+
_escaping: TokenEscapingType,
225+
) -> Option<String> {
226+
None
227+
}
228+
229+
fn get_type_lines<T: Into<QualifiedName>>(
230+
&self,
231+
_type_: Ref<Type>,
232+
_types: &TypeContainer,
233+
_name: T,
234+
_padding_cols: isize,
235+
_collapsed: bool,
236+
_escaping: TokenEscapingType,
237+
) -> Option<Vec<TypeDefinitionLine>> {
238+
None
239+
}
240+
241+
fn print_all_types(
242+
&self,
243+
_names: Vec<QualifiedName>,
244+
_types: Vec<Ref<Type>>,
245+
_data: Ref<BinaryView>,
246+
_padding_cols: isize,
247+
_escaping: TokenEscapingType,
248+
) -> Option<String> {
249+
None
250+
}
251+
}
252+
253+
impl TypeParser for MyTypeParser {
254+
fn get_option_text(&self, _option: TypeParserOption, _value: &str) -> Option<String> {
255+
None
256+
}
257+
258+
fn preprocess_source(
259+
&self,
260+
_source: &str,
261+
_file_name: &str,
262+
_platform: &binaryninja::platform::Platform,
263+
_existing_types: &TypeContainer,
264+
_options: &[String],
265+
_include_dirs: &[String],
266+
) -> Result<String, Vec<binaryninja::type_parser::TypeParserError>> {
267+
todo!()
268+
}
269+
270+
fn parse_types_from_source(
271+
&self,
272+
_source: &str,
273+
_file_name: &str,
274+
_platform: &binaryninja::platform::Platform,
275+
_existing_types: &TypeContainer,
276+
_options: &[String],
277+
_include_dirs: &[String],
278+
_auto_type_source: &str,
279+
) -> Result<
280+
binaryninja::type_parser::TypeParserResult,
281+
Vec<binaryninja::type_parser::TypeParserError>,
282+
> {
283+
todo!()
284+
}
285+
286+
fn parse_type_string(
287+
&self,
288+
_source: &str,
289+
_platform: &binaryninja::platform::Platform,
290+
_existing_types: &TypeContainer,
291+
) -> Result<QualifiedNameAndType, Vec<TypeParserError>> {
292+
todo!()
293+
}
294+
}
295+
296+
impl CustomLineFormater for MyLineFormatter {
297+
fn format_lines(
298+
&self,
299+
lines: &[DisassemblyTextLine],
300+
_settings: &LineFormatterSettings,
301+
) -> Vec<DisassemblyTextLine> {
302+
lines.to_vec()
303+
}
304+
}
305+
306+
#[test]
307+
fn test_custom_language_representation() {
308+
const LANG_REPR_NAME: &str = "test_lang_repr";
309+
let _session = Session::new().expect("Failed to initialize session");
310+
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
311+
let (_, printer) = register_type_printer("my_type_printer", MyTypePrinter {});
312+
let (_, parser) = register_type_parser("my_type_parser", MyTypeParser {});
313+
let line_formatter = register_line_formatter("my_line_formatter", MyLineFormatter {});
314+
let my_repr = register_language_representation_function_type(
315+
|core| MyLangReprType {
316+
core,
317+
printer,
318+
parser,
319+
line_formatter,
320+
},
321+
LANG_REPR_NAME,
322+
);
323+
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
324+
let func = view
325+
.function_at(&view.default_platform().unwrap(), 0x36760)
326+
.unwrap();
327+
let _repr = my_repr.create(
328+
&view.default_arch().unwrap().as_ref(),
329+
&func,
330+
&func.high_level_il(false).unwrap(),
331+
);
332+
let il = func.high_level_il(false).unwrap();
333+
334+
let settings = DisassemblySettings::new();
335+
let root_idx = il.root_instruction_index();
336+
let result = _repr.linear_lines(&il, root_idx, &settings, false);
337+
let output: String = result.iter().map(|dis| dis.to_string()).collect();
338+
let _repr = binaryninja::language_representation::get_function_language_representation(
339+
&func,
340+
LANG_REPR_NAME,
341+
)
342+
.unwrap();
343+
assert_eq!(
344+
format!("{output}"),
345+
"block 26
346+
other instr 36775
347+
other instr 3679e
348+
other instr 3679e
349+
other instr 367ba
350+
other instr 367e6
351+
other instr 3682f
352+
other instr 3682f
353+
other instr 36834
354+
other instr 3683e
355+
other instr 3684e
356+
other instr 36867
357+
other instr 36881
358+
other instr 36881
359+
other instr 36881
360+
other instr 36896
361+
other instr 368a0
362+
other instr 368bb
363+
other instr 368d2
364+
other instr 3694a
365+
other instr 36960
366+
other instr 369e1
367+
other instr 369ec
368+
other instr 36a2e
369+
other instr 36ab5
370+
other instr 36abd
371+
other instr 36ac2
372+
"
373+
);
374+
}

0 commit comments

Comments
 (0)