summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/highlevelil.py115
-rw-r--r--python/lowlevelil.py109
-rw-r--r--python/mediumlevelil.py108
3 files changed, 174 insertions, 158 deletions
diff --git a/python/highlevelil.py b/python/highlevelil.py
index ff35c018..60addda5 100644
--- a/python/highlevelil.py
+++ b/python/highlevelil.py
@@ -20,7 +20,7 @@
import ctypes
import struct
-from typing import Optional, Generator, List, Union, NewType, Tuple, ClassVar, Mapping, Set, Callable, Any
+from typing import Optional, Generator, List, Union, NewType, Tuple, ClassVar, Mapping, Set, Callable, Any, Iterator
from dataclasses import dataclass
from enum import Enum
@@ -779,6 +779,35 @@ class HighLevelILInstruction(BaseILInstruction):
"""
return []
+ def traverse(self, cb: Callable[['HighLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` is a generator that allows you to traverse the HLIL AST in a depth-first manner. It will yield the
+ result of the callback function for each node in the AST. Arguments can be passed to the callback function using
+ ``args`` and ``kwargs``.
+
+ :param Callable[[HighLevelILInstruction, Any], Any] cb: The callback function to call for each node in the HighLevelILInstruction
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+
+ :Example:
+ >>> def get_constant_less_than_value(inst: HighLevelILInstruction, value: int) -> int:
+ >>> if isinstance(inst, Constant) and inst.constant < value:
+ >>> return inst.constant
+ >>>
+ >>> list(inst.traverse(get_constant_less_than_value, 10))
+ """
+ if (result := cb(self, *args, **kwargs)) is not None:
+ yield result
+ for _, op, _ in self.detailed_operands:
+ if isinstance(op, HighLevelILInstruction):
+ yield from op.traverse(cb, *args, **kwargs)
+ elif isinstance(op, list) and all(isinstance(i, HighLevelILInstruction) for i in op):
+ for i in op:
+ yield from i.traverse(cb, *args, **kwargs) # type: ignore
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILInstruction.traverse` instead.")
def visit_all(self, cb: HighLevelILVisitorCallback,
name: str = "root", parent: Optional['HighLevelILInstruction'] = None) -> bool:
"""
@@ -802,6 +831,7 @@ class HighLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILInstruction.traverse` instead.")
def visit_operands(self, cb: HighLevelILVisitorCallback,
name: str = "root", parent: Optional['HighLevelILInstruction'] = None) -> bool:
"""
@@ -822,6 +852,7 @@ class HighLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILInstruction.traverse` instead.")
def visit(self, cb: HighLevelILVisitorCallback,
name: str = "root", parent: Optional['HighLevelILInstruction'] = None) -> bool:
"""
@@ -853,57 +884,6 @@ class HighLevelILInstruction(BaseILInstruction):
return False
return True
- def traverse(self, cb: Callable[['HighLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Any:
- """
- Traverses all HighLevelILInstructions in the operands of this instruction and any sub-instructions.
- The callback you provide only needs to accept a single instruction, but accepts anything, and can return whatever you want.
-
- None is treated as a reserved value to indicate that the traverser should continue descending into subexpressions.
-
- :param cb: Callback function that takes only the instruction
- :param args: Custom user-defined arguments
- :param kwargs: Custom user-defined keyword arguments
- :return: None if your callback doesn't return anything and all instructions were traversed, otherwise it returns the value from your callback.
- :Example:
- >>> # This traverser allows for simplified function signatures in your callback
- >>> def traverser(inst) -> int:
- >>> if isinstance(inst, Constant):
- >>> return inst.constant # Stop recursion and return the constant
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all constants used in the program
- >>> for inst in bv.hlil_instructions:
- >>> if const := inst.traverse(traverser):
- >>> print(f"Found constant {const}")
-
-
- >>> # But it also allows for complex function signatures in your callback
- >>> def traverser(inst, search_constant, skip_list: List[int] = []) -> int:
- >>> if inst.address in skip_list:
- >>> return None # Skip this instruction
- >>> if isinstance(inst, Constant):
- >>> if inst.constant == search_constant:
- >>> return inst.address # Stop recursion and return the address of this use
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all instances of 0xdeadbeaf used in the program
- >>> for inst in bv.hlil_instructions:
- >>> if use_addr := inst.traverse(traverser, 0xdeadbeaf, skip_list=[0x12345678]):
- >>> print(f"Found 0xdeadbeef use at {use_addr}")
- """
-
- if (result := cb(self, *args, **kwargs)) is not None:
- return result
- for _, op, _ in self.detailed_operands:
- if isinstance(op, HighLevelILInstruction):
- if (result := op.traverse(cb, *args, **kwargs)) is not None:
- return result
- elif isinstance(op, list) and all(isinstance(i, HighLevelILInstruction) for i in op):
- for i in op:
- if (result := i.traverse(cb, *args, **kwargs)) is not None:
- return result
- return None
-
@dataclass(frozen=True, repr=False, eq=False)
class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation):
@@ -2567,7 +2547,35 @@ class HighLevelILFunction:
return HighLevelILBasicBlock(block, self, view)
+ def traverse(self, cb: Callable[['HighLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` iterates through all the instructions in the HighLevelILInstruction and calls the callback function for
+ each instruction and sub-instruction.
+
+ :param Callable[[HighLevelILInstruction, Any], Any] cb: The callback function to call for each node in the HighLevelILInstruction
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+ :Example:
+ >>> # find all calls to memcpy where the third parameter is not a constant
+ >>> def find_non_constant_memcpy(i, target) -> HighLevelILInstruction:
+ ... match i:
+ ... case Localcall(dest=Constant(constant=c), params=[_, _, p]) if c == target and not isinstance(p, Constant):
+ ... return i
+ >>> target_address = bv.get_symbol_by_raw_name('_memcpy').address
+ >>> list(current_il_function.traverse(find_non_constant_memcpy, target_address))
+ """
+ root = self.root
+ if root is None:
+ raise ValueError("HighLevelILFunction has no root")
+ if not isinstance(root, HighLevelILBlock):
+ root = [root]
+ for instr in root:
+ yield from instr.traverse(cb, *args, **kwargs)
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILFunction.traverse` instead.")
def visit(self, cb: HighLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function
@@ -2581,6 +2589,7 @@ class HighLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILFunction.traverse` instead.")
def visit_all(self, cb: HighLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each instruction and their operands.
@@ -2593,6 +2602,7 @@ class HighLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`HighLevelILFunction.traverse` instead.")
def visit_operands(self, cb: HighLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each operand and
@@ -2606,7 +2616,6 @@ class HighLevelILFunction:
return False
return True
-
@property
def instructions(self) -> Generator[HighLevelILInstruction, None, None]:
"""A generator of hlil instructions of the current function"""
diff --git a/python/lowlevelil.py b/python/lowlevelil.py
index c8141b38..e12623a4 100644
--- a/python/lowlevelil.py
+++ b/python/lowlevelil.py
@@ -20,7 +20,7 @@
import ctypes
import struct
-from typing import Generator, List, Optional, Dict, Union, Tuple, NewType, ClassVar, Set, Callable, Any
+from typing import Generator, List, Optional, Dict, Union, Tuple, NewType, ClassVar, Set, Callable, Any, Iterator
from dataclasses import dataclass
# Binary Ninja components
@@ -35,6 +35,7 @@ from . import variable
from . import binaryview
from . import architecture
from . import types
+from . import deprecation
from .interaction import show_graph_report
from .commonil import (
BaseILInstruction, Constant, BinaryOperation, Tailcall, UnaryOperation, Comparison, SSA, Phi, FloatingPoint,
@@ -678,7 +679,36 @@ class LowLevelILInstruction(BaseILInstruction):
"""
return []
+ def traverse(self, cb: Callable[['LowLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` is a generator that allows you to traverse the LowLevelILInstruction in a depth-first manner. It will yield the
+ result of the callback function for each node in the tree. Arguments can be passed to the callback function using
+ ``args`` and ``kwargs``.
+
+ :param Callable[[LowLevelILInstruction, Any], Any] cb: The callback function to call for each node in the LowLevelILInstruction
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+
+ :Example:
+ >>> def get_constant_less_than_value(inst: LowLevelILInstruction, value: int) -> int:
+ >>> if isinstance(inst, Constant) and inst.constant < value:
+ >>> return inst.constant
+ >>>
+ >>> list(inst.traverse(get_constant_less_than_value, 10))
+ """
+ if (result := cb(self, *args, **kwargs)) is not None:
+ yield result
+ for _, op, _ in self.detailed_operands:
+ if isinstance(op, LowLevelILInstruction):
+ yield from op.traverse(cb, *args, **kwargs)
+ elif isinstance(op, list) and all(isinstance(i, LowLevelILInstruction) for i in op):
+ for i in op:
+ yield from i.traverse(cb, *args, **kwargs) # type: ignore
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILInstruction.traverse` instead.")
def visit_all(self, cb: LowLevelILVisitorCallback,
name: str = "root", parent: Optional['LowLevelILInstruction'] = None) -> bool:
"""
@@ -702,6 +732,7 @@ class LowLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILInstruction.traverse` instead.")
def visit_operands(self, cb: LowLevelILVisitorCallback,
name: str = "root", parent: Optional['LowLevelILInstruction'] = None) -> bool:
"""
@@ -722,6 +753,7 @@ class LowLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILInstruction.traverse` instead.")
def visit(self, cb: LowLevelILVisitorCallback,
name: str = "root", parent: Optional['LowLevelILInstruction'] = None) -> bool:
"""
@@ -742,58 +774,6 @@ class LowLevelILInstruction(BaseILInstruction):
return False
return True
- def traverse(self, cb: Callable[['LowLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Any:
- """
- Traverses all LowLevelILInstructions in the operands of this instruction and any sub-instructions.
- The callback you provide only needs to accept a single instruction, but accepts anything, and can return whatever you want.
-
- None is treated as a reserved value to indicate that the traverser should continue descending into subexpressions.
-
- :param cb: Callback function that takes only the instruction
- :param args: Custom user-defined arguments
- :param kwargs: Custom user-defined keyword arguments
- :return: None if your callback doesn't return anything and all instructions were traversed, otherwise it returns the value from your callback.
- :Example:
- >>> # This traverser allows for simplified function signatures in your callback
- >>> def traverser(inst) -> int:
- >>> if isinstance(inst, Constant):
- >>> return inst.constant # Stop recursion and return the constant
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all constants used in the program
- >>> for inst in bv.mlil_instructions:
- >>> if const := inst.traverse(traverser):
- >>> print(f"Found constant {const}")
-
-
- >>> # But it also allows for complex function signatures in your callback
- >>> def traverser(inst, search_constant, skip_list: List[int] = []) -> int:
- >>> if inst.address in skip_list:
- >>> return None # Skip this instruction
- >>> if isinstance(inst, Constant):
- >>> if inst.constant == search_constant:
- >>> return inst.address # Stop recursion and return the address of this use
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all instances of 0xdeadbeaf used in the program
- >>> for inst in bv.mlil_instructions:
- >>> if use_addr := inst.traverse(traverser, 0xdeadbeaf, skip_list=[0x12345678]):
- >>> print(f"Found 0xdeadbeef use at {use_addr}")
- """
-
- if (result := cb(self, *args, **kwargs)) is not None:
- return result
- for _, op, _ in self.detailed_operands:
- if isinstance(op, LowLevelILInstruction):
- if (result := op.traverse(cb, *args, **kwargs)) is not None:
- return result
- elif isinstance(op, list) and all(isinstance(i, LowLevelILInstruction) for i in op):
- for i in op:
- if (result := i.traverse(cb, *args, **kwargs)) is not None:
- return result
- return None
-
-
@property
def prefix_operands(self) -> List[LowLevelILOperandType]:
"""All operands in the expression tree in prefix order"""
@@ -3275,6 +3255,27 @@ class LowLevelILFunction:
for block in self.basic_blocks:
yield from block
+ def traverse(self, cb: Callable[['LowLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` iterates through all the instructions in the LowLevelILFunction and calls the callback function for
+ each instruction and sub-instruction.
+
+ :param Callable[[LowLevelILInstruction, Any], Any] cb: The callback function to call for each node in the LowLevelILInstruction
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+
+ :Example:
+ >>> def find_constants(instr) -> Optional[int]:
+ ... if isinstance(instr, Constant):
+ ... return instr.constant
+ >>> print(list(current_il_function.traverse(find_constants)))
+ """
+ for instr in self.instructions:
+ yield from instr.traverse(cb, *args, **kwargs)
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILFunction.traverse` instead.")
def visit(self, cb: LowLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function
@@ -3288,6 +3289,7 @@ class LowLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILFunction.traverse` instead.")
def visit_all(self, cb: LowLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each instruction and their operands.
@@ -3300,6 +3302,7 @@ class LowLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`LowLevelILFunction.traverse` instead.")
def visit_operands(self, cb: LowLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each operand and
diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py
index db5da1a1..30f29abe 100644
--- a/python/mediumlevelil.py
+++ b/python/mediumlevelil.py
@@ -21,8 +21,9 @@
import ctypes
import struct
from typing import (Optional, List, Union, Mapping,
- Generator, NewType, Tuple, ClassVar, Dict, Set, Callable, Any)
+ Generator, NewType, Tuple, ClassVar, Dict, Set, Callable, Any, Iterator)
from dataclasses import dataclass
+from . import deprecation
# Binary Ninja components
from . import _binaryninjacore as core
@@ -425,6 +426,35 @@ class MediumLevelILInstruction(BaseILInstruction):
"""
return []
+ def traverse(self, cb: Callable[['MediumLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` is a generator that allows you to traverse the MediumLevelILInstruction in a depth-first manner. It will yield the
+ result of the callback function for each node in the tree. Arguments can be passed to the callback function using
+ ``args`` and ``kwargs``.
+
+ :param Callable[[MediumLevelILInstruction, Any], Any] cb: The callback function to call for each node in the MediumLevelILInstruction
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+
+ :Example:
+ >>> def get_constant_less_than_value(inst: MediumLevelILInstruction, value: int) -> int:
+ >>> if isinstance(inst, Constant) and inst.constant < value:
+ >>> return inst.constant
+ >>>
+ >>> list(inst.traverse(get_constant_less_than_value, 10))
+ """
+ if (result := cb(self, *args, **kwargs)) is not None:
+ yield result
+ for _, op, _ in self.detailed_operands:
+ if isinstance(op, MediumLevelILInstruction):
+ yield from op.traverse(cb, *args, **kwargs)
+ elif isinstance(op, list) and all(isinstance(i, MediumLevelILInstruction) for i in op):
+ for i in op:
+ yield from i.traverse(cb, *args, **kwargs) # type: ignore
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILInstruction.traverse` instead.")
def visit_all(self, cb: MediumLevelILVisitorCallback,
name: str = "root", parent: Optional['MediumLevelILInstruction'] = None) -> bool:
"""
@@ -448,6 +478,7 @@ class MediumLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILInstruction.traverse` instead.")
def visit_operands(self, cb: MediumLevelILVisitorCallback,
name: str = "root", parent: Optional['MediumLevelILInstruction'] = None) -> bool:
"""
@@ -468,6 +499,7 @@ class MediumLevelILInstruction(BaseILInstruction):
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILInstruction.traverse` instead.")
def visit(self, cb: MediumLevelILVisitorCallback,
name: str = "root", parent: Optional['MediumLevelILInstruction'] = None) -> bool:
"""
@@ -499,57 +531,6 @@ class MediumLevelILInstruction(BaseILInstruction):
return False
return True
- def traverse(self, cb: Callable[['MediumLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Any:
- """
- Traverses all MediumLevelILInstructions in the operands of this instruction and any sub-instructions.
- The callback you provide only needs to accept a single instruction, but accepts anything, and can return whatever you want.
-
- None is treated as a reserved value to indicate that the traverser should continue descending into subexpressions.
-
- :param cb: Callback function that takes only the instruction
- :param args: Custom user-defined arguments
- :param kwargs: Custom user-defined keyword arguments
- :return: None if your callback doesn't return anything and all instructions were traversed, otherwise it returns the value from your callback.
- :Example:
- >>> # This traverser allows for simplified function signatures in your callback
- >>> def traverser(inst) -> int:
- >>> if isinstance(inst, Constant):
- >>> return inst.constant # Stop recursion and return the constant
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all constants used in the program
- >>> for inst in bv.mlil_instructions:
- >>> if const := inst.traverse(traverser):
- >>> print(f"Found constant {const}")
-
-
- >>> # But it also allows for complex function signatures in your callback
- >>> def traverser(inst, search_constant, skip_list: List[int] = []) -> int:
- >>> if inst.address in skip_list:
- >>> return None # Skip this instruction
- >>> if isinstance(inst, Constant):
- >>> if inst.constant == search_constant:
- >>> return inst.address # Stop recursion and return the address of this use
- >>> return None # Continue descending into subexpressions
-
- >>> # Finds all instances of 0xdeadbeaf used in the program
- >>> for inst in bv.mlil_instructions:
- >>> if use_addr := inst.traverse(traverser, 0xdeadbeaf, skip_list=[0x12345678]):
- >>> print(f"Found 0xdeadbeef use at {use_addr}")
- """
-
- if (result := cb(self, *args, **kwargs)) is not None:
- return result
- for _, op, _ in self.detailed_operands:
- if isinstance(op, MediumLevelILInstruction):
- if (result := op.traverse(cb, *args, **kwargs)) is not None:
- return result
- elif isinstance(op, list) and all(isinstance(i, MediumLevelILInstruction) for i in op):
- for i in op:
- if (result := i.traverse(cb, *args, **kwargs)) is not None:
- return result
- return None
-
@property
def tokens(self) -> TokenList:
"""MLIL tokens (read-only)"""
@@ -3221,6 +3202,27 @@ class MediumLevelILFunction:
for block in self.basic_blocks:
yield from block
+ def traverse(self, cb: Callable[['MediumLevelILInstruction', Any], Any], *args: Any, **kwargs: Any) -> Iterator[Any]:
+ """
+ ``traverse`` iterates through all the instructions in the MediumLevelILInstruction and calls the callback function for
+ each instruction and sub-instruction.
+
+ :param Callable[[MediumLevelILInstruction, Any], Any] cb: Callback function that takes a HighLevelILInstruction and returns a value
+ :param Any args: Custom user-defined arguments
+ :param Any kwargs: Custom user-defined keyword arguments
+ :return: An iterator of the results of the callback function
+ :rtype: Iterator[Any]
+
+ :Example:
+ >>> def find_constants(instr) -> Optional[int]:
+ ... if isinstance(instr, Constant):
+ ... return instr.constant
+ >>> print(list(current_il_function.traverse(find_constants)))
+ """
+ for instr in self.instructions:
+ yield from instr.traverse(cb, *args, **kwargs)
+
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILFunction.traverse` instead.")
def visit(self, cb: MediumLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function
@@ -3234,6 +3236,7 @@ class MediumLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILFunction.traverse` instead.")
def visit_all(self, cb: MediumLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each instruction and their operands.
@@ -3246,6 +3249,7 @@ class MediumLevelILFunction:
return False
return True
+ @deprecation.deprecated(deprecated_in="4.0.4907", details="Use :py:func:`MediumLevelILFunction.traverse` instead.")
def visit_operands(self, cb: MediumLevelILVisitorCallback) -> bool:
"""
Iterates over all the instructions in the function and calls the callback function for each operand and