├── README.md ├── __init__.py ├── images └── example.png ├── plugin.json └── vtil ├── __init__.py ├── parser.py ├── utils.py ├── vtil.py └── vtilview.py /README.md: -------------------------------------------------------------------------------- 1 | # VTIL-BinaryNinja 2 | VTIL meets Binary Ninja and provides you with a solution to analyze VTIL code in a less painful manner. 3 | 4 | ## Installation 5 | Install via the Plugin Manager in Binary Ninja, or clone this repository into your [plugin folder](https://docs.binary.ninja/guide/plugins.html#using-plugins). 6 | Note: If you default to an IL view, you will need to manually make sure you select an Assembly view as this plugin does not have any lifting at this time. 7 | 8 | ## Screenshots 9 | ![](images/example.png) 10 | 11 | ## Disclaimer 12 | This is a **very early proof of concept**. Expect bugs. 13 | 14 | Known issues: 15 | - If multiple VTIL files are open, switching tabs to one with a lot of content can cause Binary Ninja to crash 16 | - Goto labels are not clickable 17 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from . import vtil -------------------------------------------------------------------------------- /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vtil-project/VTIL-BinaryNinja/31661b2b81e13c4ce918d12e45b8e29ff42879d3/images/example.png -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginmetadataversion": 2, 3 | "name": "VTIL Plugin", 4 | "type": [ 5 | "architecture", 6 | "binaryview" 7 | ], 8 | "api": [ 9 | "python2", 10 | "python3" 11 | ], 12 | "description": "Integrates the VTIL (Virtual-machine Translation Intermediate Language) Project with Binary Ninja", 13 | "longdescription": "# VTIL-BinaryNinja\nVTIL meets Binary Ninja and provides you with a solution to analyze VTIL code in a less painful manner.\n\nNote: If you default to an IL view, you will need to manaually make sure you select an Assembly view as this plugin does not have any lifting at this time.\n\n## Installation\nInstall via the Plugin Manager in Binary Ninja, or clone this repository into your [plugin folder](https://docs.binary.ninja/guide/plugins.html#using-plugins).\n\n## Screenshots\n![](https://raw.githubusercontent.com/psifertex/VTIL-BinaryNinja/master/images/example.png)\n\n## Dislcaimer\nThis is a **very early proof of concept**. Expect bugs. \n\nKnown issues:\n- Only one VTIL file supported per Binary Ninja process\n- Goto labels are not clickable\n", 14 | "license": { 15 | "name": "BSD-3-Clause", 16 | "text": "Copyright (c) 2020, Can Bölük\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\nlist of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright notice,\nthis list of conditions and the following disclaimer in the documentation\nand/or other materials provided with the distribution.\n\n* Neither the name of [project] nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" 17 | }, 18 | "platforms": [ 19 | "Darwin", 20 | "Linux", 21 | "Windows" 22 | ], 23 | "installinstructions": { 24 | "Darwin": "Install the plugin using the plugin manager or clone the repository to your plugins folder.", 25 | "Linux": "Install the plugin using the plugin manager or clone the repository to your plugins folder.", 26 | "Windows": "Install the plugin using the plugin manager or clone the repository to your plugins folder." 27 | }, 28 | "dependencies": { 29 | "pip": [ 30 | "kaitaistruct", 31 | "capstone" 32 | ] 33 | }, 34 | "version": "1.1", 35 | "author": "Layle", 36 | "minimumbinaryninjaversion": 1528 37 | } -------------------------------------------------------------------------------- /vtil/__init__.py: -------------------------------------------------------------------------------- 1 | from .vtil import VTIL 2 | from .vtilview import VTILView 3 | 4 | VTIL.register() 5 | VTILView.register() -------------------------------------------------------------------------------- /vtil/parser.py: -------------------------------------------------------------------------------- 1 | # This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild 2 | 3 | from pkg_resources import parse_version 4 | from kaitaistruct import __version__ as ks_version, KaitaiStruct, KaitaiStream, BytesIO 5 | from enum import Enum 6 | 7 | 8 | if parse_version(ks_version) < parse_version('0.7'): 9 | raise Exception("Incompatible Kaitai Struct Python API: 0.7 or later is required, but you have %s" % (ks_version)) 10 | 11 | class VTILParser(KaitaiStruct): 12 | 13 | class ArchitectureIdentifier(Enum): 14 | amd64 = 0 15 | arm64 = 1 16 | virtual = 2 17 | def __init__(self, _io, _parent=None, _root=None): 18 | self._io = _io 19 | self._parent = _parent 20 | self._root = _root if _root else self 21 | self._read() 22 | 23 | def _read(self): 24 | self.header = self._root.Header(self._io, self, self._root) 25 | self.entrypoint = self._root.Entrypoint(self._io, self, self._root) 26 | self.routine_convention = self._root.RoutineConvention(self._io, self, self._root) 27 | self.subroutine_convention = self._root.SubroutineConvention(self._io, self, self._root) 28 | self.spec_subroutine_conventions = self._root.SpecSubroutineConventions(self._io, self, self._root) 29 | self.explored_blocks = self._root.ExploredBlocks(self._io, self, self._root) 30 | 31 | class SubroutineConvention(KaitaiStruct): 32 | def __init__(self, _io, _parent=None, _root=None): 33 | self._io = _io 34 | self._parent = _parent 35 | self._root = _root if _root else self 36 | self._read() 37 | 38 | def _read(self): 39 | self.volatile_registers_count = self._io.read_u4le() 40 | self.volatile_registers = [None] * (self.volatile_registers_count) 41 | for i in range(self.volatile_registers_count): 42 | self.volatile_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 43 | 44 | self.param_registers_count = self._io.read_u4le() 45 | self.param_registers = [None] * (self.param_registers_count) 46 | for i in range(self.param_registers_count): 47 | self.param_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 48 | 49 | self.retval_registers_count = self._io.read_u4le() 50 | self.retval_registers = [None] * (self.retval_registers_count) 51 | for i in range(self.retval_registers_count): 52 | self.retval_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 53 | 54 | self.frame_register = self._root.RegisterDesc(self._io, self, self._root) 55 | self.shadow_space = self._io.read_u8le() 56 | self.purge_stack = self._io.read_u1() 57 | 58 | 59 | class Operand(KaitaiStruct): 60 | def __init__(self, _io, _parent=None, _root=None): 61 | self._io = _io 62 | self._parent = _parent 63 | self._root = _root if _root else self 64 | self._read() 65 | 66 | def _read(self): 67 | self.sp_index = self._io.read_u4le() 68 | _on = self.sp_index 69 | if _on == 0: 70 | self.operand = self._root.ImmediateDesc(self._io, self, self._root) 71 | elif _on == 1: 72 | self.operand = self._root.RegisterDesc(self._io, self, self._root) 73 | 74 | 75 | class RegisterDesc(KaitaiStruct): 76 | def __init__(self, _io, _parent=None, _root=None): 77 | self._io = _io 78 | self._parent = _parent 79 | self._root = _root if _root else self 80 | self._read() 81 | 82 | def _read(self): 83 | self.flags = self._io.read_u8le() 84 | self.combined_id = self._io.read_u8le() 85 | self.bit_count = self._io.read_s4le() 86 | self.bit_offset = self._io.read_s4le() 87 | 88 | 89 | class RoutineConvention(KaitaiStruct): 90 | def __init__(self, _io, _parent=None, _root=None): 91 | self._io = _io 92 | self._parent = _parent 93 | self._root = _root if _root else self 94 | self._read() 95 | 96 | def _read(self): 97 | self.volatile_registers_count = self._io.read_u4le() 98 | self.volatile_registers = [None] * (self.volatile_registers_count) 99 | for i in range(self.volatile_registers_count): 100 | self.volatile_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 101 | 102 | self.param_registers_count = self._io.read_u4le() 103 | self.param_registers = [None] * (self.param_registers_count) 104 | for i in range(self.param_registers_count): 105 | self.param_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 106 | 107 | self.retval_registers_count = self._io.read_u4le() 108 | self.retval_registers = [None] * (self.retval_registers_count) 109 | for i in range(self.retval_registers_count): 110 | self.retval_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 111 | 112 | self.frame_register = self._root.RegisterDesc(self._io, self, self._root) 113 | self.shadow_space = self._io.read_u8le() 114 | self.purge_stack = self._io.read_u1() 115 | 116 | 117 | class Instruction(KaitaiStruct): 118 | def __init__(self, _io, _parent=None, _root=None): 119 | self._io = _io 120 | self._parent = _parent 121 | self._root = _root if _root else self 122 | self._read() 123 | 124 | def _read(self): 125 | self.name_len = self._io.read_u4le() 126 | self.name = (self._io.read_bytes(self.name_len)).decode(u"UTF-8") 127 | self.operands_amount = self._io.read_u4le() 128 | self.operands = [None] * (self.operands_amount) 129 | for i in range(self.operands_amount): 130 | self.operands[i] = self._root.Operand(self._io, self, self._root) 131 | 132 | self.vip = self._io.read_u8le() 133 | self.sp_offset = self._io.read_s8le() 134 | self.sp_index = self._io.read_u4le() 135 | self.sp_reset = self._io.read_u1() 136 | 137 | 138 | class ImmediateDesc(KaitaiStruct): 139 | def __init__(self, _io, _parent=None, _root=None): 140 | self._io = _io 141 | self._parent = _parent 142 | self._root = _root if _root else self 143 | self._read() 144 | 145 | def _read(self): 146 | self.imm = self._io.read_u8le() 147 | self.bitcount = self._io.read_u4le() 148 | 149 | 150 | class ExploredBlocks(KaitaiStruct): 151 | def __init__(self, _io, _parent=None, _root=None): 152 | self._io = _io 153 | self._parent = _parent 154 | self._root = _root if _root else self 155 | self._read() 156 | 157 | def _read(self): 158 | self.basic_blocks_amount = self._io.read_u4le() 159 | self.basic_blocks = [None] * (self.basic_blocks_amount) 160 | for i in range(self.basic_blocks_amount): 161 | self.basic_blocks[i] = self._root.BasicBlock(self._io, self, self._root) 162 | 163 | 164 | 165 | class SpecSubroutineConventions(KaitaiStruct): 166 | def __init__(self, _io, _parent=None, _root=None): 167 | self._io = _io 168 | self._parent = _parent 169 | self._root = _root if _root else self 170 | self._read() 171 | 172 | def _read(self): 173 | self.spec_subroutine_conventions_amount = self._io.read_u4le() 174 | self.spec_subroutine_convention = [None] * (self.spec_subroutine_conventions_amount) 175 | for i in range(self.spec_subroutine_conventions_amount): 176 | self.spec_subroutine_convention[i] = self._root.SubroutineConvention(self._io, self, self._root) 177 | 178 | 179 | 180 | class SpecSubroutineConvention(KaitaiStruct): 181 | def __init__(self, _io, _parent=None, _root=None): 182 | self._io = _io 183 | self._parent = _parent 184 | self._root = _root if _root else self 185 | self._read() 186 | 187 | def _read(self): 188 | self.vip = self._io.read_u8le() 189 | self.volatile_registers_count = self._io.read_u4le() 190 | self.volatile_registers = [None] * (self.volatile_registers_count) 191 | for i in range(self.volatile_registers_count): 192 | self.volatile_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 193 | 194 | self.param_registers_count = self._io.read_u4le() 195 | self.param_registers = [None] * (self.param_registers_count) 196 | for i in range(self.param_registers_count): 197 | self.param_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 198 | 199 | self.retval_registers_count = self._io.read_u4le() 200 | self.retval_registers = [None] * (self.retval_registers_count) 201 | for i in range(self.retval_registers_count): 202 | self.retval_registers[i] = self._root.RegisterDesc(self._io, self, self._root) 203 | 204 | self.frame_register = self._root.RegisterDesc(self._io, self, self._root) 205 | self.shadow_space = self._io.read_u8le() 206 | self.purge_stack = self._io.read_u1() 207 | 208 | 209 | class Entrypoint(KaitaiStruct): 210 | def __init__(self, _io, _parent=None, _root=None): 211 | self._io = _io 212 | self._parent = _parent 213 | self._root = _root if _root else self 214 | self._read() 215 | 216 | def _read(self): 217 | self.entry_vip = self._io.read_u8le() 218 | 219 | 220 | class BasicBlock(KaitaiStruct): 221 | def __init__(self, _io, _parent=None, _root=None): 222 | self._io = _io 223 | self._parent = _parent 224 | self._root = _root if _root else self 225 | self._read() 226 | 227 | def _read(self): 228 | self.entry_vip = self._io.read_u8le() 229 | self.sp_offset = self._io.read_s8le() 230 | self.sp_index = self._io.read_u4le() 231 | self.last_temporary_index = self._io.read_u4le() 232 | self.instruction_amount = self._io.read_u4le() 233 | self.instructions = [None] * (self.instruction_amount) 234 | for i in range(self.instruction_amount): 235 | self.instructions[i] = self._root.Instruction(self._io, self, self._root) 236 | 237 | self.prev_vip_amount = self._io.read_u4le() 238 | self.prev_vip = [None] * (self.prev_vip_amount) 239 | for i in range(self.prev_vip_amount): 240 | self.prev_vip[i] = self._io.read_u8le() 241 | 242 | self.next_vip_amount = self._io.read_u4le() 243 | self.next_vip = [None] * (self.next_vip_amount) 244 | for i in range(self.next_vip_amount): 245 | self.next_vip[i] = self._io.read_u8le() 246 | 247 | 248 | 249 | class Header(KaitaiStruct): 250 | def __init__(self, _io, _parent=None, _root=None): 251 | self._io = _io 252 | self._parent = _parent 253 | self._root = _root if _root else self 254 | self._read() 255 | 256 | def _read(self): 257 | self.magic1 = self._io.read_u4le() 258 | self.arch_id = self._root.ArchitectureIdentifier(self._io.read_u1()) 259 | self.zero_pad = self._io.read_u1() 260 | self.magic2 = self._io.read_u2le() 261 | 262 | 263 | -------------------------------------------------------------------------------- /vtil/utils.py: -------------------------------------------------------------------------------- 1 | from binaryninja.log import log_info, log_error 2 | from binaryninjaui import DockHandler 3 | 4 | from .parser import VTILParser 5 | 6 | from capstone import * 7 | 8 | import tempfile 9 | import json 10 | import os 11 | 12 | # from: https://github.com/vtil-project/VTIL-Core/blob/master/VTIL-Architecture/arch/register_desc.hpp#L40 13 | register_virtual = 0 14 | register_physical = 1 << 0 15 | register_local = 1 << 1 16 | register_flags = 1 << 2 17 | register_stack_pointer = 1 << 3 18 | register_image_base = 1 << 4 19 | register_volatile = 1 << 5 20 | register_readonly = 1 << 6 21 | register_undefined = 1 << 7 22 | register_internal = register_virtual | (1 << 8) 23 | register_special = register_flags | register_stack_pointer | register_image_base | register_undefined 24 | 25 | # from: https://github.com/vtil-project/VTIL-Core/blob/master/VTIL-Architecture/arch/register_desc.hpp#L223 26 | def is_internal(flags): 27 | return flags & register_internal == register_internal 28 | 29 | def get_physical(reg, arch): 30 | cs = None 31 | if arch == 0: 32 | cs = Cs(CS_ARCH_X86, CS_MODE_64) 33 | elif arch == 1: 34 | cs = Cs(CS_ARCH_ARM, CS_MODE_ARM) 35 | return cs.reg_name(reg) 36 | 37 | # from: https://github.com/vtil-project/VTIL-Core/blob/master/VTIL-Architecture/arch/register_desc.hpp#L244 38 | def to_string(flags, bit_offset, bit_count, local_id, architecture): 39 | prefix = "" 40 | suffix = "" 41 | 42 | if flags & register_volatile: prefix = "?" 43 | if flags & register_readonly: prefix += "&&" 44 | 45 | if bit_offset != 0: suffix = f"@{bit_offset}" 46 | if bit_count != 64: suffix += f":{bit_count}" 47 | 48 | if is_internal(flags): return f"{prefix}sr{local_id}{suffix}" 49 | if flags & register_undefined: return f"{prefix}UD{suffix}" 50 | if flags & register_flags: return f"{prefix}$flags{suffix}" 51 | if flags & register_stack_pointer: return f"{prefix}$sp{suffix}" 52 | if flags & register_image_base: return f"{prefix}base{suffix}" 53 | if flags & register_local: return f"{prefix}t{local_id}{suffix}" 54 | 55 | if flags & register_physical: 56 | reg = get_physical(local_id, architecture) 57 | return f"{prefix}{reg}{suffix}" 58 | 59 | return f"{prefix}vr{local_id}{suffix}" 60 | 61 | def find_instruction(addr, vtil): 62 | # Initialize the cache if not done already. 63 | if not hasattr(vtil, "parser_cache"): 64 | setattr(vtil, "parser_cache", {}) 65 | 66 | # If cached already, return the reslt. 67 | if addr in vtil.parser_cache: 68 | return vtil.parser_cache[addr] 69 | it = addr 70 | 71 | for basic_block in vtil.explored_blocks.basic_blocks: 72 | instructions = basic_block.instructions 73 | if it - len(instructions) > 0: 74 | it -= len(instructions) 75 | continue 76 | 77 | for instruction in instructions: 78 | if it == 0: 79 | code = "" 80 | code += instruction.name + " " 81 | 82 | for operand in instruction.operands: 83 | operand = operand.operand 84 | 85 | if isinstance(operand, VTILParser.RegisterDesc): 86 | architecture = int(bin(operand.combined_id)[2:].zfill(64)[:56], 2) # lol? 87 | local_id = int(bin(operand.combined_id)[2:].zfill(64)[56:], 2) # lol? 88 | code += to_string(operand.flags, operand.bit_offset, operand.bit_count, local_id, architecture) 89 | code += " " 90 | else: 91 | code += hex(operand.imm) + " " 92 | 93 | res = (basic_block.next_vip, instruction.sp_index, instruction.sp_reset, instruction.sp_offset, code.strip()) 94 | vtil.parser_cache[addr] = res 95 | return res 96 | it -= 1 97 | 98 | def find_block_address(vip, vtil): 99 | addr = 0 100 | 101 | for basic_block in vtil.explored_blocks.basic_blocks: 102 | if basic_block.entry_vip == vip: break 103 | 104 | addr += len(basic_block.instructions) 105 | 106 | return addr -------------------------------------------------------------------------------- /vtil/vtil.py: -------------------------------------------------------------------------------- 1 | from binaryninja.log import log_info, log_error 2 | from binaryninja.architecture import Architecture 3 | from binaryninja.function import RegisterInfo, InstructionInfo, InstructionTextToken 4 | from binaryninja.enums import InstructionTextTokenType, BranchType 5 | from binaryninja.filemetadata import FileMetadata 6 | from .parser import VTILParser 7 | from .utils import to_string, find_instruction, find_block_address 8 | 9 | # Still a horrible hack but better than files. 10 | # 11 | active_vtil_file = None 12 | 13 | def set_active_vtil_file(vtil_struct): 14 | global active_vtil_file 15 | active_vtil_file = vtil_struct 16 | 17 | class VTIL(Architecture): 18 | name = "VTIL" 19 | max_instr_length = 1 20 | stack_pointer = "$sp" 21 | 22 | regs = { 23 | "$sp" : RegisterInfo("$sp", 1) 24 | } 25 | 26 | instructions = { 27 | "str": { 28 | "tokens": [ 29 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "str"), 30 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 31 | InstructionTextToken(InstructionTextTokenType.TextToken, "["), 32 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 33 | InstructionTextToken(InstructionTextTokenType.TextToken, "+"), 34 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 35 | InstructionTextToken(InstructionTextTokenType.TextToken, "]"), 36 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, ", "), 37 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 38 | ], 39 | "operands": [3, 5, 8] 40 | }, 41 | "ldd": { 42 | "tokens": [ 43 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "ldd"), 44 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 45 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 46 | InstructionTextToken(InstructionTextTokenType.TextToken, ", ["), 47 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 48 | InstructionTextToken(InstructionTextTokenType.TextToken, "+"), 49 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 50 | InstructionTextToken(InstructionTextTokenType.TextToken, "]"), 51 | ], 52 | "operands": [2, 4, 6] 53 | }, 54 | "te": { 55 | "tokens": [ 56 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "te"), 57 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 58 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 59 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 60 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 61 | InstructionTextToken(InstructionTextTokenType.TextToken, " == "), 62 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 63 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 64 | ], 65 | "operands": [2, 4, 6] 66 | }, 67 | "tne": { 68 | "tokens": [ 69 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tne"), 70 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 71 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 72 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 73 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 74 | InstructionTextToken(InstructionTextTokenType.TextToken, " != "), 75 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 76 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 77 | ], 78 | "operands": [2, 4, 6] 79 | }, 80 | "tg": { 81 | "tokens": [ 82 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tg"), 83 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 84 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 85 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 86 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 87 | InstructionTextToken(InstructionTextTokenType.TextToken, " > "), 88 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 89 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 90 | ], 91 | "operands": [2, 4, 6] 92 | }, 93 | "tge": { 94 | "tokens": [ 95 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tge"), 96 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 97 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 98 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 99 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 100 | InstructionTextToken(InstructionTextTokenType.TextToken, " >= "), 101 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 102 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 103 | ], 104 | "operands": [2, 4, 6] 105 | }, 106 | "tl": { 107 | "tokens": [ 108 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tl"), 109 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 110 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 111 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 112 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 113 | InstructionTextToken(InstructionTextTokenType.TextToken, " < "), 114 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 115 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 116 | ], 117 | "operands": [2, 4, 6] 118 | }, 119 | "tle": { 120 | "tokens": [ 121 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tle"), 122 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 123 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 124 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 125 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 126 | InstructionTextToken(InstructionTextTokenType.TextToken, " <= "), 127 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 128 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 129 | ], 130 | "operands": [2, 4, 6] 131 | }, 132 | "tug": { 133 | "tokens": [ 134 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tug"), 135 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 136 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 137 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 138 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 139 | InstructionTextToken(InstructionTextTokenType.TextToken, " u> "), 140 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 141 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 142 | ], 143 | "operands": [2, 4, 6] 144 | }, 145 | "tuge": { 146 | "tokens": [ 147 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tuge"), 148 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 149 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 150 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 151 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 152 | InstructionTextToken(InstructionTextTokenType.TextToken, " u>= "), 153 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 154 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 155 | ], 156 | "operands": [2, 4, 6] 157 | }, 158 | "tul": { 159 | "tokens": [ 160 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tul"), 161 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 162 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 163 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 164 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 165 | InstructionTextToken(InstructionTextTokenType.TextToken, " u< "), 166 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 167 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 168 | ], 169 | "operands": [2, 4, 6] 170 | }, 171 | "tule": { 172 | "tokens": [ 173 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "tule"), 174 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 175 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 176 | InstructionTextToken(InstructionTextTokenType.TextToken, " := ("), 177 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 178 | InstructionTextToken(InstructionTextTokenType.TextToken, " u<= "), 179 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 180 | InstructionTextToken(InstructionTextTokenType.TextToken, ")") 181 | ], 182 | "operands": [2, 4, 6] 183 | }, 184 | "ifs": { 185 | "tokens": [ 186 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "ifs"), 187 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 188 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 189 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " := "), 190 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 191 | InstructionTextToken(InstructionTextTokenType.TextToken, " ? "), 192 | InstructionTextToken(InstructionTextTokenType.TextToken, "UNKNOWN"), 193 | InstructionTextToken(InstructionTextTokenType.TextToken, " : "), 194 | InstructionTextToken(InstructionTextTokenType.IntegerToken, "0") 195 | ], 196 | "operands": [2, 4, 6] 197 | }, 198 | "js": { 199 | "tokens": [ 200 | InstructionTextToken(InstructionTextTokenType.InstructionToken, "js"), 201 | InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " "), 202 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 203 | InstructionTextToken(InstructionTextTokenType.TextToken, " ? "), 204 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN"), 205 | InstructionTextToken(InstructionTextTokenType.TextToken, " : "), 206 | InstructionTextToken(InstructionTextTokenType.RegisterToken, "UNKNOWN") 207 | ], 208 | "operands": [2, 4, 6] 209 | } 210 | } 211 | 212 | def get_instruction_info(self, data, addr): 213 | global active_vtil_file 214 | result = InstructionInfo() 215 | result.length = 1 216 | 217 | next_vip, _, _, _, code = find_instruction(addr, active_vtil_file) 218 | 219 | if code != None and code.startswith("js"): 220 | try: 221 | _, _, true, false = code.split(" ") 222 | true = find_block_address(int(true, 16), active_vtil_file) 223 | false = find_block_address(int(false, 16), active_vtil_file) 224 | result.add_branch(BranchType.TrueBranch, true) 225 | result.add_branch(BranchType.FalseBranch, false) 226 | except ValueError: 227 | v1 = find_block_address(next_vip[0], active_vtil_file) 228 | v2 = find_block_address(next_vip[1], active_vtil_file) 229 | result.add_branch(BranchType.UnconditionalBranch, v2) 230 | result.add_branch(BranchType.UnconditionalBranch, v1) 231 | elif code != None and code.startswith("vxcall"): 232 | addr = find_block_address(next_vip[0], active_vtil_file) 233 | result.add_branch(BranchType.UnconditionalBranch, addr) 234 | elif code != None and code.startswith("jmp"): 235 | if len(next_vip) == 1: 236 | addr = find_block_address(next_vip[0], active_vtil_file) 237 | result.add_branch(BranchType.UnconditionalBranch, addr) 238 | else: 239 | result.add_branch(BranchType.IndirectBranch) 240 | for vip in next_vip: 241 | result.add_branch(BranchType.UnconditionalBranch, find_block_address(vip, active_vtil_file)) 242 | elif code != None and code.startswith("vexit"): 243 | result.add_branch(BranchType.FunctionReturn) 244 | 245 | return result 246 | 247 | def get_instruction_text(self, data, addr): 248 | global active_vtil_file 249 | tokens = [] 250 | 251 | next_vip, sp_index, sp_reset, sp_offset, code = find_instruction(addr, active_vtil_file) 252 | if code == None: 253 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "ERROR")) 254 | return tokens, 1 255 | 256 | if sp_index > 0: 257 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "[")) 258 | tokens.append(InstructionTextToken(InstructionTextTokenType.IntegerToken, f"{int(sp_index):>2}", value=sp_index, size=64)) 259 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, "] ")) 260 | else: 261 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) 262 | 263 | prefix = "-" 264 | if sp_offset >= 0: prefix = "+" 265 | sp_offset = abs(sp_offset) 266 | 267 | if sp_reset > 0: 268 | txt = f">{prefix}{hex(sp_offset)}" 269 | txt = f"{txt:<6}" 270 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, txt)) 271 | else: 272 | txt = f" {prefix}{hex(sp_offset)}" 273 | txt = f"{txt:<6}" 274 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, txt)) 275 | tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " ")) 276 | 277 | 278 | if " " in code: 279 | instr, operands = code.split(" ", 1) 280 | 281 | if " " in operands: 282 | operands = operands.split(" ") 283 | else: 284 | operands = [operands] 285 | 286 | if instr in self.instructions.keys(): 287 | token_set = self.instructions[instr]["tokens"] 288 | 289 | for index in self.instructions[instr]["operands"]: 290 | operand = operands.pop(0) 291 | 292 | if "0x" in operand: 293 | if instr == "js": 294 | token_set[index] = InstructionTextToken(InstructionTextTokenType.GotoLabelToken, f"vip_{operand[2:]}") 295 | elif instr == "jmp": 296 | token_set[index] = InstructionTextToken(InstructionTextTokenType.GotoLabelToken, f"vip_{hex(next_vip[0])[2:]}") 297 | else: 298 | token_set[index] = InstructionTextToken(InstructionTextTokenType.IntegerToken, operand, value=int(operand, 16), size=64) 299 | else: 300 | token_set[index] = InstructionTextToken(InstructionTextTokenType.RegisterToken, operand) 301 | 302 | tokens.extend(token_set) 303 | else: 304 | # fallback 305 | tokens.append(InstructionTextToken(InstructionTextTokenType.InstructionToken, instr)) 306 | tokens.append(InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, " ")) 307 | 308 | for operand in operands: 309 | if "0x" in operand: 310 | if instr == "jmp": 311 | tokens.append(InstructionTextToken(InstructionTextTokenType.GotoLabelToken, f"vip_{hex(next_vip[0])[2:]}")) 312 | else: 313 | tokens.append(InstructionTextToken(InstructionTextTokenType.IntegerToken, operand, value=int(operand, 16), size=64)) 314 | else: 315 | tokens.append(InstructionTextToken(InstructionTextTokenType.RegisterToken, operand)) 316 | tokens.append(InstructionTextToken(InstructionTextTokenType.OperandSeparatorToken, ", ")) 317 | 318 | tokens.pop() 319 | else: 320 | tokens.append(InstructionTextToken(InstructionTextTokenType.InstructionToken, code)) 321 | 322 | return tokens, 1 323 | 324 | 325 | def get_instruction_low_level_il(self, data, addr, il): 326 | pass 327 | -------------------------------------------------------------------------------- /vtil/vtilview.py: -------------------------------------------------------------------------------- 1 | from binaryninja.architecture import Architecture 2 | from binaryninja.binaryview import BinaryView 3 | from binaryninja.platform import Platform 4 | from binaryninja.types import Symbol 5 | from binaryninja.enums import SegmentFlag, SectionSemantics, SymbolType 6 | from binaryninja.log import log_error, log_info 7 | 8 | from .parser import VTILParser 9 | from .vtil import VTIL, set_active_vtil_file 10 | from .utils import find_block_address, find_instruction 11 | 12 | import tempfile 13 | import json 14 | import os 15 | 16 | class VTILView(BinaryView): 17 | name = "VTIL" 18 | long_name = "VTIL" 19 | vtil = None 20 | 21 | def __init__(self, data): 22 | BinaryView.__init__(self, parent_view=data, file_metadata=data.file) 23 | self.platform = Architecture["VTIL"].standalone_platform 24 | self.vtil = VTILParser.from_file(data.file.filename) 25 | set_active_vtil_file(self.vtil) 26 | 27 | @classmethod 28 | def is_valid_for_data(self, data): 29 | return data[0:4] == b'VTIL' 30 | 31 | def init(self): 32 | max_instructions = 0 33 | for basic_block in self.vtil.explored_blocks.basic_blocks: 34 | max_instructions += len(basic_block.instructions) 35 | 36 | # Fill the cache 37 | for i in range(0, max_instructions): 38 | find_instruction(i, self.vtil) 39 | 40 | self.add_auto_segment( 41 | 0, max_instructions, 0, max_instructions, 42 | SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable | SegmentFlag.SegmentExecutable 43 | ) 44 | self.add_auto_section( 45 | ".text", 0, max_instructions, 46 | SectionSemantics.ReadOnlyCodeSectionSemantics 47 | ) 48 | 49 | entry_vip = self.vtil.entrypoint.entry_vip 50 | entry_addr = find_block_address(entry_vip, self.vtil) 51 | symbol = Symbol(SymbolType.FunctionSymbol, entry_addr, f"_vip_{hex(entry_vip)[2:]}") 52 | self.define_auto_symbol(symbol) 53 | 54 | for basic_block in self.vtil.explored_blocks.basic_blocks: 55 | vip = basic_block.entry_vip 56 | addr = find_block_address(vip, self.vtil) 57 | 58 | # Append a comment to help with indirect jumps: 59 | comment = "" 60 | branch_ins = basic_block.instructions[-1] 61 | if branch_ins.name == "jmp": 62 | if isinstance(branch_ins.operands[0].operand, VTILParser.RegisterDesc): 63 | comment += "Indirect => { " + ', '.join('vip_{:x}'.format(trgt) for trgt in basic_block.next_vip) + " }" 64 | 65 | if basic_block.sp_offset != branch_ins.sp_offset: 66 | if comment != "": comment += " | " 67 | comment += f"SP Delta: {hex(basic_block.sp_offset)}" 68 | 69 | if comment != "": 70 | self.set_comment_at(addr + len(basic_block.instructions) - 1, comment) 71 | 72 | if entry_vip == vip: continue 73 | 74 | symbol = Symbol(SymbolType.FunctionSymbol, addr, f"vip_{hex(vip)[2:]}") 75 | self.define_auto_symbol(symbol) 76 | self.set_comment_at(addr, f"vip_{hex(vip)[2:]}:") 77 | 78 | 79 | self.add_entry_point(entry_addr) 80 | 81 | return True 82 | --------------------------------------------------------------------------------