summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Wagner <rusty.wagner@gmail.com>2024-04-01 15:19:59 -0400
committerRusty Wagner <rusty.wagner@gmail.com>2024-04-01 16:07:49 -0400
commita7fd2f8080123ddcbb51fdcc3d3e786e60a111b5 (patch)
treee3b81857df1c778e8c5cfa0afc567675963d5801
parent020562880d65802d6eef26ca32428a405bb78dc3 (diff)
Add md1rom binary view with pure Rust lzma implementation
-rw-r--r--binaryninjaapi.h27
-rw-r--r--binaryninjacore.h3
-rw-r--r--databuffer.cpp34
-rw-r--r--docs/about/open-source.md9
-rw-r--r--view/md1rom/CMakeLists.txt33
-rw-r--r--view/md1rom/LICENSE13
-rw-r--r--view/md1rom/README.md23
-rw-r--r--view/md1rom/md1rom.cpp467
-rw-r--r--view/md1rom/md1rom.h98
9 files changed, 705 insertions, 2 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h
index 14f38395..b27c8ca8 100644
--- a/binaryninjaapi.h
+++ b/binaryninjaapi.h
@@ -2114,6 +2114,33 @@ namespace BinaryNinja {
\returns Whether decompression was successful
*/
bool ZlibDecompress(DataBuffer& output) const;
+
+ /*! Decompress the contents of this buffer via LZMA compression
+
+ @threadunsafe
+
+ \param[out] output Output DataBuffer the decompressed contents will be stored in.
+ \returns Whether decompression was successful
+ */
+ bool LzmaDecompress(DataBuffer& output) const;
+
+ /*! Decompress the contents of this buffer via LZMA2 compression
+
+ @threadunsafe
+
+ \param[out] output Output DataBuffer the decompressed contents will be stored in.
+ \returns Whether decompression was successful
+ */
+ bool Lzma2Decompress(DataBuffer& output) const;
+
+ /*! Decompress the contents of this buffer via XZ compression
+
+ @threadunsafe
+
+ \param[out] output Output DataBuffer the decompressed contents will be stored in.
+ \returns Whether decompression was successful
+ */
+ bool XzDecompress(DataBuffer& output) const;
};
/*! TemporaryFile is used for creating temporary files, stored (temporarily) in the system's default temporary file
diff --git a/binaryninjacore.h b/binaryninjacore.h
index 5e68aa4b..6e2309dd 100644
--- a/binaryninjacore.h
+++ b/binaryninjacore.h
@@ -3323,6 +3323,9 @@ extern "C"
BINARYNINJACOREAPI BNDataBuffer* BNZlibCompress(BNDataBuffer* buf);
BINARYNINJACOREAPI BNDataBuffer* BNZlibDecompress(BNDataBuffer* buf);
+ BINARYNINJACOREAPI BNDataBuffer* BNLzmaDecompress(BNDataBuffer* buf);
+ BINARYNINJACOREAPI BNDataBuffer* BNLzma2Decompress(BNDataBuffer* buf);
+ BINARYNINJACOREAPI BNDataBuffer* BNXzDecompress(BNDataBuffer* buf);
// Save settings
BINARYNINJACOREAPI BNSaveSettings* BNCreateSaveSettings(void);
diff --git a/databuffer.cpp b/databuffer.cpp
index 861c60ed..6f9c3258 100644
--- a/databuffer.cpp
+++ b/databuffer.cpp
@@ -222,7 +222,7 @@ DataBuffer DataBuffer::FromBase64(const string& src)
bool DataBuffer::ZlibCompress(DataBuffer& output) const
{
- BNDataBuffer* result = BNZlibCompress(output.m_buffer);
+ BNDataBuffer* result = BNZlibCompress(m_buffer);
if (!result)
return false;
output = DataBuffer(result);
@@ -232,7 +232,37 @@ bool DataBuffer::ZlibCompress(DataBuffer& output) const
bool DataBuffer::ZlibDecompress(DataBuffer& output) const
{
- BNDataBuffer* result = BNZlibDecompress(output.m_buffer);
+ BNDataBuffer* result = BNZlibDecompress(m_buffer);
+ if (!result)
+ return false;
+ output = DataBuffer(result);
+ return true;
+}
+
+
+bool DataBuffer::LzmaDecompress(DataBuffer& output) const
+{
+ BNDataBuffer* result = BNLzmaDecompress(m_buffer);
+ if (!result)
+ return false;
+ output = DataBuffer(result);
+ return true;
+}
+
+
+bool DataBuffer::Lzma2Decompress(DataBuffer& output) const
+{
+ BNDataBuffer* result = BNLzma2Decompress(m_buffer);
+ if (!result)
+ return false;
+ output = DataBuffer(result);
+ return true;
+}
+
+
+bool DataBuffer::XzDecompress(DataBuffer& output) const
+{
+ BNDataBuffer* result = BNXzDecompress(m_buffer);
if (!result)
return false;
output = DataBuffer(result);
diff --git a/docs/about/open-source.md b/docs/about/open-source.md
index 9b7d18a7..28dac467 100644
--- a/docs/about/open-source.md
+++ b/docs/about/open-source.md
@@ -70,6 +70,8 @@ The previous tools are used in the generation of our documentation, but are not
- [core-foundation-sys] ([core-foundation-sys license] - APACHE 2.0 / MIT)
- [core-foundation] ([core-foundation license] - APACHE 2.0 / MIT)
- [cpufeatures] ([cpufeatures license] - APACHE 2.0 / MIT)
+ - [crc] ([crc license] - APACHE 2.0 / MIT)
+ - [crc-catalog] ([crc-catalog license] - APACHE 2.0 / MIT)
- [crc32fast] ([crc32fast license] - APACHE 2.0 / MIT)
- [crypto-common] ([crypto-common license] - APACHE 2.0 / MIT)
- [ctr] ([ctr license] - APACHE 2.0 / MIT)
@@ -126,6 +128,7 @@ The previous tools are used in the generation of our documentation, but are not
- [libloading] ([libloading license] - ISC)
- [libz-sys] ([libz-sys license] - APACHE 2.0 / MIT)
- [log] ([log license] - APACHE 2.0 / MIT)
+ - [lzma-rs] ([lzma-rs license] - MIT)
- [lzxd] ([lzxd license] - APACHE 2.0 / MIT)
- [machine-uid] ([machine-uid license] - MIT)
- [markdown] ([markdown license] - MIT)
@@ -416,6 +419,10 @@ Please note that we offer no support for running Binary Ninja with modified Qt l
[core-foundation license]: https://github.com/servo/core-foundation-rs/blob/master/LICENSE-MIT
[cpufeatures]: https://github.com/RustCrypto/utils/tree/master/cpufeatures
[cpufeatures license]: https://github.com/RustCrypto/utils/blob/master/cpufeatures/LICENSE-MIT
+[crc]: https://github.com/mrhooray/crc-rs
+[crc license]: https://github.com/mrhooray/crc-rs/blob/master/LICENSE-MIT
+[crc-catalog]: https://github.com/akhilles/crc-catalog
+[crc-catalog license]: https://github.com/akhilles/crc-catalog/blob/master/LICENSES/MIT.txt
[crc32fast]: https://github.com/srijs/rust-crc32fast
[crc32fast license]: https://github.com/srijs/rust-crc32fast/blob/master/LICENSE-MIT
[crypto-common]: https://github.com/RustCrypto/traits/tree/master/crypto-common
@@ -530,6 +537,8 @@ Please note that we offer no support for running Binary Ninja with modified Qt l
[libz-sys license]: https://github.com/rust-lang/libz-sys/blob/main/LICENSE-MIT
[log]: https://github.com/rust-lang/log
[log license]: https://github.com/rust-lang/log/blob/master/LICENSE-MIT
+[lzma-rs]: https://github.com/gendx/lzma-rs
+[lzma-rs license]: https://github.com/gendx/lzma-rs/blob/master/LICENSE
[lzxd]: https://github.com/Lonami/lzxd
[lzxd license]: https://github.com/Lonami/lzxd/blob/master/LICENSE-MIT
[machine-uid]: https://github.com/Hanaasagi/machine-uid
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();
+}