diff options
| author | Brandon Miller <brandon@vector35.com> | 2025-05-08 15:08:10 -0400 |
|---|---|---|
| committer | Brandon Miller <brandon@vector35.com> | 2025-05-08 15:08:10 -0400 |
| commit | 5147249a644f611a801aada7645b7a993fc8e314 (patch) | |
| tree | 3a18e50df5ae9edcf31f3545a7f27774021c76a1 /plugins/efi_resolver/src | |
| parent | f7e831ab40a5031cec9186096a90d94735080ed7 (diff) | |
Implement EFI resolver as a module workflow
Diffstat (limited to 'plugins/efi_resolver/src')
| -rw-r--r-- | plugins/efi_resolver/src/DxeResolver.cpp | 274 | ||||
| -rw-r--r-- | plugins/efi_resolver/src/GuidRenderer.cpp | 52 | ||||
| -rw-r--r-- | plugins/efi_resolver/src/PeiResolver.cpp | 304 | ||||
| -rw-r--r-- | plugins/efi_resolver/src/Plugin.cpp | 81 | ||||
| -rw-r--r-- | plugins/efi_resolver/src/Resolver.cpp | 753 | ||||
| -rw-r--r-- | plugins/efi_resolver/src/TypePropagation.cpp | 198 |
6 files changed, 1662 insertions, 0 deletions
diff --git a/plugins/efi_resolver/src/DxeResolver.cpp b/plugins/efi_resolver/src/DxeResolver.cpp new file mode 100644 index 00000000..f31c42ae --- /dev/null +++ b/plugins/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<uint64_t>(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<BinaryView> view, Ref<BackgroundTask> task) : Resolver(view, task) +{ + initProtocolMapping(); + setModuleEntry(DXE); +} diff --git a/plugins/efi_resolver/src/GuidRenderer.cpp b/plugins/efi_resolver/src/GuidRenderer.cpp new file mode 100644 index 00000000..2a220edc --- /dev/null +++ b/plugins/efi_resolver/src/GuidRenderer.cpp @@ -0,0 +1,52 @@ +#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 width, + vector<pair<Type*, size_t>>& context, const std::string& language) +{ + 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/plugins/efi_resolver/src/PeiResolver.cpp b/plugins/efi_resolver/src/PeiResolver.cpp new file mode 100644 index 00000000..c3f5d50a --- /dev/null +++ b/plugins/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<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() +{ + 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<BinaryView> view, Ref<BackgroundTask> task) : Resolver(view, task) +{ + initProtocolMapping(); + setModuleEntry(PEI); +} diff --git a/plugins/efi_resolver/src/Plugin.cpp b/plugins/efi_resolver/src/Plugin.cpp new file mode 100644 index 00000000..9d5a69ce --- /dev/null +++ b/plugins/efi_resolver/src/Plugin.cpp @@ -0,0 +1,81 @@ +#include "DxeResolver.h" +#include "PeiResolver.h" +#include "binaryninjaapi.h" +#include <thread> + +using namespace BinaryNinja; + +static Ref<BackgroundTask> m_efiBackgroundTask = nullptr; + +bool IsValid(BinaryView* view) +{ + if (!view) + return false; + + auto platform = view->GetDefaultPlatform(); + return (platform && platform->GetName().find("efi-") != std::string::npos); +} + + +void RunCommand(Ref<BinaryView> view) +{ + m_efiBackgroundTask = new BackgroundTask("Running EFI resolver...", true); + thread resolverThread([view]() { + LogInfo("Identifying EFI module type..."); + EFIModuleType moduleType = identifyModuleType(view); + + auto undo = view->BeginUndoActions(); + if (moduleType == PEI) + { + m_efiBackgroundTask->SetProgressText("Resolving PEIM..."); + auto resolver = PeiResolver(view, m_efiBackgroundTask); + resolver.resolvePei(); + } + else if (moduleType == DXE) + { + m_efiBackgroundTask->SetProgressText("Resolving DXE protocols..."); + auto resolver = DxeResolver(view, m_efiBackgroundTask); + resolver.resolveDxe(); + m_efiBackgroundTask->SetProgressText("Resolving MM related protocols..."); + resolver.resolveSmm(); + } + view->CommitUndoActions(undo); + m_efiBackgroundTask->Finish(); + }); + + resolverThread.detach(); +} + + +void RunWorkflow(const Ref<AnalysisContext>& analysisContext) +{ + auto view = analysisContext->GetBinaryView(); + if (IsValid(view)) + RunCommand(view); +} + + +extern "C" +{ + BN_DECLARE_CORE_ABI_VERSION + BINARYNINJAPLUGIN bool CorePluginInit() + { + EfiGuidRenderer::Register(); + auto workflow = Workflow::Instance("core.module.metaAnalysis")->Clone(); + workflow->RegisterActivity(R"~({ + "title": "EFI Resolver", + "name": "analysis.efi.efiResolver", + "role": "action", + "description": "This analysis step resolves EFI protocol interfaces and propagates type information.", + "eligibility": { + "runOnce": true, + "auto": {} + } + })~", &RunWorkflow); + + workflow->InsertAfter("core.module.extendedAnalysis", "analysis.efi.efiResolver"); + Workflow::RegisterWorkflow(workflow); + PluginCommand::Register("Run EFI Resolver", "Resolve EFI interfaces and types", &RunCommand, &IsValid); + return true; + } +} diff --git a/plugins/efi_resolver/src/Resolver.cpp b/plugins/efi_resolver/src/Resolver.cpp new file mode 100644 index 00000000..36fa190b --- /dev/null +++ b/plugins/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<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(__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<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]; + + // 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<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/plugins/efi_resolver/src/TypePropagation.cpp b/plugins/efi_resolver/src/TypePropagation.cpp new file mode 100644 index 00000000..ff0d43b9 --- /dev/null +++ b/plugins/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<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; +} |
