|
| 1 | +""" |
| 2 | +This module contains functions related to resolving PEI services and PPI protocols |
| 3 | +""" |
| 4 | +from typing import Optional |
| 5 | +from binaryninja import BinaryView, MediumLevelILCall, MediumLevelILTailcall, MediumLevelILLoadStruct, PointerType, \ |
| 6 | + MediumLevelILConstPtr, BackgroundTask, HighLevelILAssign, HighLevelILIntrinsic, ILIntrinsic, log_warn, \ |
| 7 | + MediumLevelILIntrinsic, MediumLevelILConst, NamedTypeReferenceType, MediumLevelILSetVar, HighLevelILVarInit, \ |
| 8 | + MediumLevelILIntrinsicSsa, ILException, Type |
| 9 | +from .protocols import define_protocol_types, lookup_and_define_guid |
| 10 | +from .system_table import propagate_function_param_types |
| 11 | +from .utils import non_conflicting_symbol_name, remove_type_prefix_suffix, get_var_name_from_type, get_type |
| 12 | + |
| 13 | + |
| 14 | +def define_pei_pointers(bv: BinaryView, task: BackgroundTask) -> bool: |
| 15 | + """ |
| 16 | + EFI_PEI_SERVICES can be retrieved by inline assembly code snippets, resolve EFI_PEI_SERVICES pointers according to |
| 17 | + platforms. |
| 18 | + """ |
| 19 | + if bv.arch.name in ['x86', 'x86_64']: |
| 20 | + return define_pei_idt(bv, task) |
| 21 | + if bv.arch.name == 'aarch64': |
| 22 | + return define_pei_mrs(bv, task) |
| 23 | + if bv.arch.name in ['thumb2', 'armv7']: |
| 24 | + return define_pei_mrc(bv, task) |
| 25 | + log_warn(f"Resolving Assembly PEI Services pointers not supported for {bv.arch.name}.") |
| 26 | + return False |
| 27 | + |
| 28 | + |
| 29 | +def define_pei_mrc(bv: BinaryView, task: BackgroundTask) -> bool: |
| 30 | + """ |
| 31 | + For ARMv7, the EFI_PEI_SERVICES** is stored in the TPIDRURW read/write Software Thread ID register |
| 32 | + defined in the ARMv7-A Architectural Reference Manual. |
| 33 | + """ |
| 34 | + pei_service_pointer = Type.pointer(bv.arch, Type.pointer(bv.arch, get_type(bv, "EFI_PEI_SERVICES"))) |
| 35 | + for instr in bv.mlil_instructions: |
| 36 | + if task.cancelled: |
| 37 | + return False |
| 38 | + if isinstance(instr, MediumLevelILIntrinsic): |
| 39 | + if not instr.intrinsic.name == 'Coproc_GetOneWord': |
| 40 | + continue |
| 41 | + if not len(instr.params) == 5: |
| 42 | + continue |
| 43 | + # the parameter should be 0xf, 0, 0xd, 0, 2 |
| 44 | + value = [0xf, 0, 0xd, 0, 2] |
| 45 | + mrc = True |
| 46 | + for idx in range(5): |
| 47 | + param = instr.params[idx] |
| 48 | + if not isinstance(param, MediumLevelILConst): |
| 49 | + continue |
| 50 | + if param.constant != value[idx]: |
| 51 | + mrc = False |
| 52 | + break |
| 53 | + if not mrc: |
| 54 | + continue |
| 55 | + instr.output[0].type = pei_service_pointer |
| 56 | + return True |
| 57 | + |
| 58 | + |
| 59 | +def define_pei_mrs(bv: BinaryView, task): |
| 60 | + """ |
| 61 | + For AARCH64, the PEI_SERVICES pointer is stored in TPIDREL0 register, mark return type of `_ReadStatusReg` to |
| 62 | + `EFI_PEI_SERVICES**`. |
| 63 | + """ |
| 64 | + pei_service_pointer = Type.pointer(bv.arch, Type.pointer(bv.arch, get_type(bv, "EFI_PEI_SERVICES"))) |
| 65 | + for instr in bv.mlil_instructions: |
| 66 | + if task.cancelled: |
| 67 | + return False |
| 68 | + if isinstance(instr, MediumLevelILIntrinsic): |
| 69 | + if not instr.intrinsic.name == '_ReadStatusReg': |
| 70 | + continue |
| 71 | + # if it reading tpidr_el0 |
| 72 | + if not instr.params[0].var.name == 'tpidr_el0': |
| 73 | + continue |
| 74 | + # set the type |
| 75 | + instr.output[0].type = pei_service_pointer |
| 76 | + return True |
| 77 | + |
| 78 | + |
| 79 | +def define_pei_idt(bv: BinaryView, task: BackgroundTask) -> bool: |
| 80 | + """ |
| 81 | + This function will define EFI_PEI_SERVICES pointers in x86 and x86_64 platform. |
| 82 | + According to UEFI PI Specification, for x86 and x86_64, EFI_PEI_SERVICES can be accessed via `IDTR` |
| 83 | + """ |
| 84 | + intrinsic_target_name = { |
| 85 | + # For X86 processors, the EFI_PEI_SERVICES** is stored in the 4 bytes immediately preceding the |
| 86 | + # Interrupt Descriptor Table. |
| 87 | + 'x86': ('__sidt_mems', 'IDTR32'), |
| 88 | + # For x64 processors, the EFI_PEI_SERVICES** is stored in eight bytes immediately preceding the |
| 89 | + # Interrupt Descriptor Table. |
| 90 | + 'x86_64': ('__sidt_mems', 'IDTR64'), |
| 91 | + } |
| 92 | + |
| 93 | + operand_name, intrinsic_type = intrinsic_target_name[bv.arch.name] |
| 94 | + intrinsic_type = get_type(bv, intrinsic_type) |
| 95 | + if not intrinsic_type: |
| 96 | + log_warn("Cannot find IDTR type, Your version of Binary Ninja may be out of date, please consider manually adding those definition or updating to new version.") |
| 97 | + return False |
| 98 | + for instr in bv.hlil_instructions: |
| 99 | + if task.cancelled: |
| 100 | + return False |
| 101 | + |
| 102 | + if isinstance(instr, HighLevelILAssign): |
| 103 | + if ( |
| 104 | + not isinstance(instr.src, HighLevelILIntrinsic) |
| 105 | + or not instr.src.operands |
| 106 | + or not isinstance(instr.src.operands[0], ILIntrinsic) |
| 107 | + or not instr.src.operands[0].name == operand_name |
| 108 | + or not instr.dest.vars |
| 109 | + ): |
| 110 | + continue |
| 111 | + instr.dest.vars[0].type = intrinsic_type |
| 112 | + |
| 113 | + elif isinstance(instr, HighLevelILVarInit): |
| 114 | + mlils = instr.mlils |
| 115 | + for mlil in mlils: |
| 116 | + if ( |
| 117 | + isinstance(mlil, MediumLevelILIntrinsicSsa) |
| 118 | + and mlil.intrinsic.name == operand_name |
| 119 | + and len(mlil.output) > 0 |
| 120 | + ): |
| 121 | + mlil.output[0].var.type = intrinsic_type |
| 122 | + |
| 123 | + # TODO there is a type propagation issue (vector35/binaryninja#759) related to indirect struct access in core, |
| 124 | + # manually fix it now, the following should be removed after the bug is fixed. |
| 125 | + for ref in bv.get_code_refs_for_type("EFI_PEI_SERVICES"): |
| 126 | + try: |
| 127 | + instr = ref.mlil |
| 128 | + except ILException: |
| 129 | + log_warn(f"mlil not available at {hex(ref.address)}") |
| 130 | + continue |
| 131 | + if not isinstance(instr, MediumLevelILSetVar): |
| 132 | + continue |
| 133 | + if not isinstance(instr.src, MediumLevelILLoadStruct): |
| 134 | + continue |
| 135 | + # manually propagate the type |
| 136 | + instr.dest.type = instr.src.expr_type |
| 137 | + |
| 138 | + return True |
| 139 | + |
| 140 | + |
| 141 | +def _define_descriptor(bv: BinaryView, task: BackgroundTask, descriptor_type, param) -> Optional[bool]: |
| 142 | + """ |
| 143 | + Define the descriptor type at `param`'s address. The `param` should be a ConstPtr parameter. |
| 144 | + If the analysis got cancelled or encountered an error, return False. If it doesn't meet conditions, return None. |
| 145 | + """ |
| 146 | + if not isinstance(param, MediumLevelILConstPtr): |
| 147 | + return |
| 148 | + |
| 149 | + var_addr = param.constant |
| 150 | + |
| 151 | + var = bv.get_data_var_at(var_addr) |
| 152 | + if var: |
| 153 | + if descriptor_type == var.type: |
| 154 | + # already defined |
| 155 | + return |
| 156 | + sym = bv.get_symbol_at(var_addr) |
| 157 | + |
| 158 | + if sym is not None: |
| 159 | + var_name = sym.name |
| 160 | + else: |
| 161 | + var_name = None |
| 162 | + |
| 163 | + bv.define_user_data_var(var_addr, descriptor_type, var_name) |
| 164 | + bv.update_analysis_and_wait() |
| 165 | + |
| 166 | + var = bv.get_data_var_at(var_addr) |
| 167 | + notify_descriptor = var.value |
| 168 | + |
| 169 | + if not isinstance(notify_descriptor, dict): |
| 170 | + return |
| 171 | + if "Guid" not in notify_descriptor: |
| 172 | + return |
| 173 | + |
| 174 | + # define types for guid and notify entrypoint |
| 175 | + protocol_name = lookup_and_define_guid(bv, notify_descriptor["Guid"]) |
| 176 | + if protocol_name is False: |
| 177 | + return False |
| 178 | + |
| 179 | + if "Notify" in notify_descriptor: |
| 180 | + notify_entrypoint = notify_descriptor['Notify'] |
| 181 | + func = bv.get_function_at(notify_entrypoint) |
| 182 | + if not func: |
| 183 | + return |
| 184 | + if not protocol_name: |
| 185 | + func_name = non_conflicting_symbol_name(bv, "UnknownNotify") |
| 186 | + else: |
| 187 | + func_name = non_conflicting_symbol_name(bv, f"Notify{get_var_name_from_type(protocol_name)}") |
| 188 | + func.type = f"EFI_STATUS {func_name}(EFI_PEI_SERVICES **PeiServices, EFI_PEI_NOTIFY_DESCRIPTOR* NotifyDescriptor, VOID* Ppi)" |
| 189 | + bv.update_analysis_and_wait() |
| 190 | + return propagate_function_param_types(bv, task, func) |
| 191 | + |
| 192 | + return True |
| 193 | + |
| 194 | + |
| 195 | +def define_pei_descriptor(bv: BinaryView, task: BackgroundTask) -> bool: |
| 196 | + """ |
| 197 | + Defines PEI related descriptors, currently this function will only define EFI_PEI_NOTIFY_DESCRIPTOR |
| 198 | + and EFI_PEI_PPI_DESCRIPTOR, which may be used by PEI_SERVICES. Protocol related descriptors are not |
| 199 | + supported yet. |
| 200 | + """ |
| 201 | + descriptor_types_names = ["EFI_PEI_NOTIFY_DESCRIPTOR", "EFI_PEI_PPI_DESCRIPTOR"] |
| 202 | + descriptor_types = [get_type(bv, des) for des in descriptor_types_names] |
| 203 | + for descriptor_type in descriptor_types: |
| 204 | + refs = list(bv.get_code_refs_for_type(descriptor_type)) |
| 205 | + for ref in refs: |
| 206 | + if task.cancelled: |
| 207 | + return False |
| 208 | + try: |
| 209 | + instr = ref.mlil |
| 210 | + except ILException: |
| 211 | + log_warn(f"mlil unavailable for ref: {hex(ref.address)}") |
| 212 | + continue |
| 213 | + |
| 214 | + if ( |
| 215 | + not isinstance(instr, (MediumLevelILCall, MediumLevelILTailcall)) |
| 216 | + or not isinstance(instr.dest, MediumLevelILLoadStruct) |
| 217 | + or not isinstance(instr.dest.expr_type, PointerType) |
| 218 | + ): |
| 219 | + continue |
| 220 | + |
| 221 | + function_type = instr.dest.expr_type.target |
| 222 | + params = function_type.parameters |
| 223 | + target_param = None |
| 224 | + for param in params: |
| 225 | + type_name = str(param.type) |
| 226 | + if ( |
| 227 | + not isinstance(param.type, PointerType) |
| 228 | + or not isinstance(param.type.target, NamedTypeReferenceType) |
| 229 | + ): |
| 230 | + continue |
| 231 | + if remove_type_prefix_suffix(type_name) in descriptor_types_names: |
| 232 | + target_param = params.index(param) |
| 233 | + break |
| 234 | + if target_param is None: |
| 235 | + continue |
| 236 | + |
| 237 | + if _define_descriptor(bv, task, descriptor_type, instr.params[target_param]) is False: |
| 238 | + return False |
| 239 | + return True |
| 240 | + |
| 241 | + |
| 242 | +def define_locate_ppi_types(bv: BinaryView, task: BackgroundTask) -> bool: |
| 243 | + """ |
| 244 | + define PPI protocols by resolving invocations of LocatePpi |
| 245 | + """ |
| 246 | + return define_protocol_types(bv, "EFI_PEI_SERVICES", "LocatePpi", 1, 4, task) |
0 commit comments