1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
use binaryninja::binary_view::{AnalysisState, BinaryViewBase, BinaryViewExt};
use binaryninja::function::Function;
use binaryninja::headless::Session;
use binaryninja::main_thread::execute_on_main_thread_and_wait;
use binaryninja::platform::Platform;
use binaryninja::rc::Ref;
use binaryninja::symbol::{Symbol, SymbolBuilder, SymbolType};
use std::collections::BTreeMap;
use std::path::PathBuf;
#[test]
fn test_binary_loading() {
let _session = Session::new().expect("Failed to initialize session");
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
assert!(view.has_initial_analysis(), "No initial analysis");
assert_eq!(view.analysis_progress().state, AnalysisState::IdleState);
assert_eq!(view.file().is_analysis_changed(), true);
assert_eq!(view.file().is_database_backed(), false);
}
#[test]
fn test_binary_saving() {
let _session = Session::new().expect("Failed to initialize session");
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
// Verify the contents before we modify.
let contents_addr = view.original_image_base() + 0x1560;
let original_contents = view.read_vec(contents_addr, 4);
assert_eq!(original_contents, [0x00, 0xf1, 0x00, 0x00]);
assert_eq!(view.write(contents_addr, &[0xff, 0xff, 0xff, 0xff]), 4);
// Verify that we modified the binary
let modified_contents = view.read_vec(contents_addr, 4);
assert_eq!(modified_contents, [0xff, 0xff, 0xff, 0xff]);
// HACK: To prevent us from deadlocking in save_to_path, we wait for all main thread actions to finish.
execute_on_main_thread_and_wait(|| {});
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
let temp_path = temp_dir.path().join("atox.obj.new");
// Save the modified file
assert!(view.save_to_path(&temp_path));
// Verify that the file exists and is modified.
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
assert_eq!(
new_view.read_vec(contents_addr, 4),
[0xff, 0xff, 0xff, 0xff]
);
}
#[test]
fn test_binary_saving_database() {
let _session = Session::new().expect("Failed to initialize session");
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
// Update a symbol to verify modification
let entry_function = view
.entry_point_function()
.expect("Failed to get entry point function");
let new_entry_func_symbol =
SymbolBuilder::new(SymbolType::Function, "test", entry_function.start()).create();
view.define_user_symbol(&new_entry_func_symbol);
// Verify that we modified the binary
assert_eq!(entry_function.symbol().raw_name().to_string_lossy(), "test");
// Save the modified database.
let temp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
let temp_path = temp_dir.path().join("atox.obj.bndb");
assert!(view.file().create_database(&temp_path));
// Verify that the file exists and is modified.
let new_view = binaryninja::load(temp_path).expect("Failed to load new view");
let new_entry_function = new_view
.entry_point_function()
.expect("Failed to get entry point function");
assert_eq!(
new_entry_function.symbol().raw_name().to_string_lossy(),
"test"
);
}
#[test]
fn test_binary_view_strings() {
let _session = Session::new().expect("Failed to initialize session");
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view");
let image_base = view.original_image_base();
assert!(view.strings().len() > 0);
let str_15dc = view
.strings()
.iter()
.find(|s| {
let buffer = view
.read_buffer(s.start, s.length)
.expect("Failed to read string reference");
let str = buffer.to_escaped_string(false, false);
str.contains("Microsoft")
})
.expect("Failed to find string 'Microsoft (R) Optimizing Compiler'");
assert_eq!(str_15dc.start, image_base + 0x15dc);
assert_eq!(str_15dc.length, 33);
}
// These are the target files present in OUT_DIR
// Add the files to fixtures/bin
static TARGET_FILES: [&str; 2] = ["atox.obj", "atof.obj"];
// This is what we store to check if a function matches the expected function.
// See `test_deterministic_functions` for details.
#[derive(Debug, PartialEq)]
pub struct FunctionSnapshot {
platform: Ref<Platform>,
symbol: Ref<Symbol>,
}
impl From<&Function> for FunctionSnapshot {
fn from(func: &Function) -> Self {
Self {
platform: func.platform().to_owned(),
symbol: func.symbol().to_owned(),
}
}
}
#[test]
fn test_deterministic_functions() {
let session = Session::new().expect("Failed to initialize session");
let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
for file_name in TARGET_FILES {
let path = out_dir.join(file_name);
let view = session.load(&path).expect("Failed to load view");
assert_eq!(view.analysis_progress().state, AnalysisState::IdleState);
let functions: BTreeMap<u64, FunctionSnapshot> = view
.functions()
.iter()
.map(|f| (f.start(), FunctionSnapshot::from(f.as_ref())))
.collect();
let snapshot_name = path.file_stem().unwrap().to_str().unwrap();
insta::assert_debug_snapshot!(snapshot_name, functions);
}
}
|