diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-04-01 15:19:59 -0400 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-04-01 16:07:49 -0400 |
| commit | a7fd2f8080123ddcbb51fdcc3d3e786e60a111b5 (patch) | |
| tree | e3b81857df1c778e8c5cfa0afc567675963d5801 /view | |
| parent | 020562880d65802d6eef26ca32428a405bb78dc3 (diff) | |
Add md1rom binary view with pure Rust lzma implementation
Diffstat (limited to 'view')
| -rw-r--r-- | view/md1rom/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | view/md1rom/LICENSE | 13 | ||||
| -rw-r--r-- | view/md1rom/README.md | 23 | ||||
| -rw-r--r-- | view/md1rom/md1rom.cpp | 467 | ||||
| -rw-r--r-- | view/md1rom/md1rom.h | 98 |
5 files changed, 634 insertions, 0 deletions
diff --git a/view/md1rom/CMakeLists.txt b/view/md1rom/CMakeLists.txt new file mode 100644 index 00000000..ebf2fa11 --- /dev/null +++ b/view/md1rom/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.9 FATAL_ERROR) + +project(view_md1rom) + +if(NOT BN_INTERNAL_BUILD) + add_subdirectory(${PROJECT_SOURCE_DIR}/../.. ${PROJECT_BINARY_DIR}/api) +endif() + +file(GLOB SOURCES + *.cpp + *.h) + +if(DEMO) + add_library(view_md1rom STATIC ${SOURCES}) +else() + add_library(view_md1rom SHARED ${SOURCES}) +endif() + +target_link_libraries(view_md1rom binaryninjaapi) + +set_target_properties(view_md1rom PROPERTIES + CXX_STANDARD 17 + CXX_VISIBILITY_PRESET hidden + CXX_STANDARD_REQUIRED ON + VISIBILITY_INLINES_HIDDEN ON + POSITION_INDEPENDENT_CODE ON) + +if(BN_INTERNAL_BUILD) + plugin_rpath(view_md1rom) + set_target_properties(view_md1rom PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +endif() diff --git a/view/md1rom/LICENSE b/view/md1rom/LICENSE new file mode 100644 index 00000000..265bf79a --- /dev/null +++ b/view/md1rom/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021-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. diff --git a/view/md1rom/README.md b/view/md1rom/README.md new file mode 100644 index 00000000..07cf33cc --- /dev/null +++ b/view/md1rom/README.md @@ -0,0 +1,23 @@ +# view-md1rom +This is the md1rom view plugin that ships with Binary Ninja. + +## Building + +Building the architecture plugin requires `cmake` 3.9 or above. You will also need the +[Binary Ninja API source](https://github.com/Vector35/binaryninja-api). + +Run `cmake`. This can be done either from a separate build directory or from the source +directory. Once that is complete, run `make` in the build directory to compile the plugin. + +The plugin can be found in the root of the build directory as `libview_md1rom.so`, +`libview_md1rom.dylib` or `view_md1rom.dll` depending on your platform. + +To install the plugin, first launch Binary Ninja and uncheck the "Md1rom view plugin" +option in the "Core Plugins" section. This will cause Binary Ninja to stop loading the +bundled plugin so that its replacement can be loaded. Once this is complete, you can copy +the plugin into the user plugins directory (you can locate this by using the "Open Plugin Folder" +option in the Binary Ninja UI). + +**Do not replace the view plugin in the Binary Ninja install directory. This will be overwritten +every time there is a Binary Ninja update. Use the above process to ensure that updates do not +automatically uninstall your custom build.** diff --git a/view/md1rom/md1rom.cpp b/view/md1rom/md1rom.cpp new file mode 100644 index 00000000..d9354468 --- /dev/null +++ b/view/md1rom/md1rom.cpp @@ -0,0 +1,467 @@ +#include "md1rom.h" + +using namespace std; +using namespace BinaryNinja; + +static Md1romViewType* g_md1romViewType = nullptr; + + +void BinaryNinja::InitMd1romViewType() +{ + static Md1romViewType type; + BinaryViewType::Register(&type); + g_md1romViewType = &type; +} + + +Md1romView::Md1romView(BinaryNinja::BinaryView* data, bool parseOnly): BinaryView("MD1Rom", data->GetFile(), data), + m_parseOnly(parseOnly) +{ + (void)m_parseOnly; + + CreateLogger("BinaryView"); + m_logger = CreateLogger("BinaryView.MD1RomView"); + + BinaryReader reader(data); + uint64_t offset = 0; + try + { + while (true) + { + Md1romSegment seg; + reader.Seek(offset); + seg.magic = reader.Read32(); + seg.length = reader.Read32(); + DataBuffer name = reader.Read(0x20); + size_t nameLen = name.GetData() ? strlen((char*)name.GetData()) : 0; + if (nameLen) + seg.name = std::string((char*)name.GetData(), nameLen); + + seg.addr = reader.Read32(); + seg.mode = reader.Read32(); + seg.magic2 = reader.Read32(); + seg.offset = reader.Read32(); + seg.headerStart = offset; + seg.dataStart = offset + seg.offset; + + if ((seg.magic != MAGIC_1) || (seg.magic2 != MAGIC_2)) + break; + + m_segments.emplace_back(seg); + m_logger->LogDebug("segment: %s, offset: 0x%x, length: 0x%x, addr: 0x%x, file offset: 0x%llx", + seg.name.c_str(), seg.offset, seg.length, seg.addr, offset); + + if (seg.name == "md1rom") + { + m_mainRomFound = true; + m_mainRom = seg; + } + else if (seg.name == "md1_dbginfo") + { + m_dbgInfoFound = true; + m_dbgInfoSeg = seg; + } + else if (seg.name == "md1_mddb") + { + m_dbgDbFound = true; + m_dbgDatabaseSeg = seg; + } + + offset += (seg.length + seg.offset); + if (offset % 0x10 != 0) + offset = offset - offset % 0x10 + 0x10; + + if (offset >= data->GetLength()) + break; + } + } + catch (ReadException&) + { + m_logger->LogWarn("read exception"); + } +} + +Md1romView::~Md1romView() +{ + +} + + +uint64_t Md1romView::PerformGetEntryPoint() const +{ + return m_entryPoint; +} + + +BNEndianness Md1romView::PerformGetDefaultEndianness() const +{ + return m_endian; +} + + +bool Md1romView::PerformIsRelocatable() const +{ + return m_relocatable; +} + + +size_t Md1romView::PerformGetAddressSize() const +{ + return m_addressSize; +} + + +bool Md1romView::Init() +{ + if (m_mainRomFound) + { + uint64_t mainRomBase = MAIN_ROM_BASE; + Ref<Settings> settings = GetLoadSettings(GetTypeName()); + if (settings && settings->Contains("loader.imageBase")) + mainRomBase = settings->Get<uint64_t>("loader.imageBase", this); + + AddAutoSegment(mainRomBase + m_mainRom.addr, m_mainRom.length, m_mainRom.dataStart, m_mainRom.length, + SegmentExecutable | SegmentReadable | SegmentDenyWrite); + AddAutoSection(m_mainRom.name, mainRomBase + m_mainRom.addr, m_mainRom.length, ReadOnlyCodeSectionSemantics); + m_entryPoint = mainRomBase + m_mainRom.addr; + + if (settings && settings->Contains("loader.architecture")) + { + auto arch = Architecture::GetByName(settings->Get<string>("loader.architecture", this)); + if (!m_arch || (arch && (arch->GetName() != m_arch->GetName()))) + m_arch = arch; + } + else + { + m_arch = Architecture::GetByName("nanomips"); + } + + if (m_arch) + { + SetDefaultArchitecture(m_arch); + } + else + { + LogWarn("nanoMIPS architecture not found. Code cannot be disassembled. If you are interested in purchasing " + "the nanoMIPS architecture plugin, please contact us via https://binary.ninja/support/"); + } + + if (settings && settings->Contains("loader.platform")) // handle overrides + { + Ref<Platform> platformOverride = Platform::GetByName(settings->Get<string>("loader.platform", this)); + if (platformOverride) + m_plat = platformOverride; + } + else + { + m_plat = Platform::GetByName("nanomips"); + } + + if (m_plat) + { + SetDefaultPlatform(m_plat); + } + } + + // Finished for parse only mode + if (m_parseOnly) + { + return true; + } + + if (m_plat) + { + AddEntryPointForAnalysis(m_plat, m_entryPoint); + // _start is defined by the dbginfo, so we use a different name + DefineAutoSymbol(new Symbol(FunctionSymbol, "_entry_point", m_entryPoint, GlobalBinding)); + } + + // Create the type for the segment header + StructureBuilder builder; + builder.AddMember(Type::IntegerType(4, false), "magic"); + builder.AddMember(Type::IntegerType(4, false), "length"); + builder.AddMember(Type::ArrayType(Type::IntegerType(1, true), 0x20), "name"); + builder.AddMember(Type::IntegerType(4, false), "addr"); + builder.AddMember(Type::IntegerType(4, false), "mode"); + builder.AddMember(Type::IntegerType(4, false), "magic2"); + builder.AddMember(Type::IntegerType(4, false), "offset"); + builder.AddMember(Type::IntegerType(4, false), "unk1"); + builder.AddMember(Type::IntegerType(4, false), "unk2"); + builder.AddMember(Type::IntegerType(4, false), "unk3"); + builder.AddMember(Type::IntegerType(4, false), "unk4"); + builder.AddMember(Type::IntegerType(4, false), "unk5"); + builder.AddMember(Type::IntegerType(4, false), "unk6"); + + Ref<Structure> headerStruct = builder.Finalize(); + Ref<Type> headerType = Type::StructureType(headerStruct); + QualifiedName headerName("Md1rom_Header"); + const string headerTypeId = Type::GenerateAutoTypeId("md1rom", headerName); + QualifiedName md1HeaderName = GetParentView()->DefineType(headerTypeId, headerName, headerType); + + for (const auto& seg: m_segments) + { + GetParentView()->DefineDataVariable(seg.headerStart, Type::NamedType(GetParentView(), md1HeaderName)); + GetParentView()->AddAutoSection(seg.name + "_header", seg.headerStart, seg.offset); + GetParentView()->AddAutoSection(seg.name + "_data", seg.dataStart, seg.length); + } + + m_symbolQueue = new SymbolQueue(); + ParseDebugInfo(); + // This does not seem to work, see + // https://github.com/FirmWire/FirmWire/blob/main/firmwire/vendor/mtk/mtkdb/parse_mdb.py#L30 + // ParseDebugDatabase(); + + // Process the queued symbols + m_symbolQueue->Process(); + delete m_symbolQueue; + m_symbolQueue = nullptr; + + return true; +} + + +void Md1romView::DefineMd1RomSymbol(BNSymbolType type, const string& name, uint64_t addr, + BNSymbolBinding binding, Ref<Type> typeObj) +{ + // If name is empty, symbol is not valid + if (name.size() == 0) + return; + + auto process = [=]() { + NameSpace nameSpace = GetInternalNameSpace(); + // If name does not start with alphabetic character or symbol, prepend an underscore + string rawName = name; + if (!(((name[0] >= 'A') && (name[0] <= 'Z')) || ((name[0] >= 'a') && (name[0] <= 'z')) || (name[0] == '_') + || (name[0] == '?') || (name[0] == '$') || (name[0] == '@') || (name[0] == '.'))) + rawName = "_" + name; + + return std::pair<Ref<Symbol>, Ref<Type>>( + new Symbol(type, rawName, rawName, rawName, addr, binding, nameSpace), typeObj); + }; + + if (m_symbolQueue) + { + m_symbolQueue->Append(process, [this](Symbol* symbol, Type* type) { + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), symbol, type); + }); + } + else + { + auto result = process(); + DefineAutoSymbolAndVariableOrFunction(GetDefaultPlatform(), result.first, result.second); + } +} + + +void Md1romView::ParseDebugInfo() +{ + if (!m_dbgInfoFound) + return; + + DataBuffer data = GetParentView()->ReadBuffer(m_dbgInfoSeg.dataStart, m_dbgInfoSeg.length); + if (data.GetLength() != m_dbgInfoSeg.length) + return; + + DataBuffer decompressed; + if (!data.LzmaDecompress(decompressed)) + return; + + m_logger->LogDebug("The size of decompressed buffer: 0x%zx", decompressed.GetLength()); + + Ref<FileMetadata> file = new FileMetadata; + Ref<BinaryView> view = new BinaryData(file, decompressed); + BinaryReader reader(view); + + reader.Seek(0x1c); + auto target = reader.ReadCString(); + auto hardwarePlatform = reader.ReadCString(); + auto molyVersion = reader.ReadCString(); + auto buildTime = reader.ReadCString(); + + [[maybe_unused]] auto functionSymbolsOffset = reader.Read32() + 0x10; + auto fileSymbolsOffset = reader.Read32() + 0x10; + + try + { + while (true) + { + Md1DebugSymbol symbol; + symbol.name = reader.ReadCString(); + symbol.start = reader.Read32(); + symbol.end = reader.Read32(); + m_debugSymbols.emplace_back(symbol); + DefineMd1RomSymbol(FunctionSymbol, symbol.name, symbol.start); + if (reader.GetOffset() >= fileSymbolsOffset) + break; + } + } + catch(...) + { + m_logger->LogWarn("exception while parsing debug symbols"); + } + file->Close(); +} + + +void Md1romView::ParseDebugDatabase() +{ +// if (!m_dbgDbFound) +// return; +// +// DataBuffer data = GetParentView()->ReadBuffer(m_dbgDatabaseSeg.dataStart, m_dbgDatabaseSeg.length); +// if (data.GetLength() != m_dbgDatabaseSeg.length) +// return; +// +// Ref<FileMetadata> file = new FileMetadata; +// Ref<BinaryView> view = new BinaryData(file, data); +// BinaryReader reader(view); +// +// if (reader.Read32() != 0x44544143) //CATD +// return; +// +// auto unused = reader.Read32(); +// unused = reader.Read32(); +// +// if (reader.Read32() != 0x44414548) //HEAD +// return; +// +// unused = reader.Read32(); +// auto entryCount = reader.Read32(); +// for (size_t i = 0; i < entryCount; i++) +// { +// CATDEntryHeader entry; +// entry.unknown = reader.Read32(); +// entry.offset = reader.Read32(); +// entry.size = reader.Read32(); +// m_catdEntryHeaders.emplace_back(entry); +// } +// +// for (const auto& header: m_catdEntryHeaders) +// { +// if (header.size < 16) +// continue; +// +// reader.Seek(header.offset); +// if (reader.Read32() != 0x41544144) +// continue; +// +// reader.Read32(); +// CATDEntry catdEntry; +// catdEntry.offset = reader.Read32(); +// catdEntry.size = reader.Read32(); +// if (header.size < 20) +// catdEntry.uncompressedSize = header.size; +// else +// catdEntry.uncompressedSize = reader.Read32(); +// +// m_catdEntries.emplace_back(catdEntry); +// } +// +// for (const auto& entry: m_catdEntries) +// { +// if (entry.size != entry.uncompressedSize) +// { +// reader.Seek(entry.offset); +// auto props = reader.Read64(); +// auto dictionarySize = reader.Read32(); +// auto lc = props % 9; +// props = props / 9; +// auto pb = props / 5; +// auto lp = props % 5; +// } +// else +// { +// +// } +// } +} + + +Md1romViewType::Md1romViewType(): BinaryViewType("MD1Rom", "MD1Rom") +{ + m_logger = LogRegistry::CreateLogger("BinaryView.Md1romViewType"); +} + + +Ref<BinaryView> Md1romViewType::Create(BinaryView* data) +{ + try + { + return new Md1romView(data); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +Ref<BinaryView> Md1romViewType::Parse(BinaryView* data) +{ + try + { + return new Md1romView(data, true); + } + catch (std::exception& e) + { + m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what()); + return nullptr; + } +} + + +bool Md1romViewType::IsTypeValidForData(BinaryView* data) +{ + DataBuffer sig = data->ReadBuffer(0, 4); + if (sig.GetLength() != 4) + return false; + if (memcmp(sig.GetData(), "\x88\x16\x88\x58", 4) != 0) + return false; + + sig = data->ReadBuffer(0x30, 4); + if (sig.GetLength() != 4) + return false; + if (memcmp(sig.GetData(), "\x89\x16\x89\x58", 4) != 0) + return false; + + return true; +} + + +Ref<Settings> Md1romViewType::GetLoadSettingsForData(BinaryNinja::BinaryView* data) +{ + Ref<BinaryView> viewRef = Parse(data); + if (!viewRef || !viewRef->Init()) + { + m_logger->LogError("View type '%s' could not be created", GetName().c_str()); + return nullptr; + } + + Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef); + + // specify default load settings that can be overridden + vector<string> overrides = {"loader.architecture", "loader.imageBase", "loader.platform"}; + for (const auto& override : overrides) + { + if (settings->Contains(override)) + settings->UpdateProperty(override, "readOnly", false); + } + return settings; +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + +#ifdef DEMO_VERSION + bool Md1RomPluginInit() +#else + BINARYNINJAPLUGIN bool CorePluginInit() +#endif + { + InitMd1romViewType(); + return true; + } +} diff --git a/view/md1rom/md1rom.h b/view/md1rom/md1rom.h new file mode 100644 index 00000000..445e27aa --- /dev/null +++ b/view/md1rom/md1rom.h @@ -0,0 +1,98 @@ +#include "binaryninjaapi.h" + +namespace BinaryNinja +{ + const uint32_t MAGIC_1 = 0x58881688; + const uint32_t MAGIC_2 = 0x58891689; + const uint64_t MAIN_ROM_BASE = 0x90000000; + + struct Md1romSegment + { + uint32_t magic; + uint32_t length; + std::string name; + uint32_t addr; + uint32_t mode; + uint32_t magic2; + uint32_t offset; + // start of the header in the raw view + uint32_t headerStart; + + // start of the data in the raw view + uint32_t dataStart; + }; + + struct Md1DebugSymbol + { + std::string name; + uint32_t start; + uint32_t end; + }; + + struct CATDEntryHeader + { + uint32_t unknown; + uint32_t offset; + uint32_t size; + }; + + struct CATDEntry + { + uint32_t offset; + uint32_t size; + uint32_t uncompressedSize; + }; + + class Md1romView: public BinaryView + { + bool m_parseOnly; + uint64_t m_entryPoint{}; + BNEndianness m_endian = LittleEndian; + size_t m_addressSize = 4; + Ref<Architecture> m_arch = nullptr; + Ref<Platform> m_plat = nullptr; + Ref<Logger> m_logger; + bool m_relocatable = false; + + std::vector<Md1romSegment> m_segments; + std::vector<Md1DebugSymbol> m_debugSymbols; + std::vector<CATDEntryHeader> m_catdEntryHeaders; + std::vector<CATDEntry> m_catdEntries; + + bool m_mainRomFound = false, m_dbgInfoFound = false, m_dbgDbFound = false; + Md1romSegment m_mainRom, m_dbgInfoSeg, m_dbgDatabaseSeg; + + SymbolQueue* m_symbolQueue = nullptr; + + virtual uint64_t PerformGetEntryPoint() const override; + + virtual bool PerformIsExecutable() const override { return true; } + virtual BNEndianness PerformGetDefaultEndianness() const override; + virtual bool PerformIsRelocatable() const override; + virtual size_t PerformGetAddressSize() const override; + + void ParseDebugInfo(); + void ParseDebugDatabase(); + void DefineMd1RomSymbol(BNSymbolType type, const std::string& name, uint64_t addr, + BNSymbolBinding binding=NoBinding, Ref<Type> typeObj=nullptr); + + public: + Md1romView(BinaryView* data, bool parseOnly = false); + ~Md1romView(); + + virtual bool Init() override; + }; + + class Md1romViewType: public BinaryViewType + { + Ref<Logger> m_logger; + public: + Md1romViewType(); + virtual Ref<BinaryView> Create(BinaryView* data) override; + virtual Ref<BinaryView> Parse(BinaryView* data) override; + virtual bool IsTypeValidForData(BinaryView* data) override; + virtual Ref<Settings> GetLoadSettingsForData(BinaryView* data) override; + }; + + void InitMd1romViewType(); +} |
