diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/binaryview.py | 35 | ||||
| -rw-r--r-- | python/databuffer.py | 4 | ||||
| -rw-r--r-- | python/examples/pseudo_python.py | 1100 | ||||
| -rw-r--r-- | python/function.py | 77 | ||||
| -rw-r--r-- | python/highlevelil.py | 4 | ||||
| -rw-r--r-- | python/languagerepresentation.py | 822 | ||||
| -rw-r--r-- | python/lineardisassembly.py | 10 | ||||
| -rw-r--r-- | python/scriptingprovider.py | 18 | ||||
| -rw-r--r-- | python/types.py | 67 | ||||
| -rw-r--r-- | python/variable.py | 10 |
11 files changed, 2118 insertions, 30 deletions
diff --git a/python/__init__.py b/python/__init__.py index 84494296..b6a3de97 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -79,6 +79,7 @@ from .debuginfo import * from .externallibrary import * from .undo import * from .fileaccessor import * +from .languagerepresentation import * # We import each of these by name to prevent conflicts between # log.py and the function 'log' which we don't import below from .log import ( diff --git a/python/binaryview.py b/python/binaryview.py index fe270c9a..6adb8b1e 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -8545,7 +8545,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_next_text( self, start: int, text: str, settings: Optional[_function.DisassemblySettings] = None, flags: FindFlag = FindFlag.FindCaseSensitive, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph ) -> Optional[int]: """ ``find_next_text`` searches for string ``text`` occurring in the linear view output starting at the virtual @@ -8562,7 +8562,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within """ if not isinstance(text, str): raise TypeError("text parameter is not str type") @@ -8572,13 +8572,14 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if not core.BNFindNextText(self.handle, start, text, result, settings.handle, flags, graph_type): return None return result.value def find_next_constant( self, start: int, constant: int, settings: Optional[_function.DisassemblySettings] = None, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph ) -> Optional[int]: """ ``find_next_constant`` searches for integer constant ``constant`` occurring in the linear view output starting at the virtual @@ -8587,7 +8588,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" :param int start: virtual address to start searching from. :param int constant: constant to search for :param DisassemblySettings settings: disassembly settings - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within """ if not isinstance(constant, int): raise TypeError("constant parameter is not integral type") @@ -8597,6 +8598,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") result = ctypes.c_ulonglong() + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if not core.BNFindNextConstant(self.handle, start, constant, result, settings.handle, graph_type): return None return result.value @@ -8706,7 +8708,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_all_text( self, start: int, end: int, text: str, settings: Optional[_function.DisassemblySettings] = None, - flags=FindFlag.FindCaseSensitive, graph_type=FunctionGraphType.NormalFunctionGraph, progress_func=None, + flags=FindFlag.FindCaseSensitive, graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func=None, match_callback=None ) -> QueueGenerator: """ @@ -8727,7 +8729,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" FindCaseSensitive Case-sensitive search FindCaseInsensitive Case-insensitive search ==================== ============================ - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress \ and total count. This function should return a boolean value that decides whether the \ search should continue or stop @@ -8752,6 +8754,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" raise TypeError("settings parameter is not DisassemblySettings type") if not isinstance(flags, FindFlag): raise TypeError('flag parameter must have type FindFlag') + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if progress_func: progress_func_obj = ctypes.CFUNCTYPE( @@ -8799,7 +8802,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def find_all_constant( self, start: int, end: int, constant: int, settings: Optional[_function.DisassemblySettings] = None, - graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None, + graph_type: _function.FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, progress_func: Optional[ProgressFuncType] = None, match_callback: Optional[LineMatchCallbackType] = None ) -> QueueGenerator: """ @@ -8817,7 +8820,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" :param int constant: constant to search for :param DisassemblySettings settings: DisassemblySettings object used to render the text \ to be searched - :param FunctionGraphType graph_type: the IL to search within + :param FunctionViewType graph_type: the IL to search within :param callback progress_func: optional function to be called with the current progress \ and total count. This function should return a boolean value that decides whether the \ search should continue or stop @@ -8839,6 +8842,7 @@ to a the type "tagRECT" found in the typelibrary "winX64common" settings.set_option(DisassemblyOption.WaitForIL, True) if not isinstance(settings, _function.DisassemblySettings): raise TypeError("settings parameter is not DisassemblySettings type") + graph_type = _function.FunctionViewType(graph_type)._to_core_struct() if progress_func: progress_func_obj = ctypes.CFUNCTYPE( @@ -9628,6 +9632,21 @@ to a the type "tagRECT" found in the typelibrary "winX64common" def memory_map(self): return MemoryMap(handle=self.handle) + def stringify_unicode_data( + self, arch: Optional['architecture.Architecture'], buffer: 'databuffer.DataBuffer', + allow_short_strings: bool = False + ) -> Tuple[Optional[str], Optional[StringType]]: + string = ctypes.c_char_p() + string_type = ctypes.c_int() + if arch is not None: + arch = arch.handle + if not core.BNStringifyUnicodeData( + self.handle, arch, buffer.handle, allow_short_strings, ctypes.byref(string), ctypes.byref(string_type)): + return None, None + result = string.value + core.BNFreeString(string) + return result, StringType(string_type.value) + class BinaryReader: """ ``class BinaryReader`` is a convenience class for reading binary data. diff --git a/python/databuffer.py b/python/databuffer.py index ce8a68e7..36cf4430 100644 --- a/python/databuffer.py +++ b/python/databuffer.py @@ -143,8 +143,8 @@ class DataBuffer: return False return bytes(self) == bytes(other) - def escape(self, null_terminates=False) -> str: - return core.BNDataBufferToEscapedString(self.handle, null_terminates) + def escape(self, null_terminates=False, escape_printable=False) -> str: + return core.BNDataBufferToEscapedString(self.handle, null_terminates, escape_printable) def unescape(self) -> 'DataBuffer': return DataBuffer(handle=core.BNDecodeEscapedString(str(self))) diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py new file mode 100644 index 00000000..b68b4786 --- /dev/null +++ b/python/examples/pseudo_python.py @@ -0,0 +1,1100 @@ +# Copyright (c) 2024 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +from binaryninja import (Architecture, BraceRequirement, DisassemblySettings, DisassemblyTextLine, Function, + InstructionTextToken, InstructionTextTokenType, LanguageRepresentationFunction, + LanguageRepresentationFunctionType, HighLevelILInstruction, HighLevelILFunction, + HighLevelILTokenEmitter, HighLevelILOperation, OperatorPrecedence, ScopeType, + SymbolDisplayType, SymbolDisplayResult, SymbolType, BoolType, VoidType, PointerType, + NamedTypeReferenceType, StructureType, InstructionTextTokenContext, StructureMember, + BinaryView, BuiltinType) +from typing import Optional +import struct + + +class PseudoPythonFunction(LanguageRepresentationFunction): + comment_start_string = "# " + + def perform_init_token_emitter(self, emitter): + # Python never allows braces, even if the user has set the option to show them + emitter.brace_requirement = BraceRequirement.BracesNotAllowed + + def perform_begin_lines(self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + # Ensure that the function body is indented relative to the function declaration + tokens.increase_indent() + + def perform_get_expr_text( + self, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter, settings: Optional[DisassemblySettings], + as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, + statement: bool = False + ): + with tokens.expr(instr): + if instr.operation == HighLevelILOperation.HLIL_BLOCK: + need_separator = False + body = instr.body + # Emit the lines for each statement in the body + for (idx, i) in enumerate(body): + # Don't display trailing return statements that don't have values + if (as_full_ast and idx + 1 == len(body) and i.operation == HighLevelILOperation.HLIL_RET and + len(i.src) == 0 and instr.expr_index == self.hlil.root.expr_index): + continue + + # If the statement is one that contains additional blocks of code, insert a scope separator + # to visually separate the logic. + has_blocks = i.operation in [HighLevelILOperation.HLIL_IF, HighLevelILOperation.HLIL_WHILE, + HighLevelILOperation.HLIL_DO_WHILE, HighLevelILOperation.HLIL_FOR, + HighLevelILOperation.HLIL_SWITCH] + if need_separator or (idx != 0 and has_blocks): + tokens.scope_separator() + need_separator = has_blocks + + # Emit the lines for the statement itself + self.perform_get_expr_text(i, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.new_line() + elif instr.operation == HighLevelILOperation.HLIL_IF: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "if ")) + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":\n")) + if as_full_ast: + # Only display the if body when printing the full AST. When printing basic blocks in graph view, + # the body of the if and the else part are rendered as other nodes in the graph. + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.true, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + + # Else statements need to be handled as chains, since "else if" in Python should be rendered + # as "elif" statements. + if_chain = instr.false + while if_chain is not None and if_chain.operation not in [HighLevelILOperation.HLIL_NOP, + HighLevelILOperation.HLIL_UNREACHABLE]: + if if_chain.operation == HighLevelILOperation.HLIL_IF: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "elif ")) + self.perform_get_expr_text(if_chain.condition, tokens, settings, as_full_ast) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(if_chain.true, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + if_chain = if_chain.false + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "else")) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(if_chain, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + break + elif instr.operation == HighLevelILOperation.HLIL_FOR: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "for ")) + + # If the for loop can be represented as a Python range function, show it that way + dest_var = None + if instr.init.operation == HighLevelILOperation.HLIL_VAR_INIT: + dest_var = instr.init.dest + elif instr.init.operation == HighLevelILOperation.HLIL_ASSIGN and instr.init.dest.operation == HighLevelILOperation.HLIL_VAR: + dest_var = instr.init.dest.var + if (dest_var is not None and + (instr.condition.operation in [HighLevelILOperation.HLIL_CMP_SLT, + HighLevelILOperation.HLIL_CMP_ULT]) and + instr.condition.left.operation == HighLevelILOperation.HLIL_VAR and + instr.condition.left.var == dest_var and + instr.update.operation == HighLevelILOperation.HLIL_ASSIGN and + instr.update.dest.operation == HighLevelILOperation.HLIL_VAR and + instr.update.dest.var == instr.condition.left.var and + instr.update.src.operation == HighLevelILOperation.HLIL_ADD and + instr.update.src.left.operation == HighLevelILOperation.HLIL_VAR and + instr.update.src.left.var == instr.condition.left.var): + step_by = (instr.update.src.right.operation != HighLevelILOperation.HLIL_CONST or + instr.update.src.right.constant != 1) + start_required = (instr.init.src.operation != HighLevelILOperation.HLIL_CONST or + instr.init.src.constant != 0 or step_by) + tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, dest_var.name, + context=InstructionTextTokenContext.LocalVariableTokenContext, + address=instr.expr_index, value=dest_var.identifier, + size=instr.size)) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, " in ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "range")) + tokens.append_open_paren() + if start_required: + self.perform_get_expr_text(instr.init.src, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.condition.right, tokens, settings) + if step_by: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.update.src.right, tokens, settings) + tokens.append_close_paren() + else: + # Not representable directly as a Python loop, emit it with a more HLIL-like syntax + if instr.init.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.init, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; ")) + if instr.condition.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "; ")) + if instr.update.operation != HighLevelILOperation.HLIL_NOP: + self.perform_get_expr_text(instr.update, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + elif instr.operation == HighLevelILOperation.HLIL_WHILE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while ")) + self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + elif instr.operation == HighLevelILOperation.HLIL_DO_WHILE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "do")) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.BlockScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.BlockScopeType) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "while ")) + self.perform_get_expr_text(instr.condition, tokens, settings, as_full_ast) + elif instr.operation == HighLevelILOperation.HLIL_SWITCH: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "match ")) + self.perform_get_expr_text(instr.condition, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, ":")) + tokens.begin_scope(ScopeType.SwitchScopeType) + + # Output each case + for case in instr.cases: + self.perform_get_expr_text(case, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.new_line() + + # Check for default case + if instr.default is not None and instr.default.operation not in [HighLevelILOperation.HLIL_NOP, + HighLevelILOperation.HLIL_UNREACHABLE]: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "default")) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.CaseScopeType) + self.perform_get_expr_text(instr.default, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.CaseScopeType) + tokens.end_scope(ScopeType.SwitchScopeType) + elif instr.operation == HighLevelILOperation.HLIL_CASE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "case ")) + for (i, value) in enumerate(instr.values): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " | ")) + self.perform_get_expr_text(value, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.begin_scope(ScopeType.CaseScopeType) + self.perform_get_expr_text(instr.body, tokens, settings, as_full_ast, + OperatorPrecedence.TopLevelOperatorPrecedence, True) + tokens.end_scope(ScopeType.CaseScopeType) + elif instr.operation == HighLevelILOperation.HLIL_BREAK: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "break")) + elif instr.operation == HighLevelILOperation.HLIL_CONTINUE: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "continue")) + elif instr.operation == HighLevelILOperation.HLIL_CALL: + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_TAILCALL: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return ")) + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, " # tailcall")) + elif instr.operation == HighLevelILOperation.HLIL_INTRINSIC: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, instr.intrinsic.name)) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SYSCALL: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "syscall")) + tokens.append_open_paren() + for (i, param) in enumerate(instr.params): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + if i == 0 and param.operation == HighLevelILOperation.HLIL_CONST: + platform = self.function.platform + if platform is not None: + syscall_name = platform.get_system_call_name(param.constant) + if len(syscall_name) > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, syscall_name)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "{")) + self.perform_get_expr_text(param, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "}")) + continue + self.perform_get_expr_text(param, tokens, settings) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ZX: + self.perform_get_expr_text(instr.src, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_SX: + self.perform_get_expr_text(instr.src, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_IMPORT: + # Check for import address symbol at target address, and display that if there is one + sym = instr.function.source_function.view.get_symbol_at(instr.constant) + if sym is not None: + if sym.type == SymbolType.ImportedDataSymbol or sym.type == SymbolType.ImportAddressSymbol: + sym = sym.imported_function_from_import_address_symbol(instr.constant) + if sym is not None: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IndirectImportToken, sym.short_name, + value=instr.constant, address=instr.address, + size=instr.size, operand=instr.source_operand)) + return + # Otherwise use a generic pointer token + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.DereferenceNonDataSymbols, precedence) + elif instr.operation == HighLevelILOperation.HLIL_ARRAY_INDEX: + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + tokens.append_open_bracket() + self.perform_get_expr_text(instr.index, tokens, settings) + tokens.append_close_bracket() + elif instr.operation == HighLevelILOperation.HLIL_VAR_INIT: + # Check to see if the variable appears live + appears_dead = False + ssa = instr.ssa_form + if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA: + appears_dead = not self.hlil.is_ssa_var_live(ssa.dest) + + # If the variable does not appear live, show the assignment as zero confidence (grayed out) + with tokens.force_zero_confidence(appears_dead): + tokens.append_var_text_token(instr.dest, instr, instr.size) + if instr.dest.type is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + tokens.append(instr.dest.type.get_tokens()) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + + # For the right side of the assignment, only use zero confidence if the instruction does + # not have any side effects + with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects): + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_VAR_DECLARE: + tokens.append_var_text_token(instr.var, instr, instr.size) + if instr.var.type is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + tokens.append(instr.var.type.get_tokens()) + elif instr.operation == HighLevelILOperation.HLIL_FLOAT_CONST: + # The constant value in the instruction contains the raw bits of the floating point value. Convert + # this to a floating point value and display it. + if instr.size == 4: + value = struct.unpack("<f", struct.pack("<I", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + elif instr.size == 8: + value = struct.unpack("<d", struct.pack("<Q", instr.constant))[0] + tokens.append(InstructionTextToken(InstructionTextTokenType.FloatToken, f"{value:g}")) + else: + tokens.append_integer_text_token(instr, instr.constant, instr.size) + elif instr.operation == HighLevelILOperation.HLIL_CONST: + # Check for bool type. Display these as True or False. The default handling will use C style + # booleans instead of Python style. + if instr.size == 0 or isinstance(instr.expr_type, BoolType): + if instr.constant != 0: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IntegerToken, "True", address=instr.address, + value=instr.constant)) + else: + tokens.append( + InstructionTextToken(InstructionTextTokenType.IntegerToken, "False", address=instr.address, + value=instr.constant)) + else: + tokens.append_constant_text_token(instr, instr.constant, instr.size, settings, precedence) + elif instr.operation == HighLevelILOperation.HLIL_CONST_PTR: + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.AddressOfDataSymbols, precedence) + elif instr.operation == HighLevelILOperation.HLIL_CONST_DATA: + # Constant data should be rendered according to the type of builtin function being used. + data, builtin = instr.constant_data.data_and_builtin + if builtin in [BuiltinType.BuiltinStrcpy, BuiltinType.BuiltinStrncpy]: + data = data.escape(null_terminates=True) + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"', + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstStringDataTokenContext)) + elif builtin == BuiltinType.BuiltinMemset: + tokens.append_open_brace() + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, + f"{instr.constant_data.value:#x}", + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstDataTokenContext)) + tokens.append_close_brace() + else: + string, string_type = self.function.view.stringify_unicode_data(self.function.arch, data) + if string is not None: + wide_string_prefix = "" + token_context = InstructionTextTokenContext.ConstDataTokenContext + if builtin == BuiltinType.BuiltinWcscpy: + wide_string_prefix = "L" + token_context = InstructionTextTokenContext.ConstStringDataTokenContext + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, + f'{wide_string_prefix}"{string}"', + address=instr.address, value=instr.constant_data.value, + context=token_context)) + else: + data = data.escape(null_terminates=False, escape_printable=True) + tokens.append(InstructionTextToken(InstructionTextTokenType.StringToken, f'"{data}"', + address=instr.address, value=instr.constant_data.value, + context=InstructionTextTokenContext.ConstDataTokenContext)) + elif instr.operation == HighLevelILOperation.HLIL_EXTERN_PTR: + # Extern pointer instructions have an offset associated with them. If this is nonzero, show this + # as an addition or subtraction of the offset. + parens = instr.offset != 0 and precedence >= OperatorPrecedence.AddOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.offset != 0: + precedence = OperatorPrecedence.SubOperatorPrecedence + tokens.append_pointer_text_token(instr, instr.constant, settings, + SymbolDisplayType.AddressOfDataSymbols, precedence) + if instr.offset < 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " - ")) + tokens.append_integer_text_token(instr, -instr.offset, instr.size) + elif instr.offset > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " + ")) + tokens.append_integer_text_token(instr, instr.offset, instr.size) + + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_VAR: + tokens.append(InstructionTextToken(InstructionTextTokenType.LocalVariableToken, instr.var.name, + address=instr.expr_index, size=instr.size, + value=instr.var.identifier, + context=InstructionTextTokenContext.LocalVariableTokenContext)) + elif instr.operation == HighLevelILOperation.HLIL_ASSIGN: + # Check to see if the variable appears live + appears_dead = False + if instr.dest.operation == HighLevelILOperation.HLIL_VAR: + ssa = instr.ssa_form + if ssa is not None and ssa.operation == HighLevelILOperation.HLIL_VAR_INIT_SSA: + appears_dead = not self.hlil.is_ssa_var_live(ssa.dest) + + # If the variable does not appear live, show the assignment as zero confidence (grayed out) + with tokens.force_zero_confidence(appears_dead): + self.perform_get_expr_text(instr.dest, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + + # For the right side of the assignment, only use zero confidence if the instruction does + # not have any side effects + with tokens.force_zero_confidence(appears_dead and not instr.src.has_side_effects): + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_ASSIGN_UNPACK: + tokens.append_open_paren() + for (i, dest) in enumerate(instr.dest): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(dest, tokens, settings) + tokens.append_close_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " = ")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.AssignmentOperatorPrecedence) + elif instr.operation == HighLevelILOperation.HLIL_STRUCT_FIELD: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_DEREF_FIELD: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR: + tokens.append_pointer_text_token(instr.src, instr.src.constant, settings, + SymbolDisplayType.DisplaySymbolOnly, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + else: + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.MemberAndFunctionOperatorPrecedence) + self.append_field_text_tokens(instr.src, instr.offset, instr.member_index, instr.size, tokens) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_DEREF: + if instr.src.operation == HighLevelILOperation.HLIL_CONST_PTR: + if tokens.append_pointer_text_token(instr.src, instr.src.constant, settings, + SymbolDisplayType.DereferenceNonDataSymbols, + precedence) == SymbolDisplayResult.DataSymbolResult: + expr_type = instr.src.expr_type + if isinstance(expr_type, PointerType) and expr_type.target.width != instr.size: + # If dereference size doesn't match the data variable size, append a size suffix + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if instr.size in suffix: + suffix_str = suffix[instr.size] + else: + suffix_str = f".{instr.size}" + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, suffix_str)) + else: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "*")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADDRESS_OF: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "&")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_E, HighLevelILOperation.HLIL_FCMP_E]: + parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " == ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_NE, HighLevelILOperation.HLIL_FCMP_NE]: + parens = precedence > OperatorPrecedence.EqualityOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " != ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.EqualityOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLT, HighLevelILOperation.HLIL_CMP_ULT, + HighLevelILOperation.HLIL_FCMP_LT]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " < ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SLE, HighLevelILOperation.HLIL_CMP_ULE, + HighLevelILOperation.HLIL_FCMP_LE]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " <= ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGE, HighLevelILOperation.HLIL_CMP_UGE, + HighLevelILOperation.HLIL_FCMP_GE]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " >= ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_CMP_SGT, HighLevelILOperation.HLIL_CMP_UGT, + HighLevelILOperation.HLIL_FCMP_GT]: + parens = precedence > OperatorPrecedence.CompareOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, " > ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, + OperatorPrecedence.CompareOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_AND: + if instr.size == 0: + # Size of zero is a boolean operation, show the boolean operator name + parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or + precedence == OperatorPrecedence.LogicalOrOperatorPrecedence) + operation = "and" + precedence = OperatorPrecedence.LogicalAndOperatorPrecedence + else: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + operation = "&" + precedence = OperatorPrecedence.BitwiseAndOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_OR: + if instr.size == 0: + # Size of zero is a boolean operation, show the boolean operator name + parens = (precedence >= OperatorPrecedence.BitwiseOrOperatorPrecedence or + precedence == OperatorPrecedence.LogicalAndOperatorPrecedence) + operation = "or" + precedence = OperatorPrecedence.LogicalOrOperatorPrecedence + else: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + operation = "|" + precedence = OperatorPrecedence.BitwiseOrOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(operation, instr, tokens, settings, as_full_ast, precedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_XOR: + parens = (precedence >= OperatorPrecedence.EqualityOperatorPrecedence or + precedence in [OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("^", instr, tokens, settings, as_full_ast, + OperatorPrecedence.BitwiseXorOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "adc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SBB: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sbb")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ADD_OVERFLOW: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "add_overflow")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_ADD, HighLevelILOperation.HLIL_FADD]: + parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("+", instr, tokens, settings, as_full_ast, + OperatorPrecedence.AddOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_SUB, HighLevelILOperation.HLIL_FSUB]: + parens = (precedence > OperatorPrecedence.AddOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("-", instr, tokens, settings, as_full_ast, + OperatorPrecedence.SubOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_MUL, HighLevelILOperation.HLIL_MULS_DP, + HighLevelILOperation.HLIL_MULU_DP, HighLevelILOperation.HLIL_FMUL]: + parens = (precedence > OperatorPrecedence.MultiplyOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("*", instr, tokens, settings, as_full_ast, + OperatorPrecedence.MultiplyOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_DIVS, HighLevelILOperation.HLIL_DIVU, + HighLevelILOperation.HLIL_DIVS_DP, HighLevelILOperation.HLIL_DIVU_DP]: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("//", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_MODS, HighLevelILOperation.HLIL_MODU, + HighLevelILOperation.HLIL_MODS_DP, HighLevelILOperation.HLIL_MODU_DP]: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("%", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FDIV: + parens = (precedence > OperatorPrecedence.DivideOperatorPrecedence or + precedence in [OperatorPrecedence.ShiftOperatorPrecedence, + OperatorPrecedence.BitwiseAndOperatorPrecedence, + OperatorPrecedence.BitwiseOrOperatorPrecedence, + OperatorPrecedence.BitwiseXorOperatorPrecedence]) + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("/", instr, tokens, settings, as_full_ast, + OperatorPrecedence.DivideOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_LSL: + parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens("<<", instr, tokens, settings, as_full_ast, + OperatorPrecedence.ShiftOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_LSR, HighLevelILOperation.HLIL_ASR]: + parens = precedence > OperatorPrecedence.ShiftOperatorPrecedence + if parens: + tokens.append_open_paren() + self.append_two_operand_tokens(">>", instr, tokens, settings, as_full_ast, + OperatorPrecedence.ShiftOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROL: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rol")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROR: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ror")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RLC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rlc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RRC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "rrc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.carry, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_TEST_BIT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "test_bit")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FLOOR: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "floor")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_CEIL: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "ceil")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FTRUNC: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trunc")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FABS: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fabs")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FSQRT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "sqrt")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_ROUND_TO_INT: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "round")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FCMP_O: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_o")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_FCMP_UO: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "fcmp_uo")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.left, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.right, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_NOT: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + if instr.size == 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "not ")) + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "~")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_NEG, HighLevelILOperation.HLIL_FNEG]: + parens = precedence > OperatorPrecedence.UnaryOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "-")) + self.perform_get_expr_text(instr.src, tokens, settings, as_full_ast, + OperatorPrecedence.UnaryOperatorPrecedence) + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_CONV, HighLevelILOperation.HLIL_INT_TO_FLOAT]: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "float")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_FLOAT_TO_INT, HighLevelILOperation.HLIL_BOOL_TO_INT]: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "int")) + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + tokens.append_close_paren() + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_RET: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "return")) + operands = instr.src + if len(operands) > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) + if len(operands) > 1: + tokens.append_open_paren() + for (i, operand) in enumerate(operands): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(operand, tokens, settings) + if len(operands) > 1: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_NORET: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# no return")) + elif instr.operation == HighLevelILOperation.HLIL_UNREACHABLE: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# unreachable")) + elif instr.operation == HighLevelILOperation.HLIL_UNDEF: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# undefined")) + elif instr.operation == HighLevelILOperation.HLIL_NOP: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# nop")) + elif instr.operation == HighLevelILOperation.HLIL_BP: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "breakpoint")) + tokens.append_open_paren() + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_JUMP: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# jump -> ")) + self.perform_get_expr_text(instr.dest, tokens, settings) + elif instr.operation == HighLevelILOperation.HLIL_TRAP: + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, "trap")) + tokens.append_open_paren() + tokens.append_integer_text_token(instr, instr.vector, 8) + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_GOTO: + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "goto ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name, + instr.target.label_id)) + elif instr.operation == HighLevelILOperation.HLIL_LABEL: + tokens.decrease_indent() + tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, instr.target.name, + instr.target.label_id)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + tokens.increase_indent() + elif instr.operation == HighLevelILOperation.HLIL_LOW_PART: + parens = precedence > OperatorPrecedence.MemberAndFunctionOperatorPrecedence + if parens: + tokens.append_open_paren() + self.perform_get_expr_text(instr.src, tokens, settings) + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if instr.size in suffix: + suffix_str = suffix[instr.size] + else: + suffix_str = f".{instr.size}" + tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, suffix_str, value=0, + size=instr.size)) + if parens: + tokens.append_close_paren() + elif instr.operation == HighLevelILOperation.HLIL_SPLIT: + tokens.append_open_paren() + self.perform_get_expr_text(instr.high, tokens, settings) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + self.perform_get_expr_text(instr.low, tokens, settings) + tokens.append_close_paren() + elif instr.operation in [HighLevelILOperation.HLIL_UNIMPL, HighLevelILOperation.HLIL_UNIMPL_MEM]: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, "# ")) + for token in instr.tokens: + token.token_type = InstructionTextTokenType.AnnotationToken + tokens.append(token) + else: + tokens.append(InstructionTextToken(InstructionTextTokenType.AnnotationToken, + f"# unimplemented {instr.operation.name}")) + + def append_two_operand_tokens(self, operation: str, instr: HighLevelILInstruction, tokens: HighLevelILTokenEmitter, + settings: Optional[DisassemblySettings], as_full_ast: bool, + precedence: OperatorPrecedence): + if precedence == OperatorPrecedence.SubOperatorPrecedence: + # Treat left side of subtraction as same level as addition. This lets + # (a - b) - c be represented as a - b - c, but a - (b - c) does not + # simplify at rendering + left_precedence = OperatorPrecedence.AddOperatorPrecedence + elif precedence == OperatorPrecedence.DivideOperatorPrecedence: + # Treat left side of divison as same level as multiplication. This lets + # (a / b) / c be represented as a / b / c, but a / (b / c) does not + # simplify at rendering + left_precedence = OperatorPrecedence.MultiplyOperatorPrecedence + else: + left_precedence = precedence + + self.perform_get_expr_text(instr.left, tokens, settings, as_full_ast, left_precedence) + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, f" {operation} ")) + self.perform_get_expr_text(instr.right, tokens, settings, as_full_ast, precedence) + + def append_field_text_tokens(self, var: HighLevelILInstruction, offset: int, member_index: int, size: int, + tokens: HighLevelILTokenEmitter): + var_type = var.expr_type + # Follow pointer type to its target + if isinstance(var_type, PointerType): + var_type = var_type.target + # Follow named type references to the target + if isinstance(var_type, NamedTypeReferenceType): + target_type = var_type.target(var.function.view) + if target_type is not None: + var_type = target_type + + has_field = False + if isinstance(var_type, StructureType): + # For structures, resolve field names using the type API + class Resolver: + def __init__(self, view: BinaryView, offset: int): + self.has_field = False + self.correct_size = False + self.offset = offset + self.view = view + + def resolve_func(self, base_name: Optional[NamedTypeReferenceType], + resolved_struct: Optional[StructureType], resolved_member_index: int, + struct_offset: int, adjusted_offset: int, member: StructureMember): + tokens.append(InstructionTextToken(InstructionTextTokenType.OperationToken, ".")) + name_list = HighLevelILTokenEmitter.names_for_outer_structure_members( + self.view, var_type, var) + [member.name] + tokens.append(InstructionTextToken(InstructionTextTokenType.FieldNameToken, member.name, + value=struct_offset + member.offset, typeNames=name_list)) + self.offset = adjusted_offset - member.offset + self.has_field = True + self.correct_size = member.type is not None and size == member.type.width + + resolver = Resolver(self.function.view, offset) + result = var_type.resolve_member_or_base_member(resolver.view, offset, 0, resolver.resolve_func) + if result and resolver.has_field and resolver.correct_size: + # If the field was matched, we're done + return + has_field = resolver.has_field + offset = resolver.offset + + # Generate offset syntax for the missing field + suffix = {0: "", 1: ".b", 2: ".w", 4: ".d", 8: ".q", 10: ".t", 16: ".q"} + if size in suffix: + suffix_str = suffix[size] + else: + suffix_str = f".{size}" + if (has_field or not isinstance(var_type, StructureType)) and offset == 0: + # No offset, just display a size suffix + offset_str = suffix_str + else: + # Has an offset + offset_str = f".__offset({offset:#x}){suffix_str}" + + name_list = HighLevelILTokenEmitter.names_for_outer_structure_members( + self.function.view, var_type, var) + [offset_str] + tokens.append(InstructionTextToken(InstructionTextTokenType.StructOffsetToken, offset_str, value=offset, + size=size, typeNames=name_list)) + + +class PseudoPythonFunctionType(LanguageRepresentationFunctionType): + language_name = "Pseudo Python" + + def create(self, arch: Architecture, owner: Function, hlil: HighLevelILFunction): + return PseudoPythonFunction(arch, owner, hlil) + + def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> DisassemblyTextLine: + tokens = [] + tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "def ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, func.name, value=func.start)) + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "(")) + for (i, param) in enumerate(func.type.parameters_with_all_locations): + if i > 0: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ArgumentNameToken, param.name, + context=InstructionTextTokenContext.LocalVariableTokenContext, + address=param.location.identifier)) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) + for token in param.type.get_tokens(): + token.context = InstructionTextTokenContext.LocalVariableTokenContext + token.address = param.location.identifier + tokens.append(token) + tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, ")")) + if func.can_return.value and func.type.return_value is not None and not isinstance(func.type.return_value, VoidType): + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " -> ")) + for token in func.type.return_value.get_tokens(): + token.context = InstructionTextTokenContext.FunctionReturnTokenContext + tokens.append(token) + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) + return [DisassemblyTextLine(tokens, func.start)] + + +PseudoPythonFunctionType().register() diff --git a/python/function.py b/python/function.py index 5ed61603..c8acf287 100644 --- a/python/function.py +++ b/python/function.py @@ -27,8 +27,9 @@ from dataclasses import dataclass # Binary Ninja components from . import _binaryninjacore as core from .enums import ( - AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor, - HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType + AnalysisSkipReason, FunctionGraphType, SymbolType, InstructionTextTokenType, HighlightStandardColor, + HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType, + BuiltinType ) from .exceptions import ILException @@ -45,6 +46,7 @@ from . import variable from . import flowgraph from . import callingconvention from . import workflow +from . import languagerepresentation from . import deprecation from . import __version__ @@ -76,6 +78,7 @@ ILFunctionType = Union['lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLev ILInstructionType = Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction', 'highlevelil.HighLevelILInstruction'] StringOrType = Union[str, 'types.Type', 'types.TypeBuilder'] +FunctionViewTypeOrName = Union['FunctionViewType', FunctionGraphType, str] def _function_name_(): @@ -181,6 +184,39 @@ class VariableReferenceSource: return f"<var: {repr(self.var)}, src: {repr(self.src)}>" +@dataclass +class FunctionViewType: + view_type: FunctionGraphType + name: Optional[str] + + def __init__(self, view_type: FunctionViewTypeOrName): + if isinstance(view_type, FunctionViewType): + self.view_type = view_type.view_type + self.name = view_type.name + if isinstance(view_type, FunctionGraphType): + self.view_type = view_type + self.name = None + else: + self.view_type = FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph + self.name = str(view_type) + + @staticmethod + def _from_core_struct(view_type: core.BNFunctionViewType) -> 'FunctionViewType': + if view_type.type == FunctionGraphType.HighLevelLanguageRepresentationFunctionGraph: + if view_type.name is None: + return FunctionViewType("Pseudo C") + else: + return FunctionViewType(view_type.name) + else: + return FunctionViewType(view_type.type) + + def _to_core_struct(self) -> core.BNFunctionViewType: + result = core.BNFunctionViewType() + result.type = self.view_type + result.name = self.name + return result + + class BasicBlockList: def __init__( self, function: Union['Function', 'lowlevelil.LowLevelILFunction', 'mediumlevelil.MediumLevelILFunction', @@ -1069,6 +1105,30 @@ class Function: return highlevelil.HighLevelILFunction(self.arch, result, self) @property + def pseudo_c(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + return self.language_representation("Pseudo C") + + @property + def pseudo_c_if_available(self) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + return self.language_representation_if_available("Pseudo C") + + def language_representation( + self, language: str + ) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + result = core.BNGetFunctionLanguageRepresentation(self.handle, language) + if result is None: + return None + return languagerepresentation.LanguageRepresentationFunction(handle=result) + + def language_representation_if_available( + self, language: str + ) -> Optional['languagerepresentation.LanguageRepresentationFunction']: + result = core.BNGetFunctionLanguageRepresentationIfAvailable(self.handle, language) + if result is None: + return None + return languagerepresentation.LanguageRepresentationFunction(handle=result) + + @property def type(self) -> 'types.FunctionType': """ Function type object, can be set with either a string representing the function prototype @@ -1787,7 +1847,15 @@ class Function: core.BNFreeILInstructionList(exits) def get_constant_data(self, state: RegisterValueType, value: int, size: int = 0) -> databuffer.DataBuffer: - return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size)) + return databuffer.DataBuffer(handle=core.BNGetConstantData(self.handle, state, value, size, None)) + + def get_constant_data_and_builtin( + self, state: RegisterValueType, value: int, size: int = 0 + ) -> Tuple[databuffer.DataBuffer, BuiltinType]: + builtin = ctypes.c_int() + db = databuffer.DataBuffer( + handle=core.BNGetConstantData(self.handle, state, value, size, ctypes.byref(builtin))) + return db, BuiltinType(builtin.value) def get_reg_value_at( self, addr: int, reg: 'architecture.RegisterType', arch: Optional['architecture.Architecture'] = None @@ -2078,13 +2146,14 @@ class Function: return result def create_graph( - self, graph_type: FunctionGraphType = FunctionGraphType.NormalFunctionGraph, + self, graph_type: FunctionViewTypeOrName = FunctionGraphType.NormalFunctionGraph, settings: Optional['DisassemblySettings'] = None ) -> flowgraph.CoreFlowGraph: if settings is not None: settings_obj = settings.handle else: settings_obj = None + graph_type = FunctionViewType(graph_type)._to_core_struct() return flowgraph.CoreFlowGraph(core.BNCreateFunctionGraph(self.handle, graph_type, settings_obj)) def apply_imported_types(self, sym: 'types.CoreSymbol', type: Optional[StringOrType] = None) -> None: diff --git a/python/highlevelil.py b/python/highlevelil.py index 48cbd6cc..21359685 100644 --- a/python/highlevelil.py +++ b/python/highlevelil.py @@ -907,6 +907,10 @@ class HighLevelILInstruction(BaseILInstruction): return False return True + @property + def has_side_effects(self) -> bool: + return core.BNHighLevelILHasSideEffects(self.function.handle, self.expr_index) + @dataclass(frozen=True, repr=False, eq=False) class HighLevelILUnaryBase(HighLevelILInstruction, UnaryOperation): diff --git a/python/languagerepresentation.py b/python/languagerepresentation.py new file mode 100644 index 00000000..49a300c1 --- /dev/null +++ b/python/languagerepresentation.py @@ -0,0 +1,822 @@ +# Copyright (c) 2024 Vector 35 Inc +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import ctypes +import traceback +from typing import List, Optional, Union + +# Binary Ninja components +import binaryninja +from . import _binaryninjacore as core +from . import architecture +from . import binaryview +from . import function +from . import highlevelil +from . import highlight +from . import variable +from . import types +from .log import log_error +from .enums import BraceRequirement, HighlightStandardColor, InstructionTextTokenType, OperatorPrecedence, ScopeType, \ + SymbolDisplayType, SymbolDisplayResult + + +class HighLevelILTokenEmitter: + """ + ``class HighLevelILTokenEmitter`` contains methods for emitting text tokens for High Level IL instructions. + Methods are provided for typical patterns found in various high level languages. + + This class cannot be instantiated directly. An instance of the class will be provided when the methods + in ``class LanguageRepresentationFunction`` are called. + """ + def __init__(self, handle: core.BNHighLevelILTokenEmitterHandle): + self.handle = handle + + def __del__(self): + if core is not None: + core.BNFreeHighLevelILTokenEmitter(self.handle) + + def new_line(self): + """Starts a new line in the output.""" + core.BNHighLevelILTokenEmitterNewLine(self.handle) + + def increase_indent(self): + """Increases the indentation level by one.""" + core.BNHighLevelILTokenEmitterIncreaseIndent(self.handle) + + def decrease_indent(self): + """Decreases the indentation level by one.""" + core.BNHighLevelILTokenEmitterDecreaseIndent(self.handle) + + def scope_separator(self): + """ + Indicates that visual separation of scopes is desirable at the current position. By default, + this will insert a blank line, but this can be configured by the user. + """ + core.BNHighLevelILTokenEmitterScopeSeparator(self.handle) + + def begin_scope(self, scope_type: ScopeType): + """Begins a new scope. Insertion of newlines and braces will be handled using the current settings.""" + core.BNHighLevelILTokenEmitterBeginScope(self.handle, scope_type) + + def end_scope(self, scope_type: ScopeType): + """Ends the current scope.""" + core.BNHighLevelILTokenEmitterEndScope(self.handle, scope_type) + + def scope_continuation(self, force_same_line: bool): + """Continues the previous scope with a new associated scope. This is most commonly used for ``else`` statements.""" + core.BNHighLevelILTokenEmitterScopeContinuation(self.handle, force_same_line) + + def finalize_scope(self): + """Finalizes the previous scope, indicating that there are no more associated scopes.""" + core.BNHighLevelILTokenEmitterFinalizeScope(self.handle) + + def no_indent_for_this_line(self): + """Forces there to be no indentation for the next line.""" + core.BNHighLevelILTokenEmitterNoIndentForThisLine(self.handle) + + class ZeroConfidenceContext: + """ + ``class ZeroConfidenceContext`` is a context manager that optionally forces tokens to be of zero confidence + inside the context. + """ + def __init__(self, emitter: 'HighLevelILTokenEmitter', enabled: bool): + self.emitter = emitter + self.enabled = enabled + + def __enter__(self): + if self.enabled: + core.BNHighLevelILTokenEmitterBeginForceZeroConfidence(self.emitter.handle) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.enabled: + core.BNHighLevelILTokenEmitterEndForceZeroConfidence(self.emitter.handle) + + def force_zero_confidence(self, enabled: bool = True) -> 'HighLevelILTokenEmitter.ZeroConfidenceContext': + """ + Returns a context manager that forces tokens inside of it to be of zero confidence. If ``False`` is passed + to this method, the context has no effect, allowing the caller to conditionally apply this behavior. + """ + return HighLevelILTokenEmitter.ZeroConfidenceContext(self, enabled) + + class ExprContext: + """ + ``class ExprContext`` is a context manager that associates the tokens inside the context with the given + High Level IL expression. + """ + def __init__(self, emitter: 'HighLevelILTokenEmitter', hlil_expr: highlevelil.HighLevelILInstruction): + self.emitter = emitter + self.expr = hlil_expr + self.prev_expr = None + + def __enter__(self): + expr = core.BNTokenEmitterExpr() + expr.address = self.expr.address + expr.sourceOperand = self.expr.source_operand + expr.exprIndex = self.expr.expr_index + expr.instrIndex = self.expr.instr_index + self.prev_expr = core.BNHighLevelILTokenEmitterSetCurrentExpr(self.emitter.handle, expr) + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.prev_expr is not None: + core.BNHighLevelILTokenEmitterRestoreCurrentExpr(self.emitter.handle, self.prev_expr) + + def expr(self, hlil_expr: 'highlevelil.HighLevelILInstruction') -> 'HighLevelILTokenEmitter.ExprContext': + """ + Returns a context manager that associates the tokens inside the context with the given High Level IL expression. + """ + return HighLevelILTokenEmitter.ExprContext(self, hlil_expr) + + def finalize(self): + """Finalizes all tokens in the output.""" + core.BNHighLevelILTokenEmitterFinalize(self.handle) + + def append(self, tokens: Union['architecture.InstructionTextToken', List['architecture.InstructionTextToken']]): + """Appends a token or list of tokens to the output.""" + if not isinstance(tokens, list): + tokens = [tokens] + buf = architecture.InstructionTextToken._get_core_struct(tokens) + for i in range(len(tokens)): + core.BNHighLevelILTokenEmitterAppend(self.handle, buf[i]) + + def append_open_paren(self): + """Appends an open parenthesis (``(``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenParen(self.handle) + + def append_close_paren(self): + """Appends a close parenthesis (``)``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseParen(self.handle) + + def append_open_bracket(self): + """Appends an open bracket (``[``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenBracket(self.handle) + + def append_close_bracket(self): + """Appends a close bracket (``]``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseBracket(self.handle) + + def append_open_brace(self): + """Appends an open brace (``{``) to the output.""" + core.BNHighLevelILTokenEmitterAppendOpenBrace(self.handle) + + def append_close_brace(self): + """Appends a close brace (``}``) to the output.""" + core.BNHighLevelILTokenEmitterAppendCloseBrace(self.handle) + + def append_semicolon(self): + """Appends a semicolon (``;``) to the output.""" + core.BNHighLevelILTokenEmitterAppendSemicolon(self.handle) + + @property + def current_tokens(self) -> List['function.InstructionTextToken']: + """The list of tokens on the current line (read-only).""" + count = ctypes.c_ulonglong() + tokens = core.BNHighLevelILTokenEmitterGetCurrentTokens(self.handle, count) + result = [] + if tokens is not None: + result = function.InstructionTextToken._from_core_struct(tokens, count.value) + core.BNFreeInstructionText(tokens, count.value) + return result + + @property + def lines(self) -> List['function.DisassemblyTextLine']: + """The list of lines in the output (read-only).""" + count = ctypes.c_ulonglong() + lines = core.BNHighLevelILTokenEmitterGetLines(self.handle, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, color=color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def brace_requirement(self) -> BraceRequirement: + """The requirement for insertion of braces around scopes in the output.""" + return core.BNHighLevelILTokenEmitterGetBraceRequirement(self.handle) + + @brace_requirement.setter + def brace_requirement(self, value: BraceRequirement): + core.BNHighLevelILTokenEmitterSetBraceRequirement(self.handle, value) + + @property + def braces_around_switch_cases(self): + """Whether cases within switch statements should always have braces around them.""" + return core.BNHighLevelILTokenEmitterHasBracesAroundSwitchCases(self.handle) + + @braces_around_switch_cases.setter + def braces_around_switch_cases(self, value: bool): + core.BNHighLevelILTokenEmitterSetBracesAroundSwitchCases(self.handle, value) + + @property + def default_braces_on_same_line(self): + """ + Whether braces should default to being on the same line as the statement that begins the scope. + If the user has explicitly set a preference, this setting will be ignored and the user's preference + will be used instead. + """ + return core.BNHighLevelILTokenEmitterGetDefaultBracesOnSameLine(self.handle) + + @default_braces_on_same_line.setter + def default_braces_on_same_line(self, value: bool): + core.BNHighLevelILTokenEmitterSetDefaultBracesOnSameLine(self.handle, value) + + @property + def simple_scope_allowed(self): + """Whether omitting braces around single-line scopes is allowed.""" + return core.BNHighLevelILTokenEmitterIsSimpleScopeAllowed(self.handle) + + @simple_scope_allowed.setter + def simple_scope_allowed(self, value: bool): + core.BNHighLevelILTokenEmitterSetSimpleScopeAllowed(self.handle, value) + + def append_size_token(self, size: int, token_type: InstructionTextTokenType): + """Appends a size token for the given size in the High Level IL syntax.""" + core.BNAddHighLevelILSizeToken(size, token_type, self.handle) + + def append_float_size_token(self, size: int, token_type: InstructionTextTokenType): + """Appends a floating point size token for the given size in the High Level IL syntax.""" + core.BNAddHighLevelILFloatSizeToken(size, token_type, self.handle) + + def append_var_text_token( + self, var: 'variable.CoreVariable', instr: 'highlevelil.HighLevelILInstruction', + size: int + ): + """Appends tokens for access to a variable.""" + core.BNAddHighLevelILVarTextToken( + instr.function.handle, var.to_BNVariable(), self.handle, instr.expr_index, size) + + def append_integer_text_token(self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int): + """Appends tokens for a constant intenger value.""" + core.BNAddHighLevelILIntegerTextToken(instr.function.handle, instr.expr_index, value, size, self.handle) + + def append_array_index_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int, address: int = 0): + """Appends tokens for accessing an array by index.""" + core.BNAddHighLevelILArrayIndexToken(instr.function.handle, instr.expr_index, value, size, self.handle, address) + + def append_pointer_text_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, + settings: Optional['function.DisassemblySettings'], symbol_display: SymbolDisplayType, + precedence: OperatorPrecedence, allow_short_string: bool = False + ) -> SymbolDisplayResult: + """Appends tokens for displaying a constant pointer value.""" + if settings is not None: + settings = settings.handle + return SymbolDisplayResult(core.BNAddHighLevelILPointerTextToken( + instr.function.handle, instr.expr_index, value, self.handle, settings, symbol_display, precedence, + allow_short_string)) + + def append_constant_text_token( + self, instr: 'highlevelil.HighLevelILInstruction', value: int, size: int, + settings: Optional['function.DisassemblySettings'], precedence: OperatorPrecedence + ): + """Appends tokens for a constant value.""" + if settings is not None: + settings = settings.handle + core.BNAddHighLevelILConstantTextToken( + instr.function.handle, instr.expr_index, value, size, self.handle, settings, precedence) + + @staticmethod + def names_for_outer_structure_members( + view: 'binaryview.BinaryView', struct_type: 'types.Type', var: 'highlevelil.HighLevelILInstruction', + ) -> List[str]: + """ + Gets the list of names for the outer structure members when accessing a structure member. This list + can be passed for the ``typeNames`` parameter of the ``class InstructionTextToken`` constructor. + """ + count = ctypes.c_ulonglong() + result = core.BNAddNamesForOuterStructureMembers( + view.handle, struct_type.handle, var.function.handle, var.expr_index, count) + names = [] + if result is not None: + for i in range(count.value): + names.append(result[i].decode("utf-8")) + core.BNFreeStringList(result, count.value) + return names + + +class LanguageRepresentationFunction: + """ + ``class LanguageRepresentationFunction`` represents a single function in a registered high level language. + """ + _registered_instances = [] + comment_start_string = "// " + comment_end_string = "" + annotation_start_string = "{" + annotation_end_string = "}" + + def __init__( + self, arch: Optional['architecture.Architecture'] = None, owner: Optional['function.Function'] = None, + hlil: Optional['highlevelil.HighLevelILFunction'] = None, handle=None + ): + if handle is None: + if arch is None: + raise ValueError("function representation must have an associated architecture") + if owner is None: + raise ValueError("function representation must have an owning function") + if hlil is None: + raise ValueError("function representation must have an associated High Level IL function") + self._cb = core.BNCustomLanguageRepresentationFunction() + self._cb.context = 0 + self._cb.externalRefTaken = self._cb.externalRefTaken.__class__(self._external_ref_taken) + self._cb.externalRefReleased = self._cb.externalRefReleased.__class__(self._external_ref_released) + self._cb.initTokenEmitter = self._cb.initTokenEmitter.__class__(self._init_token_emitter) + self._cb.getExprText = self._cb.getExprText.__class__(self._get_expr_text) + self._cb.beginLines = self._cb.beginLines.__class__(self._begin_lines) + self._cb.endLines = self._cb.endLines.__class__(self._end_lines) + self._cb.getCommentStartString = self._cb.getCommentStartString.__class__(self._comment_start_string) + self._cb.getCommentEndString = self._cb.getCommentEndString.__class__(self._comment_end_string) + self._cb.getAnnotationStartString = self._cb.getAnnotationStartString.__class__( + self._annotation_start_string) + self._cb.getAnnotationEndString = self._cb.getAnnotationEndString.__class__(self._annotation_end_string) + self.comment_start_string = self.__class__.comment_start_string + self.comment_end_string = self.__class__.comment_end_string + self.annotation_start_string = self.__class__.annotation_start_string + self.annotation_end_string = self.__class__.annotation_end_string + _handle = core.BNCreateCustomLanguageRepresentationFunction(arch.handle, owner.handle, hlil.handle, self._cb) + assert _handle is not None + else: + self.comment_start_string = core.BNGetLanguageRepresentationFunctionCommentStartString(handle) + self.comment_end_string = core.BNGetLanguageRepresentationFunctionCommentEndString(handle) + self.annotation_start_string = core.BNGetLanguageRepresentationFunctionAnnotationStartString(handle) + self.annotation_end_string = core.BNGetLanguageRepresentationFunctionAnnotationEndString(handle) + _handle = handle + assert _handle is not None + self.handle: core.BNLanguageRepresentationFunctionHandle = _handle + + def __del__(self): + if core is not None: + core.BNFreeLanguageRepresentationFunction(self.handle) + + def _external_ref_taken(self, ctxt): + try: + self.__class__._registered_instances.append(self) + except: + log_error(traceback.format_exc()) + + def _external_ref_released(self, ctxt): + try: + self.__class__._registered_instances.remove(self) + except: + log_error(traceback.format_exc()) + + def _init_token_emitter(self, ctxt, emitter: core.BNHighLevelILTokenEmitterHandle): + try: + emitter = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(emitter)) + self.perform_init_token_emitter(emitter) + except: + log_error(traceback.format_exc()) + + def _get_expr_text( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle, settings: Optional[core.BNDisassemblySettingsHandle], + as_full_ast: bool, precedence: OperatorPrecedence, statement: bool + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + if settings is not None: + settings = function.DisassemblySettings(core.BNNewDisassemblySettingsReference(settings)) + self.perform_get_expr_text(instr, tokens, settings, as_full_ast, precedence, statement) + except: + log_error(traceback.format_exc()) + + def _begin_lines( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + self.perform_begin_lines(instr, tokens) + except: + log_error(traceback.format_exc()) + + def _end_lines( + self, ctxt, hlil: core.BNHighLevelILFunctionHandle, expr_index: int, + tokens: core.BNHighLevelILTokenEmitterHandle + ): + try: + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + instr = hlil.get_expr(highlevelil.ExpressionIndex(expr_index)) + tokens = HighLevelILTokenEmitter(core.BNNewHighLevelILTokenEmitterReference(tokens)) + self.perform_end_lines(instr, tokens) + except: + log_error(traceback.format_exc()) + + def _comment_start_string(self, ctxt): + try: + return core.BNAllocString(self.comment_start_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("// ") + + def _comment_end_string(self, ctxt): + try: + return core.BNAllocString(self.comment_end_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _annotation_start_string(self, ctxt): + try: + return core.BNAllocString(self.annotation_start_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("{") + + def _annotation_end_string(self, ctxt): + try: + return core.BNAllocString(self.annotation_end_string) + except: + log_error(traceback.format_exc()) + return core.BNAllocString("}") + + def perform_init_token_emitter(self, emitter: HighLevelILTokenEmitter): + """Override this method to initialize the options for the token emitter before it is used.""" + pass + + def perform_get_expr_text( + self, instr: 'highlevelil.HighLevelILInstruction', tokens: HighLevelILTokenEmitter, + settings: Optional['function.DisassemblySettings'], as_full_ast: bool = True, + precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, statement: bool = False + ): + """ + This method must be overridden by all language representation plugins. + + This method is called to emit the tokens for a given High Level IL instruction. + """ + raise NotImplementedError + + def perform_begin_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + """This method can be overridden to emit tokens at the start of a function.""" + pass + + def perform_end_lines(self, instr: highlevelil.HighLevelILInstruction, tokens: HighLevelILTokenEmitter): + """This method can be overridden to emit tokens at the end of a function.""" + pass + + def get_expr_text( + self, instr: 'highlevelil.HighLevelILInstruction', settings: Optional['function.DisassemblySettings'], + as_full_ast: bool = True, precedence: OperatorPrecedence = OperatorPrecedence.TopLevelOperatorPrecedence, + statement: bool = False + ) -> List['function.DisassemblyTextLine']: + """Gets the lines of tokens for a given High Level IL instruction.""" + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionExprText(self.handle, instr.function.handle, instr.expr_index, + settings, as_full_ast, precedence, statement, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def get_linear_lines( + self, instr: 'highlevelil.HighLevelILInstruction', + settings: Optional['function.DisassemblySettings'] = None, + as_full_ast: bool = True + ) -> List['function.DisassemblyTextLine']: + """ + Generates lines for the given High Level IL instruction in the style of the linear view. To get the lines + for the entire function, pass the ``root`` property of a ``class HighLevelILFunction``. + """ + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionLinearLines(self.handle, instr.function.handle, instr.expr_index, + settings, as_full_ast, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + def get_block_lines( + self, block: 'highlevelil.HighLevelILBasicBlock', settings: Optional['function.DisassemblySettings'] = None + ) -> List['function.DisassemblyTextLine']: + """Generates lines for a single High Level IL basic block.""" + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionBlockLines(self.handle, block.handle, settings, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + if lines[i].instrIndex != 0xffffffffffffffff: + il_instr = instr.function[lines[i].instrIndex] # type: ignore + else: + il_instr = None + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, il_instr, color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result + + @property + def arch(self) -> 'architecture.Architecture': + return architecture.CoreArchitecture._from_cache(core.BNGetLanguageRepresentationArchitecture(self.handle)) + + @property + def function(self) -> 'function.Function': + return function.Function(handle=core.BNGetLanguageRepresentationOwnerFunction(self.handle)) + + @property + def high_level_il(self) -> 'highlevelil.HighLevelILFunction': + return self.hlil + + @property + def hlil(self) -> 'highlevelil.HighLevelILFunction': + return highlevelil.HighLevelILFunction(arch=self.arch, + handle=core.BNGetLanguageRepresentationILFunction(self.handle), source_func=self.function) + + +class _LanguageRepresentationFunctionTypeMetaClass(type): + def __iter__(self): + binaryninja._init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetLanguageRepresentationFunctionTypeList(count) + assert types is not None, "core.BNGetLanguageRepresentationFunctionTypeList returned None" + try: + for i in range(0, count.value): + yield CoreLanguageRepresentationFunctionType(handle=types[i]) + finally: + core.BNFreeLanguageRepresentationFunctionTypeList(types) + + def __getitem__(cls, value): + binaryninja._init_plugins() + lang = core.BNGetLanguageRepresentationFunctionTypeByName(str(value)) + if lang is None: + raise KeyError("'%s' is not a valid language" % str(value)) + return CoreLanguageRepresentationFunctionType(handle=lang) + + +class LanguageRepresentationFunctionType(metaclass=_LanguageRepresentationFunctionTypeMetaClass): + """ + ``class LanguageRepresentationFunctionType`` represents a custom language representation function type. + This class provides methods to create ``class LanguageRepresentationFunction`` instances for functions, as well + as manage the printing and parsing of types. + """ + _registered_languages = [] + language_name = None + + def __init__(self, handle=None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNLanguageRepresentationFunctionType) + + def register(self): + """Registers the language representation function type.""" + if self.__class__.language_name is None: + raise ValueError("Language name is missing") + self._cb = core.BNCustomLanguageRepresentationFunctionType() + self._cb.context = 0 + self._cb.create = self._cb.create.__class__(self._create) + self._cb.isValid = self._cb.isValid.__class__(self._is_valid) + self._cb.getTypePrinter = self._cb.getTypePrinter.__class__(self._type_printer) + self._cb.getTypeParser = self._cb.getTypeParser.__class__(self._type_parser) + self._cb.getFunctionTypeTokens = self._cb.getFunctionTypeTokens.__class__(self._function_type_tokens) + self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines) + self.handle = core.BNRegisterLanguageRepresentationFunctionType(self.__class__.language_name, self._cb) + self.__class__._registered_languages.append(self) + + def _create( + self, ctxt, arch: core.BNArchitectureHandle, owner: core.BNFunctionHandle, + hlil: core.BNHighLevelILFunctionHandle + ): + try: + arch = architecture.CoreArchitecture._from_cache(arch) + owner = function.Function(handle=core.BNNewFunctionReference(owner)) + hlil = highlevelil.HighLevelILFunction(handle=core.BNNewHighLevelILFunctionReference(hlil)) + result = self.create(arch, owner, hlil) + if result is None: + raise ValueError(f"create returned None for language representation '{self.name}'") + handle = core.BNNewLanguageRepresentationFunctionReference(result.handle) + assert handle is not None, "core.BNNewLanguageRepresentationFunctionReference returned None" + return ctypes.cast(handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _is_valid(self, ctxt, view: core.BNBinaryViewHandle) -> bool: + try: + view = binaryview.BinaryView(handle=core.BNNewViewReference(view)) + return self.is_valid(view) + except: + log_error(traceback.format_exc()) + return False + + def _type_printer(self, ctxt): + try: + result = self.type_printer + if result is None: + return None + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _type_parser(self, ctxt): + try: + result = self.type_parser + if result is None: + return None + return ctypes.cast(result.handle, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def _function_type_tokens( + self, ctxt, func: core.BNFunctionHandle, settings: Optional[core.BNDisassemblySettingsHandle], + count: ctypes.POINTER(ctypes.c_ulonglong) + ): + try: + func = function.Function(handle=core.BNNewFunctionReference(func)) + if settings is not None: + settings = function.DisassemblySettings(handle=core.BNNewDisassemblySettingsReference(settings)) + lines = self.function_type_tokens(func, settings) + + count[0] = len(lines) + self.line_buf = (core.BNDisassemblyTextLine * len(lines))() + for i in range(len(lines)): + line = lines[i] + color = line.highlight + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + self.line_buf[i].highlight = color._to_core_struct() + if line.address is None: + if len(line.tokens) > 0: + self.line_buf[i].addr = line.tokens[0].address + else: + self.line_buf[i].addr = 0 + else: + self.line_buf[i].addr = line.address + if line.il_instruction is not None: + self.line_buf[i].instrIndex = line.il_instruction.instr_index + else: + self.line_buf[i].instrIndex = 0xffffffffffffffff + + self.line_buf[i].count = len(line.tokens) + self.line_buf[i].tokens = function.InstructionTextToken._get_core_struct(line.tokens) + + return ctypes.cast(self.line_buf, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_lines(self, ctxt, lines, count): + self.line_buf = None + + @property + def name(self) -> str: + if hasattr(self, 'handle'): + return core.BNGetLanguageRepresentationFunctionTypeName(self.handle) + return self.__class__.language_name + + def create( + self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction' + ) -> LanguageRepresentationFunction: + """ + This method must be overridden. This creates the ``class LanguageRepresentationFunction`` object for the + given architecture, owner function, and High Level IL function. + """ + raise NotImplementedError + + def is_valid(self, view: 'binaryview.BinaryView') -> bool: + """Returns whether the language is valid for the given binary view.""" + return True + + @property + def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']: + """ + Returns the type printer for displaying types in this language. If ``None`` is returned, the default type + printer will be used. + """ + return None + + @property + def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']: + """ + Returns the type parser for parsing types in this language. If ``None`` is returned, the default type + parser will be used. + """ + return None + + def function_type_tokens( + self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] + ) -> List['function.DisassemblyTextLine']: + """ + Returns a list of lines representing a function prototype in this language. If no lines are returned, the + default C-style prototype will be used. + """ + return [] + + def __repr__(self): + return f"<LanguageRepresentationFunctionType: {self.name}>" + + +_language_cache = {} + + +class CoreLanguageRepresentationFunctionType(LanguageRepresentationFunctionType): + def __init__(self, handle: core.BNLanguageRepresentationFunctionTypeHandle): + super(CoreLanguageRepresentationFunctionType, self).__init__(handle=handle) + if type(self) is CoreLanguageRepresentationFunctionType: + global _language_cache + _language_cache[ctypes.addressof(handle.contents)] = self + + @classmethod + def _from_cache(cls, handle) -> 'LanguageRepresentationFunctionType': + """ + Look up a representation type from a given BNLanguageRepresentationFunctionType handle + :param handle: BNLanguageRepresentationFunctionType pointer + :return: Respresentation type instance responsible for this handle + """ + global _language_cache + return _language_cache.get(ctypes.addressof(handle.contents)) or cls(handle) + + def create( + self, arch: 'architecture.Architecture', owner: 'function.Function', hlil: 'highlevelil.HighLevelILFunction' + ) -> LanguageRepresentationFunction: + raise NotImplementedError + + def is_valid(self, view: 'binaryview.BinaryView') -> bool: + return core.BNIsLanguageRepresentationFunctionTypeValid(self.handle, view.handle) + + @property + def type_printer(self) -> Optional['binaryninja.typeprinter.TypePrinter']: + result = core.BNGetLanguageRepresentationFunctionTypePrinter(self.handle) + if result is None: + return None + return binaryninja.typeprinter.TypePrinter(handle=result) + + @property + def type_parser(self) -> Optional['binaryninja.typeparser.TypeParser']: + result = core.BNGetLanguageRepresentationFunctionTypeParser(self.handle) + if result is None: + return None + return binaryninja.typeparser.TypeParser(handle=result) + + def function_type_tokens( + self, func: 'function.Function', settings: Optional['function.DisassemblySettings'] + ) -> List['function.DisassemblyTextLine']: + count = ctypes.c_ulonglong() + if settings is not None: + settings = settings.handle + lines = core.BNGetLanguageRepresentationFunctionTypeFunctionTypeTokens(self.handle, func.handle, settings, count) + result = [] + if lines is not None: + result = [] + for i in range(0, count.value): + addr = lines[i].addr + color = highlight.HighlightColor._from_core_struct(lines[i].highlight) + tokens = function.InstructionTextToken._from_core_struct(lines[i].tokens, lines[i].count) + result.append(function.DisassemblyTextLine(tokens, addr, color=color)) + core.BNFreeDisassemblyTextLines(lines, count.value) + return result diff --git a/python/lineardisassembly.py b/python/lineardisassembly.py index a19148ca..40a0d6c5 100644 --- a/python/lineardisassembly.py +++ b/python/lineardisassembly.py @@ -345,12 +345,13 @@ class LinearViewObject: @staticmethod def language_representation( - view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None + view: 'binaryview.BinaryView', settings: Optional['_function.DisassemblySettings'] = None, + language: str = "Pseudo C" ) -> 'LinearViewObject': _settings = settings if _settings is not None: _settings = _settings.handle - return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings)) + return LinearViewObject(core.BNCreateLinearViewLanguageRepresentation(view.handle, _settings, language)) @staticmethod def data_only( @@ -453,12 +454,13 @@ class LinearViewObject: @staticmethod def single_function_language_representation( - func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None + func: '_function.Function', settings: Optional['_function.DisassemblySettings'] = None, + language: str = "Pseudo C" ) -> 'LinearViewObject': _settings = settings if _settings is not None: _settings = _settings.handle - return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings)) + return LinearViewObject(core.BNCreateLinearViewSingleFunctionLanguageRepresentation(func.handle, _settings, language)) class LinearViewCursor: diff --git a/python/scriptingprovider.py b/python/scriptingprovider.py index 02b070fa..4b76939f 100644 --- a/python/scriptingprovider.py +++ b/python/scriptingprovider.py @@ -1809,23 +1809,23 @@ def _get_current_il_function(instance: PythonScriptingInstance): from binaryninja import FunctionGraphType if instance.interpreter.locals["current_ui_view_location"] is not None: ilType = instance.interpreter.locals["current_ui_view_location"].getILViewType() - if ilType == FunctionGraphType.LowLevelILFunctionGraph: + if ilType.view_type == FunctionGraphType.LowLevelILFunctionGraph: return instance.interpreter.locals["current_llil"] - elif ilType == FunctionGraphType.LiftedILFunctionGraph: + elif ilType.view_type == FunctionGraphType.LiftedILFunctionGraph: return instance.interpreter.locals["current_lifted_il"] - elif ilType == FunctionGraphType.LowLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.LowLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_llil_ssa"] - elif ilType == FunctionGraphType.MappedMediumLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.MappedMediumLevelILFunctionGraph: return instance.interpreter.locals["current_mapped_mlil"] - elif ilType == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.MappedMediumLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_mapped_mlil_ssa"] - elif ilType == FunctionGraphType.MediumLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.MediumLevelILFunctionGraph: return instance.interpreter.locals["current_mlil"] - elif ilType == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.MediumLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_mlil_ssa"] - elif ilType == FunctionGraphType.HighLevelILFunctionGraph: + elif ilType.view_type == FunctionGraphType.HighLevelILFunctionGraph: return instance.interpreter.locals["current_hlil"] - elif ilType == FunctionGraphType.HighLevelILSSAFormFunctionGraph: + elif ilType.view_type == FunctionGraphType.HighLevelILSSAFormFunctionGraph: return instance.interpreter.locals["current_hlil_ssa"] return None diff --git a/python/types.py b/python/types.py index 2c2b652f..d5fd3544 100644 --- a/python/types.py +++ b/python/types.py @@ -20,7 +20,7 @@ import ctypes import typing -from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar +from typing import Generator, List, Union, Tuple, Optional, Iterable, Dict, Generic, TypeVar, Callable from dataclasses import dataclass import uuid @@ -53,6 +53,7 @@ SomeType = Union['TypeBuilder', 'Type'] TypeContainerType = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] NameSpaceType = Optional[Union[str, List[str], 'NameSpace']] TypeParserResult = typeparser.TypeParserResult +ResolveMemberCallback = Callable[['NamedTypeReferenceType', 'StructureType', int, int, int, 'StructureMember'], None] # The following are needed to prevent the type checker from getting # confused as we have member functions in `Type` named the same thing _int = int @@ -384,6 +385,12 @@ class CoreSymbol: def handle(self): return self._handle + def imported_function_from_import_address_symbol(self, addr: int) -> Optional['CoreSymbol']: + sym = core.BNImportedFunctionFromImportAddressSymbol(self._handle, addr) + if sym is None: + return None + return CoreSymbol(sym) + class Symbol(CoreSymbol): """ @@ -2717,6 +2724,40 @@ class StructureType(Type): ntr_type, guid, name, self.alignment, self.width, self.platform, self.confidence ) + def resolve_member_or_base_member( + self, view: Optional['binaryview.BinaryView'], offset: int, size: int, + resolve_func: ResolveMemberCallback, member_index_hint: Optional[int] = None + ) -> bool: + if view is not None: + view = view.handle + + def resolve_member_callback( + ctxt, base_name: core.BNNamedTypeReferenceHandle, resolved_struct: core.BNStructureHandle, + member_index: int, struct_offset: int, adjusted_offset: int, member: core.BNStructureMember + ): + if base_name: + base_name = NamedTypeReferenceType.create_from_handle(core.BNNewNamedTypeReference(base_name)) + if resolved_struct: + resolved_struct = StructureType.from_core_struct(core.BNNewStructureReference(resolved_struct)) + t = Type.create(core.BNNewTypeReference(member.type), confidence=member.typeConfidence) + struct_member = StructureMember( + t, member.name, member.offset, MemberAccess(member.access), MemberScope(member.scope) + ) + resolve_func(base_name, resolved_struct, member_index, struct_offset, adjusted_offset, struct_member) + + member_index_hint_value = 0 + if member_index_hint is not None: + member_index_hint_value = member_index_hint + return core.BNResolveStructureMemberOrBaseMember( + self.struct_handle, view, offset, size, None, + ctypes.CFUNCTYPE( + None, ctypes.c_void_p, core.BNNamedTypeReferenceHandle, core.BNStructureHandle, ctypes.c_size_t, + ctypes.c_uint64, ctypes.c_uint64, core.BNStructureMember + )(resolve_member_callback), + member_index_hint is not None, + member_index_hint_value + ) + @property def children(self) -> List[Type]: return [member.type for member in self.members] @@ -3064,6 +3105,30 @@ class FunctionType(Type): return result @property + def parameters_with_all_locations(self) -> List[FunctionParameter]: + """Type parameters list with default locations filled in with values (read-only)""" + count = ctypes.c_ulonglong() + params = core.BNGetTypeParameters(self._handle, count) + assert params is not None, "core.BNGetTypeParameters returned None" + result = [] + for i in range(0, count.value): + param_type = Type.create( + core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence + ) + name = params[i].name + if (params[i].location.type + == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): + name = self._platform.arch.get_reg_name(params[i].location.storage) + elif params[i].location.type == VariableSourceType.StackVariableSourceType: + name = "arg_%x" % params[i].location.storage + param_location = variable.VariableNameAndType( + params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type + ) + result.append(FunctionParameter(param_type, params[i].name, param_location)) + core.BNFreeTypeParameterList(params, count.value) + return result + + @property def has_variable_arguments(self) -> BoolWithConfidence: """Whether type has variable arguments (read-only)""" result = core.BNTypeHasVariableArguments(self._handle) diff --git a/python/variable.py b/python/variable.py index 8ed3f545..7b4121bf 100644 --- a/python/variable.py +++ b/python/variable.py @@ -20,14 +20,14 @@ # IN THE SOFTWARE. import ctypes -from typing import List, Generator, Optional, Union, Set, Dict +from typing import List, Generator, Optional, Union, Set, Dict, Tuple from dataclasses import dataclass import binaryninja from . import _binaryninjacore as core from . import databuffer from . import decorators -from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType +from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType, BuiltinType FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", "binaryninja.mediumlevelil.MediumLevelILFunction", @@ -223,6 +223,12 @@ class ConstantData(RegisterValue): raise ValueError(f"ConstantData requires a Function instance: {self.size}") return self.function.get_constant_data(self.type, self.value, self.size) + @property + def data_and_builtin(self) -> Tuple[databuffer.DataBuffer, BuiltinType]: + if self.function is None: + raise ValueError(f"ConstantData requires a Function instance: {self.size}") + return self.function.get_constant_data_and_builtin(self.type, self.value, self.size) + @dataclass(frozen=True) class ValueRange: |
