Skip to content
This repository has been archived by the owner on Nov 19, 2024. It is now read-only.

Commit

Permalink
Major update
Browse files Browse the repository at this point in the history
- Added field symbolizing
- Fixed x86 support
- Added GraphViz graph generation
- Updated to latest Binary Ninja version (ABI 40)
- Fixed many edge cases resulting in crashes and incorrect listings
- Changed naming scheme to reflect the PDB equivalent
- Fixed this param unmerging immediately in function body
- Added more tagging
- Moved all plugin commands to a submenu (now under MSVC)
  • Loading branch information
emesare committed Oct 25, 2023
1 parent 2746e10 commit 38976f6
Show file tree
Hide file tree
Showing 26 changed files with 795 additions and 523 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ cmake_install.cmake
vcpkg_installed/
CMakeFiles/

.vscode/launch.json

*.exe
*.dll

Expand Down
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "binaryninja-api"]
path = binaryninja-api
url = https://github.com/Vector35/binaryninja-api
url = https://github.com/Vector35/binaryninja-api
106 changes: 50 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,85 @@ Parses and symbolizes MSVC RTTI information in [Binary Ninja].
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).

```c
void* data_140010320 = ParentA_objLocator // ParentA
struct ParentA_vfTable =
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)() = ParentA::vFunc_1
int64_t (* const vFunc_2)() = ParentA::vFunc_2
int64_t (* const vFunc_1)() __pure = ParentA::vFunc_1
int64_t (* const vFunc_2)() __pure = ParentA::vFunc_2
}
void* data_140010340 = ParentB_objLocator // ParentB
struct ParentB_vfTable =
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)() = ParentB::vFunc_1
int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1
}
void* data_140010358 = SomeClass_objLocator // SomeClass : ParentA, ParentB
struct SomeClass_vfTable =
void* data_140010358 = SomeClass::`RTTI Complete Object Locator
struct SomeClass::VTable SomeClass::`vftable =
{
void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0
int64_t (* const vFunc_1)() = ParentA::vFunc_1
int64_t (* const vFunc_2)() = ParentA::vFunc_2
int64_t (* const vFunc_3)() = SomeClass::vFunc_3
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_objLocator // __offset(8) SomeClass : ParentA, ParentB
struct SomeClass_vfTable =
void* data_140010380 = SomeClass::`RTTI Complete Object Locator{for `ParentB}
struct ParentB::VTable SomeClass::`vftable{for `ParentB} =
{
int64_t (* const vFunc_0)(int64_t arg1, int32_t arg2) = SomeClass::vFunc_0
int64_t (* const vFunc_1)() = ParentB::vFunc_1
}
void* data_140010398 = type_info_objLocator // type_info
struct type_info_vfTable =
{
void*** (* const vFunc_0)(void*** arg1, char arg2) = type_info::vFunc_0
}
void* data_1400103a8 = std::exception_objLocator // std::exception
struct std::exception_vfTable =
{
void*** (* const vFunc_0)(void*** arg1, char arg2) = std::exception::vFunc_0
int64_t (* const vFunc_1)() = std::exception::vFunc_1
void* (* const vFunc_0)(void* arg1, int32_t arg2) = SomeClass::vFunc_0
int64_t (* const vFunc_1)() __pure = ParentB::vFunc_1
}
```
## Example Constructor Listing
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 `SomeClass` in `simple.cpp` (found in test\bins), as well as the accompanying auto created type.
```c
struct __base(ParentA, 0) __base(ParentB, 0) __data_var_refs SomeClass
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.
```cpp
class __base(Animal, 0) __base(Flying, 0) Bird
{
struct SomeClass_vfTable* vft_SomeClass;
struct __ptr_offset(0x8) SomeClass_vfTable* vft_ptr_offset(0x8) SomeClass;
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;
};
struct SomeClass* SomeClass::SomeClass(struct SomeClass* this)
class Bird* Bird::Bird(class Bird* this, int32_t arg2)
{
arg_8 = this;
ParentA::ParentA(arg_8);
ParentB::ParentB(&arg_8->vft_ptr_offset(0x8) SomeClass);
arg_8->vft_SomeClass = &SomeClass_vfTable;
arg_8->vft_ptr_offset(0x8) SomeClass = &__ptr_offset(0x8) SomeClass_vfTable;
return arg_8;
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;
}
```

## Example Virtual Function Listing

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 `SomeClass` in `simple.cpp` (found in test\bins).
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).

```c
struct SomeClass* SomeClass::vFunc_0(struct SomeClass* this, int32_t arg2)
uint64_t Bird::vFunc_0(class Bird* this)
{
sub_140001140(this);
if ((arg2 & 1) != 0)
int32_t var_18 = 0;
uint64_t field_20;
while (true)
{
j_sub_140005f3c(this);
field_20 = ((uint64_t)this->field_20);
if (var_18 >= field_20)
{
break;
}
fputs("Tweet!");
var_18 = (var_18 + 1);
}
return this;
return field_20;
}
```

## TODO

- ~~Identify virtual functions~~ and integrate with component view.
- Provide a UI to view associated classes.
- Graphviz support.
- Automatic scan on binary open.
- Provide CI for releasing new versions automatically and on all platforms.
- Provide better logging.
- Fixup cross references between defined symbols and their RVA pointers.
- Provide statistics on discovered functions and other useful information after completion.
```
[Binary Ninja]: https://binary.ninja
2 changes: 1 addition & 1 deletion binaryninja-api
Submodule binaryninja-api updated 148 files
4 changes: 3 additions & 1 deletion include/base_class_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class BaseClassArray

BaseClassArray(BinaryView* view, uint64_t address, int32_t length);
std::vector<BaseClassDescriptor> GetBaseClassDescriptors();
BaseClassDescriptor GetRootClassDescriptor();
Ref<Type> GetType();
Ref<Symbol> CreateSymbol(std::string name, std::string rawName);
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
};
5 changes: 3 additions & 2 deletions include/base_class_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ class BaseClassDescriptor
int32_t m_where_pdispValue;
int32_t m_where_vdispValue;
uint32_t m_attributesValue;
int32_t m_pClassHeirarchyDescriptorValue;
int32_t m_pClassHierarchyDescriptorValue;

BaseClassDescriptor(BinaryView* view, uint64_t address);
TypeDescriptor GetTypeDescriptor();
Ref<Symbol> CreateSymbol(std::string name);
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

using namespace BinaryNinja;

Ref<Type> GetClassHeirarchyDescriptorType();
Ref<Type> GetClassHierarchyDescriptorType();

class ClassHeirarchyDescriptor
class ClassHierarchyDescriptor
{
private:
Ref<BinaryView> m_view;
Expand All @@ -22,7 +22,9 @@ class ClassHeirarchyDescriptor
uint32_t m_numBaseClassesValue;
int32_t m_pBaseClassArrayValue;

ClassHeirarchyDescriptor(BinaryView* view, uint64_t address);
ClassHierarchyDescriptor(BinaryView* view, uint64_t address);
BaseClassArray GetBaseClassArray();
Ref<Symbol> CreateSymbol(std::string name, std::string rawName);
BaseClassDescriptor GetRootBaseClassDescriptor();
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
};
4 changes: 2 additions & 2 deletions include/constructor.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class Constructor
private:
Ref<BinaryView> m_view;
Ref<Function> m_func;
std::string GetRawName();

public:
Constructor(BinaryView* view, Function* func);
bool IsValid();
std::string GetName();
std::vector<Constructor> GetInnerConstructors();
std::optional<VirtualFunctionTable> GetRootVirtualFunctionTable();
size_t AddTag();
Ref<Type> CreateObjectType();
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
};
16 changes: 9 additions & 7 deletions include/object_locator.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
#include <binaryninjaapi.h>

#include "type_descriptor.h"
#include "class_heirarchy_descriptor.h"
#include "class_hierarchy_descriptor.h"
#include "virtual_function_table.h"

using namespace BinaryNinja;

constexpr auto COL_SIG_REV1 = 1;

Ref<Type> GetCompleteObjectLocatorType(BinaryView* view);

class CompleteObjectLocator
{
private:
Expand All @@ -25,15 +23,19 @@ class CompleteObjectLocator
uint32_t m_offsetValue;
uint32_t m_cdOffsetValue;
int32_t m_pTypeDescriptorValue;
int32_t m_pClassHeirarchyDescriptorValue;
int32_t m_pClassHierarchyDescriptorValue;
int32_t m_pSelfValue;

CompleteObjectLocator(BinaryView* view, uint64_t address);
std::string GetUniqueName();
TypeDescriptor GetTypeDescriptor();
ClassHeirarchyDescriptor GetClassHeirarchyDescriptor();
ClassHierarchyDescriptor GetClassHierarchyDescriptor();
std::optional<VirtualFunctionTable> GetVirtualFunctionTable();
bool IsValid();
bool IsSubObject();
Ref<Symbol> CreateSymbol(std::string name, std::string rawName);
std::optional<TypeDescriptor> GetSubObjectTypeDescriptor();
Ref<Type> GetType();
std::string GetAssociatedClassName();
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
std::string GetClassName();
};
3 changes: 2 additions & 1 deletion include/type_descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ class TypeDescriptor
TypeDescriptor(BinaryView* view, uint64_t address);
std::string GetDemangledName();
Ref<Type> GetType();
Ref<Symbol> CreateSymbol(std::string name, std::string rawName);
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
};
9 changes: 8 additions & 1 deletion include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@ using namespace BinaryNinja;
uint64_t ReadIntWithSize(BinaryReader* reader, size_t size);
std::string IntToHex(uint64_t val);
Ref<TagType> GetConstructorTagType(BinaryView* view);
Ref<TagType> GetVirtualFunctionTableTagType(BinaryView* view);
Ref<TagType> GetVirtualFunctionTableTagType(BinaryView* view);
Ref<TagType> GetVirtualFunctionTagType(BinaryView* view);
Ref<TagType> GetCOLocatorTagType(BinaryView* view);
Ref<Type> GetPointerTypeChildStructure(Ref<Type> ptrType);
std::optional<size_t> GetSSAVariableUnscopedDefinition(Ref<MediumLevelILFunction> mlil, SSAVariable var);
std::optional<size_t> WalkToSSAVariableOffset(Ref<MediumLevelILFunction> mlil, SSAVariable var);
std::vector<SSAVariable> GetSSAVariablesForVariable(Ref<MediumLevelILFunction> func, const Variable& var);
uint64_t ResolveRelPointer(BinaryView* view, uint64_t ptrVal);
2 changes: 1 addition & 1 deletion include/virtual_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ class VirtualFunction
// 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<Symbol> CreateSymbol(std::string name, std::string rawName);
Ref<Symbol> CreateSymbol(std::string name);
};
6 changes: 4 additions & 2 deletions include/virtual_function_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class VirtualFunctionTable
VirtualFunctionTable(BinaryView* view, uint64_t address);
std::vector<VirtualFunction> GetVirtualFunctions();
CompleteObjectLocator GetCOLocator();
Ref<Type> GetType(std::string name, std::string idName);
Ref<Symbol> CreateSymbol(std::string name, std::string rawName);
Ref<Type> GetType();
Ref<Symbol> CreateSymbol();
std::string GetSymbolName();
std::string GetTypeName();
};
2 changes: 2 additions & 0 deletions llvm-demangle/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ set_target_properties(llvm-demangle PROPERTIES
POSITION_INDEPENDENT_CODE ON
CXX_STANDARD 17)

set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON)

target_include_directories(llvm-demangle PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
23 changes: 18 additions & 5 deletions src/base_class_array.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <binaryninjaapi.h>

#include "class_heirarchy_descriptor.h"
#include "class_hierarchy_descriptor.h"
#include "utils.h"

using namespace BinaryNinja;

Expand All @@ -19,12 +20,18 @@ std::vector<BaseClassDescriptor> BaseClassArray::GetBaseClassDescriptors()

for (size_t i = 0; i < m_length; i++)
{
baseClassDescriptors.emplace_back(BaseClassDescriptor(m_view, m_view->GetStart() + reader.Read32()));
baseClassDescriptors.emplace_back(
BaseClassDescriptor(m_view, ResolveRelPointer(m_view, (uint64_t)reader.Read32())));
}

return baseClassDescriptors;
}

BaseClassDescriptor BaseClassArray::GetRootClassDescriptor()
{
return GetBaseClassDescriptors().front();
}

Ref<Type> BaseClassArray::GetType()
{
StructureBuilder baseClassArrayBuilder;
Expand All @@ -34,10 +41,16 @@ Ref<Type> BaseClassArray::GetType()
return TypeBuilder::StructureType(&baseClassArrayBuilder).Finalize();
}

Ref<Symbol> BaseClassArray::CreateSymbol(std::string name, std::string rawName)
Ref<Symbol> BaseClassArray::CreateSymbol()
{
Ref<Symbol> baseClassArraySym = new Symbol {DataSymbol, name, name, rawName, m_address};
Ref<Symbol> baseClassArraySym = new Symbol {DataSymbol, GetSymbolName(), m_address};
m_view->DefineUserSymbol(baseClassArraySym);
m_view->DefineDataVariable(m_address, GetType());
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'";
}
Loading

2 comments on commit 38976f6

@ExecuteProtect
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just read through the entire changelog and I have to say, very nicely done.

@emesare
Copy link
Owner Author

@emesare emesare commented on 38976f6 Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just read through the entire changelog and I have to say, very nicely done.

Thank you 😄

Please sign in to comment.