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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
|
use crate::cache::{
register_cache_destructor, ViewID, FUNCTION_CACHE, GUID_CACHE, MATCHED_FUNCTION_CACHE,
};
use crate::convert::{to_bn_symbol_at_address, to_bn_type};
use crate::matcher::{
invalidate_function_matcher_cache, Matcher, MatcherSettings, PlatformID, PLAT_MATCHER_CACHE,
};
use crate::{build_function, cache};
use binaryninja::binary_view::{BinaryView, BinaryViewExt};
use binaryninja::command::{Command, FunctionCommand};
use binaryninja::function::{Function, FunctionUpdateType};
use binaryninja::logger::Logger;
use binaryninja::rc::Ref;
use binaryninja::tags::TagType;
use binaryninja::ObjectDestructor;
use log::LevelFilter;
use warp::signature::function::constraints::FunctionConstraint;
use warp::signature::function::Function as WarpFunction;
mod add;
mod copy;
mod create;
mod find;
mod load;
mod types;
mod workflow;
// TODO: This icon is a little much
const TAG_ICON: &str = "🌏";
const TAG_NAME: &str = "WARP";
fn get_warp_tag_type(view: &BinaryView) -> Ref<TagType> {
view.tag_type_by_name(TAG_NAME)
.unwrap_or_else(|| view.create_tag_type(TAG_NAME, TAG_ICON))
}
// What happens to the function when it is matched.
// TODO: add user: bool
// TODO: Rename to markup_function or something.
pub fn on_matched_function(function: &Function, matched: &WarpFunction) {
let view = function.view();
// TODO: Using user symbols here is problematic
// TODO: For one they queue up a bunch of main thread actions
// TODO: Secondly by queueing up those main thread actions if you attempt to save the file
// TODO: Before the undo actions are done completing
view.define_user_symbol(&to_bn_symbol_at_address(
&view,
&matched.symbol,
function.symbol().address(),
));
function.set_user_type(&to_bn_type(&function.arch(), &matched.ty));
// TODO: Add metadata. (both binja metadata and warp metadata)
function.add_tag(
&get_warp_tag_type(&view),
matched.guid.to_string(),
None,
true,
None,
);
// Seems to be the only way to get the analysis update to work correctly.
function.mark_updates_required(FunctionUpdateType::FullAutoFunctionUpdate);
}
struct DebugFunction;
impl FunctionCommand for DebugFunction {
fn action(&self, _view: &BinaryView, func: &Function) {
if let Ok(llil) = func.low_level_il() {
log::info!("{:#?}", build_function(func, &llil));
}
}
fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
true
}
}
struct DebugMatcher;
impl FunctionCommand for DebugMatcher {
fn action(&self, _view: &BinaryView, function: &Function) {
let Ok(llil) = function.low_level_il() else {
log::error!("No LLIL for function 0x{:x}", function.start());
return;
};
let platform = function.platform();
// Build the matcher every time this is called to make sure we aren't in a bad state.
let matcher = Matcher::from_platform(platform);
let func = build_function(function, &llil);
// TODO: Clean this up.
if let Some(possible_matches) = matcher.functions.get(&func.guid) {
let print_constraint = |prefix: &str, constraint: &FunctionConstraint| {
log::info!(
" {} {} ({})",
prefix,
constraint
.to_owned()
.symbol
.map(|s| s.name)
.unwrap_or("*".to_string()),
constraint
.guid
.map(|g| g.to_string())
.unwrap_or("*".to_string())
);
};
for possible_match in possible_matches.value() {
log::info!("{} ({})", possible_match.symbol.name, possible_match.guid);
for constraint in &possible_match.constraints.call_sites {
print_constraint("CS", constraint);
}
for constraint in &possible_match.constraints.call_sites {
print_constraint("ADJ", constraint);
}
}
} else {
log::error!(
"No possible matches found for the function 0x{:x}",
function.start()
);
};
}
fn valid(&self, _view: &BinaryView, _function: &Function) -> bool {
true
}
}
struct DebugCache;
impl Command for DebugCache {
fn action(&self, view: &BinaryView) {
let view_id = ViewID::from(view);
let function_cache = FUNCTION_CACHE.get_or_init(Default::default);
if let Some(cache) = function_cache.get(&view_id) {
log::info!("View functions: {}", cache.cache.len());
}
let matched_function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default);
if let Some(cache) = matched_function_cache.get(&view_id) {
log::info!("View matched functions: {}", cache.cache.len());
}
let function_guid_cache = GUID_CACHE.get_or_init(Default::default);
if let Some(cache) = function_guid_cache.get(&view_id) {
log::info!("View function guids: {}", cache.cache.len());
}
let plat_cache = PLAT_MATCHER_CACHE.get_or_init(Default::default);
if let Some(plat) = view.default_platform() {
let platform_id = PlatformID::from(plat);
if let Some(cache) = plat_cache.get(&platform_id) {
log::info!("Platform functions: {}", cache.functions.len());
log::info!("Platform types: {}", cache.types.len());
log::info!("Platform settings: {:?}", cache.settings);
}
}
}
fn valid(&self, _view: &BinaryView) -> bool {
true
}
}
struct DebugInvalidateCache;
impl Command for DebugInvalidateCache {
fn action(&self, view: &BinaryView) {
invalidate_function_matcher_cache();
let destructor = cache::CacheDestructor {};
destructor.destruct_view(view);
log::info!("Invalidated all WARP caches...");
}
fn valid(&self, _view: &BinaryView) -> bool {
true
}
}
#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn CorePluginInit() -> bool {
Logger::new("WARP").with_level(LevelFilter::Debug).init();
// Register our matcher settings.
MatcherSettings::register();
// Make sure caches are flushed when the views get destructed.
register_cache_destructor();
workflow::insert_workflow();
binaryninja::command::register_command(
"WARP\\Run Matcher",
"Run the matcher manually",
workflow::RunMatcher {},
);
binaryninja::command::register_command(
"WARP\\Debug\\Cache",
"Debug cache sizes... because...",
DebugCache {},
);
binaryninja::command::register_command(
"WARP\\Debug\\Invalidate Caches",
"Invalidate all WARP caches",
DebugInvalidateCache {},
);
binaryninja::command::register_command_for_function(
"WARP\\Debug\\Function Signature",
"Print the entire signature for the function",
DebugFunction {},
);
binaryninja::command::register_command_for_function(
"WARP\\Debug\\Function Matcher",
"Print all possible matches for the function",
DebugMatcher {},
);
binaryninja::command::register_command(
"WARP\\Debug\\Apply Signature File Types",
"Load all types from a signature file and ignore functions",
types::LoadTypes {},
);
binaryninja::command::register_command(
"WARP\\Load Signature File",
"Load file into the matcher, this does NOT kick off matcher analysis",
load::LoadSignatureFile {},
);
binaryninja::command::register_command_for_function(
"WARP\\Copy Function GUID",
"Copy the computed GUID for the function",
copy::CopyFunctionGUID {},
);
binaryninja::command::register_command(
"WARP\\Find Function From GUID",
"Locate the function in the view using a GUID",
find::FindFunctionFromGUID {},
);
binaryninja::command::register_command(
"WARP\\Generate Signature File",
"Generates a signature file containing all binary view functions",
create::CreateSignatureFile {},
);
binaryninja::command::register_command_for_function(
"WARP\\Add Function Signature to File",
"Stores the signature for the function in the signature file",
add::AddFunctionSignature {},
);
true
}
|