import ctypes
import inspect
from typing import Generator, Optional, List, Tuple, Union, Mapping, Any, Dict, Iterator
from dataclasses import dataclass
from . import binaryview
from . import function
from . import _binaryninjacore as core
from . import types
[docs]
class Component:
"""
Components are objects that can contain Functions and other Components.
They can be queried for information about the functions contained within them.
Components have a Guid, which persistent across saves and loads of the database, and should be
used for retrieving components when such is required and a reference to the Component cannot be held.
"""
def __init__(self, handle=None):
assert handle is not None, "Cannot create component directly, run `bv.create_component?`"
self.handle = handle
self.guid = core.BNComponentGetGuid(self.handle)
def __eq__(self, other):
if not isinstance(other, Component):
return NotImplemented
return core.BNComponentsEqual(self.handle, other.handle)
def __ne__(self, other):
if not isinstance(other, Component):
return NotImplemented
return core.BNComponentsNotEqual(self.handle, other.handle)
def __repr__(self):
return f'<Component "{self.display_name}" "({self.guid[:8]}...")>'
def __del__(self):
if (hasattr(self, 'handle')):
core.BNFreeComponent(self.handle)
def __str__(self):
return self._sprawl_component(self)
def __hash__(self):
return hash(self.guid)
def _sprawl_component(self, c, depth=1, out=None):
"""
Recursive quick function to print out the component's tree of items
:param c: Current cycle's component. On initial call, pass `self`
:param depth: Current tree depth.
:param out: Current text
:return:
"""
_out = ([repr(c)] if not out else out.split('\n')) + [(' ' * depth + repr(f)) for f in c.functions]
_out += [' ' * (depth+1) + repr(i) for i in (c.get_referenced_data_variables() + c.get_referenced_types())]
for i in c.components:
_out.append(' ' * depth + repr(i))
_out = self._sprawl_component(i, depth+1, '\n'.join(_out)).split('\n')
return '\n'.join(_out)
[docs]
def add_function(self, func: 'function.Function') -> bool:
"""
Add function to this component.
:param func: Function to add
:return: True if function was successfully added.
"""
return core.BNComponentAddFunctionReference(self.handle, func.handle)
[docs]
def contains_function(self, func: 'function.Function') -> bool:
"""
Check whether this component contains a function.
:param func: Function to check
:return: True if this component contains the function.
"""
return core.BNComponentContainsFunction(self.handle, func.handle)
[docs]
def remove_function(self, func: 'function.Function') -> bool:
"""
Remove function from this component.
:param func: Function to remove
:return: True if function was successfully removed.
"""
return core.BNComponentRemoveFunctionReference(self.handle, func.handle)
[docs]
def add_component(self, component: 'Component') -> bool:
"""
Move component to this component. This will remove it from the old parent.
:param component: Component to add to this component.
:return: True if the component was successfully moved to this component
"""
return core.BNComponentAddComponent(self.handle, component.handle)
[docs]
def contains_component(self, component: 'Component') -> bool:
"""
Check whether this component contains a component.
:param component: Component to check
:return: True if this component contains the component.
"""
return core.BNComponentContainsComponent(self.handle, component.handle)
[docs]
def remove_component(self, component: 'Component') -> bool:
"""
Remove a component from the current component, moving it to the root.
This function has no effect when used from the root component.
Use `BinaryView.remove_component` to Remove a component from the tree entirely.
:param component: Component to remove
:return:
"""
return self.view.root_component.add_component(component)
[docs]
def add_data_variable(self, data_variable):
return core.BNComponentAddDataVariable(self.handle, data_variable.address)
[docs]
def contains_data_variable(self, data_variable):
return core.BNComponentContainsDataVariable(self.handle, data_variable.address)
[docs]
def remove_data_variable(self, data_variable):
return core.BNComponentRemoveDataVariable(self.handle, data_variable.address)
@property
def display_name(self) -> str:
"""Original Name of the component (read-only)"""
return core.BNComponentGetDisplayName(self.handle)
@property
def name(self) -> str:
"""Original name set for this component
:note: The `.display_name` property should be used for `bv.get_component_by_path()` lookups.
This can differ from the .display_name property if one of its sibling components has the same .original_name; In that
case, .name will be an automatically generated unique name (e.g. "MyComponentName (1)") while .original_name will
remain what was originally set (e.g. "MyComponentName")
If this component has a duplicate name and is moved to a component where none of its siblings share its name,
the .name property will return the original "MyComponentName"
"""
return core.BNComponentGetOriginalName(self.handle)
@name.setter
def name(self, _name):
core.BNComponentSetName(self.handle, _name)
@property
def parent(self) -> Optional['Component']:
"""
The component that contains this component, if it exists.
"""
bn_component = core.BNComponentGetParent(self.handle)
if bn_component is not None:
return Component(bn_component)
return None
@property
def view(self):
bn_binaryview = core.BNComponentGetView(self.handle)
if bn_binaryview is not None:
return binaryview.BinaryView(handle=bn_binaryview)
return None
@property
def components(self) -> List['Component']:
"""
``components`` is an iterator for all Components contained within this Component
:return: A list of components
:Example:
>>> for subcomp in component.components:
... print(repr(component))
"""
count = ctypes.c_ulonglong(0)
bn_components = core.BNComponentGetContainedComponents(self.handle, count)
components = []
try:
for i in range(count.value):
components.append(Component(core.BNNewComponentReference(bn_components[i])))
finally:
core.BNFreeComponents(bn_components, count.value)
return components
@property
def function_list(self) -> List['function.Function']:
"""
``function_list`` List of all Functions contained within this Component
:warning: .functions Should be used instead of this in any performance sensitive context.
:return: A list of functions
:Example:
>>> for func in component.functions:
... print(func.name)
"""
count = ctypes.c_ulonglong(0)
bn_functions = core.BNComponentGetContainedFunctions(self.handle, count)
funcs = []
try:
for i in range(count.value):
bn_function = core.BNNewFunctionReference(bn_functions[i])
funcs.append(function.Function(self.view, bn_function))
finally:
core.BNFreeFunctionList(bn_functions, count.value)
return funcs
@property
def functions(self) -> Iterator['function.Function']:
"""
``functions`` is an iterator for all Functions contained within this Component
:return: An iterator containing Components
:rtype: ComponentIterator
:Example:
>>> for func in component.functions:
... print(func.name)
"""
@dataclass
class FunctionIterator:
view: 'binaryview.BinaryView'
comp: Component
def __iter__(self):
count = ctypes.c_ulonglong(0)
bn_functions = core.BNComponentGetContainedFunctions(self.comp.handle, count)
try:
for i in range(count.value):
bn_function = core.BNNewFunctionReference(bn_functions[i])
yield function.Function(self.view, bn_function)
finally:
core.BNFreeFunctionList(bn_functions, count.value)
return iter(FunctionIterator(self.view, self))
@property
def data_variable_list(self):
data_vars = []
count = ctypes.c_ulonglong(0)
bn_data_vars = core.BNComponentGetContainedDataVariables(self.handle, count)
try:
for i in range(count.value):
bn_data_var = bn_data_vars[i]
data_var = binaryview.DataVariable.from_core_struct(bn_data_var, self.view)
data_vars.append(data_var)
finally:
core.BNFreeDataVariables(bn_data_vars, count.value)
return data_vars
@property
def data_variables(self):
@dataclass
class DataVariableIterator:
view: 'binaryview.BinaryView'
comp: Component
def __iter__(self):
count = ctypes.c_ulonglong(0)
bn_data_vars = core.BNComponentGetContainedDataVariables(self.comp.handle, count)
try:
for i in range(count.value):
bn_data_var = bn_data_vars[i]
yield binaryview.DataVariable.from_core_struct(bn_data_var, self.view)
finally:
core.BNFreeDataVariables(bn_data_vars, count.value)
return iter(DataVariableIterator(self.view, self))
[docs]
def get_referenced_data_variables(self, recursive=False):
"""
Get data variables referenced by this component
:param recursive: Optional; Get all DataVariables referenced by this component and sub-components.
:return: List of DataVariables
"""
data_vars = []
count = ctypes.c_ulonglong(0)
if recursive:
bn_data_vars = core.BNComponentGetReferencedDataVariablesRecursive(self.handle, count)
else:
bn_data_vars = core.BNComponentGetReferencedDataVariables(self.handle, count)
try:
for i in range(count.value):
bn_data_var = bn_data_vars[i]
data_var = binaryview.DataVariable.from_core_struct(bn_data_var, self.view)
data_vars.append(data_var)
finally:
core.BNFreeDataVariables(bn_data_vars, count.value)
return data_vars
[docs]
def get_referenced_types(self, recursive=False):
"""
Get Types referenced by this component
:param recursive: Optional; Get all Types referenced by this component and sub-components.
:return: List of Types
"""
_types = []
count = ctypes.c_ulonglong(0)
if recursive:
bn_types = core.BNComponentGetReferencedTypesRecursive(self.handle, count)
else:
bn_types = core.BNComponentGetReferencedTypes(self.handle, count)
try:
for i in range(count.value):
_types.append(types.Type.create(core.BNNewTypeReference(bn_types[i])))
finally:
core.BNComponentFreeReferencedTypes(bn_types, count.value)
return _types