├── .gitignore ├── img ├── decompiler.png └── binja_vs_ghidra.png ├── .gitmodules ├── LICENSE ├── CMakeLists.txt ├── README.md └── src └── pcode_architecture.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /img/decompiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pd0wm/binaryninja-pcode/HEAD/img/decompiler.png -------------------------------------------------------------------------------- /img/binja_vs_ghidra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pd0wm/binaryninja-pcode/HEAD/img/binja_vs_ghidra.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/ghidra"] 2 | path = third_party/ghidra 3 | url = https://github.com/NationalSecurityAgency/ghidra 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | binaryninja-pcode is a library built around the SLEIGH library. 2 | 3 | SLEIGH and the processor definition files under the pypcode/processors directory 4 | originate from the Ghidra project (https://ghidra-sre.org/). SLEIGH is released 5 | under the terms of the Apache 2 license as defined in docs/ghidra/LICENSE. See 6 | NOTICE file in docs/ghidra/NOTICE. 7 | 8 | The remaining code of binaryninja-pcode, unless stated otherwise, is licensed under the 9 | terms of the 2-clause BSD license below. 10 | 11 | ================================================================================ 12 | 13 | Copyright (c) 2021, Arizona Board of Regents 14 | 15 | Redistribution and use in source and binary forms, with or without 16 | modification, are permitted provided that the following conditions are met: 17 | 18 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 19 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9 FATAL_ERROR) 2 | 3 | project(binaryninja-pcode CXX C) 4 | 5 | if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD)) 6 | set(BN_API_PATH $ENV{BN_API_PATH}) 7 | if(NOT BN_API_PATH) 8 | message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH") 9 | endif() 10 | endif() 11 | 12 | if((NOT BN_INSTALL_DIR) AND (NOT BN_INTERNAL_BUILD) AND WIN32) 13 | set(BN_INSTALL_DIR $ENV{BN_INSTALL_DIR}) 14 | if(NOT BN_INSTALL_DIR) 15 | message(FATAL_ERROR "Provide path to Binary Ninja installation in BN_INSTALL_DIR") 16 | endif() 17 | endif() 18 | 19 | if(NOT BN_INTERNAL_BUILD) 20 | add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api) 21 | endif() 22 | 23 | find_package(Qt6 REQUIRED COMPONENTS Core) 24 | 25 | set (CMAKE_CXX_STANDARD 17) 26 | 27 | set(SLEIGH_COMMON 28 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/address.cc 29 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/context.cc 30 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/float.cc 31 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/globalcontext.cc 32 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/opcodes.cc 33 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc 34 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/pcodeparse.cc 35 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/pcoderaw.cc 36 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc 37 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.cc 38 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/sleighbase.cc 39 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghpatexpress.cc 40 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghpattern.cc 41 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghsymbol.cc 42 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/space.cc 43 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/translate.cc 44 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/xml.cc 45 | ) 46 | 47 | add_executable(sleigh 48 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/filemanage.cc 49 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc 50 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghparse.cc 51 | third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.cc 52 | ${SLEIGH_COMMON} 53 | ) 54 | 55 | file(GLOB_RECURSE slaspec_FILES third_party/ghidra/Ghidra/Processors/*.slaspec) 56 | foreach(slaspec ${slaspec_FILES}) 57 | get_filename_component(sla ${slaspec} NAME_WLE) 58 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/out/sla) 59 | 60 | add_custom_command( 61 | OUTPUT ${CMAKE_BINARY_DIR}/out/sla/${sla}.sla 62 | COMMAND ${CMAKE_BINARY_DIR}/sleigh ${slaspec} ${CMAKE_BINARY_DIR}/out/sla/${sla}.sla 63 | DEPENDS ${CMAKE_BINARY_DIR}/sleigh 64 | DEPENDS ${slaspec} 65 | ) 66 | list(APPEND SLA_FILES ${CMAKE_BINARY_DIR}/out/sla/${sla}.sla) 67 | endforeach() 68 | 69 | include_directories( 70 | . 71 | ${Qt6Core_INCLUDE_DIRS} 72 | ) 73 | 74 | add_library(${PROJECT_NAME} SHARED 75 | ${SLEIGH_COMMON} 76 | ${SLA_FILES} 77 | src/pcode_architecture.cpp 78 | ) 79 | 80 | qt_add_resources(${PROJECT_NAME} "sla" 81 | PREFIX "/" 82 | BASE ${CMAKE_BINARY_DIR} 83 | FILES ${SLA_FILES} 84 | ) 85 | 86 | target_link_libraries(${PROJECT_NAME} 87 | binaryninjaapi 88 | Qt6::Core 89 | ) 90 | 91 | set_target_properties(${PROJECT_NAME} PROPERTIES 92 | CXX_STANDARD 17 93 | CXX_VISIBILITY_PRESET hidden 94 | CXX_STANDARD_REQUIRED ON 95 | VISIBILITY_INLINES_HIDDEN ON 96 | POSITION_INDEPENDENT_CODE ON 97 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/bin 98 | ) 99 | 100 | bn_install_plugin(${PROJECT_NAME}) 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Binary Ninja PCode 2 | This plugin serves as a bridge between Binary Ninja and Ghidra's disassembler. This allows using all of Ghidra's supported architectures in Binary Ninja. It also makes a very poor attempt to convert p-code into Binary Ninja's Low Level IL. However, the outputted IL is very verbose and doesn't translate well into high level IL or Pseudo C. 3 | 4 | ![Comparison of disassembly between Binary Ninja and Ghidra](img/binja_vs_ghidra.png) 5 | *Comparison of disassembly between Binary Ninja and Ghidra. Architecture x86-64 - Binary: /bin/ls.* 6 | 7 | 8 | # Compilation 9 | Ensure the repository is cloned with submodules (`git submodule update --init`). 10 | ```bash 11 | mkdir build 12 | cd build 13 | CMAKE_PREFIX_PATH=/gcc_64/lib/cmake BN_INSTALL_DIR= BN_API_PATH= cmake .. 14 | make -j$(nproc) install 15 | ``` 16 | 17 | # Usage 18 | Specify an architecture to be loaded using the `LOAD_SLA` environment variable. If no architecture (or `all`) is specified all architectures wil be loaded on startup, which might take a while. 19 | 20 | ```bash 21 | LOAD_SLA="x86-64" ~/binaryninja/binaryninja 22 | ``` 23 | 24 | # Known issues 25 | - The Low Level IL is very verbose and produces unusable pseudo C. 26 | - The plugin does not set the sleigh context specified in the .pspec. For x86-64 this is current hard-coded. If you get unexpected results like wrong instruction sizes, try setting the context for you architecture manually. 27 | - No support for calling conventions or special registers (e.g. `sp`, `lp`). 28 | - Overlapping registers are not handled. E.g. `rax` and `eax` are treated as separate registers. 29 | - The disassembly is not tokenized into numbers and addresses, so clicking on an address doesn't work. 30 | - Translation from pcode to IL is missing some instructions, and there are probably bugs. 31 | 32 | ![Verbose Pseudo C output from Binary Ninja compared to Ghidra](img/decompiler.png) 33 | *Verbose Pseudo C output from Binary Ninja compared to Ghidra. Architecture V850 - Binary: some VW ecu dump.* 34 | 35 | # Credits 36 | - [Ghidra](https://github.com/NationalSecurityAgency/ghidra) - for the enormous amount of work that went into creating and supporting all these different architectures. 37 | - [pypcode](https://github.com/angr/pypcode) - python library from the `angr` project to interface with pcode. Used some of their helper classes to simplify loading bytes into the pcode context. 38 | 39 | # Supported architectures 40 | ``` 41 | 6502 42 | 65c02 43 | 68020 44 | 68030 45 | 68040 46 | 6805 47 | 6809 48 | 80251 49 | 80390 50 | 8048 51 | 8051 52 | 8085 53 | AARCH64 54 | AARCH64_AppleSilicon 55 | AARCH64BE 56 | ARM4_be 57 | ARM4_le 58 | ARM4t_be 59 | ARM4t_le 60 | ARM5_be 61 | ARM5_le 62 | ARM5t_be 63 | ARM5t_le 64 | ARM6_be 65 | ARM6_le 66 | ARM7_be 67 | ARM7_le 68 | ARM8_be 69 | ARM8_le 70 | avr32a 71 | avr8 72 | avr8e 73 | avr8eind 74 | avr8xmega 75 | coldfire 76 | CP1600 77 | CR16B 78 | CR16C 79 | Dalvik_Base 80 | Dalvik_DEX_Android10 81 | Dalvik_DEX_Android11 82 | Dalvik_DEX_Android12 83 | Dalvik_DEX_KitKat 84 | Dalvik_DEX_Lollipop 85 | Dalvik_DEX_Marshmallow 86 | Dalvik_DEX_Nougat 87 | Dalvik_DEX_Oreo 88 | Dalvik_DEX_Pie 89 | Dalvik_ODEX_KitKat 90 | data-be-64 91 | data-le-64 92 | dsPIC30F 93 | dsPIC33C 94 | dsPIC33E 95 | dsPIC33F 96 | H6309 97 | HC05 98 | HC08 99 | HCS08 100 | HCS12 101 | JVM 102 | m8c 103 | MCS96 104 | mips32be 105 | mips32le 106 | mips32R6be 107 | mips32R6le 108 | mips64be 109 | mips64le 110 | mx51 111 | pa-risc32be 112 | pic12c5xx 113 | pic16 114 | pic16c5x 115 | pic16f 116 | pic17c7xx 117 | pic18 118 | PIC24E 119 | PIC24F 120 | PIC24H 121 | ppc_32_4xx_be 122 | ppc_32_4xx_le 123 | ppc_32_be 124 | ppc_32_le 125 | ppc_32_quicciii_be 126 | ppc_32_quicciii_le 127 | ppc_64_be 128 | ppc_64_isa_altivec_be 129 | ppc_64_isa_altivec_le 130 | ppc_64_isa_altivec_vle_be 131 | ppc_64_isa_be 132 | ppc_64_isa_le 133 | ppc_64_isa_vle_be 134 | ppc_64_le 135 | riscv 136 | riscv 137 | sh-1 138 | sh-2 139 | sh-2a 140 | SparcV9_32 141 | SparcV9_64 142 | SuperH4_be 143 | SuperH4_le 144 | TI_MSP430 145 | TI_MSP430X 146 | toy_be 147 | toy_be_posStack 148 | toy_builder_be 149 | toy_builder_be_align2 150 | toy_builder_le 151 | toy_builder_le_align2 152 | toy_le 153 | toy_wsz_be 154 | toy_wsz_le 155 | toy64_be 156 | toy64_be_harvard 157 | toy64_le 158 | tricore 159 | V850 160 | x86 161 | x86-64 162 | z180 163 | z80 164 | ``` 165 | 166 | -------------------------------------------------------------------------------- /src/pcode_architecture.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/sleigh.hh" 10 | #include "third_party/ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/loadimage.hh" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "binaryninjaapi.h" 17 | #include "lowlevelilinstruction.h" 18 | 19 | using namespace BinaryNinja; 20 | 21 | template 22 | std::string format( const std::string& format, Args ... args ) 23 | { 24 | int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0' 25 | if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); } 26 | auto size = static_cast( size_s ); 27 | std::unique_ptr buf( new char[ size ] ); 28 | std::snprintf( buf.get(), size, format.c_str(), args ... ); 29 | return std::string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside 30 | } 31 | 32 | // https://github.com/angr/pypcode/blob/master/pypcode/native/csleigh.cc 33 | class SimpleLoadImage : public LoadImage 34 | { 35 | uintb m_baseaddr; 36 | int4 m_length; 37 | const unsigned char *m_data; 38 | 39 | public: 40 | SimpleLoadImage() : LoadImage("nofile") { 41 | m_baseaddr = 0; 42 | m_data = NULL; 43 | m_length = 0; 44 | } 45 | 46 | void setData(uintb ad, const unsigned char *ptr,int4 sz) { 47 | m_baseaddr = ad; 48 | m_data = ptr; 49 | m_length = sz; 50 | } 51 | 52 | void loadFill(uint1 *ptr, int4 size, const Address &addr) { 53 | uintb start = addr.getOffset(); 54 | uintb max = m_baseaddr + m_length - 1; 55 | 56 | // 57 | // When decoding an instruction, SLEIGH will attempt to pull in several 58 | // bytes at a time, starting at each instruction boundary. 59 | // 60 | // If the start address is outside of the defined range, bail out. 61 | // Otherwise, if we have some data to provide but cannot sastisfy the 62 | // entire request, fill the remainder of the buffer with zero. 63 | // 64 | if (start > max || start < m_baseaddr) { 65 | throw std::out_of_range("Attempting to lift outside buffer range"); 66 | } 67 | 68 | for(int4 i = 0; i < size; i++) { 69 | uintb curoff = start + i; 70 | if ((curoff < m_baseaddr) || (curoff>max)) { 71 | ptr[i] = 0; 72 | continue; 73 | } 74 | uintb diff = curoff - m_baseaddr; 75 | ptr[i] = m_data[(int4)diff]; 76 | } 77 | } 78 | 79 | virtual string getArchType(void) const { return "myload"; } 80 | virtual void adjustVma(long adjust) { } 81 | }; 82 | 83 | typedef struct { 84 | OpCode opcode; 85 | std::optional output; 86 | std::vector inputs; 87 | } PcodeOp; 88 | 89 | class PcodeEmitCacher : public PcodeEmit { 90 | public: 91 | vector m_ops; 92 | 93 | PcodeEmitCacher() { 94 | } 95 | 96 | void dump(const Address &addr, OpCode opc, VarnodeData *outvar, VarnodeData *vars, int4 isize) { 97 | PcodeOp op; 98 | op.opcode = opc; 99 | 100 | if (outvar != nullptr) { 101 | op.output = *outvar; 102 | } 103 | 104 | if (vars != nullptr && isize > 0) { 105 | for (int i = 0; i < isize; i++) { 106 | op.inputs.push_back(vars[i]); 107 | } 108 | } 109 | m_ops.push_back(op); 110 | } 111 | }; 112 | 113 | class AssemblyEmitCacher : public AssemblyEmit { 114 | public: 115 | Address m_addr; 116 | string m_mnem; 117 | string m_body; 118 | 119 | void dump(const Address &addr, const string &mnem, const string &body) { 120 | m_addr = addr; 121 | m_mnem = mnem; 122 | m_body = body; 123 | }; 124 | }; 125 | 126 | class PcodeArchitecture : public Architecture { 127 | SimpleLoadImage m_loader; 128 | ContextInternal m_context_internal; 129 | DocumentStorage m_document_storage; 130 | Document *m_document; 131 | Element *m_tags; 132 | std::unique_ptr m_sleigh; 133 | std::mutex m_sleigh_mutex; 134 | 135 | std::map m_register_varnodes; 136 | std::map m_register_names; 137 | std::map m_register_nums; 138 | std::vector m_userops; 139 | 140 | size_t m_addr_size; 141 | BNEndianness m_endianness; 142 | 143 | public: 144 | PcodeArchitecture(QFileInfo sla): Architecture("pcode_" + sla.baseName().toStdString()) { 145 | QFile qrc_file(sla.absoluteFilePath()); 146 | QTemporaryFile * tmp_file = QTemporaryFile::createNativeFile(qrc_file); // Returns a pointer to a temporary file 147 | const std::string tmp_path = tmp_file->fileName().toStdString(); 148 | 149 | qWarning() << "Loading" << sla.baseName(); 150 | try { 151 | m_document = m_document_storage.openDocument(tmp_path); 152 | m_tags = m_document->getRoot(); 153 | m_document_storage.registerTag(m_tags); 154 | } catch (...) { 155 | LogError("Error opening %s", tmp_path.c_str()); 156 | throw; 157 | } 158 | 159 | m_sleigh.reset(new Sleigh(&m_loader, &m_context_internal)); 160 | m_sleigh->initialize(m_document_storage); 161 | 162 | // TODO: get from pspec 163 | if (sla.baseName() == "x86-64") { 164 | m_sleigh->setContextDefault("addrsize", 2); 165 | m_sleigh->setContextDefault("bit64", 1); 166 | m_sleigh->setContextDefault("opsize", 1); 167 | m_sleigh->setContextDefault("rexprefix", 0); 168 | m_sleigh->setContextDefault("longMode", 1); 169 | } 170 | 171 | m_addr_size = m_sleigh->getDefaultCodeSpace()->getAddrSize(); 172 | m_endianness = m_sleigh->getDefaultCodeSpace()->isBigEndian() ? BigEndian : LittleEndian; 173 | 174 | // Registers 175 | std::map registers; 176 | m_sleigh->getAllRegisters(registers); 177 | int i = 0; 178 | for (auto const& [varnode, name] : registers) { 179 | LogInfo("%d - size %d offset %llu name %s", i, varnode.size, varnode.offset, name.c_str()); 180 | m_register_nums[varnode] = i; 181 | m_register_varnodes[i] = varnode; 182 | m_register_names[i] = name; 183 | i++; 184 | } 185 | 186 | // Userops 187 | m_sleigh->getUserOpNames(m_userops); 188 | i = 0; 189 | for (auto const &op : m_userops) { 190 | LogInfo("%d - %s", i, op.c_str()); 191 | i++; 192 | } 193 | } 194 | 195 | BNEndianness GetEndianness() const override { 196 | return m_endianness; 197 | } 198 | 199 | size_t GetAddressSize() const override { 200 | return m_addr_size; 201 | } 202 | 203 | virtual vector GetFullWidthRegisters() override { 204 | std::vector res; 205 | for (auto const& [num, varnode] : m_register_varnodes) { 206 | res.push_back(num); 207 | } 208 | return res; 209 | } 210 | 211 | virtual vector GetAllRegisters() override { 212 | std::vector res; 213 | for (auto const& [num, varnode] : m_register_varnodes) { 214 | res.push_back(num); 215 | } 216 | return res; 217 | } 218 | 219 | virtual BNRegisterInfo GetRegisterInfo(uint32_t reg) override { 220 | VarnodeData reg_node = m_register_varnodes[reg]; 221 | 222 | // TODO, handle overlapping registers 223 | BNRegisterInfo result = {reg, 0, reg_node.size, NoExtend}; 224 | return result; 225 | } 226 | 227 | virtual string GetRegisterName(uint32_t reg) override { 228 | return m_register_names[reg]; 229 | } 230 | 231 | virtual string GetIntrinsicName(uint32_t intrinsic) override { 232 | return m_userops[intrinsic]; 233 | } 234 | 235 | virtual string GetFlagName(uint32_t reg) override { 236 | stringstream ss; 237 | ss << std::setfill ('0') << std::setw(4) << std::hex << reg; 238 | return "$U" + ss.str(); 239 | } 240 | 241 | 242 | bool GetInstructionInfo(const uint8_t* data, uint64_t addr, size_t maxLen, InstructionInfo& result) override { 243 | std::lock_guard guard(m_sleigh_mutex); 244 | 245 | m_loader.setData(addr, data, maxLen); 246 | Address pcode_addr(m_sleigh->getDefaultCodeSpace(), addr); 247 | 248 | try { 249 | 250 | PcodeEmitCacher pcode; 251 | result.length = m_sleigh->oneInstruction(pcode, pcode_addr); 252 | 253 | for (auto const &op : pcode.m_ops) { 254 | // TODO: Do we need to deal with different addr spaces? 255 | switch(op.opcode) { 256 | case CPUI_BRANCH: 257 | result.AddBranch(UnconditionalBranch, op.inputs[0].getAddr().getOffset()); 258 | break; 259 | case CPUI_CBRANCH: 260 | result.AddBranch(TrueBranch, op.inputs[0].getAddr().getOffset()); 261 | result.AddBranch(FalseBranch, addr + result.length); 262 | break; 263 | case CPUI_BRANCHIND: 264 | // TODO 265 | break; 266 | case CPUI_CALL: 267 | result.AddBranch(CallDestination, op.inputs[0].getAddr().getOffset()); 268 | break; 269 | case CPUI_CALLIND: 270 | // TODO 271 | break; 272 | case CPUI_CALLOTHER: 273 | // TODO 274 | break; 275 | case CPUI_RETURN: 276 | result.AddBranch(FunctionReturn); 277 | break; 278 | default: 279 | break; 280 | } 281 | } 282 | return true; 283 | } catch (...) { 284 | return false; 285 | } 286 | } 287 | 288 | bool GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len, std::vector& result) override { 289 | std::lock_guard guard(m_sleigh_mutex); 290 | 291 | m_loader.setData(addr, data, len); 292 | Address pcode_addr(m_sleigh->getDefaultCodeSpace(), addr); 293 | 294 | try { 295 | AssemblyEmitCacher assembly; 296 | 297 | // Update length of actually processed instruction 298 | len = m_sleigh->printAssembly(assembly, pcode_addr); 299 | 300 | result.push_back(InstructionTextToken(InstructionToken, assembly.m_mnem)); 301 | result.push_back(InstructionTextToken(TextToken, " " + assembly.m_body)); 302 | return true; 303 | } catch (...) { 304 | return false; 305 | } 306 | } 307 | 308 | ExprId ILReadVarNode(LowLevelILFunction& il, VarnodeData data) { 309 | spacetype typ = data.space->getType(); 310 | 311 | if (typ == IPTR_CONSTANT) { 312 | return il.Const(data.size, data.offset); 313 | } else if (typ == IPTR_PROCESSOR) { // Registers 314 | return il.Register(data.size, m_register_nums[data]); 315 | } else if (typ == IPTR_INTERNAL) { // Temporaries 316 | return il.LowPart(data.size, il.Flag(data.offset)); 317 | } else { 318 | LogWarn("read unknown space %d", typ); 319 | return il.Undefined(); 320 | } 321 | } 322 | 323 | ExprId ILWriteVarnode(LowLevelILFunction& il, VarnodeData dst, ExprId src) { 324 | spacetype typ = dst.space->getType(); 325 | 326 | if (typ == IPTR_PROCESSOR) { // Registers 327 | return il.SetRegister(dst.size, m_register_nums[dst], src); 328 | } else if (typ == IPTR_INTERNAL) { // Temporaries 329 | ExprId tmp = il.LowPart(dst.size, src); // Truncate to output size 330 | return il.SetFlag(dst.offset, tmp); 331 | } else { 332 | LogWarn("write unknown space %d", typ); 333 | return il.Undefined(); 334 | } 335 | } 336 | 337 | bool GetInstructionLowLevelIL(const uint8_t* data, uint64_t addr, size_t& len, LowLevelILFunction& il) override { 338 | std::lock_guard guard(m_sleigh_mutex); 339 | 340 | m_loader.setData(addr, data, len); 341 | Address pcode_addr(m_sleigh->getDefaultCodeSpace(), addr); 342 | 343 | try { 344 | 345 | PcodeEmitCacher pcode; 346 | AssemblyEmitCacher assembly; 347 | len = m_sleigh->oneInstruction(pcode, pcode_addr); 348 | 349 | for (auto const &op : pcode.m_ops) { 350 | if (op.opcode == CPUI_COPY) { // 1 351 | il.AddInstruction(ILWriteVarnode(il, *op.output, ILReadVarNode(il, op.inputs[0]))); 352 | } else if (op.opcode == CPUI_LOAD) { // 2 353 | // Output contains destination 354 | // Input 0 contains some information about the space, assume this is RAM for now 355 | // Input 1 is a temporary with an offset in the space 356 | ExprId offset = ILReadVarNode(il, op.inputs[1]); 357 | ExprId val = il.Load(op.output->size, offset); 358 | il.AddInstruction(ILWriteVarnode(il, *op.output, val)); 359 | } else if (op.opcode == CPUI_STORE) { // 3 360 | // Input 0 contains some information about the space, assume this is RAM for now 361 | // Input 1 is a temporary with an offset in the space 362 | // Input 2 is the value to store 363 | ExprId val = ILReadVarNode(il, op.inputs[2]); 364 | ExprId offset = ILReadVarNode(il, op.inputs[1]); 365 | il.AddInstruction(il.Store(op.inputs[1].size, offset, val)); 366 | } else if (op.opcode == CPUI_BRANCH) { // 4 367 | //https://github.com/Vector35/arch-mips/blob/staging/il.cpp#L144 368 | uint64_t target = op.inputs[0].getAddr().getOffset(); 369 | 370 | BNLowLevelILLabel* label = il.GetLabelForAddress(this, target); 371 | if (label) { 372 | il.AddInstruction(il.Goto(*label)); 373 | } else { 374 | il.AddInstruction(il.Jump(il.ConstPointer(m_addr_size, target))); 375 | } 376 | } else if (op.opcode == CPUI_CBRANCH) { // 5 377 | //https://github.com/Vector35/arch-mips/blob/staging/il.cpp#L154 378 | ExprId cond = ILReadVarNode(il, op.inputs[1]); 379 | 380 | uint64_t target_true = op.inputs[0].getAddr().getOffset(); 381 | uint64_t target_false = addr + len; 382 | 383 | BNLowLevelILLabel* label_true = il.GetLabelForAddress(this, target_true); 384 | BNLowLevelILLabel* label_false = il.GetLabelForAddress(this, target_false); 385 | 386 | // Jump to label if it exists, otherwise create it 387 | LowLevelILLabel code_true, code_false; 388 | if (label_true && label_false) { 389 | il.AddInstruction(il.If(cond, *label_true, *label_false)); 390 | } else if (label_true) { 391 | il.AddInstruction(il.If(cond, *label_true, code_false)); 392 | il.MarkLabel(code_false); 393 | il.AddInstruction(il.Jump(il.ConstPointer(m_addr_size, target_false))); 394 | } else if (label_false) { 395 | il.AddInstruction(il.If(cond, code_true, *label_false)); 396 | il.MarkLabel(code_true); 397 | il.AddInstruction(il.Jump(il.ConstPointer(m_addr_size, target_true))); 398 | } else { 399 | il.AddInstruction(il.If(cond, code_true, code_false)); 400 | il.MarkLabel(code_true); 401 | il.AddInstruction(il.Jump(il.ConstPointer(m_addr_size, target_true))); 402 | il.MarkLabel(code_false); 403 | il.AddInstruction(il.Jump(il.ConstPointer(m_addr_size, target_false))); 404 | } 405 | } else if (op.opcode == CPUI_CALL){ // 7 406 | uint64_t target = op.inputs[0].getAddr().getOffset(); 407 | il.AddInstruction(il.Call(il.ConstPointer(m_addr_size, target))); 408 | } else if (op.opcode == CPUI_CALLIND){ // 8 409 | ExprId target = ILReadVarNode(il, op.inputs[0]); 410 | il.AddInstruction(il.Call(target)); 411 | } else if (op.opcode == CPUI_CALLOTHER){ // 10 412 | il.AddInstruction(il.Intrinsic({}, op.inputs[0].offset, {})); 413 | } else if (op.opcode == CPUI_RETURN){ // 10 414 | il.AddInstruction(il.Return(ILReadVarNode(il, op.inputs[0]))); 415 | } else if (op.opcode == CPUI_INT_EQUAL){ // 11 416 | ExprId a = ILReadVarNode(il, op.inputs[0]); 417 | ExprId b = ILReadVarNode(il, op.inputs[1]); 418 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareEqual(op.output->size, a, b))); 419 | } else if (op.opcode == CPUI_INT_NOTEQUAL){ // 12 420 | ExprId a = ILReadVarNode(il, op.inputs[0]); 421 | ExprId b = ILReadVarNode(il, op.inputs[1]); 422 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareNotEqual(op.output->size, a, b))); 423 | } else if (op.opcode == CPUI_INT_SLESS){ // 13 424 | ExprId a = ILReadVarNode(il, op.inputs[0]); 425 | ExprId b = ILReadVarNode(il, op.inputs[1]); 426 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareSignedLessThan(op.output->size, a, b))); 427 | } else if (op.opcode == CPUI_INT_SLESSEQUAL){ // 14 428 | ExprId a = ILReadVarNode(il, op.inputs[0]); 429 | ExprId b = ILReadVarNode(il, op.inputs[1]); 430 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareSignedLessEqual(op.output->size, a, b))); 431 | } else if (op.opcode == CPUI_INT_LESS){ // 15 432 | ExprId a = ILReadVarNode(il, op.inputs[0]); 433 | ExprId b = ILReadVarNode(il, op.inputs[1]); 434 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareUnsignedLessThan(op.output->size, a, b))); 435 | } else if (op.opcode == CPUI_INT_LESSEQUAL){ // 16 436 | ExprId a = ILReadVarNode(il, op.inputs[0]); 437 | ExprId b = ILReadVarNode(il, op.inputs[1]); 438 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.CompareUnsignedLessEqual(op.output->size, a, b))); 439 | } else if (op.opcode == CPUI_INT_ZEXT){ // 17 440 | ExprId a = ILReadVarNode(il, op.inputs[0]); 441 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.ZeroExtend(op.output->size, a))); 442 | } else if (op.opcode == CPUI_INT_SEXT){ // 18 443 | ExprId a = ILReadVarNode(il, op.inputs[0]); 444 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.SignExtend(op.output->size, a))); 445 | } else if (op.opcode == CPUI_INT_ADD){ // 19 446 | ExprId a = ILReadVarNode(il, op.inputs[0]); 447 | ExprId b = ILReadVarNode(il, op.inputs[1]); 448 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Add(op.output->size, a, b))); 449 | } else if (op.opcode == CPUI_INT_SUB){ // 20 450 | ExprId a = ILReadVarNode(il, op.inputs[0]); 451 | ExprId b = ILReadVarNode(il, op.inputs[1]); 452 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Sub(op.output->size, a, b))); 453 | } else if (op.opcode == CPUI_INT_CARRY){ // 21 454 | ExprId a = ILReadVarNode(il, op.inputs[0]); 455 | ExprId b = ILReadVarNode(il, op.inputs[1]); 456 | ExprId res = il.Add(op.inputs[0].size + 1, a, b); 457 | ExprId carry = il.And(op.inputs[0].size, il.LogicalShiftRight(op.inputs[0].size, res, il.Const(4, op.inputs[0].size * 8)), il.Const(4, 1)); 458 | il.AddInstruction(ILWriteVarnode(il, *op.output, carry)); 459 | } else if (op.opcode == CPUI_INT_SCARRY){ // 22 460 | // Does this work? 461 | ExprId a = ILReadVarNode(il, op.inputs[0]); 462 | ExprId b = ILReadVarNode(il, op.inputs[1]); 463 | il.AddInstruction(il.AddCarry(op.inputs[0].size, a, b, il.Flag(op.output->offset), 1)); 464 | } else if (op.opcode == CPUI_INT_NEGATE){ // 25 465 | ExprId a = ILReadVarNode(il, op.inputs[0]); 466 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Neg(op.output->size, a))); 467 | } else if (op.opcode == CPUI_INT_XOR){ // 26 468 | ExprId a = ILReadVarNode(il, op.inputs[0]); 469 | ExprId b = ILReadVarNode(il, op.inputs[1]); 470 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Xor(op.output->size, a, b))); 471 | } else if (op.opcode == CPUI_INT_AND){ // 27 472 | ExprId a = ILReadVarNode(il, op.inputs[0]); 473 | ExprId b = ILReadVarNode(il, op.inputs[1]); 474 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.And(op.output->size, a, b))); 475 | } else if (op.opcode == CPUI_INT_OR){ // 28 476 | ExprId a = ILReadVarNode(il, op.inputs[0]); 477 | ExprId b = ILReadVarNode(il, op.inputs[1]); 478 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Or(op.output->size, a, b))); 479 | } else if (op.opcode == CPUI_INT_LEFT){ // 29 480 | ExprId a = ILReadVarNode(il, op.inputs[0]); 481 | ExprId b = ILReadVarNode(il, op.inputs[1]); 482 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.ShiftLeft(op.output->size, a, b))); 483 | } else if (op.opcode == CPUI_INT_RIGHT){ // 30 484 | ExprId a = ILReadVarNode(il, op.inputs[0]); 485 | ExprId b = ILReadVarNode(il, op.inputs[1]); 486 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.LogicalShiftRight(op.output->size, a, b))); 487 | } else if (op.opcode == CPUI_INT_SRIGHT){ // 31 488 | ExprId a = ILReadVarNode(il, op.inputs[0]); 489 | ExprId b = ILReadVarNode(il, op.inputs[1]); 490 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.ArithShiftRight(op.output->size, a, b))); 491 | } else if (op.opcode == CPUI_INT_MULT){ // 32 492 | ExprId a = ILReadVarNode(il, op.inputs[0]); 493 | ExprId b = ILReadVarNode(il, op.inputs[1]); 494 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.Mult(op.output->size, a, b))); 495 | } else if (op.opcode == CPUI_INT_DIV){ // 33 496 | ExprId a = ILReadVarNode(il, op.inputs[0]); 497 | ExprId b = ILReadVarNode(il, op.inputs[1]); 498 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.DivUnsigned(op.output->size, a, b))); 499 | } else if (op.opcode == CPUI_INT_SDIV){ // 34 500 | ExprId a = ILReadVarNode(il, op.inputs[0]); 501 | ExprId b = ILReadVarNode(il, op.inputs[1]); 502 | il.AddInstruction(ILWriteVarnode(il, *op.output, il.DivSigned(op.output->size, a, b))); 503 | } else if (op.opcode == CPUI_BOOL_NEGATE){ // 37 504 | ExprId a = ILReadVarNode(il, op.inputs[0]); 505 | ExprId out = il.CompareEqual(op.output->size, a, il.Const(op.output->size, 0)); 506 | il.AddInstruction(ILWriteVarnode(il, *op.output, out)); 507 | } else if (op.opcode == CPUI_BOOL_XOR){ // 38 508 | ExprId a = ILReadVarNode(il, op.inputs[0]); 509 | ExprId b = ILReadVarNode(il, op.inputs[1]); 510 | ExprId out = il.BoolToInt(op.output->size, il.Xor(op.output->size, a, b)); 511 | il.AddInstruction(ILWriteVarnode(il, *op.output, out)); 512 | } else if (op.opcode == CPUI_BOOL_AND){ // 39 513 | ExprId a = ILReadVarNode(il, op.inputs[0]); 514 | ExprId b = ILReadVarNode(il, op.inputs[1]); 515 | ExprId out = il.BoolToInt(op.output->size, il.And(op.output->size, a, b)); 516 | il.AddInstruction(ILWriteVarnode(il, *op.output, out)); 517 | } else if (op.opcode == CPUI_BOOL_OR){ // 40 518 | ExprId a = ILReadVarNode(il, op.inputs[0]); 519 | ExprId b = ILReadVarNode(il, op.inputs[1]); 520 | ExprId out = il.BoolToInt(op.output->size, il.Or(op.output->size, a, b)); 521 | il.AddInstruction(ILWriteVarnode(il, *op.output, out)); 522 | } else if (op.opcode == CPUI_SUBPIECE){ // 63 523 | ExprId a = il.LowPart(op.inputs[1].size, ILReadVarNode(il, op.inputs[0])); 524 | il.AddInstruction(ILWriteVarnode(il, *op.output, a)); 525 | } else { 526 | stringstream ss; 527 | ss << format("[0x%lx] unknown opcode %d ", addr, op.opcode); 528 | if (op.output) { 529 | ss << format("output - space: %d - size: %d - offset: 0x%lx, ", op.output->space->getType(), op.output->size, op.output->offset); 530 | } 531 | for (int i = 0; i < op.inputs.size(); i++) { 532 | ss << format("input[%d] - space: %d - size: %d - offset: 0x%lx, ", i, op.inputs[i].space->getType(), op.inputs[i].size, op.inputs[i].offset); 533 | } 534 | LogInfo("%s", ss.str().c_str()); 535 | } 536 | } 537 | 538 | return true; 539 | } catch (...) { 540 | return false; 541 | } 542 | } 543 | 544 | 545 | 546 | }; 547 | 548 | 549 | extern "C" 550 | { 551 | BN_DECLARE_CORE_ABI_VERSION 552 | 553 | 554 | BINARYNINJAPLUGIN bool CorePluginInit() { 555 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 556 | 557 | if (!env.contains("LOAD_SLA")) { 558 | QDirIterator it(":/out/sla", QDirIterator::Subdirectories); 559 | stringstream ss; 560 | while (it.hasNext()) { 561 | it.next(); 562 | ss << it.fileInfo().baseName().toStdString() << " "; 563 | } 564 | qWarning().noquote() << "LOAD_SLA environment variable not set. Available values: all" << QString::fromStdString(ss.str()); 565 | } 566 | 567 | QString to_load = env.value("LOAD_SLA", "all"); 568 | 569 | QDirIterator it(":/out/sla", QDirIterator::Subdirectories); 570 | while (it.hasNext()) { 571 | it.next(); 572 | QFileInfo f = it.fileInfo(); 573 | if (f.baseName() != to_load && to_load != "all") { 574 | continue; 575 | } 576 | 577 | Architecture* arch = new PcodeArchitecture(f); 578 | Architecture::Register(arch); 579 | } 580 | 581 | return true; 582 | } 583 | } 584 | --------------------------------------------------------------------------------