diff --git a/platform/efi/efi_resolver/CMakeLists.txt b/platform/efi/efi_resolver/CMakeLists.txt new file mode 100644 index 000000000..03fabbf66 --- /dev/null +++ b/platform/efi/efi_resolver/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.22 FATAL_ERROR) + +project(efi_resolver) + +option(DEBUG "DEBUG Mode" ON) + +if((NOT BN_API_PATH) AND (NOT BN_INTERNAL_BUILD)) + set(BN_API_PATH $ENV{BN_API_PATH}) + if(NOT BN_API_PATH) + message(FATAL_ERROR "Provide path to Binary Ninja API source in BN_API_PATH") + endif() +endif() +if(NOT BN_INTERNAL_BUILD) + set(HEADLESS ON CACHE BOOL "") + add_subdirectory(${BN_API_PATH} ${PROJECT_BINARY_DIR}/api) +endif() + +# Binary Ninja plugin ---------------------------------------------------------- + +file( + GLOB_RECURSE SOURCE_FILES + CONFIGURE_DEPENDS # Automatically reconfigure if source files are added/removed. + ${PROJECT_SOURCE_DIR}/src/*.cpp ${PROJECT_SOURCE_DIR}/include/*.h +) + +add_library(efi_resolver SHARED ${SOURCE_FILES}) +target_link_libraries(efi_resolver binaryninjaapi) +target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_compile_features(efi_resolver PRIVATE cxx_std_17 c_std_99) + +# Library targets linking against the Binary Ninja API need to be compiled with +# position-independent code on Linux. +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + target_compile_options(efi_resolver PRIVATE "-fPIC") +endif() + +# Configure plugin output directory for internal builds, otherwise configure +# plugin installation for public builds. + +if(BN_INTERNAL_BUILD) + set_target_properties(efi_resolver PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR} + RUNTIME_OUTPUT_DIRECTORY ${BN_CORE_PLUGIN_DIR}) +else() + bn_install_plugin(${PROJECT_NAME}) +endif() + diff --git a/platform/efi/efi_resolver/LICENSE b/platform/efi/efi_resolver/LICENSE new file mode 100644 index 000000000..5cca49655 --- /dev/null +++ b/platform/efi/efi_resolver/LICENSE @@ -0,0 +1,13 @@ +Copyright 2023-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. \ No newline at end of file diff --git a/platform/efi/efi_resolver/README.md b/platform/efi/efi_resolver/README.md new file mode 100644 index 000000000..93ba468bb --- /dev/null +++ b/platform/efi/efi_resolver/README.md @@ -0,0 +1,128 @@ +# EFI Resolver +Author: **Vector 35 Inc** + +_A Binary Ninja built-in plugin that automatically resolves type information for EFI protocol usage._ + +This repository contains C++ version of EFI Resolver, which is bundled with Binary Ninja. For the original Python +version, please refer to https://github.com/vector35/efi-resolver/tree/main + +## Description: + +EFI Resolver is a Binary Ninja plugin that automates the task of resolving EFI protocol type information. It supports both DXE files and PEI files. It propagates parameter pointers from entry points to system table, MM system table, boot services, and runtime services to any global variables where they are stored. For PEI files, it also supports identifying [processor-specific mechanisms](https://uefi.org/specs/PI/1.8/V1_PEI_Foundation.html#pei-services-table-retrieval) for retrieving PEI services pointers. The plugin also identifies references to the boot services, MM protocol functions and PEI services, and applies type information according to the GUID passed to these functions. The plugin supports the core UEFI specification, and allows users to provide custom vendor protocols. + +## Build Instructions + +```bash +git clone https://github.com/Vector35/binaryninja-api.git +git clone https://github.com/Vector35/efi-resolver.git && cd efi-resolver +export BN_API_PATH=../binaryninja-api # Or specifying the path to api repo +cmake -S . -B build -GNinja +cmake --build build -t install +``` + +## License + +This plugin is released under an Apache-2.0 license. + +## Supplying Custom UEFI Protocol GUIDs and Types + +By default, EFI Resolver propagates types and GUIDs using Binary Ninja's native platform types for EFI. Many UEFI +firmware binaries include types (and GUIDs) for proprietary protocols. This section describes how users can supply +custom UEFI types and GUIDs for use with EFI Resolver type propagation. + +### User-supplied EFI GUIDs + +EFI Resolver uses a JSON file to associate user-supplied EFI GUIDs with types for propagation. GUIDs for proprietary +protocol types can be used with EFI Resolver by creating a file at `/types/efi-guids.json` containing JSON +entries in the following format: + +```json +{ + "EFI_EXAMPLE_CUSTOM_PROTOCOL_GUID": [ + 19088743, + 35243, + 52719, + 1, + 35, + 69, + 103, + 137, + 171, + 205, + 239 + ] +} +``` + +In this example, the protocol type of `EFI_EXAMPLE_CUSTOM_PROTOCOL` is mapped to the +`{0x01234567,0x89ab,0xcdef,{0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef}}` GUID (named `EFI_EXAMPLE_CUSTOM_PROTOCOL_GUID`). +To test that the file is a valid JSON file, run `python -m json.tool < efi-guids.json`. + +__Note: user-supplied proprietary GUIDs from `efi-guids.json` are used to name variables regardless of whether or not an associated platform type has been loaded. If EFI Resolver fails to query the type for an EFI protocol interface, it will set the variable type for the protocol interface pointer to `VOID*`.__ + +### User-supplied EFI Platform Types + +Types and structures for proprietary protocols are to be imported using Binary Ninja's standard mechanism for loading +user-supplied platform types. Instructions on adding custom platform types can be found [here](https://docs.binary.ninja/guide/types/platformtypes.html). Available EFI platform names include: +- `efi-x86` +- `efi-x86_64` +- `efi-thumb2` +- `efi-armv7` +- `efi-aarch64` +- `efi-windows-aarch64` +- `efi-windows-x86` +- `efi-windows-x86_64` + +To avoid having to add duplicate types in each platform-specific `*.c` file, it is recommended to add common types +to a top-level `efi.c` file and `#include` the file in the platform-specific `*.c` files. For example: + +```C +// /types/platform/efi-x86_64.c including /types/efi.c +#include "../efi.c" +``` + +To test that C source files containing custom EFI platform types are in the correct format, use the `bv.platform.parse_types_from_source_file` API. + +Alternatively, user types can be supplied manually from type libraries, header files, or any other mechanism supported +by Binary Ninja. Just ensure that the name for types associated with GUIDs match what is in `efi-guids.json`. Protocol +GUID names in `efi-guids.json` should end with `_PROTOCOL_GUID` and the prefix must be identical to the associated +protocol type name. For example, if the GUID is named `EFI_EXAMPLE_PROTOCOL_GUID`, EFI Resolver will attempt to +look up a type named `EFI_EXAMPLE_PROTOCOL`. + +### Full Example + +In summary, including a custom platform type of `EFI_EXAMPLE_CUSTOM_PROTOCOL` for the `efi-x86` platform and associating +it with a GUID named `EFI_EXAMPLE_CUSTOM_PROTOCOL_GUID` requires two steps: + +1. Create the `/types/platform/efi-x86.c` header file: + +```C +struct EFI_EXAMPLE_CUSTOM_PROTOCOL +{ + uint32_t length; +} +``` + +2. Create the `/types/efi-guids.json` file: + +```json +{ + "EFI_EXAMPLE_CUSTOM_PROTOCOL_GUID": [ + 19088743, + 35243, + 52719, + 1, + 35, + 69, + 103, + 137, + 171, + 205, + 239 + ] +} +``` + +After a Binary Ninja restart, when a binary is loaded with the `efi-x86` platform, the `EFI_EXAMPLE_CUSTOM_PROTOCOL` +type will be imported. When EFI Resolver runs, it will detect uses of `EFI_EXAMPLE_CUSTOM_PROTOCOL_GUID` and propagate +the `EFI_EXAMPLE_CUSTOM_PROTOCOL` type. \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/DxeResolver.h b/platform/efi/efi_resolver/include/DxeResolver.h new file mode 100644 index 000000000..eef6e6c24 --- /dev/null +++ b/platform/efi/efi_resolver/include/DxeResolver.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Resolver.h" + +class DxeResolver : Resolver +{ + bool resolveBootServices(); + bool resolveRuntimeServices(); + + bool resolveSmmTables(string serviceName, string tableName); + bool resolveSmmServices(); + bool resolveSmiHandlers(); + +public: + /*! + resolve BootServices and RuntimeServices, define protocol types that loaded by BootServices + */ + bool resolveDxe(); + + /*! + Define MMST/SMMST and resolve SMM related protocols + */ + bool resolveSmm(); + + DxeResolver(Ref view, Ref task); +}; \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/GuidRenderer.h b/platform/efi/efi_resolver/include/GuidRenderer.h new file mode 100644 index 000000000..e30aa8809 --- /dev/null +++ b/platform/efi/efi_resolver/include/GuidRenderer.h @@ -0,0 +1,20 @@ +#pragma once + +#include "binaryninjaapi.h" +#include + +using namespace BinaryNinja; +using namespace std; + +class EfiGuidRenderer : public BinaryNinja::DataRenderer +{ + EfiGuidRenderer() = default; + +public: + bool IsValidForData(BinaryView*, uint64_t address, Type*, vector>&) override; + + vector GetLinesForData(BinaryView*, uint64_t address, Type*, + const vector& prefix, size_t width, vector>&) override; + + static void Register(); +}; \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/ModuleType.h b/platform/efi/efi_resolver/include/ModuleType.h new file mode 100644 index 000000000..187dcc36f --- /dev/null +++ b/platform/efi/efi_resolver/include/ModuleType.h @@ -0,0 +1,23 @@ +#pragma once + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; + +enum EFIModuleType +{ + UNKNOWN, + PEI, + DXE, +}; + +static inline EFIModuleType identifyModuleType(BinaryView* bv) +{ + std::string viewType = bv->GetCurrentView(); + if (viewType == "Linear:PE") + return DXE; + else if (viewType == "Linear:TE") + return PEI; + else + return UNKNOWN; +} \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/PeiResolver.h b/platform/efi/efi_resolver/include/PeiResolver.h new file mode 100644 index 000000000..2ecf8ace1 --- /dev/null +++ b/platform/efi/efi_resolver/include/PeiResolver.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Resolver.h" + +class PeiResolver : Resolver +{ + bool resolvePeiIdt(); + bool resolvePeiMrc(); + bool resolvePeiMrs(); + bool resolvePlatformPointers(); + bool resolvePeiDescriptors(); + bool resolvePeiServices(); + +public: + /*! + resolve Pei related types and PPIs, this function will also resolve processor-specific pointers + and tried to define the EFI_PEI_DESCRIPTORS + */ + bool resolvePei(); + PeiResolver(Ref view, Ref task); +}; \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/Resolver.h b/platform/efi/efi_resolver/include/Resolver.h new file mode 100644 index 000000000..91bb18622 --- /dev/null +++ b/platform/efi/efi_resolver/include/Resolver.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include + +#include "GuidRenderer.h" +#include "ModuleType.h" +#include "TypePropagation.h" +#include "binaryninjaapi.h" +#include "highlevelilinstruction.h" +#include "lowlevelilinstruction.h" +#include "mediumlevelilinstruction.h" + +using namespace BinaryNinja; +using namespace std; + +typedef array EFI_GUID; + +class Resolver +{ +protected: + Ref m_view; + Ref m_task; + size_t m_width; + map> m_protocol; + map m_user_guids; + + vector> m_service_usages; + vector> m_protocol_usages; + vector> m_guid_usages; + vector> m_variable_usages; + + bool parseUserGuidIfExists(const string& filePath); + bool parseProtocolMapping(const string& filePath); + + /*! + For backward compatibility, if a user saved a bndb with older version Binary Ninja + this function will try to retrieve types from Platform Types if it doesn't find one + in BinaryView + */ + Ref GetTypeFromViewAndPlatform(string type_name); + void initProtocolMapping(); + +public: + bool setModuleEntry(EFIModuleType fileType); + bool resolveGuidInterface(Ref func, uint64_t addr, int guid_pos, int interface_pos); + Resolver(Ref view, Ref task); + + pair lookupGuid(EFI_GUID guidBytes); + pair defineAndLookupGuid(uint64_t addr); + + string nonConflictingName(const string& basename); + static string nonConflictingLocalName(Ref func, const string& basename); + + /*! + Define the structure used at the callsite with type `typeName`, propagate it to the data section. If it's a + structure type, define it fields according to the `followFields` parameter. The input `addr` should be a call + instruction \param func the function that contains the callsite (it's parent function) \param addr address of the + callsite \param typeName the type that need to define \param paramIdx the parameter index that want to define \param + followFields whether to define the structure's fields if they are pointers \return False if failed + + \b Example: + \code{.cpp} + refs = bv->GetCodeReferencesForType(QualifiedName("EFI_GET_VARIABLE")); + for (auto ref : refs) + { + // ... some checking, need to make sure is a call instruction + bool ok = defineTypeAtCallsite(ref.func, ref.addr, "EFI_GUID", 2, false); + } + \endcode + */ + bool defineTypeAtCallsite( + Ref func, uint64_t addr, string typeName, int paramIdx, bool followFields = false); + vector HighLevelILExprsAt(Ref func, Ref arch, uint64_t addr); +}; \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/TypePropagation.h b/platform/efi/efi_resolver/include/TypePropagation.h new file mode 100644 index 000000000..1985e2ab7 --- /dev/null +++ b/platform/efi/efi_resolver/include/TypePropagation.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Utils.h" +#include "binaryninjaapi.h" + +using namespace BinaryNinja; + +class TypePropagation +{ + Ref m_view; + std::deque m_queue; + Ref m_platform; + +public: + TypePropagation(BinaryView* view); + bool propagateFuncParamTypes(Function* func); + bool propagateFuncParamTypes(Function* func, SSAVariable ssa_var); +}; \ No newline at end of file diff --git a/platform/efi/efi_resolver/include/Utils.h b/platform/efi/efi_resolver/include/Utils.h new file mode 100644 index 000000000..981908b28 --- /dev/null +++ b/platform/efi/efi_resolver/include/Utils.h @@ -0,0 +1,40 @@ +#pragma once + +#include "binaryninjaapi.h" + +using namespace BinaryNinja; + +static inline std::string GetOriginalTypeName(Ref type) +{ + std::string result; + if (type->IsPointer()) + { + if (type->GetChildType().GetValue()->IsNamedTypeRefer()) + { + return type->GetChildType().GetValue()->GetNamedTypeReference()->GetName().GetString(); + } + return type->GetTypeName().GetString(); + } + if (type->IsNamedTypeRefer()) + return type->GetNamedTypeReference()->GetName().GetString(); + + return type->GetTypeName().GetString(); +} + +static inline std::string GetVarNameForTypeStr(const std::string typeStr) +{ + std::istringstream iss(typeStr); + std::string word; + std::string result; + + while (std::getline(iss, word, '_')) + { + if (!word.empty()) + { + word[0] = std::toupper(word[0]); + std::transform(word.begin() + 1, word.end(), word.begin() + 1, ::tolower); + result += word; + } + } + return result; +} diff --git a/platform/efi/efi_resolver/src/DxeResolver.cpp b/platform/efi/efi_resolver/src/DxeResolver.cpp new file mode 100644 index 000000000..f31c42ae0 --- /dev/null +++ b/platform/efi/efi_resolver/src/DxeResolver.cpp @@ -0,0 +1,274 @@ +#include "DxeResolver.h" + +bool DxeResolver::resolveBootServices() +{ + m_task->SetProgressText("Resolving Boot Services..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_BOOT_SERVICES")); + // search reference of `EFI_BOOT_SERVICES` so that we can easily parse different services + + for (auto& ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation == MLIL_CALL_SSA || instr.operation == MLIL_TAILCALL_SSA) + { + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_LOAD_STRUCT_SSA) + continue; + auto offset = dest.GetOffset(); + + if (offset == 0x18 + m_width * 16 || offset == 0x18 + m_width * 32) + { + // HandleProtocol, OpenProtocol + // Guid:1, Interface:2 + resolveGuidInterface(ref.func, ref.addr, 1, 2); + } + else if (offset == 0x18 + m_width * 37) + { + // LocateProtocol + resolveGuidInterface(ref.func, ref.addr, 0, 2); + } + } + } + return true; +} + +bool DxeResolver::resolveRuntimeServices() +{ + m_task->SetProgressText("Resolving Runtime Services..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_RUNTIME_SERVICES")); + + for (auto& ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation == MLIL_CALL_SSA || instr.operation == MLIL_TAILCALL_SSA) + { + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_LOAD_STRUCT_SSA) + continue; + auto offset = dest.GetOffset(); + if (offset == 0x18 + m_width * 6 || offset == 0x18 + m_width * 8) + { + // TODO implement this + // GetVariable and SetVariable + } + } + } + return true; +} + +bool DxeResolver::resolveSmmTables(string serviceName, string tableName) +{ + m_task->SetProgressText("Defining MM tables..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName(serviceName)); + // both versions use the same type, so we only need to search for this one + for (auto& ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation != MLIL_CALL_SSA && instr.operation != MLIL_TAILCALL_SSA) + continue; + + auto destExpr = instr.GetDestExpr(); + if (destExpr.operation != MLIL_LOAD_STRUCT_SSA) + continue; + + if (destExpr.GetOffset() != 8) + continue; + + auto params = instr.GetParameterExprs(); + if (params.size() < 2) + continue; + + auto smstAddr = params[1]; + if (smstAddr.operation != MLIL_CONST_PTR) + continue; + + QualifiedNameAndType result; + string errors; + bool ok = m_view->ParseTypeString(tableName, result, errors); + if (!ok) + return false; + m_view->DefineDataVariable(smstAddr.GetValue().value, result.type); + m_view->DefineUserSymbol(new Symbol(DataSymbol, "gMmst", smstAddr.GetValue().value)); + m_view->UpdateAnalysisAndWait(); + } + return true; +} + +bool DxeResolver::resolveSmmServices() +{ + m_task->SetProgressText("Resolving MM services..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_MM_SYSTEM_TABLE")); + auto refs_smm = m_view->GetCodeReferencesForType(QualifiedName("EFI_SMM_SYSTEM_TABLE2")); + // These tables have same type information, we can just iterate once + refs.insert(refs.end(), refs_smm.begin(), refs_smm.end()); + + for (auto& ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation == MLIL_CALL_SSA || instr.operation == MLIL_TAILCALL_SSA) + { + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_LOAD_STRUCT_SSA) + continue; + auto offset = dest.GetOffset(); + + if (offset == 0x18 + m_width * 0x14) + { + // SmmHandleProtocol + resolveGuidInterface(ref.func, ref.addr, 1, 2); + } + else if (offset == 0x18 + m_width * 0x17) + { + // SmmLocateProtocol + resolveGuidInterface(ref.func, ref.addr, 0, 2); + } + } + } + return true; +} + +bool DxeResolver::resolveSmiHandlers() +{ + m_task->SetProgressText("Resolving SMI Handlers..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_MM_SW_REGISTER")); + auto refs_smm_sw = m_view->GetCodeReferencesForType(QualifiedName("EFI_SMM_SW_REGISTER2")); + auto refs_mm_sx = m_view->GetCodeReferencesForType(QualifiedName("EFI_MM_SX_REGISTER")); + auto refs_smm_sx = m_view->GetCodeReferencesForType(QualifiedName("EFI_SMM_SX_REGISTER2")); + // Define them together + + refs.insert(refs.end(), refs_smm_sw.begin(), refs_smm_sw.end()); + refs.insert(refs.end(), refs_smm_sx.begin(), refs_smm_sw.end()); + refs.insert(refs.end(), refs_mm_sx.begin(), refs_mm_sx.end()); + + for (auto& ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation == MLIL_CALL_SSA || instr.operation == MLIL_TAILCALL_SSA) + { + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_LOAD_STRUCT_SSA) + continue; + + auto offset = dest.GetOffset(); + if (offset == 0) + { + auto parameters = instr.GetParameterExprs(); + if (parameters.size() < 4) + continue; + + // TODO we should be able to parse registerContext, but it's normally an aliased variable + // and we have some issues relate to that + auto dispatchFunction = parameters[1]; + if (dispatchFunction.operation != MLIL_CONST_PTR) + continue; + auto funcAddr = static_cast(dispatchFunction.GetConstant()); + auto targetFunc = m_view->GetAnalysisFunction(m_view->GetDefaultPlatform(), funcAddr); + auto funcType = targetFunc->GetType(); + std::ostringstream ss; + ss << "SmiHandler_" << std::hex << funcAddr; + string funcName = ss.str(); + + // typedef enum + string handleTypeStr = + "EFI_STATUS SmiHandler(EFI_HANDLE DispatchHandle, VOID* Context, VOID* CommBuffer, UINTN* " + "CommBufferSize);"; + QualifiedNameAndType result; + string errors; + bool ok = m_view->ParseTypeString(handleTypeStr, result, errors); + if (!ok) + return false; + targetFunc->SetUserType(result.type); + m_view->DefineUserSymbol(new Symbol(FunctionSymbol, funcName, funcAddr)); + m_view->UpdateAnalysisAndWait(); + + // After setting the type, we want to propagate the parameters' type + TypePropagation propagator(m_view); + propagator.propagateFuncParamTypes(targetFunc); + } + } + } + return true; +} + +bool DxeResolver::resolveDxe() +{ + if (!resolveBootServices()) + return false; + if (!resolveRuntimeServices()) + return false; + return true; +} + +bool DxeResolver::resolveSmm() +{ + if (!resolveSmmTables("EFI_SMM_GET_SMST_LOCATION2", "EFI_SMM_SYSTEM_TABLE2*")) + return false; + if (!resolveSmmTables("EFI_MM_GET_MMST_LOCATION", "EFI_MM_SYSTEM_TABLE*")) + return false; + if (!resolveSmmServices()) + return false; + if (!resolveSmiHandlers()) + return false; + return true; +} + +DxeResolver::DxeResolver(Ref view, Ref task) : Resolver(view, task) +{ + initProtocolMapping(); + setModuleEntry(DXE); +} diff --git a/platform/efi/efi_resolver/src/GuidRenderer.cpp b/platform/efi/efi_resolver/src/GuidRenderer.cpp new file mode 100644 index 000000000..deac0265a --- /dev/null +++ b/platform/efi/efi_resolver/src/GuidRenderer.cpp @@ -0,0 +1,51 @@ +#include "GuidRenderer.h" + +bool isType(const vector>& context, const string& name) +{ + if (context.empty()) + return false; + + auto [deepestType, size] = context.back(); + if (!deepestType->IsNamedTypeRefer()) + return false; + + return deepestType->GetTypeName().GetString() == name; +} + +bool EfiGuidRenderer::IsValidForData(BinaryView* bv, uint64_t address, Type* type, vector>& context) +{ + return isType(context, "EFI_GUID"); +} + +static string formatGuid(uint32_t data1, uint16_t data2, uint16_t data3, uint64_t data4) +{ + std::ostringstream oss; + oss << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << data1 << "-" << std::setw(4) << data2 + << "-" << std::setw(4) << data3 << "-" << std::setw(16) << data4; + return oss.str(); +} + +vector EfiGuidRenderer::GetLinesForData(BinaryView* bv, uint64_t address, Type*, + const vector& prefix, size_t, vector>& context) +{ + BinaryReader reader(bv); + reader.Seek(address); + auto data1 = reader.Read32(); + auto data2 = reader.Read16(); + auto data3 = reader.Read16(); + auto data4 = reader.ReadBE64(); + string guidStr = formatGuid(data1, data2, data3, data4); + + DisassemblyTextLine line; + line.addr = address; + line.tokens = prefix; + line.tokens.emplace_back(TextToken, "[EFI_GUID(\""); + line.tokens.emplace_back(StringToken, guidStr); + line.tokens.emplace_back(TextToken, "\")]"); + return {line}; +} + +void EfiGuidRenderer::Register() +{ + DataRendererContainer::RegisterTypeSpecificDataRenderer(new EfiGuidRenderer()); +} diff --git a/platform/efi/efi_resolver/src/PeiResolver.cpp b/platform/efi/efi_resolver/src/PeiResolver.cpp new file mode 100644 index 000000000..c3f5d50a8 --- /dev/null +++ b/platform/efi/efi_resolver/src/PeiResolver.cpp @@ -0,0 +1,304 @@ +#include "PeiResolver.h" + +bool PeiResolver::resolvePeiIdt() +{ + string archName = m_view->GetDefaultArchitecture()->GetName(); + string intrinsicName; + if (archName == "x86") + intrinsicName = "IDTR32"; + else + intrinsicName = "IDTR64"; + + auto refs = m_view->GetCodeReferencesForType(QualifiedName(intrinsicName)); + for (auto ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto mlil = ref.func->GetMediumLevelIL(); + auto instrIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlil->GetInstruction(instrIdx); + + auto hlil = ref.func->GetHighLevelIL(); + auto hlils = HighLevelILExprsAt(ref.func, m_view->GetDefaultArchitecture(), ref.addr); + + for (auto expr : hlils) + { + if (expr.operation != HLIL_INTRINSIC || expr.GetParent().operation != HLIL_ASSIGN + || expr.GetParent().GetDestExpr().operation != HLIL_STRUCT_FIELD + || expr.GetParent().GetDestExpr().GetSourceExpr().operation != HLIL_VAR) + continue; + + auto var = expr.GetParent().GetDestExpr().GetSourceExpr().GetVariable(); + ref.func->CreateUserVariable(var, m_view->GetTypeByName(QualifiedName(intrinsicName)), intrinsicName); + } + + if (instr.operation == MLIL_INTRINSIC) + { + // binja doesn't do type propagation on intrinsic instructions + auto output_params = instr.GetOutputVariables(); + if (output_params.size() < 1) + continue; + ref.func->CreateUserVariable( + output_params[0], m_view->GetTypeByName(QualifiedName(intrinsicName)), intrinsicName); + } + m_view->UpdateAnalysisAndWait(); + } + + // TODO There is an issue related to structure's type propagation, binja doesn't propagate indirect structure access + // properly + // here is a temporary fix, should be removed after vector35/binaryninja/#749 got fixed + refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_PEI_SERVICES")); + for (auto ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto mlil = ref.func->GetMediumLevelIL(); + auto instrIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlil->GetInstruction(instrIdx); + + if (instr.operation != MLIL_SET_VAR) + continue; + + if (instr.GetSourceExpr().operation != MLIL_LOAD_STRUCT) + continue; + + ref.func->CreateUserVariable(instr.GetDestVariable(), + mlil->GetExprType(instr.GetSourceExpr()).GetValue(), + nonConflictingLocalName(ref.func, "EfiPeiServices")); + m_view->UpdateAnalysisAndWait(); + } + + return true; +} + +bool PeiResolver::resolvePeiMrc() +{ + auto funcs = m_view->GetAnalysisFunctionList(); + for (auto func : funcs) + { + if (m_task->IsCancelled()) + return false; + + auto mlil = func->GetMediumLevelIL(); + auto blocks = mlil->GetBasicBlocks(); + for (auto block : blocks) + { + for (size_t i = block->GetStart(); i < block->GetEnd(); i++) + { + auto instr = mlil->GetInstruction(i); + if (instr.operation != MLIL_INTRINSIC) + continue; + uint32_t intrinsicIdx = instr.GetIntrinsic(); + + if (m_view->GetDefaultArchitecture()->GetIntrinsicName(intrinsicIdx) != "Coproc_GetOneWord") + continue; + auto intrinsicParams = instr.GetParameterExprs(); + if (intrinsicParams.size() != 5) + continue; + + bool found = true; + + const int value[5] = {0xf, 0x0, 0xd, 0x0, 0x2}; + for (int j = 0; j < 5; j++) + { + auto param = intrinsicParams[j]; + if (param.operation != MLIL_CONST) + { + found = false; + break; + } + + if (param.GetConstant() != value[j]) + { + found = false; + break; + } + } + + if (!found) + continue; + + // At this point, we can make sure this instruction fetches EFI_PEI_SERVICES + auto output = instr.GetOutputVariables(); + if (output.size() > 0) + { + auto pointerType = Type::PointerType(m_view->GetDefaultArchitecture(), + Type::PointerType(m_view->GetDefaultArchitecture(), + m_view->GetTypeByName(QualifiedName("EFI_PEI_SERVICES")))); + func->CreateUserVariable(output[0], pointerType, nonConflictingLocalName(func, "PeiServices")); + m_view->UpdateAnalysisAndWait(); + } + } + } + } + return true; +} + +bool PeiResolver::resolvePeiMrs() +{ + // ideally we don't need this function, but since we don't support type propagation on intrinsic instructions + // we have to manually propagate it + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_PEI_SERVICES")); + for (auto ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto mlil = ref.func->GetMediumLevelIL(); + auto instrIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlil->GetInstruction(instrIdx); + if (instr.operation == MLIL_INTRINSIC) + { + auto params = instr.GetOutputVariables(); + if (params.size() < 1) + continue; + + auto pointerType = Type::PointerType(m_view->GetDefaultArchitecture(), + Type::PointerType( + m_view->GetDefaultArchitecture(), m_view->GetTypeByName(QualifiedName("EFI_PEI_SERVICES")))); + ref.func->CreateUserVariable(params[0], pointerType, nonConflictingLocalName(ref.func, "EfiPeiServices")); + m_view->UpdateAnalysisAndWait(); + } + } + return true; +} + +bool PeiResolver::resolvePlatformPointers() +{ + m_task->SetProgressText("Resolving PEI Services Pointers..."); + string archName = m_view->GetDefaultArchitecture()->GetName(); + string intrinsicTypeName; + + if (archName == "x86" || archName == "x86-64") + { + return resolvePeiIdt(); + } + else if (archName == "arm" || archName == "thumb2") + { + return resolvePeiMrc(); + } + else if (archName == "aarch64") + { + return resolvePeiMrs(); + } + LogError("Not supported arch: %s", archName.c_str()); + return false; +} + +bool PeiResolver::resolvePeiDescriptors() +{ + m_task->SetProgressText("Defining PEI Descriptors..."); + const string descriptorNames[2] = {"EFI_PEI_NOTIFY_DESCRIPTOR", "EFI_PEI_PPI_DESCRIPTOR"}; + for (auto descriptor : descriptorNames) + { + auto refs = m_view->GetCodeReferencesForType(QualifiedName(descriptor)); + for (auto ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto mlil = ref.func->GetMediumLevelIL(); + auto instrIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlil->GetInstruction(instrIdx); + + if (instr.operation != MLIL_CALL && instr.operation != MLIL_TAILCALL) + continue; + + auto destExpr = instr.GetDestExpr(); + if (destExpr.operation != MLIL_LOAD_STRUCT) + continue; + + // at this point this instruction is probably a call to LocatPpi, InstallPpi or NotifyPpi + if (!mlil->GetExprType(destExpr).GetValue()->IsPointer()) + continue; + + auto funcType = mlil->GetExprType(destExpr).GetValue()->GetChildType().GetValue(); + auto params = funcType->GetParameters(); + int targetParamIdx = -1; + for (int i = 0; i < params.size(); i++) + { + auto param = params[i]; + if (!param.type.GetValue()->IsPointer()) + continue; + auto paramTypeName = param.type.GetValue()->GetChildType().GetValue()->GetTypeName().GetString(); + if (paramTypeName.find(descriptor) != paramTypeName.npos) + { + // this is the param + targetParamIdx = i; + break; + } + } + if (targetParamIdx < 0) + continue; + + // Now we are confident that this position is a call that pass Descriptor as a parameter + defineTypeAtCallsite(ref.func, ref.addr, descriptor, targetParamIdx, true); + } + } + return true; +} + +bool PeiResolver::resolvePeiServices() +{ + m_task->SetProgressText("Resolving PPIs..."); + auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_PEI_SERVICES")); + + for (auto ref : refs) + { + if (m_task->IsCancelled()) + return false; + + auto func = ref.func; + auto mlil = func->GetMediumLevelIL(); + if (!mlil) + continue; + + auto mlilSsa = mlil->GetSSAForm(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), ref.addr); + auto instr = mlilSsa->GetInstruction(mlil->GetSSAInstructionIndex(mlilIdx)); + + if (instr.operation == MLIL_CALL_SSA || instr.operation == MLIL_TAILCALL_SSA) + { + auto dest = instr.GetDestExpr(); + if (dest.operation != MLIL_LOAD_STRUCT_SSA) + continue; + auto offset = dest.GetOffset(); + + if (offset == 0x18 + m_width * 2) + { + // LocatePpi + resolveGuidInterface(ref.func, ref.addr, 1, 4); + } + else if (offset == 0x18 || offset == 0x18 + m_width || offset == 0x18 + m_width * 3) + { + // InstallPpi, ReinstallPpi, NotifyPpi + } + } + } + return true; +} + +bool PeiResolver::resolvePei() +{ + if (!setModuleEntry(PEI)) + return false; + + if (!resolvePlatformPointers()) + return false; + + if (!resolvePeiDescriptors()) + return false; + + if (!resolvePeiServices()) + return false; + + return true; +} + +PeiResolver::PeiResolver(Ref view, Ref task) : Resolver(view, task) +{ + initProtocolMapping(); + setModuleEntry(PEI); +} diff --git a/platform/efi/efi_resolver/src/Plugin.cpp b/platform/efi/efi_resolver/src/Plugin.cpp new file mode 100644 index 000000000..b562975c1 --- /dev/null +++ b/platform/efi/efi_resolver/src/Plugin.cpp @@ -0,0 +1,65 @@ +#include "DxeResolver.h" +#include "PeiResolver.h" +#include "binaryninjaapi.h" +#include + +using namespace BinaryNinja; + +extern "C" +{ +BN_DECLARE_CORE_ABI_VERSION + +BINARYNINJAPLUGIN void CorePluginDependencies() +{ + BinaryNinja::AddOptionalPluginDependency("arch_x86"); + BinaryNinja::AddOptionalPluginDependency("arch_armv7"); + BinaryNinja::AddOptionalPluginDependency("arch_arm64"); + BinaryNinja::AddOptionalPluginDependency("platform_efi"); +} + +static Ref efiBackgroundTask = nullptr; + +void Run(Ref view) +{ + efiBackgroundTask = new BackgroundTask("Loading EFI protocol mappings!", true); + thread resolverThread([view]() { + LogInfo("Entering new thread"); + + LogInfo("Identifying module type"); + EFIModuleType moduleType = identifyModuleType(view); + +#ifndef DEBUG + auto undo = view->BeginUndoActions(); +#endif + if (moduleType == PEI) + { + efiBackgroundTask->SetProgressText("Resolving PEIM..."); + auto resolver = PeiResolver(view, efiBackgroundTask); + resolver.resolvePei(); + } + else if (moduleType == DXE) + { + efiBackgroundTask->SetProgressText("Resolving DXE protocols..."); + auto resolver = DxeResolver(view, efiBackgroundTask); + resolver.resolveDxe(); + efiBackgroundTask->SetProgressText("Resolving MM related protocols..."); + resolver.resolveSmm(); + } + +#ifndef DEBUG + view->CommitUndoActions(undo); +#endif + efiBackgroundTask->Finish(); + }); + resolverThread.detach(); +} + +BINARYNINJAPLUGIN bool CorePluginInit() +{ + EfiGuidRenderer::Register(); + + PluginCommand::Register("EFI Resolver\\Resolve EFI Types And Protocols", "Resolve EFI Protocols", &Run); + + return true; +} +} diff --git a/platform/efi/efi_resolver/src/Resolver.cpp b/platform/efi/efi_resolver/src/Resolver.cpp new file mode 100644 index 000000000..36fa190b0 --- /dev/null +++ b/platform/efi/efi_resolver/src/Resolver.cpp @@ -0,0 +1,753 @@ +#include "Resolver.h" + +string Resolver::nonConflictingName(const string& basename) +{ + int idx = 0; + string name = basename; + do + { + auto sym = m_view->GetSymbolByRawName(name); + if (!sym) + return name; + else + { + name = basename + to_string(idx); + idx += 1; + } + } while (true); +} + +string Resolver::nonConflictingLocalName(Ref func, const string& basename) +{ + string name = basename; + int idx = 0; + while (true) + { + bool ok = true; + for (const auto& varPair : func->GetVariables()) + { + if (varPair.second.name == name) + { + ok = false; + break; + } + } + if (ok) + break; + name = basename + to_string(idx); + idx += 1; + } + return name; +} + +static string GetBundledEfiPath() +{ + string path = GetBundledPluginDirectory(); +#if defined(_WIN32) + return path + "..\\types\\efi.c"; +#elif defined(__APPLE__) + return path + "/../../Resources/types/efi.c"; +#else + return path + "../types/efi.c"; +#endif +} + +static string GetUserGuidPath() +{ + string path = GetUserDirectory(); +#if defined(_WIN32) + return path + "\\types\\efi-guids.json"; +#elif defined(__APPLE__) + return path + "/types/efi-guids.json"; +#else + return path + "/types/efi-guids.json"; +#endif +} + +static EFI_GUID parseGuid(const string& guidStr) +{ + EFI_GUID guid; + istringstream iss(guidStr); + string token; + unsigned long value; + + getline(iss, token, ','); + value = stoul(token, nullptr, 16); + guid[0] = static_cast(value); + guid[1] = static_cast(value >> 8); + guid[2] = static_cast(value >> 16); + guid[3] = static_cast(value >> 24); + + getline(iss, token, ','); + value = stoul(token, nullptr, 16); + guid[4] = static_cast(value); + guid[5] = static_cast(value >> 8); + + getline(iss, token, ','); + value = stoul(token, nullptr, 16); + guid[6] = static_cast(value); + guid[7] = static_cast(value >> 8); + + for (int i = 8; i < 16; i++) + { + getline(iss, token, ','); + value = stoul(token, nullptr, 16); + guid[i] = static_cast(value); + } + return guid; +} + +bool Resolver::parseProtocolMapping(const string& filePath) +{ + vector> guids; + ifstream efiDefs; + string line; + + m_protocol.clear(); + + efiDefs.open(filePath.c_str()); + if (!efiDefs.is_open()) + return false; + + while (getline(efiDefs, line)) + { + if (m_task->IsCancelled()) + return false; + + if (line.substr(0, 12) == "///@protocol") + { + string guid = line.substr(12); + guid.erase(remove_if(guid.begin(), guid.end(), [](char c) { return c == '{' || c == '}' || c == ' '; }), + guid.end()); + guids.emplace_back(parseGuid(guid), ""); + } + else if (line.substr(0, 11) == "///@binding") + { + istringstream iss(line.substr(11)); + string guidName, guid; + iss >> guidName >> guid; + guid.erase(remove_if(guid.begin(), guid.end(), [](char c) { return c == '{' || c == '}' || c == ' '; }), + guid.end()); + guids.emplace_back(parseGuid(guid), guidName); + } + else if (line.substr(0, 6) == "struct") + { + if (guids.empty()) + continue; + istringstream iss(line.substr(6)); + string name; + iss >> name; + for (const auto& guidInfo : guids) + { + if (guidInfo.second.empty()) + { + m_protocol[guidInfo.first] = make_pair(name, name + "_GUID"); + } + else + { + m_protocol[guidInfo.first] = make_pair(name, guidInfo.second); + } + } + } + else + { + guids.clear(); + } + } + efiDefs.close(); + + return true; +} + +bool Resolver::parseUserGuidIfExists(const string& filePath) +{ + ifstream userJson(filePath); + if (!userJson.is_open()) + return false; + + nlohmann::json jsonContent; + userJson >> jsonContent; + + for (const auto& element : jsonContent.items()) + { + if (m_task->IsCancelled()) + return false; + + const auto& guidName = element.key(); + auto guidBytes = element.value(); + if (guidBytes.size() != 11) + { + LogError("Error: GUID array size is incorrect for %s", guidName.c_str()); + return false; + } + EFI_GUID guid; + guid[0] = static_cast(int(guidBytes[0])); + guid[1] = static_cast(int(guidBytes[0]) >> 8); + guid[2] = static_cast(int(guidBytes[0]) >> 16); + guid[3] = static_cast(int(guidBytes[0]) >> 24); + + guid[4] = static_cast(int(guidBytes[1])); + guid[5] = static_cast(int(guidBytes[1]) >> 8); + + guid[6] = static_cast(int(guidBytes[2])); + guid[7] = static_cast(int(guidBytes[2]) >> 8); + + for (int i = 8; i < 16; i++) + guid[i] = static_cast(int(guidBytes[i - 5])); + + // Insert the GUID and its name into the map + m_user_guids[guid] = guidName; + } + + return true; +} + +void Resolver::initProtocolMapping() +{ + if (!m_protocol.empty()) + return; + auto fileName = GetBundledEfiPath(); + if (!parseProtocolMapping(fileName)) + LogAlert("Binary Ninja Version Too Low. Please upgrade to a new version."); + + fileName = GetUserGuidPath(); + parseUserGuidIfExists(fileName); +} + +bool Resolver::setModuleEntry(EFIModuleType fileType) +{ + // Wait until initial analysis is finished + m_view->UpdateAnalysisAndWait(); + + uint64_t entry = m_view->GetEntryPoint(); + auto entryFunc = m_view->GetAnalysisFunction(m_view->GetDefaultPlatform(), entry); + if (!entryFunc) + { + LogDebug("Entry func Not found... "); + return false; + } + + // TODO sometimes the parameter at callsite cannot be correctly recognized, #Vector35/binaryninja-api/4529 + // temporary workaround for this issue, adjust callsite types in entry function if it doesn't has parameters + + // Note: we only adjust the callsite in entry function, this is just a temp fix and it cannot cover all cases + auto callsites = entryFunc->GetCallSites(); + LogDebug("Checking callsites at 0x%llx", entryFunc->GetStart()); + LogDebug("callsite count : %zu", callsites.size()); + for (auto callsite : entryFunc->GetCallSites()) + { + auto mlil = entryFunc->GetMediumLevelIL(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), callsite.addr); + auto instr = mlil->GetInstruction(mlilIdx); + LogDebug("Checking Callsite at 0x%llx", callsite.addr); + if (instr.operation == MLIL_CALL || instr.operation == MLIL_TAILCALL) + { + auto params = instr.GetParameterExprs(); + if (params.size() == 0) + { + // no parameter at call site, check whether it's correctly recognized + auto constantPtr = instr.GetDestExpr(); + if (constantPtr.operation == MLIL_CONST_PTR) + { + auto addr = constantPtr.GetConstant(); + auto funcType = m_view->GetAnalysisFunction(m_view->GetDefaultPlatform(), addr)->GetType(); + entryFunc->SetUserCallTypeAdjustment(m_view->GetDefaultArchitecture(), callsite.addr, funcType); + m_view->UpdateAnalysisAndWait(); + } + else + LogDebug("Operation not ConstPtr: %d", constantPtr.operation); + } + else + LogDebug("param size not zero"); + } + } + + string errors; + QualifiedNameAndType result; + bool ok; + + string typeString; + switch (fileType) + { + case PEI: + { + typeString = "EFI_STATUS _ModuleEntry(EFI_PEI_FILE_HANDLE FileHandle, EFI_PEI_SERVICES **PeiServices)"; + ok = m_view->ParseTypeString(typeString, result, errors, {}, true); + break; + } + + case DXE: + { + typeString = "EFI_STATUS _ModuleEntry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable)"; + ok = m_view->ParseTypeString(typeString, result, errors, {}, true); + break; + } + + case UNKNOWN: + { + LogAlert("Could not identify EFI module type"); + return false; + } + } + + if (!ok) + return false; + + // use UserType so that it would not be overwritten + entryFunc->SetUserType(result.type); + m_view->DefineUserSymbol(new Symbol(FunctionSymbol, "_ModuleEntry", entry)); + m_view->UpdateAnalysisAndWait(); + + TypePropagation propagation = TypePropagation(m_view); + return propagation.propagateFuncParamTypes(entryFunc); +} + +vector Resolver::HighLevelILExprsAt(Ref func, Ref arch, uint64_t addr) +{ + auto llil = func->GetLowLevelIL(); + auto mlil = func->GetMediumLevelIL(); + auto hlil = func->GetHighLevelIL(); + + size_t llilIdx = func->GetLowLevelILForInstruction(arch, addr); + size_t llilExprIdx = llil->GetIndexForInstruction(llilIdx); + auto mlilIdxes = llil->GetMediumLevelILExprIndexes(llilExprIdx); + + vector hlils; + + for (size_t mlilIdx : mlilIdxes) + { + auto hlilIdxes = mlil->GetHighLevelILExprIndexes(mlilIdx); + for (auto hlilIdx : hlilIdxes) + { + auto hlilExpr = hlil->GetExpr(hlilIdx); + hlils.push_back(hlilExpr); + } + } + return hlils; +} + +Ref Resolver::GetTypeFromViewAndPlatform(string typeName) +{ + QualifiedNameAndType result; + string errors; + bool ok = m_view->ParseTypeString(typeName, result, errors); + if (!ok) + { + // TODO how to retrieve platform types? + return nullptr; + } + return result.type; +} + +bool Resolver::resolveGuidInterface(Ref func, uint64_t addr, int guidPos, int interfacePos) +{ + auto hlils = HighLevelILExprsAt(func, m_view->GetDefaultArchitecture(), addr); + for (auto hlil : hlils) + { + if (hlil.operation != HLIL_CALL) + continue; + + HighLevelILInstruction instr; + if (hlil.GetParameterExprs().size() == 1 && hlil.GetParameterExprs()[0].operation == HLIL_CALL) + instr = hlil.GetParameterExprs()[0]; + else + instr = hlil; + + auto params = instr.GetParameterExprs(); + if (params.size() <= max(guidPos, interfacePos)) + continue; + + auto guidAddr = params[guidPos].GetValue(); + EFI_GUID guid; + if (guidAddr.state == ConstantValue || guidAddr.state == ConstantPointerValue) + { + if (m_view->Read(&guid, guidAddr.value, 16) < 16) + continue; + } + else if (guidAddr.state == StackFrameOffset) + { + auto mlil = instr.GetMediumLevelIL(); + int64_t offset = 0; + vector contentBytes; + while (offset < 16) + { + auto var = mlil.GetVariableForStackLocation(guidAddr.value + offset); + if (!func->GetVariableType(var)) + break; + + auto width = func->GetVariableType(var)->GetWidth(); + if (width == 0 || width > 8) + break; + + auto value = mlil.GetStackContents(guidAddr.value + offset, width); + int64_t content; + if (value.state == ConstantValue || value.state == ConstantPointerValue) + content = value.value; + else + break; + + for (auto i = 0; i < width; i++) + { + contentBytes.push_back(static_cast(content >> (i * 8))); + } + } + if (contentBytes.size() != 16) + continue; + + memcpy(guid.data(), contentBytes.data(), 16); + } + else if (params[guidPos].operation == HLIL_VAR) + { + // want to check whether is a protocol wrapper + auto ssa = params[guidPos].GetSSAForm(); + HighLevelILInstruction ssaExpr; + if (ssa.operation != HLIL_VAR_SSA) + continue; + if (ssa.GetSSAVariable().version != 0) + { + auto incomming_def = func->GetHighLevelIL()->GetSSAVarDefinition(ssa.GetSSAVariable()); + if (!incomming_def) + continue; + auto incomming_def_ssa = func->GetHighLevelIL()->GetSSAForm()->GetExpr(incomming_def); + if (incomming_def_ssa.operation != HLIL_VAR_INIT_SSA) + continue; + if (incomming_def_ssa.GetSourceExpr().operation != HLIL_VAR_SSA) + continue; + if (incomming_def_ssa.GetSourceExpr().GetSSAVariable().version != 0) + continue; + ssaExpr = incomming_def_ssa.GetSourceExpr(); + } + else + ssaExpr = ssa; + + auto funcParams = func->GetParameterVariables().GetValue(); + bool found = false; + int incomingGuidIdx; + for (int i = 0; i < funcParams.size(); i++) + { + if (funcParams[i] == ssaExpr.GetSSAVariable().var) + { + incomingGuidIdx = i; + found = true; + break; + } + } + if (!found) + continue; + + // see if output interface varible is an incoming parameter + auto interfaceInstrSsa = params[interfacePos].GetSSAForm(); + if (interfaceInstrSsa.operation != HLIL_VAR_SSA) + continue; + + if (interfaceInstrSsa.GetSSAVariable().version != 0) + { + auto incomingDef = + func->GetHighLevelIL()->GetSSAForm()->GetSSAVarDefinition(interfaceInstrSsa.GetSSAVariable()); + auto defExpr = func->GetHighLevelIL()->GetSSAForm()->GetExpr(incomingDef); + if (defExpr.operation != HLIL_VAR_INIT_SSA) + continue; + if (defExpr.GetSourceExpr().operation != HLIL_VAR_SSA) + continue; + if (defExpr.GetSourceExpr().GetSSAVariable().version != 0) + continue; + interfaceInstrSsa = defExpr.GetSourceExpr(); + } + found = false; + int incomingInstrIdx; + for (int i = 0; i < funcParams.size(); i++) + { + if (funcParams[i] == interfaceInstrSsa.GetSSAVariable().var) + { + incomingInstrIdx = i; + found = true; + break; + } + } + if (!found) + continue; + + LogInfo("Found EFI Protocol wrapper at 0x%llx, checking reference to this function", addr); + + auto refs = m_view->GetCodeReferences(func->GetStart()); + for (auto& ref : refs) + resolveGuidInterface(ref.func, ref.addr, incomingGuidIdx, incomingInstrIdx); + continue; + } + + if (guid.empty()) + continue; + + auto names = lookupGuid(guid); + string protocol_name = names.first; + string guidName = names.second; + + if (protocol_name.empty()) + { + // protocol name is empty + if (!guidName.empty()) + { + // user added guid, check whether the user has added the protocol type + string possible_protocol_type = guidName; + size_t pos = possible_protocol_type.rfind("_GUID"); + if (pos != string::npos) + possible_protocol_type.erase(pos, 5); + + // check whether `possible_protocol_type` is in bv.types + QualifiedNameAndType result; + string errors; + bool ok = m_view->ParseTypeString(possible_protocol_type, result, errors); + if (ok) + protocol_name = possible_protocol_type; + } + else + { + // use UnknownProtocol as defult + LogWarn("Unknown EFI Protocol referenced at 0x%llx", addr); + guidName = nonConflictingName("UnknownProtocolGuid"); + } + } + + // now we just need to rename the GUID and apply the protocol type + auto sym = m_view->GetSymbolByAddress(guidAddr.value); + auto guidVarName = guidName; + if (sym) + guidVarName = sym->GetRawName(); + + QualifiedNameAndType result; + string errors; + bool ok = m_view->ParseTypeString("EFI_GUID", result, errors); + if (!ok) + return false; + m_view->DefineDataVariable(guidAddr.value, result.type); + m_view->DefineUserSymbol(new Symbol(DataSymbol, guidVarName, guidAddr.value)); + + if (protocol_name.empty()) + { + LogWarn("Found unknown protocol at 0x%llx", addr); + protocol_name = "VOID*"; + } + + auto protocolType = GetTypeFromViewAndPlatform(protocol_name); + if (!protocolType) + continue; + protocolType = Type::PointerType(m_view->GetDefaultArchitecture(), protocolType); + auto interfaceParam = params[interfacePos]; + + // TODO we need to check whether it is an aliased var, or it can probably overwrite the other interfaces + + if (interfaceParam.operation == HLIL_ADDRESS_OF) + { + interfaceParam = interfaceParam.GetSourceExpr(); + if (interfaceParam.operation == HLIL_VAR) + { + string interfaceName = guidName; + if (guidName.substr(0, 19) == "UnknownProtocolGuid") + { + interfaceName.replace(0, 19, "UnknownProtocolInterface"); + interfaceName = nonConflictingLocalName(func, interfaceName); + } + else + { + interfaceName = nonConflictingLocalName(func, GetVarNameForTypeStr(guidName)); + } + func->CreateUserVariable(interfaceParam.GetVariable(), protocolType, interfaceName); + } + } + else if (interfaceParam.operation == HLIL_CONST_PTR) + { + auto dataVarAddr = interfaceParam.GetValue().value; + m_view->DefineDataVariable(dataVarAddr, protocolType); + string interfaceName = guidName; + if (interfaceName.find("GUID") != interfaceName.npos) + { + interfaceName = interfaceName.replace(interfaceName.find("GUID"), 4, "INTERFACE"); + interfaceName = GetVarNameForTypeStr(interfaceName); + } + else if (guidName.substr(0, 19) == "UnknownProtocolGuid") + { + interfaceName.replace(15, 4, "Interface"); + } + m_view->DefineUserSymbol(new Symbol(DataSymbol, interfaceName, dataVarAddr)); + } + m_view->UpdateAnalysisAndWait(); + } + + return true; +} + +bool Resolver::defineTypeAtCallsite( + Ref func, uint64_t addr, const string typeName, int paramIdx, bool followFields) +{ + auto mlil = func->GetMediumLevelIL(); + size_t mlilIdx = mlil->GetInstructionStart(m_view->GetDefaultArchitecture(), addr); + auto instr = mlil->GetInstruction(mlilIdx); + + auto params = instr.GetParameterExprs(); + if (params.size() < paramIdx + 1) + return false; + + auto param = params[paramIdx]; + if (param.operation != MLIL_CONST_PTR) + return false; + + uint64_t varAddr = param.GetConstant(); + DataVariable datavar; + auto ok = m_view->GetDataVariableAtAddress(varAddr, datavar); + if (ok) + { + string datavarTypeName = datavar.type.GetValue()->GetTypeName().GetString(); + if (datavarTypeName.find(typeName) != datavarTypeName.npos) + // the variable already has this type, return + return false; + } + + // Now we want to define the type at varAddr + + if (typeName == "EFI_GUID") + { + // If it's GUID, we want to define it with name + defineAndLookupGuid(varAddr); + // defining a GUID should never fail. Also it can not have fields + return true; + } + + QualifiedNameAndType result; + string errors; + ok = m_view->ParseTypeString(typeName, result, errors); + if (!ok) + { + LogError("Cannot parse type %s when trying to define type at 0x%llx", typeName.c_str(), addr); + return false; + } + + m_view->DefineDataVariable(varAddr, result.type); + + if (!followFields) + return true; + + // We want to define the Guid field and the Notify field, which are both pointers + DataVariable structVar; + ok = m_view->GetDataVariableAtAddress(varAddr, structVar); + if (!ok) + return false; + + if (!structVar.type.GetValue()->IsNamedTypeRefer()) + return false; + + auto structTypeId = structVar.type.GetValue()->GetNamedTypeReference()->GetTypeId(); + auto structStructureType = m_view->GetTypeById(structTypeId)->GetStructure(); + + if (!structStructureType) + return false; + auto members = structStructureType->GetMembers(); + + // we want to keep this name for renaming NotifyFunction + string guidName; + for (auto member : members) + { + auto memberOffset = member.offset; + auto memberType = member.type.GetValue(); + auto memberName = member.name; + + // we only want to define pointers + if (!memberType->IsPointer() && !(memberType->IsNamedTypeRefer() && memberName == "Notify")) + continue; + + if (memberName == "Guid") + { + uint64_t guidAddr = 0; + m_view->Read(&guidAddr, varAddr + memberOffset, m_view->GetAddressSize()); + auto name = defineAndLookupGuid(guidAddr); + guidName = name.second; + } + else if (memberName == "Notify") + { + // Notify has the type EFI_NOTIFY_ENTRY_POINT + // which is a NamedTypeRefer + uint64_t funcAddr; + m_view->Read(&funcAddr, varAddr + memberOffset, m_view->GetAddressSize()); + auto notifyFunc = m_view->GetAnalysisFunction(m_view->GetDefaultPlatform(), funcAddr); + if (!notifyFunc) + continue; + + string funcName = guidName; + if (guidName.empty()) + funcName = nonConflictingName("UnknownNotify"); + else + funcName = "Notify" + funcName.replace(funcName.find("GUID"), 4, ""); + + string notifyTypeStr = + "EFI_STATUS Notify(EFI_PEI_SERVICES **PeiServices, EFI_PEI_NOTIFY_DESCRIPTOR* NotifyDescriptor, VOID* " + "Ppi)"; + ok = m_view->ParseTypeString(notifyTypeStr, result, errors); + notifyFunc->SetUserType(result.type); + m_view->DefineUserSymbol(new Symbol(FunctionSymbol, funcName, funcAddr)); + m_view->UpdateAnalysisAndWait(); + + TypePropagation propagator(m_view); + propagator.propagateFuncParamTypes(notifyFunc); + } + } + return true; +} + +Resolver::Resolver(Ref view, Ref task) +{ + m_view = view; + m_task = task; + m_width = m_view->GetAddressSize(); +} + +pair Resolver::lookupGuid(EFI_GUID guidBytes) +{ + auto it = m_protocol.find(guidBytes); + if (it != m_protocol.end()) + return it->second; + + auto user_it = m_user_guids.find(guidBytes); + if (user_it != m_user_guids.end()) + return make_pair(string(), user_it->second); + + return {}; +} + +pair Resolver::defineAndLookupGuid(uint64_t addr) +{ + EFI_GUID guidBytes; + try + { + auto readSize = m_view->Read(&guidBytes, addr, 16); + if (readSize != 16) + return make_pair(string(), string()); + } + catch (ReadException) + { + LogError("Read GUID failed at 0x%llx", addr); + return make_pair(string(), string()); + } + auto namePair = lookupGuid(guidBytes); + string protocolName = namePair.first; + string guidName = namePair.second; + + QualifiedNameAndType result; + string errors; + // must use ParseTypeString, + // m_view->GetTypeByName() doesn't return a NamedTypeReference and the DataRenderer doesn't applied + bool ok = m_view->ParseTypeString("EFI_GUID", result, errors); + if (!ok) + return make_pair(string(""), string("")); + m_view->DefineDataVariable(addr, result.type); + if (guidName.empty()) + { + m_view->DefineUserSymbol(new Symbol(DataSymbol, nonConflictingName("UnknownGuid"), addr)); + LogDebug("Found UnknownGuid at 0x%llx", addr); + } + else + { + m_view->DefineUserSymbol(new Symbol(DataSymbol, guidName, addr)); + LogDebug("Define %s at 0x%llx", guidName.c_str(), addr); + } + + return namePair; +} diff --git a/platform/efi/efi_resolver/src/TypePropagation.cpp b/platform/efi/efi_resolver/src/TypePropagation.cpp new file mode 100644 index 000000000..ff0d43b94 --- /dev/null +++ b/platform/efi/efi_resolver/src/TypePropagation.cpp @@ -0,0 +1,198 @@ +#include "TypePropagation.h" +#include "highlevelilinstruction.h" + +TypePropagation::TypePropagation(BinaryView* view) +{ + m_view = view; + m_queue.clear(); + m_platform = view->GetDefaultPlatform(); +} + +const std::map defaultName = {{"EFI_SYSTEM_TABLE", "gST"}, {"EFI_BOOT_SERVICES", "gBS"}, + {"EFI_RUNTIME_SERVICES", "gRT"}, {"EFI_MM_SYSTEM_TABLE", "gMmst"}, {"EFI_SMM_SYSTEM_TABLE2", "gSmmst"}, + {"EFI_HANDLE", "gHandle"}}; + +bool TypePropagation::propagateFuncParamTypes(Function* func) +{ + m_queue.push_back(func->GetStart()); + + LogDebug("Start Type propagation from 0x%llx", func->GetStart()); + + while (!m_queue.empty()) + { + uint64_t addr = m_queue.front(); + m_queue.pop_front(); + + Ref target_func = m_view->GetAnalysisFunction(m_platform, addr); + auto params = target_func->GetType()->GetParameters(); + bool update = false; + + auto param_vars = target_func->GetParameterVariables().GetValue(); + for (auto var : param_vars) + { + bool propagate = false; + auto var_type = target_func->GetVariableType(var).GetValue(); + + if (var_type->IsPointer()) + { + Ref target_type = var_type->GetChildType().GetValue(); + if (target_type->IsPointer() || target_type->IsNamedTypeRefer()) + propagate = true; + } + else if (var_type->IsNamedTypeRefer()) + { + Ref target_type = m_view->GetTypeById(var_type->GetNamedTypeReference()->GetTypeId()); + if (target_type->IsPointer()) + propagate = true; + } + if (!propagate) + continue; + + // Check whether the param is an aliased var. If it's an aliased var, it may not be directly used in the + // function + Ref hlil_func_ssa = target_func->GetHighLevelIL()->GetSSAForm(); + std::set aliased_vars = target_func->GetHighLevelILAliasedVariables(); + + auto it = aliased_vars.find(var); + if (it == aliased_vars.end()) + { + // not an aliaed var, use version 0 + update |= propagateFuncParamTypes(target_func, SSAVariable(var, 0)); + } + else + { + // this param is an aliased var, get the ssa_var + auto uses = target_func->GetHighLevelIL()->GetVariableUses(var); + for (auto use : uses) + { + auto hlil_instr = target_func->GetHighLevelIL()->GetExpr(use); + hlil_instr = hlil_instr.GetParent(); + if (hlil_instr.operation != HLIL_VAR_INIT) + continue; + SSAVariable ssa_var = hlil_instr.GetSSAForm().GetDestSSAVariable(); + update |= propagateFuncParamTypes(target_func, ssa_var); + } + } + } + + if (update) + m_view->UpdateAnalysisAndWait(); + } + return true; +} + +bool TypePropagation::propagateFuncParamTypes(Function* func, SSAVariable ssa_var) +{ + bool update = false; + auto mlil_func_ssa = func->GetMediumLevelIL()->GetSSAForm(); + auto uses = mlil_func_ssa->GetSSAVarUses(ssa_var); + for (auto use : uses) + { + auto instr = mlil_func_ssa->GetInstruction(use); + switch (instr.operation) + { + case MLIL_CALL_SSA: + case MLIL_TAILCALL_SSA: + { + // propagate variable type to sub function + auto dest = instr.GetDestExpr(); + if (!dest.GetValue().IsConstant()) + continue; + Ref subfunc = m_view->GetAnalysisFunction(m_platform, dest.GetValue().value); + + if (!subfunc) + continue; + + auto subfunc_type = subfunc->GetType(); + auto subfunc_params = subfunc->GetType()->GetParameters(); + + auto instr_params = instr.GetParameterExprs(); + for (int i = 0; i < instr_params.size(); i++) + { + if (instr_params[i].operation != MLIL_VAR_SSA) + continue; + if (instr_params[i].GetSourceSSAVariable() != ssa_var) + continue; + if (i >= subfunc_params.size()) + break; + auto ssa_var_type = func->GetVariableType(ssa_var.var).GetValue(); + auto typeName = GetOriginalTypeName(ssa_var_type); + + auto changeFuncType = + [](BinaryView* bv, Ref funcType, std::string paramName, Ref paramType, int paramIdx) { + auto newFuncType = TypeBuilder(funcType); + auto adjustedParams = newFuncType.GetParameters(); + adjustedParams.at(paramIdx) = FunctionParameter(paramName, paramType); + newFuncType.SetParameters(adjustedParams); + return newFuncType.Finalize(); + }; + + subfunc->SetUserType( + changeFuncType(m_view, subfunc_type, GetVarNameForTypeStr(typeName), ssa_var_type, i)); + m_view->UpdateAnalysisAndWait(); + + if (std::find(m_queue.begin(), m_queue.end(), subfunc->GetStart()) == m_queue.end()) + m_queue.push_back(subfunc->GetStart()); + update = true; + break; + } + break; + } + + case MLIL_STORE_SSA: + { + auto target = instr.GetDestExpr(); + if (!target.GetValue().IsConstant()) + continue; + auto constant = target.GetValue().value; + auto ssa_var_type = func->GetVariableType(ssa_var.var).GetValue(); + auto typeName = GetOriginalTypeName(ssa_var_type); + + auto it = defaultName.find(typeName); + if (it != defaultName.end()) + typeName = it->second; + + m_view->DefineDataVariable(constant, ssa_var_type); + m_view->DefineUserSymbol(new Symbol(DataSymbol, typeName, constant)); + + update = true; + break; + } + + case MLIL_SET_VAR_SSA: + { + auto src = instr.GetSourceExpr(); + auto dest = instr.GetDestSSAVariable(); + + auto dest_type = func->GetVariableType(dest.var); + Confidence> src_type; + switch (src.operation) + { + case MLIL_VAR_SSA: + src_type = func->GetVariableType(src.GetSourceSSAVariable().var); + break; + + case MLIL_LOAD_SSA: + case MLIL_LOAD_STRUCT_SSA: + src_type = src.GetType(); + break; + + default: + continue; + } + + if (src_type.GetValue() && src_type.GetValue() != dest_type.GetValue()) + { + func->CreateUserVariable(dest.var, src_type, func->GetVariableName(dest.var)); + update |= propagateFuncParamTypes(func, SSAVariable(dest.var, dest.version)); + } + break; + } + + default: + LogInfo("Not handled case during type propagation. At %llx: %d", instr.address, instr.operation); + break; + } + } + return update; +} diff --git a/platform/efi/platform_efi.cpp b/platform/efi/platform_efi.cpp index beee259f8..8393af683 100644 --- a/platform/efi/platform_efi.cpp +++ b/platform/efi/platform_efi.cpp @@ -11,11 +11,16 @@ Ref g_efiX86Windows, g_efiX64Windows, g_efiArm64Windows; class EFIX86Platform : public Platform { + uint32_t m_idtr; + Ref m_idtrtype; + public: EFIX86Platform(Architecture* arch) : Platform(arch, "efi-x86") { Ref cc; + m_idtr = arch->GetRegisterByName("idtr"); + cc = arch->GetCallingConventionByName("cdecl"); if (cc) { @@ -50,16 +55,47 @@ class EFIX86Platform : public Platform return g_efiX86; return nullptr; } + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_idtrtype) + m_idtrtype = Type::NamedType(QualifiedName("IDTR32"), GetTypeByName(QualifiedName("IDTR32"))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_idtr) + return m_idtrtype; + + return nullptr; + } + }; class EFIX86WindowsPlatform : public Platform { + uint32_t m_idtr; + Ref m_idtrtype; + public: EFIX86WindowsPlatform(Architecture* arch) : Platform(arch, "efi-windows-x86") { Ref cc; + m_idtr = arch->GetRegisterByName("idtr"); + cc = arch->GetCallingConventionByName("cdecl"); if (cc) { @@ -94,16 +130,45 @@ class EFIX86WindowsPlatform : public Platform return g_efiX86Windows; return nullptr; } + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_idtrtype) + m_idtrtype = Type::NamedType(QualifiedName("IDTR32"), GetTypeByName(QualifiedName("IDTR32"))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_idtr) + return m_idtrtype; + + return nullptr; + } }; class EFIX64Platform : public Platform { + uint32_t m_idtr; + Ref m_idtrtype; + public: EFIX64Platform(Architecture* arch) : Platform(arch, "efi-x86_64") { Ref cc; + m_idtr = arch->GetRegisterByName("idtr"); cc = arch->GetCallingConventionByName("win64"); if (cc) { @@ -123,16 +188,45 @@ class EFIX64Platform : public Platform return g_efiX64; return nullptr; } + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_idtrtype) + m_idtrtype = Type::NamedType(QualifiedName("IDTR64"), GetTypeByName(QualifiedName("IDTR64"))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_idtr) + return m_idtrtype; + + return nullptr; + } }; class EFIX64WindowsPlatform : public Platform { + uint32_t m_idtr; + Ref m_idtrtype; + public: EFIX64WindowsPlatform(Architecture* arch) : Platform(arch, "efi-windows-x86_64") { Ref cc; + m_idtr = arch->GetRegisterByName("idtr"); cc = arch->GetCallingConventionByName("win64"); if (cc) { @@ -152,6 +246,31 @@ class EFIX64WindowsPlatform : public Platform return g_efiX64Windows; return nullptr; } + + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_idtrtype) + m_idtrtype = Type::NamedType(QualifiedName("IDTR64"), GetTypeByName(QualifiedName("IDTR64"))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_idtr) + return m_idtrtype; + + return nullptr; + } }; @@ -186,11 +305,15 @@ class EFIArmv7Platform : public Platform class EFIArm64Platform : public Platform { + uint32_t m_tpidrel0; + Ref m_tpidrel0type; + public: EFIArm64Platform(Architecture* arch) : Platform(arch, "efi-aarch64") { Ref cc; + m_tpidrel0 = arch->GetRegisterByName("tpidr_el0"); cc = arch->GetCallingConventionByName("cdecl"); if (cc) { @@ -201,6 +324,26 @@ class EFIArm64Platform : public Platform } } + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_tpidrel0type) + m_tpidrel0type = Type::NamedType(QualifiedName("EFI_PEI_SERVICES"), + Type::PointerType(view->GetDefaultArchitecture(), + Type::PointerType( + view->GetDefaultArchitecture(), GetTypeByName(QualifiedName("EFI_PEI_SERVICES"))))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + static Ref Recognize(BinaryView* view, Metadata* metadata) { Ref subsystem = metadata->Get("Subsystem"); @@ -210,16 +353,27 @@ class EFIArm64Platform : public Platform return g_efiArm64; return nullptr; } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_tpidrel0) + return m_tpidrel0type; + + return nullptr; + } }; class EFIArm64WindowsPlatform : public Platform { + uint32_t m_tpidrel0; + Ref m_tpidrel0type; public: EFIArm64WindowsPlatform(Architecture* arch) : Platform(arch, "efi-windows-aarch64") { Ref cc; + m_tpidrel0 = arch->GetRegisterByName("tpidr_el0"); cc = arch->GetCallingConventionByName("cdecl"); if (cc) { @@ -230,6 +384,26 @@ class EFIArm64WindowsPlatform : public Platform } } + virtual void BinaryViewInit(BinaryView* view) override + { + if (!m_tpidrel0type) + m_tpidrel0type = Type::NamedType(QualifiedName("EFI_PEI_SERVICES"), + Type::PointerType(view->GetDefaultArchitecture(), + Type::PointerType( + view->GetDefaultArchitecture(), GetTypeByName(QualifiedName("EFI_PEI_SERVICES"))))); + + auto ctx = PluginCommandContext(); + ctx.binaryView = view; + auto commandList = PluginCommand::GetValidList(ctx); + for (auto command : commandList) + { + if (command.GetName() == "EFI Resolver\\Resolve EFI Types And Protocols") + { + command.Execute(ctx); + } + } + } + static Ref Recognize(BinaryView* view, Metadata* metadata) { Ref subsystem = metadata->Get("Subsystem"); @@ -239,6 +413,14 @@ class EFIArm64WindowsPlatform : public Platform return g_efiArm64Windows; return nullptr; } + + virtual Ref GetGlobalRegisterType(uint32_t reg) override + { + if (reg == m_tpidrel0) + return m_tpidrel0type; + + return nullptr; + } }; extern "C"