diff options
| author | kat <kat@vector35.com> | 2024-10-23 21:56:29 -0400 |
|---|---|---|
| committer | kat <kat@vector35.com> | 2024-10-23 21:58:47 -0400 |
| commit | 3006c68e108a18f9b8782bc937dc9ec7e2cb3b54 (patch) | |
| tree | f589bb38909349142c09b6215244205aef0a57ac /view/sharedcache/api | |
| parent | 80fcccec8f686e0e5c89626b60af121a97baf856 (diff) | |
Initial commit of the alpha dyld_shared_cache view API Plugin.
This is an early release of our DSC processing plugin. We're still hard at work improving this feature. You should be able to just drop in a dyld_shared_cache and use the 'Shared Cache Triage' view to load and analyze images.
Diffstat (limited to 'view/sharedcache/api')
| -rw-r--r-- | view/sharedcache/api/CMakeLists.txt | 55 | ||||
| -rw-r--r-- | view/sharedcache/api/MetadataSerializable.hpp | 500 | ||||
| -rw-r--r-- | view/sharedcache/api/python/CMakeLists.txt | 50 | ||||
| -rw-r--r-- | view/sharedcache/api/python/__init__.py | 8 | ||||
| -rw-r--r-- | view/sharedcache/api/python/_sharedcachecore.py | 659 | ||||
| -rw-r--r-- | view/sharedcache/api/python/_sharedcachecore_template.py | 43 | ||||
| -rw-r--r-- | view/sharedcache/api/python/generator.cpp | 617 | ||||
| -rw-r--r-- | view/sharedcache/api/python/sharedcache.py | 260 | ||||
| -rw-r--r-- | view/sharedcache/api/python/sharedcache_enums.py | 14 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcache.cpp | 224 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcacheapi.h | 820 | ||||
| -rw-r--r-- | view/sharedcache/api/sharedcachecore.h | 156 |
12 files changed, 3406 insertions, 0 deletions
diff --git a/view/sharedcache/api/CMakeLists.txt b/view/sharedcache/api/CMakeLists.txt new file mode 100644 index 00000000..b09ae321 --- /dev/null +++ b/view/sharedcache/api/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) + +project(sharedcacheapi) +file(GLOB BN_MACHO_API_SOURCES *.cpp *.h) +add_library(sharedcacheapi OBJECT ${BN_MACHO_API_SOURCES}) + +if (VIEW_NAME) + target_compile_definitions(sharedcacheapi PRIVATE VIEW_NAME="${VIEW_NAME}") +else() + error("VIEW_NAME must be defined") +endif() + +function(get_recursive_include_dirs target result) + # Initialize an empty list to store include directories + set(include_dirs "") + + # Get the include directories of the current target + get_target_property(current_target_includes ${target} INTERFACE_INCLUDE_DIRECTORIES) + if(current_target_includes) + list(APPEND include_dirs ${current_target_includes}) + endif() + + # Get the libraries that this target links to + get_target_property(linked_libraries ${target} INTERFACE_LINK_LIBRARIES) + if(linked_libraries) + foreach(linked_library IN LISTS linked_libraries) + # Skip plain library names (non-target libraries) + if(TARGET ${linked_library}) + # Recursively get include directories from linked libraries + get_recursive_include_dirs(${linked_library} linked_library_includes) + list(APPEND include_dirs ${linked_library_includes}) + endif() + endforeach() + endif() + + # Set the result to the collected include directories + set(${result} ${include_dirs} PARENT_SCOPE) +endfunction() + +get_recursive_include_dirs(binaryninjaapi INCLUDES) + +target_include_directories(sharedcacheapi + PUBLIC ${PROJECT_SOURCE_DIR} ${INCLUDES}) + +set_target_properties(sharedcacheapi PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/out) + +if (NOT DEMO) + add_subdirectory(python) +endif()
\ No newline at end of file diff --git a/view/sharedcache/api/MetadataSerializable.hpp b/view/sharedcache/api/MetadataSerializable.hpp new file mode 100644 index 00000000..dd3951ea --- /dev/null +++ b/view/sharedcache/api/MetadataSerializable.hpp @@ -0,0 +1,500 @@ +// +// Created by kat on 5/31/23. +// + +/* + * Welcome to, this file. + * + * This is a metadata serialization helper. + * + * Have you ever wished turning a complex datastructure into a Metadata object was as easy in C++ as it is in python? + * Do you like macros and templates? + * + * Great news. + * + * Implement these on your `public MetadataSerializable` subclass: + * ``` + void Store() override { + MSS(m_someVariable); + MSS(m_someOtherVariable); + } + void Load() override { + MSL(m_someVariable); + MSL(m_someOtherVariable); + } + ``` + * Then, you can turn your object into a Metadata object with `AsMetadata()`, and load it back with + `LoadFromMetadata()`. + * + * Serialized fields will be automatically repopulated. + * + * Other ser/deser formats (rapidjson objects, strings) also exist. You can use these to achieve nesting, but probably + avoid that. + * */ + +#include "binaryninjaapi.h" +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/prettywriter.h" + +#ifndef SHAREDCACHE_METADATASERIALIZABLE_HPP +#define SHAREDCACHE_METADATASERIALIZABLE_HPP + +#define MSS(name) store(#name, name) +#define MSS_CAST(name, type) store(#name, (type) name) +#define MSS_SUBCLASS(name) Serialize(#name, name) +#define MSL(name) name = load(#name, name) +#define MSL_CAST(name, storedType, type) name = (type)load(#name, (storedType) name) +#define MSL_SUBCLASS(name) Deserialize(#name, name) + +using namespace BinaryNinja; + +class MetadataSerializable +{ +protected: + struct SerialContext + { + rapidjson::Document doc; + rapidjson::Document::AllocatorType allocator; + }; + struct DeserContext + { + rapidjson::Document doc; + }; + + DeserContext m_activeDeserContext; + SerialContext m_activeContext; + +public: + MetadataSerializable() + { + m_activeContext.doc.SetObject(); + m_activeContext.allocator = m_activeContext.doc.GetAllocator(); + } + + // copy constructor + MetadataSerializable(const MetadataSerializable& other) + { + m_activeContext.doc.CopyFrom(other.m_activeContext.doc, m_activeContext.doc.GetAllocator()); + } + + // copy assignment + MetadataSerializable& operator=(const MetadataSerializable& other) + { + m_activeContext.doc.CopyFrom(other.m_activeContext.doc, m_activeContext.doc.GetAllocator()); + return *this; + } + + virtual ~MetadataSerializable() + { + } + + void SetupSerContext(rapidjson::Document::AllocatorType* alloc = nullptr) + { + m_activeContext.doc.SetObject(); + m_activeContext.allocator = m_activeContext.doc.GetAllocator(); + } + void S() + { + // fixme factor out + } + void Serialize(std::string& name, bool b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, bool& b) { b = m_activeDeserContext.doc[name.c_str()].GetBool(); } + + void Serialize(std::string& name, uint8_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, uint8_t& b) + { + b = static_cast<uint8_t>(m_activeDeserContext.doc[name.c_str()].GetUint64()); + } + + void Serialize(std::string& name, uint16_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, uint16_t& b) + { + b = static_cast<uint16_t>(m_activeDeserContext.doc[name.c_str()].GetUint64()); + } + + void Serialize(std::string& name, uint32_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, uint32_t& b) + { + b = static_cast<uint32_t>(m_activeDeserContext.doc[name.c_str()].GetUint64()); + } + + void Serialize(std::string& name, uint64_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, uint64_t& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetUint64(); + } + + void Serialize(std::string& name, int8_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, int8_t& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetInt64(); + } + + void Serialize(std::string& name, int16_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, int16_t& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetInt64(); + } + + void Serialize(std::string& name, int32_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, int32_t& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetInt(); + } + + void Serialize(std::string& name, int64_t b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, b, m_activeContext.allocator); + } + void Deserialize(std::string& name, int64_t& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetInt64(); + } + + void Serialize(std::string& name, std::string b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value value(b.c_str(), m_activeContext.allocator); + m_activeContext.doc.AddMember(key, value, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::string& b) + { + b = m_activeDeserContext.doc[name.c_str()].GetString(); + } + + void Serialize(std::string& name, std::map<uint64_t, std::string> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value p(rapidjson::kArrayType); + p.PushBack(i.first, m_activeContext.allocator); + rapidjson::Value value(i.second.c_str(), m_activeContext.allocator); + p.PushBack(value, m_activeContext.allocator); + bArr.PushBack(p, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::map<uint64_t, std::string>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString(); + } + + void Serialize(std::string& name, std::unordered_map<uint64_t, std::string> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value p(rapidjson::kArrayType); + p.PushBack(i.first, m_activeContext.allocator); + rapidjson::Value value(i.second.c_str(), m_activeContext.allocator); + p.PushBack(value, m_activeContext.allocator); + bArr.PushBack(p, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Serialize(std::string& name, std::unordered_map<std::string, std::string> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value p(rapidjson::kArrayType); + rapidjson::Value key(i.first.c_str(), m_activeContext.allocator); + rapidjson::Value value(i.second.c_str(), m_activeContext.allocator); + p.PushBack(key, m_activeContext.allocator); + p.PushBack(value, m_activeContext.allocator); + bArr.PushBack(p, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::unordered_map<uint64_t, std::string>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetString(); + } + + void Serialize(std::string& name, std::unordered_map<uint64_t, uint64_t> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value p(rapidjson::kArrayType); + p.PushBack(i.first, m_activeContext.allocator); + p.PushBack(i.second, m_activeContext.allocator); + bArr.PushBack(p, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::unordered_map<uint64_t, uint64_t>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + b[i.GetArray()[0].GetUint64()] = i.GetArray()[1].GetUint64(); + } + + // std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>> + void Serialize(std::string& name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value classes(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value classArr(rapidjson::kArrayType); + rapidjson::Value classKey(i.first.c_str(), m_activeContext.allocator); + classArr.PushBack(classKey, m_activeContext.allocator); + rapidjson::Value membersArr(rapidjson::kArrayType); + for (auto& j : i.second) + { + rapidjson::Value member(rapidjson::kArrayType); + member.PushBack(j.first, m_activeContext.allocator); + member.PushBack(j.second, m_activeContext.allocator); + membersArr.PushBack(member, m_activeContext.allocator); + } + classArr.PushBack(membersArr, m_activeContext.allocator); + classes.PushBack(classArr, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, classes, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::unordered_map<std::string, std::unordered_map<uint64_t, uint64_t>>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + { + std::string key = i.GetArray()[0].GetString(); + std::unordered_map<uint64_t, uint64_t> memArray; + for (auto& member : i.GetArray()[1].GetArray()) + { + memArray[member.GetArray()[0].GetUint64()] = member.GetArray()[1].GetUint64(); + } + b[key] = memArray; + } + } + + void Deserialize(std::string& name, std::unordered_map<std::string, std::string>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetString(); + } + + void Serialize(std::string& name, std::vector<std::string> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (const auto& s : b) + { + rapidjson::Value value(s.c_str(), m_activeContext.allocator); + bArr.PushBack(value, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::vector<std::string>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + b.emplace_back(i.GetString()); + } + + void Serialize(std::string& name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value segV(rapidjson::kArrayType); + segV.PushBack(i.first, m_activeContext.allocator); + segV.PushBack(i.second.first, m_activeContext.allocator); + segV.PushBack(i.second.second, m_activeContext.allocator); + bArr.PushBack(segV, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::vector<std::pair<uint64_t, std::pair<uint64_t, uint64_t>>>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + { + std::pair<uint64_t, std::pair<uint64_t, uint64_t>> j; + j.first = i.GetArray()[0].GetUint64(); + j.second.first = i.GetArray()[1].GetUint64(); + j.second.second = i.GetArray()[2].GetUint64(); + b.push_back(j); + } + } + + void Serialize(std::string& name, std::vector<std::pair<uint64_t, bool>> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value segV(rapidjson::kArrayType); + segV.PushBack(i.first, m_activeContext.allocator); + segV.PushBack(i.second, m_activeContext.allocator); + bArr.PushBack(segV, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::vector<std::pair<uint64_t, bool>>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + { + std::pair<uint64_t, bool> j; + j.first = i.GetArray()[0].GetUint64(); + j.second = i.GetArray()[1].GetBool(); + b.push_back(j); + } + } + + void Serialize(std::string& name, std::vector<uint64_t> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + bArr.PushBack(i, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::vector<uint64_t>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + { + b.push_back(i.GetUint64()); + } + } + + // std::unordered_map<std::string, uint64_t> + void Serialize(std::string& name, std::unordered_map<std::string, uint64_t> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& i : b) + { + rapidjson::Value p(rapidjson::kArrayType); + rapidjson::Value key(i.first.c_str(), m_activeContext.allocator); + p.PushBack(key, m_activeContext.allocator); + p.PushBack(i.second, m_activeContext.allocator); + bArr.PushBack(p, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + void Deserialize(std::string& name, std::unordered_map<std::string, uint64_t>& b) + { + for (auto& i : m_activeDeserContext.doc[name.c_str()].GetArray()) + { + b[i.GetArray()[0].GetString()] = i.GetArray()[1].GetUint64(); + } + } + + template <typename T> + void store(std::string x, T y) + { + Serialize(x, y); + } + + template <typename T> + T load(std::string x, T y) + { + T val; + Deserialize(x, val); + return val; + } + + rapidjson::Document& GetDoc() + { + S(); + Store(); + return m_activeContext.doc; + } + +public: + virtual void Store() = 0; + virtual void Load() = 0; + + std::string AsString() + { + rapidjson::StringBuffer strbuf; + rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(strbuf); + GetDoc().Accept(writer); + + std::string s = strbuf.GetString(); + return s; + } + rapidjson::Document& AsDocument() { return GetDoc(); } + void LoadFromString(const std::string& s) + { + m_activeDeserContext.doc.Parse(s.c_str()); + Load(); + } + void LoadFromValue(rapidjson::Value& s) + { + m_activeDeserContext.doc.CopyFrom(s, m_activeDeserContext.doc.GetAllocator()); + Load(); + } + Ref<Metadata> AsMetadata() { return new Metadata(AsString()); } + bool LoadFromMetadata(const Ref<Metadata>& meta) + { + if (!meta->IsString()) + return false; + LoadFromString(meta->GetString()); + return true; + } +}; + +#endif // SHAREDCACHE_METADATASERIALIZABLE_HPP diff --git a/view/sharedcache/api/python/CMakeLists.txt b/view/sharedcache/api/python/CMakeLists.txt new file mode 100644 index 00000000..c04a57ff --- /dev/null +++ b/view/sharedcache/api/python/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(sharedcache-python-api) + +file(GLOB PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/*.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/_sharedcachecore.py) +list(REMOVE_ITEM PYTHON_SOURCES ${PROJECT_SOURCE_DIR}/enums.py) + +add_executable(sharedcache_generator + ${PROJECT_SOURCE_DIR}/generator.cpp) +target_link_libraries(sharedcache_generator binaryninjaapi) +target_include_directories(sharedcache_generator PUBLIC {PROJECT_SOURCE_DIR}/../../api) + +set_target_properties(sharedcache_generator PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON + BUILD_WITH_INSTALL_RPATH OFF + RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +if(BN_INTERNAL_BUILD) + set(PYTHON_OUTPUT_DIRECTORY ${BN_RESOURCE_DIR}/python/binaryninja/sharedcache/) +else() + set(PYTHON_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out/plugins/sharedcache/) +endif() + +if(WIN32) + if (BN_INTERNAL_BUILD) + add_custom_command(TARGET sharedcache_generator PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BN_CORE_OUTPUT_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/) + else() + add_custom_command(TARGET sharedcache_generator PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BN_INSTALL_DIR}/binaryninjacore.dll ${PROJECT_BINARY_DIR}/) + endif() +endif() + +add_custom_target(sharedcache_generator_copy ALL + BYPRODUCTS ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PROJECT_SOURCE_DIR}/enums.py + DEPENDS ${PYTHON_SOURCES} ${PROJECT_SOURCE_DIR}/../sharedcachecore.h $<TARGET_FILE:sharedcache_generator> + COMMAND ${CMAKE_COMMAND} -E echo "Copying SharedCache Python Sources" + COMMAND ${CMAKE_COMMAND} -E make_directory ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=0 $<TARGET_FILE:sharedcache_generator> + ${PROJECT_SOURCE_DIR}/../sharedcachecore.h + ${PROJECT_SOURCE_DIR}/_sharedcachecore.py + ${PROJECT_SOURCE_DIR}/_sharedcachecore_template.py + ${PROJECT_SOURCE_DIR}/sharedcache_enums.py + + COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_SOURCES} ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/_sharedcachecore.py ${PYTHON_OUTPUT_DIRECTORY} + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/sharedcache_enums.py ${PYTHON_OUTPUT_DIRECTORY}) + diff --git a/view/sharedcache/api/python/__init__.py b/view/sharedcache/api/python/__init__.py new file mode 100644 index 00000000..1f65af93 --- /dev/null +++ b/view/sharedcache/api/python/__init__.py @@ -0,0 +1,8 @@ +import os + +from binaryninja._binaryninjacore import BNGetUserPluginDirectory +user_plugin_dir = os.path.realpath(BNGetUserPluginDirectory()) +current_path = os.path.realpath(__file__) + +from .sharedcache import * + diff --git a/view/sharedcache/api/python/_sharedcachecore.py b/view/sharedcache/api/python/_sharedcachecore.py new file mode 100644 index 00000000..93228468 --- /dev/null +++ b/view/sharedcache/api/python/_sharedcachecore.py @@ -0,0 +1,659 @@ +import binaryninja +import ctypes, os + +from typing import Optional +from . import sharedcache_enums +# Load core module +import platform +core = None +core_platform = platform.system() + +# By the time the debugger is loaded, binaryninja has not fully initialized. +# So we cannot call binaryninja.bundled_plugin_path() +from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString +if core_platform == "Darwin": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib")) + +elif core_platform == "Linux": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so")) + +elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll")) +else: + raise Exception("OS not supported") + +def cstr(var) -> Optional[ctypes.c_char_p]: + if var is None: + return None + if isinstance(var, bytes): + return var + return var.encode("utf-8") + +def pyNativeStr(arg): + if isinstance(arg, str): + return arg + else: + return arg.decode('utf8') + +def free_string(value:ctypes.c_char_p) -> None: + BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + +# Type definitions +from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle +class BNDSCBackingCache(ctypes.Structure): + @property + def path(self): + return pyNativeStr(self._path) + @path.setter + def path(self, value): + self._path = cstr(value) +BNDSCBackingCacheHandle = ctypes.POINTER(BNDSCBackingCache) +class BNDSCBackingCacheMapping(ctypes.Structure): + pass +BNDSCBackingCacheMappingHandle = ctypes.POINTER(BNDSCBackingCacheMapping) +class BNDSCImage(ctypes.Structure): + @property + def name(self): + return pyNativeStr(self._name) + @name.setter + def name(self, value): + self._name = cstr(value) +BNDSCImageHandle = ctypes.POINTER(BNDSCImage) +class BNDSCImageMemoryMapping(ctypes.Structure): + @property + def filePath(self): + return pyNativeStr(self._filePath) + @filePath.setter + def filePath(self, value): + self._filePath = cstr(value) + @property + def name(self): + return pyNativeStr(self._name) + @name.setter + def name(self, value): + self._name = cstr(value) +BNDSCImageMemoryMappingHandle = ctypes.POINTER(BNDSCImageMemoryMapping) +class BNDSCMappedMemoryRegion(ctypes.Structure): + @property + def name(self): + return pyNativeStr(self._name) + @name.setter + def name(self, value): + self._name = cstr(value) +BNDSCMappedMemoryRegionHandle = ctypes.POINTER(BNDSCMappedMemoryRegion) +class BNDSCMemoryUsageInfo(ctypes.Structure): + pass +BNDSCMemoryUsageInfoHandle = ctypes.POINTER(BNDSCMemoryUsageInfo) +class BNDSCSymbolRep(ctypes.Structure): + @property + def name(self): + return pyNativeStr(self._name) + @name.setter + def name(self, value): + self._name = cstr(value) + @property + def image(self): + return pyNativeStr(self._image) + @image.setter + def image(self, value): + self._image = cstr(value) +BNDSCSymbolRepHandle = ctypes.POINTER(BNDSCSymbolRep) +DSCViewLoadProgressEnum = ctypes.c_int +DSCViewStateEnum = ctypes.c_int +class BNSharedCache(ctypes.Structure): + pass +BNSharedCacheHandle = ctypes.POINTER(BNSharedCache) + +# Structure definitions +BNDSCBackingCache._fields_ = [ + ("_path", ctypes.c_char_p), + ("isPrimary", ctypes.c_bool), + ("mappings", ctypes.POINTER(BNDSCBackingCacheMapping)), + ("mappingCount", ctypes.c_ulonglong), + ] +BNDSCBackingCacheMapping._fields_ = [ + ("vmAddress", ctypes.c_ulonglong), + ("size", ctypes.c_ulonglong), + ("fileOffset", ctypes.c_ulonglong), + ] +BNDSCImage._fields_ = [ + ("_name", ctypes.c_char_p), + ("headerAddress", ctypes.c_ulonglong), + ("mappings", ctypes.POINTER(BNDSCImageMemoryMapping)), + ("mappingCount", ctypes.c_ulonglong), + ] +BNDSCImageMemoryMapping._fields_ = [ + ("_filePath", ctypes.c_char_p), + ("_name", ctypes.c_char_p), + ("vmAddress", ctypes.c_ulonglong), + ("size", ctypes.c_ulonglong), + ("loaded", ctypes.c_bool), + ("rawViewOffset", ctypes.c_ulonglong), + ] +BNDSCMappedMemoryRegion._fields_ = [ + ("vmAddress", ctypes.c_ulonglong), + ("size", ctypes.c_ulonglong), + ("_name", ctypes.c_char_p), + ] +BNDSCMemoryUsageInfo._fields_ = [ + ("sharedCacheRefs", ctypes.c_ulonglong), + ("mmapRefs", ctypes.c_ulonglong), + ] +BNDSCSymbolRep._fields_ = [ + ("address", ctypes.c_ulonglong), + ("_name", ctypes.c_char_p), + ("_image", ctypes.c_char_p), + ] + +# Function definitions +# ------------------------------------------------------- +# _BNDSCFindSymbolAtAddressAndApplyToAddress + +_BNDSCFindSymbolAtAddressAndApplyToAddress = core.BNDSCFindSymbolAtAddressAndApplyToAddress +_BNDSCFindSymbolAtAddressAndApplyToAddress.restype = None +_BNDSCFindSymbolAtAddressAndApplyToAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ctypes.c_ulonglong, + ctypes.c_bool, + ] + + +# noinspection PyPep8Naming +def BNDSCFindSymbolAtAddressAndApplyToAddress( + cache: ctypes.POINTER(BNSharedCache), + symbolLocation: int, + targetLocation: int, + triggerReanalysis: bool + ) -> None: + return _BNDSCFindSymbolAtAddressAndApplyToAddress(cache, symbolLocation, targetLocation, triggerReanalysis) + + +# ------------------------------------------------------- +# _BNDSCViewFastGetBackingCacheCount + +_BNDSCViewFastGetBackingCacheCount = core.BNDSCViewFastGetBackingCacheCount +_BNDSCViewFastGetBackingCacheCount.restype = ctypes.c_ulonglong +_BNDSCViewFastGetBackingCacheCount.argtypes = [ + ctypes.POINTER(BNBinaryView), + ] + + +# noinspection PyPep8Naming +def BNDSCViewFastGetBackingCacheCount( + view: ctypes.POINTER(BNBinaryView) + ) -> int: + return _BNDSCViewFastGetBackingCacheCount(view) + + +# ------------------------------------------------------- +# _BNDSCViewFreeAllImages + +_BNDSCViewFreeAllImages = core.BNDSCViewFreeAllImages +_BNDSCViewFreeAllImages.restype = None +_BNDSCViewFreeAllImages.argtypes = [ + ctypes.POINTER(BNDSCImage), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewFreeAllImages( + images: ctypes.POINTER(BNDSCImage), + count: int + ) -> None: + return _BNDSCViewFreeAllImages(images, count) + + +# ------------------------------------------------------- +# _BNDSCViewFreeBackingCaches + +_BNDSCViewFreeBackingCaches = core.BNDSCViewFreeBackingCaches +_BNDSCViewFreeBackingCaches.restype = None +_BNDSCViewFreeBackingCaches.argtypes = [ + ctypes.POINTER(BNDSCBackingCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewFreeBackingCaches( + caches: ctypes.POINTER(BNDSCBackingCache), + count: int + ) -> None: + return _BNDSCViewFreeBackingCaches(caches, count) + + +# ------------------------------------------------------- +# _BNDSCViewFreeLoadedRegions + +_BNDSCViewFreeLoadedRegions = core.BNDSCViewFreeLoadedRegions +_BNDSCViewFreeLoadedRegions.restype = None +_BNDSCViewFreeLoadedRegions.argtypes = [ + ctypes.POINTER(BNDSCMappedMemoryRegion), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewFreeLoadedRegions( + images: ctypes.POINTER(BNDSCMappedMemoryRegion), + count: int + ) -> None: + return _BNDSCViewFreeLoadedRegions(images, count) + + +# ------------------------------------------------------- +# _BNDSCViewFreeSymbols + +_BNDSCViewFreeSymbols = core.BNDSCViewFreeSymbols +_BNDSCViewFreeSymbols.restype = None +_BNDSCViewFreeSymbols.argtypes = [ + ctypes.POINTER(BNDSCSymbolRep), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewFreeSymbols( + symbols: ctypes.POINTER(BNDSCSymbolRep), + count: int + ) -> None: + return _BNDSCViewFreeSymbols(symbols, count) + + +# ------------------------------------------------------- +# _BNDSCViewGetAllImages + +_BNDSCViewGetAllImages = core.BNDSCViewGetAllImages +_BNDSCViewGetAllImages.restype = ctypes.POINTER(BNDSCImage) +_BNDSCViewGetAllImages.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetAllImages( + cache: ctypes.POINTER(BNSharedCache), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNDSCImage)]: + result = _BNDSCViewGetAllImages(cache, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNDSCViewGetBackingCaches + +_BNDSCViewGetBackingCaches = core.BNDSCViewGetBackingCaches +_BNDSCViewGetBackingCaches.restype = ctypes.POINTER(BNDSCBackingCache) +_BNDSCViewGetBackingCaches.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetBackingCaches( + cache: ctypes.POINTER(BNSharedCache), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNDSCBackingCache)]: + result = _BNDSCViewGetBackingCaches(cache, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNDSCViewGetImageHeaderForAddress + +_BNDSCViewGetImageHeaderForAddress = core.BNDSCViewGetImageHeaderForAddress +_BNDSCViewGetImageHeaderForAddress.restype = ctypes.POINTER(ctypes.c_byte) +_BNDSCViewGetImageHeaderForAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetImageHeaderForAddress( + cache: ctypes.POINTER(BNSharedCache), + address: int + ) -> Optional[Optional[str]]: + result = _BNDSCViewGetImageHeaderForAddress(cache, address) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNDSCViewGetImageHeaderForName + +_BNDSCViewGetImageHeaderForName = core.BNDSCViewGetImageHeaderForName +_BNDSCViewGetImageHeaderForName.restype = ctypes.POINTER(ctypes.c_byte) +_BNDSCViewGetImageHeaderForName.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_char_p, + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetImageHeaderForName( + cache: ctypes.POINTER(BNSharedCache), + name: Optional[str] + ) -> Optional[Optional[str]]: + result = _BNDSCViewGetImageHeaderForName(cache, cstr(name)) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNDSCViewGetImageNameForAddress + +_BNDSCViewGetImageNameForAddress = core.BNDSCViewGetImageNameForAddress +_BNDSCViewGetImageNameForAddress.restype = ctypes.POINTER(ctypes.c_byte) +_BNDSCViewGetImageNameForAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetImageNameForAddress( + cache: ctypes.POINTER(BNSharedCache), + address: int + ) -> Optional[Optional[str]]: + result = _BNDSCViewGetImageNameForAddress(cache, address) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNDSCViewGetInstallNames + +_BNDSCViewGetInstallNames = core.BNDSCViewGetInstallNames +_BNDSCViewGetInstallNames.restype = ctypes.POINTER(ctypes.c_char_p) +_BNDSCViewGetInstallNames.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetInstallNames( + cache: ctypes.POINTER(BNSharedCache), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(ctypes.c_char_p)]: + result = _BNDSCViewGetInstallNames(cache, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNDSCViewGetLoadProgress + +_BNDSCViewGetLoadProgress = core.BNDSCViewGetLoadProgress +_BNDSCViewGetLoadProgress.restype = DSCViewLoadProgressEnum +_BNDSCViewGetLoadProgress.argtypes = [ + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetLoadProgress( + sessionID: int + ) -> DSCViewLoadProgressEnum: + return _BNDSCViewGetLoadProgress(sessionID) + + +# ------------------------------------------------------- +# _BNDSCViewGetLoadedRegions + +_BNDSCViewGetLoadedRegions = core.BNDSCViewGetLoadedRegions +_BNDSCViewGetLoadedRegions.restype = ctypes.POINTER(BNDSCMappedMemoryRegion) +_BNDSCViewGetLoadedRegions.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetLoadedRegions( + cache: ctypes.POINTER(BNSharedCache), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNDSCMappedMemoryRegion)]: + result = _BNDSCViewGetLoadedRegions(cache, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNDSCViewGetMemoryUsageInfo + +_BNDSCViewGetMemoryUsageInfo = core.BNDSCViewGetMemoryUsageInfo +_BNDSCViewGetMemoryUsageInfo.restype = BNDSCMemoryUsageInfo +_BNDSCViewGetMemoryUsageInfo.argtypes = [ + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetMemoryUsageInfo( + ) -> BNDSCMemoryUsageInfo: + return _BNDSCViewGetMemoryUsageInfo() + + +# ------------------------------------------------------- +# _BNDSCViewGetNameForAddress + +_BNDSCViewGetNameForAddress = core.BNDSCViewGetNameForAddress +_BNDSCViewGetNameForAddress.restype = ctypes.POINTER(ctypes.c_byte) +_BNDSCViewGetNameForAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetNameForAddress( + cache: ctypes.POINTER(BNSharedCache), + address: int + ) -> Optional[Optional[str]]: + result = _BNDSCViewGetNameForAddress(cache, address) + if not result: + return None + string = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value)) + BNFreeString(result) + return string + + +# ------------------------------------------------------- +# _BNDSCViewGetState + +_BNDSCViewGetState = core.BNDSCViewGetState +_BNDSCViewGetState.restype = DSCViewStateEnum +_BNDSCViewGetState.argtypes = [ + ctypes.POINTER(BNSharedCache), + ] + + +# noinspection PyPep8Naming +def BNDSCViewGetState( + cache: ctypes.POINTER(BNSharedCache) + ) -> DSCViewStateEnum: + return _BNDSCViewGetState(cache) + + +# ------------------------------------------------------- +# _BNDSCViewLoadAllSymbolsAndWait + +_BNDSCViewLoadAllSymbolsAndWait = core.BNDSCViewLoadAllSymbolsAndWait +_BNDSCViewLoadAllSymbolsAndWait.restype = ctypes.POINTER(BNDSCSymbolRep) +_BNDSCViewLoadAllSymbolsAndWait.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.POINTER(ctypes.c_ulonglong), + ] + + +# noinspection PyPep8Naming +def BNDSCViewLoadAllSymbolsAndWait( + cache: ctypes.POINTER(BNSharedCache), + count: ctypes.POINTER(ctypes.c_ulonglong) + ) -> Optional[ctypes.POINTER(BNDSCSymbolRep)]: + result = _BNDSCViewLoadAllSymbolsAndWait(cache, count) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNDSCViewLoadImageContainingAddress + +_BNDSCViewLoadImageContainingAddress = core.BNDSCViewLoadImageContainingAddress +_BNDSCViewLoadImageContainingAddress.restype = ctypes.c_bool +_BNDSCViewLoadImageContainingAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewLoadImageContainingAddress( + cache: ctypes.POINTER(BNSharedCache), + address: int + ) -> bool: + return _BNDSCViewLoadImageContainingAddress(cache, address) + + +# ------------------------------------------------------- +# _BNDSCViewLoadImageWithInstallName + +_BNDSCViewLoadImageWithInstallName = core.BNDSCViewLoadImageWithInstallName +_BNDSCViewLoadImageWithInstallName.restype = ctypes.c_bool +_BNDSCViewLoadImageWithInstallName.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_char_p, + ] + + +# noinspection PyPep8Naming +def BNDSCViewLoadImageWithInstallName( + cache: ctypes.POINTER(BNSharedCache), + name: Optional[str] + ) -> bool: + return _BNDSCViewLoadImageWithInstallName(cache, cstr(name)) + + +# ------------------------------------------------------- +# _BNDSCViewLoadSectionAtAddress + +_BNDSCViewLoadSectionAtAddress = core.BNDSCViewLoadSectionAtAddress +_BNDSCViewLoadSectionAtAddress.restype = ctypes.c_bool +_BNDSCViewLoadSectionAtAddress.argtypes = [ + ctypes.POINTER(BNSharedCache), + ctypes.c_ulonglong, + ] + + +# noinspection PyPep8Naming +def BNDSCViewLoadSectionAtAddress( + cache: ctypes.POINTER(BNSharedCache), + name: int + ) -> bool: + return _BNDSCViewLoadSectionAtAddress(cache, name) + + +# ------------------------------------------------------- +# _BNDSCViewLoadedImageCount + +_BNDSCViewLoadedImageCount = core.BNDSCViewLoadedImageCount +_BNDSCViewLoadedImageCount.restype = ctypes.c_ulonglong +_BNDSCViewLoadedImageCount.argtypes = [ + ctypes.POINTER(BNSharedCache), + ] + + +# noinspection PyPep8Naming +def BNDSCViewLoadedImageCount( + cache: ctypes.POINTER(BNSharedCache) + ) -> int: + return _BNDSCViewLoadedImageCount(cache) + + +# ------------------------------------------------------- +# _BNFreeSharedCacheReference + +_BNFreeSharedCacheReference = core.BNFreeSharedCacheReference +_BNFreeSharedCacheReference.restype = None +_BNFreeSharedCacheReference.argtypes = [ + ctypes.POINTER(BNSharedCache), + ] + + +# noinspection PyPep8Naming +def BNFreeSharedCacheReference( + cache: ctypes.POINTER(BNSharedCache) + ) -> None: + return _BNFreeSharedCacheReference(cache) + + +# ------------------------------------------------------- +# _BNGetSharedCache + +_BNGetSharedCache = core.BNGetSharedCache +_BNGetSharedCache.restype = ctypes.POINTER(BNSharedCache) +_BNGetSharedCache.argtypes = [ + ctypes.POINTER(BNBinaryView), + ] + + +# noinspection PyPep8Naming +def BNGetSharedCache( + data: ctypes.POINTER(BNBinaryView) + ) -> Optional[ctypes.POINTER(BNSharedCache)]: + result = _BNGetSharedCache(data) + if not result: + return None + return result + + +# ------------------------------------------------------- +# _BNNewSharedCacheReference + +_BNNewSharedCacheReference = core.BNNewSharedCacheReference +_BNNewSharedCacheReference.restype = ctypes.POINTER(BNSharedCache) +_BNNewSharedCacheReference.argtypes = [ + ctypes.POINTER(BNSharedCache), + ] + + +# noinspection PyPep8Naming +def BNNewSharedCacheReference( + cache: ctypes.POINTER(BNSharedCache) + ) -> Optional[ctypes.POINTER(BNSharedCache)]: + result = _BNNewSharedCacheReference(cache) + if not result: + return None + return result + + + +# Helper functions +def handle_of_type(value, handle_type): + if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p): + return ctypes.cast(value, ctypes.POINTER(handle_type)) + raise ValueError('expected pointer to %s' % str(handle_type)) diff --git a/view/sharedcache/api/python/_sharedcachecore_template.py b/view/sharedcache/api/python/_sharedcachecore_template.py new file mode 100644 index 00000000..6b5b174f --- /dev/null +++ b/view/sharedcache/api/python/_sharedcachecore_template.py @@ -0,0 +1,43 @@ +import binaryninja +import ctypes, os + +from typing import Optional +from . import sharedcache_enums +# Load core module +import platform +core = None +core_platform = platform.system() + +# By the time the debugger is loaded, binaryninja has not fully initialized. +# So we cannot call binaryninja.bundled_plugin_path() +from binaryninja._binaryninjacore import BNGetBundledPluginDirectory, BNFreeString +if core_platform == "Darwin": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.dylib")) + +elif core_platform == "Linux": + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "libsharedcache.so")) + +elif (core_platform == "Windows") or (core_platform.find("CYGWIN_NT") == 0): + _base_path = BNGetBundledPluginDirectory() + core = ctypes.CDLL(os.path.join(_base_path, "sharedcache.dll")) +else: + raise Exception("OS not supported") + +def cstr(var) -> Optional[ctypes.c_char_p]: + if var is None: + return None + if isinstance(var, bytes): + return var + return var.encode("utf-8") + +def pyNativeStr(arg): + if isinstance(arg, str): + return arg + else: + return arg.decode('utf8') + +def free_string(value:ctypes.c_char_p) -> None: + BNFreeString(ctypes.cast(value, ctypes.POINTER(ctypes.c_byte))) + diff --git a/view/sharedcache/api/python/generator.cpp b/view/sharedcache/api/python/generator.cpp new file mode 100644 index 00000000..91ff88c0 --- /dev/null +++ b/view/sharedcache/api/python/generator.cpp @@ -0,0 +1,617 @@ +/* +Copyright 2020-2024 Vector 35 Inc. + +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 + +http://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. +*/ + + + +#include <stdio.h> +#include <inttypes.h> +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +map<string, string> g_pythonKeywordReplacements = { + {"False", "False_"}, + {"True", "True_"}, + {"None", "None_"}, + {"and", "and_"}, + {"as", "as_"}, + {"assert", "assert_"}, + {"async", "async_"}, + {"await", "await_"}, + {"break", "break_"}, + {"class", "class_"}, + {"continue", "continue_"}, + {"def", "def_"}, + {"del", "del_"}, + {"elif", "elif_"}, + {"else", "else_"}, + {"except", "except_"}, + {"finally", "finally_"}, + {"for", "for_"}, + {"from", "from_"}, + {"global", "global_"}, + {"if", "if_"}, + {"import", "import_"}, + {"in", "in_"}, + {"is", "is_"}, + {"lambda", "lambda_"}, + {"nonlocal", "nonlocal_"}, + {"not", "not_"}, + {"or", "or_"}, + {"pass", "pass_"}, + {"raise", "raise_"}, + {"return", "return_"}, + {"try", "try_"}, + {"while", "while_"}, + {"with", "with_"}, + {"yield", "yield_"}, +}; + + +void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false) +{ + switch (type->GetClass()) + { + case BoolTypeClass: + fprintf(out, "ctypes.c_bool"); + break; + case IntegerTypeClass: + switch (type->GetWidth()) + { + case 1: + if (type->IsSigned()) + fprintf(out, "ctypes.c_byte"); + else + fprintf(out, "ctypes.c_ubyte"); + break; + case 2: + if (type->IsSigned()) + fprintf(out, "ctypes.c_short"); + else + fprintf(out, "ctypes.c_ushort"); + break; + case 4: + if (type->IsSigned()) + fprintf(out, "ctypes.c_int"); + else + fprintf(out, "ctypes.c_uint"); + break; + default: + if (type->IsSigned()) + fprintf(out, "ctypes.c_longlong"); + else + fprintf(out, "ctypes.c_ulonglong"); + break; + } + break; + case FloatTypeClass: + if (type->GetWidth() == 4) + fprintf(out, "ctypes.c_float"); + else + fprintf(out, "ctypes.c_double"); + break; + case NamedTypeReferenceClass: + if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass) + { + string name = type->GetNamedTypeReference()->GetName().GetString(); + if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger") + name = name.substr(3); + else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger") + name = name.substr(2); + else if (name.size() > 15 && name.substr(0, 7) == "BNDebug") + name = name.substr(2); + else if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + fprintf(out, "%sEnum", name.c_str()); + } + else + { + fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + } + break; + case PointerTypeClass: + if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) + { + fprintf(out, "ctypes.c_void_p"); + break; + } + else if ((type->GetChildType()->GetClass() == IntegerTypeClass) && + (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned())) + { + if (isReturnType) + fprintf(out, "ctypes.POINTER(ctypes.c_byte)"); + else + fprintf(out, "ctypes.c_char_p"); + break; + } + else if (type->GetChildType()->GetClass() == FunctionTypeClass) + { + fprintf(out, "ctypes.CFUNCTYPE("); + OutputType(out, type->GetChildType()->GetChildType(), true, true); + for (auto& i : type->GetChildType()->GetParameters()) + { + fprintf(out, ", "); + OutputType(out, i.type); + } + fprintf(out, ")"); + break; + } + fprintf(out, "ctypes.POINTER("); + OutputType(out, type->GetChildType()); + fprintf(out, ")"); + break; + case ArrayTypeClass: + OutputType(out, type->GetChildType()); + fprintf(out, " * %" PRId64, type->GetElementCount()); + break; + default: + fprintf(out, "None"); + break; + } +} + + +void OutputSwizzledType(FILE* out, Type* type) +{ + switch (type->GetClass()) + { + case BoolTypeClass: + fprintf(out, "bool"); + break; + case IntegerTypeClass: + fprintf(out, "int"); + break; + case FloatTypeClass: + fprintf(out, "float"); + break; + case NamedTypeReferenceClass: + if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass) + { + string name = type->GetNamedTypeReference()->GetName().GetString(); + if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger") + name = name.substr(3); + else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger") + name = name.substr(2); + else if (name.size() > 15 && name.substr(0, 7) == "BNDebug") + name = name.substr(2); + else if (name.size() > 2 && name.substr(0, 2) == "BN") + name = name.substr(2); + fprintf(out, "%sEnum", name.c_str()); + } + else + { + fprintf(out, "%s", type->GetNamedTypeReference()->GetName().GetString().c_str()); + } + break; + case PointerTypeClass: + if (type->GetChildType()->GetClass() == VoidTypeClass) + { + fprintf(out, "Optional[ctypes.c_void_p]"); + break; + } + else if ((type->GetChildType()->GetClass() == IntegerTypeClass) && + (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned())) + { + fprintf(out, "Optional[str]"); + break; + } + else if (type->GetChildType()->GetClass() == FunctionTypeClass) + { + fprintf(out, "ctypes.CFUNCTYPE("); + OutputType(out, type->GetChildType()->GetChildType(), true, true); + for (auto& i : type->GetChildType()->GetParameters()) + { + fprintf(out, ", "); + OutputType(out, i.type); + } + fprintf(out, ")"); + break; + } + fprintf(out, "ctypes.POINTER("); + OutputType(out, type->GetChildType()); + fprintf(out, ")"); + break; + case ArrayTypeClass: + OutputType(out, type->GetChildType()); + fprintf(out, " * %" PRId64, type->GetElementCount()); + break; + default: + fprintf(out, "None"); + break; + } +} + + +int main(int argc, char* argv[]) +{ + if (argc < 5) + { + fprintf(stderr, "Usage: generator <header> <output> <output_template> <output_enum>\n"); + return 1; + } + + // Parse API header to get type and function information + map<QualifiedName, Ref<Type>> types, vars, funcs; + string errors; + auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture()); + + // Enable ephemeral settings + Settings::Instance()->LoadSettingsFile(""); + Settings::Instance()->Set("analysis.types.parserName", "ClangTypeParser"); + bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); + + if (!ok) + { + fprintf(stderr, "Errors: %s\n", errors.c_str()); + return 1; + } + + FILE* out = fopen(argv[2], "w"); + FILE* out_template = fopen(argv[3], "r"); + FILE* enums = fopen(argv[4], "w"); + + fprintf(enums, "import enum\n"); + + // Copy the content of the template to the output file + int c; + while((c = fgetc(out_template)) != EOF) + fputc(c, out); + + // Create type objects + fprintf(out, "# Type definitions\n"); + for (auto& i : types) + { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; + if (name == "BNBinaryView") + { + fprintf(out, "from binaryninja._binaryninjacore import BNBinaryView, BNBinaryViewHandle\n"); + continue; + } + if (i.second->GetClass() == StructureTypeClass) + { + fprintf(out, "class %s(ctypes.Structure):\n", name.c_str()); + + // python uses str's, C uses byte-arrays + bool stringField = false; + for (auto& arg : i.second->GetStructure()->GetMembers()) + { + if ((arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + fprintf(out, "\t@property\n\tdef %s(self):\n\t\treturn pyNativeStr(self._%s)\n", arg.name.c_str(), arg.name.c_str()); + fprintf(out, "\t@%s.setter\n\tdef %s(self, value):\n\t\tself._%s = cstr(value)\n", arg.name.c_str(), arg.name.c_str(), arg.name.c_str()); + stringField = true; + } + } + + if (!stringField) + fprintf(out, "\tpass\n"); + + fprintf(out, "%sHandle = ctypes.POINTER(%s)\n", name.c_str(), name.c_str()); + } + else if (i.second->GetClass() == EnumerationTypeClass) + { + bool isBNAPIEnum = false; + if (name.size() > 16 && name.substr(0, 11) == "_BNDebugger") + name = name.substr(3); + else if (name.size() > 15 && name.substr(0, 10) == "BNDebugger") + name = name.substr(2); + else if (name.size() > 15 && name.substr(0, 7) == "BNDebug") + name = name.substr(2); + else if (name.size() > 2 && name.substr(0, 2) == "BN") + { + name = name.substr(2); + isBNAPIEnum = false; + } + else + continue; + + if (isBNAPIEnum) + { + fprintf(out, "from binaryninja._binaryninjacore import %sEnum\n", name.c_str()); + continue; + } + + fprintf(out, "%sEnum = ctypes.c_int\n", name.c_str()); + + fprintf(enums, "\n\nclass %s(enum.IntEnum):\n", name.c_str()); + for (auto& j : i.second->GetEnumeration()->GetMembers()) + { + fprintf(enums, "\t%s = %" PRId64 "\n", j.name.c_str(), j.value); + } + } + else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || + (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) + { + fprintf(out, "%s = ", name.c_str()); + OutputType(out, i.second); + fprintf(out, "\n"); + } + } + + + fprintf(out, "\n# Structure definitions\n"); + set<QualifiedName> structsToProcess; + set<QualifiedName> finishedStructs; + for (auto& i : types) + structsToProcess.insert(i.first); + while (structsToProcess.size() != 0) + { + set<QualifiedName> currentStructList = structsToProcess; + structsToProcess.clear(); + bool processedSome = false; + for (auto& i : currentStructList) + { + string name; + if (i.size() != 1) + continue; + Ref<Type> type = types[i]; + name = i[0]; + if ((type->GetClass() == StructureTypeClass) && (type->GetStructure()->GetMembers().size() != 0)) + { + bool requiresDependency = false; + for (auto& j : type->GetStructure()->GetMembers()) + { + if ((j.type->GetClass() == NamedTypeReferenceClass) && + (types[j.type->GetNamedTypeReference()->GetName()]->GetClass() == StructureTypeClass) && + (finishedStructs.count(j.type->GetNamedTypeReference()->GetName()) == 0)) + { + // This structure needs another structure that isn't fully defined yet, need to wait + // for the dependencies to be defined + structsToProcess.insert(i); + requiresDependency = true; + break; + } + } + + if (requiresDependency) + continue; + fprintf(out, "%s._fields_ = [\n", name.c_str()); + for (auto& j : type->GetStructure()->GetMembers()) + { + // To help the python->C wrappers + if ((j.type->GetClass() == PointerTypeClass) && + (j.type->GetChildType()->GetWidth() == 1) && + (j.type->GetChildType()->IsSigned())) + { + fprintf(out, "\t\t(\"_%s\", ", j.name.c_str()); + } + else + fprintf(out, "\t\t(\"%s\", ", j.name.c_str()); + OutputType(out, j.type); + fprintf(out, "),\n"); + } + fprintf(out, "\t]\n"); + finishedStructs.insert(i); + processedSome = true; + } + else if (type->GetClass() == NamedTypeReferenceClass) + { + if (type->GetNamedTypeReference()->GetTypeReferenceClass() == StructNamedTypeClass) + { + fprintf(out, "%s = %s\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str()); + fprintf(out, "%sHandle = %sHandle\n", name.c_str(), type->GetNamedTypeReference()->GetName().GetString().c_str()); + } + else if (type->GetNamedTypeReference()->GetTypeReferenceClass() == EnumNamedTypeClass) + { + fprintf(out, "%s = ctypes.c_int\n", name.c_str()); + } + finishedStructs.insert(i); + processedSome = true; + } + } + + if (!processedSome && structsToProcess.size() != 0) + { + fprintf(stderr, "Detected dependency cycle in structures\n"); + for (auto& i : structsToProcess) + fprintf(stderr, "%s\n", i.GetString().c_str()); + return 1; + } + } + + fprintf(out, "\n# Function definitions\n"); + for (auto& i : funcs) + { + string name; + if (i.first.size() != 1) + continue; + name = i.first[0]; + + // Check for a string result, these will be automatically wrapped to free the string + // memory and return a Python string + bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && + (i.second->GetChildType()->GetChildType()->GetWidth() == 1) && + (i.second->GetChildType()->GetChildType()->IsSigned()); + // Pointer returns will be automatically wrapped to return None on null pointer + bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); + + // From python -> C python3 requires str -> str.encode('charmap') + bool swizzleArgs = true; + if (name == "BNFreeString") + swizzleArgs = false; + + bool callbackConvention = false; + if (name == "BNAllocString") + { + // Don't perform automatic wrapping of string allocation, and return a void + // pointer so that callback functions (which is the only valid use of BNDebuggerAllocString) + // can properly return the result + stringResult = false; + callbackConvention = true; + swizzleArgs = false; + } + + string funcName = string("_") + name; + + fprintf(out, "# -------------------------------------------------------\n"); + fprintf(out, "# %s\n\n", funcName.c_str()); + fprintf(out, "%s = core.%s\n", funcName.c_str(), name.c_str()); + fprintf(out, "%s.restype = ", funcName.c_str()); + OutputType(out, i.second->GetChildType(), true, callbackConvention); + fprintf(out, "\n"); + if (!i.second->HasVariableArguments()) + { + fprintf(out, "%s.argtypes = [\n", funcName.c_str()); + for (auto& j : i.second->GetParameters()) + { + fprintf(out, "\t\t"); + if (name == "BNFreeString") + { + // BNDebuggerFreeString expects a pointer to a string allocated by the core, so do not use + // a c_char_p here, as that would be allocated by the Python runtime. This can + // be enforced by outputting like a return value. + OutputType(out, j.type, true); + } + else + { + OutputType(out, j.type); + } + fprintf(out, ",\n"); + } + fprintf(out, "\t]"); + } + else + { + // As of writing this, only BNLog's have variable instruction lengths, but in an attempt not to break in the future: + if (funcName.compare(0, 6, "_BNLog") == 0) + { + if (funcName != "_BNLog") + { + fprintf(out, "def %s(*args):\n", name.c_str()); + fprintf(out, "\treturn %s(*[cstr(arg) for arg in args])\n\n", funcName.c_str()); + continue; + } + else + { + fprintf(out, "def %s(level, *args):\n", name.c_str()); + fprintf(out, "\treturn %s(level, *[cstr(arg) for arg in args])\n\n", funcName.c_str()); + continue; + } + } + } + fprintf(out, "\n\n\n# noinspection PyPep8Naming\n"); + fprintf(out, "def %s(", name.c_str()); + if (!i.second->HasVariableArguments()) + { + size_t argN = 0; + for (auto& arg: i.second->GetParameters()) + { + string argName = arg.name; + if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end()) + argName = g_pythonKeywordReplacements[argName]; + + if (argName.empty()) + argName = "arg" + to_string(argN); + + if (argN > 0) + fprintf(out, ", "); + fprintf(out, "\n\t\t"); + fprintf(out, "%s: ", argName.c_str()); + if (swizzleArgs) + OutputSwizzledType(out, arg.type); + else + OutputType(out, arg.type); + argN ++; + } + } + fprintf(out, "\n\t\t) -> "); + if (swizzleArgs) + { + if (stringResult || pointerResult) + fprintf(out, "Optional["); + OutputSwizzledType(out, i.second->GetChildType()); + if (stringResult || pointerResult) + fprintf(out, "]"); + } + else + { + OutputType(out, i.second->GetChildType()); + } + fprintf(out, ":\n"); + + string stringArgFuncCall = funcName + "("; + size_t argN = 0; + for (auto& arg : i.second->GetParameters()) + { + string argName = arg.name; + if (g_pythonKeywordReplacements.find(argName) != g_pythonKeywordReplacements.end()) + argName = g_pythonKeywordReplacements[argName]; + + if (argName.empty()) + argName = "arg" + to_string(argN); + + if (swizzleArgs && (arg.type->GetClass() == PointerTypeClass) && + (arg.type->GetChildType()->GetClass() == IntegerTypeClass) && + (arg.type->GetChildType()->GetWidth() == 1) && + (arg.type->GetChildType()->IsSigned())) + { + stringArgFuncCall += string("cstr(") + argName + "), "; + } + else + { + stringArgFuncCall += argName + ", "; + } + argN++; + } + if (argN > 0) + stringArgFuncCall = stringArgFuncCall.substr(0, stringArgFuncCall.size()-2); + stringArgFuncCall += ")"; + + if (stringResult) + { + // Emit wrapper to get Python string and free native memory + fprintf(out, "\tresult = "); + fprintf(out, "%s\n", stringArgFuncCall.c_str()); + fprintf(out, "\tif not result:\n"); + fprintf(out, "\t\treturn None\n"); + fprintf(out, "\tstring = str(pyNativeStr(ctypes.cast(result, ctypes.c_char_p).value))\n"); + fprintf(out, "\tBNFreeString(result)\n"); + fprintf(out, "\treturn string\n"); + } + else if (pointerResult) + { + // Emit wrapper to return None on null pointer + fprintf(out, "\tresult = "); + fprintf(out, "%s\n", stringArgFuncCall.c_str()); + fprintf(out, "\tif not result:\n"); + fprintf(out, "\t\treturn None\n"); + fprintf(out, "\treturn result\n"); + } + else + { + fprintf(out, "\treturn "); + fprintf(out, "%s\n", stringArgFuncCall.c_str()); + } + fprintf(out, "\n\n"); + } + + fprintf(out, "\n# Helper functions\n"); + fprintf(out, "def handle_of_type(value, handle_type):\n"); + fprintf(out, "\tif isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); + fprintf(out, "\t\treturn ctypes.cast(value, ctypes.POINTER(handle_type))\n"); + fprintf(out, "\traise ValueError('expected pointer to %%s' %% str(handle_type))\n"); + + fclose(out); + fclose(enums); + return 0; +} diff --git a/view/sharedcache/api/python/sharedcache.py b/view/sharedcache/api/python/sharedcache.py new file mode 100644 index 00000000..ca031e03 --- /dev/null +++ b/view/sharedcache/api/python/sharedcache.py @@ -0,0 +1,260 @@ +import os +import ctypes +import dataclasses +import traceback + +import binaryninja +from binaryninja._binaryninjacore import BNFreeStringList, BNAllocString, BNFreeString + +from . import _sharedcachecore as sccore +from .sharedcache_enums import * + + +@dataclasses.dataclass +class DSCMemoryMapping: + filePath: str + name: str + vmAddress: int + rawViewOffset: int + size: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>" + + +@dataclasses.dataclass +class LoadedRegion: + name: str + headerAddress: int + mappings: list[DSCMemoryMapping] + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<LoadedRegion {self.name} @ {self.headerAddress:x}>" + + +@dataclasses.dataclass +class DSCBackingCacheMapping: + vmAddress: int + size: int + fileOffset: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCBackingCacheMapping {self.vmAddress:x}+{self.size:x} @ {self.fileOffset:x}" + + +@dataclasses.dataclass +class DSCBackingCache: + path: str + isPrimary: bool + mappings: list[DSCBackingCacheMapping] + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCBackingCache {self.path} {'Primary' if self.isPrimary else 'Secondary'} | {len(self.mappings)} mappings>" + + +@dataclasses.dataclass +class DSCImageMemoryMapping: + filePath: str + name: str + vmAddress: int + size: int + loaded: bool + rawViewOffset: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCImageMemoryMapping '{self.name}' {os.path.basename(self.filePath)} raw<{self.rawViewOffset:x}>: {self.vmAddress:x}+{self.size:x}>" + + +@dataclasses.dataclass +class DSCImage: + name: str + headerAddress: int + mappings: list[DSCImageMemoryMapping] + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCImage {self.name} @ {self.headerAddress:x}>" + + +@dataclasses.dataclass +class DSCSymbol: + name: str + image: str + address: int + + def __str__(self): + return repr(self) + + def __repr__(self): + return f"<DSCSymbol {self.name} @ {self.address:x} ({self.image}>" + + +class SharedCache: + def __init__(self, view): + self.handle = sccore.BNGetSharedCache(view.handle) + + def load_image_with_install_name(self, installName): + str = BNAllocString(installName.encode('utf-8')) + return sccore.BNDSCViewLoadImageWithInstallName(self.handle, str) + + def load_section_at_address(self, addr): + return sccore.BNDSCViewLoadSectionAtAddress(self.handle, addr) + + def load_image_containing_address(self, addr): + return sccore.BNDSCViewLoadImageContainingAddress(self.handle, addr) + + @property + def caches(self): + count = ctypes.c_ulonglong() + value = sccore.BNDSCViewGetBackingCaches(self.handle, count) + if value is None: + return [] + + result = [] + for i in range(count.value): + mappings = [] + for j in range(value[i].mappingCount): + mapping = DSCBackingCacheMapping( + value[i].mappings[j].vmAddress, + value[i].mappings[j].size, + value[i].mappings[j].fileOffset + ) + mappings.append(mapping) + result.append(DSCBackingCache( + value[i].path, + value[i].isPrimary, + mappings + )) + + sccore.BNDSCViewFreeBackingCaches(value, count) + return result + + @property + def images(self): + count = ctypes.c_ulonglong() + value = sccore.BNDSCViewGetAllImages(self.handle, count) + if value is None: + return [] + + result = [] + for i in range(count.value): + mappings = [] + for j in range(value[i].mappingCount): + mapping = DSCImageMemoryMapping( + value[i].mappings[j].filePath, + value[i].mappings[j].name, + value[i].mappings[j].vmAddress, + value[i].mappings[j].size, + value[i].mappings[j].loaded, + value[i].mappings[j].rawViewOffset + ) + mappings.append(mapping) + result.append(DSCImage( + value[i].name, + value[i].headerAddress, + mappings + )) + + sccore.BNDSCViewFreeAllImages(value, count) + return result + + @property + def loaded_images(self): + count = ctypes.c_ulonglong() + value = sccore.BNDSCViewGetLoadedImages(self.handle, count) + if value is None: + return [] + + result = [] + for i in range(count.value): + mappings = [] + for j in range(value[i].mappingCount): + mapping = DSCMemoryMapping( + value[i].mappings[j].filePath, + value[i].mappings[j].name, + value[i].mappings[j].vmAddress, + value[i].mappings[j].rawViewOffset, + value[i].mappings[j].size + ) + mappings.append(mapping) + result.append(LoadedRegion( + value[i].name, + value[i].headerAddress, + mappings + )) + + sccore.BNDSCViewFreeLoadedImages(value, count) + return result + + def load_all_symbols_and_wait(self): + count = ctypes.c_ulonglong() + value = sccore.BNDSCViewLoadAllSymbolsAndWait(self.handle, count) + if value is None: + return [] + result = [] + for i in range(count.value): + sym = DSCSymbol( + value[i].name, + value[i].image, + value[i].address + ) + result.append(sym) + + sccore.BNDSCViewFreeSymbols(value, count) + return result + + @property + def image_names(self): + count = ctypes.c_ulonglong() + value = sccore.BNDSCViewGetInstallNames(self.handle, count) + if value is None: + return [] + + result = [] + for i in range(count.value): + result.append(value[i].decode('utf-8')) + + BNFreeStringList(value, count) + return result + + @property + def image_count(self): + return sccore.BNDSCViewLoadedImageCount(self.handle) + + @property + def state(self): + return DSCViewState(sccore.BNDSCViewGetState(self.handle)) + + def get_name_for_address(self, address): + name = sccore.BNDSCViewGetNameForAddress(self.handle, address) + if name is None: + return "" + result = name + return result + + def get_image_name_for_address(self, address): + name = sccore.BNDSCViewGetImageNameForAddress(self.handle, address) + if name is None: + return "" + result = name + return result + + def find_symbol_at_addr_and_apply_to_addr(self, symbolAddress, targetAddress, triggerReanalysis) -> None: + sccore.BNDSCFindSymbolAtAddressAndApplyToAddress(self.handle, symbolAddress, targetAddress, triggerReanalysis) diff --git a/view/sharedcache/api/python/sharedcache_enums.py b/view/sharedcache/api/python/sharedcache_enums.py new file mode 100644 index 00000000..34668424 --- /dev/null +++ b/view/sharedcache/api/python/sharedcache_enums.py @@ -0,0 +1,14 @@ +import enum + + +class DSCViewLoadProgress(enum.IntEnum): + LoadProgressNotStarted = 0 + LoadProgressLoadingCaches = 1 + LoadProgressLoadingImages = 2 + LoadProgressFinished = 3 + + +class DSCViewState(enum.IntEnum): + Unloaded = 0 + Loaded = 1 + LoadedWithImages = 2 diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp new file mode 100644 index 00000000..5ca7c480 --- /dev/null +++ b/view/sharedcache/api/sharedcache.cpp @@ -0,0 +1,224 @@ +// +// Created by kat on 5/21/23. +// + +#include "sharedcacheapi.h" + +namespace SharedCacheAPI { + + SharedCache::SharedCache(Ref<BinaryView> view) { + m_object = BNGetSharedCache(view->GetObject()); + } + + BNDSCViewLoadProgress SharedCache::GetLoadProgress(Ref<BinaryView> view) + { + return BNDSCViewGetLoadProgress(view->GetFile()->GetSessionId()); + } + + uint64_t SharedCache::FastGetBackingCacheCount(Ref<BinaryView> view) + { + return BNDSCViewFastGetBackingCacheCount(view->GetObject()); + } + + bool SharedCache::LoadImageWithInstallName(std::string installName) + { + char* str = BNAllocString(installName.c_str()); + return BNDSCViewLoadImageWithInstallName(m_object, str); + } + + bool SharedCache::LoadSectionAtAddress(uint64_t addr) + { + return BNDSCViewLoadSectionAtAddress(m_object, addr); + } + + bool SharedCache::LoadImageContainingAddress(uint64_t addr) + { + return BNDSCViewLoadImageContainingAddress(m_object, addr); + } + + std::vector<std::string> SharedCache::GetAvailableImages() + { + size_t count; + char** value = BNDSCViewGetInstallNames(m_object, &count); + if (value == nullptr) + { + return {}; + } + + std::vector<std::string> result; + for (size_t i = 0; i < count; i++) + { + result.push_back(value[i]); + } + + BNFreeStringList(value, count); + return result; + } + + std::vector<DSCMemoryRegion> SharedCache::GetLoadedMemoryRegions() + { + size_t count; + BNDSCMappedMemoryRegion* value = BNDSCViewGetLoadedRegions(m_object, &count); + if (value == nullptr) + { + return {}; + } + + std::vector<DSCMemoryRegion> result; + for (size_t i = 0; i < count; i++) + { + DSCMemoryRegion region; + region.vmAddress = value[i].vmAddress; + region.size = value[i].size; + region.prettyName = value[i].name; + result.push_back(region); + } + + BNDSCViewFreeLoadedRegions(value, count); + return result; + } + std::vector<BackingCache> SharedCache::GetBackingCaches() + { + size_t count; + BNDSCBackingCache* value = BNDSCViewGetBackingCaches(m_object, &count); + if (value == nullptr) + { + return {}; + } + + std::vector<BackingCache> result; + for (size_t i = 0; i < count; i++) + { + BackingCache cache; + cache.path = value[i].path; + cache.isPrimary = value[i].isPrimary; + for (size_t j = 0; j < value[i].mappingCount; j++) + { + BackingCacheMapping mapping; + mapping.vmAddress = value[i].mappings[j].vmAddress; + mapping.size = value[i].mappings[j].size; + mapping.fileOffset = value[i].mappings[j].fileOffset; + cache.mappings.push_back(mapping); + } + result.push_back(cache); + } + + BNDSCViewFreeBackingCaches(value, count); + return result; + } + + std::vector<DSCImage> SharedCache::GetImages() + { + size_t count; + BNDSCImage* value = BNDSCViewGetAllImages(m_object, &count); + if (value == nullptr) + { + return {}; + } + + std::vector<DSCImage> result; + for (size_t i = 0; i < count; i++) + { + DSCImage img; + img.name = value[i].name; + img.headerAddress = value[i].headerAddress; + for (size_t j = 0; j < value[i].mappingCount; j++) + { + DSCImageMemoryMapping mapping; + mapping.filePath = value[i].mappings[j].filePath; + mapping.name = value[i].mappings[j].name; + mapping.vmAddress = value[i].mappings[j].vmAddress; + mapping.rawViewOffset = value[i].mappings[j].rawViewOffset; + mapping.size = value[i].mappings[j].size; + mapping.loaded = value[i].mappings[j].loaded; + img.mappings.push_back(mapping); + } + result.push_back(img); + } + + BNDSCViewFreeAllImages(value, count); + return result; + } + + std::vector<DSCSymbol> SharedCache::LoadAllSymbolsAndWait() + { + size_t count; + BNDSCSymbolRep* value = BNDSCViewLoadAllSymbolsAndWait(m_object, &count); + if (value == nullptr) + { + return {}; + } + + std::vector<DSCSymbol> result; + for (size_t i = 0; i < count; i++) + { + DSCSymbol sym; + sym.address = value[i].address; + sym.name = value[i].name; + sym.image = value[i].image; + result.push_back(sym); + } + + BNDSCViewFreeSymbols(value, count); + return result; + } + + std::string SharedCache::GetNameForAddress(uint64_t address) + { + char* name = BNDSCViewGetNameForAddress(m_object, address); + if (name == nullptr) + return {}; + std::string result = name; + BNFreeString(name); + return result; + } + + std::string SharedCache::GetImageNameForAddress(uint64_t address) + { + char* name = BNDSCViewGetImageNameForAddress(m_object, address); + if (name == nullptr) + return {}; + std::string result = name; + BNFreeString(name); + return result; + } + + std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForImage(std::string name) + { + char* str = BNAllocString(name.c_str()); + char* outputStr = BNDSCViewGetImageHeaderForName(m_object, str); + if (outputStr == nullptr) + return {}; + std::string output = outputStr; + BNFreeString(outputStr); + if (output.empty()) + return {}; + SharedCacheMachOHeader header; + header.LoadFromString(output); + return header; + } + + std::optional<SharedCacheMachOHeader> SharedCache::GetMachOHeaderForAddress(uint64_t address) + { + char* outputStr = BNDSCViewGetImageHeaderForAddress(m_object, address); + if (outputStr == nullptr) + return {}; + std::string output = outputStr; + BNFreeString(outputStr); + if (output.empty()) + return {}; + SharedCacheMachOHeader header; + header.LoadFromString(output); + return header; + } + + BNDSCViewState SharedCache::GetState() + { + return BNDSCViewGetState(m_object); + } + + void SharedCache::FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis) const + { + BNDSCFindSymbolAtAddressAndApplyToAddress(m_object, symbolLocation, targetLocation, triggerReanalysis); + } +} // namespace SharedCacheAPI diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h new file mode 100644 index 00000000..efd38532 --- /dev/null +++ b/view/sharedcache/api/sharedcacheapi.h @@ -0,0 +1,820 @@ +#pragma once + +#include <binaryninjaapi.h> +#include "MetadataSerializable.hpp" +#include "view/macho/machoview.h" +#include "sharedcachecore.h" + +using namespace BinaryNinja; + +namespace SharedCacheAPI { + template<class T> + class SCRefCountObject { + void AddRefInternal() { m_refs.fetch_add(1); } + + void ReleaseInternal() { + if (m_refs.fetch_sub(1) == 1) + delete this; + } + + public: + std::atomic<int> m_refs; + T *m_object; + + SCRefCountObject() : m_refs(0), m_object(nullptr) {} + + virtual ~SCRefCountObject() {} + + T *GetObject() const { return m_object; } + + static T *GetObject(SCRefCountObject *obj) { + if (!obj) + return nullptr; + return obj->GetObject(); + } + + void AddRef() { AddRefInternal(); } + + void Release() { ReleaseInternal(); } + + void AddRefForRegistration() { AddRefInternal(); } + }; + + + template<class T, T *(*AddObjectReference)(T *), void (*FreeObjectReference)(T *)> + class SCCoreRefCountObject { + void AddRefInternal() { m_refs.fetch_add(1); } + + void ReleaseInternal() { + if (m_refs.fetch_sub(1) == 1) { + if (!m_registeredRef) + delete this; + } + } + + public: + std::atomic<int> m_refs; + bool m_registeredRef = false; + T *m_object; + + SCCoreRefCountObject() : m_refs(0), m_object(nullptr) {} + + virtual ~SCCoreRefCountObject() {} + + T *GetObject() const { return m_object; } + + static T *GetObject(SCCoreRefCountObject *obj) { + if (!obj) + return nullptr; + return obj->GetObject(); + } + + void AddRef() { + if (m_object && (m_refs != 0)) + AddObjectReference(m_object); + AddRefInternal(); + } + + void Release() { + if (m_object) + FreeObjectReference(m_object); + ReleaseInternal(); + } + + void AddRefForRegistration() { m_registeredRef = true; } + + void ReleaseForRegistration() { + m_object = nullptr; + m_registeredRef = false; + if (m_refs == 0) + delete this; + } + }; + + + template<class T> + class SCRef { + T *m_obj; +#ifdef BN_REF_COUNT_DEBUG + void* m_assignmentTrace = nullptr; +#endif + + public: + SCRef<T>() : m_obj(NULL) {} + + SCRef<T>(T *obj) : m_obj(obj) { + if (m_obj) { + m_obj->AddRef(); +#ifdef BN_REF_COUNT_DEBUG + m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name()); +#endif + } + } + + SCRef<T>(const SCRef<T> &obj) : m_obj(obj.m_obj) { + if (m_obj) { + m_obj->AddRef(); +#ifdef BN_REF_COUNT_DEBUG + m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name()); +#endif + } + } + + SCRef<T>(SCRef<T> &&other) : m_obj(other.m_obj) { + other.m_obj = 0; +#ifdef BN_REF_COUNT_DEBUG + m_assignmentTrace = other.m_assignmentTrace; +#endif + } + + ~SCRef<T>() { + if (m_obj) { + m_obj->Release(); +#ifdef BN_REF_COUNT_DEBUG + BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace); +#endif + } + } + + SCRef<T> &operator=(const Ref<T> &obj) { +#ifdef BN_REF_COUNT_DEBUG + if (m_obj) + BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace); + if (obj.m_obj) + m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name()); +#endif + T *oldObj = m_obj; + m_obj = obj.m_obj; + if (m_obj) + m_obj->AddRef(); + if (oldObj) + oldObj->Release(); + return *this; + } + + SCRef<T> &operator=(SCRef<T> &&other) { + if (m_obj) { +#ifdef BN_REF_COUNT_DEBUG + BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace); +#endif + m_obj->Release(); + } + m_obj = other.m_obj; + other.m_obj = 0; +#ifdef BN_REF_COUNT_DEBUG + m_assignmentTrace = other.m_assignmentTrace; +#endif + return *this; + } + + SCRef<T> &operator=(T *obj) { +#ifdef BN_REF_COUNT_DEBUG + if (m_obj) + BNUnregisterObjectRefDebugTrace(typeid(T).name(), m_assignmentTrace); + if (obj) + m_assignmentTrace = BNRegisterObjectRefDebugTrace(typeid(T).name()); +#endif + T *oldObj = m_obj; + m_obj = obj; + if (m_obj) + m_obj->AddRef(); + if (oldObj) + oldObj->Release(); + return *this; + } + + operator T *() const { + return m_obj; + } + + T *operator->() const { + return m_obj; + } + + T &operator*() const { + return *m_obj; + } + + bool operator!() const { + return m_obj == NULL; + } + + bool operator==(const T *obj) const { + return T::GetObject(m_obj) == T::GetObject(obj); + } + + bool operator==(const SCRef<T> &obj) const { + return T::GetObject(m_obj) == T::GetObject(obj.m_obj); + } + + bool operator!=(const T *obj) const { + return T::GetObject(m_obj) != T::GetObject(obj); + } + + bool operator!=(const SCRef<T> &obj) const { + return T::GetObject(m_obj) != T::GetObject(obj.m_obj); + } + + bool operator<(const T *obj) const { + return T::GetObject(m_obj) < T::GetObject(obj); + } + + bool operator<(const SCRef<T> &obj) const { + return T::GetObject(m_obj) < T::GetObject(obj.m_obj); + } + + T *GetPtr() const { + return m_obj; + } + }; + + struct DSCMemoryRegion { + uint64_t vmAddress; + uint64_t size; + std::string prettyName; + }; + + struct BackingCacheMapping { + uint64_t vmAddress; + uint64_t size; + uint64_t fileOffset; + }; + + struct BackingCache { + std::string path; + bool isPrimary; + std::vector<BackingCacheMapping> mappings; + }; + + struct DSCImageMemoryMapping { + std::string filePath; + std::string name; + uint64_t vmAddress; + uint64_t size; + bool loaded; + uint64_t rawViewOffset; + }; + + struct DSCImage { + std::string name; + uint64_t headerAddress; + std::vector<DSCImageMemoryMapping> mappings; + }; + + struct DSCSymbol { + uint64_t address; + std::string name; + std::string image; + }; + + using namespace BinaryNinja; + struct SharedCacheMachOHeader : public MetadataSerializable { + uint64_t textBase = 0; + uint64_t loadCommandOffset = 0; + mach_header_64 ident; + std::string identifierPrefix; + std::string installName; + + std::vector<std::pair<uint64_t, bool>> entryPoints; + std::vector<uint64_t> m_entryPoints; //list of entrypoints + + symtab_command symtab; + dysymtab_command dysymtab; + dyld_info_command dyldInfo; + routines_command_64 routines64; + function_starts_command functionStarts; + std::vector<section_64> moduleInitSections; + linkedit_data_command exportTrie; + linkedit_data_command chainedFixups {}; + + uint64_t relocationBase; + // Section and program headers, internally use 64-bit form as it is a superset of 32-bit + std::vector<segment_command_64> segments; //only three types of sections __TEXT, __DATA, __IMPORT + segment_command_64 linkeditSegment; + std::vector<section_64> sections; + std::vector<std::string> sectionNames; + + std::vector<section_64> symbolStubSections; + std::vector<section_64> symbolPointerSections; + + std::vector<std::string> dylibs; + + build_version_command buildVersion; + std::vector<build_tool_version> buildToolVersions; + + std::string exportTriePath; + + bool dysymPresent = false; + bool dyldInfoPresent = false; + bool exportTriePresent = false; + bool chainedFixupsPresent = false; + bool routinesPresent = false; + bool functionStartsPresent = false; + bool relocatable = false; + void Serialize(const std::string& name, mach_header_64 b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.magic, m_activeContext.allocator); + bArr.PushBack(b.cputype, m_activeContext.allocator); + bArr.PushBack(b.cpusubtype, m_activeContext.allocator); + bArr.PushBack(b.filetype, m_activeContext.allocator); + bArr.PushBack(b.ncmds, m_activeContext.allocator); + bArr.PushBack(b.sizeofcmds, m_activeContext.allocator); + bArr.PushBack(b.flags, m_activeContext.allocator); + bArr.PushBack(b.reserved, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, mach_header_64& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.magic = bArr[0].GetInt64(); + b.cputype = bArr[1].GetInt64(); + b.cpusubtype = bArr[2].GetInt64(); + b.filetype = bArr[3].GetInt64(); + b.ncmds = bArr[4].GetInt64(); + b.sizeofcmds = bArr[5].GetInt64(); + b.flags = bArr[6].GetInt64(); + b.reserved = bArr[7].GetInt64(); + } + + void Serialize(const std::string& name, symtab_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.symoff, m_activeContext.allocator); + bArr.PushBack(b.nsyms, m_activeContext.allocator); + bArr.PushBack(b.stroff, m_activeContext.allocator); + bArr.PushBack(b.strsize, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, symtab_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.symoff = bArr[2].GetUint(); + b.nsyms = bArr[3].GetUint(); + b.stroff = bArr[4].GetUint(); + b.strsize = bArr[5].GetUint(); + } + + void Serialize(const std::string& name, dysymtab_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.ilocalsym, m_activeContext.allocator); + bArr.PushBack(b.nlocalsym, m_activeContext.allocator); + bArr.PushBack(b.iextdefsym, m_activeContext.allocator); + bArr.PushBack(b.nextdefsym, m_activeContext.allocator); + bArr.PushBack(b.iundefsym, m_activeContext.allocator); + bArr.PushBack(b.nundefsym, m_activeContext.allocator); + bArr.PushBack(b.tocoff, m_activeContext.allocator); + bArr.PushBack(b.ntoc, m_activeContext.allocator); + bArr.PushBack(b.modtaboff, m_activeContext.allocator); + bArr.PushBack(b.nmodtab, m_activeContext.allocator); + bArr.PushBack(b.extrefsymoff, m_activeContext.allocator); + bArr.PushBack(b.nextrefsyms, m_activeContext.allocator); + bArr.PushBack(b.indirectsymoff, m_activeContext.allocator); + bArr.PushBack(b.nindirectsyms, m_activeContext.allocator); + bArr.PushBack(b.extreloff, m_activeContext.allocator); + bArr.PushBack(b.nextrel, m_activeContext.allocator); + bArr.PushBack(b.locreloff, m_activeContext.allocator); + bArr.PushBack(b.nlocrel, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, dysymtab_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.ilocalsym = bArr[2].GetUint(); + b.nlocalsym = bArr[3].GetUint(); + b.iextdefsym = bArr[4].GetUint(); + b.nextdefsym = bArr[5].GetUint(); + b.iundefsym = bArr[6].GetUint(); + b.nundefsym = bArr[7].GetUint(); + b.tocoff = bArr[8].GetUint(); + b.ntoc = bArr[9].GetUint(); + b.modtaboff = bArr[10].GetUint(); + b.nmodtab = bArr[11].GetUint(); + b.extrefsymoff = bArr[12].GetUint(); + b.nextrefsyms = bArr[13].GetUint(); + b.indirectsymoff = bArr[14].GetUint(); + b.nindirectsyms = bArr[15].GetUint(); + b.extreloff = bArr[16].GetUint(); + b.nextrel = bArr[17].GetUint(); + b.locreloff = bArr[18].GetUint(); + b.nlocrel = bArr[19].GetUint(); + } + + void Serialize(const std::string& name, dyld_info_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.rebase_off, m_activeContext.allocator); + bArr.PushBack(b.rebase_size, m_activeContext.allocator); + bArr.PushBack(b.bind_off, m_activeContext.allocator); + bArr.PushBack(b.bind_size, m_activeContext.allocator); + bArr.PushBack(b.weak_bind_off, m_activeContext.allocator); + bArr.PushBack(b.weak_bind_size, m_activeContext.allocator); + bArr.PushBack(b.lazy_bind_off, m_activeContext.allocator); + bArr.PushBack(b.lazy_bind_size, m_activeContext.allocator); + bArr.PushBack(b.export_off, m_activeContext.allocator); + bArr.PushBack(b.export_size, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, dyld_info_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.rebase_off = bArr[2].GetUint(); + b.rebase_size = bArr[3].GetUint(); + b.bind_off = bArr[4].GetUint(); + b.bind_size = bArr[5].GetUint(); + b.weak_bind_off = bArr[6].GetUint(); + b.weak_bind_size = bArr[7].GetUint(); + b.lazy_bind_off = bArr[8].GetUint(); + b.lazy_bind_size = bArr[9].GetUint(); + b.export_off = bArr[10].GetUint(); + b.export_size = bArr[11].GetUint(); + } + + void Serialize(const std::string& name, routines_command_64 b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.init_address, m_activeContext.allocator); + bArr.PushBack(b.init_module, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, routines_command_64& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.init_address = bArr[2].GetUint(); + b.init_module = bArr[3].GetUint(); + } + + void Serialize(const std::string& name, function_starts_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.funcoff, m_activeContext.allocator); + bArr.PushBack(b.funcsize, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, function_starts_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.funcoff = bArr[2].GetUint(); + b.funcsize = bArr[3].GetUint(); + } + + void Serialize(const std::string& name, std::vector<section_64> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& s : b) + { + rapidjson::Value sArr(rapidjson::kArrayType); + std::string sectNameStr; + char sectName[16]; + memcpy(sectName, s.sectname, 16); + sectName[15] = 0; + sectNameStr = std::string(sectName); + sArr.PushBack(rapidjson::Value(sectNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator); + std::string segNameStr; + char segName[16]; + memcpy(segName, s.segname, 16); + segName[15] = 0; + segNameStr = std::string(segName); + sArr.PushBack(rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator); + sArr.PushBack(s.addr, m_activeContext.allocator); + sArr.PushBack(s.size, m_activeContext.allocator); + sArr.PushBack(s.offset, m_activeContext.allocator); + sArr.PushBack(s.align, m_activeContext.allocator); + sArr.PushBack(s.reloff, m_activeContext.allocator); + sArr.PushBack(s.nreloc, m_activeContext.allocator); + sArr.PushBack(s.flags, m_activeContext.allocator); + sArr.PushBack(s.reserved1, m_activeContext.allocator); + sArr.PushBack(s.reserved2, m_activeContext.allocator); + sArr.PushBack(s.reserved3, m_activeContext.allocator); + bArr.PushBack(sArr, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, std::vector<section_64>& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + for (auto& s : bArr) + { + section_64 sec; + auto s2 = s.GetArray(); + std::string sectNameStr = s2[0].GetString(); + memcpy(sec.sectname, sectNameStr.c_str(), sectNameStr.size()); + std::string segNameStr = s2[1].GetString(); + memcpy(sec.segname, segNameStr.c_str(), segNameStr.size()); + sec.addr = s2[2].GetUint64(); + sec.size = s2[3].GetUint64(); + sec.offset = s2[4].GetUint(); + sec.align = s2[5].GetUint(); + sec.reloff = s2[6].GetUint(); + sec.nreloc = s2[7].GetUint(); + sec.flags = s2[8].GetUint(); + sec.reserved1 = s2[9].GetUint(); + sec.reserved2 = s2[10].GetUint(); + sec.reserved3 = s2[11].GetUint(); + b.push_back(sec); + } + } + + void Serialize(const std::string& name, linkedit_data_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.dataoff, m_activeContext.allocator); + bArr.PushBack(b.datasize, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, linkedit_data_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.dataoff = bArr[2].GetUint(); + b.datasize = bArr[3].GetUint(); + } + + void Serialize(const std::string& name, segment_command_64 b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + std::string segNameStr; + char segName[16]; + memcpy(segName, b.segname, 16); + segName[15] = 0; + segNameStr = std::string(segName); + bArr.PushBack(rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator); + bArr.PushBack(b.vmaddr, m_activeContext.allocator); + bArr.PushBack(b.vmsize, m_activeContext.allocator); + bArr.PushBack(b.fileoff, m_activeContext.allocator); + bArr.PushBack(b.filesize, m_activeContext.allocator); + bArr.PushBack(b.maxprot, m_activeContext.allocator); + bArr.PushBack(b.initprot, m_activeContext.allocator); + bArr.PushBack(b.nsects, m_activeContext.allocator); + bArr.PushBack(b.flags, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, segment_command_64& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + std::string segNameStr = bArr[0].GetString(); + memcpy(b.segname, segNameStr.c_str(), segNameStr.size()); + b.vmaddr = bArr[1].GetUint64(); + b.vmsize = bArr[2].GetUint64(); + b.fileoff = bArr[3].GetUint64(); + b.filesize = bArr[4].GetUint64(); + b.maxprot = bArr[5].GetUint(); + b.initprot = bArr[6].GetUint(); + b.nsects = bArr[7].GetUint(); + b.flags = bArr[8].GetUint(); + } + + void Serialize(const std::string& name, std::vector<segment_command_64> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& s : b) + { + rapidjson::Value sArr(rapidjson::kArrayType); + std::string segNameStr; + char segName[16]; + memcpy(segName, s.segname, 16); + segName[15] = 0; + segNameStr = std::string(segName); + sArr.PushBack(rapidjson::Value(segNameStr.c_str(), m_activeContext.allocator), m_activeContext.allocator); + sArr.PushBack(s.vmaddr, m_activeContext.allocator); + sArr.PushBack(s.vmsize, m_activeContext.allocator); + sArr.PushBack(s.fileoff, m_activeContext.allocator); + sArr.PushBack(s.filesize, m_activeContext.allocator); + sArr.PushBack(s.maxprot, m_activeContext.allocator); + sArr.PushBack(s.initprot, m_activeContext.allocator); + sArr.PushBack(s.nsects, m_activeContext.allocator); + sArr.PushBack(s.flags, m_activeContext.allocator); + bArr.PushBack(sArr, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, std::vector<segment_command_64>& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + for (auto& s : bArr) + { + segment_command_64 sec; + auto s2 = s.GetArray(); + std::string segNameStr = s2[0].GetString(); + memcpy(sec.segname, segNameStr.c_str(), segNameStr.size()); + sec.vmaddr = s2[1].GetUint64(); + sec.vmsize = s2[2].GetUint64(); + sec.fileoff = s2[3].GetUint64(); + sec.filesize = s2[4].GetUint64(); + sec.maxprot = s2[5].GetUint(); + sec.initprot = s2[6].GetUint(); + sec.nsects = s2[7].GetUint(); + sec.flags = s2[8].GetUint(); + b.push_back(sec); + } + } + + void Serialize(const std::string& name, build_version_command b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + bArr.PushBack(b.cmd, m_activeContext.allocator); + bArr.PushBack(b.cmdsize, m_activeContext.allocator); + bArr.PushBack(b.platform, m_activeContext.allocator); + bArr.PushBack(b.minos, m_activeContext.allocator); + bArr.PushBack(b.sdk, m_activeContext.allocator); + bArr.PushBack(b.ntools, m_activeContext.allocator); + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, build_version_command& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + b.cmd = bArr[0].GetUint(); + b.cmdsize = bArr[1].GetUint(); + b.platform = bArr[2].GetUint(); + b.minos = bArr[3].GetUint(); + b.sdk = bArr[4].GetUint(); + b.ntools = bArr[5].GetUint(); + } + + void Serialize(const std::string& name, std::vector<build_tool_version> b) + { + S(); + rapidjson::Value key(name.c_str(), m_activeContext.allocator); + rapidjson::Value bArr(rapidjson::kArrayType); + for (auto& s : b) + { + rapidjson::Value sArr(rapidjson::kArrayType); + sArr.PushBack(s.tool, m_activeContext.allocator); + sArr.PushBack(s.version, m_activeContext.allocator); + bArr.PushBack(sArr, m_activeContext.allocator); + } + m_activeContext.doc.AddMember(key, bArr, m_activeContext.allocator); + } + + void Deserialize(const std::string& name, std::vector<build_tool_version>& b) + { + auto bArr = m_activeDeserContext.doc[name.c_str()].GetArray(); + for (auto& s : bArr) + { + build_tool_version sec; + auto s2 = s.GetArray(); + sec.tool = s2[0].GetUint(); + sec.version = s2[1].GetUint(); + b.push_back(sec); + } + } + + void Store() override { + MSS(textBase); + MSS(loadCommandOffset); + MSS_SUBCLASS(ident); + MSS(identifierPrefix); + MSS(installName); + MSS(entryPoints); + MSS(m_entryPoints); + MSS_SUBCLASS(symtab); + MSS_SUBCLASS(dysymtab); + MSS_SUBCLASS(dyldInfo); + // MSS_SUBCLASS(routines64); + MSS_SUBCLASS(functionStarts); + MSS_SUBCLASS(moduleInitSections); + MSS_SUBCLASS(exportTrie); + MSS_SUBCLASS(chainedFixups); + MSS(relocationBase); + MSS_SUBCLASS(segments); + MSS_SUBCLASS(linkeditSegment); + MSS_SUBCLASS(sections); + MSS(sectionNames); + MSS_SUBCLASS(symbolStubSections); + MSS_SUBCLASS(symbolPointerSections); + MSS(dylibs); + MSS_SUBCLASS(buildVersion); + MSS_SUBCLASS(buildToolVersions); + MSS(exportTriePath); + MSS(dysymPresent); + MSS(dyldInfoPresent); + MSS(exportTriePresent); + MSS(chainedFixupsPresent); + MSS(routinesPresent); + MSS(functionStartsPresent); + MSS(relocatable); + } + void Load() override { + MSL(textBase); + MSL(loadCommandOffset); + MSL_SUBCLASS(ident); + MSL(identifierPrefix); + MSL(installName); + MSL(entryPoints); + MSL(m_entryPoints); + MSL_SUBCLASS(symtab); + MSL_SUBCLASS(dysymtab); + MSL_SUBCLASS(dyldInfo); + // MSL_SUBCLASS(routines64); // FIXME CRASH but also do we even use this? + MSL_SUBCLASS(functionStarts); + MSL_SUBCLASS(moduleInitSections); + MSL_SUBCLASS(exportTrie); + MSL_SUBCLASS(chainedFixups); + MSL(relocationBase); + MSL_SUBCLASS(segments); + MSL_SUBCLASS(linkeditSegment); + MSL_SUBCLASS(sections); + MSL(sectionNames); + MSL_SUBCLASS(symbolStubSections); + MSL_SUBCLASS(symbolPointerSections); + MSL(dylibs); + MSL_SUBCLASS(buildVersion); + MSL_SUBCLASS(buildToolVersions); + MSL(exportTriePath); + MSL(dysymPresent); + MSL(dyldInfoPresent); + MSL(exportTriePresent); + MSL(chainedFixupsPresent); + // MSL(routinesPresent); + MSL(functionStartsPresent); + MSL(relocatable); + } + }; + + + class SharedCache : public SCCoreRefCountObject<BNSharedCache, BNNewSharedCacheReference, BNFreeSharedCacheReference> { + public: + SharedCache(Ref<BinaryView> view); + + BNDSCViewState GetState(); + static BNDSCViewLoadProgress GetLoadProgress(Ref<BinaryView> view); + static uint64_t FastGetBackingCacheCount(Ref<BinaryView> view); + + bool LoadImageWithInstallName(std::string installName); + bool LoadSectionAtAddress(uint64_t addr); + bool LoadImageContainingAddress(uint64_t addr); + std::vector<std::string> GetAvailableImages(); + + std::vector<DSCSymbol> LoadAllSymbolsAndWait(); + + std::string GetNameForAddress(uint64_t address); + std::string GetImageNameForAddress(uint64_t address); + + std::vector<BackingCache> GetBackingCaches(); + std::vector<DSCImage> GetImages(); + + std::optional<SharedCacheMachOHeader> GetMachOHeaderForImage(std::string name); + std::optional<SharedCacheMachOHeader> GetMachOHeaderForAddress(uint64_t address); + + std::vector<DSCMemoryRegion> GetLoadedMemoryRegions(); + + void FindSymbolAtAddrAndApplyToAddr(uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis = true) const; + }; +}
\ No newline at end of file diff --git a/view/sharedcache/api/sharedcachecore.h b/view/sharedcache/api/sharedcachecore.h new file mode 100644 index 00000000..1c382e48 --- /dev/null +++ b/view/sharedcache/api/sharedcachecore.h @@ -0,0 +1,156 @@ +#pragma once + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __GNUC__ + #ifdef SHAREDCACHE_LIBRARY + #define SHAREDCACHE_FFI_API __attribute__((visibility("default"))) + #else // SHAREDCACHE_LIBRARY + #define SHAREDCACHE_FFI_API + #endif // SHAREDCACHE_LIBRARY +#else // __GNUC__ + #ifdef _MSC_VER + #ifndef DEMO_VERSION + #ifdef SHAREDCACHE_LIBRARY + #define SHAREDCACHE_FFI_API __declspec(dllexport) + #else // SHAREDCACHE_LIBRARY + #define SHAREDCACHE_FFI_API __declspec(dllimport) + #endif // SHAREDCACHE_LIBRARY + #else + #define SHAREDCACHE_FFI_API + #endif + #else // _MSC_VER + #define SHAREDCACHE_FFI_API + #endif // _MSC_VER +#endif // __GNUC__C + +#define CORE_ALLOCATED_STRUCT(T) + +#define CORE_ALLOCATED_CLASS(T) \ + public: \ + CORE_ALLOCATED_STRUCT(T) \ + private: + +#define DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, ns) \ + namespace ns { class cls; } struct handle { ns::cls* object; } + +#define DECLARE_SHAREDCACHE_API_OBJECT(handle, cls) DECLARE_SHAREDCACHE_API_OBJECT_INTERNAL(handle, cls, SharedCacheCore) + +#define IMPLEMENT_SHAREDCACHE_API_OBJECT(handle) \ + CORE_ALLOCATED_CLASS(handle) \ + private: \ + handle m_apiObject; \ + public: \ + typedef handle* APIHandle; \ + handle* GetAPIObject() { return &m_apiObject; } \ + private: +#define INIT_SHAREDCACHE_API_OBJECT() \ + m_apiObject.object = this; + + typedef enum BNDSCViewState { + Unloaded, + Loaded, + LoadedWithImages, + } BNDSCViewState; + + typedef enum BNDSCViewLoadProgress { + LoadProgressNotStarted, + LoadProgressLoadingCaches, + LoadProgressLoadingImages, + LoadProgressFinished, + } BNDSCViewLoadProgress; + + typedef struct BNBinaryView BNBinaryView; + typedef struct BNSharedCache BNSharedCache; + + typedef struct BNDSCImageMemoryMapping { + char* filePath; + char* name; + uint64_t vmAddress; + uint64_t size; + bool loaded; + uint64_t rawViewOffset; + } BNDSCImageMemoryMapping; + + typedef struct BNDSCImage { + char* name; + uint64_t headerAddress; + BNDSCImageMemoryMapping* mappings; + size_t mappingCount; + } BNDSCImage; + + typedef struct BNDSCMappedMemoryRegion { + uint64_t vmAddress; + uint64_t size; + char* name; + } BNDSCMappedMemoryRegion; + + typedef struct BNDSCBackingCacheMapping { + uint64_t vmAddress; + uint64_t size; + uint64_t fileOffset; + } BNDSCBackingCacheMapping; + + typedef struct BNDSCBackingCache { + char* path; + bool isPrimary; + BNDSCBackingCacheMapping* mappings; + size_t mappingCount; + } BNDSCBackingCache; + + typedef struct BNDSCMemoryUsageInfo { + uint64_t sharedCacheRefs; + uint64_t mmapRefs; + } BNDSCMemoryUsageInfo; + + typedef struct BNDSCSymbolRep { + uint64_t address; + char* name; + char* image; + } BNDSCSymbolRep; + + SHAREDCACHE_FFI_API BNSharedCache* BNGetSharedCache(BNBinaryView* data); + + SHAREDCACHE_FFI_API BNSharedCache* BNNewSharedCacheReference(BNSharedCache* cache); + SHAREDCACHE_FFI_API void BNFreeSharedCacheReference(BNSharedCache* cache); + + SHAREDCACHE_FFI_API char** BNDSCViewGetInstallNames(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API uint64_t BNDSCViewLoadedImageCount(BNSharedCache* cache); + + SHAREDCACHE_FFI_API bool BNDSCViewLoadImageWithInstallName(BNSharedCache* cache, char* name); + SHAREDCACHE_FFI_API bool BNDSCViewLoadSectionAtAddress(BNSharedCache* cache, uint64_t name); + SHAREDCACHE_FFI_API bool BNDSCViewLoadImageContainingAddress(BNSharedCache* cache, uint64_t address); + + SHAREDCACHE_FFI_API char* BNDSCViewGetNameForAddress(BNSharedCache* cache, uint64_t address); + SHAREDCACHE_FFI_API char* BNDSCViewGetImageNameForAddress(BNSharedCache* cache, uint64_t address); + + SHAREDCACHE_FFI_API BNDSCViewState BNDSCViewGetState(BNSharedCache* cache); + SHAREDCACHE_FFI_API BNDSCViewLoadProgress BNDSCViewGetLoadProgress(uint64_t sessionID); + SHAREDCACHE_FFI_API uint64_t BNDSCViewFastGetBackingCacheCount(BNBinaryView* view); + + SHAREDCACHE_FFI_API BNDSCSymbolRep* BNDSCViewLoadAllSymbolsAndWait(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API void BNDSCViewFreeSymbols(BNDSCSymbolRep* symbols, size_t count); + + SHAREDCACHE_FFI_API BNDSCMappedMemoryRegion* BNDSCViewGetLoadedRegions(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API void BNDSCViewFreeLoadedRegions(BNDSCMappedMemoryRegion* images, size_t count); + + SHAREDCACHE_FFI_API BNDSCImage* BNDSCViewGetAllImages(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API void BNDSCViewFreeAllImages(BNDSCImage* images, size_t count); + + SHAREDCACHE_FFI_API BNDSCBackingCache* BNDSCViewGetBackingCaches(BNSharedCache* cache, size_t* count); + SHAREDCACHE_FFI_API void BNDSCViewFreeBackingCaches(BNDSCBackingCache* caches, size_t count); + + SHAREDCACHE_FFI_API void BNDSCFindSymbolAtAddressAndApplyToAddress(BNSharedCache* cache, uint64_t symbolLocation, uint64_t targetLocation, bool triggerReanalysis); + + SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForAddress(BNSharedCache* cache, uint64_t address); + SHAREDCACHE_FFI_API char* BNDSCViewGetImageHeaderForName(BNSharedCache* cache, char* name); + + [[maybe_unused]] SHAREDCACHE_FFI_API BNDSCMemoryUsageInfo BNDSCViewGetMemoryUsageInfo(); + +#ifdef __cplusplus +} +#endif |
