├── .clang-format ├── .gitattributes ├── .github └── workflows │ └── build.yml ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── src ├── commands.cc ├── commands.h ├── main.cc ├── meta_basic_block.cc └── meta_basic_block.h ├── tests ├── CMakeLists.txt └── triton_simplify_test.cc ├── thirdparty ├── CMakeLists.txt └── binaryninja-api │ ├── CMakeLists.txt │ ├── binaryninjacore.cc │ ├── binaryninjacore_stable.cc │ └── regenerate-api-stubs.sh └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | # Use the Google style in this project. 2 | BasedOnStyle: Google 3 | 4 | # Some folks prefer to write "int& foo" while others prefer "int &foo". The 5 | # Google Style Guide only asks for consistency within a project, we chose 6 | # "int& foo" for this project: 7 | DerivePointerAlignment: false 8 | PointerAlignment: Left 9 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | vcpkg-overlay-ports/triton/*.patch text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | BUILD_TYPE: Release 7 | 8 | jobs: 9 | build: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | include: 14 | - os: ubuntu-20.04 15 | - os: windows-2019 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | submodules: recursive 22 | 23 | - name: Install ninja-build tool 24 | uses: seanmiddleditch/gha-setup-ninja@v3 25 | 26 | - name: Create Build Environment 27 | env: 28 | RUNNER_WORKSPACE: ${{ runner.workspace }} 29 | working-directory: ${{ github.workspace }} 30 | shell: bash 31 | run: | 32 | ./vcpkg/bootstrap-vcpkg.sh 33 | cmake -E make_directory "${RUNNER_WORKSPACE}/build" 34 | 35 | - name: Enable Developer Command Prompt (Windows) 36 | if: matrix.os == 'windows-2019' 37 | uses: ilammy/msvc-dev-cmd@v1.7.0 38 | 39 | - name: Configure CMake 40 | working-directory: ${{ runner.workspace }}/build 41 | shell: bash 42 | run: | 43 | cmake "${GITHUB_WORKSPACE}" -G Ninja \ 44 | "-DCMAKE_BUILD_TYPE=${BUILD_TYPE}" \ 45 | "-DCMAKE_TOOLCHAIN_FILE=${GITHUB_WORKSPACE}/vcpkg/scripts/buildsystems/vcpkg.cmake" 46 | 47 | - name: Build 48 | working-directory: ${{ runner.workspace }}/build 49 | shell: bash 50 | run: cmake --build . --config "${BUILD_TYPE}" 51 | 52 | - name: Upload Build Artifacts (Windows) 53 | if: matrix.os == 'windows-2019' 54 | uses: actions/upload-artifact@v3 55 | with: 56 | name: triton_bn_win64 57 | path: ${{ runner.workspace }}\build\*.dll 58 | retention-days: 3 59 | 60 | - name: Upload Build Artifacts (Ubuntu) 61 | if: matrix.os == 'ubuntu-20.04' 62 | uses: actions/upload-artifact@v3 63 | with: 64 | name: triton_bn_ubuntu2004 65 | path: ${{ runner.workspace }}/build/*.so 66 | retention-days: 3 67 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vcpkg"] 2 | path = vcpkg 3 | url = https://github.com/microsoft/vcpkg.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [0.2.0] - 2024-07-17 6 | 7 | ### Added 8 | 9 | - Add support for ARM64 binaries 10 | - Add new "Patch" commands that modifies original function/basic block after simplification 11 | - Add a new NOP-like instruction removal pass 12 | 13 | ### Changed 14 | 15 | - Previous simplification actions are now under the "Preview" subsection 16 | 17 | ## [0.1.1] - 2024-04-06 18 | 19 | ### Changed 20 | 21 | - Target Binary Ninja v4.0.x 22 | 23 | ## [0.1.0] - 2022-06-23 24 | 25 | Initial release 26 | 27 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | 3 | project("triton-bn" VERSION 0.2.0 LANGUAGES CXX) 4 | 5 | option(TRITON_BN_BUILD_TESTS "Build test executables" OFF) 6 | 7 | set(TRITON_BN_BINARYNINJA_CHANNEL "stable" CACHE 8 | STRING "Binary Ninja channel, either 'stable' or 'dev'") 9 | 10 | set(CMAKE_CXX_STANDARD 17) 11 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 12 | set(CMAKE_CXX_EXTENSIONS OFF) 13 | 14 | find_package(triton CONFIG REQUIRED) 15 | 16 | # Binary Ninja 17 | add_subdirectory("thirdparty") 18 | 19 | # Plugin module 20 | add_library(triton_bn_plugin SHARED 21 | "src/main.cc" 22 | "src/meta_basic_block.h" 23 | "src/meta_basic_block.cc" 24 | "src/commands.h" 25 | "src/commands.cc" 26 | ) 27 | target_link_libraries(triton_bn_plugin PRIVATE 28 | BinaryNinja::API 29 | triton::triton 30 | ) 31 | 32 | # Tests 33 | if(TRITON_BN_BUILD_TESTS) 34 | add_subdirectory("tests") 35 | endif() 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2022 triton-bn developers 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # triton-bn ![Static Badge](https://img.shields.io/badge/Binary_Ninja_API-v4.0.x-blue) 2 | 3 | `triton-bn` is a small Binary Ninja plugin that can be used to apply 4 | [Triton](https://github.com/jonathansalwan/Triton)'s dead store eliminitation 5 | pass on basic blocks or functions. 6 | The plugin supports the `x86_64`, `x86` and `aarch64` architectures. 7 | 8 | This plugin may also serve as a base for people that would want to play with 9 | Triton inside of Binary Ninja. 10 | 11 | ## How to Build 12 | 13 | On Windows: 14 | ``` 15 | $ git clone --recurse-submodule https://github.com/ergrelet/triton-bn.git && cd triton-bn 16 | $ ./vcpkg/bootstrap-vcpkg.bat 17 | $ cmake -B build -DCMAKE_TOOLCHAIN_FILE=./vcpkg/scripts/buildsystems/vcpkg.cmake 18 | $ cmake --build build --config Release -- -maxcpucount 19 | ``` 20 | 21 | On Linux distributions: 22 | ``` 23 | $ git clone --recurse-submodule https://github.com/ergrelet/triton-bn.git && cd triton-bn 24 | $ ./vcpkg/bootstrap-vcpkg.sh 25 | $ cmake -B build -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_TOOLCHAIN_FILE=./vcpkg/scripts/buildsystems/vcpkg.cmake 26 | $ cmake --build build -- -j$(nproc) 27 | ``` 28 | 29 | ## How to Install 30 | 31 | Check out the official Binary Ninja documentation to know where to copy the 32 | files: 33 | [Using Plugins](https://docs.binary.ninja/guide/plugins.html) 34 | 35 | 36 | ## Know Limitations 37 | * Instructions that use RIP/PC-relative addressing aren't relocated properly in preview mode 38 | -------------------------------------------------------------------------------- /src/commands.cc: -------------------------------------------------------------------------------- 1 | #include "commands.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "meta_basic_block.h" 11 | 12 | namespace triton_bn { 13 | 14 | using namespace BinaryNinja; 15 | 16 | static std::vector SimplifyBasicBlockCommon( 17 | BinaryNinja::BinaryView* p_view, bool padding); 18 | static std::vector SimplifyFunctionCommon(BinaryView* p_view, 19 | bool padding); 20 | static FlowGraph* GenerateFlowGraphFromMetaBasicBlocks( 21 | std::vector basic_blocks); 22 | 23 | void SimplifyBasicBlockPreviewCommand(BinaryNinja::BinaryView* p_view) { 24 | auto simplified_basic_blocks = SimplifyBasicBlockCommon(p_view, false); 25 | if (simplified_basic_blocks.empty()) { 26 | LogError("Failed to simplify basic block"); 27 | return; 28 | } 29 | 30 | // Construct result flow graph and display it 31 | FlowGraph* flow_graph = 32 | GenerateFlowGraphFromMetaBasicBlocks(std::move(simplified_basic_blocks)); 33 | 34 | const std::string report_title = fmt::format( 35 | "Simplified basic block (0x{:x})", simplified_basic_blocks[0].GetStart()); 36 | p_view->ShowGraphReport(report_title, flow_graph); 37 | 38 | LogInfo("Basic block has been simplified and preview rendered"); 39 | } 40 | 41 | void SimplifyBasicBlockPatchCommand(BinaryNinja::BinaryView* p_view) { 42 | auto simplified_basic_blocks = SimplifyBasicBlockCommon(p_view, true); 43 | if (simplified_basic_blocks.empty()) { 44 | LogError("Failed to simplify basic block"); 45 | return; 46 | } 47 | 48 | // Patch code 49 | for (auto& basic_block : simplified_basic_blocks) { 50 | for (auto& instruction : basic_block.triton_bb().getInstructions()) { 51 | p_view->Write(instruction.getAddress(), instruction.getOpcode(), 52 | instruction.getSize()); 53 | } 54 | } 55 | // Rerun analysis 56 | p_view->UpdateAnalysis(); 57 | 58 | LogInfo("Basic block has been simplified and patches applied"); 59 | } 60 | 61 | static std::vector SimplifyBasicBlockCommon( 62 | BinaryNinja::BinaryView* p_view, bool padding) { 63 | // Get currently selected address in the view 64 | const auto current_offset = p_view->GetCurrentOffset(); 65 | LogDebug("Current offset=0x%p", (void*)current_offset); 66 | 67 | // Find the function in which this address resides 68 | const auto candidate_basic_blocks = 69 | p_view->GetBasicBlocksForAddress(current_offset); 70 | if (candidate_basic_blocks.empty()) { 71 | LogError("Failed to find the currently selected basic block"); 72 | return {}; 73 | } 74 | // TODO: Alert the users if multiple candidate basic blocks exist? 75 | const auto basic_block = candidate_basic_blocks[0]; 76 | LogDebug("Current basic block=0x%p", (void*)basic_block->GetStart()); 77 | 78 | // Determine the current platform/architecture 79 | const std::string architecture_name = 80 | p_view->GetDefaultArchitecture()->GetName(); 81 | LogDebug("Architecture is '%s'", architecture_name.c_str()); 82 | 83 | triton::Context triton{}; 84 | if (architecture_name == "x86_64") { 85 | triton.setArchitecture(triton::arch::ARCH_X86_64); 86 | } else if (architecture_name == "x86") { 87 | triton.setArchitecture(triton::arch::ARCH_X86); 88 | } else if (architecture_name == "aarch64") { 89 | triton.setArchitecture(triton::arch::ARCH_AARCH64); 90 | } else { 91 | LogError("Unsupported architecture '%s'", architecture_name.c_str()); 92 | return {}; 93 | } 94 | 95 | auto meta_basic_blocks = 96 | ExtractMetaBasicBlocksFromBasicBlock(*p_view, basic_block, triton); 97 | 98 | // Simplify basic block 99 | return SimplifyMetaBasicBlocks(triton, std::move(meta_basic_blocks), padding); 100 | } 101 | 102 | bool ValidateSimplifyBasicBlockCommand(BinaryView* p_view) { 103 | return ValidateSimplifyFunctionCommand(p_view); 104 | } 105 | 106 | void SimplifyFunctionPreviewCommand(BinaryView* p_view) { 107 | auto simplified_basic_blocks = SimplifyFunctionCommon(p_view, false); 108 | if (simplified_basic_blocks.empty()) { 109 | LogError("Failed to simplify function"); 110 | return; 111 | } 112 | 113 | // Construct result flow graph and display it 114 | const auto candidate_functions = 115 | p_view->GetAnalysisFunctionsContainingAddress( 116 | simplified_basic_blocks[0].GetStart()); 117 | if (candidate_functions.empty()) { 118 | LogError("Failed to simplify function"); 119 | return; 120 | } 121 | 122 | const std::string current_function_name = 123 | candidate_functions[0]->GetSymbol()->GetFullName(); 124 | const std::string report_title = 125 | fmt::format("Simplified function ({})", current_function_name); 126 | FlowGraph* flow_graph = 127 | GenerateFlowGraphFromMetaBasicBlocks(std::move(simplified_basic_blocks)); 128 | p_view->ShowGraphReport(report_title, flow_graph); 129 | 130 | LogInfo("Function has been simplified and preview rendered"); 131 | } 132 | 133 | void SimplifyFunctionPatchCommand(BinaryView* p_view) { 134 | auto simplified_basic_blocks = SimplifyFunctionCommon(p_view, true); 135 | if (simplified_basic_blocks.empty()) { 136 | LogError("Failed to simplify function"); 137 | return; 138 | } 139 | 140 | // Patch code 141 | for (auto& basic_block : simplified_basic_blocks) { 142 | for (auto& instruction : basic_block.triton_bb().getInstructions()) { 143 | p_view->Write(instruction.getAddress(), instruction.getOpcode(), 144 | instruction.getSize()); 145 | } 146 | } 147 | // Rerun analysis 148 | p_view->UpdateAnalysis(); 149 | 150 | LogInfo("Function has been simplified and patches applied"); 151 | } 152 | 153 | static std::vector SimplifyFunctionCommon(BinaryView* p_view, 154 | bool padding) { 155 | // Get currently selected address in the view 156 | const auto current_offset = p_view->GetCurrentOffset(); 157 | LogDebug("Current offset=0x%p", (void*)current_offset); 158 | 159 | // Find the function in which this address resides 160 | const auto candidate_functions = 161 | p_view->GetAnalysisFunctionsContainingAddress(current_offset); 162 | if (candidate_functions.empty()) { 163 | LogError("Failed to find the currently selected function"); 164 | return {}; 165 | } 166 | // TODO: Alert the users if multiple candidate functions exist 167 | const auto current_function = candidate_functions[0]; 168 | LogDebug("Current function=0x%p", (void*)current_function->GetStart()); 169 | 170 | // Determine the current architecture 171 | const std::string architecture_name = 172 | p_view->GetDefaultArchitecture()->GetName(); 173 | LogDebug("Architecture is '%s'", architecture_name.c_str()); 174 | 175 | // Intialize Triton's context 176 | triton::Context triton{}; 177 | if (architecture_name == "x86_64") { 178 | triton.setArchitecture(triton::arch::ARCH_X86_64); 179 | } else if (architecture_name == "x86") { 180 | triton.setArchitecture(triton::arch::ARCH_X86); 181 | } else if (architecture_name == "aarch64") { 182 | triton.setArchitecture(triton::arch::ARCH_AARCH64); 183 | } else { 184 | LogError("Unsupported architecture '%s'", architecture_name.c_str()); 185 | return {}; 186 | } 187 | 188 | // Create `MetaBasicBlock`s from the current Binja function's basic blocks 189 | auto meta_basic_blocks = 190 | ExtractMetaBasicBlocksFromFunction(*p_view, current_function, triton); 191 | LogDebug("%zu meta basic block(s) extracted", meta_basic_blocks.size()); 192 | 193 | if (Settings::Instance()->Get("triton-bn.mergeBasicBlocks")) { 194 | // Merge basic blocks 195 | meta_basic_blocks = MergeMetaBasicBlocks(std::move(meta_basic_blocks)); 196 | } 197 | 198 | // Simplify basic blocks 199 | return SimplifyMetaBasicBlocks(triton, std::move(meta_basic_blocks), padding); 200 | } 201 | 202 | bool ValidateSimplifyFunctionCommand(BinaryView* p_view) { 203 | if (p_view == nullptr) { 204 | return false; 205 | } 206 | BinaryView& view = *p_view; 207 | 208 | // Check platform compatibility 209 | const std::string architecture_name = 210 | view.GetDefaultArchitecture()->GetName(); 211 | if (architecture_name != "x86_64" && architecture_name != "x86" && 212 | architecture_name != "aarch64") { 213 | LogError("Unsupported architecture"); 214 | return false; 215 | } 216 | 217 | return true; 218 | } 219 | 220 | static FlowGraph* GenerateFlowGraphFromMetaBasicBlocks( 221 | std::vector basic_blocks) { 222 | auto* flow_graph = new FlowGraph(); 223 | 224 | std::unordered_map graph_nodes_addr_map{}; 225 | std::vector graph_nodes{}; 226 | for (auto& meta_bb : basic_blocks) { 227 | // Generate disassembly 228 | std::vector disassembly_lines{}; 229 | const auto& instructions = meta_bb.triton_bb().getInstructions(); 230 | for (auto& instr : instructions) { 231 | InstructionTextToken instr_token( 232 | BNInstructionTextTokenType::InstructionToken, instr.getDisassembly(), 233 | 0, static_cast(instr.getSize())); 234 | instr_token.address = instr.getAddress(); 235 | 236 | { 237 | DisassemblyTextLine line; 238 | line.addr = instr.getAddress(); 239 | line.tokens = {instr_token}; 240 | disassembly_lines.emplace_back(std::move(line)); 241 | } 242 | } 243 | 244 | // Construct new node 245 | { 246 | auto* node = new FlowGraphNode(flow_graph); 247 | node->SetLines(disassembly_lines); 248 | graph_nodes.emplace_back(node); 249 | graph_nodes_addr_map[meta_bb.GetStart()] = node; 250 | } 251 | } 252 | 253 | if (basic_blocks.size() != graph_nodes.size()) { 254 | LogError("Invalid graph node count"); 255 | return nullptr; 256 | } 257 | 258 | // Construct graph 259 | for (size_t i = 0; i < graph_nodes.size(); i++) { 260 | FlowGraphNode* graph_node = graph_nodes[i]; 261 | MetaBasicBlock& meta_bb = basic_blocks[i]; 262 | 263 | // Resolve outgoing edges 264 | for (const BasicBlockEdge& outgoing_edge : meta_bb.outgoing_edges()) { 265 | const BasicBlock* target = outgoing_edge.target; 266 | if (target == nullptr) { 267 | continue; 268 | } 269 | 270 | const uint64_t target_addr = target->GetStart(); 271 | const auto node_it = graph_nodes_addr_map.find(target_addr); 272 | if (node_it == std::cend(graph_nodes_addr_map)) { 273 | continue; 274 | } 275 | 276 | graph_node->AddOutgoingEdge(outgoing_edge.type, node_it->second); 277 | } 278 | 279 | flow_graph->AddNode(graph_node); 280 | } 281 | 282 | return flow_graph; 283 | } 284 | 285 | } // namespace triton_bn 286 | -------------------------------------------------------------------------------- /src/commands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace triton_bn { 6 | 7 | void SimplifyBasicBlockPreviewCommand(BinaryNinja::BinaryView* p_view); 8 | void SimplifyBasicBlockPatchCommand(BinaryNinja::BinaryView* p_view); 9 | bool ValidateSimplifyBasicBlockCommand(BinaryNinja::BinaryView* p_view); 10 | 11 | void SimplifyFunctionPreviewCommand(BinaryNinja::BinaryView* p_view); 12 | void SimplifyFunctionPatchCommand(BinaryNinja::BinaryView* p_view); 13 | bool ValidateSimplifyFunctionCommand(BinaryNinja::BinaryView* p_view); 14 | 15 | } // namespace triton_bn 16 | -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "commands.h" 4 | 5 | using namespace BinaryNinja; 6 | 7 | extern "C" { 8 | 9 | BN_DECLARE_CORE_ABI_VERSION 10 | 11 | BINARYNINJAPLUGIN bool CorePluginInit() { 12 | auto settings = Settings::Instance(); 13 | settings->RegisterGroup("triton-bn", "triton-bn"); 14 | settings->RegisterSetting("triton-bn.mergeBasicBlocks", R"({ 15 | "title" : "Merge basic blocks before simplification", 16 | "type" : "boolean", 17 | "default" : true, 18 | "description" : "Automatically merge basic blocks linked with a single unconditional branch before running the simplification passes on functions." 19 | })"); 20 | 21 | // Preview commands 22 | PluginCommand::Register("triton-bn\\Preview\\Simplify basic block (DSE)", 23 | "Simplify basic block using Triton's DSE pass", 24 | triton_bn::SimplifyBasicBlockPreviewCommand, 25 | triton_bn::ValidateSimplifyBasicBlockCommand); 26 | PluginCommand::Register("triton-bn\\Preview\\Simplify function (DSE)", 27 | "Simplify function using Triton's DSE pass", 28 | triton_bn::SimplifyFunctionPreviewCommand, 29 | triton_bn::ValidateSimplifyFunctionCommand); 30 | // Patch commands 31 | PluginCommand::Register("triton-bn\\Patch\\Simplify basic block (DSE)", 32 | "Simplify basic block using Triton's DSE pass", 33 | triton_bn::SimplifyBasicBlockPatchCommand, 34 | triton_bn::ValidateSimplifyBasicBlockCommand); 35 | PluginCommand::Register("triton-bn\\Patch\\Simplify function (DSE)", 36 | "Simplify function using Triton's DSE pass", 37 | triton_bn::SimplifyFunctionPatchCommand, 38 | triton_bn::ValidateSimplifyFunctionCommand); 39 | 40 | return true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/meta_basic_block.cc: -------------------------------------------------------------------------------- 1 | #include "meta_basic_block.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace triton_bn { 7 | 8 | using namespace BinaryNinja; 9 | 10 | static bool IsCallInstruction(const triton::arch::Instruction&); 11 | static bool IsJumpInstruction(const triton::arch::Instruction& instr); 12 | static void MergeLinkedBasicBlocks(const BasicBlockEdge& edge, 13 | MetaBasicBlock& root_bb, 14 | MetaBasicBlock& target_bb); 15 | static triton::arch::BasicBlock RemoveNopLikeInstructions( 16 | const triton::Context& triton_context, 17 | const triton::arch::BasicBlock& triton_bb, bool padding = false); 18 | 19 | // Transform a given "Binary Ninja" basic block into one or several 20 | // `MetaBasicBlock`s that can be simplified with Triton 21 | std::vector ExtractMetaBasicBlocksFromBasicBlock( 22 | BinaryView& view, Ref basic_block, triton::Context& triton) { 23 | // TODO: Merge fallthrough automatically? 24 | std::vector result{}; 25 | triton::arch::BasicBlock triton_bb{}; 26 | 27 | const auto default_arch = view.GetDefaultArchitecture(); 28 | const size_t max_instr_len = default_arch->GetMaxInstructionLength(); 29 | 30 | // Iterate through the disassembly 31 | uint64_t last_instr_addr = 0; 32 | DisassemblySettings settings{}; 33 | auto disassembly_lines = basic_block->GetDisassemblyText(&settings); 34 | for (size_t i = 0; i < disassembly_lines.size(); i++) { 35 | const uint64_t cur_instr_addr = disassembly_lines[i].addr; 36 | if (cur_instr_addr == last_instr_addr) { 37 | // Note: Might happen when the basic block has a 'fallthrough' edge, 38 | // ignore. 39 | continue; 40 | } 41 | last_instr_addr = cur_instr_addr; 42 | 43 | const DataBuffer cur_instr_data = 44 | view.ReadBuffer(cur_instr_addr, max_instr_len); 45 | BinaryNinja::InstructionInfo binja_instruction{}; 46 | if (!default_arch->GetInstructionInfo(&cur_instr_data[0], cur_instr_addr, 47 | max_instr_len, binja_instruction)) { 48 | continue; 49 | } 50 | 51 | // Add disassembled instruction to the basic block 52 | triton::arch::Instruction new_instr( 53 | cur_instr_addr, static_cast(cur_instr_data.GetData()), 54 | binja_instruction.length); 55 | try { 56 | triton.disassembly(new_instr); 57 | } catch (triton::exceptions::Disassembly& ex) { 58 | LogError("Failed to disassemble instruction at address 0x%p", 59 | (void*)cur_instr_addr); 60 | return {}; 61 | } 62 | triton_bb.add(new_instr); 63 | LogDebug("0x%p - %zu - '%s'", (void*)cur_instr_addr, 64 | binja_instruction.length, new_instr.getDisassembly().c_str()); 65 | 66 | // Split basic blocks on `call` instructions to make them simplifiable 67 | if (IsCallInstruction(new_instr)) { 68 | LogDebug("call detected: %s", new_instr.getDisassembly().c_str()); 69 | // Add basic block to the result 70 | result.emplace_back(MetaBasicBlock(triton_bb, basic_block)); 71 | triton_bb = {}; 72 | } 73 | } 74 | // Add basic block to the result 75 | result.emplace_back(MetaBasicBlock(triton_bb, basic_block)); 76 | 77 | return result; 78 | } 79 | 80 | // Same as `ExtractMetaBasicBlocksFromBasicBlock`, except we extract 81 | // `MetaBasicBlock`s from the basic blocks of a given "Binary Ninja" function. 82 | std::vector ExtractMetaBasicBlocksFromFunction( 83 | BinaryView& view, Ref function, triton::Context& triton) { 84 | std::vector func_meta_basic_blocks{}; 85 | 86 | // Iterate through the basic blocks 87 | for (auto binja_bb : function->GetBasicBlocks()) { 88 | auto meta_basic_blocks = 89 | ExtractMetaBasicBlocksFromBasicBlock(view, std::move(binja_bb), triton); 90 | std::move(std::begin(meta_basic_blocks), std::end(meta_basic_blocks), 91 | back_inserter(func_meta_basic_blocks)); 92 | } 93 | 94 | return func_meta_basic_blocks; 95 | } 96 | 97 | // Merge `MetaBasicBlock`s which are linked with single unconditional branches 98 | std::vector MergeMetaBasicBlocks( 99 | std::vector basic_blocks) { 100 | using BasicBlockMergeMap = 101 | std::unordered_map>; 102 | 103 | std::vector merged_meta_basic_blocks{}; 104 | 105 | // Populate the `"Binary Ninja" index -> MetaBasicBlock` map 106 | BasicBlockMergeMap binja_bb_map{}; 107 | for (auto& meta_bb : basic_blocks) { 108 | binja_bb_map[meta_bb.binja_bb()->GetIndex()] = 109 | std::make_pair(&meta_bb, false); 110 | } 111 | 112 | // Iterate over the `MetaBasicBlock`s 113 | for (auto cur_meta_bb : basic_blocks) { 114 | auto cur_bb_it = binja_bb_map.find(cur_meta_bb.binja_bb()->GetIndex()); 115 | if (cur_bb_it == std::end(binja_bb_map)) { 116 | LogError("The basic block index map isn't valid"); 117 | return {}; 118 | } 119 | auto& cur_bb_pair = cur_bb_it->second; 120 | if (cur_bb_pair.second) { 121 | // Basic block has been merged, ignore. 122 | continue; 123 | } 124 | 125 | // Iterate through unconditionally linked blocks and merge them until it's 126 | // not possible 127 | for (;;) { 128 | const std::vector& outgoing_egdes = 129 | cur_meta_bb.outgoing_edges(); 130 | 131 | const auto outgoing_edge_it = std::find_if( 132 | std::cbegin(outgoing_egdes), std::cend(outgoing_egdes), 133 | [](const BasicBlockEdge& edge) { 134 | return edge.type == BNBranchType::UnconditionalBranch && 135 | edge.target.GetPtr() != nullptr && 136 | edge.target->GetIncomingEdges().size() == 1; 137 | }); 138 | if (outgoing_edge_it == std::cend(outgoing_egdes)) { 139 | // No mergeable outgoing edge found, stop the merging process 140 | break; 141 | } 142 | 143 | auto target_bb_it = 144 | binja_bb_map.find(outgoing_edge_it->target->GetIndex()); 145 | if (target_bb_it == std::end(binja_bb_map)) { 146 | // Invalid target basic block, stop the merging process 147 | break; 148 | } 149 | 150 | auto& target_bb_pair = target_bb_it->second; 151 | if (target_bb_pair.second) { 152 | // Target has already been merged, stop the merging process 153 | LogDebug("Target already merged? Aborting"); 154 | break; 155 | } 156 | 157 | // Proceed with the merge 158 | MetaBasicBlock* target_meta_bb = target_bb_pair.first; 159 | MergeLinkedBasicBlocks(*outgoing_edge_it, cur_meta_bb, *target_meta_bb); 160 | // Mark basic block as merged 161 | target_bb_pair.second = true; 162 | } 163 | 164 | merged_meta_basic_blocks.emplace_back(std::move(cur_meta_bb)); 165 | } 166 | 167 | return merged_meta_basic_blocks; 168 | } 169 | 170 | // Merge two `MetaBasicBlock`s linked by a given edge 171 | static void MergeLinkedBasicBlocks(const BasicBlockEdge& edge, 172 | MetaBasicBlock& root_bb, 173 | MetaBasicBlock& target_bb) { 174 | triton::arch::BasicBlock& cur_triton_bb = root_bb.triton_bb(); 175 | triton::arch::BasicBlock& target_triton_bb = target_bb.triton_bb(); 176 | 177 | // Merge Triton's basic block 178 | const uint64_t instruction_count = cur_triton_bb.getSize(); 179 | if (instruction_count > 0) { 180 | const uint64_t last_instr_index = instruction_count - 1; 181 | const triton::arch::Instruction last_instr = 182 | cur_triton_bb.getInstructions()[last_instr_index]; 183 | // Remove last instruction if it's a `jmp` 184 | if (IsJumpInstruction(last_instr)) { 185 | LogDebug("jump detected: %s", last_instr.getDisassembly().c_str()); 186 | cur_triton_bb.remove(last_instr_index); 187 | } 188 | } 189 | // Merge Triton instructions into the current basic block 190 | for (triton::arch::Instruction& instr : target_triton_bb.getInstructions()) { 191 | cur_triton_bb.add(instr); 192 | } 193 | // Remove the merged edge 194 | root_bb.RemoveOutgoingEdge(edge); 195 | // Merge Binja's outgoing edges 196 | root_bb.AddOutgoingEdges(target_bb.outgoing_edges()); 197 | } 198 | 199 | static bool IsCallInstruction(const triton::arch::Instruction& instr) { 200 | switch (instr.getArchitecture()) { 201 | case triton::arch::ARCH_X86_64: 202 | case triton::arch::ARCH_X86: 203 | return instr.getDisassembly().find("call") == 0; 204 | case triton::arch::ARCH_AARCH64: 205 | // Match `bl` and `blr` 206 | return instr.getDisassembly().find("bl") == 0; 207 | default: 208 | return false; 209 | } 210 | } 211 | 212 | static bool IsJumpInstruction(const triton::arch::Instruction& instr) { 213 | switch (instr.getArchitecture()) { 214 | case triton::arch::ARCH_X86_64: 215 | case triton::arch::ARCH_X86: 216 | return instr.getDisassembly().find("jmp") == 0; 217 | case triton::arch::ARCH_AARCH64: { 218 | const std::string disassembly = instr.getDisassembly(); 219 | // Match `b` and `br` but not `bl` or `bl.XX`, so we add a space at the 220 | // end 221 | return disassembly.find("b ") == 0 || disassembly.find("br ") == 0; 222 | } 223 | default: 224 | return false; 225 | } 226 | } 227 | 228 | // Function inspired from Triton's DSE utility. 229 | // This function looks for instruction that behave like NOP instructions and 230 | // removes them from the given basic block and returns a new basic block as a 231 | // result. 232 | static triton::arch::BasicBlock RemoveNopLikeInstructions( 233 | const triton::Context& triton, const triton::arch::BasicBlock& triton_bb, 234 | bool padding) { 235 | triton::arch::BasicBlock in = triton_bb; 236 | triton::arch::BasicBlock out; 237 | 238 | triton::arch::Architecture arch; 239 | arch.setArchitecture(triton.getArchitecture()); 240 | const auto nop_instr = arch.getNopInstruction(); 241 | const auto& pc_reg = arch.getProgramCounter(); 242 | 243 | for (auto& instr : in.getInstructions()) { 244 | triton::Context tmp_ctx(triton.getArchitecture()); 245 | // Symbolize all registers 246 | for (auto& [reg_t, reg] : tmp_ctx.getAllRegisters()) { 247 | tmp_ctx.symbolizeRegister(reg); 248 | } 249 | // Concretize RIP 250 | const auto instruction_addr = instr.getAddress(); 251 | tmp_ctx.setConcreteRegisterValue(pc_reg, instruction_addr); 252 | 253 | // Execute instruction symbolically 254 | tmp_ctx.processing(instr); 255 | const auto post_instruction_addr = tmp_ctx.getConcreteRegisterValue(pc_reg); 256 | 257 | // Iterate over all symbolic expressions generated by the instruction and 258 | // keep only those which modified the CPU or memory state meaningfully 259 | std::vector 260 | effectual_symbolic_expressions; 261 | for (const auto& expr : instr.symbolicExpressions) { 262 | // Check for PC being assigned the value of the instruction located right 263 | // after the one we executed 264 | if (expr->getOriginRegister().getId() == pc_reg.getId()) { 265 | if (post_instruction_addr > instruction_addr && 266 | post_instruction_addr - instruction_addr == instr.getSize()) { 267 | // Instruction doesn't "jump around", ignore PC-related assignment 268 | continue; 269 | } 270 | } 271 | 272 | // Check for same-register assignments 273 | if (expr->isRegister()) { 274 | const auto& lhs_origin_reg = expr->getOriginRegister(); 275 | if (expr->getAst()->getType() == triton::ast::REFERENCE_NODE) { 276 | auto* reference_node = reinterpret_cast( 277 | expr->getAst().get()); 278 | const auto& rhs_origin_reg = 279 | reference_node->getSymbolicExpression()->getOriginRegister(); 280 | if (lhs_origin_reg.getId() == rhs_origin_reg.getId()) { 281 | // Both sides of the assignment contain the same symbolic register, 282 | // ignore 283 | continue; 284 | } 285 | } 286 | } 287 | 288 | effectual_symbolic_expressions.push_back(expr); 289 | } 290 | 291 | // Check instruction's side effects 292 | if (effectual_symbolic_expressions.empty()) { 293 | // Instruction has no side effects, get rid of it 294 | LogDebugF("NOP-like instruction removed: '{}'", instr.getDisassembly()); 295 | if (padding) { 296 | // Replace with a nop padding of the appropriate size 297 | size_t padding_size = 0; 298 | while (instr.getSize() > padding_size) { 299 | out.add(nop_instr); 300 | padding_size += nop_instr.getSize(); 301 | } 302 | } 303 | } else { 304 | // Instruction has side effects, keep it in the basic block 305 | out.add(instr); 306 | } 307 | } 308 | 309 | return out; 310 | } 311 | 312 | // Simplify the given `MetaBasicBlock`s with Triton's dead store elimination 313 | // pass 314 | std::vector SimplifyMetaBasicBlocks( 315 | const triton::Context& triton, std::vector basic_blocks, 316 | bool padding) { 317 | // Simplify basic blocks 318 | std::vector simplified_basic_blocks(basic_blocks.size()); 319 | { 320 | const auto triton_arch = triton.getArchitecture(); 321 | bool transform_failed = false; 322 | std::transform( 323 | std::begin(basic_blocks), std::end(basic_blocks), 324 | std::begin(simplified_basic_blocks), 325 | [&](MetaBasicBlock meta_bb) -> MetaBasicBlock { 326 | // Intialize Triton's context 327 | triton::Context triton{}; 328 | triton.setArchitecture(triton_arch); 329 | 330 | // Simplify basic blocks and disassemble the result 331 | try { 332 | auto simplified_triton_bb = 333 | triton.simplify(meta_bb.triton_bb(), padding); 334 | simplified_triton_bb = RemoveNopLikeInstructions( 335 | triton, simplified_triton_bb, padding); 336 | triton.disassembly(simplified_triton_bb, meta_bb.GetStart()); 337 | meta_bb.set_triton_bb(simplified_triton_bb); 338 | return std::move(meta_bb); 339 | } catch (triton::exceptions::Exception& ex) { 340 | LogError("Failed to simplify basic block: %s", ex.what()); 341 | transform_failed = true; 342 | return {}; 343 | } 344 | }); 345 | if (transform_failed) { 346 | LogError("Failed to simplify function"); 347 | return {}; 348 | } 349 | } 350 | 351 | // Regroup split simplified basic blocks 352 | std::unordered_map final_bbs_addr_map{}; 353 | std::vector final_basic_blocks{}; 354 | final_basic_blocks.reserve(simplified_basic_blocks.size()); 355 | for (MetaBasicBlock meta_bb : simplified_basic_blocks) { 356 | const uint64_t bb_addr = meta_bb.GetStart(); 357 | if (final_bbs_addr_map[bb_addr] == nullptr) { 358 | // Add to the list 359 | final_basic_blocks.emplace_back(std::move(meta_bb)); 360 | final_bbs_addr_map[bb_addr] = 361 | &final_basic_blocks[final_basic_blocks.size() - 1]; 362 | } else { 363 | // Regroup with the previous basic block 364 | MetaBasicBlock* previous_bb = final_bbs_addr_map[bb_addr]; 365 | for (auto& instr : meta_bb.triton_bb().getInstructions()) { 366 | previous_bb->triton_bb().add(instr); 367 | } 368 | } 369 | } 370 | 371 | return final_basic_blocks; 372 | } 373 | 374 | } // namespace triton_bn -------------------------------------------------------------------------------- /src/meta_basic_block.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace triton_bn { 11 | 12 | struct MetaBasicBlock { 13 | MetaBasicBlock() = default; 14 | explicit MetaBasicBlock(triton::arch::BasicBlock triton_bb, 15 | BinaryNinja::Ref binja_bb) 16 | : triton_bb_(triton_bb), 17 | binja_bb_(binja_bb), 18 | outgoing_edges_(binja_bb_->GetOutgoingEdges()) { 19 | assert(binja_bb_.GetPtr() != nullptr); 20 | } 21 | 22 | triton::arch::BasicBlock& triton_bb() { return triton_bb_; } 23 | void set_triton_bb(triton::arch::BasicBlock triton_bb) { 24 | triton_bb_ = std::move(triton_bb); 25 | } 26 | 27 | BinaryNinja::BasicBlock* binja_bb() { return binja_bb_; } 28 | 29 | const std::vector& outgoing_edges() const { 30 | return outgoing_edges_; 31 | } 32 | 33 | uint64_t GetStart() const { return binja_bb_->GetStart(); } 34 | std::vector GetIncomingEdges() const { 35 | return binja_bb_->GetIncomingEdges(); 36 | } 37 | 38 | void AddOutgoingEdges( 39 | std::vector outgoing_edges) { 40 | std::move(std::begin(outgoing_edges), std::end(outgoing_edges), 41 | back_inserter(outgoing_edges_)); 42 | } 43 | 44 | void RemoveOutgoingEdge(const BinaryNinja::BasicBlockEdge& edge_to_remove) { 45 | auto it = std::remove_if( 46 | std::begin(outgoing_edges_), std::end(outgoing_edges_), 47 | [&](BinaryNinja::BasicBlockEdge& edge) { 48 | if (edge.target.GetPtr() != nullptr) { 49 | return edge.target->GetIndex() == edge_to_remove.target->GetIndex(); 50 | } 51 | return false; 52 | }); 53 | outgoing_edges_.erase(it, std::end(outgoing_edges_)); 54 | } 55 | 56 | private: 57 | triton::arch::BasicBlock triton_bb_{}; 58 | BinaryNinja::Ref binja_bb_{}; 59 | std::vector outgoing_edges_{}; 60 | }; 61 | 62 | std::vector ExtractMetaBasicBlocksFromBasicBlock( 63 | BinaryNinja::BinaryView& view, 64 | BinaryNinja::Ref basic_block, 65 | triton::Context& triton); 66 | 67 | std::vector ExtractMetaBasicBlocksFromFunction( 68 | BinaryNinja::BinaryView& view, 69 | BinaryNinja::Ref function, triton::Context& triton); 70 | 71 | std::vector MergeMetaBasicBlocks( 72 | std::vector basic_blocks); 73 | 74 | std::vector SimplifyMetaBasicBlocks( 75 | const triton::Context& triton, std::vector basic_blocks, 76 | bool padding = false); 77 | 78 | } // namespace triton_bn 79 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(triton_simplify_test "triton_simplify_test.cc") 2 | target_link_libraries(triton_simplify_test PRIVATE triton::triton) 3 | -------------------------------------------------------------------------------- /tests/triton_simplify_test.cc: -------------------------------------------------------------------------------- 1 | // Code taken and adapted from Triton's `dead_store_elimination.py` unit test. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define TRITON_INSTR_STR(STR) \ 10 | triton::arch::Instruction { (uint8_t*)STR, (uint32_t)(sizeof(STR) - 1) } 11 | 12 | static void bb_test(uint64_t bb_addr, 13 | std::function get_bb, 14 | triton::arch::architecture_e triton_arch) { 15 | /* Init the triton context */ 16 | triton::API triton{}; 17 | triton.setArchitecture(triton_arch); 18 | 19 | // Process test case 20 | auto bb = get_bb(); 21 | // Disassemble instructions 22 | try { 23 | triton.disassembly(bb, bb_addr); 24 | { 25 | std::ostringstream ostr; 26 | ostr << bb; 27 | printf("Original:\n%s\n", ostr.str().c_str()); 28 | } 29 | 30 | // Simply basic block 31 | auto simplified_bb = triton.simplify(bb); 32 | 33 | // Check result 34 | triton.disassembly(simplified_bb, bb_addr); 35 | { 36 | std::ostringstream ostr; 37 | ostr << simplified_bb; 38 | printf("Simplified:\n%s\n", ostr.str().c_str()); 39 | } 40 | } catch (triton::exceptions::Exception& ex) { 41 | printf("Test failed: %s\n", ex.what()); 42 | return; 43 | } 44 | } 45 | 46 | // Code from VMProtect 47 | constexpr uint64_t kBB1Address = 0x140004149; 48 | static triton::arch::BasicBlock get_bb1() { 49 | return {{ 50 | TRITON_INSTR_STR("\x66\xd3\xd7"), // rcl di, cl 51 | TRITON_INSTR_STR("\x58"), // pop rax 52 | TRITON_INSTR_STR("\x66\x41\x0f\xa4\xdb\x01"), // shld r11w, bx, 1 53 | TRITON_INSTR_STR("\x41\x5b"), // pop r11 54 | TRITON_INSTR_STR("\x80\xe6\xca"), // and dh, 0CAh 55 | TRITON_INSTR_STR("\x66\xf7\xd7"), // not di 56 | TRITON_INSTR_STR("\x5f"), // pop rdi 57 | TRITON_INSTR_STR("\x66\x41\xc1\xc1\x0c"), // rol r9w, 0Ch 58 | TRITON_INSTR_STR("\xf9"), // stc 59 | TRITON_INSTR_STR("\x41\x58"), // pop r8 60 | TRITON_INSTR_STR("\xf5"), // cmc 61 | TRITON_INSTR_STR("\xf8"), // clc 62 | TRITON_INSTR_STR("\x66\x41\xc1\xe1\x0b"), // shl r9w, 0Bh 63 | TRITON_INSTR_STR("\x5a"), // pop rdx 64 | TRITON_INSTR_STR("\x66\x81\xf9\xeb\xd2"), // cmp cx, 0D2EBh 65 | TRITON_INSTR_STR("\x48\x0f\xa3\xf1"), // bt rcx, rsi 66 | TRITON_INSTR_STR("\x41\x59"), // pop r9 67 | TRITON_INSTR_STR("\x66\x41\x21\xe2"), // and r10w, sp 68 | TRITON_INSTR_STR("\x41\xc1\xd2\x10"), // rcl r10d, 10h 69 | TRITON_INSTR_STR("\x41\x5a"), // pop r10 70 | TRITON_INSTR_STR("\x66\x0f\xba\xf9\x0c"), // btc cx, 0Ch 71 | TRITON_INSTR_STR("\x49\x0f\xcc"), // bswap r12 72 | TRITON_INSTR_STR( 73 | "\x48\x3d\x97\x74\x7d\xc7"), // cmp rax, 0FFFFFFFFC77D7497h 74 | TRITON_INSTR_STR("\x41\x5c"), // pop r12 75 | TRITON_INSTR_STR("\x66\xd3\xc1"), // rol cx, cl 76 | TRITON_INSTR_STR("\xf5"), // cmc 77 | TRITON_INSTR_STR("\x66\x0f\xba\xf5\x01"), // btr bp, 1 78 | TRITON_INSTR_STR("\x66\x41\xd3\xfe"), // sar r14w, cl 79 | TRITON_INSTR_STR("\x5d"), // pop rbp 80 | TRITON_INSTR_STR("\x66\x41\x29\xf6"), // sub r14w, si 81 | TRITON_INSTR_STR("\x66\x09\xf6"), // or si, si 82 | TRITON_INSTR_STR("\x01\xc6"), // add esi, eax 83 | TRITON_INSTR_STR("\x66\x0f\xc1\xce"), // xadd si, cx 84 | TRITON_INSTR_STR("\x9d"), // popfq 85 | TRITON_INSTR_STR("\x0f\x9f\xc1"), // setnle cl 86 | TRITON_INSTR_STR("\x0f\x9e\xc1"), // setle cl 87 | TRITON_INSTR_STR("\x4c\x0f\xbe\xf0"), // movsx r14, al 88 | TRITON_INSTR_STR("\x59"), // pop rcx 89 | TRITON_INSTR_STR("\xf7\xd1"), // not ecx 90 | TRITON_INSTR_STR("\x59"), // pop rcx 91 | TRITON_INSTR_STR( 92 | "\x4c\x8d\xa8\xed\x19\x28\xc9"), // lea r13, [rax - 36D7E613h] 93 | TRITON_INSTR_STR("\x66\xf7\xd6"), // not si 94 | TRITON_INSTR_STR("\x41\x5e"), // pop r14 95 | TRITON_INSTR_STR("\x66\xf7\xd6"), // not si 96 | TRITON_INSTR_STR("\x66\x44\x0f\xbe\xea"), // movsx r13w, dl 97 | TRITON_INSTR_STR("\x41\xbd\xb2\x6b\x48\xb7"), // mov r13d, 0B7486BB2h 98 | TRITON_INSTR_STR("\x5e"), // pop rsi 99 | TRITON_INSTR_STR("\x66\x41\xbd\xca\x44"), // mov r13w, 44CAh 100 | TRITON_INSTR_STR( 101 | "\x4c\x8d\xab\x31\x11\x63\x14"), // lea r13, [rbx + 14631131h] 102 | TRITON_INSTR_STR("\x41\x0f\xcd"), // bswap r13d 103 | TRITON_INSTR_STR("\x41\x5d"), // pop r13 104 | TRITON_INSTR_STR("\xc3"), // ret 105 | }}; 106 | } 107 | 108 | constexpr uint64_t kBB2Address = 0x0042fed1; 109 | static triton::arch::BasicBlock get_bb2() { 110 | return {{ 111 | TRITON_INSTR_STR("\x03\x00"), // add eax, dword [eax] 112 | TRITON_INSTR_STR("\x00\x5a\x00"), // add byte [edx], bl 113 | TRITON_INSTR_STR("\x00\x11"), // add byte [ecx], dl 114 | TRITON_INSTR_STR("\x20\x3c\x51"), // and byte [ecx+edx*2], bh 115 | TRITON_INSTR_STR("\x58"), // pop eax 116 | TRITON_INSTR_STR("\x21\x0a"), // and dword [edx], ecx 117 | TRITON_INSTR_STR("\x06"), // push es 118 | TRITON_INSTR_STR("\x20\x78\x4f"), // and byte [eax+0x4f], bh 119 | TRITON_INSTR_STR("\xcc") // int3 120 | }}; 121 | } 122 | 123 | constexpr uint64_t kBB3Address = 0x00026db0; 124 | static triton::arch::BasicBlock get_bb3() { 125 | return {{ 126 | TRITON_INSTR_STR("\xf3\x0f\x1e\xfa"), // endbr64 127 | TRITON_INSTR_STR( 128 | "\x48\x8b\x05\xc5\xe0\x03\x00"), // mov rax, qword [rel 129 | // _vtable_for_QSvgRect] 130 | TRITON_INSTR_STR("\x55"), // push rbp 131 | TRITON_INSTR_STR("\x48\x89\xfd"), // mov rbp, rdi 132 | TRITON_INSTR_STR("\x48\x83\xc0\x10"), // add rax, 0x10 133 | TRITON_INSTR_STR("\x48\x89\x07"), // mov qword [rdi], rax 134 | TRITON_INSTR_STR("\xe8\x35\xf2\x01\x00"), // call QSvgNode::~QSvgNode 135 | TRITON_INSTR_STR("\x48\x89\xef"), // mov rdi, rbp 136 | TRITON_INSTR_STR("\xbe\x80\x01\x00\x00"), // mov esi, 0x180 137 | TRITON_INSTR_STR("\x5d"), // pop rbp 138 | TRITON_INSTR_STR("\xe9\xc7\x0e\xff\xff") // jmp operator delete 139 | }}; 140 | } 141 | 142 | int main(int argc, char* argv[]) { 143 | std::printf("TritonSimplifyTest\n"); 144 | 145 | bb_test(kBB1Address, get_bb1, triton::arch::ARCH_X86_64); 146 | bb_test(kBB2Address, get_bb2, triton::arch::ARCH_X86); 147 | bb_test(kBB3Address, get_bb3, triton::arch::ARCH_X86_64); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /thirdparty/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory("binaryninja-api") 2 | -------------------------------------------------------------------------------- /thirdparty/binaryninja-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | if(TRITON_BN_BINARYNINJA_CHANNEL STREQUAL "stable") 4 | set(_triton_bn_binaryninjacore_suffix "_stable") 5 | set(_triton_bn_binaryninja_git_tag "v4.0.4958-stable") 6 | else() 7 | set(_triton_bn_binaryninjacore_suffix "") 8 | set(_triton_bn_binaryninja_git_tag 9 | "68ac3ce7b8442c4a5bcc8259dd1fa156cdcefa03") # 2024-04-05 10 | endif() 11 | FetchContent_Declare(binaryninjaapi 12 | GIT_REPOSITORY https://github.com/Vector35/binaryninja-api.git 13 | GIT_TAG ${_triton_bn_binaryninja_git_tag} 14 | ) 15 | FetchContent_GetProperties(binaryninjaapi) 16 | if(NOT binaryninjaapi_POPULATED) 17 | FetchContent_Populate(binaryninjaapi) # For binaryninjaapi_SOURCE_DIR 18 | endif() 19 | add_library(binaryninjacore SHARED 20 | binaryninjacore${_triton_bn_binaryninjacore_suffix}.cc 21 | ) 22 | set_target_properties(binaryninjacore PROPERTIES 23 | SOVERSION 1 24 | ) 25 | target_include_directories(binaryninjacore PRIVATE 26 | "${binaryninjaapi_SOURCE_DIR}" 27 | ) 28 | set(CORE_LIBRARY binaryninjacore) 29 | set(BN_CORE_LIBRARY "${CORE_LIBRARY}") 30 | set(HEADLESS TRUE) 31 | if(binaryninjaapi_POPULATED) 32 | add_subdirectory("${binaryninjaapi_SOURCE_DIR}" "${binaryninjaapi_BINARY_DIR}") 33 | endif() 34 | add_library(BinaryNinja::API ALIAS binaryninjaapi) -------------------------------------------------------------------------------- /thirdparty/binaryninja-api/regenerate-api-stubs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2011-2024 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Updates the Binary Ninja API stub file 17 | 18 | # Exit on error 19 | set -e 20 | 21 | # Portable "readlink -e" 22 | canonical_path() 23 | ( 24 | file=$1 25 | cd $(dirname "$file") 26 | file=$(basename "$file") 27 | limit=0 28 | while [ -L "$file" -a $limit -lt 1000 ]; do 29 | file=$(readlink "$file") 30 | cd $(dirname "$file") 31 | file=$(basename "$file") 32 | limit=$(($limit+1)) 33 | done 34 | echo "$(pwd -P)/$file" 35 | ) 36 | 37 | THIS=$(basename "$0") 38 | THIS_DIR=$(dirname "$(canonical_path "$0")") 39 | 40 | if [ -z "$1" ]; then 41 | echo "usage: ${THIS} PATH_TO_BINARYNINJA_API" 2>&1 42 | exit 1 43 | fi 44 | 45 | BINARYNINJA_API_SRC=$(canonical_path "$1") 46 | 47 | cd "${THIS_DIR}" 48 | 49 | ( 50 | cat < 68 | #include 69 | 70 | // clang-format off 71 | #include "exceptions.h" // NOLINT 72 | #define BINARYNINJACORE_LIBRARY 73 | #include "binaryninjacore.h" // NOLINT 74 | // clang-format on 75 | 76 | extern "C" { 77 | EOF 78 | # The following extracts all C API functions from the Binary Ninja API 79 | # and creates empty stub functions for them. This is later compiled 80 | # into an API compatible library that is used to link against. This 81 | # avoids having to use install a copy of Binary Ninja on the machine 82 | # that builds BinExport. 83 | # Note that the script line below can be replaced with a small Clang 84 | # tool to make it more robust. 85 | cat "${BINARYNINJA_API_SRC}/binaryninjacore.h" | \ 86 | sed ' 87 | /^[[:space:]]*__attribute__/d; 88 | s,//.*$,, 89 | ' | \ 90 | perl -pe 's/void\n/void/igs' | \ 91 | clang-format --style='{BasedOnStyle: Google, Language: Cpp, ColumnLimit: 100000}' | \ 92 | grep ^BINARYNINJACOREAPI | \ 93 | sed ' 94 | s,\(BINARYNINJACOREAPI void .*\);,\1 {},; 95 | s,\(BINARYNINJACOREAPI .*\);,\1 { return {}; }, 96 | ' 97 | cat < binaryninjacore.cc -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", 3 | "name": "triton-bn", 4 | "version": "0.2.0", 5 | "description": "Binary Ninja plugin that can be used to apply Triton's dead store eliminitation pass on basic blocks or functions.", 6 | "homepage": "https://github.com/ergrelet/triton-bn", 7 | "license": "Apache-2.0", 8 | "supports": "!arm", 9 | "builtin-baseline": "a34c873a9717a888f58dc05268dea15592c2f0ff", 10 | "dependencies": [ 11 | { 12 | "name": "triton", 13 | "version>=": "2023-08-16" 14 | } 15 | ] 16 | } 17 | --------------------------------------------------------------------------------