summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorPeter LaFosse <peter@vector35.com>2018-11-08 09:58:36 -0500
committerPeter LaFosse <peter@vector35.com>2018-11-13 11:07:27 -0500
commitd8ff0a6324fe92ccafe296298409c50cc4b526fa (patch)
treec5bf8cdf43902d3122a6747d5c42d9197afc0808 /python
parent9df7c7c621c82e6caa7a09c4273e4feba86277f2 (diff)
Add expression parsing APIs
Add text-based and constant-based searching
Diffstat (limited to 'python')
-rw-r--r--python/architecture.py2
-rw-r--r--python/binaryview.py117
-rw-r--r--python/scriptingprovider.py20
3 files changed, 121 insertions, 18 deletions
diff --git a/python/architecture.py b/python/architecture.py
index 5f280a98..7bef1b67 100644
--- a/python/architecture.py
+++ b/python/architecture.py
@@ -2426,6 +2426,8 @@ class CoreArchitecture(Architecture):
result = databuffer.DataBuffer()
errors = ctypes.c_char_p()
if not core.BNAssemble(self.handle, code, addr, result.handle, errors):
+ error_str = errors.value
+ core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
raise ValueError("Could not assemble: %s" % errors.value)
if isinstance(str(result), bytes):
return str(result)
diff --git a/python/binaryview.py b/python/binaryview.py
index afde3a3c..60ccf91f 100644
--- a/python/binaryview.py
+++ b/python/binaryview.py
@@ -22,11 +22,12 @@ import struct
import traceback
import ctypes
import abc
+import numbers
# Binary Ninja components
from binaryninja import _binaryninjacore as core
from binaryninja.enums import (AnalysisState, SymbolType, InstructionTextTokenType,
- Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics)
+ Endianness, ModificationStatus, StringType, SegmentFlag, SectionSemantics, FindFlag)
import binaryninja
from binaryninja import associateddatastore # required for _BinaryViewAssociatedDataStore
from binaryninja import log
@@ -37,6 +38,7 @@ from binaryninja import basicblock
from binaryninja import lineardisassembly
from binaryninja import metadata
from binaryninja import highlight
+from binaryninja import function
# 2-3 compatibility
from binaryninja import range
@@ -3614,21 +3616,20 @@ class BinaryView(object):
"""
core.BNRegisterPlatformTypes(self.handle, platform.handle)
- def find_next_data(self, start, data, flags = 0):
+ def find_next_data(self, start, data, flags=FindFlag.FindCaseSensitive):
"""
- ``find_next_data`` searchs for the bytes in data starting at the virtual address ``start`` either, case-sensitive,
- or case-insensitive.
+ ``find_next_data`` searchs for the bytes ``data`` starting at the virtual address ``start`` until the end of the BinaryView.
:param int start: virtual address to start searching from.
- :param str data: bytes to search for
- :param FindFlags flags: case-sensitivity flag, one of the following:
+ :param str data: data to search for
+ :param FindFlag flags: (optional) defaults to case-insensitive data search
- ==================== ======================
- FindFlags Description
- ==================== ======================
- NoFindFlags Case-sensitive find
- FindCaseInsensitive Case-insensitive find
- ==================== ======================
+ ==================== ============================
+ FindFlag Description
+ ==================== ============================
+ FindCaseSensitive Case-sensitive search
+ FindCaseInsensitive Case-insensitive search
+ ===================== ============================
"""
buf = databuffer.DataBuffer(str(data))
result = ctypes.c_ulonglong()
@@ -3636,6 +3637,55 @@ class BinaryView(object):
return None
return result.value
+
+ def find_next_text(self, start, text, settings=None, flags=FindFlag.FindCaseSensitive):
+ """
+ ``find_next_text`` searchs for string ``text`` occuring in the linear view output starting at the virtual
+ address ``start`` until the end of the BinaryView.
+
+ :param int start: virtual address to start searching from.
+ :param str text: text to search for
+ :param FindFlag flags: (optional) defaults to case-insensitive data search
+
+ ==================== ============================
+ FindFlag Description
+ ==================== ============================
+ FindCaseSensitive Case-sensitive search
+ FindCaseInsensitive Case-insensitive search
+ ===================== ============================
+ """
+ if not isinstance(text, str):
+ raise TypeError("text parameter is not str type")
+ if settings is None:
+ settings = function.DisassemblySettings()
+ if not isinstance(settings, function.DisassemblySettings):
+ raise TypeError("settings parameter is not DisassemblySettings type")
+
+ result = ctypes.c_ulonglong()
+ if not core.BNFindNextText(self.handle, start, text, result, settings.handle, flags):
+ return None
+ return result.value
+
+ def find_next_constant(self, start, constant, settings=None):
+ """
+ ``find_next_constant`` searchs for integer constant ``constant`` occuring in the linear view output starting at the virtual
+ address ``start`` until the end of the BinaryView.
+
+ :param int start: virtual address to start searching from.
+ :param int constant: constant to search for
+ """
+ if not isinstance(constant, numbers.Integral):
+ raise TypeError("constant parameter is not integral type")
+ if settings is None:
+ settings = function.DisassemblySettings()
+ if not isinstance(settings, function.DisassemblySettings):
+ raise TypeError("settings parameter is not DisassemblySettings type")
+
+ result = ctypes.c_ulonglong()
+ if not core.BNFindNextConstant(self.handle, start, constant, result, settings.handle):
+ return None
+ return result.value
+
def reanalyze(self):
"""
``reanalyze`` causes all functions to be reanalyzed. This function does not wait for the analysis to finish.
@@ -3798,6 +3848,49 @@ class BinaryView(object):
except AttributeError:
raise AttributeError("attribute '%s' is read only" % name)
+ def parse_expression(self, expression, here=0):
+ """
+ Evaluates an string expression to an integer value.
+
+ The parser uses the following rules:
+ - symbols are defined by the lexer as `[A-Za-z0-9_:<>][A-Za-z0-9_:$\-<>]+` or anything enclosed in either single or
+ double quotes
+ - Numbers are defaulted to hexadecimal thus `_printf + 10` is equivilent to `printf + 0x10` If decimal numbers required use the decimal prefix.
+ - Since numbers and symbols can be ambiguous its recommended that you prefix your numbers with the following:
+ - 0x - Hexadecimal
+ - 0n - Decimal
+ - 0 - Octal
+ - In the case of an ambiguous number/symbol (one with no prefix) for instance `12345` we will first attempt
+ to look up the string as a symbol, if a symbol is found its address is used, otherwise we attempt to convert
+ it to a hexadecmial number.
+ - The following operations are valid: +, -, *, /, %, (), &, |, ^, ~
+ - In addition to the above operators there are _il-style_ dereference operators
+ - [<expression>] - read the _current address size_ at <expression>
+ - [<expression>].b - read the byte at <expression>
+ - [<expression>].w - read the word (2 bytes) at <expression>
+ - [<expression>].d - read the dword (4 bytes) at <expression>
+ - [<expression>].q - read the quadword (8 bytes) at <expression>
+ - The `$here` keyword can be used in calculations and is defined as the `here` parameter
+
+ :param string expression: Arithmetic expression to be evaluated
+ :param int here: (optional) Base address for relative expressions, defaults to zero
+ :rtype: int
+ """
+ offset = ctypes.c_ulonglong()
+ errors = ctypes.c_char_p()
+ if not core.BNParseExpression(self.handle, expression, offset, here, errors):
+ error_str = errors.value
+ core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte)))
+ raise ValueError(error_str)
+ return offset.value
+
+ def eval(self, expression, here=0):
+ """
+ Evaluates an string expression to an integer value. This is a more concise alias for the `parse_expression` API
+ See `parse_expression` for details on usage.
+ """
+ return self.parse_expression(expression, here)
+
class BinaryReader(object):
"""
diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py
index 5b5d830e..52584ec7 100644
--- a/python/scriptingprovider.py
+++ b/python/scriptingprovider.py
@@ -557,12 +557,20 @@ class PythonScriptingInstance(ScriptingInstance):
for line in code.split(b'\n'):
self.interpreter.push(line.decode('charmap'))
- if self.locals["here"] != self.active_addr:
- if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]):
- sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"])
- elif self.locals["current_address"] != self.active_addr:
- if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]):
- sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"])
+ tryNavigate = True
+ if isinstance(self.locals["here"], str) or isinstance(self.locals["current_address"], str):
+ try:
+ self.locals["here"] = self.active_view.parse_expression(self.locals["here"], self.active_addr)
+ except ValueError as e:
+ sys.stderr.write(e)
+ tryNavigate = False
+ if tryNavigate:
+ if self.locals["here"] != self.active_addr:
+ if not self.active_view.file.navigate(self.active_view.file.view, self.locals["here"]):
+ sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["here"])
+ elif self.locals["current_address"] != self.active_addr:
+ if not self.active_view.file.navigate(self.active_view.file.view, self.locals["current_address"]):
+ sys.stderr.write("Address 0x%x is not valid for the current view\n" % self.locals["current_address"])
if self.active_view is not None:
self.active_view.update_analysis()
except: