summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorPeter LaFosse <peter@vector35.com>2021-11-05 22:11:51 -0400
committerPeter LaFosse <peter@vector35.com>2021-11-05 22:12:03 -0400
commit719ec9860158123d229dd729993834c66b13d8c9 (patch)
tree6f5d37c61eca79f8d8682115cd6939e17bf68bdd /python
parentafe2b869b0bc8ea2b4969163899d495d46c85fef (diff)
Turn some apis back into Lists for ease of use, improve __repr__ for list and mapping containers
Diffstat (limited to 'python')
-rw-r--r--python/binaryview.py48
-rw-r--r--python/function.py146
-rw-r--r--python/highlevelil.py28
-rw-r--r--python/lowlevelil.py30
-rw-r--r--python/mediumlevelil.py30
5 files changed, 175 insertions, 107 deletions
diff --git a/python/binaryview.py b/python/binaryview.py
index e44d9559..88642e23 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -1058,29 +1058,25 @@ class Segment:
return core.BNSegmentIsAutoDefined(self.handle)
@property
- def relocation_ranges(self) -> Generator[Tuple[int, int], None, None]:
+ def relocation_ranges(self) -> List[Tuple[int, int]]:
"""List of relocation range tuples (read-only)"""
count = ctypes.c_ulonglong()
ranges = core.BNSegmentGetRelocationRanges(self.handle, count)
assert ranges is not None, "core.BNSegmentGetRelocationRanges returned None"
-
try:
- for i in range(0, count.value):
- yield (ranges[i].start, ranges[i].end)
+ return [(ranges[i].start, ranges[i].end) for i in range(count.value)]
finally:
core.BNFreeRelocationRanges(ranges, count)
- def relocation_ranges_at(self, addr:int) -> Generator[Tuple[int, int], None, None]:
+ def relocation_ranges_at(self, addr:int) -> List[Tuple[int, int]]:
"""List of relocation range tuples (read-only)"""
count = ctypes.c_ulonglong()
ranges = core.BNSegmentGetRelocationRangesAtAddress(self.handle, addr, count)
assert ranges is not None, "core.BNSegmentGetRelocationRangesAtAddress returned None"
-
try:
- for i in range(0, count.value):
- yield (ranges[i].start, ranges[i].end)
+ return [(ranges[i].start, ranges[i].end) for i in range(count.value)]
finally:
core.BNFreeRelocationRanges(ranges, count)
@@ -1278,6 +1274,16 @@ class _BinaryViewAssociatedDataStore(associateddatastore._AssociatedDataStore):
class SymbolMapping(collections.abc.Mapping): # type: ignore
+ """
+ SymbolMapping object is used to improve performance of the `bv.symbols` API.
+ This allows pythonic code like this to have reasonable performance characteristics
+
+ >>> my_symbols = get_my_symbols()
+ >>> for symbol in my_symbols:
+ >>> if bv.symbols[symbol].address == 0x41414141:
+ >>> print("Found")
+
+ """
def __init__(self, view:'BinaryView'):
self._symbol_list = None
self._count = None
@@ -1285,6 +1291,9 @@ class SymbolMapping(collections.abc.Mapping): # type: ignore
self._view = view
self._n = 0
+ def __repr__(self):
+ return f"<SymbolMapping {len(self)} symbols: {self._symbol_cache}>"
+
def __del__(self):
if core is not None and self._symbol_list is not None:
core.BNFreeSymbolList(self._symbol_list, len(self))
@@ -1362,12 +1371,25 @@ class SymbolMapping(collections.abc.Mapping): # type: ignore
class TypeMapping(collections.abc.Mapping): # type: ignore
+ """
+ TypeMapping object is used to improve performance of the `bv.types` API.
+ This allows pythonic code like this to have reasonable performance characteristics
+
+ >>> my_types = get_my_types()
+ >>> for type_name in my_types:
+ >>> if bv.types[type_name].width == 4:
+ >>> print("Found")
+
+ """
def __init__(self, view:'BinaryView'):
self._type_list = None
self._count = None
self._type_cache:Optional[Mapping[_types.QualifiedName, _types.Type]] = None
self._view = view
+ def __repr__(self):
+ return f"<TypeMapping {len(self)} symbols: {self._type_cache}>"
+
def __del__(self):
if core is not None and self._type_list is not None:
core.BNFreeTypeList(self._type_list, len(self))
@@ -2358,11 +2380,8 @@ class BinaryView:
count = ctypes.c_ulonglong()
ranges = core.BNGetRelocationRanges(self.handle, count)
assert ranges is not None, "core.BNGetRelocationRanges returned None"
- result = []
try:
- for i in range(0, count.value):
- result.append((ranges[i].start, ranges[i].end))
- return result
+ return [(ranges[i].start, ranges[i].end) for i in range(count.value)]
finally:
core.BNFreeRelocationRanges(ranges, count)
@@ -2372,11 +2391,8 @@ class BinaryView:
count = ctypes.c_ulonglong()
ranges = core.BNGetRelocationRangesAtAddress(self.handle, addr, count)
assert ranges is not None, "core.BNGetRelocationRangesAtAddress returned None"
- result = []
try:
- for i in range(0, count.value):
- result.append((ranges[i].start, ranges[i].end))
- return result
+ return [(ranges[i].start, ranges[i].end) for i in range(count.value)]
finally:
core.BNFreeRelocationRanges(ranges, count)
diff --git a/python/function.py b/python/function.py
index a0bbeab3..41b5ac82 100644
--- a/python/function.py
+++ b/python/function.py
@@ -171,20 +171,19 @@ class VariableReferenceSource:
class BasicBlockList:
def __init__(self, function:'Function'):
- count = ctypes.c_ulonglong(0)
- blocks = core.BNGetFunctionBasicBlockList(function.handle, count)
- assert blocks is not None, "core.BNGetFunctionBasicBlockList returned None"
- self._blocks = blocks
- self._count = count.value
+ self._count, self._blocks = function._basic_block_list()
self._function = function
self._n = 0
+ def __repr__(self):
+ return f"<BasicBlockList {len(self)} BasicBlocks: {list(self)}>"
+
def __del__(self):
if core is not None:
core.BNFreeBasicBlockList(self._blocks, len(self))
def __len__(self):
- return self._count
+ return self._count.value
def __iter__(self):
return self
@@ -195,7 +194,7 @@ class BasicBlockList:
block = core.BNNewBasicBlockReference(self._blocks[self._n])
assert block is not None, "core.BNNewBasicBlockReference returned None"
self._n += 1
- return basicblock.BasicBlock(block, self._function.view)
+ return self._function._instantiate_block(block)
def __getitem__(self, i:Union[int, slice]) -> Union['basicblock.BasicBlock', List['basicblock.BasicBlock']]:
if isinstance(i, int):
@@ -205,7 +204,7 @@ class BasicBlockList:
raise IndexError(f"Index {i} out of bounds for BasicBlockList of size {len(self)}")
block = core.BNNewBasicBlockReference(self._blocks[i])
assert block is not None, "core.BNNewBasicBlockReference returned None"
- return basicblock.BasicBlock(block, self._function.view)
+ return self._function._instantiate_block(block)
elif isinstance(i, slice):
result = []
if i.start < 0 or i.start >= len(self) or i.stop < 0 or i.stop >= len(self):
@@ -214,9 +213,65 @@ class BasicBlockList:
for j in range(i.start, i.stop, i.step if i.step is not None else 1):
block = core.BNNewBasicBlockReference(self._blocks[j])
assert block is not None, "core.BNNewBasicBlockReference returned None"
- result.append(basicblock.BasicBlock(block, self._function.view))
+ result.append(self._function._instantiate_block(block))
return result
- raise ValueError("FunctionList.__getitem__ supports argument of type integer or slice")
+ raise ValueError("BasicBlockList.__getitem__ supports argument of type integer or slice only")
+
+
+class TagList:
+ def __init__(self, function:'Function'):
+ self._count = ctypes.c_ulonglong()
+ self._tags = core.BNGetAddressTagReferences(function.handle, self._count)
+ assert self._tags is not None, "core.BNGetAddressTagReferences returned None"
+ self._function = function
+ self._n = 0
+
+ def __repr__(self):
+ return f"<TagList {len(self)} Tags: {list(self)}>"
+
+ def __del__(self):
+ if core is not None:
+ core.BNFreeTagReferences(self._tags, len(self))
+
+ def __len__(self):
+ return self._count.value
+
+ def __iter__(self):
+ return self
+
+ def __next__(self) -> Tuple['architecture.Architecture', int, 'binaryview.Tag']:
+ if self._n >= len(self):
+ raise StopIteration
+ core_tag = core.BNNewTagReference(self._tags[self._n].tag)
+ arch = architecture.CoreArchitecture._from_cache(self._tags[self._n].arch)
+ address = self._tags[self._n].addr
+ assert core_tag is not None, "core.BNNewTagReference returned None"
+ self._n += 1
+ return (arch, address, binaryview.Tag(core_tag))
+
+ def __getitem__(self, i:Union[int, slice]) -> Union[Tuple['architecture.Architecture', int, 'binaryview.Tag'], List[Tuple['architecture.Architecture', int, 'binaryview.Tag']]]:
+ if isinstance(i, int):
+ if i < 0:
+ i = len(self) + i
+ if i >= len(self):
+ raise IndexError(f"Index {i} out of bounds for TagList of size {len(self)}")
+
+ core_tag = core.BNNewTagReference(self._tags[i].tag)
+ arch = architecture.CoreArchitecture._from_cache(self._tags[i].arch)
+ assert core_tag is not None, "core.BNNewTagReference returned None"
+ return (arch, self._tags[i].addr, binaryview.Tag(core_tag))
+ elif isinstance(i, slice):
+ result = []
+ if i.start < 0 or i.start >= len(self) or i.stop < 0 or i.stop >= len(self):
+ raise IndexError(f"Slice {i} out of bounds for FunctionList of size {len(self)}")
+
+ for j in range(i.start, i.stop, i.step if i.step is not None else 1):
+ core_tag = core.BNNewTagReference(self._tags[j].tag)
+ assert core_tag is not None, "core.BNNewTagReference returned None"
+ arch = architecture.CoreArchitecture._from_cache(self._tags[j].arch)
+ result.append((arch, self._tags[j].addr, binaryview.Tag(core_tag)))
+ return result
+ raise ValueError("TagList.__getitem__ supports argument of type integer or slice only")
class Function:
@@ -429,8 +484,18 @@ class Function:
"""Whether the function has analysis that needs to be updated (read-only)"""
return core.BNIsFunctionUpdateNeeded(self.handle)
+ def _basic_block_list(self):
+ count = ctypes.c_ulonglong()
+ blocks = core.BNGetFunctionBasicBlockList(self.handle, count)
+ assert blocks is not None, "core.BNGetFunctionBasicBlockList returned None"
+ return (count, blocks)
+
+ def _instantiate_block(self, handle):
+ return basicblock.BasicBlock(handle, self.view)
+
@property
def basic_blocks(self) -> BasicBlockList:
+ """function.BasicBlockList of BasicBlocks in the current function (read-only)"""
return BasicBlockList(self)
@property
@@ -473,27 +538,16 @@ class Function:
return self.view.create_tag(type, data, user)
@property
- def address_tags(self) -> Generator[Tuple['architecture.Architecture', int, 'binaryview.Tag'], None, None]:
+ def address_tags(self) -> TagList:
"""
- ``address_tags`` gets a list of all address Tags in the function.
- Tags are returned as a list of (arch, address, Tag) tuples.
+ ``address_tags`` gets a TagList of all address Tags in the function.
+ Tags are returned as an iterable indexable object TagList of (arch, address, Tag) tuples.
- :rtype: Generator((Architecture, int, Tag))
+ :rtype: TagList((Architecture, int, Tag))
"""
- count = ctypes.c_ulonglong()
- tags = core.BNGetAddressTagReferences(self.handle, count)
- assert tags is not None, "core.BNGetAddressTagReferences returned None"
- try:
- for i in range(0, count.value):
- arch = architecture.CoreArchitecture._from_cache(tags[i].arch)
- core_tag = core.BNNewTagReference(tags[i].tag)
- assert core_tag is not None, "core.BNNewTagReference returned None"
- tag = binaryview.Tag(core_tag)
- yield (arch, tags[i].addr, tag)
- finally:
- core.BNFreeTagReferences(tags, count.value)
+ return TagList(self)
- def get_address_tags_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> Generator['binaryview.Tag', None, None]:
+ def get_address_tags_at(self, addr:int, arch:Optional['architecture.Architecture']=None) -> List['binaryview.Tag']:
"""
``get_address_tags_at`` gets a generator of all Tags in the function at a given address.
@@ -508,11 +562,13 @@ class Function:
count = ctypes.c_ulonglong()
tags = core.BNGetAddressTags(self.handle, arch.handle, addr, count)
assert tags is not None, "core.BNGetAddressTags returned None"
+ result = []
try:
for i in range(0, count.value):
core_tag = core.BNNewTagReference(tags[i])
assert core_tag is not None
- yield binaryview.Tag(core_tag)
+ result.append(binaryview.Tag(core_tag))
+ return result
finally:
core.BNFreeTagList(tags, count.value)
@@ -626,20 +682,22 @@ class Function:
return tag
@property
- def function_tags(self) -> Generator['binaryview.Tag', None, None]:
+ def function_tags(self) -> List['binaryview.Tag']:
"""
``function_tags`` gets a list of all function Tags for the function.
- :rtype: Generator(Tag)
+ :rtype: List(Tag)
"""
count = ctypes.c_ulonglong()
tags = core.BNGetFunctionTags(self.handle, count)
assert tags is not None, "core.BNGetFunctionTags returned None"
try:
- for i in range(0, count.value):
+ result = []
+ for i in range(count.value):
core_tag = core.BNNewTagReference(tags[i])
assert core_tag is not None
- yield binaryview.Tag(core_tag)
+ result.append(binaryview.Tag(core_tag))
+ return result
finally:
core.BNFreeTagList(tags, count.value)
@@ -799,50 +857,46 @@ class Function:
self.set_user_type(value)
@property
- def stack_layout(self) -> Generator['variable.Variable', None, None]:
+ def stack_layout(self) -> List['variable.Variable']:
"""List of function stack variables (read-only)"""
count = ctypes.c_ulonglong()
v = core.BNGetStackLayout(self.handle, count)
assert v is not None, "core.BNGetStackLayout returned None"
try:
- for i in range(0, count.value):
- yield variable.Variable.from_BNVariable(self, v[i].var)
+ return [variable.Variable.from_BNVariable(self, v[i].var) for i in range(count.value)]
finally:
core.BNFreeVariableNameAndTypeList(v, count.value)
@property
- def core_var_stack_layout(self) -> Generator['variable.CoreVariable', None, None]:
+ def core_var_stack_layout(self) -> List['variable.CoreVariable']:
"""List of function stack variables (read-only)"""
count = ctypes.c_ulonglong()
v = core.BNGetStackLayout(self.handle, count)
assert v is not None, "core.BNGetStackLayout returned None"
try:
- for i in range(0, count.value):
- yield variable.CoreVariable.from_BNVariable(v[i].var)
+ return [variable.CoreVariable.from_BNVariable(v[i].var) for i in range(count.value)]
finally:
core.BNFreeVariableNameAndTypeList(v, count.value)
@property
- def vars(self) -> Generator['variable.Variable', None, None]:
- """Generator of function variables (read-only)"""
+ def vars(self) -> List['variable.Variable']:
+ """List of function variables (read-only)"""
count = ctypes.c_ulonglong()
v = core.BNGetFunctionVariables(self.handle, count)
assert v is not None, "core.BNGetFunctionVariables returned None"
try:
- for i in range(0, count.value):
- yield variable.Variable.from_BNVariable(self, v[i].var)
+ return [variable.Variable.from_BNVariable(self, v[i].var) for i in range(count.value)]
finally:
core.BNFreeVariableNameAndTypeList(v, count.value)
@property
- def core_vars(self) -> Generator['variable.CoreVariable', None, None]:
- """Generator of CoreVariable objects"""
+ def core_vars(self) -> List['variable.CoreVariable']:
+ """List of CoreVariable objects"""
count = ctypes.c_ulonglong()
v = core.BNGetFunctionVariables(self.handle, count)
assert v is not None, "core.BNGetFunctionVariables returned None"
try:
- for i in range(0, count.value):
- yield variable.CoreVariable.from_BNVariable(v[i].var)
+ return [variable.CoreVariable.from_BNVariable(v[i].var) for i in range(count.value)]
finally:
core.BNFreeVariableNameAndTypeList(v, count.value)
diff --git a/python/highlevelil.py b/python/highlevelil.py
index 8c8b1958..44985fd4 100644
--- a/python/highlevelil.py
+++ b/python/highlevelil.py
@@ -2171,31 +2171,25 @@ class HighLevelILFunction:
def root(self, value:HighLevelILInstruction) -> None:
core.BNSetHighLevelILRootExpr(value.expr_index)
- @property
- def basic_blocks(self) -> Generator['HighLevelILBasicBlock', None, None]:
- """list of HighLevelILBasicBlock objects (read-only)"""
+ def _basic_block_list(self):
count = ctypes.c_ulonglong()
blocks = core.BNGetHighLevelILBasicBlockList(self.handle, count)
assert blocks is not None, "core.BNGetHighLevelILBasicBlockList returned None"
+ return (count, blocks)
- view = None
- if self._source_function is not None:
- view = self._source_function.view
+ def _instantiate_block(self, handle):
+ return HighLevelILBasicBlock(handle, self, self.view)
- try:
- for i in range(0, count.value):
- core_block = core.BNNewBasicBlockReference(blocks[i])
- assert core_block is not None, "core.BNNewBasicBlockReference is None"
- yield HighLevelILBasicBlock(core_block, self, view)
- finally:
- core.BNFreeBasicBlockList(blocks, count.value)
+ @property
+ def basic_blocks(self) -> 'function.BasicBlockList':
+ """function.BasicBlockList of HighLevelILBasicBlock objects (read-only)"""
+ return function.BasicBlockList(self)
@property
def instructions(self) -> Generator[HighLevelILInstruction, None, None]:
"""A generator of hlil instructions of the current function"""
for block in self.basic_blocks:
- for i in block:
- yield i
+ yield from block
@property
def ssa_form(self) -> 'HighLevelILFunction':
@@ -2218,6 +2212,10 @@ class HighLevelILFunction:
return self._arch
@property
+ def view(self) -> 'binaryview.BinaryView':
+ return self.source_function.view
+
+ @property
def source_function(self) -> 'function.Function':
assert self._source_function is not None
return self._source_function
diff --git a/python/lowlevelil.py b/python/lowlevelil.py
index 313feec4..3bf8a592 100644
--- a/python/lowlevelil.py
+++ b/python/lowlevelil.py
@@ -2783,29 +2783,25 @@ class LowLevelILFunction:
"""Number of temporary flags (read-only)"""
return core.BNGetLowLevelILTemporaryFlagCount(self.handle)
- @property
- def basic_blocks(self) -> Generator['LowLevelILBasicBlock', None, None]:
- """list of LowLevelILBasicBlock objects (read-only)"""
+ def _basic_block_list(self):
count = ctypes.c_ulonglong()
blocks = core.BNGetLowLevelILBasicBlockList(self.handle, count)
assert blocks is not None, "core.BNGetLowLevelILBasicBlockList returned None"
- view = None
- if self._source_function is not None:
- view = self._source_function.view
- try:
- for i in range(0, count.value):
- core_block = core.BNNewBasicBlockReference(blocks[i])
- assert core_block is not None, "core.BNNewBasicBlockReference returned None"
- yield LowLevelILBasicBlock(core_block, self, view)
- finally:
- core.BNFreeBasicBlockList(blocks, count.value)
+ return (count, blocks)
+
+ def _instantiate_block(self, handle):
+ return LowLevelILBasicBlock(handle, self, self.view)
+
+ @property
+ def basic_blocks(self) -> 'function.BasicBlockList':
+ """function.BasicBlockList of LowLevelILBasicBlock objects (read-only)"""
+ return function.BasicBlockList(self)
@property
def instructions(self) -> Generator['LowLevelILInstruction', None, None]:
"""A generator of llil instructions of the current llil function"""
for block in self.basic_blocks:
- for i in block:
- yield i
+ yield from block
@property
def ssa_form(self) -> 'LowLevelILFunction':
@@ -2863,6 +2859,10 @@ class LowLevelILFunction:
self._source_function = value
@property
+ def view(self) -> 'binaryview.BinaryView':
+ return self.source_function.view
+
+ @property
def il_form(self) -> FunctionGraphType:
if len(list(self.basic_blocks)) < 1:
return FunctionGraphType.InvalidILViewType
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index 86927b0a..6c75e7cc 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -2683,29 +2683,25 @@ class MediumLevelILFunction:
_arch = self._arch
core.BNMediumLevelILSetCurrentAddress(self.handle, _arch.handle, value)
- @property
- def basic_blocks(self) -> Generator['MediumLevelILBasicBlock', None, None]:
- """list of MediumLevelILBasicBlock objects (read-only)"""
+ def _basic_block_list(self):
count = ctypes.c_ulonglong()
blocks = core.BNGetMediumLevelILBasicBlockList(self.handle, count)
assert blocks is not None, "core.BNGetMediumLevelILBasicBlockList returned None"
- view = None
- if self._source_function is not None:
- view = self._source_function.view
- try:
- for i in range(0, count.value):
- core_block = core.BNNewBasicBlockReference(blocks[i])
- assert core_block is not None
- yield MediumLevelILBasicBlock(core_block, self, view)
- finally:
- core.BNFreeBasicBlockList(blocks, count.value)
+ return (count, blocks)
+
+ def _instantiate_block(self, handle):
+ return MediumLevelILBasicBlock(handle, self, self.view)
+
+ @property
+ def basic_blocks(self) -> 'function.BasicBlockList':
+ """function.BasicBlockList of MediumLevelILBasicBlock objects (read-only)"""
+ return function.BasicBlockList(self)
@property
def instructions(self) -> Generator[MediumLevelILInstruction, None, None]:
"""A generator of mlil instructions of the current function"""
for block in self.basic_blocks:
- for i in block:
- yield i
+ yield from block
@property
def ssa_form(self) -> Optional['MediumLevelILFunction']:
@@ -3004,6 +3000,10 @@ class MediumLevelILFunction:
return self._arch
@property
+ def view(self) -> 'binaryview.BinaryView':
+ return self.source_function.view
+
+ @property
def source_function(self) -> 'function.Function':
return self._source_function