├── .clang-format ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── arch_ebpf.cpp ├── disassembler.cpp ├── disassembler.h ├── il.cpp ├── il.h ├── opcodes.h ├── syscalls.cpp ├── syscalls.h └── util.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | CMakeFiles 3 | CMakeCache.txt 4 | Makefile 5 | cmake_install.cmake 6 | build.ninja 7 | *.o 8 | *.so 9 | *.so.* 10 | *.dylib 11 | *.dll 12 | *.exp 13 | *.ilk 14 | *.lib 15 | *.pdb 16 | *.obj 17 | .qt 18 | binaryninja-api 19 | *.bndb 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "capstone"] 2 | path = capstone 3 | url = https://github.com/terorie/capstone.git 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.3.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: mixed-line-ending 8 | - repo: https://github.com/pre-commit/mirrors-clang-format 9 | rev: v14.0.6 10 | hooks: 11 | - id: clang-format 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13 FATAL_ERROR) 2 | 3 | project(arch_ebpf) 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 | if(NOT BN_INTERNAL_BUILD) 12 | add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api) 13 | endif() 14 | 15 | file(GLOB SOURCES 16 | arch_ebpf.cpp 17 | disassembler.cpp 18 | il.cpp 19 | syscalls.cpp 20 | *.h 21 | ) 22 | 23 | if(DEMO) 24 | add_library(arch_ebpf STATIC ${SOURCES}) 25 | else() 26 | add_library(arch_ebpf SHARED ${SOURCES}) 27 | endif() 28 | 29 | set(CAPSTONE_ARCHITECTURE_DEFAULT OFF CACHE INTERNAL "disable arch by default" FORCE) 30 | set(CAPSTONE_BPF_SUPPORT ON CACHE INTERNAL "enable BPF only" FORCE) 31 | set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "" FORCE) 32 | add_subdirectory(${PROJECT_SOURCE_DIR}/capstone) 33 | set_target_properties(capstone PROPERTIES 34 | POSITION_INDEPENDENT_CODE ON) 35 | 36 | target_link_libraries(arch_ebpf binaryninjaapi capstone) 37 | 38 | set_target_properties(arch_ebpf PROPERTIES 39 | CXX_STANDARD 17 40 | CXX_VISIBILITY_PRESET hidden 41 | CXX_STANDARD_REQUIRED ON 42 | VISIBILITY_INLINES_HIDDEN ON 43 | POSITION_INDEPENDENT_CODE ON) 44 | 45 | if(BN_INTERNAL_BUILD) 46 | plugin_rpath(arch_ebpf) 47 | set_target_properties(arch_ebpf PROPERTIES 48 | LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} 49 | RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) 50 | endif() 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Binary Ninja eBPF & Solana support 2 | 3 | Author: **[terorie](https://github.com/terorie)** 4 | 5 | This BN plugin implements a Capstone-powered disassembler and lifter for the Solana bytecode format. 6 | 7 | Kernel eBPF support is WIP. 8 | 9 | ## Dependencies 10 | 11 | ```shell 12 | # For Capstone v5 13 | git submodule update --init 14 | 15 | # Binary Ninja SDK 16 | git clone https://github.com/Vector35/binaryninja-api --depth=1 17 | 18 | # Qt6 (macOS) 19 | brew install qt6 20 | # Qt6 (Debian) 21 | apt install qt6-base-dev libgl1-mesa-dev 22 | 23 | # An installation of Binary Ninja 24 | # Use latest development build, stable is always broken 25 | # 26 | # macOS: /Applications/Binary Ninja.app 27 | # Linux: ~/binaryninja 28 | ``` 29 | 30 | ## Building 31 | 32 | ```shell 33 | mkdir build 34 | 35 | # protip: use -DHEADLESS=1 if you don't want to get Qt6 36 | cmake -B build . -G Ninja -DBN_API_PATH=./binaryninja-api 37 | 38 | cmake --build build 39 | ``` 40 | 41 | To install, copy or symlink `build/libarch_ebpf.so` into `~/.binaryninja/plugins`. 42 | 43 | When starting BN, the log should display: `[Core] Loaded native plugin arch_ebpf`. 44 | -------------------------------------------------------------------------------- /arch_ebpf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | using namespace BinaryNinja; 6 | 7 | #include 8 | #include 9 | 10 | #include "disassembler.h" 11 | #include "il.h" 12 | #include "opcodes.h" 13 | #include "syscalls.h" 14 | 15 | enum ElfBpfRelocationType { 16 | R_BPF_NONE = 0, 17 | R_BPF_64_64 = 1, 18 | R_BPF_64_ABS64 = 2, 19 | R_BPF_64_ABS32 = 3, 20 | R_BPF_64_NODYLD32 = 4, 21 | R_BPF_64_RELATIVE = 8, 22 | R_BPF_64_32 = 10, 23 | }; 24 | 25 | static const char* 26 | GetRelocationString(ElfBpfRelocationType relocType) 27 | { 28 | static std::map relocTable = { 29 | { R_BPF_NONE, "R_BPF_NONE" }, 30 | { R_BPF_64_64, "R_BPF_64_64" }, 31 | { R_BPF_64_ABS64, "R_BPF_64_ABS64" }, 32 | { R_BPF_64_ABS32, "R_BPF_64_ABS32" }, 33 | { R_BPF_64_NODYLD32, "R_BPF_64_NODYLD32" }, 34 | { R_BPF_64_RELATIVE, "R_BPF_64_RELATIVE" }, 35 | { R_BPF_64_32, "R_BPF_64_32" }, 36 | }; 37 | if (relocTable.count(relocType)) 38 | return relocTable.at(relocType); 39 | return "Unknown eBPF relocation"; 40 | } 41 | 42 | static void WriteNop(uint8_t* data) 43 | { 44 | data[0] = BPF_OPC_XOR64_REG; 45 | memset(data + 1, 0, 7); 46 | } 47 | 48 | static bool IsBranch(const uint8_t* data) 49 | { 50 | return (data[0] & 0x7) == 0x5; 51 | } 52 | 53 | static bool IsLongIns(const uint8_t* data) 54 | { 55 | return data[0] == BPF_OPC_LDDW; 56 | } 57 | 58 | class EBPFArchitecture : public Architecture { 59 | private: 60 | BNEndianness endian; 61 | 62 | BNRegisterInfo RegisterInfo(uint32_t fullWidthReg, size_t offset, size_t size) 63 | { 64 | BNRegisterInfo result; 65 | result.fullWidthRegister = fullWidthReg; 66 | result.offset = offset; 67 | result.size = size; 68 | result.extend = NoExtend; 69 | return result; 70 | } 71 | 72 | public: 73 | EBPFArchitecture(const char* name, BNEndianness endian_) 74 | : Architecture(name) 75 | { 76 | endian = endian_; 77 | } 78 | 79 | virtual BNEndianness GetEndianness() const override { return endian; } 80 | 81 | virtual size_t GetAddressSize() const override { return 8; } 82 | 83 | virtual size_t GetDefaultIntegerSize() const override { return 8; } 84 | 85 | virtual size_t GetInstructionAlignment() const override { return 8; } 86 | 87 | virtual size_t GetMaxInstructionLength() const override { return 16; } 88 | 89 | virtual bool GetInstructionInfo(const uint8_t* data, 90 | uint64_t addr, 91 | size_t maxLen, 92 | InstructionInfo& result) override 93 | { 94 | struct decomp_result res; 95 | struct cs_insn* insn = &(res.insn); 96 | 97 | if (maxLen < 8) { 98 | return false; 99 | } 100 | if (!ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) { 101 | goto beach; 102 | } 103 | 104 | result.length = 8; 105 | switch (insn->id) { 106 | case BPF_INS_JMP: 107 | result.AddBranch(UnconditionalBranch, JumpDest(data, addr, endian == LittleEndian)); 108 | break; 109 | case BPF_INS_JEQ: 110 | case BPF_INS_JGT: 111 | case BPF_INS_JGE: 112 | case BPF_INS_JSET: 113 | case BPF_INS_JNE: 114 | case BPF_INS_JSGT: 115 | case BPF_INS_JSGE: 116 | case BPF_INS_JLT: 117 | case BPF_INS_JLE: 118 | case BPF_INS_JSLT: 119 | case BPF_INS_JSLE: 120 | result.AddBranch(TrueBranch, JumpDest(data, addr, endian == LittleEndian)); 121 | result.AddBranch(FalseBranch, addr + 8); 122 | break; 123 | case BPF_INS_CALL: 124 | result.AddBranch(CallDestination, CallDest(data, addr, endian == LittleEndian)); 125 | break; 126 | case BPF_INS_SYSCALL: 127 | result.AddBranch(SystemCall); 128 | break; 129 | case BPF_INS_CALLX: 130 | result.AddBranch(CallDestination); 131 | break; 132 | case BPF_INS_EXIT: 133 | result.AddBranch(FunctionReturn); 134 | break; 135 | case BPF_INS_LDDW: 136 | result.length = 16; 137 | break; 138 | } 139 | 140 | beach: 141 | return true; 142 | } 143 | 144 | virtual bool GetInstructionText(const uint8_t* data, 145 | uint64_t addr, 146 | size_t& len, 147 | std::vector& result) override 148 | { 149 | bool rc = false; 150 | struct decomp_result res; 151 | struct cs_insn* insn = &(res.insn); 152 | struct cs_detail* detail = &(res.detail); 153 | struct cs_bpf* bpf = &(detail->bpf); 154 | size_t strlenMnem; 155 | 156 | char buf[256]; 157 | #define FMT_I64(x) \ 158 | do { \ 159 | if ((x) >= 0) \ 160 | std::snprintf(buf, sizeof(buf), "+%#lx", (x)); \ 161 | else \ 162 | std::snprintf(buf, sizeof(buf), "-%#lx", -(x)); \ 163 | } while (0) 164 | 165 | if (len < 8) { 166 | goto beach; 167 | } 168 | if (!ebpf_decompose(data, 16, addr, endian == LittleEndian, &res)) { 169 | goto beach; 170 | } 171 | 172 | /* mnemonic */ 173 | result.emplace_back(InstructionToken, insn->mnemonic); 174 | 175 | /* padding between mnemonic and operands */ 176 | result.emplace_back(TextToken, std::string(10 - strlen(insn->mnemonic), ' ')); 177 | 178 | // special instructions 179 | switch (insn->id) { 180 | case BPF_INS_CALL: { 181 | int64_t off = (int32_t)bpf->operands[0].imm; 182 | off = off * 8 + 8; 183 | FMT_I64(off); 184 | result.emplace_back(PossibleAddressToken, buf, bpf->operands[0].imm, 8); 185 | len = 8; 186 | return true; 187 | } 188 | case BPF_INS_SYSCALL: { 189 | uint32_t id = bpf->operands[0].imm; 190 | if (SbfSyscalls.find(id) != SbfSyscalls.end()) { 191 | const char* name = SbfSyscalls[id]; 192 | result.emplace_back(TextToken, name); 193 | } else { 194 | std::snprintf(buf, sizeof(buf), "%#08x", id); 195 | result.emplace_back(TextToken, buf); 196 | } 197 | return true; 198 | } 199 | } 200 | 201 | /* operands */ 202 | for (int i = 0; i < bpf->op_count; ++i) { 203 | struct cs_bpf_op* op = &(bpf->operands[i]); 204 | int64_t val; 205 | 206 | switch (op->type) { 207 | case BPF_OP_REG: 208 | result.emplace_back(RegisterToken, GetRegisterName(op->reg)); 209 | break; 210 | case BPF_OP_IMM: 211 | val = (int32_t)op->imm; 212 | FMT_I64(val); 213 | result.emplace_back(IntegerToken, buf, op->imm, 8); 214 | break; 215 | case BPF_OP_OFF: 216 | val = Int16SignExtend(op->off); 217 | FMT_I64(val); 218 | result.emplace_back(CodeRelativeAddressToken, buf, val, 2); 219 | break; 220 | case BPF_OP_MEM: 221 | result.emplace_back(TextToken, "["); 222 | 223 | result.emplace_back(RegisterToken, GetRegisterName(op->mem.base)); 224 | val = Int16SignExtend(op->mem.disp) * 8 + 8; 225 | FMT_I64(val); 226 | result.emplace_back(IntegerToken, buf, val, 2); 227 | 228 | result.emplace_back(TextToken, "]"); 229 | break; 230 | default: 231 | std::sprintf(buf, "unknown (%d)", op->type); 232 | result.emplace_back(TextToken, buf); 233 | break; 234 | } 235 | 236 | if (i < bpf->op_count - 1) { 237 | result.emplace_back(OperandSeparatorToken, ", "); 238 | } 239 | } 240 | 241 | rc = true; 242 | if (IsLongIns(data)) { 243 | len = 16; 244 | } else { 245 | len = 8; 246 | } 247 | beach: 248 | return rc; 249 | } 250 | 251 | virtual bool GetInstructionLowLevelIL(const uint8_t* data, 252 | uint64_t addr, 253 | size_t& len, 254 | LowLevelILFunction& il) override 255 | { 256 | bool rc = false; 257 | struct decomp_result res; 258 | 259 | if (len < 8) { 260 | goto beach; 261 | } 262 | if (!ebpf_decompose(data, len, addr, endian == LittleEndian, &res)) { 263 | il.AddInstruction(il.Undefined()); 264 | goto beach; 265 | } 266 | 267 | rc = GetLowLevelILForBPFInstruction(this, il, data, addr, &res, endian == LittleEndian); 268 | if (IsLongIns(data)) { 269 | len = 16; 270 | } else { 271 | len = 8; 272 | } 273 | 274 | beach: 275 | return rc; 276 | } 277 | 278 | virtual size_t GetFlagWriteLowLevelIL(BNLowLevelILOperation op, size_t size, uint32_t flagWriteType, 279 | uint32_t flag, BNRegisterOrConstant* operands, size_t operandCount, LowLevelILFunction& il) override 280 | { 281 | return 0; 282 | } 283 | 284 | virtual ExprId GetSemanticFlagGroupLowLevelIL(uint32_t semGroup, LowLevelILFunction& il) override 285 | { 286 | return il.Unimplemented(); 287 | } 288 | 289 | virtual std::string GetRegisterName(uint32_t regId) override 290 | { 291 | const char* result = ebpf_reg_to_str(regId); 292 | if (result == NULL) { 293 | result = "unknown"; 294 | } 295 | return result; 296 | } 297 | 298 | virtual std::vector GetAllFlags() override 299 | { 300 | return {}; 301 | } 302 | 303 | virtual std::string GetFlagName(uint32_t flag) override 304 | { 305 | return "ERR_FLAG_NAME"; 306 | } 307 | 308 | virtual std::vector GetAllFlagWriteTypes() override 309 | { 310 | return {}; 311 | } 312 | 313 | virtual std::string GetFlagWriteTypeName(uint32_t writeType) override 314 | { 315 | return "invalid"; 316 | } 317 | 318 | virtual std::vector GetFlagsWrittenByFlagWriteType(uint32_t writeType) override 319 | { 320 | return {}; 321 | } 322 | 323 | virtual uint32_t GetSemanticClassForFlagWriteType(uint32_t writeType) override 324 | { 325 | return IL_FLAGCLASS_NONE; 326 | } 327 | 328 | virtual std::vector GetAllSemanticFlagClasses() override 329 | { 330 | return {}; 331 | } 332 | 333 | virtual std::string GetSemanticFlagClassName(uint32_t semClass) override 334 | { 335 | return ""; 336 | } 337 | 338 | virtual std::vector GetAllSemanticFlagGroups() override 339 | { 340 | return {}; 341 | } 342 | 343 | virtual std::string GetSemanticFlagGroupName(uint32_t semGroup) override 344 | { 345 | return ""; 346 | } 347 | 348 | virtual std::vector GetFlagsRequiredForSemanticFlagGroup(uint32_t semGroup) override 349 | { 350 | return {}; 351 | } 352 | 353 | virtual std::map GetFlagConditionsForSemanticFlagGroup(uint32_t semGroup) override 354 | { 355 | return {}; 356 | } 357 | 358 | virtual BNFlagRole GetFlagRole(uint32_t flag, uint32_t semClass) override 359 | { 360 | return ZeroFlagRole; 361 | } 362 | 363 | virtual std::vector GetFlagsRequiredForFlagCondition(BNLowLevelILFlagCondition cond, uint32_t) override 364 | { 365 | return {}; 366 | } 367 | 368 | virtual std::vector GetFullWidthRegisters() override 369 | { 370 | return { 371 | BPF_REG_R0, 372 | BPF_REG_R1, 373 | BPF_REG_R2, 374 | BPF_REG_R3, 375 | BPF_REG_R4, 376 | BPF_REG_R5, 377 | BPF_REG_R6, 378 | BPF_REG_R7, 379 | BPF_REG_R8, 380 | BPF_REG_R9, 381 | BPF_REG_R10, 382 | BPF_REG_R11 383 | }; 384 | } 385 | 386 | virtual std::vector GetAllRegisters() override 387 | { 388 | return { 389 | BPF_REG_R0, 390 | BPF_REG_R1, 391 | BPF_REG_R2, 392 | BPF_REG_R3, 393 | BPF_REG_R4, 394 | BPF_REG_R5, 395 | BPF_REG_R6, 396 | BPF_REG_R7, 397 | BPF_REG_R8, 398 | BPF_REG_R9, 399 | BPF_REG_R10, 400 | BPF_REG_R11 401 | }; 402 | } 403 | 404 | virtual std::vector GetGlobalRegisters() override 405 | { 406 | return {}; 407 | } 408 | 409 | virtual BNRegisterInfo GetRegisterInfo(uint32_t regId) override 410 | { 411 | switch (regId) { 412 | case BPF_REG_R0: 413 | case BPF_REG_R1: 414 | case BPF_REG_R2: 415 | case BPF_REG_R3: 416 | case BPF_REG_R4: 417 | case BPF_REG_R5: 418 | case BPF_REG_R6: 419 | case BPF_REG_R7: 420 | case BPF_REG_R8: 421 | case BPF_REG_R9: 422 | case BPF_REG_R10: 423 | case BPF_REG_R11: 424 | return RegisterInfo(regId, 0, 8); 425 | default: 426 | return RegisterInfo(0, 0, 0); 427 | } 428 | } 429 | 430 | virtual uint32_t GetStackPointerRegister() override 431 | { 432 | return BPF_REG_R11; 433 | } 434 | 435 | virtual uint32_t GetLinkRegister() override 436 | { 437 | return BPF_REG_R10; 438 | } 439 | 440 | /*************************************************************************/ 441 | 442 | virtual bool CanAssemble() override 443 | { 444 | return false; 445 | } 446 | 447 | virtual bool Assemble(const std::string& code, uint64_t addr, DataBuffer& result, std::string& errors) override 448 | { 449 | return false; 450 | } 451 | 452 | /*************************************************************************/ 453 | 454 | virtual bool IsNeverBranchPatchAvailable(const uint8_t* data, 455 | uint64_t addr, 456 | size_t len) override 457 | { 458 | return false; 459 | } 460 | 461 | virtual bool IsAlwaysBranchPatchAvailable(const uint8_t* data, 462 | uint64_t addr, 463 | size_t len) override 464 | { 465 | if (len < 8) { 466 | return false; 467 | } 468 | return IsBranch(data); 469 | } 470 | 471 | virtual bool IsInvertBranchPatchAvailable(const uint8_t* data, 472 | uint64_t addr, 473 | size_t len) override 474 | { 475 | if (len < 8) { 476 | return false; 477 | } 478 | return IsBranch(data) && data[0] != BPF_OPC_JSET_IMM && data[0] != BPF_OPC_JSET_REG; 479 | } 480 | 481 | virtual bool IsSkipAndReturnZeroPatchAvailable(const uint8_t* data, 482 | uint64_t addr, 483 | size_t len) override 484 | { 485 | return false; 486 | } 487 | 488 | virtual bool IsSkipAndReturnValuePatchAvailable(const uint8_t* data, 489 | uint64_t addr, 490 | size_t len) override 491 | { 492 | return false; 493 | } 494 | 495 | /*************************************************************************/ 496 | 497 | virtual bool ConvertToNop(uint8_t* data, uint64_t, size_t len) override 498 | { 499 | if (len < 8) { 500 | return false; 501 | } 502 | if (IsLongIns(data) && len >= 16) { 503 | WriteNop(data + 8); 504 | } 505 | WriteNop(data); 506 | return true; 507 | } 508 | 509 | virtual bool AlwaysBranch(uint8_t* data, uint64_t addr, size_t len) override 510 | { 511 | if (len < 8 || !IsBranch(data)) { 512 | return false; 513 | } 514 | data[0] = BPF_OPC_JA; 515 | data[1] = 0; 516 | return true; 517 | } 518 | 519 | virtual bool InvertBranch(uint8_t* data, uint64_t addr, size_t len) override 520 | { 521 | if (len < 8 || !IsBranch(data)) { 522 | return false; 523 | } 524 | uint8_t new_opc = data[0] & 0x0F; 525 | switch (data[0] >> 4) { 526 | case 0x0: // JA 527 | WriteNop(data); 528 | break; 529 | case 0x1: // JEQ 530 | case 0x5: // JNE 531 | new_opc |= (new_opc ^ 0x40); 532 | break; 533 | case 0x2: // JGT 534 | new_opc |= 0xb0; // JLE 535 | break; 536 | case 0x3: // JGE 537 | new_opc |= 0xa0; // JLT 538 | break; 539 | case 0x6: // JSGT 540 | new_opc |= 0xd0; // JSLE 541 | break; 542 | case 0x7: // JSGE 543 | new_opc |= 0xc0; // JLT 544 | break; 545 | case 0xa: // JLT 546 | new_opc |= 0x70; // JSGE 547 | break; 548 | case 0xb: // JLE 549 | new_opc |= 0x20; // JGT 550 | break; 551 | case 0xc: // JSLT 552 | new_opc |= 0x70; // JSGE 553 | break; 554 | case 0xd: // JSLE 555 | new_opc |= 0x60; // JSGT 556 | break; 557 | default: 558 | // JSET cannot be inverted 559 | return false; 560 | } 561 | return true; 562 | } 563 | 564 | virtual bool SkipAndReturnValue(uint8_t* data, 565 | uint64_t addr, 566 | size_t len, 567 | uint64_t value) override 568 | { 569 | return false; 570 | } 571 | }; 572 | 573 | class SolanaCallingConvention : public CallingConvention { 574 | public: 575 | SolanaCallingConvention(Architecture* arch) 576 | : CallingConvention(arch, "solana") 577 | { 578 | } 579 | 580 | virtual std::vector GetIntegerArgumentRegisters() override 581 | { 582 | return { 583 | BPF_REG_R1, 584 | BPF_REG_R2, 585 | BPF_REG_R3, 586 | BPF_REG_R4, 587 | BPF_REG_R5, 588 | }; 589 | } 590 | 591 | virtual std::vector GetCallerSavedRegisters() override 592 | { 593 | return { 594 | BPF_REG_R10, 595 | }; 596 | } 597 | 598 | virtual std::vector GetCalleeSavedRegisters() override 599 | { 600 | return { 601 | BPF_REG_R6, 602 | BPF_REG_R7, 603 | BPF_REG_R8, 604 | BPF_REG_R9, 605 | }; 606 | } 607 | 608 | virtual uint32_t GetIntegerReturnValueRegister() override 609 | { 610 | return BPF_REG_R0; 611 | } 612 | }; 613 | 614 | static void HijackAsSyscall(uint8_t* dest, uint32_t id, bool le) 615 | { 616 | uint64_t ins = 0x0000000000000085 | (((uint64_t)id) << 32); 617 | if (!le) { 618 | ins = bswap64(ins); 619 | } 620 | (*(uint64_t*)dest) = ins; 621 | } 622 | 623 | class EbpfElfRelocationHandler : public RelocationHandler { 624 | public: 625 | virtual bool ApplyRelocation(Ref view, Ref arch, Ref reloc, uint8_t* dest, size_t len) override 626 | { 627 | auto sym = reloc->GetSymbol(); 628 | std::string symName; 629 | if (sym) { 630 | symName = sym->GetFullName(); 631 | } 632 | 633 | bool le = arch->GetEndianness() == LittleEndian; 634 | auto info = reloc->GetInfo(); 635 | uint64_t* dest64 = (uint64_t*)dest; 636 | uint32_t* dest32 = (uint32_t*)dest; 637 | uint16_t* dest16 = (uint16_t*)dest; 638 | auto swap64 = [&arch, le](uint32_t x) { return le ? x : bswap64(x); }; 639 | auto swap32 = [&arch, le](uint32_t x) { return le ? x : bswap32(x); }; 640 | auto swap16 = [&arch, le](uint16_t x) { return le ? x : bswap16(x); }; 641 | uint64_t target = reloc->GetTarget(); 642 | std::vector> sections; 643 | uint64_t rela_src; 644 | switch (info.nativeType) { 645 | case R_BPF_64_64: 646 | dest32[1] = swap32((uint32_t)((target + info.addend) & 0xffffffff)); 647 | dest32[3] = swap32((uint32_t)((target + info.addend) >> 32)); 648 | break; 649 | case R_BPF_64_ABS64: 650 | dest64[0] = swap64(target + info.addend); 651 | break; 652 | case R_BPF_64_ABS32: 653 | case R_BPF_64_NODYLD32: 654 | dest64[0] = swap32((uint32_t)(target + info.addend)); 655 | break; 656 | case R_BPF_64_RELATIVE: 657 | // Super weird reloc 658 | sections = view->GetSectionsAt(reloc->GetAddress()); 659 | if (!sections.empty() && sections[0]->GetName() == ".text") { 660 | rela_src = 0; 661 | rela_src = swap32(dest32[1]) | ((uint64_t)(swap32(dest32[3])) << 32); 662 | // wtf? 663 | if (rela_src < 0x100000000) { 664 | rela_src += 0x100000000; 665 | } 666 | dest32[1] = swap32((uint32_t)((rela_src)&0xffffffff)); 667 | dest32[3] = swap32((uint32_t)((rela_src) >> 32)); 668 | } else { 669 | // i give up 670 | } 671 | sections.clear(); 672 | break; 673 | case R_BPF_64_32: 674 | // If reloc matches a known syscall name, rewrite instruction to BPF_INS_SYSCALL instead. 675 | if (!symName.empty()) { 676 | for (auto const& other : SbfSyscalls) { 677 | if (symName.compare(other.second) == 0) { 678 | HijackAsSyscall(dest, other.first, le); 679 | return true; 680 | } 681 | } 682 | } 683 | 684 | // TODO This isn't documented as pc-rel, but BPF_INS_CALL takes pc-rel immediate 685 | dest32[1] = swap32((uint32_t)((target + info.addend - reloc->GetAddress()) / 8 - 1)); 686 | break; 687 | } 688 | return true; 689 | } 690 | 691 | virtual bool GetRelocationInfo(Ref view, Ref arch, std::vector& result) override 692 | { 693 | std::set relocTypes; 694 | for (auto& reloc : result) { 695 | reloc.type = StandardRelocationType; 696 | reloc.size = 8; 697 | reloc.pcRelative = false; 698 | reloc.dataRelocation = false; 699 | switch (reloc.nativeType) { 700 | case R_BPF_NONE: 701 | reloc.type = IgnoredRelocation; 702 | break; 703 | case R_BPF_64_64: 704 | break; 705 | case R_BPF_64_ABS64: 706 | reloc.dataRelocation = true; 707 | break; 708 | case R_BPF_64_ABS32: 709 | case R_BPF_64_NODYLD32: 710 | reloc.dataRelocation = true; 711 | reloc.size = 4; 712 | break; 713 | case R_BPF_64_RELATIVE: 714 | reloc.pcRelative = true; // not really?? 715 | break; 716 | case R_BPF_64_32: 717 | reloc.size = 4; 718 | break; 719 | default: 720 | reloc.type = UnhandledRelocation; 721 | relocTypes.insert(reloc.nativeType); 722 | break; 723 | } 724 | } 725 | for (auto& reloc : relocTypes) 726 | LogWarn("Unsupported ELF relocation type: %s", GetRelocationString((ElfBpfRelocationType)reloc)); 727 | return true; 728 | } 729 | }; 730 | 731 | void DefineSolanaCTypes(BNTypeLibrary* lib) 732 | { 733 | // SolAccountInfo 734 | { 735 | StructureBuilder b; 736 | b.AddMember(Type::PointerType(8, Type::IntegerType(1, false)), "key"); 737 | b.AddMember(Type::PointerType(8, Type::IntegerType(8, false)), "lamports"); 738 | b.AddMember(Type::IntegerType(8, false), "data_len"); 739 | b.AddMember(Type::PointerType(8, Type::IntegerType(1, false)), "data"); 740 | b.AddMember(Type::PointerType(8, Type::IntegerType(1, false)), "owner"); 741 | b.AddMember(Type::IntegerType(8, false), "rent_epoch"); 742 | b.AddMember(Type::BoolType(), "is_signer"); 743 | b.AddMember(Type::BoolType(), "is_writable"); 744 | b.AddMember(Type::BoolType(), "executable"); 745 | auto structure = b.Finalize(); 746 | 747 | QualifiedName name = std::string("SolAccountInfo"); 748 | std::string typeId = Type::GenerateAutoTypeId("ebpf_le", name); 749 | auto coreName = name.GetAPIObject(); 750 | auto type = Type::StructureType(structure); 751 | BNAddTypeLibraryNamedType(lib, &coreName, type->GetObject()); 752 | } 753 | } 754 | 755 | /* 756 | void RemapSegment(BinaryView *view, const Ref& segment, uint64_t naddr) { 757 | uint64_t start = segment->GetStart(); 758 | uint64_t length = segment->GetLength(); 759 | 760 | // Redefine segment 761 | // Probably better to have our own loader, but whatever 762 | view->RemoveAutoSegment(segment->GetStart(), segment->GetLength()); 763 | view->AddUserSegment(naddr, segment->GetLength(), segment->GetDataOffset(), segment->GetDataLength(), segment->GetFlags()); 764 | 765 | // Unfortunately, we'll have to move sections too 766 | // BinaryNinja is supposed to have a Segment::SetStart to take care of this but it's only in core, not C API :/ 767 | auto sections = view->GetSections(); 768 | for (auto const §ion : sections) { 769 | auto section_start = section->GetStart(); 770 | if (section_start >= start && section_start < start+length) { 771 | // Pain 772 | view->RemoveAutoSection(section->GetName()); 773 | view->AddUserSection( 774 | section->GetName(), 775 | section->GetStart(), 776 | section->GetLength(), 777 | section->GetSemantics(), 778 | section->GetType(), 779 | section->GetAlignment(), 780 | section->GetEntrySize(), 781 | section->GetLinkedSection(), 782 | section->GetInfoSection(), 783 | section->GetInfoData() 784 | ); 785 | } 786 | } 787 | } 788 | 789 | void FixupSolanaSegments(BinaryView *view) 790 | { 791 | // Remap segments to their intended location 792 | bool textRemapped = false, rodataRemapped = false; 793 | for (auto const& segment : view->GetSegments()) { 794 | uint32_t flags = segment->GetFlags(); 795 | if (flags == 5 && !textRemapped) { 796 | puts("remapping text"); 797 | RemapSegment(view, segment, 0x100000000); 798 | textRemapped = true; 799 | } else if (flags == 4 && !rodataRemapped) { 800 | puts("remapping rodata"); 801 | RemapSegment(view, segment, 0x200000000); 802 | rodataRemapped = true; 803 | } 804 | } 805 | } 806 | */ 807 | 808 | void HijackSolanaBinaryView(BinaryView* view) 809 | { 810 | // Callback when a new BinaryView of a Solana program is loaded. 811 | // FixupSolanaSegments(view); 812 | } 813 | 814 | extern "C" { 815 | BN_DECLARE_CORE_ABI_VERSION 816 | 817 | BINARYNINJAPLUGIN bool CorePluginInit() 818 | { 819 | Architecture* ebpf_be = new EBPFArchitecture("ebpf_be", BigEndian); 820 | Architecture::Register(ebpf_be); 821 | 822 | Architecture* ebpf_le = new EBPFArchitecture("ebpf_le", LittleEndian); 823 | Architecture::Register(ebpf_le); 824 | 825 | #define EM_BPF 247 826 | BinaryViewType::RegisterArchitecture( 827 | "ELF", 828 | EM_BPF, 829 | BigEndian, 830 | ebpf_be); 831 | 832 | BinaryViewType::RegisterArchitecture( 833 | "ELF", 834 | EM_BPF, 835 | LittleEndian, 836 | ebpf_le); 837 | 838 | Ref conv; 839 | conv = new SolanaCallingConvention(ebpf_be); 840 | ebpf_be->RegisterCallingConvention(conv); 841 | ebpf_be->SetDefaultCallingConvention(conv); 842 | conv = new SolanaCallingConvention(ebpf_le); 843 | ebpf_le->RegisterCallingConvention(conv); 844 | ebpf_le->SetDefaultCallingConvention(conv); 845 | auto ebpf_le_platform = ebpf_le->GetStandalonePlatform(); 846 | 847 | ebpf_le->RegisterRelocationHandler("ELF", new EbpfElfRelocationHandler()); 848 | ebpf_be->RegisterRelocationHandler("ELF", new EbpfElfRelocationHandler()); 849 | 850 | // TypeLibrary has no C++ bindings so we have to use Core API here. 851 | BNTypeLibrary* solanaCTypes = BNNewTypeLibrary(ebpf_le->GetObject(), "solana_c"); 852 | BNAddTypeLibraryPlatform(solanaCTypes, ebpf_le_platform->GetObject()); 853 | DefineSolanaCTypes(solanaCTypes); 854 | BNFinalizeTypeLibrary(solanaCTypes); 855 | 856 | BinaryViewType::RegisterBinaryViewInitialAnalysisCompletionEvent([](BinaryView* view) { 857 | if (view->GetDefaultArchitecture()->GetName() == "ebpf_le") { 858 | HijackSolanaBinaryView(view); 859 | } 860 | }); 861 | 862 | return true; 863 | } 864 | } 865 | -------------------------------------------------------------------------------- /disassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "disassembler.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | thread_local csh handle_lil = 0; 8 | thread_local csh handle_big = 0; 9 | 10 | extern "C" int 11 | ebpf_init(void) 12 | { 13 | int rc = -1; 14 | 15 | if (handle_lil || handle_big) { 16 | goto beach; 17 | } 18 | 19 | if (cs_open(CS_ARCH_BPF, (cs_mode)(CS_MODE_BIG_ENDIAN | CS_MODE_BPF_EXTENDED | CS_MODE_SBFV2), &handle_big) != CS_ERR_OK) { 20 | goto beach; 21 | } 22 | if (cs_open(CS_ARCH_BPF, (cs_mode)(CS_MODE_LITTLE_ENDIAN | CS_MODE_BPF_EXTENDED | CS_MODE_SBFV2), &handle_lil) != CS_ERR_OK) { 23 | goto beach; 24 | } 25 | 26 | cs_option(handle_big, CS_OPT_DETAIL, CS_OPT_ON); 27 | cs_option(handle_lil, CS_OPT_DETAIL, CS_OPT_ON); 28 | 29 | rc = 0; 30 | beach: 31 | if (rc) { 32 | ebpf_release(); 33 | } 34 | return rc; 35 | } 36 | 37 | extern "C" void 38 | ebpf_release(void) 39 | { 40 | if (handle_lil) { 41 | cs_close(&handle_lil); 42 | handle_lil = 0; 43 | } 44 | 45 | if (handle_big) { 46 | cs_close(&handle_big); 47 | handle_big = 0; 48 | } 49 | } 50 | 51 | extern "C" bool 52 | ebpf_decompose(const uint8_t* data, 53 | int size, 54 | uint64_t addr, 55 | bool lil_end, 56 | struct decomp_result* res) 57 | { 58 | if (!handle_lil) { 59 | ebpf_init(); 60 | } 61 | 62 | csh handle; 63 | cs_insn* insn = 0; 64 | 65 | handle = handle_big; 66 | if (lil_end) 67 | handle = handle_lil; 68 | res->handle = handle; 69 | 70 | size_t n = cs_disasm(handle, data, size, addr, 1, &insn); 71 | if (n != 1) { 72 | goto beach; 73 | } 74 | 75 | res->status = STATUS_SUCCESS; 76 | 77 | memcpy(&(res->insn), insn, sizeof(cs_insn)); 78 | memcpy(&(res->detail), insn->detail, sizeof(cs_detail)); 79 | 80 | beach: 81 | if (insn) { 82 | cs_free(insn, 1); 83 | insn = 0; 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | extern "C" int 90 | ebpf_disassemble(struct decomp_result* res, char* buf, size_t len) 91 | { 92 | int rc = -1; 93 | 94 | if (len < strlen(res->insn.mnemonic) + strlen(res->insn.op_str) + 2) { 95 | goto beach; 96 | } 97 | 98 | std::strncpy(buf, res->insn.mnemonic, len); 99 | std::strncat(buf, " ", len); 100 | std::strncat(buf, res->insn.op_str, len); 101 | buf[len - 1] = 0; 102 | 103 | rc = 0; 104 | beach: 105 | return rc; 106 | } 107 | 108 | extern "C" const char* 109 | ebpf_reg_to_str(uint32_t rid) 110 | { 111 | if (!handle_lil) { 112 | ebpf_init(); 113 | } 114 | return cs_reg_name(handle_lil, rid); 115 | } 116 | -------------------------------------------------------------------------------- /disassembler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util.h" 4 | #include 5 | #include 6 | 7 | //***************************************************************************** 8 | // structs and types 9 | //***************************************************************************** 10 | enum bpf_status_t { 11 | STATUS_ERROR_UNSPEC = -1, 12 | STATUS_SUCCESS = 0, 13 | STATUS_UNDEF_INSTR 14 | }; 15 | 16 | /* operand type */ 17 | enum operand_type_t { REG, 18 | VAL, 19 | LABEL }; 20 | 21 | struct decomp_request { 22 | uint8_t* data; 23 | int size; 24 | uint32_t addr; 25 | bool lil_end; 26 | }; 27 | 28 | struct decomp_result { 29 | /* actual capstone handle used, in case caller wants to do extra stuff 30 | (this can be one of two handles opened for BE or LE disassembling) */ 31 | csh handle; 32 | 33 | bpf_status_t status; 34 | 35 | cs_insn insn; 36 | cs_detail detail; 37 | }; 38 | 39 | //***************************************************************************** 40 | // function prototypes 41 | //***************************************************************************** 42 | extern "C" int ebpf_init(void); 43 | extern "C" void ebpf_release(void); 44 | extern "C" bool ebpf_decompose( 45 | const uint8_t* data, int size, uint64_t addr, 46 | bool lil_end, struct decomp_result* result); 47 | extern "C" int ebpf_disassemble( 48 | struct decomp_result*, char* buf, size_t len); 49 | extern "C" const char* ebpf_reg_to_str(uint32_t rid); 50 | 51 | static inline int16_t Int16SignExtend(uint32_t x) 52 | { 53 | int16_t ret; 54 | if (x < 0x8000) { 55 | ret = (int16_t)x; 56 | } else { 57 | ret = (int16_t)(0x10000 - x); 58 | } 59 | return ret; 60 | } 61 | 62 | static inline uint64_t JumpDest(const uint8_t* data, uint64_t addr, bool le) 63 | { 64 | uint16_t raw = *(const uint16_t*)(data + 2); 65 | if (!le) 66 | raw = bswap16(raw); 67 | int16_t off = (int16_t)raw; 68 | return addr + (int64_t)off * 8 + 8; 69 | } 70 | 71 | static inline uint64_t JumpDest(struct cs_bpf_op* op, uint64_t addr) 72 | { 73 | int64_t off = Int16SignExtend(op->off); 74 | return addr + (int64_t)off * 8 + 8; 75 | } 76 | 77 | static inline uint64_t CallDest(const uint8_t* data, uint64_t addr, bool le) 78 | { 79 | uint32_t raw = *(const uint32_t*)(data + 4); 80 | if (!le) 81 | raw = bswap32(raw); 82 | int64_t off = (int32_t)raw; 83 | return addr + (int64_t)off * 8 + 8; 84 | } 85 | 86 | static inline uint64_t CallDest(struct cs_bpf_op* op, uint64_t addr) 87 | { 88 | int64_t off = (int32_t)op->imm; 89 | return addr + (int64_t)off * 8 + 8; 90 | } 91 | -------------------------------------------------------------------------------- /il.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace BinaryNinja; 4 | 5 | #include 6 | 7 | #include "disassembler.h" 8 | #include "il.h" 9 | 10 | static ExprId operToIL(LowLevelILFunction& il, struct cs_bpf_op* op) 11 | { 12 | ExprId res; 13 | if (!op) { 14 | return il.Unimplemented(); 15 | } 16 | 17 | switch (op->type) { 18 | case BPF_OP_REG: 19 | res = il.Register(8, op->reg); 20 | break; 21 | case BPF_OP_IMM: 22 | res = il.Const(8, (int32_t)op->imm); 23 | break; 24 | case BPF_OP_MEM: 25 | res = il.Add(8, il.Register(8, op->mem.base), il.Const(8, (int64_t)Int16SignExtend(op->mem.disp))); 26 | break; 27 | default: 28 | res = il.Unimplemented(); 29 | break; 30 | } 31 | return res; 32 | } 33 | 34 | static ExprId JumpAlways( 35 | Architecture* arch, 36 | LowLevelILFunction& il, 37 | uint64_t target) 38 | { 39 | BNLowLevelILLabel* label = il.GetLabelForAddress(arch, target); 40 | if (label) 41 | return il.Goto(*label); 42 | else 43 | return il.Jump(il.ConstPointer(8, target)); 44 | } 45 | 46 | static void _JumpConditional( 47 | Architecture* arch, 48 | LowLevelILFunction& il, 49 | uint64_t t, 50 | uint64_t f, 51 | ExprId condition) 52 | { 53 | BNLowLevelILLabel* trueLabel = il.GetLabelForAddress(arch, t); 54 | BNLowLevelILLabel* falseLabel = il.GetLabelForAddress(arch, f); 55 | 56 | if (trueLabel && falseLabel) { 57 | il.AddInstruction(il.If(condition, *trueLabel, *falseLabel)); 58 | return; 59 | } 60 | 61 | LowLevelILLabel trueCode, falseCode; 62 | if (trueLabel) { 63 | il.AddInstruction(il.If(condition, *trueLabel, falseCode)); 64 | il.MarkLabel(falseCode); 65 | il.AddInstruction(il.Jump(il.ConstPointer(8, f))); 66 | return; 67 | } 68 | if (falseLabel) { 69 | il.AddInstruction(il.If(condition, trueCode, *falseLabel)); 70 | il.MarkLabel(trueCode); 71 | il.AddInstruction(il.Jump(il.ConstPointer(8, t))); 72 | return; 73 | } 74 | 75 | il.AddInstruction(il.If(condition, trueCode, falseCode)); 76 | il.MarkLabel(trueCode); 77 | il.AddInstruction(il.Jump(il.ConstPointer(8, t))); 78 | il.MarkLabel(falseCode); 79 | il.AddInstruction(il.Jump(il.ConstPointer(8, f))); 80 | } 81 | 82 | static void JumpConditional( 83 | Architecture* arch, 84 | LowLevelILFunction& il, 85 | uint64_t addr, 86 | struct cs_bpf_op* op, 87 | ExprId condition) 88 | { 89 | uint64_t t = JumpDest(op, addr); 90 | uint64_t f = addr + 8; 91 | _JumpConditional(arch, il, t, f, condition); 92 | } 93 | 94 | extern thread_local csh handle_lil; 95 | 96 | bool GetLowLevelILForBPFInstruction(Architecture* arch, LowLevelILFunction& il, 97 | const uint8_t* data, uint64_t addr, decomp_result* res, bool le) 98 | { 99 | struct cs_insn* insn = &(res->insn); 100 | struct cs_detail* detail = &(res->detail); 101 | struct cs_bpf* bpf = &(detail->bpf); 102 | 103 | // clang-format off 104 | /* create convenient access to instruction operands */ 105 | cs_bpf_op *oper0 = NULL, *oper1 = NULL, *oper2 = NULL, *oper3 = NULL, *oper4 = NULL; 106 | #define REQUIRE1OP if(!oper0) goto ReturnUnimpl; 107 | #define REQUIRE2OPS if(!oper0 || !oper1) goto ReturnUnimpl; 108 | #define REQUIRE3OPS if(!oper0 || !oper1 || !oper2) goto ReturnUnimpl; 109 | #define REQUIRE4OPS if(!oper0 || !oper1 || !oper2 || !oper3) goto ReturnUnimpl; 110 | #define REQUIRE5OPS if(!oper0 || !oper1 || !oper2 || !oper3 || !oper4) goto ReturnUnimpl; 111 | switch(bpf->op_count) { 112 | default: 113 | case 5: oper4 = &(bpf->operands[4]); 114 | case 4: oper3 = &(bpf->operands[3]); 115 | case 3: oper2 = &(bpf->operands[2]); 116 | case 2: oper1 = &(bpf->operands[1]); 117 | case 1: oper0 = &(bpf->operands[0]); 118 | case 0: break; 119 | } 120 | // clang-format on 121 | 122 | // printf("id=%s\n", cs_insn_name(handle_lil, insn->id)); 123 | // fflush(stdout); 124 | 125 | ExprId ei0, ei1, ei2; 126 | switch (insn->id) { 127 | // Legacy load/store class 128 | case BPF_INS_LDDW: 129 | REQUIRE2OPS 130 | ei0 = il.SetRegister(8, oper0->reg, operToIL(il, oper1)); 131 | il.AddInstruction(ei0); 132 | break; 133 | // Load/Store class 134 | case BPF_INS_LDXB: 135 | REQUIRE2OPS 136 | ei0 = il.Load(8, operToIL(il, oper1)); 137 | ei0 = il.LowPart(1, ei0); 138 | ei0 = il.SetRegister(8, oper0->reg, ei0); 139 | il.AddInstruction(ei0); 140 | break; 141 | case BPF_INS_LDXH: 142 | REQUIRE2OPS 143 | ei0 = il.Load(8, operToIL(il, oper1)); 144 | ei0 = il.LowPart(2, ei0); 145 | ei0 = il.SetRegister(8, oper0->reg, ei0); 146 | il.AddInstruction(ei0); 147 | break; 148 | case BPF_INS_LDXW: 149 | REQUIRE2OPS 150 | ei0 = il.Load(8, operToIL(il, oper1)); 151 | ei0 = il.LowPart(4, ei0); 152 | ei0 = il.SetRegister(8, oper0->reg, ei0); 153 | il.AddInstruction(ei0); 154 | break; 155 | case BPF_INS_LDXDW: 156 | REQUIRE2OPS 157 | ei0 = il.Load(8, operToIL(il, oper1)); 158 | ei0 = il.SetRegister(8, oper0->reg, ei0); 159 | il.AddInstruction(ei0); 160 | break; 161 | case BPF_INS_STXB: 162 | REQUIRE2OPS 163 | ei0 = il.LowPart(1, operToIL(il, oper1)); 164 | ei0 = il.Store(8, operToIL(il, oper0), ei0); 165 | il.AddInstruction(ei0); 166 | break; 167 | case BPF_INS_STXH: 168 | REQUIRE2OPS 169 | ei0 = il.LowPart(2, operToIL(il, oper1)); 170 | ei0 = il.Store(8, operToIL(il, oper0), ei0); 171 | il.AddInstruction(ei0); 172 | break; 173 | case BPF_INS_STXW: 174 | REQUIRE2OPS 175 | ei0 = il.LowPart(4, operToIL(il, oper1)); 176 | ei0 = il.Store(8, operToIL(il, oper0), ei0); 177 | il.AddInstruction(ei0); 178 | break; 179 | case BPF_INS_STXDW: 180 | REQUIRE2OPS 181 | ei0 = il.Store(8, operToIL(il, oper0), operToIL(il, oper1)); 182 | il.AddInstruction(ei0); 183 | break; 184 | // ALU64 class 185 | case BPF_INS_ADD64: 186 | REQUIRE2OPS 187 | ei0 = il.Add(8, operToIL(il, oper0), operToIL(il, oper1)); 188 | ei0 = il.SetRegister(8, oper0->reg, ei0); 189 | il.AddInstruction(ei0); 190 | break; 191 | case BPF_INS_SUB64: 192 | REQUIRE2OPS 193 | ei0 = il.Sub(8, operToIL(il, oper0), operToIL(il, oper1)); 194 | ei0 = il.SetRegister(8, oper0->reg, ei0); 195 | il.AddInstruction(ei0); 196 | break; 197 | case BPF_INS_MUL64: 198 | REQUIRE2OPS 199 | ei0 = il.Mult(8, operToIL(il, oper0), operToIL(il, oper1)); 200 | ei0 = il.SetRegister(8, oper0->reg, ei0); 201 | il.AddInstruction(ei0); 202 | break; 203 | case BPF_INS_DIV64: 204 | REQUIRE2OPS 205 | ei0 = il.DivUnsigned(8, operToIL(il, oper0), operToIL(il, oper1)); 206 | ei0 = il.SetRegister(8, oper0->reg, ei0); 207 | il.AddInstruction(ei0); 208 | break; 209 | case BPF_INS_OR64: 210 | REQUIRE2OPS 211 | ei0 = il.Or(8, operToIL(il, oper0), operToIL(il, oper1)); 212 | ei0 = il.SetRegister(8, oper0->reg, ei0); 213 | il.AddInstruction(ei0); 214 | break; 215 | case BPF_INS_AND64: 216 | REQUIRE2OPS 217 | ei0 = il.And(8, operToIL(il, oper0), operToIL(il, oper1)); 218 | ei0 = il.SetRegister(8, oper0->reg, ei0); 219 | il.AddInstruction(ei0); 220 | break; 221 | case BPF_INS_LSH64: 222 | REQUIRE2OPS 223 | ei0 = il.ShiftLeft(8, operToIL(il, oper0), operToIL(il, oper1)); 224 | ei0 = il.SetRegister(8, oper0->reg, ei0); 225 | il.AddInstruction(ei0); 226 | break; 227 | case BPF_INS_RSH64: 228 | REQUIRE2OPS 229 | ei0 = il.LogicalShiftRight(8, operToIL(il, oper0), operToIL(il, oper1)); 230 | ei0 = il.SetRegister(8, oper0->reg, ei0); 231 | il.AddInstruction(ei0); 232 | break; 233 | case BPF_INS_NEG64: 234 | REQUIRE1OP 235 | ei0 = il.Neg(8, operToIL(il, oper0)); 236 | ei0 = il.SetRegister(8, oper0->reg, ei0); 237 | il.AddInstruction(ei0); 238 | break; 239 | case BPF_INS_MOD64: 240 | REQUIRE2OPS 241 | ei0 = il.ModUnsigned(8, operToIL(il, oper0), operToIL(il, oper1)); 242 | ei0 = il.SetRegister(8, oper0->reg, ei0); 243 | il.AddInstruction(ei0); 244 | break; 245 | case BPF_INS_XOR64: 246 | REQUIRE2OPS 247 | ei0 = il.Xor(8, operToIL(il, oper0), operToIL(il, oper1)); 248 | ei0 = il.SetRegister(8, oper0->reg, ei0); 249 | il.AddInstruction(ei0); 250 | break; 251 | case BPF_INS_MOV64: 252 | REQUIRE2OPS 253 | ei0 = il.SetRegister(8, oper0->reg, operToIL(il, oper1)); 254 | il.AddInstruction(ei0); 255 | break; 256 | case BPF_INS_ARSH64: 257 | REQUIRE2OPS 258 | ei0 = il.ArithShiftRight(8, operToIL(il, oper0), operToIL(il, oper1)); 259 | ei0 = il.SetRegister(8, oper0->reg, ei0); 260 | il.AddInstruction(ei0); 261 | break; 262 | // ALU32 class 263 | case BPF_INS_ADD: 264 | REQUIRE2OPS 265 | ei0 = il.Add(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 266 | ei0 = il.SetRegister(8, oper0->reg, ei0); 267 | il.AddInstruction(ei0); 268 | break; 269 | case BPF_INS_SUB: 270 | REQUIRE2OPS 271 | ei0 = il.Sub(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 272 | ei0 = il.SetRegister(8, oper0->reg, ei0); 273 | il.AddInstruction(ei0); 274 | break; 275 | case BPF_INS_MUL: 276 | REQUIRE2OPS 277 | ei0 = il.Mult(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 278 | ei0 = il.SetRegister(8, oper0->reg, ei0); 279 | il.AddInstruction(ei0); 280 | break; 281 | case BPF_INS_DIV: 282 | REQUIRE2OPS 283 | ei0 = il.DivUnsigned(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 284 | ei0 = il.SetRegister(8, oper0->reg, ei0); 285 | il.AddInstruction(ei0); 286 | break; 287 | case BPF_INS_OR: 288 | REQUIRE2OPS 289 | ei0 = il.Or(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 290 | ei0 = il.SetRegister(8, oper0->reg, ei0); 291 | il.AddInstruction(ei0); 292 | break; 293 | case BPF_INS_AND: 294 | REQUIRE2OPS 295 | ei0 = il.And(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 296 | ei0 = il.SetRegister(8, oper0->reg, ei0); 297 | il.AddInstruction(ei0); 298 | break; 299 | case BPF_INS_LSH: 300 | REQUIRE2OPS 301 | ei0 = il.ShiftLeft(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 302 | ei0 = il.SetRegister(8, oper0->reg, ei0); 303 | il.AddInstruction(ei0); 304 | break; 305 | case BPF_INS_RSH: 306 | REQUIRE2OPS 307 | ei0 = il.LogicalShiftRight(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 308 | ei0 = il.SetRegister(8, oper0->reg, ei0); 309 | il.AddInstruction(ei0); 310 | break; 311 | case BPF_INS_NEG: 312 | REQUIRE1OP 313 | ei0 = il.Neg(4, il.LowPart(4, operToIL(il, oper0))); 314 | ei0 = il.SetRegister(8, oper0->reg, ei0); 315 | il.AddInstruction(ei0); 316 | break; 317 | case BPF_INS_MOD: 318 | REQUIRE2OPS 319 | ei0 = il.ModUnsigned(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 320 | ei0 = il.SetRegister(8, oper0->reg, ei0); 321 | il.AddInstruction(ei0); 322 | break; 323 | case BPF_INS_XOR: 324 | REQUIRE2OPS 325 | ei0 = il.Xor(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 326 | ei0 = il.SetRegister(8, oper0->reg, ei0); 327 | il.AddInstruction(ei0); 328 | break; 329 | case BPF_INS_MOV: 330 | REQUIRE2OPS 331 | ei0 = il.SetRegister(8, oper0->reg, il.LowPart(4, operToIL(il, oper1))); 332 | il.AddInstruction(ei0); 333 | break; 334 | case BPF_INS_ARSH: 335 | REQUIRE2OPS 336 | ei0 = il.ArithShiftRight(4, il.LowPart(4, operToIL(il, oper0)), il.LowPart(4, operToIL(il, oper1))); 337 | ei0 = il.SetRegister(8, oper0->reg, ei0); 338 | il.AddInstruction(ei0); 339 | break; 340 | // ALU extension class 341 | case BPF_INS_LE16: 342 | case BPF_INS_BE16: 343 | REQUIRE1OP 344 | if ((insn->id == BPF_INS_LE16) == le) { 345 | ei0 = il.LowPart(2, operToIL(il, oper0)); 346 | } else { 347 | ei0 = il.RotateLeft(2, il.LowPart(2, operToIL(il, oper0)), il.Const(2, 8)); 348 | } 349 | il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); 350 | break; 351 | case BPF_INS_LE32: 352 | case BPF_INS_BE32: 353 | REQUIRE1OP 354 | if ((insn->id == BPF_INS_LE32) == le) { 355 | ei0 = il.LowPart(2, operToIL(il, oper0)); 356 | } else { 357 | ei1 = operToIL(il, oper0); 358 | // clang-format off 359 | // dst = (( src & 0xff) << 24) 360 | ei0 = il.ShiftLeft(4, il.LowPart(1, ei1), il.Const(8, 24)); 361 | // dst |= (((src >> 8) & 0xff) << 16) 362 | ei0 = il.Or(4, ei0, il.ShiftLeft(4, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(4, 8))), il.Const(4, 16))); 363 | // dst |= (((src >> 16) & 0xff) << 8) 364 | ei0 = il.Or(4, ei0, il.ShiftLeft(4, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(4, 16))), il.Const(4, 8))); 365 | // dst |= ((src >> 24) & 0xff) 366 | ei0 = il.Or(4, ei0, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(4, 24)))); 367 | // clang-format on 368 | } 369 | il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); 370 | break; 371 | case BPF_INS_LE64: 372 | case BPF_INS_BE64: 373 | REQUIRE1OP 374 | if ((insn->id == BPF_INS_LE16) == le) { 375 | il.AddInstruction(il.Nop()); 376 | } else { 377 | ei1 = operToIL(il, oper0); 378 | // clang-format off 379 | // dst = (( src & 0xff) << 56) 380 | ei0 = il.ShiftLeft(8, il.LowPart(1, ei1), il.Const(8, 56)); 381 | // dst |= (((src >> 8) & 0xff) << 48) 382 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 8))), il.Const(8, 48))); 383 | // dst |= (((src >> 16) & 0xff) << 40) 384 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 16))), il.Const(8, 40))); 385 | // dst |= (((src >> 24) & 0xff) << 32) 386 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 24))), il.Const(8, 32))); 387 | // dst |= (((src >> 32) & 0xff) << 24) 388 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 32))), il.Const(8, 24))); 389 | // dst |= (((src >> 40) & 0xff) << 16) 390 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 40))), il.Const(8, 16))); 391 | // dst |= (((src >> 48) & 0xff) << 8) 392 | ei0 = il.Or(8, ei0, il.ShiftLeft(8, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 48))), il.Const(8, 8))); 393 | // dst |= ((src >> 56) & 0xff) 394 | ei0 = il.Or(8, ei0, il.LowPart(1, il.LogicalShiftRight(4, ei1, il.Const(8, 56)))); 395 | // clang-format on 396 | il.AddInstruction(il.SetRegister(8, oper0->reg, ei0)); 397 | } 398 | break; 399 | // Jump class 400 | case BPF_INS_JMP: 401 | REQUIRE1OP 402 | il.AddInstruction(JumpAlways(arch, il, JumpDest(oper0, addr))); 403 | break; 404 | case BPF_INS_JEQ: 405 | REQUIRE2OPS 406 | ei0 = il.CompareEqual(8, operToIL(il, oper0), operToIL(il, oper1)); 407 | JumpConditional(arch, il, addr, oper2, ei0); 408 | break; 409 | case BPF_INS_JGT: 410 | REQUIRE2OPS 411 | ei0 = il.CompareUnsignedGreaterThan(8, operToIL(il, oper0), operToIL(il, oper1)); 412 | JumpConditional(arch, il, addr, oper2, ei0); 413 | break; 414 | case BPF_INS_JGE: 415 | REQUIRE2OPS 416 | ei0 = il.CompareUnsignedGreaterEqual(8, operToIL(il, oper0), operToIL(il, oper1)); 417 | JumpConditional(arch, il, addr, oper2, ei0); 418 | break; 419 | case BPF_INS_JNE: 420 | REQUIRE2OPS 421 | ei0 = il.CompareNotEqual(8, operToIL(il, oper0), operToIL(il, oper1)); 422 | JumpConditional(arch, il, addr, oper2, ei0); 423 | break; 424 | case BPF_INS_JSGT: 425 | REQUIRE2OPS 426 | ei0 = il.CompareSignedGreaterThan(8, operToIL(il, oper0), operToIL(il, oper1)); 427 | JumpConditional(arch, il, addr, oper2, ei0); 428 | break; 429 | case BPF_INS_JSGE: 430 | REQUIRE2OPS 431 | ei0 = il.CompareSignedGreaterEqual(8, operToIL(il, oper0), operToIL(il, oper1)); 432 | JumpConditional(arch, il, addr, oper2, ei0); 433 | break; 434 | // Call class 435 | case BPF_INS_CALL: 436 | REQUIRE1OP 437 | ei0 = il.ConstPointer(8, CallDest(oper0, addr)); 438 | ei0 = il.Call(ei0); 439 | il.AddInstruction(ei0); 440 | break; 441 | case BPF_INS_SYSCALL: 442 | REQUIRE1OP 443 | if (oper0->imm == 0xb6fc1a11) { 444 | il.AddInstruction(il.Trap(0)); 445 | break; 446 | } 447 | il.AddInstruction(il.SystemCall()); 448 | break; 449 | case BPF_INS_CALLX: 450 | REQUIRE1OP 451 | ei0 = il.Call(operToIL(il, oper0)); 452 | il.AddInstruction(ei0); 453 | break; 454 | case BPF_INS_EXIT: 455 | il.AddInstruction(il.Return(il.Const(1, 1))); 456 | break; 457 | ReturnUnimpl: 458 | default: 459 | il.AddInstruction(il.Unimplemented()); 460 | break; 461 | } 462 | 463 | return true; 464 | } 465 | -------------------------------------------------------------------------------- /il.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define IL_FLAGCLASS_NONE 0 4 | 5 | bool GetLowLevelILForBPFInstruction(Architecture* arch, LowLevelILFunction& il, const uint8_t* data, uint64_t addr, decomp_result* res, bool le); 6 | -------------------------------------------------------------------------------- /opcodes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Reference: https://bpf.wtf/sol-0x03-isa/ 4 | 5 | // Legacy Load/Store class 6 | #define BPF_OPC_LDDW 0x18 7 | #define BPF_OPC_LDABSB 0x30 8 | #define BPF_OPC_LDABSH 0x28 9 | #define BPF_OPC_LDABSW 0x20 10 | #define BPF_OPC_LDABSDW 0x38 11 | #define BPF_OPC_LDINDB 0x50 12 | #define BPF_OPC_LDINDH 0x48 13 | #define BPF_OPC_LDINDW 0x40 14 | #define BPF_OPC_LDINDDW 0x58 15 | 16 | // Load/Store class 17 | #define BPF_OPC_LDXB 0x71 18 | #define BPF_OPC_LDXH 0x69 19 | #define BPF_OPC_LDXW 0x61 20 | #define BPF_OPC_LDXDW 0x79 21 | #define BPF_OPC_STB 0x72 22 | #define BPF_OPC_STH 0x6a 23 | #define BPF_OPC_STW 0x62 24 | #define BPF_OPC_STDW 0x7a 25 | #define BPF_OPC_STXB 0x73 26 | #define BPF_OPC_STXH 0x6b 27 | #define BPF_OPC_STXW 0x63 28 | #define BPF_OPC_STXDW 0x7b 29 | 30 | // ALU64 class 31 | #define BPF_OPC_ADD64_IMM 0x07 32 | #define BPF_OPC_ADD64_REG 0x0f 33 | #define BPF_OPC_SUB64_IMM 0x17 34 | #define BPF_OPC_SUB64_REG 0x1f 35 | #define BPF_OPC_MUL64_IMM 0x27 36 | #define BPF_OPC_MUL64_REG 0x2f 37 | #define BPF_OPC_DIV64_IMM 0x37 38 | #define BPF_OPC_DIV64_REG 0x3f 39 | #define BPF_OPC_OR64_IMM 0x47 40 | #define BPF_OPC_OR64_REG 0x4f 41 | #define BPF_OPC_AND64_IMM 0x57 42 | #define BPF_OPC_AND64_REG 0x5f 43 | #define BPF_OPC_LSH64_IMM 0x67 44 | #define BPF_OPC_LSH64_REG 0x6f 45 | #define BPF_OPC_RSH64_IMM 0x77 46 | #define BPF_OPC_RSH64_REG 0x7f 47 | #define BPF_OPC_NEG64_IMM 0x87 48 | #define BPF_OPC_MOD64_IMM 0x97 49 | #define BPF_OPC_MOD64_REG 0x9f 50 | #define BPF_OPC_XOR64_IMM 0xa7 51 | #define BPF_OPC_XOR64_REG 0xaf 52 | #define BPF_OPC_MOV64_IMM 0xb7 53 | #define BPF_OPC_MOV64_REG 0xbf 54 | #define BPF_OPC_ARSH64_IMM 0xc7 55 | #define BPF_OPC_ARSH64_REG 0xcf 56 | #define BPF_OPC_SDIV64_IMM 0xe7 57 | #define BPF_OPC_SDIV64_REG 0xef 58 | 59 | // ALU32 class 60 | #define BPF_OPC_ADD32_IMM 0x04 61 | #define BPF_OPC_ADD32_REG 0x0c 62 | #define BPF_OPC_SUB32_IMM 0x14 63 | #define BPF_OPC_SUB32_REG 0x1c 64 | #define BPF_OPC_MUL32_IMM 0x24 65 | #define BPF_OPC_MUL32_REG 0x2c 66 | #define BPF_OPC_DIV32_IMM 0x34 67 | #define BPF_OPC_DIV32_REG 0x3c 68 | #define BPF_OPC_OR32_IMM 0x44 69 | #define BPF_OPC_OR32_REG 0x4c 70 | #define BPF_OPC_AND32_IMM 0x54 71 | #define BPF_OPC_AND32_REG 0x5c 72 | #define BPF_OPC_LSH32_IMM 0x64 73 | #define BPF_OPC_LSH32_REG 0x6c 74 | #define BPF_OPC_RSH32_IMM 0x74 75 | #define BPF_OPC_RSH32_REG 0x7c 76 | #define BPF_OPC_NEG32_IMM 0x84 77 | #define BPF_OPC_MOD32_IMM 0x94 78 | #define BPF_OPC_MOD32_REG 0x9c 79 | #define BPF_OPC_XOR32_IMM 0xa4 80 | #define BPF_OPC_XOR32_REG 0xac 81 | #define BPF_OPC_MOV32_IMM 0xb4 82 | #define BPF_OPC_MOV32_REG 0xbc 83 | 84 | // Endian ALU extension 85 | #define BPF_OPC_LE 0xd4 86 | #define BPF_OPC_BE 0xdc 87 | 88 | // Jump class 89 | #define BPF_OPC_JA 0x05 90 | #define BPF_OPC_JEQ_IMM 0x15 91 | #define BPF_OPC_JEQ_REG 0x1d 92 | #define BPF_OPC_JGT_IMM 0x25 93 | #define BPF_OPC_JGT_REG 0x2d 94 | #define BPF_OPC_JGE_IMM 0x35 95 | #define BPF_OPC_JGE_REG 0x3d 96 | #define BPF_OPC_JSET_IMM 0x45 97 | #define BPF_OPC_JSET_REG 0x4d 98 | #define BPF_OPC_JNE_IMM 0x55 99 | #define BPF_OPC_JNE_REG 0x5d 100 | #define BPF_OPC_JSGT_IMM 0x65 101 | #define BPF_OPC_JSGT_REG 0x6d 102 | #define BPF_OPC_JSGE_IMM 0x75 103 | #define BPF_OPC_JSGE_REG 0x7d 104 | #define BPF_OPC_JLT_IMM 0xa5 105 | #define BPF_OPC_JLT_REG 0xad 106 | #define BPF_OPC_JLE_IMM 0xb5 107 | #define BPF_OPC_JLE_REG 0xbd 108 | #define BPF_OPC_JSLT_IMM 0xc5 109 | #define BPF_OPC_JSLT_REG 0xcd 110 | #define BPF_OPC_JSLE_IMM 0xd5 111 | #define BPF_OPC_JSLE_REG 0xdd 112 | 113 | // Call class 114 | #define BPF_OPC_CALL 0x85 115 | #define BPF_OPC_CALLX 0x8d 116 | #define BPF_OPC_EXIT 0x95 117 | -------------------------------------------------------------------------------- /syscalls.cpp: -------------------------------------------------------------------------------- 1 | #include "syscalls.h" 2 | 3 | // Reference: https://bpf.wtf/sol-0x04-syscalls/ 4 | 5 | std::map SbfSyscalls = { 6 | { 0xb6fc1a11, "abort" }, 7 | { 0x686093bb, "sol_panic_" }, 8 | { 0x207559bd, "sol_log_" }, 9 | { 0x5c2a3178, "sol_log_64_" }, 10 | { 0x52ba5096, "sol_log_compute_units_" }, 11 | { 0x7ef088ca, "sol_log_pubkey" }, 12 | { 0x9377323c, "sol_create_program_address" }, 13 | { 0x48504a38, "sol_try_find_program_address" }, 14 | { 0x11f49d86, "sol_sha256" }, 15 | { 0xd7793abb, "sol_keccak256" }, 16 | { 0x17e40350, "sol_secp256k1_recover" }, 17 | { 0x174c5122, "sol_blake3" }, 18 | { 0xaa2607ca, "sol_curve_validate_point" }, 19 | { 0xdd1c41a6, "sol_curve_group_op" }, 20 | { 0xd56b5fe9, "sol_get_clock_sysvar" }, 21 | { 0x23a29a61, "sol_get_epoch_schedule_sysvar" }, 22 | { 0x3b97b73c, "sol_get_fees_sysvar" }, 23 | { 0xbf7188f6, "sol_get_rent_sysvar" }, 24 | { 0x717cc4a3, "sol_memcpy_" }, 25 | { 0x434371f8, "sol_memmove_" }, 26 | { 0x5fdcde31, "sol_memcmp_" }, 27 | { 0x3770fb22, "sol_memset_" }, 28 | { 0xa22b9c85, "sol_invoke_signed_c" }, 29 | { 0xd7449092, "sol_invoke_signed_rust" }, 30 | { 0x83f00e8f, "sol_alloc_free_" }, 31 | { 0xa226d3eb, "sol_set_return_data" }, 32 | { 0x5d2245e4, "sol_get_return_data" }, 33 | { 0x7317b434, "sol_log_data" }, 34 | { 0xadb8efc8, "sol_get_processed_sibling_instruction" }, 35 | { 0x85532d94, "sol_get_stack_height" } 36 | }; 37 | -------------------------------------------------------------------------------- /syscalls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern std::map SbfSyscalls; 7 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define bswap16(x) __builtin_bswap16(x) 4 | #define bswap32(x) __builtin_bswap32(x) 5 | #define bswap64(x) __builtin_bswap64(x) 6 | --------------------------------------------------------------------------------