summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--platform/efi/platform_efi.cpp1
-rw-r--r--view/pe/peview.cpp2
-rw-r--r--view/pe/teview.cpp387
-rw-r--r--view/pe/teview.h97
4 files changed, 486 insertions, 1 deletions
diff --git a/platform/efi/platform_efi.cpp b/platform/efi/platform_efi.cpp
index 4a1763ef..717922f6 100644
--- a/platform/efi/platform_efi.cpp
+++ b/platform/efi/platform_efi.cpp
@@ -249,7 +249,6 @@ public:
}
};
-
extern "C"
{
BN_DECLARE_CORE_ABI_VERSION
diff --git a/view/pe/peview.cpp b/view/pe/peview.cpp
index 794cb5d6..19cf7e09 100644
--- a/view/pe/peview.cpp
+++ b/view/pe/peview.cpp
@@ -10,6 +10,7 @@
#include <utility>
#include "peview.h"
#include "coffview.h"
+#include "teview.h"
#define STRING_READ_CHUNK_SIZE 32
@@ -2965,6 +2966,7 @@ extern "C"
{
InitPEViewType();
InitCOFFViewType();
+ InitTEViewType();
return true;
}
}
diff --git a/view/pe/teview.cpp b/view/pe/teview.cpp
new file mode 100644
index 00000000..46b06a9a
--- /dev/null
+++ b/view/pe/teview.cpp
@@ -0,0 +1,387 @@
+// BinaryView for EFI Terse Executable images
+//
+// Resources:
+// - https://uefi.org/specs/PI/1.8/V1_TE_Image.html
+// - edk2/BaseTools/Source/C/GenFw/GenFw.c
+
+#include "teview.h"
+
+using namespace BinaryNinja;
+using namespace std;
+
+static TEViewType* g_teViewType = nullptr;
+
+void BinaryNinja::InitTEViewType()
+{
+ static TEViewType type;
+ BinaryViewType::Register(&type);
+ g_teViewType = &type;
+}
+
+void TEView::ReadTEImageHeader(BinaryReader& reader, struct TEImageHeader& header)
+{
+ header.magic = reader.Read16();
+ header.machine = reader.Read16();
+ header.numberOfSections = reader.Read8();
+ header.subsystem = reader.Read8();
+ header.strippedSize = reader.Read16();
+ header.addressOfEntrypoint = reader.Read32();
+ header.baseOfCode = reader.Read32();
+ header.imageBase = reader.Read64();
+ header.dataDirectory[0].virtualAddress = reader.Read32();
+ header.dataDirectory[0].size = reader.Read32();
+ header.dataDirectory[1].virtualAddress = reader.Read32();
+ header.dataDirectory[1].size = reader.Read32();
+
+ m_logger->LogDebug(
+ "TEImageHeader:\n"
+ "\tmagic: 0x%04x\n"
+ "\tmachine: 0x%04x\n"
+ "\tnumberOfSections: 0x%02x\n"
+ "\tsubsystem: 0x%02x\n"
+ "\tstrippedSize: 0x%04x\n"
+ "\taddressOfEntrypoint: 0x%08x\n"
+ "\tbaseOfCode: 0x%08x\n"
+ "\timageBase: 0x%016x\n"
+ "\tdataDirectory[0].virtualAddress: 0x%08x\n"
+ "\tdataDirectory[0].size: 0x%08x\n"
+ "\tdataDirectory[1].virtualAddress: 0x%08x\n"
+ "\tdataDirectory[1].size: 0x%08x\n",
+ header.magic,
+ header.machine,
+ header.numberOfSections,
+ header.subsystem,
+ header.strippedSize,
+ header.addressOfEntrypoint,
+ header.baseOfCode,
+ header.imageBase,
+ header.dataDirectory[0].virtualAddress,
+ header.dataDirectory[0].size,
+ header.dataDirectory[1].virtualAddress,
+ header.dataDirectory[1].size
+ );
+}
+
+void TEView::ReadTEImageSectionHeaders(BinaryReader& reader, uint32_t numSections)
+{
+ for (uint32_t i = 0; i < numSections; i++) {
+ TEImageSectionHeader section;
+ section.name = reader.ReadString(8);
+ section.virtualSize = reader.Read32();
+ section.virtualAddress = reader.Read32();
+ section.sizeOfRawData = reader.Read32();
+ section.pointerToRawData = reader.Read32();
+ section.pointerToRelocations = reader.Read32();
+ section.pointerToLineNumbers = reader.Read32();
+ section.numberOfRelocations = reader.Read16();
+ section.numberOfLineNumbers = reader.Read16();
+ section.characteristics = reader.Read32();
+
+ m_logger->LogDebug(
+ "TEImageSectionHeader[%i]\n"
+ "\tname: %s\n"
+ "\tvirtualSize: %08x\n"
+ "\tvirtualAddress: %08x\n"
+ "\tsizeOfRawData: %08x\n"
+ "\tpointerToRawData: %08x\n"
+ "\tpointerToRelocations: %08x\n"
+ "\tpointerToLineNumbers: %08x\n"
+ "\tnumberOfRelocations: %04x\n"
+ "\tnumberOfLineNumbers: %04x\n"
+ "\tcharacteristics: %08x\n",
+ i,
+ section.name.c_str(),
+ section.virtualSize,
+ section.virtualAddress,
+ section.sizeOfRawData,
+ section.pointerToRawData,
+ section.pointerToRelocations,
+ section.pointerToLineNumbers,
+ section.numberOfRelocations,
+ section.numberOfLineNumbers,
+ section.characteristics
+ );
+
+ m_sections.push_back(section);
+ }
+}
+
+void TEView::CreateSections()
+{
+ for (size_t i = 0; i < m_sections.size(); i++) {
+ auto section = m_sections[i];
+ uint32_t flags = 0;
+ if (section.characteristics & EFI_IMAGE_SCN_MEM_WRITE)
+ flags |= SegmentWritable;
+ if (section.characteristics & EFI_IMAGE_SCN_MEM_READ)
+ flags |= SegmentReadable;
+ if (section.characteristics & EFI_IMAGE_SCN_MEM_EXECUTE)
+ flags |= SegmentExecutable;
+
+ AddAutoSegment(
+ section.virtualAddress + m_imageBase,
+ section.virtualSize,
+ section.virtualAddress,
+ section.virtualSize,
+ flags
+ );
+
+ BNSectionSemantics semantics = DefaultSectionSemantics;
+ uint32_t pFlags = flags & 0x7;
+ if (pFlags == (SegmentReadable | SegmentExecutable))
+ semantics = ReadOnlyCodeSectionSemantics;
+ else if (pFlags == SegmentReadable)
+ semantics = ReadOnlyDataSectionSemantics;
+ else if (pFlags == (SegmentReadable | SegmentWritable))
+ semantics = ReadWriteDataSectionSemantics;
+ AddAutoSection(section.name, section.virtualAddress + m_imageBase, section.virtualSize, semantics);
+ }
+}
+
+void TEView::AssignHeaderTypes()
+{
+ StructureBuilder dataDirectoryBuilder;
+ dataDirectoryBuilder.AddMember(Type::IntegerType(4, false), "VirtualAddress");
+ dataDirectoryBuilder.AddMember(Type::IntegerType(4, false), "Size");
+ auto dataDirectoryStruct = dataDirectoryBuilder.Finalize();
+ auto dataDirectoryType = Type::StructureType(dataDirectoryStruct);
+
+ StructureBuilder headerBuilder;
+ headerBuilder.AddMember(Type::IntegerType(2, false), "Signature");
+ headerBuilder.AddMember(Type::IntegerType(2, false), "Machine");
+ headerBuilder.AddMember(Type::IntegerType(1, false), "NumberOfSections");
+ headerBuilder.AddMember(Type::IntegerType(1, false), "Subsystem");
+ headerBuilder.AddMember(Type::IntegerType(2, false), "StrippedSize");
+ headerBuilder.AddMember(Type::IntegerType(4, false), "AddressOfEntryPoint");
+ headerBuilder.AddMember(Type::IntegerType(4, false), "BaseOfCode");
+ headerBuilder.AddMember(Type::IntegerType(8, false), "ImageBase");
+ headerBuilder.AddMember(Type::ArrayType(dataDirectoryType, 2), "DataDirectory");
+
+ auto headerStruct = headerBuilder.Finalize();
+ auto headerType = Type::StructureType(headerStruct);
+ QualifiedName headerName = string("TE_Header");
+ auto headerTypeId = Type::GenerateAutoTypeId("te", headerName);
+ QualifiedName headerTypeName = DefineType(headerTypeId, headerName, headerType);
+ DefineDataVariable(m_imageBase, Type::NamedType(this, headerTypeName));
+
+ StructureBuilder sectionBuilder;
+ sectionBuilder.AddMember(Type::IntegerType(8, false), "Name");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "VirtualSize");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "VirtualAddress");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "SizeOfRawData");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "PointerToRawData");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "PointerToRelocations");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "PointerToLinenumbers");
+ sectionBuilder.AddMember(Type::IntegerType(2, false), "NumberOfRelocations");
+ sectionBuilder.AddMember(Type::IntegerType(2, false), "NumberOfLinenumbers");
+ sectionBuilder.AddMember(Type::IntegerType(4, false), "Characteristics");
+ auto sectionStruct = sectionBuilder.Finalize();
+ auto sectionType = Type::StructureType(sectionStruct);
+ for (size_t i = 0; i < m_sections.size(); i++)
+ {
+ QualifiedName sectionName = string("TE_Section_Header_") + to_string(i);
+ auto sectionTypeId = Type::GenerateAutoTypeId("te", sectionName);
+ QualifiedName sectionTypeName = DefineType(sectionTypeId, sectionName, sectionType);
+ DefineDataVariable(
+ m_imageBase + EFI_TE_IMAGE_HEADER_SIZE + (EFI_TE_SECTION_HEADER_SIZE * i),
+ Type::NamedType(this, sectionTypeName)
+ );
+ }
+}
+
+TEView::TEView(BinaryView* bv, bool parseOnly) : BinaryView("TE", bv->GetFile(), bv), m_parseOnly(parseOnly)
+{
+ CreateLogger("BinaryView");
+ m_logger = CreateLogger("BinaryView.TEView");
+ m_backedByDatabase = bv->GetFile()->IsBackedByDatabase("TE");
+}
+
+void TEView::HandleUserOverrides()
+{
+ auto settings = GetLoadSettings(GetTypeName());
+ if (!settings)
+ return;
+
+ if (settings->Contains("loader.imageBase"))
+ m_imageBase = settings->Get<uint64_t>("loader.imageBase", this);
+
+ if (settings->Contains("loader.architecture"))
+ m_arch = Architecture::GetByName(settings->Get<string>("loader.architecture", this));
+}
+
+bool TEView::Init()
+{
+ BinaryReader reader(GetParentView(), LittleEndian);
+ struct TEImageHeader header;
+ Ref<Platform> platform;
+
+ try
+ {
+ // Read image header and section headers
+ ReadTEImageHeader(reader, header);
+ ReadTEImageSectionHeaders(reader, header.numberOfSections);
+
+ // Set architecture and platform
+ HandleUserOverrides();
+ if (m_arch)
+ {
+ auto archName = m_arch->GetName();
+ if (archName == "x86")
+ platform = Platform::GetByName("efi-x86");
+ if (archName == "x86_64")
+ platform = Platform::GetByName("efi-x86_64");
+ if (archName == "aarch64")
+ platform = Platform::GetByName("efi-aarch64");
+ if (!platform)
+ {
+ m_logger->LogError("TE architecture '%s' is not supported", archName.c_str());
+ return false;
+ }
+ }
+ else
+ {
+ // Adjusted image base after objcopy and applying new TE header
+ m_imageBase = header.imageBase + header.strippedSize - EFI_TE_IMAGE_HEADER_SIZE;
+ m_logger->LogDebug("TE adjusted image base: %08x\n", m_imageBase);
+ switch (header.machine)
+ {
+ case IMAGE_FILE_MACHINE_I386:
+ platform = Platform::GetByName("efi-x86");
+ break;
+ case IMAGE_FILE_MACHINE_AMD64:
+ platform = Platform::GetByName("efi-x86_64");
+ break;
+ case IMAGE_FILE_MACHINE_ARM64:
+ platform = Platform::GetByName("efi-aarch64");
+ break;
+ default:
+ LogError("TE architecture '0x%x' is not supported", header.machine);
+ return false;
+ }
+
+ if (!platform) {
+ // Should never occur as long as the platforms exist
+ m_logger->LogError("Failed to set platform for TE file");
+ return false;
+ }
+
+ m_arch = platform->GetArchitecture();
+ }
+
+ SetDefaultPlatform(platform);
+ SetDefaultArchitecture(m_arch);
+
+ // Create a segment for the header so that it can be viewed and create sections
+ uint64_t headerSegmentSize = reader.GetOffset();
+ AddAutoSegment(m_imageBase, headerSegmentSize, 0, headerSegmentSize, SegmentReadable);
+ CreateSections();
+ AssignHeaderTypes();
+
+ // Finished for parse only mode
+ if (m_parseOnly)
+ return true;
+
+ m_entryPoint = m_imageBase + header.addressOfEntrypoint;
+ DefineAutoSymbol(new Symbol(FunctionSymbol, "_start", m_entryPoint));
+ AddEntryPointForAnalysis(platform, m_entryPoint);
+ }
+ catch (std::exception& e)
+ {
+ m_logger->LogError("Failed to parse TE headers: %s\n", e.what());
+ return false;
+ }
+
+ return true;
+}
+
+uint64_t TEView::PerformGetEntryPoint() const
+{
+ return m_imageBase + m_entryPoint;
+}
+
+size_t TEView::PerformGetAddressSize() const
+{
+ return m_arch->GetAddressSize();
+}
+
+TEViewType::TEViewType() : BinaryViewType("TE", "TE")
+{
+ m_logger = LogRegistry::CreateLogger("BinaryView");
+}
+
+Ref<BinaryView> TEViewType::Create(BinaryView* bv)
+{
+ try
+ {
+ return new TEView(bv);
+ }
+ catch (std::exception& e)
+ {
+ m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what());
+ return nullptr;
+ }
+}
+
+Ref<BinaryView> TEViewType::Parse(BinaryView* bv)
+{
+ try
+ {
+ return new TEView(bv, true);
+ }
+ catch (std::exception& e)
+ {
+ m_logger->LogError("%s<BinaryViewType> failed to create view! '%s'", GetName().c_str(), e.what());
+ return nullptr;
+ }
+}
+
+bool TEViewType::IsTypeValidForData(BinaryView* bv)
+{
+ // Check the VZ signature
+ DataBuffer sig = bv->ReadBuffer(0, 2);
+ if (sig.GetLength() != 2)
+ return false;
+ if (memcmp(sig.GetData(), "VZ", 2))
+ return false;
+
+ // Check section header names for .text section
+ BinaryReader reader(bv, LittleEndian);
+ reader.Seek(0x4);
+ uint8_t numSections;
+ if (!reader.TryRead8(numSections))
+ return false;
+
+ for (uint8_t i = 0; i < numSections; i++)
+ {
+ reader.Seek(EFI_TE_IMAGE_HEADER_SIZE + (i * EFI_TE_SECTION_HEADER_SIZE));
+ uint64_t name;
+ if (!reader.TryRead64(name))
+ return false;
+
+ if (name == 0x747865742e) // .text
+ return true;
+ }
+
+ return false;
+}
+
+Ref<Settings> TEViewType::GetLoadSettingsForData(BinaryView *bv)
+{
+ Ref<BinaryView> viewRef = Parse(bv);
+ if (!viewRef || !viewRef->Init())
+ {
+ m_logger->LogError("View type '%s' could not be created", GetName().c_str());
+ return nullptr;
+ }
+
+ // specify default load settings that can be overridden
+ Ref<Settings> settings = GetDefaultLoadSettingsForData(viewRef);
+ vector<string> overrides = {"loader.architecture", "loader.imageBase"};
+ for (const auto& override : overrides)
+ {
+ if (settings->Contains(override))
+ settings->UpdateProperty(override, "readOnly", false);
+ }
+
+ return settings;
+}
diff --git a/view/pe/teview.h b/view/pe/teview.h
new file mode 100644
index 00000000..230539b5
--- /dev/null
+++ b/view/pe/teview.h
@@ -0,0 +1,97 @@
+#pragma once
+
+#include "binaryninjaapi.h"
+#include "peview.h"
+
+#ifdef WIN32
+#pragma warning(disable: 4005)
+#endif
+
+
+// EFI_IMAGE_DATA_DIRECTORY
+struct TEImageDataDirectory {
+ uint32_t virtualAddress;
+ uint32_t size;
+};
+
+#define EFI_TE_IMAGE_HEADER_SIZE 40
+#define EFI_TE_SECTION_HEADER_SIZE 40
+
+// EFI section header characteristics bit masks
+#define EFI_IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define EFI_IMAGE_SCN_MEM_READ 0x40000000
+#define EFI_IMAGE_SCN_MEM_WRITE 0x80000000
+
+// EFI_TE_IMAGE_HEADER
+struct TEImageHeader {
+ uint16_t magic;
+ uint16_t machine;
+ uint8_t numberOfSections;
+ uint8_t subsystem;
+ uint16_t strippedSize;
+ uint32_t addressOfEntrypoint;
+ uint32_t baseOfCode;
+ uint64_t imageBase;
+ struct TEImageDataDirectory dataDirectory[2];
+};
+
+// EFI_IMAGE_SECTION_HEADER
+struct TEImageSectionHeader {
+ std::string name; // 8 bytes
+ uint32_t virtualSize;
+ uint32_t virtualAddress;
+ uint32_t sizeOfRawData;
+ uint32_t pointerToRawData;
+ uint32_t pointerToRelocations;
+ uint32_t pointerToLineNumbers;
+ uint16_t numberOfRelocations;
+ uint16_t numberOfLineNumbers;
+ uint32_t characteristics;
+};
+
+namespace BinaryNinja
+{
+ class TEView: public BinaryView
+ {
+ bool m_parseOnly;
+ std::vector<TEImageSectionHeader> m_sections;
+ bool m_relocatable = false;
+ Ref<Logger> m_logger;
+ bool m_backedByDatabase;
+ uint64_t m_imageBase;
+ Ref<Architecture> m_arch;
+ uint64_t m_entryPoint;
+
+ protected:
+ virtual uint64_t PerformGetEntryPoint() const override;
+ virtual bool PerformIsExecutable() const override { return true; }
+ virtual BNEndianness PerformGetDefaultEndianness() const override { return LittleEndian; }
+ virtual bool PerformIsRelocatable() const override { return m_relocatable; }
+ virtual size_t PerformGetAddressSize() const override;
+
+ public:
+ TEView(BinaryView* data, bool parseOnly = false);
+ virtual bool Init() override;
+
+ private:
+ void ReadTEImageHeader(BinaryReader& reader, struct TEImageHeader& imageHeader);
+ void ReadTEImageSectionHeaders(BinaryReader& reader, uint32_t numSections);
+ void HandleUserOverrides();
+ void CreateSections();
+ void AssignHeaderTypes();
+ };
+
+ class TEViewType: public BinaryViewType
+ {
+ Ref<Logger> m_logger;
+
+ public:
+ TEViewType();
+ 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 InitTEViewType();
+}