summaryrefslogtreecommitdiff
path: root/platform/efi/efi_resolver/src
diff options
context:
space:
mode:
authorZichuan Li <34680029+river-li@users.noreply.github.com>2024-08-15 11:53:05 -0400
committerBrandon Miller <brandon@vector35.com>2025-05-08 07:23:08 -0400
commit7f08117cfeb48a8f48d08e14cb498ee028efcef2 (patch)
tree345eab220c00cfa098e94fa261dce092fc8d5182 /platform/efi/efi_resolver/src
parent64de95854f9ad4da90b9cf2ee69a80f5dddd7a18 (diff)
Move EFI Resolver to API
Support all existing features in EFI Resolver, 1. Doesn't support running on existing BNDBs (though we tried to support this in python plugins, it doesn't work well) 2. Perform analysis on MLIL rather than HLIL, previous pattern matching on HLIL constains many false negatives
Diffstat (limited to 'platform/efi/efi_resolver/src')
-rw-r--r--platform/efi/efi_resolver/src/DxeResolver.cpp255
-rw-r--r--platform/efi/efi_resolver/src/GuidRenderer.cpp56
-rw-r--r--platform/efi/efi_resolver/src/PeiResolver.cpp278
-rw-r--r--platform/efi/efi_resolver/src/Plugin.cpp64
-rw-r--r--platform/efi/efi_resolver/src/Resolver.cpp676
-rw-r--r--platform/efi/efi_resolver/src/TypePropagation.cpp183
6 files changed, 1512 insertions, 0 deletions
diff --git a/platform/efi/efi_resolver/src/DxeResolver.cpp b/platform/efi/efi_resolver/src/DxeResolver.cpp
new file mode 100644
index 00000000..f801a8e5
--- /dev/null
+++ b/platform/efi/efi_resolver/src/DxeResolver.cpp
@@ -0,0 +1,255 @@
+#include "DxeResolver.h"
+
+bool DxeResolver::resolveBootServices()
+{
+ 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()
+{
+ 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)
+{
+ 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()
+{
+ 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()
+{
+ auto refs = m_view->GetCodeReferencesForType(QualifiedName("EFI_MM_SW_REGISTER"));
+ 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) {
+ /* EFI_MM_SW_REGISTER, we want to rename the second parameter according to the third parameter
+ typedef enum EFI_STATUS (* EFI_MM_SW_REGISTER)(
+ struct EFI_MM_SW_DISPATCH_PROTOCOL* This,
+ EFI_MM_HANDLER_ENTRY_POINT DispatchFunction,
+ struct EFI_MM_SW_REGISTER_CONTEXT*
+ RegisterContext, EFI_HANDLE* DispatchHandle); */
+
+ auto parameters = instr.GetParameterExprs();
+ if (parameters.size() < 3)
+ continue;
+
+ auto dispatchFunction = parameters[1];
+ auto registerContext = parameters[2];
+
+ // Determine the function name according to the registerContext
+ /* struct EFI_MM_SW_REGISTER_CONTEXT
+ * {
+ * UINTN SwMmiInputValue;
+ * };
+ */
+ string funcName = "SmiHandler";
+
+ if (dispatchFunction.operation != MLIL_CONST_PTR)
+ continue;
+ auto funcAddr = static_cast<uint64_t> (dispatchFunction.GetConstant());
+ auto targetFunc = m_view->GetAnalysisFunction(m_view->GetDefaultPlatform(), funcAddr);
+ auto funcType = targetFunc->GetType();
+ // 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<BinaryView> view, Ref<BackgroundTask> 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 00000000..ef148094
--- /dev/null
+++ b/platform/efi/efi_resolver/src/GuidRenderer.cpp
@@ -0,0 +1,56 @@
+#include "GuidRenderer.h"
+
+bool isType(const vector<pair<Type*, size_t>>& 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<pair<Type*, size_t>>& 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<DisassemblyTextLine> EfiGuidRenderer::GetLinesForData(
+ BinaryView* bv, uint64_t address, Type*, const vector<InstructionTextToken>& prefix,
+ size_t, vector<pair<Type*, size_t>>& 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 00000000..2699f2bb
--- /dev/null
+++ b/platform/efi/efi_resolver/src/PeiResolver.cpp
@@ -0,0 +1,278 @@
+#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<HLIL_ASSIGN>().operation != HLIL_STRUCT_FIELD ||
+ expr.GetParent().GetDestExpr<HLIL_ASSIGN>().GetSourceExpr<HLIL_STRUCT_FIELD>().operation != HLIL_VAR)
+ continue;
+
+ auto var = expr.GetParent().GetDestExpr<HLIL_ASSIGN>().GetSourceExpr<HLIL_STRUCT_FIELD>().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<MLIL_INTRINSIC>();
+ 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<MLIL_SET_VAR>().operation != MLIL_LOAD_STRUCT)
+ continue;
+
+ ref.func->CreateUserVariable(instr.GetDestVariable<MLIL_SET_VAR>(),
+ mlil->GetExprType(instr.GetSourceExpr<MLIL_SET_VAR>()).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<MLIL_INTRINSIC>();
+
+ if (m_view->GetDefaultArchitecture()->GetIntrinsicName(intrinsicIdx) != "Coproc_GetOneWord")
+ continue;
+ auto intrinsicParams = instr.GetParameterExprs<MLIL_INTRINSIC>();
+ 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<MLIL_CONST>() != 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()
+{
+ 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()
+{
+ 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()
+{
+ 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<BinaryView> view, Ref<BackgroundTask> 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 00000000..bfa19a7f
--- /dev/null
+++ b/platform/efi/efi_resolver/src/Plugin.cpp
@@ -0,0 +1,64 @@
+#include "DxeResolver.h"
+#include "PeiResolver.h"
+#include "binaryninjaapi.h"
+#include <thread>
+
+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<BackgroundTask> efiBackgroundTask = nullptr;
+
+void Run(Ref<BinaryView> view)
+{
+ efiBackgroundTask = new BackgroundTask("Loading EFI protocol mappings!", true);
+ thread resolverThread([view]() {
+ LogInfo("Entering new thread");
+
+ LogInfo("Identifying module type");
+ EFIModuleType moduleType = identifyModuleType(view);
+
+#ifdef 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();
+ }
+
+#ifdef DEBUG
+ resolver.m_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 00000000..94510321
--- /dev/null
+++ b/platform/efi/efi_resolver/src/Resolver.cpp
@@ -0,0 +1,676 @@
+#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<Function> 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(__linux__)
+ return path + "../types/efi.c";
+#else
+ return path + "/../../Resources/types/efi.c";
+#endif
+}
+
+static string GetUserGuidPath()
+{
+ string path = GetUserDirectory();
+#if defined(_WIN32)
+ return path + "\\types\\efi-guids.json";
+#elif defined(__linux__)
+ 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<uint8_t>(value);
+ guid[1] = static_cast<uint8_t>(value >> 8);
+ guid[2] = static_cast<uint8_t>(value >> 16);
+ guid[3] = static_cast<uint8_t>(value >> 24);
+
+ getline(iss, token, ',');
+ value = stoul(token, nullptr, 16);
+ guid[4] = static_cast<uint8_t>(value);
+ guid[5] = static_cast<uint8_t>(value >> 8);
+
+ getline(iss, token, ',');
+ value = stoul(token, nullptr, 16);
+ guid[6] = static_cast<uint8_t>(value);
+ guid[7] = static_cast<uint8_t>(value >> 8);
+
+ for (int i = 8; i < 16; i++) {
+ getline(iss, token, ',');
+ value = stoul(token, nullptr, 16);
+ guid[i] = static_cast<uint8_t>(value);
+ }
+ return guid;
+}
+
+bool Resolver::parseProtocolMapping(const string& filePath)
+{
+ vector<pair<EFI_GUID, string>> 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<uint8_t>(int(guidBytes[0]));
+ guid[1] = static_cast<uint8_t>(int(guidBytes[0]) >> 8);
+ guid[2] = static_cast<uint8_t>(int(guidBytes[0]) >> 16);
+ guid[3] = static_cast<uint8_t>(int(guidBytes[0]) >> 24);
+
+ guid[4] = static_cast<uint8_t>(int(guidBytes[1]));
+ guid[5] = static_cast<uint8_t>(int(guidBytes[1]) >> 8);
+
+ guid[6] = static_cast<uint8_t>(int(guidBytes[2]));
+ guid[7] = static_cast<uint8_t>(int(guidBytes[2]) >> 8);
+
+ for (int i = 8; i < 16; i++)
+ guid[i] = static_cast<uint8_t>(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<HighLevelILInstruction> Resolver::HighLevelILExprsAt(Ref<Function> func, Ref<Architecture> 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<HighLevelILInstruction> 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<Type> 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<Function> 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<uint8_t> 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<uint8_t>(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];
+ 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.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<Function> 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<BinaryView> view, Ref<BackgroundTask> task)
+{
+ m_view = view;
+ m_task = task;
+ m_width = m_view->GetAddressSize();
+}
+
+pair<string, string> 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<string, string> 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 00000000..4b4fac6c
--- /dev/null
+++ b/platform/efi/efi_resolver/src/TypePropagation.cpp
@@ -0,0 +1,183 @@
+#include "TypePropagation.h"
+#include "highlevelilinstruction.h"
+
+TypePropagation::TypePropagation(BinaryView* view)
+{
+ m_view = view;
+ m_queue.clear();
+ m_platform = view->GetDefaultPlatform();
+}
+
+const std::map<std::string, std::string> 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<Function> 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<Type> target_type = var_type->GetChildType().GetValue();
+ if (target_type->IsPointer() || target_type->IsNamedTypeRefer())
+ propagate = true;
+ } else if (var_type->IsNamedTypeRefer()) {
+ Ref<Type> 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<HighLevelILFunction> hlil_func_ssa = target_func->GetHighLevelIL()->GetSSAForm();
+ std::set<Variable> 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<Function> 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<Type> funcType, std::string paramName, Ref<Type> 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<MLIL_STORE_SSA>();
+ 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<MLIL_SET_VAR_SSA>();
+ auto dest = instr.GetDestSSAVariable<MLIL_SET_VAR_SSA>();
+
+ auto dest_type = func->GetVariableType(dest.var);
+ Confidence<Ref<Type>> 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;
+}