binaryninja/
workflow.rs

1use binaryninjacore_sys::*;
2
3// Needed for documentation.
4#[allow(unused)]
5use crate::binary_view::{memory_map::MemoryMap, BinaryViewBase, BinaryViewExt};
6
7use crate::basic_block::BasicBlock;
8use crate::binary_view::BinaryView;
9use crate::flowgraph::FlowGraph;
10use crate::function::{Function, NativeBlock};
11use crate::high_level_il::HighLevelILFunction;
12use crate::low_level_il::{LowLevelILMutableFunction, LowLevelILRegularFunction};
13use crate::medium_level_il::MediumLevelILFunction;
14use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
15use crate::string::{BnString, IntoCStr};
16use std::ffi::c_char;
17use std::ptr;
18use std::ptr::NonNull;
19
20pub mod activity;
21pub use activity::Activity;
22
23#[repr(transparent)]
24/// The AnalysisContext struct is used to represent the current state of
25/// analysis for a given function. It allows direct modification of IL and other
26/// analysis information.
27pub struct AnalysisContext {
28    handle: NonNull<BNAnalysisContext>,
29}
30
31impl AnalysisContext {
32    pub(crate) unsafe fn from_raw(handle: NonNull<BNAnalysisContext>) -> Self {
33        Self { handle }
34    }
35
36    #[allow(unused)]
37    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNAnalysisContext>) -> Ref<Self> {
38        Ref::new(Self { handle })
39    }
40
41    /// BinaryView for the current AnalysisContext
42    pub fn view(&self) -> Ref<BinaryView> {
43        let result = unsafe { BNAnalysisContextGetBinaryView(self.handle.as_ptr()) };
44        assert!(!result.is_null());
45        unsafe { BinaryView::ref_from_raw(result) }
46    }
47
48    /// [`Function`] for the current AnalysisContext
49    pub fn function(&self) -> Ref<Function> {
50        let result = unsafe { BNAnalysisContextGetFunction(self.handle.as_ptr()) };
51        assert!(!result.is_null());
52        unsafe { Function::ref_from_raw(result) }
53    }
54
55    /// [`LowLevelILMutableFunction`] used to represent Lifted Level IL
56    pub unsafe fn lifted_il_function(&self) -> Option<Ref<LowLevelILMutableFunction>> {
57        let result = unsafe { BNAnalysisContextGetLiftedILFunction(self.handle.as_ptr()) };
58        unsafe {
59            Some(LowLevelILMutableFunction::ref_from_raw(
60                NonNull::new(result)?.as_ptr(),
61            ))
62        }
63    }
64
65    pub fn set_lifted_il_function(&self, value: &LowLevelILRegularFunction) {
66        unsafe { BNSetLiftedILFunction(self.handle.as_ptr(), value.handle) }
67    }
68
69    /// [`LowLevelILMutableFunction`] used to represent Low Level IL
70    pub unsafe fn llil_function(&self) -> Option<Ref<LowLevelILMutableFunction>> {
71        let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.handle.as_ptr()) };
72        unsafe {
73            Some(LowLevelILMutableFunction::ref_from_raw(
74                NonNull::new(result)?.as_ptr(),
75            ))
76        }
77    }
78
79    pub fn set_llil_function(&self, value: &LowLevelILRegularFunction) {
80        unsafe { BNSetLowLevelILFunction(self.handle.as_ptr(), value.handle) }
81    }
82
83    /// [`MediumLevelILFunction`] used to represent Medium Level IL
84    pub fn mlil_function(&self) -> Option<Ref<MediumLevelILFunction>> {
85        let result = unsafe { BNAnalysisContextGetMediumLevelILFunction(self.handle.as_ptr()) };
86        unsafe {
87            Some(MediumLevelILFunction::ref_from_raw(
88                NonNull::new(result)?.as_ptr(),
89            ))
90        }
91    }
92
93    pub fn set_mlil_function(&self, value: &MediumLevelILFunction) {
94        // TODO: Mappings FFI
95        unsafe {
96            BNSetMediumLevelILFunction(
97                self.handle.as_ptr(),
98                value.handle,
99                ptr::null_mut(),
100                0,
101                ptr::null_mut(),
102                0,
103            )
104        }
105    }
106
107    /// [`HighLevelILFunction`] used to represent High Level IL
108    pub fn hlil_function(&self, full_ast: bool) -> Option<Ref<HighLevelILFunction>> {
109        let result = unsafe { BNAnalysisContextGetHighLevelILFunction(self.handle.as_ptr()) };
110        unsafe {
111            Some(HighLevelILFunction::ref_from_raw(
112                NonNull::new(result)?.as_ptr(),
113                full_ast,
114            ))
115        }
116    }
117
118    pub fn inform(&self, request: &str) -> bool {
119        let request = request.to_cstr();
120        unsafe { BNAnalysisContextInform(self.handle.as_ptr(), request.as_ptr()) }
121    }
122
123    pub fn set_basic_blocks<I>(&self, blocks: I)
124    where
125        I: IntoIterator<Item = BasicBlock<NativeBlock>>,
126    {
127        let blocks: Vec<_> = blocks.into_iter().collect();
128        let mut blocks_raw: Vec<*mut BNBasicBlock> =
129            blocks.iter().map(|block| block.handle).collect();
130        unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) }
131    }
132}
133
134impl ToOwned for AnalysisContext {
135    type Owned = Ref<Self>;
136
137    fn to_owned(&self) -> Self::Owned {
138        unsafe { RefCountable::inc_ref(self) }
139    }
140}
141
142unsafe impl RefCountable for AnalysisContext {
143    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
144        Ref::new(Self {
145            handle: NonNull::new(BNNewAnalysisContextReference(handle.handle.as_ptr()))
146                .expect("valid handle"),
147        })
148    }
149
150    unsafe fn dec_ref(handle: &Self) {
151        BNFreeAnalysisContext(handle.handle.as_ptr());
152    }
153}
154
155#[repr(transparent)]
156pub struct Workflow {
157    handle: NonNull<BNWorkflow>,
158}
159
160impl Workflow {
161    pub(crate) unsafe fn from_raw(handle: NonNull<BNWorkflow>) -> Self {
162        Self { handle }
163    }
164
165    pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNWorkflow>) -> Ref<Self> {
166        Ref::new(Self { handle })
167    }
168
169    /// Create a new unregistered [Workflow] with no activities.
170    /// Returns a [WorkflowBuilder] that can be used to configure and register the new [Workflow].
171    ///
172    /// To get a copy of an existing registered [Workflow] use [Workflow::clone_to].
173    pub fn build(name: &str) -> WorkflowBuilder {
174        let name = name.to_cstr();
175        let result = unsafe { BNCreateWorkflow(name.as_ptr()) };
176        WorkflowBuilder {
177            handle: unsafe { Workflow::ref_from_raw(NonNull::new(result).unwrap()) },
178        }
179    }
180
181    /// Make a new unregistered [Workflow], copying all activities and the execution strategy.
182    /// Returns a [WorkflowBuilder] that can be used to configure and register the new [Workflow].
183    ///
184    /// * `name` - the name for the new [Workflow]
185    pub fn clone_to(&self, name: &str) -> WorkflowBuilder {
186        self.clone_to_with_root(name, "")
187    }
188
189    /// Make a new unregistered [Workflow], copying all activities, within `root_activity`, and the execution strategy.
190    ///
191    /// * `name` - the name for the new [Workflow]
192    /// * `root_activity` - perform the clone operation with this activity as the root
193    pub fn clone_to_with_root(&self, name: &str, root_activity: &str) -> WorkflowBuilder {
194        let raw_name = name.to_cstr();
195        let activity = root_activity.to_cstr();
196        let workflow = unsafe {
197            Self::ref_from_raw(
198                NonNull::new(BNWorkflowClone(
199                    self.handle.as_ptr(),
200                    raw_name.as_ptr(),
201                    activity.as_ptr(),
202                ))
203                .unwrap(),
204            )
205        };
206        WorkflowBuilder { handle: workflow }
207    }
208
209    /// Get an existing [Workflow] by name.
210    pub fn get(name: &str) -> Option<Ref<Workflow>> {
211        let name = name.to_cstr();
212        let result = unsafe { BNWorkflowGet(name.as_ptr()) };
213        let handle = NonNull::new(result)?;
214        Some(unsafe { Workflow::ref_from_raw(handle) })
215    }
216
217    /// Clone the existing [Workflow] named `name`.
218    /// Returns a [WorkflowBuilder] that can be used to configure and register the new [Workflow].
219    pub fn cloned(name: &str) -> Option<WorkflowBuilder> {
220        Self::get(name).map(|workflow| workflow.clone_to(name))
221    }
222
223    /// List of all registered [Workflow]'s
224    pub fn list() -> Array<Workflow> {
225        let mut count = 0;
226        let result = unsafe { BNGetWorkflowList(&mut count) };
227        assert!(!result.is_null());
228        unsafe { Array::new(result, count, ()) }
229    }
230
231    pub fn name(&self) -> String {
232        let result = unsafe { BNGetWorkflowName(self.handle.as_ptr()) };
233        assert!(!result.is_null());
234        unsafe { BnString::into_string(result) }
235    }
236
237    /// Determine if an Activity exists in this [Workflow].
238    pub fn contains(&self, activity: &str) -> bool {
239        let activity = activity.to_cstr();
240        unsafe { BNWorkflowContains(self.handle.as_ptr(), activity.as_ptr()) }
241    }
242
243    /// Retrieve the configuration as an adjacency list in JSON for the [Workflow].
244    pub fn configuration(&self) -> String {
245        self.configuration_with_activity("")
246    }
247
248    /// Retrieve the configuration as an adjacency list in JSON for the
249    /// [Workflow], just for the given `activity`.
250    ///
251    /// `activity` - return the configuration for the `activity`
252    pub fn configuration_with_activity(&self, activity: &str) -> String {
253        let activity = activity.to_cstr();
254        let result = unsafe { BNWorkflowGetConfiguration(self.handle.as_ptr(), activity.as_ptr()) };
255        assert!(!result.is_null());
256        unsafe { BnString::into_string(result) }
257    }
258
259    /// Whether this [Workflow] is registered or not. A [Workflow] becomes immutable once registered.
260    pub fn registered(&self) -> bool {
261        unsafe { BNWorkflowIsRegistered(self.handle.as_ptr()) }
262    }
263
264    pub fn size(&self) -> usize {
265        unsafe { BNWorkflowSize(self.handle.as_ptr()) }
266    }
267
268    /// Retrieve the Activity object for the specified `name`.
269    pub fn activity(&self, name: &str) -> Option<Ref<Activity>> {
270        let name = name.to_cstr();
271        let result = unsafe { BNWorkflowGetActivity(self.handle.as_ptr(), name.as_ptr()) };
272        NonNull::new(result).map(|a| unsafe { Activity::ref_from_raw(a) })
273    }
274
275    /// Retrieve the list of activity roots for the [Workflow], or if
276    /// specified just for the given `activity`.
277    ///
278    /// * `activity` - if specified, return the roots for the `activity`
279    pub fn activity_roots(&self, activity: &str) -> Array<BnString> {
280        let activity = activity.to_cstr();
281        let mut count = 0;
282        let result = unsafe {
283            BNWorkflowGetActivityRoots(self.handle.as_ptr(), activity.as_ptr(), &mut count)
284        };
285        assert!(!result.is_null());
286        unsafe { Array::new(result as *mut *mut c_char, count, ()) }
287    }
288
289    /// Retrieve the list of all activities, or optionally a filtered list.
290    ///
291    /// * `activity` - if specified, return the direct children and optionally the descendants of the `activity` (includes `activity`)
292    /// * `immediate` - whether to include only direct children of `activity` or all descendants
293    pub fn subactivities(&self, activity: &str, immediate: bool) -> Array<BnString> {
294        let activity = activity.to_cstr();
295        let mut count = 0;
296        let result = unsafe {
297            BNWorkflowGetSubactivities(
298                self.handle.as_ptr(),
299                activity.as_ptr(),
300                immediate,
301                &mut count,
302            )
303        };
304        assert!(!result.is_null());
305        unsafe { Array::new(result as *mut *mut c_char, count, ()) }
306    }
307
308    /// Generate a FlowGraph object for the current [Workflow] and optionally show it in the UI.
309    ///
310    /// * `activity` - if specified, generate the Flowgraph using `activity` as the root
311    /// * `sequential` - whether to generate a **Composite** or **Sequential** style graph
312    pub fn graph(&self, activity: &str, sequential: Option<bool>) -> Option<Ref<FlowGraph>> {
313        let sequential = sequential.unwrap_or(false);
314        let activity = activity.to_cstr();
315        let graph =
316            unsafe { BNWorkflowGetGraph(self.handle.as_ptr(), activity.as_ptr(), sequential) };
317        if graph.is_null() {
318            return None;
319        }
320        Some(unsafe { FlowGraph::ref_from_raw(graph) })
321    }
322
323    /// Not yet implemented.
324    pub fn show_metrics(&self) {
325        unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"metrics".as_ptr()) }
326    }
327
328    /// Show the Workflow topology in the UI.
329    pub fn show_topology(&self) {
330        unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"topology".as_ptr()) }
331    }
332
333    /// Not yet implemented.
334    pub fn show_trace(&self) {
335        unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"trace".as_ptr()) }
336    }
337}
338
339impl ToOwned for Workflow {
340    type Owned = Ref<Self>;
341
342    fn to_owned(&self) -> Self::Owned {
343        unsafe { RefCountable::inc_ref(self) }
344    }
345}
346
347unsafe impl RefCountable for Workflow {
348    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
349        Ref::new(Self {
350            handle: NonNull::new(BNNewWorkflowReference(handle.handle.as_ptr()))
351                .expect("valid handle"),
352        })
353    }
354
355    unsafe fn dec_ref(handle: &Self) {
356        BNFreeWorkflow(handle.handle.as_ptr());
357    }
358}
359
360impl CoreArrayProvider for Workflow {
361    type Raw = *mut BNWorkflow;
362    type Context = ();
363    type Wrapped<'a> = Guard<'a, Workflow>;
364}
365
366unsafe impl CoreArrayProviderInner for Workflow {
367    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
368        BNFreeWorkflowList(raw, count)
369    }
370
371    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
372        Guard::new(
373            Workflow::from_raw(NonNull::new(*raw).expect("valid handle")),
374            context,
375        )
376    }
377}
378
379#[must_use = "Workflow is not registered until `register` is called"]
380pub struct WorkflowBuilder {
381    handle: Ref<Workflow>,
382}
383
384impl WorkflowBuilder {
385    fn raw_handle(&self) -> *mut BNWorkflow {
386        self.handle.handle.as_ptr()
387    }
388
389    /// Register an [Activity] with this Workflow and insert it before the designated position.
390    ///
391    /// * `activity` - the [Activity] to register
392    /// * `sibling` - the activity to insert the new activity before
393    pub fn activity_before(self, activity: &Activity, sibling: &str) -> Result<Self, ()> {
394        self.register_activity(activity)?
395            .insert(sibling, vec![activity.name()])
396    }
397
398    /// Register an [Activity] with this Workflow and insert it in the designated position.
399    ///
400    /// * `activity` - the [Activity] to register
401    /// * `sibling` - the activity to insert the new activity after
402    pub fn activity_after(self, activity: &Activity, sibling: &str) -> Result<Self, ()> {
403        self.register_activity(activity)?
404            .insert_after(sibling, vec![activity.name()])
405    }
406
407    /// Register an [Activity] with this Workflow.
408    ///
409    /// * `activity` - the [Activity] to register
410    pub fn register_activity(self, activity: &Activity) -> Result<Self, ()> {
411        self.register_activity_with_subactivities::<Vec<String>>(activity, vec![])
412    }
413
414    /// Register an [Activity] with this Workflow.
415    ///
416    /// * `activity` - the [Activity] to register
417    /// * `subactivities` - the list of Activities to assign
418    pub fn register_activity_with_subactivities<I>(
419        self,
420        activity: &Activity,
421        subactivities: I,
422    ) -> Result<Self, ()>
423    where
424        I: IntoIterator,
425        I::Item: IntoCStr,
426    {
427        let subactivities_raw: Vec<_> = subactivities.into_iter().map(|x| x.to_cstr()).collect();
428        let mut subactivities_ptr: Vec<*const _> =
429            subactivities_raw.iter().map(|x| x.as_ptr()).collect();
430        let result = unsafe {
431            BNWorkflowRegisterActivity(
432                self.raw_handle(),
433                activity.handle.as_ptr(),
434                subactivities_ptr.as_mut_ptr(),
435                subactivities_ptr.len(),
436            )
437        };
438        let Some(activity_ptr) = NonNull::new(result) else {
439            return Err(());
440        };
441        let _ = unsafe { Activity::ref_from_raw(activity_ptr) };
442        Ok(self)
443    }
444
445    /// Register this [Workflow], making it immutable and available for use.
446    pub fn register(self) -> Result<Ref<Workflow>, ()> {
447        self.register_with_config("")
448    }
449
450    /// Register this [Workflow], making it immutable and available for use.
451    ///
452    /// * `configuration` - a JSON representation of the workflow configuration
453    pub fn register_with_config(self, config: &str) -> Result<Ref<Workflow>, ()> {
454        // TODO: We need to hide the JSON here behind a sensible/typed API.
455        let config = config.to_cstr();
456        if unsafe { BNRegisterWorkflow(self.raw_handle(), config.as_ptr()) } {
457            Ok(self.handle)
458        } else {
459            Err(())
460        }
461    }
462
463    /// Assign the list of `activities` as the new set of children for the specified `activity`.
464    ///
465    /// * `activity` - the Activity node to assign children
466    /// * `activities` - the list of Activities to assign
467    pub fn subactivities<I>(self, activity: &str, activities: I) -> Result<Self, ()>
468    where
469        I: IntoIterator,
470        I::Item: IntoCStr,
471    {
472        let activity = activity.to_cstr();
473        let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect();
474        let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect();
475        let result = unsafe {
476            BNWorkflowAssignSubactivities(
477                self.raw_handle(),
478                activity.as_ptr(),
479                input_list_ptr.as_mut_ptr(),
480                input_list.len(),
481            )
482        };
483        if result {
484            Ok(self)
485        } else {
486            Err(())
487        }
488    }
489
490    /// Remove all Activity nodes from this [Workflow].
491    pub fn clear(self) -> Result<Self, ()> {
492        let result = unsafe { BNWorkflowClear(self.raw_handle()) };
493        if result {
494            Ok(self)
495        } else {
496            Err(())
497        }
498    }
499
500    /// Insert the list of `activities` before the specified `activity` and at the same level.
501    ///
502    /// * `activity` - the Activity node for which to insert `activities` before
503    /// * `activities` - the list of Activities to insert
504    pub fn insert<I>(self, activity: &str, activities: I) -> Result<Self, ()>
505    where
506        I: IntoIterator,
507        I::Item: IntoCStr,
508    {
509        let activity = activity.to_cstr();
510        let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect();
511        let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect();
512        let result = unsafe {
513            BNWorkflowInsert(
514                self.raw_handle(),
515                activity.as_ptr(),
516                input_list_ptr.as_mut_ptr(),
517                input_list.len(),
518            )
519        };
520        if result {
521            Ok(self)
522        } else {
523            Err(())
524        }
525    }
526
527    /// Insert the list of `activities` after the specified `activity` and at the same level.
528    ///
529    /// * `activity` - the Activity node for which to insert `activities` after
530    /// * `activities` - the list of Activities to insert
531    pub fn insert_after<I>(self, activity: &str, activities: I) -> Result<Self, ()>
532    where
533        I: IntoIterator,
534        I::Item: IntoCStr,
535    {
536        let activity = activity.to_cstr();
537        let input_list: Vec<_> = activities.into_iter().map(|a| a.to_cstr()).collect();
538        let mut input_list_ptr: Vec<*const _> = input_list.iter().map(|x| x.as_ptr()).collect();
539        let result = unsafe {
540            BNWorkflowInsertAfter(
541                self.raw_handle(),
542                activity.as_ptr(),
543                input_list_ptr.as_mut_ptr(),
544                input_list.len(),
545            )
546        };
547        if result {
548            Ok(self)
549        } else {
550            Err(())
551        }
552    }
553
554    /// Remove the specified `activity`
555    pub fn remove(self, activity: &str) -> Result<Self, ()> {
556        let activity = activity.to_cstr();
557        let result = unsafe { BNWorkflowRemove(self.raw_handle(), activity.as_ptr()) };
558        if result {
559            Ok(self)
560        } else {
561            Err(())
562        }
563    }
564
565    /// Replace the specified `activity`.
566    ///
567    /// * `activity` - the Activity to replace
568    /// * `new_activity` - the replacement Activity
569    pub fn replace(self, activity: &str, new_activity: &str) -> Result<Self, ()> {
570        let activity = activity.to_cstr();
571        let new_activity = new_activity.to_cstr();
572        let result = unsafe {
573            BNWorkflowReplace(self.raw_handle(), activity.as_ptr(), new_activity.as_ptr())
574        };
575        if result {
576            Ok(self)
577        } else {
578            Err(())
579        }
580    }
581}