diff --git a/.clang-format b/.clang-format deleted file mode 100644 index 2a81fb8..0000000 --- a/.clang-format +++ /dev/null @@ -1,86 +0,0 @@ ---- -AccessModifierOffset: -4 -AlignAfterOpenBracket: DontAlign -AlignConsecutiveMacros: Consecutive -AlignConsecutiveAssignments: None -AlignConsecutiveDeclarations: None -AlignEscapedNewlines: DontAlign -AlignOperands: false -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: Inline -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: 'Yes' -BinPackArguments: true -BinPackParameters: true -BreakBeforeBraces: Custom -BraceWrapping: - AfterCaseLabel: true - AfterClass: true - AfterControlStatement: 'true' - AfterEnum: true - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - AfterExternBlock: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon -BreakInheritanceList: AfterColon -ColumnLimit: 120 -CompactNamespaces: true -ConstructorInitializerAllOnOneLineOrOnePerLine: false -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -EmptyLineBeforeAccessModifier: Always -FixNamespaceComments: true -IncludeBlocks: Preserve -IndentCaseLabels: false -IndentPPDirectives: BeforeHash -IndentWrappedFunctionNames: true -KeepEmptyLinesAtTheStartOfBlocks: false -MaxEmptyLinesToKeep: 2 -NamespaceIndentation: All -PenaltyIndentedWhitespace: 100 -PointerAlignment: Left -SortIncludes: Never -SortUsingDeclarations: true -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: true -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 2 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false -SpacesInSquareBrackets: false -IndentWidth: 4 -TabWidth: 4 -UseTab: AlignWithSpaces - -... \ No newline at end of file diff --git a/.github/4.0_0001-Support-building-plugins-without-an-install.patch b/.github/4.0_0001-Support-building-plugins-without-an-install.patch deleted file mode 100644 index 53ea71e..0000000 --- a/.github/4.0_0001-Support-building-plugins-without-an-install.patch +++ /dev/null @@ -1,216 +0,0 @@ -From 6c9f6915db3d578227194f737728dc3165115d39 Mon Sep 17 00:00:00 2001 -From: Glenn Smith -Date: Thu, 4 Apr 2024 21:49:55 -0700 -Subject: [PATCH 1/1] Support building plugins without an install - ---- - CMakeLists.txt | 56 ++++++++++++++----- - cmake/generate_stubs.py | 121 ++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 163 insertions(+), 14 deletions(-) - create mode 100644 cmake/generate_stubs.py - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 2c543f85..621ff15e 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -30,10 +30,36 @@ add_library(binaryninjaapi STATIC ${BN_API_SOURCES}) - target_include_directories(binaryninjaapi - PUBLIC ${PROJECT_SOURCE_DIR}) - --find_package(BinaryNinjaCore REQUIRED) --target_link_libraries(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARIES}) --target_link_directories(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARY_DIRS}) --target_compile_definitions(binaryninjaapi PUBLIC ${BinaryNinjaCore_DEFINITIONS}) -+find_package(BinaryNinjaCore) -+if(BinaryNinjaCore_FOUND) -+ target_link_libraries(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARIES}) -+ target_link_directories(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARY_DIRS}) -+ target_compile_definitions(binaryninjaapi PUBLIC ${BinaryNinjaCore_DEFINITIONS}) -+else() -+ if(APPLE) -+ target_link_options(binaryninjaapi PUBLIC -undefined dynamic_lookup) -+ elseif(MSVC) -+ # Generate stubs.cpp with implementations of all the BNAPI functions -+ execute_process(COMMAND python ${PROJECT_SOURCE_DIR}/cmake/generate_stubs.py ${PROJECT_SOURCE_DIR}/binaryninjacore.h ${PROJECT_BINARY_DIR}/stubs) -+ -+ # Compile those stubs into a stub library we can use to fool the linker -+ add_library(binaryninjacore_stubs SHARED ${PROJECT_BINARY_DIR}/stubs/stubs.cpp) -+ set_target_properties(binaryninjacore_stubs -+ PROPERTIES OUTPUT_NAME binaryninjacore -+ SOVERSION 1 -+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ ) -+ target_include_directories(binaryninjacore_stubs PUBLIC ${PROJECT_SOURCE_DIR}) -+ -+ # Be sure to only link against the stubs archive file -+ add_dependencies(binaryninjaapi binaryninjacore_stubs) -+ target_link_libraries(binaryninjaapi PUBLIC "$/$.lib") -+ else() -+ target_link_options(binaryninjaapi PUBLIC "LINKER:--allow-shlib-undefined") -+ endif() -+endif() - - add_subdirectory(vendor/fmt EXCLUDE_FROM_ALL) - set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON) -@@ -87,16 +113,18 @@ function(bn_install_plugin target) - list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake") - - # BinaryNinjaCore has the user plugins dir define that we want -- find_package(BinaryNinjaCore REQUIRED) -- if(WIN32) -- install(TARGETS ${target} RUNTIME -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -- -- install(FILES $ -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR} OPTIONAL) -- else() -- install(TARGETS ${target} LIBRARY -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ find_package(BinaryNinjaCore) -+ if(BinaryNinjaCore_FOUND) -+ if(WIN32) -+ install(TARGETS ${target} RUNTIME -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ -+ install(FILES $ -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR} OPTIONAL) -+ else() -+ install(TARGETS ${target} LIBRARY -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ endif() - endif() - endif() - endfunction() -diff --git a/cmake/generate_stubs.py b/cmake/generate_stubs.py -new file mode 100644 -index 00000000..4e062037 ---- /dev/null -+++ b/cmake/generate_stubs.py -@@ -0,0 +1,121 @@ -+# Copyright (c) 2015-2024 Vector 35 Inc -+# -+# Permission is hereby granted, free of charge, to any person obtaining a copy -+# of this software and associated documentation files (the "Software"), to -+# deal in the Software without restriction, including without limitation the -+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+# sell copies of the Software, and to permit persons to whom the Software is -+# furnished to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in -+# all copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+# IN THE SOFTWARE. -+ -+# Based on BinExport -+ -+# Copyright 2011-2024 Google LLC -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# https://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+ -+import argparse -+import os -+import re -+import sys -+ -+parser = argparse.ArgumentParser(sys.argv[0]) -+parser.add_argument("core_header") -+parser.add_argument("build_dir") -+ -+options = parser.parse_args() -+ -+print(f"GENERATE STUBS: {options.core_header} -> {options.build_dir}") -+print(f"{sys.executable} {' '.join(sys.argv)}") -+ -+os.makedirs(options.build_dir, exist_ok=True) -+output_source = os.path.join(options.build_dir, 'stubs.cpp') -+ -+with open(output_source, 'w') as stubs: -+ stubs.write(""" -+// Copyright (c) 2015-2024 Vector 35 Inc -+// -+// Permission is hereby granted, free of charge, to any person obtaining a copy -+// of this software and associated documentation files (the "Software"), to -+// deal in the Software without restriction, including without limitation the -+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+// sell copies of the Software, and to permit persons to whom the Software is -+// furnished to do so, subject to the following conditions: -+// -+// The above copyright notice and this permission notice shall be included in -+// all copies or substantial portions of the Software. -+// -+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+// IN THE SOFTWARE. -+ -+// Based on BinExport -+ -+// Copyright 2011-2024 Google LLC -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// https://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+ -+#define BINARYNINJACORE_LIBRARY -+#include "binaryninjacore.h" -+#undef BINARYNINJACORE_LIBRARY -+ -+extern "C" { -+""") -+ -+ with open(options.core_header, 'r') as header: -+ header_conts = header.read() -+ -+ # Quick preprocess of comments in case we have commented defs -+ header_conts = re.sub(r'//.*\n', '\n', header_conts) -+ -+ # Pull out all core api functions to generate stubs -+ for match in re.finditer( -+ r'(?m:)\t(BINARYNINJACOREAPI [^;]*);', -+ header_conts -+ ): -+ group = match.group(1) -+ -+ # Void functions don't have a return (TODO: breaks if we ever return a fn ptr) -+ void_group = re.search(r'(?m:)BINARYNINJACOREAPI\s+void\s+.*', group) -+ if void_group is not None: -+ stubs.write(f'{match.group(1)} {{ }}\n') -+ else: -+ stubs.write(f'{match.group(1)} {{ return {{ }}; }}\n') -+ -+ stubs.write(""" -+} -+ """) -\ No newline at end of file --- -2.44.0.windows.1 - diff --git a/.github/4.1_0001-Support-building-plugins-without-an-install.patch b/.github/4.1_0001-Support-building-plugins-without-an-install.patch deleted file mode 100644 index 96fe152..0000000 --- a/.github/4.1_0001-Support-building-plugins-without-an-install.patch +++ /dev/null @@ -1,216 +0,0 @@ -From 8071d66a654cbbf193b65813ab35d4b894f428b4 Mon Sep 17 00:00:00 2001 -From: Glenn Smith -Date: Thu, 4 Apr 2024 21:49:55 -0700 -Subject: [PATCH] Support building plugins without an install - ---- - CMakeLists.txt | 56 ++++++++++++++----- - cmake/generate_stubs.py | 121 ++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 163 insertions(+), 14 deletions(-) - create mode 100644 cmake/generate_stubs.py - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index 57ed627e..0a434a5d 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -33,10 +33,36 @@ add_library(binaryninjaapi STATIC ${BN_API_SOURCES}) - target_include_directories(binaryninjaapi - PUBLIC ${PROJECT_SOURCE_DIR}) - --find_package(BinaryNinjaCore REQUIRED) --target_link_libraries(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARIES}) --target_link_directories(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARY_DIRS}) --target_compile_definitions(binaryninjaapi PUBLIC ${BinaryNinjaCore_DEFINITIONS}) -+find_package(BinaryNinjaCore) -+if(BinaryNinjaCore_FOUND) -+ target_link_libraries(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARIES}) -+ target_link_directories(binaryninjaapi PUBLIC ${BinaryNinjaCore_LIBRARY_DIRS}) -+ target_compile_definitions(binaryninjaapi PUBLIC ${BinaryNinjaCore_DEFINITIONS}) -+else() -+ if(APPLE) -+ target_link_options(binaryninjaapi PUBLIC -undefined dynamic_lookup) -+ elseif(MSVC) -+ # Generate stubs.cpp with implementations of all the BNAPI functions -+ execute_process(COMMAND python ${PROJECT_SOURCE_DIR}/cmake/generate_stubs.py ${PROJECT_SOURCE_DIR}/binaryninjacore.h ${PROJECT_BINARY_DIR}/stubs) -+ -+ # Compile those stubs into a stub library we can use to fool the linker -+ add_library(binaryninjacore_stubs SHARED ${PROJECT_BINARY_DIR}/stubs/stubs.cpp) -+ set_target_properties(binaryninjacore_stubs -+ PROPERTIES OUTPUT_NAME binaryninjacore -+ SOVERSION 1 -+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/stubs -+ ) -+ target_include_directories(binaryninjacore_stubs PUBLIC ${PROJECT_SOURCE_DIR}) -+ -+ # Be sure to only link against the stubs archive file -+ add_dependencies(binaryninjaapi binaryninjacore_stubs) -+ target_link_libraries(binaryninjaapi PUBLIC "$/$.lib") -+ else() -+ target_link_options(binaryninjaapi PUBLIC "LINKER:--allow-shlib-undefined") -+ endif() -+endif() - - if(BN_REF_COUNT_DEBUG) - target_compile_definitions(binaryninjaapi PUBLIC BN_REF_COUNT_DEBUG) -@@ -94,16 +120,18 @@ function(bn_install_plugin target) - list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake") - - # BinaryNinjaCore has the user plugins dir define that we want -- find_package(BinaryNinjaCore REQUIRED) -- if(WIN32) -- install(TARGETS ${target} RUNTIME -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -- -- install(FILES $ -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR} OPTIONAL) -- else() -- install(TARGETS ${target} LIBRARY -- DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ find_package(BinaryNinjaCore) -+ if(BinaryNinjaCore_FOUND) -+ if(WIN32) -+ install(TARGETS ${target} RUNTIME -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ -+ install(FILES $ -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR} OPTIONAL) -+ else() -+ install(TARGETS ${target} LIBRARY -+ DESTINATION ${BinaryNinjaCore_USER_PLUGINS_DIR}) -+ endif() - endif() - endif() - endfunction() -diff --git a/cmake/generate_stubs.py b/cmake/generate_stubs.py -new file mode 100644 -index 00000000..48ab574f ---- /dev/null -+++ b/cmake/generate_stubs.py -@@ -0,0 +1,121 @@ -+# Copyright (c) 2015-2024 Vector 35 Inc -+# -+# Permission is hereby granted, free of charge, to any person obtaining a copy -+# of this software and associated documentation files (the "Software"), to -+# deal in the Software without restriction, including without limitation the -+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+# sell copies of the Software, and to permit persons to whom the Software is -+# furnished to do so, subject to the following conditions: -+# -+# The above copyright notice and this permission notice shall be included in -+# all copies or substantial portions of the Software. -+# -+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+# IN THE SOFTWARE. -+ -+# Based on BinExport -+ -+# Copyright 2011-2024 Google LLC -+# -+# Licensed under the Apache License, Version 2.0 (the "License"); -+# you may not use this file except in compliance with the License. -+# You may obtain a copy of the License at -+# -+# https://www.apache.org/licenses/LICENSE-2.0 -+# -+# Unless required by applicable law or agreed to in writing, software -+# distributed under the License is distributed on an "AS IS" BASIS, -+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+# See the License for the specific language governing permissions and -+# limitations under the License. -+ -+import argparse -+import os -+import re -+import sys -+ -+parser = argparse.ArgumentParser(sys.argv[0]) -+parser.add_argument("core_header") -+parser.add_argument("build_dir") -+ -+options = parser.parse_args() -+ -+print(f"GENERATE STUBS: {options.core_header} -> {options.build_dir}") -+print(f"{sys.executable} {' '.join(sys.argv)}") -+ -+os.makedirs(options.build_dir, exist_ok=True) -+output_source = os.path.join(options.build_dir, 'stubs.cpp') -+ -+with open(output_source, 'w') as stubs: -+ stubs.write(""" -+// Copyright (c) 2015-2024 Vector 35 Inc -+// -+// Permission is hereby granted, free of charge, to any person obtaining a copy -+// of this software and associated documentation files (the "Software"), to -+// deal in the Software without restriction, including without limitation the -+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -+// sell copies of the Software, and to permit persons to whom the Software is -+// furnished to do so, subject to the following conditions: -+// -+// The above copyright notice and this permission notice shall be included in -+// all copies or substantial portions of the Software. -+// -+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -+// IN THE SOFTWARE. -+ -+// Based on BinExport -+ -+// Copyright 2011-2024 Google LLC -+// -+// Licensed under the Apache License, Version 2.0 (the "License"); -+// you may not use this file except in compliance with the License. -+// You may obtain a copy of the License at -+// -+// https://www.apache.org/licenses/LICENSE-2.0 -+// -+// Unless required by applicable law or agreed to in writing, software -+// distributed under the License is distributed on an "AS IS" BASIS, -+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+// See the License for the specific language governing permissions and -+// limitations under the License. -+ -+#define BINARYNINJACORE_LIBRARY -+#include "binaryninjacore.h" -+#undef BINARYNINJACORE_LIBRARY -+ -+extern "C" { -+""") -+ -+ with open(options.core_header, 'r') as header: -+ header_conts = header.read() -+ -+ # Quick preprocess of comments in case we have commented defs -+ header_conts = re.sub(r'//.*\n', '\n', header_conts) -+ -+ # Pull out all core api functions to generate stubs -+ for match in re.finditer( -+ r'(?m:)\t(BINARYNINJACOREAPI [^;]*);', -+ header_conts -+ ): -+ group = match.group(1) -+ -+ # Void functions don't have a return (TODO: breaks if we ever return a fn ptr) -+ void_group = re.search(r'(?m:)BINARYNINJACOREAPI\s+void\s+.*', group) -+ if void_group is not None: -+ stubs.write(f'{match.group(1)} {{ }}\n') -+ else: -+ stubs.write(f'{match.group(1)} {{ return {{ }}; }}\n') -+ -+ stubs.write(""" -+} -+ """) -\ No newline at end of file --- -2.43.3 - diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1bdf7a8..acccb59 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,15 +15,8 @@ jobs: strategy: matrix: api-version: - - ref: b7c8e9bfbae0eec8d7be47cd42f61c14542cc28c - abi: 54 - patch-file: 4.0_0001-Support-building-plugins-without-an-install.patch - - ref: 68ac3ce7b8442c4a5bcc8259dd1fa156cdcefa03 - abi: 58 - patch-file: 4.1_0001-Support-building-plugins-without-an-install.patch - - ref: cb4f924f561769b4ff7597d266664c5136e66ccb - abi: 59 - patch-file: 4.1_0001-Support-building-plugins-without-an-install.patch + - ref: 52c4e5695f9c86cb319f07682c27be9e4e576559 + abi: 78 platform: - name: ubuntu binary: libbinja-msvc.so @@ -58,10 +51,6 @@ jobs: vendor examples - - name: Apply binaryninja-api patch - working-directory: binaryninja-api - run: git apply --verbose ../.github/${{matrix.api-version.patch-file}} - - name: Build run: | cmake --preset ${{env.CONFIGURE_PRESET}} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5434285..a3b2100 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -34,7 +34,7 @@ jobs: level: ${{ github.event.inputs.level }} - name: Bump plugin.json version run: | - jq --rawfile readme README.md '.version = "${{ steps.semver.outputs.new_version }}" | .longdescription = $readme' plugin.json > plugin.json.tmp + jq --rawfile readme README.md '.version = "${{ steps.semver.outputs.new_version }}"' plugin.json > plugin.json.tmp mv plugin.json.tmp plugin.json - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v5 diff --git a/.gitignore b/.gitignore index 294a7b2..b0a9893 100644 --- a/.gitignore +++ b/.gitignore @@ -372,4 +372,8 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd + +cmake-build-debug +.idea +.DS_STORE \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6995a85..9d7c2fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,6 @@ project( LANGUAGES CXX ) -set(HEADLESS 1) find_path( BN_API_PATH NAMES binaryninjaapi.h diff --git a/CMakePresets.json b/CMakePresets.json index 7216ca9..ecebf29 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -43,7 +43,11 @@ }, "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/" + "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/install/", + "BN_INTERNAL_BUILD": "TRUE" + }, + "environment": { + "BN_INTERNAL_BUILD": "TRUE" } } ] diff --git a/README.md b/README.md index 53d0b69..44b09c1 100644 --- a/README.md +++ b/README.md @@ -2,90 +2,102 @@ Parses and symbolizes MSVC RTTI information in [Binary Ninja]. -## Example Virtual Function Table Listing +## Example Complete Object Locator -Arguably the most import function of symbolizing RTTI information is the virtual function tables. The listing below is the symbolized view of `simple.cpp` (found in test\bins). +This analysis can be triggered with the `MSVC\\Find RTTI` command. ```cpp -void* data_140010320 = ParentA::`RTTI Complete Object Locator -struct ParentA::VTable ParentA::`vftable = -{ - void* (* const vFunc_0)(void* arg1, int32_t arg2) = ParentB::vFunc_0 - int64_t (* const vFunc_1)() __pure = ParentA::vFunc_1 - int64_t (* const vFunc_2)() __pure = ParentA::vFunc_2 -} -void* data_140010340 = ParentB::`RTTI Complete Object Locator -struct ParentB::VTable ParentB::`vftable = -{ - void* (* const vFunc_0)(void* arg1, int32_t arg2) = ParentB::vFunc_0 - int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1 -} -void* data_140010358 = SomeClass::`RTTI Complete Object Locator -struct SomeClass::VTable SomeClass::`vftable = +struct _RTTICompleteObjectLocator MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'} = { - void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0 - int64_t (* const vFunc_1)() __pure = ParentA::vFunc_1 - int64_t (* const vFunc_2)() __pure = ParentA::vFunc_2 - int64_t (* const vFunc_3)() __pure = SomeClass::vFunc_3 -} -void* data_140010380 = SomeClass::`RTTI Complete Object Locator{for `ParentB} -struct ParentB::VTable SomeClass::`vftable{for `ParentB} = -{ - void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0 - int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1 + enum signature = COL_SIG_REV1 + uint32_t offset = 0x10 + uint32_t cdOffset = 0x0 + void* __based(start) pTypeDescriptor = class MapTrackView `RTTI Type Descriptor' {__dos_header + 0x2071e8} + struct _RTTIClassHierarchyDescriptor* __based(start) pClassHierarchyDescriptor = MapTrackView::`RTTI Class Hierarchy Descriptor' {__dos_header + 0x1c6128} + void* __based(start) pSelf = MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'} {__dos_header + 0x1c61a0} } ``` -## Example Constructor Listing +_The above listing includes type information deduced seperately through demangled names_ -Based off the information collected from the RTTI scan, we can deduce constructors and create types and symbolize their structures. Using the [type inheritence](https://binary.ninja/2023/05/03/3.4-finally-freed.html#inherited-types) in [Binary Ninja] we can make these types easily composable. The listing below shows the fully symbolized constructor function for `Bird` in `overrides.cpp` (found in test\bins), as well as the accompanying auto created type. +## Example Virtual Function Table Listing + +This analysis can be triggered with the `MSVC\\Find VFTs` command. ```cpp -class __base(Animal, 0) __base(Flying, 0) Bird -{ - struct `Bird::VTable`* vtable; - char const* field_8; - struct `Flying::VTable`* vtable_Flying; - int32_t field_18; - __padding char _1C[4]; - int32_t field_20; -}; - -class Bird* Bird::Bird(class Bird* this, int32_t arg2) +void* data_14013bfd8 = MapTrackView::`RTTI Complete Object Locator'{for `QPaintDevice'} +struct QPaintDevice::MapTrackView::VTable MapTrackView::`vftable'{for `QPaintDevice'} = { - Animal::Animal(this); - Flying::Flying(&this->vtable_Flying); - this->vtable = &Bird::`vftable'; - this->vtable_Flying = &Bird::`vftable'{for `Flying}; - this->field_8 = "A bird"; - this->field_18 = 0x58; - this->field_20 = arg2; - return this; + int64_t (* const vFunc_0)(int64_t arg1, char arg2, int512_t arg3) = sub_140053114 + int32_t (* const vFunc_1)(QWidget* this) = Qt5Widgets:QWidget::devType(QWidget* this) const__ptr64 + class QPaintEngine* __ptr64 (* const vFunc_2)(QWidget* this) = Qt5Widgets:QWidget::paintEngine(QWidget* this) const__ptr64 + int32_t (* const vFunc_3)(QWidget* this, enum QPaintDevice::PaintDeviceMetric arg2) = Qt5Widgets:QWidget::metric(QWidget* this, enum QPaintDevice::PaintDeviceMetric) const__ptr64 + void (* const vFunc_4)(QWidget* this, class QPainter* __ptr64 arg2) = Qt5Widgets:QWidget::initPainter(QWidget* this, class QPainter* __ptr64) const__ptr64 + class QPaintDevice* __ptr64 (* const vFunc_5)(QWidget* this, class QPoint* __ptr64 arg2) = Qt5Widgets:QWidget::redirected(QWidget* this, class QPoint* __ptr64) const__ptr64 + class QPainter* __ptr64 (* const vFunc_6)(QWidget* this) = Qt5Widgets:QWidget::sharedPainter(QWidget* this) const__ptr64 } ``` -## Example Virtual Function Listing +_The above listing includes type information deduced seperately through demangled names_ -Using the newly created constructor object type in [Example Constructor Listing](#example-constructor-listing) we can apply it to all virtual functions as the first parameter. The listing below shows a fully symbolized virtual function for `Bird` in `overrides.cpp` (found in test\bins). +## Exposed Metadata -```cpp -uint64_t Bird::vFunc_0(class Bird* this) -{ - int32_t var_18 = 0; - uint64_t field_20; - while (true) - { - field_20 = ((uint64_t)this->field_20); - if (var_18 >= field_20) - { - break; - } - fputs("Tweet!"); - var_18 = (var_18 + 1); +This plugin will store metadata on the view queryable view the `msvc` key. + +### Example Metadata + +```py +# data = bv.query_metadata("msvc") +data = { + "classes": { + "5368823328": { + "className": "Animal", + "vft": { + "address": 5368818736, + "functions": [{"address": 5368779647}, {"address": 5368779152}], + }, + }, + "5368823464": { + "className": "Flying", + "vft": {"address": 5368818768, "functions": [{"address": 5368778982}]}, + }, + "5368823600": { + "baseClassName": "Animal", + "className": "Bird", + "vft": { + "address": 5368818816, + "functions": [{"address": 5368779137}, {"address": 5368779272}], + }, + }, + "5368823808": { + "baseClassName": "Flying", + "className": "Bird", + "classOffset": 16, + "vft": {"address": 5368818848, "functions": [{"address": 5368778982}]}, + }, + "5368823856": { + "className": "type_info", + "vft": {"address": 5368818888, "functions": [{"address": 5368778927}]}, + }, } - return field_20; } - ``` +### Example Scripts + +Example scripts can be found in the [scripts](/scripts) folder. + +- `class_dump.py` + - Headless script that shows how to execute the RTTI commands programmatically. +- `class_graphviz.py` + - Headless script that outputs a graphviz graph of the classes. +- `class_traverser.py` + - A UI script that when executed, popup a QT dialog list of class information. +- `class_types.py` + - A script that will generate class types with the appropriate vtables already added. + +#### Class Traverser Example + +![class_traverser.png](fixtures/class_traverser.png) + [Binary Ninja]: https://binary.ninja diff --git a/docs/info.md b/docs/info.md index 24da8e1..33d3be2 100644 --- a/docs/info.md +++ b/docs/info.md @@ -18,6 +18,7 @@ struct _RTTICompleteObjectLocator { // The below are offsets from image base (if signature is set to COL_SIG_REV1). int pTypeDescriptor; int pClassDescriptor; + // Only present if signature is set to COL_SIG_REV1 int pSelf; } ``` diff --git a/fixtures/class_traverser.png b/fixtures/class_traverser.png new file mode 100644 index 0000000..61b362d Binary files /dev/null and b/fixtures/class_traverser.png differ diff --git a/include/base_class_array.h b/include/base_class_array.h deleted file mode 100644 index 1a1a102..0000000 --- a/include/base_class_array.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include "base_class_descriptor.h" - -using namespace BinaryNinja; - -class BaseClassArray -{ -private: - Ref m_view; - -public: - uint64_t m_address; - int32_t m_length; - - BaseClassArray(BinaryView* view, uint64_t address, int32_t length); - std::vector GetBaseClassDescriptors(); - BaseClassDescriptor GetRootClassDescriptor(); - Ref GetType(); - Ref CreateSymbol(); - std::string GetSymbolName(); -}; \ No newline at end of file diff --git a/include/base_class_descriptor.h b/include/base_class_descriptor.h deleted file mode 100644 index b714ec2..0000000 --- a/include/base_class_descriptor.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -#include "type_descriptor.h" -using namespace BinaryNinja; - -Ref GetBaseClassDescriptorType(BinaryView* view); - -class BaseClassDescriptor -{ -private: - Ref m_view; - -public: - uint64_t m_address; - - // Structure values. - int32_t m_pTypeDescriptorValue; - uint32_t m_numContainedBasesValue; - int32_t m_where_mdispValue; - int32_t m_where_pdispValue; - int32_t m_where_vdispValue; - uint32_t m_attributesValue; - int32_t m_pClassHierarchyDescriptorValue; - - BaseClassDescriptor(BinaryView* view, uint64_t address); - TypeDescriptor GetTypeDescriptor(); - Ref CreateSymbol(); - std::string GetSymbolName(); -}; \ No newline at end of file diff --git a/include/class_hierarchy_descriptor.h b/include/class_hierarchy_descriptor.h deleted file mode 100644 index 76d5189..0000000 --- a/include/class_hierarchy_descriptor.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "base_class_array.h" - -using namespace BinaryNinja; - -Ref GetClassHierarchyDescriptorType(); - -class ClassHierarchyDescriptor -{ -private: - Ref m_view; - -public: - uint64_t m_address; - - // Structure values. - uint32_t m_signatureValue; - uint32_t m_attributesValue; - uint32_t m_numBaseClassesValue; - int32_t m_pBaseClassArrayValue; - - ClassHierarchyDescriptor(BinaryView* view, uint64_t address); - BaseClassArray GetBaseClassArray(); - Ref CreateSymbol(); - std::string GetSymbolName(); -}; \ No newline at end of file diff --git a/include/constructor.h b/include/constructor.h deleted file mode 100644 index 6279345..0000000 --- a/include/constructor.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -#include "virtual_function_table.h" -#include "object_locator.h" - -using namespace BinaryNinja; - -class Constructor -{ -private: - Ref m_view; - Ref m_func; - -public: - Constructor(BinaryView* view, Function* func); - bool IsValid(); - std::string GetName(); - std::optional GetRootVirtualFunctionTable(); - size_t AddTag(); - Ref CreateSymbol(); - std::string GetSymbolName(); -}; \ No newline at end of file diff --git a/include/object_locator.h b/include/object_locator.h deleted file mode 100644 index 8fd32ff..0000000 --- a/include/object_locator.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -#include "type_descriptor.h" -#include "class_hierarchy_descriptor.h" -#include "virtual_function_table.h" - -using namespace BinaryNinja; - -constexpr auto COL_SIG_REV1 = 1; - -class CompleteObjectLocator -{ -private: - Ref m_view; - -public: - uint64_t m_address; - - // Structure values. - uint32_t m_signatureValue; - uint32_t m_offsetValue; - uint32_t m_cdOffsetValue; - int32_t m_pTypeDescriptorValue; - int32_t m_pClassHierarchyDescriptorValue; - int32_t m_pSelfValue; - - CompleteObjectLocator(BinaryView* view, uint64_t address); - TypeDescriptor GetTypeDescriptor(); - ClassHierarchyDescriptor GetClassHierarchyDescriptor(); - std::optional GetVirtualFunctionTable(); - bool IsValid(); - bool IsSubObject(); - std::optional GetSubObjectTypeDescriptor(); - Ref GetType(); - std::string GetAssociatedClassName(); - Ref CreateSymbol(); - std::string GetSymbolName(); - std::string GetClassName(); -}; \ No newline at end of file diff --git a/include/rtti.h b/include/rtti.h new file mode 100644 index 0000000..9a0ebb9 --- /dev/null +++ b/include/rtti.h @@ -0,0 +1,118 @@ +#pragma once + +#include "binaryninjaapi.h" + +constexpr const char *VIEW_METADATA_MSVC = "msvc"; + +namespace BinaryNinja { + struct BaseClassArray + { + uint32_t length; + std::vector descriptors; + + BaseClassArray(BinaryView *view, uint64_t address, uint32_t length); + }; + + struct ClassHierarchyDescriptor + { + uint32_t signature; + uint32_t attributes; + uint32_t numBaseClasses; + int32_t pBaseClassArray; + + ClassHierarchyDescriptor(BinaryView *view, uint64_t address); + }; + + struct BaseClassDescriptor + { + int32_t pTypeDescriptor; + uint32_t numContainedBases; + int32_t where_mdisp; + int32_t where_pdisp; + int32_t where_vdisp; + uint32_t attributes; + int32_t pClassHierarchyDescriptor; + + BaseClassDescriptor(BinaryView *view, uint64_t address); + }; + + struct TypeDescriptor + { + uint64_t pVFTable; + uint64_t spare; + std::string name; + + TypeDescriptor(BinaryView *view, uint64_t address); + }; + + struct CompleteObjectLocator + { + uint32_t signature; + uint32_t offset; + uint32_t cdOffset; + int32_t pTypeDescriptor; + int32_t pClassHeirarchyDescriptor; + // Only on 64 bit + int32_t pSelf; + + CompleteObjectLocator(BinaryView *view, uint64_t address); + }; + + struct VirtualFunctionInfo + { + uint64_t funcAddr; + + Ref SerializedMetadata(); + + static VirtualFunctionInfo DeserializedMetadata(const Ref &metadata); + }; + + struct VirtualFunctionTableInfo + { + uint64_t address; + std::vector virtualFunctions; + + Ref SerializedMetadata(); + + static VirtualFunctionTableInfo DeserializedMetadata(const Ref &metadata); + }; + + struct ClassInfo + { + std::string className; + std::optional baseClassName; + std::optional classOffset; + std::optional vft; + + Ref SerializedMetadata(); + + static ClassInfo DeserializedMetadata(const Ref &metadata); + }; + + class MicrosoftRTTIProcessor + { + Ref m_view; + Ref m_logger; + bool allowMangledClassNames; + bool checkWritableRData; + + std::map m_classInfo; + + void DeserializedMetadata(const Ref &metadata); + + std::optional DemangleName(const std::string &mangledName); + + std::optional ProcessRTTI(uint64_t coLocatorAddr); + + std::optional ProcessVFT(uint64_t vftAddr, const ClassInfo &classInfo); + + public: + MicrosoftRTTIProcessor(const Ref &view, bool useMangled = true, bool checkRData = true); + + Ref SerializedMetadata(); + + void ProcessRTTI(); + + void ProcessVFT(); + }; +} diff --git a/include/type_descriptor.h b/include/type_descriptor.h deleted file mode 100644 index 1d9f7ad..0000000 --- a/include/type_descriptor.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include - -using namespace BinaryNinja; - -class TypeDescriptor -{ -private: - Ref m_view; - -public: - uint64_t m_address; - - // Structure values. - uint64_t m_pVFTableValue; - uint64_t m_spareValue; - std::string m_nameValue; - - TypeDescriptor(BinaryView* view, uint64_t address); - std::string GetDemangledName(); - Ref GetType(); - Ref CreateSymbol(); - std::string GetSymbolName(); -}; \ No newline at end of file diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index eca93ab..0000000 --- a/include/utils.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -using namespace BinaryNinja; - -std::string IntToHex(uint64_t val); -Ref GetConstructorTagType(BinaryView* view); -Ref GetVirtualFunctionTableTagType(BinaryView* view); -Ref GetVirtualFunctionTagType(BinaryView* view); -Ref GetCOLocatorTagType(BinaryView* view); -Ref GetPointerTypeChildStructure(Ref ptrType); -std::optional GetSSAVariableUnscopedDefinition(Ref mlil, SSAVariable var); -std::optional WalkToSSAVariableOffset(Ref mlil, SSAVariable var); -std::vector GetSSAVariablesForVariable(Ref func, const Variable& var); -uint64_t ResolveRelPointer(BinaryView* view, uint64_t ptrVal); \ No newline at end of file diff --git a/include/virtual_function.h b/include/virtual_function.h deleted file mode 100644 index 983006c..0000000 --- a/include/virtual_function.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -using namespace BinaryNinja; - -class VirtualFunction -{ -private: - Ref m_view; - -public: - uint64_t m_vftAddr; - Ref m_func; - - VirtualFunction(BinaryView* view, uint64_t vftAddr, Ref func); - // If the function is only referenced once. - // NOTE: If you create for example, a vfunc that is able to be deduped, two vtables will point to that function, we - // don't want to mislead by renaming the function to the last of those vtables associated class. - bool IsUnique(); - Ref CreateSymbol(std::string name); -}; \ No newline at end of file diff --git a/include/virtual_function_table.h b/include/virtual_function_table.h deleted file mode 100644 index e947c5f..0000000 --- a/include/virtual_function_table.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -#include "virtual_function.h" - -using namespace BinaryNinja; - -class CompleteObjectLocator; - -class VirtualFunctionTable -{ -private: - Ref m_view; - -public: - uint64_t m_address; - - VirtualFunctionTable(BinaryView* view, uint64_t address); - std::vector GetVirtualFunctions(); - std::optional GetCOLocator(); - Ref GetType(); - Ref GetObjectType(); - Ref CreateSymbol(); - std::string GetSymbolName(); - std::string GetTypeName(); -}; \ No newline at end of file diff --git a/plugin.json b/plugin.json index 36ceaca..db2967c 100644 --- a/plugin.json +++ b/plugin.json @@ -1,15 +1,14 @@ { "pluginmetadataversion": 2, - "name": "MSVC Native", + "name": "MSVC RTTI", "author": "Mason Reed", "type": [ "analysis" ], "api": [ - "python3" + "cpp" ], "description": "Parses and symbolizes MSVC RTTI information in Binary Ninja", - "longdescription": "# MSVC RTTI\n\nParses and symbolizes MSVC RTTI information in [Binary Ninja].\n\n## Example Virtual Function Table Listing\n\nArguably the most import function of symbolizing RTTI information is the virtual function tables. The listing below is the symbolized view of `simple.cpp` (found in test\\bins).\n\n```cpp\nvoid* data_140010320 = ParentA::`RTTI Complete Object Locator\nstruct ParentA::VTable ParentA::`vftable = \n{\n void* (* const vFunc_0)(void* arg1, int32_t arg2) = ParentB::vFunc_0\n int64_t (* const vFunc_1)() __pure = ParentA::vFunc_1\n int64_t (* const vFunc_2)() __pure = ParentA::vFunc_2\n}\nvoid* data_140010340 = ParentB::`RTTI Complete Object Locator\nstruct ParentB::VTable ParentB::`vftable = \n{\n void* (* const vFunc_0)(void* arg1, int32_t arg2) = ParentB::vFunc_0\n int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1\n}\nvoid* data_140010358 = SomeClass::`RTTI Complete Object Locator\nstruct SomeClass::VTable SomeClass::`vftable = \n{\n void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0\n int64_t (* const vFunc_1)() __pure = ParentA::vFunc_1\n int64_t (* const vFunc_2)() __pure = ParentA::vFunc_2\n int64_t (* const vFunc_3)() __pure = SomeClass::vFunc_3\n}\nvoid* data_140010380 = SomeClass::`RTTI Complete Object Locator{for `ParentB}\nstruct ParentB::VTable SomeClass::`vftable{for `ParentB} = \n{\n void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0\n int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1\n}\n```\n\n## Example Constructor Listing\n\nBased off the information collected from the RTTI scan, we can deduce constructors and create types and symbolize their structures. Using the [type inheritence](https://binary.ninja/2023/05/03/3.4-finally-freed.html#inherited-types) in [Binary Ninja] we can make these types easily composable. The listing below shows the fully symbolized constructor function for `Bird` in `overrides.cpp` (found in test\\bins), as well as the accompanying auto created type.\n\n```cpp\nclass __base(Animal, 0) __base(Flying, 0) Bird\n{\n struct `Bird::VTable`* vtable;\n char const* field_8;\n struct `Flying::VTable`* vtable_Flying;\n int32_t field_18;\n __padding char _1C[4];\n int32_t field_20;\n};\n\nclass Bird* Bird::Bird(class Bird* this, int32_t arg2)\n{\n Animal::Animal(this);\n Flying::Flying(&this->vtable_Flying);\n this->vtable = &Bird::`vftable';\n this->vtable_Flying = &Bird::`vftable'{for `Flying};\n this->field_8 = \"A bird\";\n this->field_18 = 0x58;\n this->field_20 = arg2;\n return this;\n}\n```\n\n## Example Virtual Function Listing\n\nUsing the newly created constructor object type in [Example Constructor Listing](#example-constructor-listing) we can apply it to all virtual functions as the first parameter. The listing below shows a fully symbolized virtual function for `Bird` in `overrides.cpp` (found in test\\bins).\n\n```cpp\nuint64_t Bird::vFunc_0(class Bird* this)\n{\n int32_t var_18 = 0;\n uint64_t field_20;\n while (true)\n {\n field_20 = ((uint64_t)this->field_20);\n if (var_18 >= field_20)\n {\n break;\n }\n fputs(\"Tweet!\");\n var_18 = (var_18 + 1);\n }\n return field_20;\n}\n\n```\n\n[Binary Ninja]: https://binary.ninja\n", "license": { "name": "MIT", "text": "Copyright 2023 Mason Reed\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." @@ -20,6 +19,6 @@ "Linux" ], "dependencies": {}, - "version": "1.2.1", + "version": "2.0.0", "minimumbinaryninjaversion": 4771 } diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..de8d360 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,86 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "graphviz" +version = "0.20.3" +description = "Simple Python interface for Graphviz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"}, + {file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"}, +] + +[package.extras] +dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] + +[[package]] +name = "pyside6" +version = "6.7.3" +description = "Python bindings for the Qt cross-platform application and UI framework" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "PySide6-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:1c21c4cf6cdd29bd13bbd7a2514756a19188eab992b92af03e64bf06a9b33d5b"}, + {file = "PySide6-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a21480cc746358f70768975fcc452322f03b3c3622625bfb1743b40ce4e24beb"}, + {file = "PySide6-6.7.3-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:c2a1313296c0088d1c3d231d0a8ccf0eda52b84139d0c4065fded76e4a4378f4"}, + {file = "PySide6-6.7.3-cp39-abi3-win_amd64.whl", hash = "sha256:3ac8dcb4ca82d276e319f89dd99098b01061f255a2b9104216504aece5e0faf8"}, +] + +[package.dependencies] +PySide6-Addons = "6.7.3" +PySide6-Essentials = "6.7.3" +shiboken6 = "6.7.3" + +[[package]] +name = "pyside6-addons" +version = "6.7.3" +description = "Python bindings for the Qt cross-platform application and UI framework (Addons)" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "PySide6_Addons-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:3174cb3a373c09c98740b452e8e8f4945d64cfa18ed8d43964111d570f0dc647"}, + {file = "PySide6_Addons-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bde1eb03dbffd089b50cd445847aaecaf4056cea84c49ea592d00f84f247251e"}, + {file = "PySide6_Addons-6.7.3-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:5a9e0df31345fe6caea677d916ea48b53ba86f95cc6499c57f89e392447ad6db"}, + {file = "PySide6_Addons-6.7.3-cp39-abi3-win_amd64.whl", hash = "sha256:d8a19c2b2446407724c81c33ebf3217eaabd092f0f72da8130c17079e04a7813"}, +] + +[package.dependencies] +PySide6-Essentials = "6.7.3" +shiboken6 = "6.7.3" + +[[package]] +name = "pyside6-essentials" +version = "6.7.3" +description = "Python bindings for the Qt cross-platform application and UI framework (Essentials)" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "PySide6_Essentials-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:f9e08a4e9e7dc7b5ab72fde20abce8c97df7af1b802d9743f098f577dfe1f649"}, + {file = "PySide6_Essentials-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cda6fd26aead48f32e57f044d18aa75dc39265b49d7957f515ce7ac3989e7029"}, + {file = "PySide6_Essentials-6.7.3-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:acdde06b74f26e7d26b4ae1461081b32a6cb17fcaa2a580050b5e0f0f12236c9"}, + {file = "PySide6_Essentials-6.7.3-cp39-abi3-win_amd64.whl", hash = "sha256:f0950fcdcbcd4f2443336dc6a5fe692172adc225f876839583503ded0ab2f2a7"}, +] + +[package.dependencies] +shiboken6 = "6.7.3" + +[[package]] +name = "shiboken6" +version = "6.7.3" +description = "Python/C++ bindings helper module" +optional = false +python-versions = "<3.13,>=3.9" +files = [ + {file = "shiboken6-6.7.3-cp39-abi3-macosx_11_0_universal2.whl", hash = "sha256:285fe3cf79be3135fe1ad1e2b9ff6db3a48698887425af6aa6ed7a05a9abc3d6"}, + {file = "shiboken6-6.7.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f0852e5781de78be5b13c140ec4c7fb9734e2aaf2986eb2d6a224363e03efccc"}, + {file = "shiboken6-6.7.3-cp39-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:f0dd635178e64a45be2f84c9f33dd79ac30328da87f834f21a0baf69ae210e6e"}, + {file = "shiboken6-6.7.3-cp39-abi3-win_amd64.whl", hash = "sha256:5f29325dfa86fde0274240f1f38e421303749d3174ce3ada178715b5f4719db9"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "<3.13,>=3.9" +content-hash = "eed63860227b1a5787a18c195827fb818b09e161fbee1207424b88745f77797d" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a8895a4 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "binja-msvc" +version = "0.1.0" +description = "" +authors = ["Mason Reed "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "<3.13,>=3.9" +pyside6 = "^6.7.3" +graphviz = "^0.20.3" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/scripts/class_dump.py b/scripts/class_dump.py new file mode 100644 index 0000000..e90bcb0 --- /dev/null +++ b/scripts/class_dump.py @@ -0,0 +1,57 @@ +# data looks like: +# { +# 'classes': { +# '5368823328': {'className': 'Animal'}, +# '5368823464': {'className': 'Flying'}, +# '5368823600': {'className': 'Bird'}, +# '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, +# '5368823856': {'className': 'type_info'} +# } +# } +import sys +from time import sleep + +from binaryninja import BinaryView, PluginCommandContext, PluginCommand + +if len(sys.argv) != 2: + print("Usage: python class_dump.py ") + sys.exit(1) +file_path = sys.argv[1] + +print("Loading binary...") +view = BinaryView.load(file_path) +cxt = PluginCommandContext(view) +print("Finding RTTI...") +PluginCommand.get_valid_list(cxt)['MSVC\\Find RTTI'].execute(cxt) + +# Wait for metadata to be valid +while True: + try: + if view.query_metadata("msvc"): + break + except KeyError as e: + sleep(1) +data = view.query_metadata("msvc") + + +class ClassInfo: + def __init__(self, class_name: str, base_classes: dict[int, str]): + self.class_name = class_name + self.base_classes = base_classes + + def __repr__(self): + return f"ClassInfo(class_name={self.class_name}, base_classes={self.base_classes})" + + +classes = {} +for class_addr, class_data in data['classes'].items(): + class_name = class_data.get('className') + base_class_name = class_data.get('baseClassName', None) + base_class_offset = class_data.get('classOffset', 0) + if class_name not in classes: + classes[class_name] = ClassInfo(class_name=class_name, base_classes={}) + if base_class_name: + classes[class_name].base_classes[base_class_offset] = base_class_name + +for class_info in classes.values(): + print(class_info) diff --git a/scripts/class_graph.py b/scripts/class_graph.py new file mode 100644 index 0000000..a3ce26b --- /dev/null +++ b/scripts/class_graph.py @@ -0,0 +1,78 @@ +# data looks like: +# { +# 'classes': { +# '5368823328': {'className': 'Animal'}, +# '5368823464': {'className': 'Flying'}, +# '5368823600': {'className': 'Bird'}, +# '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, +# '5368823856': {'className': 'type_info'} +# } +# } + + +# FYI we do not have this in binja +from PySide6.QtCharts import QChart, QChartView, QLineSeries +from PySide6.QtWidgets import QDialog, QVBoxLayout + +data = { + 'classes': { + '5368823328': {'className': 'Animal'}, + '5368823464': {'className': 'Flying'}, + '5368823600': {'className': 'Bird'}, + '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, + '5368823856': {'className': 'type_info'} + } +} + + +class GraphDialog(QDialog): + def __init__(self): + super().__init__() + self.setWindowTitle("Class Hierarchy Graph") + self.resize(800, 600) + + layout = QVBoxLayout(self) + + self.chart = QChart() + self.chart_view = QChartView(self.chart) + + self.series = QLineSeries() + self.add_data_to_series() + + self.chart.addSeries(self.series) + self.chart.createDefaultAxes() + + layout.addWidget(self.chart_view) + + def add_data_to_series(self): + # Dummy implementation of graph based only on the given data; here we'll just display one line from (0,0) to (1,1) + self.series.append(0, 0) + self.series.append(1, 1) + + # Add nodes and connections based on the data provided + nodes = data['classes'] + positions = {} + x, y = 0, 0 + + for node_id, node_info in nodes.items(): + class_name = node_info.get('className', 'Unknown') + base_class_name = node_info.get('baseClassName', None) + class_offset = node_info.get('classOffset', 0) + + positions[class_name] = (x, y + class_offset) + x += 1 + + if base_class_name: + base_position = positions.get(base_class_name, (0, 0)) + self.series.append(*base_position) + self.series.append(x, y) + + y += 10 + + +def create_window(): + dialog = GraphDialog() + dialog.exec_() + + +execute_on_main_thread(create_window) diff --git a/scripts/class_graphviz.py b/scripts/class_graphviz.py new file mode 100644 index 0000000..497e6a5 --- /dev/null +++ b/scripts/class_graphviz.py @@ -0,0 +1,56 @@ +import sys +from time import sleep + +import graphviz +from binaryninja import BinaryView, PluginCommandContext, PluginCommand + +# Whether to open the png after it is created. +AUTO_OPEN = True + +if len(sys.argv) != 3: + print("Usage: python class_graphviz.py ") + sys.exit(1) +file_path = sys.argv[1] +out_path = sys.argv[1] + +print("Loading binary...") +view = BinaryView.load(file_path) +cxt = PluginCommandContext(view) +print("Finding RTTI...") +PluginCommand.get_valid_list(cxt)['MSVC\\Find RTTI'].execute(cxt) + +# Wait for metadata to be valid +while True: + try: + if view.query_metadata("msvc"): + break + except KeyError as e: + sleep(0.2) + +print("Creating graph...") +data = view.query_metadata("msvc") + + +def create_graph(data): + dot = graphviz.Digraph() + classes = data.get('classes', {}) + for class_info in classes.values(): + class_name = class_info.get('className') + base_class_name = class_info.get('baseClassName', None) + dot.node(class_name) + if base_class_name: + dot.edge(base_class_name, class_name) + return dot + + +# data = { +# 'classes': { +# '5368823328': {'className': 'Animal'}, +# '5368823464': {'className': 'Flying'}, +# '5368823600': {'className': 'Bird'}, +# '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, +# '5368823856': {'className': 'type_info'} +# } +# } +dot = create_graph(data) +dot.render(out_path, view=AUTO_OPEN, format='png') diff --git a/scripts/class_traverser.py b/scripts/class_traverser.py new file mode 100644 index 0000000..bf50d1a --- /dev/null +++ b/scripts/class_traverser.py @@ -0,0 +1,83 @@ +from PySide6.QtWidgets import (QVBoxLayout, QTreeWidget, + QTreeWidgetItem, QDialog, QLabel, QLineEdit) + +# data looks like: +# { +# 'classes': { +# '5368823328': {'className': 'Animal'}, +# '5368823464': {'className': 'Flying'}, +# '5368823600': {'className': 'Bird'}, +# '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, +# '5368823856': {'className': 'type_info'} +# } +# } +data = bv.query_metadata("msvc") + + +class MainDialog(QDialog): + def __init__(self): + super().__init__() + + self.setWindowTitle("Class Traverser") + self.setGeometry(100, 100, 600, 400) + + # Create the main layout + main_layout = QVBoxLayout() + + # Create the filter input + self.filter_input = QLineEdit() + self.filter_input.setPlaceholderText("Filter classes") + self.filter_input.textChanged.connect(self.filter_tree) + main_layout.addWidget(self.filter_input) + + # Create the tree widget + self.tree_widget = QTreeWidget() + self.tree_widget.setHeaderLabels(["Address", "Class Name", "Base Class Name", "Class Offset"]) + + # Populate the tree widget using the given data + self.populate_tree(data["classes"]) + + # Add the tree widget and buttons to the layout + main_layout.addWidget(self.tree_widget) + + # Add a label to show the number of classes + num_classes_label = QLabel(f"Number of classes: {len(data['classes'])}") + main_layout.addWidget(num_classes_label) + + # Set the main layout for the dialog + self.setLayout(main_layout) + + def filter_tree(self, text): + for i in range(self.tree_widget.topLevelItemCount()): + item = self.tree_widget.topLevelItem(i) + self.filter_tree_recursively(item, text.lower()) + + def filter_tree_recursively(self, item, filter_text): + match = filter_text in item.text(1).lower() or filter_text in item.text(2).lower() + item.setHidden(not match) + for j in range(item.childCount()): + child = item.child(j) + self.filter_tree_recursively(child, filter_text) + + def populate_tree(self, classes): + sorted_classes = dict(sorted(classes.items(), key=lambda item: item[1].get('className', ''))) + # Connect the double-click signal to a handler function + self.tree_widget.itemDoubleClicked.connect(self.on_item_double_clicked) + for class_addr, class_info in sorted_classes.items(): + tree_item = QTreeWidgetItem() + tree_item.setText(0, hex(int(class_addr))) + tree_item.setText(1, class_info.get("className", "")) + tree_item.setText(2, class_info.get("baseClassName", "")) + tree_item.setText(3, str(class_info.get("classOffset", ""))) + self.tree_widget.addTopLevelItem(tree_item) + + def on_item_double_clicked(self, item, column): + bv.navigate(bv.view, int(item.text(0), 0)) + + +def create_window(): + dialog = MainDialog() + dialog.exec_() + + +execute_on_main_thread(create_window) diff --git a/scripts/class_types.py b/scripts/class_types.py new file mode 100644 index 0000000..aab7a37 --- /dev/null +++ b/scripts/class_types.py @@ -0,0 +1,66 @@ +# data looks like: +# { +# 'classes': { +# '5368823328': {'className': 'Animal'}, +# '5368823464': {'className': 'Flying'}, +# '5368823600': {'className': 'Bird'}, +# '5368823808': {'baseClassName': 'Flying', 'className': 'Bird', 'classOffset': 16}, +# '5368823856': {'className': 'type_info'} +# } +# } +from typing import List + +from binaryninja import BinaryView, BaseStructure, NamedTypeReferenceClass, StructureVariant, NamedTypeReferenceType, \ + StructureBuilder, PointerType + +view: BinaryView = bv +data = view.query_metadata("msvc") + + +class ClassInfo: + def __init__(self, class_name: str, base_classes: dict[int, str]): + self.class_name = class_name + self.base_classes = base_classes + + def create_type(self, view: BinaryView): + # Check if the type name already exists in the view + if self.class_name in view.type: + return + # TODO: Some classes are skipped in creation... + # TODO: I think they are ones that did not get a non base class node? + # Create a new Binary Ninja structure type for the class. + struct_builder = StructureBuilder.create(None, StructureVariant.ClassStructureType) + should_add_vtable = True + # Add base classes to the structure with appropriate offsets. + base_structs: List[BaseStructure] = [] + for offset, base_class_name in class_info.base_classes.items(): + if offset == 0: + should_add_vtable = False # Vtable at 0 is not our class vtable... + base_struct_ntr = NamedTypeReferenceType.create(NamedTypeReferenceClass.ClassNamedTypeClass, + None, base_class_name) + base_structs.append(BaseStructure(base_struct_ntr, offset)) + struct_builder.base_structures = base_structs + if should_add_vtable: + vtable_ntr = NamedTypeReferenceType.create(NamedTypeReferenceClass.StructNamedTypeClass, None, + self.class_name + "::VTable") + vtable_ntr_ptr = PointerType.create(view.arch, vtable_ntr, True) + struct_builder.insert(0, vtable_ntr_ptr, "vtable", False) + # Finally create the type. + view.define_type("msvc_" + self.class_name, self.class_name, struct_builder) + + def __repr__(self): + return f"ClassInfo(class_name={self.class_name}, base_classes={self.base_classes})" + + +classes = {} +for class_addr, class_data in data['classes'].items(): + class_name = class_data.get('className') + base_class_name = class_data.get('baseClassName', None) + base_class_offset = class_data.get('classOffset', 0) + if class_name not in classes: + classes[class_name] = ClassInfo(class_name=class_name, base_classes={}) + if base_class_name: + classes[class_name].base_classes[base_class_offset] = base_class_name + +for class_info in classes.values(): + class_info.create_type(view) diff --git a/src/base_class_array.cpp b/src/base_class_array.cpp deleted file mode 100644 index d6b10a8..0000000 --- a/src/base_class_array.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include - -#include "class_hierarchy_descriptor.h" -#include "utils.h" - -using namespace BinaryNinja; - -BaseClassArray::BaseClassArray(BinaryView* view, uint64_t address, int32_t length) -{ - m_view = view; - m_address = address; - m_length = length; -} - -std::vector BaseClassArray::GetBaseClassDescriptors() -{ - std::vector baseClassDescriptors = {}; - BinaryReader reader = BinaryReader(m_view); - reader.Seek(m_address); - - for (size_t i = 0; i < m_length; i++) - { - baseClassDescriptors.emplace_back( - BaseClassDescriptor(m_view, ResolveRelPointer(m_view, (uint64_t)reader.Read32()))); - } - - return baseClassDescriptors; -} - -BaseClassDescriptor BaseClassArray::GetRootClassDescriptor() -{ - return GetBaseClassDescriptors().front(); -} - -Ref BaseClassArray::GetType() -{ - StructureBuilder baseClassArrayBuilder; - baseClassArrayBuilder.AddMember( - Type::ArrayType(Type::IntegerType(4, true), m_length), "arrayOfBaseClassDescriptors"); - - return TypeBuilder::StructureType(&baseClassArrayBuilder).Finalize(); -} - -Ref BaseClassArray::CreateSymbol() -{ - Ref baseClassArraySym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(baseClassArraySym); - m_view->DefineUserDataVariable(m_address, GetType()); - return baseClassArraySym; -} - -// Example: Animal::`RTTI Base Class Array' -std::string BaseClassArray::GetSymbolName() -{ - return GetRootClassDescriptor().GetTypeDescriptor().GetDemangledName() + "::`RTTI Base Class Array'"; -} \ No newline at end of file diff --git a/src/base_class_descriptor.cpp b/src/base_class_descriptor.cpp deleted file mode 100644 index 6872521..0000000 --- a/src/base_class_descriptor.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#include "base_class_descriptor.h" -#include "utils.h" - -using namespace BinaryNinja; - -Ref GetPMDType(BinaryView* view) -{ - Ref typeCache = view->GetTypeById("msvc_PMD"); - - if (typeCache == nullptr) - { - Ref intType = Type::IntegerType(4, true); - - StructureBuilder pmdBuilder; - pmdBuilder.AddMember(intType, "mdisp"); - pmdBuilder.AddMember(intType, "pdisp"); - pmdBuilder.AddMember(intType, "vdisp"); - - view->DefineType("msvc_PMD", QualifiedName("_PMD"), TypeBuilder::StructureType(&pmdBuilder).Finalize()); - typeCache = view->GetTypeById("msvc_PMD"); - } - - return typeCache; -} - -Ref GetBaseClassDescriptorType(BinaryView* view) -{ - Ref typeCache = view->GetTypeById("msvc_RTTIBaseClassDescriptor"); - - if (typeCache == nullptr) - { - Ref uintType = Type::IntegerType(4, false); - Ref intType = Type::IntegerType(4, true); - - StructureBuilder baseClassDescriptorBuilder; - baseClassDescriptorBuilder.AddMember(intType, "pTypeDescriptor"); - baseClassDescriptorBuilder.AddMember(uintType, "numContainedBases"); - baseClassDescriptorBuilder.AddMember(GetPMDType(view), "where"); - baseClassDescriptorBuilder.AddMember(uintType, "attributes"); - baseClassDescriptorBuilder.AddMember(intType, "pClassDescriptor"); - - view->DefineType("msvc_RTTIBaseClassDescriptor", QualifiedName("_RTTIBaseClassDescriptor"), - TypeBuilder::StructureType(&baseClassDescriptorBuilder).Finalize()); - typeCache = view->GetTypeById("msvc_RTTIBaseClassDescriptor"); - } - - return typeCache; -} - -BaseClassDescriptor::BaseClassDescriptor(BinaryView* view, uint64_t address) -{ - BinaryReader reader = BinaryReader(view); - reader.Seek(address); - - m_view = view; - m_address = address; - m_pTypeDescriptorValue = (int32_t)reader.Read32(); - m_numContainedBasesValue = reader.Read32(); - m_where_mdispValue = (int32_t)reader.Read32(); - m_where_pdispValue = (int32_t)reader.Read32(); - m_where_vdispValue = (int32_t)reader.Read32(); - m_attributesValue = reader.Read32(); - m_pClassHierarchyDescriptorValue = (int32_t)reader.Read32(); -} - -TypeDescriptor BaseClassDescriptor::GetTypeDescriptor() -{ - return TypeDescriptor(m_view, ResolveRelPointer(m_view, m_pTypeDescriptorValue)); -} - -Ref BaseClassDescriptor::CreateSymbol() -{ - Ref baseClassDescriptorSym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(baseClassDescriptorSym); - m_view->DefineUserDataVariable(m_address, GetBaseClassDescriptorType(m_view)); - return baseClassDescriptorSym; -} - -// Example: Animal::`RTTI Base Class Descriptor at (0,-1,0,64)' -std::string BaseClassDescriptor::GetSymbolName() -{ - return GetTypeDescriptor().GetDemangledName() + "::`RTTI Base Class Descriptor at (" - + std::to_string(m_where_mdispValue) + "," + std::to_string(m_where_pdispValue) + "," - + std::to_string(m_where_vdispValue) + "," + std::to_string(m_attributesValue) + ")'"; -} \ No newline at end of file diff --git a/src/class_hierarchy_descriptor.cpp b/src/class_hierarchy_descriptor.cpp deleted file mode 100644 index 10d8f2d..0000000 --- a/src/class_hierarchy_descriptor.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#include "class_hierarchy_descriptor.h" -#include "utils.h" - -using namespace BinaryNinja; - -Ref GetClassHierarchyDescriptorType(BinaryView* view) -{ - Ref typeCache = view->GetTypeById("msvc_RTTIClassHierarchyDescriptor"); - - if (typeCache == nullptr) - { - Ref uintType = Type::IntegerType(4, false); - Ref intType = Type::IntegerType(4, true); - - StructureBuilder classHierarchyDescriptorBuilder; - classHierarchyDescriptorBuilder.AddMember(uintType, "signature"); - classHierarchyDescriptorBuilder.AddMember(uintType, "attributes"); - classHierarchyDescriptorBuilder.AddMember(uintType, "numBaseClasses"); - classHierarchyDescriptorBuilder.AddMember(intType, "pBaseClassArray"); - - view->DefineType("msvc_RTTIClassHierarchyDescriptor", QualifiedName("_RTTIClassHierarchyDescriptor"), - TypeBuilder::StructureType(&classHierarchyDescriptorBuilder).Finalize()); - - typeCache = view->GetTypeById("msvc_RTTIClassHierarchyDescriptor"); - } - - return typeCache; -} - -ClassHierarchyDescriptor::ClassHierarchyDescriptor(BinaryView* view, uint64_t address) -{ - BinaryReader reader = BinaryReader(view); - reader.Seek(address); - - m_view = view; - m_address = address; - m_signatureValue = reader.Read32(); - m_attributesValue = reader.Read32(); - m_numBaseClassesValue = reader.Read32(); - m_pBaseClassArrayValue = (int32_t)reader.Read32(); -} - -BaseClassArray ClassHierarchyDescriptor::GetBaseClassArray() -{ - return BaseClassArray(m_view, ResolveRelPointer(m_view, m_pBaseClassArrayValue), m_numBaseClassesValue); -} - -Ref ClassHierarchyDescriptor::CreateSymbol() -{ - Ref classDescSym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(classDescSym); - m_view->DefineUserDataVariable(m_address, GetClassHierarchyDescriptorType(m_view)); - return classDescSym; -} - -// Example: Animal::`RTTI Class Hierarchy Descriptor' -std::string ClassHierarchyDescriptor::GetSymbolName() -{ - return GetBaseClassArray().GetRootClassDescriptor().GetTypeDescriptor().GetDemangledName() - + "::`RTTI Class Hierarchy Descriptor'"; -} \ No newline at end of file diff --git a/src/constructor.cpp b/src/constructor.cpp deleted file mode 100644 index 791049e..0000000 --- a/src/constructor.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include - -#include "constructor.h" -#include "utils.h" - -using namespace BinaryNinja; - -Constructor::Constructor(BinaryView* view, Function* func) -{ - m_view = view; - m_func = func; -} - -bool Constructor::IsValid() -{ - // TODO: Check to make sure we have the first param as a _vfTable** type... - // TODO: x86 getting constant pointer recognition failures, see: binaryninja-api issue 4399. - return GetRootVirtualFunctionTable().has_value(); -} - -std::string Constructor::GetName() -{ - auto coLocator = GetRootVirtualFunctionTable()->GetCOLocator(); - if (!coLocator.has_value()) - return "Constructor_" + std::to_string(GetRootVirtualFunctionTable()->m_address); - return coLocator->GetTypeDescriptor().GetDemangledName(); -} - -std::optional Constructor::GetRootVirtualFunctionTable() -{ - Ref vftTagType = GetVirtualFunctionTableTagType(m_view); - Ref mlil = m_func->GetMediumLevelIL()->GetSSAForm(); - - for (auto& block : mlil->GetBasicBlocks()) - { - for (size_t instIdx = block->GetStart(); instIdx < block->GetEnd(); instIdx++) - { - MediumLevelILInstruction inst = (*mlil)[instIdx]; - - if (inst.operation == MLIL_STORE_SSA) - { - auto destExpr = inst.GetDestExpr(); - auto sourceExpr = inst.GetSourceExpr(); - DataVariable sourceDataVar = {}; - - if (m_view->GetDataVariableAtAddress(sourceExpr.GetValue().value, sourceDataVar)) - { - for (auto& tag : m_view->GetDataTags(sourceDataVar.address)) - { - if (tag->GetType() != vftTagType) - continue; - - switch (destExpr.operation) - { - case MLIL_VAR_SSA: - return VirtualFunctionTable(m_view, sourceDataVar.address); - } - } - } - } - } - } - - return std::nullopt; -} - -size_t Constructor::AddTag() -{ - auto tagType = GetConstructorTagType(m_view); - auto constructorName = GetName(); - m_func->CreateUserFunctionTag(tagType, constructorName, true); - - size_t tagCount = 0; - for (auto funcTags : m_view->GetAllTagReferencesOfType(tagType)) - { - if (funcTags.tag->GetData() == constructorName) - { - tagCount++; - } - } - return tagCount; -} - -Ref Constructor::CreateSymbol() -{ - Ref newFuncSym = new Symbol {FunctionSymbol, GetSymbolName(), m_func->GetStart()}; - m_view->DefineUserSymbol(newFuncSym); - return newFuncSym; -} - -std::string Constructor::GetSymbolName() -{ - std::string symName = GetName(); - // If this constructor is not the first of its class, add a counter to the end of it. - size_t tagCount = AddTag(); - if (tagCount > 1) - { - return symName + "::" + symName + "_" + std::to_string(tagCount); - } - return symName + "::" + symName; -} \ No newline at end of file diff --git a/src/object_locator.cpp b/src/object_locator.cpp deleted file mode 100644 index 9edca8f..0000000 --- a/src/object_locator.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include "object_locator.h" -#include "utils.h" - -using namespace BinaryNinja; - -CompleteObjectLocator::CompleteObjectLocator(BinaryView* view, uint64_t address) -{ - BinaryReader reader = BinaryReader(view); - reader.Seek(address); - - m_view = view; - m_address = address; - m_signatureValue = reader.Read32(); - m_offsetValue = reader.Read32(); - m_cdOffsetValue = reader.Read32(); - m_pTypeDescriptorValue = (int32_t)reader.Read32(); - m_pClassHierarchyDescriptorValue = (int32_t)reader.Read32(); - if (m_signatureValue == COL_SIG_REV1) - { - m_pSelfValue = (int32_t)reader.Read32(); - } - else - { - m_pSelfValue = 0; - } -} - -TypeDescriptor CompleteObjectLocator::GetTypeDescriptor() -{ - if (m_signatureValue == COL_SIG_REV1) - return TypeDescriptor(m_view, m_view->GetStart() + m_pTypeDescriptorValue); - return TypeDescriptor(m_view, m_pTypeDescriptorValue); -} - -ClassHierarchyDescriptor CompleteObjectLocator::GetClassHierarchyDescriptor() -{ - if (m_signatureValue == COL_SIG_REV1) - return ClassHierarchyDescriptor(m_view, m_view->GetStart() + m_pClassHierarchyDescriptorValue); - return ClassHierarchyDescriptor(m_view, m_pClassHierarchyDescriptorValue); -} - -std::optional CompleteObjectLocator::GetVirtualFunctionTable() -{ - size_t addrSize = m_view->GetAddressSize(); - std::vector objLocatorRefs = m_view->GetDataReferences(m_address); - if (objLocatorRefs.empty()) - return std::nullopt; - return std::optional(VirtualFunctionTable(m_view, objLocatorRefs.front() + addrSize)); -} - -bool CompleteObjectLocator::IsValid() -{ - uint64_t startAddr = m_view->GetStart(); - uint64_t endAddr = m_view->GetEnd(); - - if (m_signatureValue > 1) - return false; - - if (m_signatureValue == COL_SIG_REV1) - { - if (m_pSelfValue != m_address - startAddr) - return false; - - // Relative addrs - if (m_pTypeDescriptorValue + startAddr > endAddr) - return false; - - if (m_pClassHierarchyDescriptorValue + startAddr > endAddr) - return false; - } - else - { - // Absolute addrs - if (m_pTypeDescriptorValue < startAddr || m_pTypeDescriptorValue > endAddr) - return false; - - if (m_pClassHierarchyDescriptorValue < startAddr || m_pClassHierarchyDescriptorValue > endAddr) - return false; - } - - return true; -} - -bool CompleteObjectLocator::IsSubObject() -{ - return m_offsetValue > 0; -} - -// TODO: This fails sometimes, figure out what causes this. -std::optional CompleteObjectLocator::GetSubObjectTypeDescriptor() -{ - if (!IsSubObject()) - return std::nullopt; - - for (auto baseClassDescs : GetClassHierarchyDescriptor().GetBaseClassArray().GetBaseClassDescriptors()) - { - if ((int32_t)m_offsetValue == baseClassDescs.m_where_mdispValue) - { - return baseClassDescs.GetTypeDescriptor(); - } - } - - return std::nullopt; -} - -Ref CompleteObjectLocator::GetType() -{ - auto sigValStr = std::to_string(m_signatureValue); - Ref typeCache = m_view->GetTypeById("msvc_RTTICompleteObjectLocator" + sigValStr); - - if (typeCache == nullptr) - { - Ref uintType = Type::IntegerType(4, false); - Ref intType = Type::IntegerType(4, true); - - StructureBuilder completeObjectLocatorBuilder; - // TODO: make signature an enum with COL_SIG_REV0 & COL_SIG_REV1? - completeObjectLocatorBuilder.AddMember(uintType, "signature"); - completeObjectLocatorBuilder.AddMember(uintType, "offset"); - completeObjectLocatorBuilder.AddMember(uintType, "cdOffset"); - completeObjectLocatorBuilder.AddMember(intType, "pTypeDescriptor"); - completeObjectLocatorBuilder.AddMember(intType, "pClassHierarchyDescriptor"); - - if (m_signatureValue == COL_SIG_REV1) - { - completeObjectLocatorBuilder.AddMember(intType, "pSelf"); - } - - m_view->DefineType("msvc_RTTICompleteObjectLocator" + sigValStr, QualifiedName("_RTTICompleteObjectLocator"), - TypeBuilder::StructureType(&completeObjectLocatorBuilder).Finalize()); - - typeCache = m_view->GetTypeById("msvc_RTTICompleteObjectLocator" + sigValStr); - } - - return typeCache; -} - -std::string CompleteObjectLocator::GetAssociatedClassName() -{ - if (auto subObjectTypeDesc = GetSubObjectTypeDescriptor()) - { - return subObjectTypeDesc->GetDemangledName(); - } - return GetTypeDescriptor().GetDemangledName(); -} - -Ref CompleteObjectLocator::CreateSymbol() -{ - Ref COLocSym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(COLocSym); - m_view->DefineUserDataVariable(m_address, GetType()); - return COLocSym; -} - -std::string CompleteObjectLocator::GetSymbolName() -{ - std::string symName = GetTypeDescriptor().GetDemangledName() + "::`RTTI Complete Object Locator'"; - if (auto subObjectTypeDesc = GetSubObjectTypeDescriptor()) - { - symName = symName + "{for `" + subObjectTypeDesc->GetDemangledName() + "'}"; - } - return symName; -} - -std::string CompleteObjectLocator::GetClassName() -{ - return GetTypeDescriptor().GetDemangledName(); -} \ No newline at end of file diff --git a/src/plugin.cpp b/src/plugin.cpp index 46c1b54..4e2dbd2 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -1,381 +1,70 @@ -#include +#include "rtti.h" -#include "object_locator.h" -#include "constructor.h" -#include "utils.h" +#include using namespace BinaryNinja; -// Used to prevent reading over segment bounds. -const size_t COLocatorSize = 24; +static Ref rttiBackgroundTask = nullptr; +static Ref vftBackgroundTask = nullptr; -void CreateConstructorsAtFunction(BinaryView* view, Function* func) +void ScanRTTI(Ref view) { - Constructor constructor = Constructor(view, func); - // Skip invalid & already tagged constructors. - if (!constructor.IsValid() || !func->GetFunctionTagsOfType(GetConstructorTagType(view)).empty()) - return; - - // TODO: Apply this to the return type. - Ref objType = constructor.GetRootVirtualFunctionTable()->GetObjectType(); - if (objType == nullptr) - { - LogError("Invalid class type for constructor %llx!", func->GetStart()); - return; - } - - // TODO: Doing any changes to the func here do not get applied... - auto newVFuncType = [](BinaryView* bv, Ref funcType, Ref thisType) { - auto newFuncType = TypeBuilder(funcType); - auto adjustedParams = newFuncType.GetParameters(); - if (adjustedParams.empty()) - adjustedParams.push_back({}); - adjustedParams.at(0) = FunctionParameter("this", Type::PointerType(bv->GetAddressSize(), thisType)); - newFuncType.SetParameters(adjustedParams); - return newFuncType.Finalize(); - }; - - func->SetUserType(newVFuncType(view, func->GetType(), objType)); - - // Apply to function name. - constructor.CreateSymbol(); + std::thread scanThread([view = std::move(view)]() { + rttiBackgroundTask = new BackgroundTask("Scanning for RTTI...", false); + auto processor = MicrosoftRTTIProcessor(view); + processor.ProcessRTTI(); + view->StoreMetadata(VIEW_METADATA_MSVC, processor.SerializedMetadata(), true); + rttiBackgroundTask->Finish(); + }); + scanThread.detach(); } -void CreateSymbolsFromCOLocatorAddress(BinaryView* view, uint64_t address) +void ScanVFT(Ref view) { - CompleteObjectLocator coLocator = CompleteObjectLocator(view, address); - if (!coLocator.IsValid()) - { - LogError("Invalid Colocator! %llx", coLocator.m_address); - return; - } - - ClassHierarchyDescriptor classDesc = coLocator.GetClassHierarchyDescriptor(); - TypeDescriptor typeDesc = coLocator.GetTypeDescriptor(); - BaseClassArray baseClassArray = classDesc.GetBaseClassArray(); - std::optional vfTable = coLocator.GetVirtualFunctionTable(); - if (!vfTable.has_value()) - { - LogError("Invalid virtual function table for CoLocator! %llx", coLocator.m_address); - return; - } - - for (auto&& baseClassDesc : baseClassArray.GetBaseClassDescriptors()) - { - baseClassDesc.CreateSymbol(); - } - - coLocator.CreateSymbol(); - vfTable->CreateSymbol(); - typeDesc.CreateSymbol(); - classDesc.CreateSymbol(); - baseClassArray.CreateSymbol(); - - // Add tag to objLocator... - view->CreateUserDataTag(coLocator.m_address, GetCOLocatorTagType(view), coLocator.GetClassName()); - // Add tag to vfTable... - view->CreateUserDataTag(vfTable->m_address, GetVirtualFunctionTableTagType(view), vfTable->GetSymbolName()); + std::thread scanThread([view = std::move(view)]() { + vftBackgroundTask = new BackgroundTask("Scanning for VFTs...", false); + auto processor = MicrosoftRTTIProcessor(view); + processor.ProcessVFT(); + view->StoreMetadata(VIEW_METADATA_MSVC, processor.SerializedMetadata(), true); + vftBackgroundTask->Finish(); + }); + scanThread.detach(); } -void GenerateClassTypes(BinaryView* view) +bool MetadataExists(Ref view) { - std::string undoId = view->BeginUndoActions(); - view->BeginBulkModifySymbols(); - - auto newVFuncType = [](BinaryView* bv, Ref funcType, Ref thisType) { - auto newFuncType = TypeBuilder(funcType); - auto adjustedParams = newFuncType.GetParameters(); - if (adjustedParams.empty()) - adjustedParams.push_back({}); - adjustedParams.at(0) = FunctionParameter("this", Type::PointerType(bv->GetAddressSize(), thisType)); - newFuncType.SetParameters(adjustedParams); - return newFuncType.Finalize(); - }; - auto vftTagType = GetVirtualFunctionTagType(view); - - for (auto coLocatorTag : view->GetAllTagReferencesOfType(GetCOLocatorTagType(view))) - { - auto coLocator = CompleteObjectLocator(view, coLocatorTag.addr); - auto vfTable = coLocator.GetVirtualFunctionTable(); - auto className = coLocator.GetClassName(); - - // Create the classes type. - auto classTy = vfTable->GetObjectType(); - - if (classTy == nullptr) - { - LogError("Invalid class type for CoLocator! %llx", coLocatorTag.addr); - return; - } - - size_t vFuncIdx = 0; - for (auto&& vFunc : vfTable->GetVirtualFunctions()) - { - // Must be owned by the class, no inheritence, OR must be unique to the vtable. - if (vFunc.IsUnique()) - { - // TODO: Check here to see if we remove it more than once, if so are "unique" vFunc isnt so unique... - // Remove "Unresolved ownership" tag. - vFunc.m_func->RemoveUserFunctionTagsOfType(vftTagType); - vFunc.m_func->CreateUserFunctionTag(vftTagType, "Resolved to " + className, true); - vFunc.CreateSymbol(className + "::vFunc_" + std::to_string(vFuncIdx)); - vFunc.m_func->SetUserType(newVFuncType(view, vFunc.m_func->GetType(), classTy)); - } - else if (vFunc.m_func->GetUserFunctionTagsOfType(vftTagType).empty()) - { - vFunc.m_func->CreateUserFunctionTag(vftTagType, "Unresolved ownership", true); - } - vFuncIdx++; - } - } - - view->EndBulkModifySymbols(); - view->CommitUndoActions(undoId); - view->Reanalyze(); + return view->QueryMetadata(VIEW_METADATA_MSVC) != nullptr; } -void ScanRTTIView(BinaryView* view) -{ - uint64_t bvStartAddr = view->GetStart(); - std::string undoId = view->BeginUndoActions(); - view->BeginBulkModifySymbols(); - BinaryReader optReader = BinaryReader(view); - auto addrSize = view->GetAddressSize(); - - // Scan data sections for colocators. - for (Ref segment : view->GetSegments()) - { - if (segment->GetFlags() == (SegmentReadable | SegmentContainsData)) - { - LogDebug("Attempting to find CompleteObjectLocators in segment %llx", segment->GetStart()); - for (uint64_t currAddr = segment->GetStart(); currAddr < segment->GetEnd() - COLocatorSize; - currAddr += addrSize) - { - optReader.Seek(currAddr); - uint32_t sigVal = optReader.Read32(); - if (sigVal == 1 && addrSize == 8) - { - optReader.SeekRelative(16); - if (optReader.Read32() == currAddr - bvStartAddr) - { - CreateSymbolsFromCOLocatorAddress(view, currAddr); - } - } - else if (sigVal == 0 && addrSize == 4) - { - // Check ?AV - optReader.SeekRelative(8); - uint64_t typeDescNameAddr = optReader.Read32() + 8; - if (typeDescNameAddr > view->GetStart() && typeDescNameAddr < view->GetEnd()) - { - // Make sure we do not read across segment boundary. - auto typeDescSegment = view->GetSegmentAt(typeDescNameAddr); - if (typeDescSegment != nullptr && typeDescSegment->GetEnd() - typeDescNameAddr > 4) - { - optReader.Seek(typeDescNameAddr); - if (optReader.ReadString(4) == ".?AV") - { - CreateSymbolsFromCOLocatorAddress(view, currAddr); - } - } - } - } - } - } - } - view->EndBulkModifySymbols(); - view->CommitUndoActions(undoId); - view->Reanalyze(); -} +extern "C" { +BN_DECLARE_CORE_ABI_VERSION -void ScanConstructorView(BinaryView* view) +BINARYNINJAPLUGIN bool CorePluginInit() { - std::string undoId = view->BeginUndoActions(); - view->BeginBulkModifySymbols(); - - std::vector funcRefs = {}; - for (auto vtableTag : view->GetAllTagReferencesOfType(GetVirtualFunctionTableTagType(view))) - { - for (auto codeRef : view->GetCodeReferences(vtableTag.addr)) - { - uint64_t funcStart = codeRef.func->GetStart(); - if (std::find(funcRefs.begin(), funcRefs.end(), funcStart) != funcRefs.end()) - continue; - funcRefs.push_back(funcStart); - CreateConstructorsAtFunction(view, codeRef.func); - } - } - - view->EndBulkModifySymbols(); - view->CommitUndoActions(undoId); - view->Reanalyze(); + // TODO: In the future we will have a module level workflow which: + // TODO: 1. Symbolizes RTTI information + // TODO: 2. Creates Virtual Function Tables + // TODO: 3. Populates MSVC metadata entry + // TODO: And a function level workflow which: + // TODO: 1. Uses MSVC metadata to identify if a function is apart of a VFT + // TODO: 2. Identify if the function is unique to a class, renaming and retyping if true + // TODO: 3. Identify functions which address a VFT and are probably a constructor (alloc use), retyping if true + // TODO: 4. Identify functions which address a VFT and are probably a deconstructor (free use), retyping if true + + // Ref msvcWorkflow = Workflow::Instance("core.function.defaultAnalysis")->Clone("MSVCWorkflow"); + // msvcWorkflow->RegisterActivity(new Activity("extension.msvc.rttiAnalysis", &RTTIAnalysis)); + // msvcWorkflow->Insert("core.module.defaultAnalysis", "extension.msvc.rttiAnalysis"); + // Workflow::RegisterWorkflow(msvcWorkflow, + // R"#({ + // "title" : "MSVC Workflow", + // "description" : "Analyze MSVC RTTI", + // "capabilities" : [] + // })#"); + + PluginCommand::Register("MSVC\\Find RTTI", "Scans for all RTTI in view.", ScanRTTI); + PluginCommand::Register("MSVC\\Find VFTs", "Scans for all VFTs in the view.", ScanVFT, MetadataExists); + + return true; } - -void ScanClassFieldsView(BinaryView* view) -{ - std::string undoId = view->BeginUndoActions(); - view->BeginBulkModifySymbols(); - - auto applyFieldAccessesToNamedType = [](BinaryView* bv, Ref targetType) { - auto typeName = targetType->GetStructureName(); - bool newMemberAdded = false; - auto appliedStruct = bv->CreateStructureFromOffsetAccess(typeName, &newMemberAdded); - if (newMemberAdded) - { - bv->DefineUserType(typeName, targetType->WithReplacedStructure(targetType->GetStructure(), appliedStruct)); - } - }; - - for (auto coLocatorTag : view->GetAllTagReferencesOfType(GetCOLocatorTagType(view))) - { - auto coLocator = CompleteObjectLocator(view, coLocatorTag.addr); - - for (auto baseClassDesc : coLocator.GetClassHierarchyDescriptor().GetBaseClassArray().GetBaseClassDescriptors()) - { - auto baseClassType = - view->GetTypeByName(QualifiedName(baseClassDesc.GetTypeDescriptor().GetDemangledName())); - if (baseClassType != nullptr) - { - applyFieldAccessesToNamedType(view, baseClassType); - } - } - - auto classType = view->GetTypeByName(QualifiedName(coLocator.GetClassName())); - if (classType != nullptr) - { - applyFieldAccessesToNamedType(view, classType); - } - } - - view->EndBulkModifySymbols(); - view->CommitUndoActions(undoId); - view->Reanalyze(); } - -void GenerateConstructorGraphViz(BinaryView* view) -{ - std::stringstream out; - out << "digraph Constructors {node [shape=record];\n"; - - auto tagType = GetConstructorTagType(view); - for (auto constructorTag : view->GetAllTagReferencesOfType(tagType)) - { - auto classNamedType = GetPointerTypeChildStructure( - constructorTag.func->GetVariableType(constructorTag.func->GetParameterVariables()->front())); - auto classNamedTypeRef = classNamedType->GetNamedTypeReference(); - if (classNamedType == nullptr || classNamedTypeRef == nullptr) - { - LogWarn("class with data (%s) has invalid this param", constructorTag.tag->GetData().c_str()); - continue; - } - - auto className = classNamedType->GetTypeName().GetString(); - out << '"' << className << '"' << " [label=\"{" << className; - - auto classType = view->GetTypeById(classNamedTypeRef->GetTypeId()); - if (!classType->IsStructure()) - { - LogWarn("class %s has invalid this param, not a structure...", className.c_str()); - continue; - } - - out << "}\"];\n"; - - auto classTypeStruct = classType->GetStructure(); - for (auto baseStruct : classTypeStruct->GetBaseStructures()) - { - out << '"' << className << "\"->\"" << baseStruct.type->GetName().GetString() << "\";\n"; - } - } - - out << "}"; - view->ShowPlainTextReport("MSVC Constructor GraphViz DOT", out.str()); -} - -void MakeComponents(BinaryView* view) -{ - std::string undoId = view->BeginUndoActions(); - auto classesComp = view->CreateComponentWithName("MSVC Classes"); - for (auto coLocatorTag : view->GetAllTagReferencesOfType(GetCOLocatorTagType(view))) - { - auto coLocator = CompleteObjectLocator(view, coLocatorTag.addr); - - auto className = coLocator.GetClassName(); - if (coLocator.IsSubObject()) - { - className = className + " (" + coLocator.GetAssociatedClassName() + ")"; - } - - auto classComp = view->CreateComponentWithName(className, classesComp->GetGuid()); - - DataVariable coLocatorVar = {}; - if (view->GetDataVariableAtAddress(coLocator.m_address, coLocatorVar)) - { - classComp->AddDataVariable(coLocatorVar); - } - - if (auto vtable = coLocator.GetVirtualFunctionTable()) - { - DataVariable vtableVar = {}; - if (view->GetDataVariableAtAddress(coLocator.GetVirtualFunctionTable()->m_address, vtableVar)) - { - classComp->AddDataVariable(vtableVar); - } - - for (auto vFunc : vtable->GetVirtualFunctions()) - { - if (vFunc.IsUnique()) - { - classComp->AddFunction(vFunc.m_func); - } - } - } - } - view->CommitUndoActions(undoId); -} - -bool DoesRTTIExist(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Complete Object Locator"); - if (tagType != nullptr) - { - return true; - } - - return false; -} - -bool CanScanForRTTI(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Complete Object Locator"); - if (tagType == nullptr) - { - return true; - } - - return false; -} - -extern "C" -{ - BN_DECLARE_CORE_ABI_VERSION - - BINARYNINJAPLUGIN bool CorePluginInit() - { - PluginCommand::Register("MSVC\\Find RTTI", "Scans for all RTTI in view.", ScanRTTIView, CanScanForRTTI); - PluginCommand::Register("MSVC\\Generate Class Types", - "Creates class types and applies them to owned virtual functions.", GenerateClassTypes, DoesRTTIExist); - PluginCommand::Register( - "MSVC\\Find Constructors", "Scans for all constructors in view.", ScanConstructorView, DoesRTTIExist); - PluginCommand::Register("MSVC\\Find Class Fields", - "Scans for all class fields in view. Useful to run after scanning for constructors.", ScanClassFieldsView, - DoesRTTIExist); - PluginCommand::Register("MSVC\\Generate Constructors Graphviz", - "Makes a graph from all the available MSVC constructors.", GenerateConstructorGraphViz, DoesRTTIExist); - PluginCommand::Register("MSVC\\Make Class Components", - "Adds relevant data variables and functions to class components.", MakeComponents, DoesRTTIExist); - - return true; - } -} \ No newline at end of file diff --git a/src/rtti.cpp b/src/rtti.cpp new file mode 100644 index 0000000..640bc44 --- /dev/null +++ b/src/rtti.cpp @@ -0,0 +1,672 @@ +#include "rtti.h" + +using namespace BinaryNinja; + +constexpr int COL_SIG_REV0 = 0; +constexpr int COL_SIG_REV1 = 1; +constexpr int RTTI_CONFIDENCE = 100; + + +ClassHierarchyDescriptor::ClassHierarchyDescriptor(BinaryView *view, uint64_t address) +{ + BinaryReader reader = BinaryReader(view); + reader.Seek(address); + signature = reader.Read32(); + attributes = reader.Read32(); + numBaseClasses = reader.Read32(); + pBaseClassArray = static_cast(reader.Read32()); +} + + +BaseClassDescriptor::BaseClassDescriptor(BinaryView *view, uint64_t address) +{ + BinaryReader reader = BinaryReader(view); + reader.Seek(address); + pTypeDescriptor = static_cast(reader.Read32()); + numContainedBases = reader.Read32(); + where_mdisp = static_cast(reader.Read32()); + where_pdisp = static_cast(reader.Read32()); + where_vdisp = static_cast(reader.Read32()); + attributes = reader.Read32(); + pClassHierarchyDescriptor = static_cast(reader.Read32()); +} + + +BaseClassArray::BaseClassArray(BinaryView *view, uint64_t address, uint32_t length) : length(length) +{ + BinaryReader reader = BinaryReader(view); + reader.Seek(address); + descriptors = {}; + for (size_t i = 0; i < length; i++) + descriptors.emplace_back(reader.Read32()); +} + + +TypeDescriptor::TypeDescriptor(BinaryView *view, uint64_t address) +{ + BinaryReader reader = BinaryReader(view); + reader.Seek(address); + pVFTable = reader.ReadPointer(); + spare = reader.ReadPointer(); + name = reader.ReadCString(512); +} + + +CompleteObjectLocator::CompleteObjectLocator(BinaryView *view, uint64_t address) +{ + BinaryReader reader = BinaryReader(view); + reader.Seek(address); + signature = reader.Read32(); + offset = reader.Read32(); + cdOffset = reader.Read32(); + pTypeDescriptor = static_cast(reader.Read32()); + pClassHeirarchyDescriptor = static_cast(reader.Read32()); + if (signature == COL_SIG_REV1) + { + pSelf = static_cast(reader.Read32()); + } else + { + pSelf = 0; + } +} + + +std::optional ReadCompleteObjectorLocator(BinaryView *view, uint64_t address) +{ + auto coLocator = CompleteObjectLocator(view, address); + uint64_t startAddr = view->GetStart(); + uint64_t endAddr = view->GetEnd(); + + if (coLocator.signature > 1) + return std::nullopt; + + if (coLocator.signature == COL_SIG_REV1) + { + if (coLocator.pSelf + startAddr != address) + return std::nullopt; + + // Relative addrs + if (coLocator.pTypeDescriptor + startAddr > endAddr) + return std::nullopt; + + if (coLocator.pClassHeirarchyDescriptor + startAddr > endAddr) + return std::nullopt; + } else + { + // Absolute addrs + if (coLocator.pTypeDescriptor < startAddr || coLocator.pTypeDescriptor > endAddr) + return std::nullopt; + + if (coLocator.pClassHeirarchyDescriptor < startAddr || coLocator.pClassHeirarchyDescriptor > endAddr) + return std::nullopt; + } + + return coLocator; +} + + +Ref GetPMDType(BinaryView *view) +{ + auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("PMD")); + Ref typeCache = view->GetTypeById(typeId); + + if (typeCache == nullptr) + { + Ref intType = Type::IntegerType(4, true); + + StructureBuilder pmdBuilder; + pmdBuilder.AddMember(intType, "mdisp"); + pmdBuilder.AddMember(intType, "pdisp"); + pmdBuilder.AddMember(intType, "vdisp"); + + view->DefineType(typeId, QualifiedName("_PMD"), TypeBuilder::StructureType(&pmdBuilder).Finalize()); + typeCache = view->GetTypeById(typeId); + } + + return typeCache; +} + + +Ref ClassHierarchyDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy); + +Ref BaseClassDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy) +{ + auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTIBaseClassDescriptor")); + Ref typeCache = view->GetTypeById(typeId); + + if (typeCache == nullptr) + { + Ref uintType = Type::IntegerType(4, false); + + StructureBuilder baseClassDescriptorBuilder; + // Would require creating a new type for every type descriptor length. Instead just use void* + Ref pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType()) + .SetPointerBase(ptrBaseTy, 0) + .Finalize(); + baseClassDescriptorBuilder.AddMember(pTypeDescType, "pTypeDescriptor"); + baseClassDescriptorBuilder.AddMember(uintType, "numContainedBases"); + baseClassDescriptorBuilder.AddMember(GetPMDType(view), "where"); + baseClassDescriptorBuilder.AddMember(uintType, "attributes"); + Ref pClassDescType = TypeBuilder::PointerType(4, ClassHierarchyDescriptorType(view, ptrBaseTy)) + .SetPointerBase(ptrBaseTy, 0) + .Finalize(); + baseClassDescriptorBuilder.AddMember(pClassDescType, "pClassDescriptor"); + + view->DefineType(typeId, QualifiedName("_RTTIBaseClassDescriptor"), + TypeBuilder::StructureType(&baseClassDescriptorBuilder).Finalize()); + typeCache = view->GetTypeById(typeId); + } + + return typeCache; +} + + +Ref BaseClassArrayType(BinaryView *view, const uint64_t length, BNPointerBaseType ptrBaseTy) +{ + StructureBuilder baseClassArrayBuilder; + Ref pBaseClassDescType = TypeBuilder::PointerType(4, BaseClassDescriptorType(view, ptrBaseTy)) + .SetPointerBase(ptrBaseTy, 0) + .Finalize(); + baseClassArrayBuilder.AddMember( + Type::ArrayType(pBaseClassDescType, length), "arrayOfBaseClassDescriptors"); + return TypeBuilder::StructureType(&baseClassArrayBuilder).Finalize(); +} + + +Ref ClassHierarchyDescriptorType(BinaryView *view, BNPointerBaseType ptrBaseTy) +{ + auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTIClassHierarchyDescriptor")); + Ref typeCache = view->GetTypeById(typeId); + + if (typeCache == nullptr) + { + Ref uintType = Type::IntegerType(4, false); + + StructureBuilder classHierarchyDescriptorBuilder; + classHierarchyDescriptorBuilder.AddMember(uintType, "signature"); + classHierarchyDescriptorBuilder.AddMember(uintType, "attributes"); + classHierarchyDescriptorBuilder.AddMember(uintType, "numBaseClasses"); + Ref pBaseClassArrayType = TypeBuilder::PointerType(4, Type::VoidType()) + .SetPointerBase(ptrBaseTy, 0) + .Finalize(); + classHierarchyDescriptorBuilder.AddMember(pBaseClassArrayType, "pBaseClassArray"); + + view->DefineType(typeId, QualifiedName("_RTTIClassHierarchyDescriptor"), + TypeBuilder::StructureType(&classHierarchyDescriptorBuilder).Finalize()); + + typeCache = view->GetTypeById(typeId); + } + + return typeCache; +} + + +Ref CompleteObjectLocator64Type(BinaryView *view) +{ + auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTICompleteObjectLocator64")); + Ref typeCache = view->GetTypeById(typeId); + + if (typeCache == nullptr) + { + Ref arch = view->GetDefaultArchitecture(); + Ref uintType = Type::IntegerType(4, false); + + StructureBuilder completeObjectLocatorBuilder; + Ref sigEnum = EnumerationBuilder() + .AddMemberWithValue("COL_SIG_REV0", 0) + .AddMemberWithValue("COL_SIG_REV1", 1) + .Finalize(); + Ref sigType = Type::EnumerationType(arch, sigEnum, 4); + completeObjectLocatorBuilder.AddMember(sigType, "signature"); + completeObjectLocatorBuilder.AddMember(uintType, "offset"); + completeObjectLocatorBuilder.AddMember(uintType, "cdOffset"); + Ref pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType()) + .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0) + .Finalize(); + completeObjectLocatorBuilder.AddMember(pTypeDescType, "pTypeDescriptor"); + Ref pClassHierarchyDescType = TypeBuilder::PointerType( + 4, ClassHierarchyDescriptorType(view, RelativeToBinaryStartPointerBaseType)) + .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0) + .Finalize(); + completeObjectLocatorBuilder.AddMember(pClassHierarchyDescType, "pClassHierarchyDescriptor"); + Ref pSelfType = TypeBuilder::PointerType(4, Type::NamedType(view, typeId)) + .SetPointerBase(RelativeToBinaryStartPointerBaseType, 0) + .Finalize(); + completeObjectLocatorBuilder.AddMember(pSelfType, "pSelf"); + + view->DefineType(typeId, QualifiedName("_RTTICompleteObjectLocator"), + TypeBuilder::StructureType(&completeObjectLocatorBuilder).Finalize()); + + typeCache = view->GetTypeById(typeId); + } + + return typeCache; +} + + +Ref CompleteObjectLocator32Type(BinaryView *view) +{ + auto typeId = Type::GenerateAutoTypeId("msvc_rtti", QualifiedName("RTTICompleteObjectLocator32")); + Ref typeCache = view->GetTypeById(typeId); + + if (typeCache == nullptr) + { + Ref arch = view->GetDefaultArchitecture(); + Ref uintType = Type::IntegerType(4, false); + + StructureBuilder completeObjectLocatorBuilder; + Ref sigEnum = EnumerationBuilder() + .AddMemberWithValue("COL_SIG_REV0", 0) + .AddMemberWithValue("COL_SIG_REV1", 1) + .Finalize(); + Ref sigType = Type::EnumerationType(arch, sigEnum, 4); + completeObjectLocatorBuilder.AddMember(sigType, "signature"); + completeObjectLocatorBuilder.AddMember(uintType, "offset"); + completeObjectLocatorBuilder.AddMember(uintType, "cdOffset"); + Ref pTypeDescType = TypeBuilder::PointerType(4, Type::VoidType()) + .Finalize(); + completeObjectLocatorBuilder.AddMember(pTypeDescType, "pTypeDescriptor"); + Ref pClassHierarchyDescType = TypeBuilder::PointerType( + 4, ClassHierarchyDescriptorType(view, AbsolutePointerBaseType)) + .Finalize(); + completeObjectLocatorBuilder.AddMember(pClassHierarchyDescType, "pClassHierarchyDescriptor"); + + view->DefineType(typeId, QualifiedName("_RTTICompleteObjectLocator"), + TypeBuilder::StructureType(&completeObjectLocatorBuilder).Finalize()); + + typeCache = view->GetTypeById(typeId); + } + + return typeCache; +} + + +Ref TypeDescriptorType(BinaryView *view, uint64_t length) +{ + size_t addrSize = view->GetAddressSize(); + StructureBuilder typeDescriptorBuilder; + typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType(), true), "pVFTable"); + typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "spare"); + // Char array needs to be individually resized. + typeDescriptorBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true, "char"), length), "name"); + return TypeBuilder::StructureType(&typeDescriptorBuilder).Finalize(); +} + + +Ref ClassInfo::SerializedMetadata() +{ + std::map > classInfoMeta; + classInfoMeta["className"] = new Metadata(className); + if (baseClassName.has_value()) + classInfoMeta["baseClassName"] = new Metadata(baseClassName.value()); + if (classOffset.has_value()) + classInfoMeta["classOffset"] = new Metadata(classOffset.value()); + if (vft.has_value()) + classInfoMeta["vft"] = vft->SerializedMetadata(); + return new Metadata(classInfoMeta); +} + +ClassInfo ClassInfo::DeserializedMetadata(const Ref &metadata) +{ + std::map > classInfoMeta = metadata->GetKeyValueStore(); + ClassInfo info = {classInfoMeta["className"]->GetString()}; + if (classInfoMeta.find("baseClassName") != classInfoMeta.end()) + info.baseClassName = classInfoMeta["baseClassName"]->GetString(); + if (classInfoMeta.find("classOffset") != classInfoMeta.end()) + info.classOffset = classInfoMeta["classOffset"]->GetUnsignedInteger(); + if (classInfoMeta.find("vft") != classInfoMeta.end()) + info.vft = VirtualFunctionTableInfo::DeserializedMetadata(classInfoMeta["vft"]); + return info; +} + +Ref VirtualFunctionTableInfo::SerializedMetadata() +{ + std::vector > funcsMeta; + funcsMeta.reserve(virtualFunctions.size()); + for (auto &vFunc: virtualFunctions) + funcsMeta.emplace_back(vFunc.SerializedMetadata()); + std::map > vftMeta; + vftMeta["address"] = new Metadata(address); + vftMeta["functions"] = new Metadata(funcsMeta); + return new Metadata(vftMeta); +} + +VirtualFunctionTableInfo VirtualFunctionTableInfo::DeserializedMetadata(const Ref &metadata) +{ + std::map > vftMeta = metadata->GetKeyValueStore(); + VirtualFunctionTableInfo vftInfo = {vftMeta["address"]->GetUnsignedInteger()}; + if (vftMeta.find("functions") != vftMeta.end()) + { + for (auto &entry: vftMeta["functions"]->GetArray()) + vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo::DeserializedMetadata(entry)); + } + return vftInfo; +} + +Ref VirtualFunctionInfo::SerializedMetadata() +{ + std::map > vFuncMeta; + vFuncMeta["address"] = new Metadata(funcAddr); + return new Metadata(vFuncMeta); +} + +VirtualFunctionInfo VirtualFunctionInfo::DeserializedMetadata(const Ref &metadata) +{ + std::map > vFuncMeta = metadata->GetKeyValueStore(); + VirtualFunctionInfo vFuncInfo = {vFuncMeta["address"]->GetUnsignedInteger()}; + return vFuncInfo; +} + +Ref MicrosoftRTTIProcessor::SerializedMetadata() +{ + std::map > classesMeta; + for (auto &[coLocatorAddr, classInfo]: m_classInfo) + { + auto addrStr = std::to_string(coLocatorAddr); + classesMeta[addrStr] = classInfo.SerializedMetadata(); + } + + std::map > msvcMeta; + msvcMeta["classes"] = new Metadata(classesMeta); + return new Metadata(msvcMeta); +} + +void MicrosoftRTTIProcessor::DeserializedMetadata(const Ref &metadata) +{ + std::map > msvcMeta = metadata->GetKeyValueStore(); + if (msvcMeta.find("classes") != msvcMeta.end()) + { + for (auto &[coLocatorAddrStr, classInfoMeta]: msvcMeta["classes"]->GetKeyValueStore()) + { + uint64_t coLocatorAddr = std::stoull(coLocatorAddrStr); + m_classInfo[coLocatorAddr] = ClassInfo::DeserializedMetadata(classInfoMeta); + } + } +} + +std::optional MicrosoftRTTIProcessor::DemangleName(const std::string &mangledName) +{ + QualifiedName demangledName = {}; + Ref outType = {}; + if (!DemangleMS(m_view->GetDefaultArchitecture(), mangledName, outType, demangledName, true)) + { + // Try to use LLVM demangler. + if (!DemangleLLVM(mangledName, demangledName, true)) + return allowMangledClassNames ? std::optional(mangledName) : std::nullopt; + auto demangledNameStr = demangledName.GetString(); + size_t beginFind = demangledNameStr.find_first_of(' '); + if (beginFind != std::string::npos) + demangledNameStr.erase(0, beginFind + 1); + size_t endFind = demangledNameStr.find(" `RTTI Type Descriptor Name'"); + if (endFind != std::string::npos) + demangledNameStr.erase(endFind, demangledNameStr.length()); + return demangledNameStr; + } + return demangledName.GetString(); +} + + +std::optional MicrosoftRTTIProcessor::ProcessRTTI(uint64_t coLocatorAddr) +{ + // Get complete object locator then check to see if its valid. + auto coLocator = ReadCompleteObjectorLocator(m_view, coLocatorAddr); + if (!coLocator.has_value()) + return std::nullopt; + + auto startAddr = m_view->GetStart(); + auto resolveAddr = [&](const uint64_t relAddr) { + return coLocator->signature == COL_SIG_REV1 ? startAddr + relAddr : relAddr; + }; + + auto ptrBaseTy = coLocator->signature ? RelativeToBinaryStartPointerBaseType : AbsolutePointerBaseType; + + // Get type descriptor then check to see if the class name was demangled. + auto typeDescAddr = resolveAddr(coLocator->pTypeDescriptor); + auto typeDesc = TypeDescriptor(m_view, typeDescAddr); + auto className = DemangleName(typeDesc.name); + if (!className.has_value()) + return std::nullopt; + + auto classInfo = ClassInfo{className.value()}; + if (coLocator->offset > 0) + classInfo.classOffset = coLocator->offset; + + auto typeDescSymName = fmt::format("class {} `RTTI Type Descriptor'", classInfo.className); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, typeDescSymName, typeDescAddr}); + m_view->DefineDataVariable(typeDescAddr, + Confidence(TypeDescriptorType(m_view, typeDesc.name.length()), RTTI_CONFIDENCE)); + + auto classHierarchyDescAddr = resolveAddr(coLocator->pClassHeirarchyDescriptor); + auto classHierarchyDesc = ClassHierarchyDescriptor(m_view, classHierarchyDescAddr); + auto classHierarchyDescName = fmt::format("{}::`RTTI Class Hierarchy Descriptor'", classInfo.className); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, classHierarchyDescName, classHierarchyDescAddr}); + m_view->DefineDataVariable(classHierarchyDescAddr, + Confidence(ClassHierarchyDescriptorType(m_view, ptrBaseTy), RTTI_CONFIDENCE)); + + auto baseClassArrayAddr = resolveAddr(classHierarchyDesc.pBaseClassArray); + auto baseClassArray = BaseClassArray(m_view, baseClassArrayAddr, classHierarchyDesc.numBaseClasses); + auto baseClassArrayName = fmt::format("{}::`RTTI Base Class Array'", classInfo.className); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, baseClassArrayName, baseClassArrayAddr}); + m_view->DefineDataVariable(baseClassArrayAddr, + Confidence(BaseClassArrayType(m_view, baseClassArray.length, ptrBaseTy), + RTTI_CONFIDENCE)); + + for (auto pBaseClassDescAddr: baseClassArray.descriptors) + { + auto baseClassDescAddr = resolveAddr(pBaseClassDescAddr); + auto baseClassDesc = BaseClassDescriptor(m_view, baseClassDescAddr); + + auto baseClassTypeDescAddr = resolveAddr(baseClassDesc.pTypeDescriptor); + auto baseClassTypeDesc = TypeDescriptor(m_view, baseClassTypeDescAddr); + auto baseClassName = DemangleName(baseClassTypeDesc.name); + if (!baseClassName.has_value()) + { + m_logger->LogWarn("Skipping BaseClassDescriptor with mangled name %llx", baseClassTypeDescAddr); + continue; + } + + if (baseClassDesc.where_mdisp == coLocator->offset && classInfo.className != baseClassName.value()) + classInfo.baseClassName = baseClassName; + + auto baseClassDescName = fmt::format("{}::`RTTI Base Class Descriptor at ({},{},{},{})", baseClassName.value(), + baseClassDesc.where_mdisp, baseClassDesc.where_pdisp, + baseClassDesc.where_vdisp, baseClassDesc.attributes); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, baseClassDescName, baseClassDescAddr}); + m_view->DefineDataVariable(baseClassDescAddr, + Confidence(BaseClassDescriptorType(m_view, ptrBaseTy), RTTI_CONFIDENCE)); + } + + auto coLocatorName = fmt::format("{}::`RTTI Complete Object Locator'", className.value()); + if (classInfo.baseClassName.has_value()) + coLocatorName += fmt::format("{{for `{}'}}", classInfo.baseClassName.value()); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, coLocatorName, coLocatorAddr}); + if (coLocator->signature == COL_SIG_REV1) + m_view->DefineDataVariable(coLocatorAddr, Confidence(CompleteObjectLocator64Type(m_view), RTTI_CONFIDENCE)); + else + m_view->DefineDataVariable(coLocatorAddr, Confidence(CompleteObjectLocator32Type(m_view), RTTI_CONFIDENCE)); + + return classInfo; +} + + +std::optional MicrosoftRTTIProcessor::ProcessVFT(uint64_t vftAddr, const ClassInfo &classInfo) +{ + VirtualFunctionTableInfo vftInfo = {vftAddr}; + // Gather all virtual functions + BinaryReader reader = BinaryReader(m_view); + reader.Seek(vftAddr); + std::vector > virtualFunctions = {}; + while (true) + { + uint64_t vFuncAddr = reader.ReadPointer(); + auto funcs = m_view->GetAnalysisFunctionsForAddress(vFuncAddr); + if (funcs.empty()) + { + Ref segment = m_view->GetSegmentAt(vFuncAddr); + if (segment == nullptr || !(segment->GetFlags() & (SegmentExecutable | SegmentDenyWrite))) + { + // Last CompleteObjectLocator or hit the next CompleteObjectLocator + break; + } + // TODO: Is likely a function check here? + m_logger->LogDebug("Discovered function from virtual function table... %llx", vFuncAddr); + auto vFunc = m_view->AddFunctionForAnalysis(m_view->GetDefaultPlatform(), vFuncAddr, true); + funcs.emplace_back(vFunc); + } + // Only ever add one function. + virtualFunctions.emplace_back(funcs.front()); + } + + if (virtualFunctions.empty()) + { + m_logger->LogDebug("Skipping empty virtual function table... %llx", vftAddr); + return std::nullopt; + } + + for (auto &func: virtualFunctions) + vftInfo.virtualFunctions.emplace_back(VirtualFunctionInfo{func->GetStart()}); + + // Create virtual function table type + auto vftTypeName = fmt::format("{}::VTable", classInfo.className); + if (classInfo.baseClassName.has_value()) + { + vftTypeName = fmt::format("{}::{}", classInfo.baseClassName.value(), vftTypeName); + // TODO: What is the correct form for the name? + } + // TODO: Hack the debug type id is used here to allow the PDB type (debug info) to overwrite the RTTI vtable type. + auto typeId = Type::GenerateAutoDebugTypeId(vftTypeName); + Ref vftType = m_view->GetTypeById(typeId); + + // TODO we need to inherit vtables in cases where the backing functions are coupled to a no member field vtable. + // TOOD: Inheriting vtables is a terrible idea usually btw. + + if (vftType == nullptr) + { + size_t addrSize = m_view->GetAddressSize(); + StructureBuilder vftBuilder = {}; + vftBuilder.SetPropagateDataVariableReferences(true); + size_t vFuncIdx = 0; + for (auto &&vFunc: virtualFunctions) + { + // TODO: Identify when the functions name can be used instead of the vFunc_* placeholder. + auto vFuncName = fmt::format("vFunc_{}", vFuncIdx); + // NOTE: The analyzed function type might not be available here. + vftBuilder.AddMember( + Type::PointerType(addrSize, vFunc->GetType(), true), vFuncName); + vFuncIdx++; + } + m_view->DefineType(typeId, vftTypeName, + Confidence(TypeBuilder::StructureType(&vftBuilder).Finalize(), RTTI_CONFIDENCE)); + vftType = m_view->GetTypeById(typeId); + } + + auto vftName = fmt::format("{}::`vftable'", classInfo.className); + if (classInfo.baseClassName.has_value()) + vftName += fmt::format("{{for `{}'}}", classInfo.baseClassName.value()); + m_view->DefineAutoSymbol(new Symbol{DataSymbol, vftName, vftAddr}); + m_view->DefineDataVariable(vftAddr, Confidence(vftType, RTTI_CONFIDENCE)); + return vftInfo; +} + + +MicrosoftRTTIProcessor::MicrosoftRTTIProcessor(const Ref &view, bool useMangled, bool checkRData) : m_view( + view) +{ + m_logger = new Logger("Microsoft RTTI"); + allowMangledClassNames = useMangled; + checkWritableRData = checkRData; + m_classInfo = {}; + auto metadata = view->QueryMetadata(VIEW_METADATA_MSVC); + if (metadata != nullptr) + { + // Load in metadata to the processor. + DeserializedMetadata(metadata); + } +} + + +void MicrosoftRTTIProcessor::ProcessRTTI() +{ + auto start_time = std::chrono::high_resolution_clock::now(); + uint64_t startAddr = m_view->GetStart(); + uint64_t endAddr = m_view->GetEnd(); + BinaryReader optReader = BinaryReader(m_view); + auto addrSize = m_view->GetAddressSize(); + + auto scan = [&](const Ref &segment) { + for (uint64_t coLocatorAddr = segment->GetStart(); coLocatorAddr < segment->GetEnd() - 0x18; + coLocatorAddr += addrSize) + { + optReader.Seek(coLocatorAddr); + uint32_t sigVal = optReader.Read32(); + if (sigVal == COL_SIG_REV1) + { + // Check for self reference + optReader.SeekRelative(16); + if (optReader.Read32() == coLocatorAddr - startAddr) + { + if (auto classInfo = ProcessRTTI(coLocatorAddr)) + m_classInfo[coLocatorAddr] = classInfo.value(); + } + } else if (sigVal == COL_SIG_REV0) + { + // Check ?AV + optReader.SeekRelative(8); + uint64_t typeDescNameAddr = optReader.Read32() + 8; + if (typeDescNameAddr > startAddr && typeDescNameAddr < endAddr) + { + // Make sure we do not read across segment boundary. + auto typeDescSegment = m_view->GetSegmentAt(typeDescNameAddr); + if (typeDescSegment != nullptr && typeDescSegment->GetEnd() - typeDescNameAddr > 4) + { + optReader.Seek(typeDescNameAddr); + auto typeDescNameStart = optReader.ReadString(4); + if (typeDescNameStart == ".?AV" || typeDescNameStart == ".?AU" || typeDescNameStart == ".?AW") + { + if (auto classInfo = ProcessRTTI(coLocatorAddr)) + m_classInfo[coLocatorAddr] = classInfo.value(); + } + } + } + } + } + }; + + // Scan data sections for colocators. + auto rdataSection = m_view->GetSectionByName(".rdata"); + for (const Ref &segment: m_view->GetSegments()) + { + if (segment->GetFlags() == (SegmentReadable | SegmentContainsData)) + { + m_logger->LogDebug("Attempting to find VirtualFunctionTables in segment %llx", segment->GetStart()); + scan(segment); + } else if (checkWritableRData && rdataSection && rdataSection->GetStart() == segment->GetStart()) + { + m_logger->LogDebug("Attempting to find VirtualFunctionTables in writable rdata segment %llx", + segment->GetStart()); + scan(segment); + } + } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed_time = end_time - start_time; + m_logger->LogInfo("ProcessRTTI took %f seconds", elapsed_time.count()); +} + +void MicrosoftRTTIProcessor::ProcessVFT() +{ + auto start_time = std::chrono::high_resolution_clock::now(); + for (auto &[coLocatorAddr, classInfo]: m_classInfo) + { + for (auto &ref: m_view->GetDataReferences(coLocatorAddr)) + { + auto vftAddr = ref + m_view->GetAddressSize(); + if (auto vftInfo = ProcessVFT(vftAddr, classInfo)) + m_classInfo[coLocatorAddr].vft = vftInfo.value(); + } + } + + auto end_time = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed_time = end_time - start_time; + m_logger->LogInfo("ProcessVFT took %f seconds", elapsed_time.count()); +} diff --git a/src/type_descriptor.cpp b/src/type_descriptor.cpp deleted file mode 100644 index d0565b8..0000000 --- a/src/type_descriptor.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include - -#include "object_locator.h" -#include "utils.h" - -using namespace BinaryNinja; - -TypeDescriptor::TypeDescriptor(BinaryView* view, uint64_t address) -{ - BinaryReader reader = BinaryReader(view); - reader.Seek(address); - - m_view = view; - m_address = address; - m_pVFTableValue = reader.ReadPointer(); - m_spareValue = reader.ReadPointer(); - m_nameValue = reader.ReadCString(512); -} - -std::string TypeDescriptor::GetDemangledName() -{ - QualifiedName outName = {}; - Ref outTy = {}; - - try - { - if (DemangleMS(m_view->GetDefaultArchitecture(), m_nameValue, outTy, outName, true)) - { - return outName.GetString(); - } - } - catch (const std::exception& e) - { - LogError("Failed to demangle type descriptor %llx...", m_address); - } - - return m_nameValue; -} - -Ref TypeDescriptor::GetType() -{ - size_t addrSize = m_view->GetAddressSize(); - - StructureBuilder typeDescriptorBuilder; - typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType(), true), "pVFTable"); - typeDescriptorBuilder.AddMember(Type::PointerType(addrSize, Type::VoidType()), "spare"); - // Char array needs to be individually resized. - typeDescriptorBuilder.AddMember(Type::ArrayType(Type::IntegerType(1, true, "char"), m_nameValue.length()), "name"); - - return TypeBuilder::StructureType(&typeDescriptorBuilder).Finalize(); -} - -Ref TypeDescriptor::CreateSymbol() -{ - Ref typeDescSym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(typeDescSym); - m_view->DefineUserDataVariable(m_address, GetType()); - return typeDescSym; -} - -// Example: class Animal `RTTI Type Descriptor' -std::string TypeDescriptor::GetSymbolName() -{ - return "class " + GetDemangledName() + " `RTTI Type Descriptor'"; -} \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp deleted file mode 100644 index dd3be3e..0000000 --- a/src/utils.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include - -using namespace BinaryNinja; - -std::string IntToHex(uint64_t val) -{ - std::stringstream stream; - stream << std::hex << val; - return stream.str(); -} - -Ref GetConstructorTagType(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Constructor"); - if (tagType == nullptr) - { - tagType = new TagType(view, "MSVC Constructor", "👷"); - view->AddTagType(tagType); - } - return tagType; -} - -Ref GetVirtualFunctionTableTagType(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Virtual Function Table"); - if (tagType == nullptr) - { - tagType = new TagType(view, "MSVC Virtual Function Table", "🗞"); - view->AddTagType(tagType); - } - return tagType; -} - -Ref GetVirtualFunctionTagType(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Virtual Function"); - if (tagType == nullptr) - { - tagType = new TagType(view, "MSVC Virtual Function", "☝️"); - view->AddTagType(tagType); - } - return tagType; -} - -Ref GetCOLocatorTagType(BinaryView* view) -{ - Ref tagType = view->GetTagType("MSVC Complete Object Locator"); - if (tagType == nullptr) - { - tagType = new TagType(view, "MSVC Complete Object Locator", "✨"); - view->AddTagType(tagType); - } - return tagType; -} - -Ref GetPointerTypeChildStructure(Ref ptrType) -{ - if (!ptrType->IsPointer()) - return 0; - Ref childType = ptrType->GetChildType().GetValue(); - while (childType->IsPointer()) - { - childType = childType->GetChildType().GetValue(); - } - return childType; -} - -uint64_t ResolveRelPointer(BinaryView* view, uint64_t ptrVal) -{ - switch (view->GetAddressSize()) - { - case 8: - return view->GetStart() + ptrVal; - case 4: - return ptrVal; - default: - // TODO: Handle this correctly. - return 0; - } -} - -// void GetAllMembersForStructure(BinaryView* view, Ref func, StructureBuilder structBuilder, Variable var) -// { -// auto mlil = func->GetMediumLevelIL(); -// for (auto varRef : func->GetMediumLevelILVariableReferences(var)) -// { -// auto inst = mlil[varRef.exprId]; -// } - -// // TODO: Scan the function for accesses to the var, make sure this works correctly with inherited classes, i.e. make -// // sure they dont already exist. -// } \ No newline at end of file diff --git a/src/virtual_function.cpp b/src/virtual_function.cpp deleted file mode 100644 index d5b6af2..0000000 --- a/src/virtual_function.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#include "virtual_function.h" -#include "virtual_function_table.h" -#include "object_locator.h" - -using namespace BinaryNinja; - -VirtualFunction::VirtualFunction(BinaryView* view, uint64_t vftAddr, Ref func) -{ - m_view = view; - m_vftAddr = vftAddr; - m_func = func; -} - -bool VirtualFunction::IsUnique() -{ - auto dataRefs = m_view->GetDataReferences(m_func->GetStart()); - if (dataRefs.size() == 1) - return true; - int classRoots = 0; - for (auto dataRefAddr : dataRefs) - { - if (classRoots > 1) - break; - auto vfTable = VirtualFunctionTable(m_view, dataRefAddr); - if (auto coLocator = vfTable.GetCOLocator()) - { - if (coLocator->IsValid() && coLocator->GetClassHierarchyDescriptor().m_numBaseClassesValue <= 1) - { - classRoots++; - } - } - } - return classRoots == 1; -} - -// TODO: IsThunk -// TODO: IsConstructor? -// TODO: IsDestructor? - -Ref VirtualFunction::CreateSymbol(std::string name) -{ - Ref newFuncSym = new Symbol {FunctionSymbol, name, m_func->GetStart()}; - m_view->DefineUserSymbol(newFuncSym); - return newFuncSym; -} \ No newline at end of file diff --git a/src/virtual_function_table.cpp b/src/virtual_function_table.cpp deleted file mode 100644 index d957443..0000000 --- a/src/virtual_function_table.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include - -#include "virtual_function_table.h" -#include "object_locator.h" -#include "utils.h" - -using namespace BinaryNinja; - -VirtualFunctionTable::VirtualFunctionTable(BinaryView* view, uint64_t vftAddr) -{ - m_view = view; - m_address = vftAddr; -} - -std::vector VirtualFunctionTable::GetVirtualFunctions() -{ - std::vector vFuncs = {}; - BinaryReader reader = BinaryReader(m_view); - reader.Seek(m_address); - - while (true) - { - uint64_t vFuncAddr = reader.ReadPointer(); - auto funcs = m_view->GetAnalysisFunctionsForAddress(vFuncAddr); - if (funcs.empty()) - { - Ref segment = m_view->GetSegmentAt(vFuncAddr); - if (segment == nullptr) - { - // Last CompleteObjectLocator? - break; - } - - if (segment->GetFlags() & (SegmentExecutable | SegmentDenyWrite)) - { - LogInfo("Discovered function from vtable reference -> %llx", vFuncAddr); - auto vFunc = m_view->CreateUserFunction(m_view->GetDefaultPlatform(), vFuncAddr); - funcs.emplace_back(vFunc); - } - else - { - // Hit the next CompleteObjectLocator - break; - } - } - - for (auto func : funcs) - { - vFuncs.emplace_back(VirtualFunction(m_view, m_address, func)); - } - } - - return vFuncs; -} - -std::optional VirtualFunctionTable::GetCOLocator() -{ - std::vector dataRefs = m_view->GetDataReferencesFrom(m_address - m_view->GetAddressSize()); - if (dataRefs.empty()) - { - LogError("Invalid COLocator for vtable %llx", m_address); - return std::nullopt; - } - return CompleteObjectLocator(m_view, dataRefs.front()); -} - -Ref VirtualFunctionTable::GetType() -{ - QualifiedName typeName = QualifiedName(GetTypeName()); - Ref typeCache = Type::NamedType(m_view, typeName); - - if (m_view->GetTypeByName(typeName) == nullptr) - { - size_t addrSize = m_view->GetAddressSize(); - StructureBuilder vftBuilder = {}; - vftBuilder.SetPropagateDataVariableReferences(true); - size_t vFuncIdx = 0; - for (auto&& vFunc : GetVirtualFunctions()) - { - // TODO: This needs to be fixed, must update vfunc type to this ptr to our structure. - vftBuilder.AddMember( - Type::PointerType(addrSize, vFunc.m_func->GetType(), true), "vFunc_" + std::to_string(vFuncIdx)); - vFuncIdx++; - } - - m_view->DefineUserType(typeName, TypeBuilder::StructureType(&vftBuilder).Finalize()); - - typeCache = Type::NamedType(m_view, typeName); - } - - return typeCache; -} - -Ref VirtualFunctionTable::GetObjectType() -{ - auto coLocator = GetCOLocator(); - if (!coLocator.has_value()) - return nullptr; - QualifiedName typeName = QualifiedName(coLocator->GetClassName()); - Ref typeCache = Type::NamedType(m_view, typeName); - - if (m_view->GetTypeByName(typeName) == nullptr) - { - StructureBuilder objBuilder = {}; - std::vector innerStructures = {}; - objBuilder.SetStructureType(ClassStructureType); - - for (auto baseClass : coLocator->GetClassHierarchyDescriptor().GetBaseClassArray().GetBaseClassDescriptors()) - { - auto baseClassName = baseClass.GetTypeDescriptor().GetDemangledName(); - auto baseVFTableSyms = m_view->GetSymbolsByName(baseClassName + "::`vftable'"); - if (baseVFTableSyms.empty()) - continue; - auto baseVFTable = VirtualFunctionTable(m_view, baseVFTableSyms.front()->GetAddress()); - if (baseVFTable.m_address == m_address) - continue; - // Add as base class. - auto baseClassTy = baseVFTable.GetObjectType(); - if (baseClassTy == nullptr || baseClassTy->GetNamedTypeReference() == nullptr) - { - LogError("Failed to get class type for base class %s...", baseClassName.c_str()); - continue; - } - innerStructures.emplace_back(BaseStructure( - baseClassTy->GetNamedTypeReference(), baseClass.m_where_mdispValue, baseClassTy->GetWidth())); - } - - // TODO: Maybe we should let this be overriden by the base classes... in cases where there is one at 0x0 offset - objBuilder.AddMemberAtOffset(Type::PointerType(m_view->GetAddressSize(), GetType()), "vtable", 0); - - objBuilder.SetBaseStructures(innerStructures); - - m_view->DefineUserType(typeName, TypeBuilder::StructureType(&objBuilder).Finalize()); - - typeCache = Type::NamedType(m_view, typeName); - } - - return typeCache; -} - -Ref VirtualFunctionTable::CreateSymbol() -{ - Ref newFuncSym = new Symbol {DataSymbol, GetSymbolName(), m_address}; - m_view->DefineUserSymbol(newFuncSym); - m_view->DefineUserDataVariable(m_address, GetType()); - return newFuncSym; -} - -// Example: Animal::`vftable' -// If subobject this will return: Bird::`vftable'{for `Flying'} -std::string VirtualFunctionTable::GetSymbolName() -{ - auto coLocator = GetCOLocator(); - if (!coLocator.has_value()) - return std::to_string(m_address) + "::`vftable'"; - std::string className = coLocator->GetClassName(); - if (coLocator->IsSubObject()) - return className + "::`vftable'" + "{for `" + coLocator->GetAssociatedClassName() + "'}"; - return className + "::`vftable'"; -} - -// Example: Animal::VTable -// If subobject this will return the type name of the subobject type. -std::string VirtualFunctionTable::GetTypeName() -{ - auto coLocator = GetCOLocator(); - if (!coLocator.has_value()) - return std::to_string(m_address) + "::VTable"; - return coLocator->GetAssociatedClassName() + "::VTable"; -} \ No newline at end of file