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/md1rom/md1rom.cpp | |
| parent | 020562880d65802d6eef26ca32428a405bb78dc3 (diff) | |
Add md1rom binary view with pure Rust lzma implementation
Diffstat (limited to 'view/md1rom/md1rom.cpp')
| -rw-r--r-- | view/md1rom/md1rom.cpp | 467 |
1 files changed, 467 insertions, 0 deletions
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; + } +} |
