def call_writable_access_mechanism(self, outfile, register, access_mechanism, value): if access_mechanism.name == "mcr": self._call_mcr_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "mcrr": self._call_mcrr_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "vmsr": self._call_vmsr_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "msr_banked": self._call_msr_banked_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "str": self._call_str_access_mechanism(outfile, register, access_mechanism, value) else: msg = "Access mechanism {am} is not supported using " msg += "aarch32 gas intel assembler syntax ({register})" msg = msg.format( am=access_mechanism.name, register=register.name ) logger.warn(msg)
def _generate_register_set(self, outfile, reg): """ Generate a C function that writes the given register """ if not reg.is_writeable(): return rname = reg.name.lower() prefix = self._register_function_prefix(reg) suffix = config.register_write_function size_type = self._register_size_type(reg) gadget = self.gadgets["pal.c.function_definition"] gadget.name = prefix + "_" + rname + "_" + suffix gadget.return_type = "void" gadget.args = [(size_type, "val")] if reg.access_mechanisms["msr_register"]: am = reg.access_mechanisms["msr_register"][0] if config.encoded_functions: self._generate_aarch64_encoded_set(outfile, reg, am) else: self._generate_msr_register_set(outfile, reg, am) elif reg.access_mechanisms["mcr"]: am = reg.access_mechanisms["mcr"][0] self._generate_mcr_set(outfile, reg, am) elif reg.access_mechanisms["mcrr"]: gadget.args = [("uint64_t", "val")] am = reg.access_mechanisms["mcrr"][0] self._generate_mcrr_set(outfile, reg, am) elif reg.access_mechanisms["msr_banked"]: am = reg.access_mechanisms["msr_banked"][0] self._generate_msr_banked_set(outfile, reg, am) elif reg.access_mechanisms["vmsr"]: am = reg.access_mechanisms["vmsr"][0] self._generate_vmsr_set(outfile, reg, am) elif reg.access_mechanisms["str"]: am = reg.access_mechanisms["str"][0] if not reg.is_readable(): self._generate_external_constants(outfile, reg, am) self._generate_str_set(outfile, reg, am) elif reg.access_mechanisms["msr_immediate"]: msg = "MSRimmediate access mechanism not supported for register {r}" logger.warn(msg.format(r=reg.name.lower())) else: msg = "Failed to generate {f} function for writeable register {r}" msg = msg.format( f=config.register_write_function, r=reg.name.lower() ) logger.error(msg)
def call_readable_access_mechanism(self, outfile, register, access_mechanism, result): if access_mechanism.name == "mrc": self._call_mrc_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "mrrc": self._call_mrrc_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "vmrs": self._call_vmrs_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "mrs_banked": self._call_mrs_banked_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "ldr": self._call_ldr_access_mechanism(outfile, register, access_mechanism, result) else: msg = "Access mechanism {am} is not supported using " msg += "aarch32 gas intel assembler syntax ({register})" msg = msg.format( am=access_mechanism.name, register=register.name ) logger.warn(msg)
def _set_register_long_name(self, reg, reg_node): long_name_node = reg_node.find("./reg_long_name") if long_name_node is not None: reg.long_name = long_name_node.text logger.debug("long_name = " + reg.long_name) else: logger.warn(str(reg.name) + " long_name attribute not found")
def call_writable_access_mechanism(self, outfile, register, access_mechanism, result): if access_mechanism.name == "msr_register": self._call_msr_register_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "str": self._call_str_access_mechanism(outfile, register, access_mechanism, result) else: msg = "Access mechnism {am} is not supported using " msg += "aarch64 gas intel assembler syntax ({register})" msg = msg.format(am=access_mechanism.name, register=register.name) logger.warn(msg)
def generate(self, regs, outpath): try: outfile_path = os.path.abspath(os.path.join(outpath, "pal.h")) logger.info("Generating C Header: " + str(outfile_path)) regs = transforms["remove_reserved_0"].transform(regs) regs = transforms["remove_reserved_1"].transform(regs) regs = transforms["remove_reserved_sign_extended"].transform(regs) regs = transforms["remove_implementation_defined"].transform(regs) regs = transforms["special_to_underscore"].transform(regs) regs = transforms["remove_redundant_am"].transform(regs) regs = transforms["remove_redundant_fields"].transform(regs) regs = transforms["unique_fieldset_names"].transform(regs) regs = filters["no_access_mechanism"].filter_exclusive(regs) if config.encoded_functions: msg = "Encoded accessors are only supported for aarch64 " msg += "registers (aarch32 and external not supported)" logger.warn(msg) regs = filters["aarch64"].filter_inclusive(regs) self.gadgets["pal.header_depends"].includes = [ "<stdint.h>", "aarch32_gcc_accessor_macros.h", "aarch64_gcc_accessor_macros.h" ] unique = [] for reg in regs: external_mechs = reg.access_mechanisms["ldr"] + \ reg.access_mechanisms["str"] for mech in external_mechs: if mech.component not in unique: unique.append(mech.component) self.gadgets["pal.external_component"].components = unique with open(outfile_path, "w") as outfile: self._generate(outfile, regs) except Exception as e: msg = "{g} failed to generate output {out}: {exception}".format( g=str(type(self).__name__), out=outpath, exception=e) raise PalGeneratorException(msg)
def call_writable_access_mechanism(self, outfile, register, access_mechanism, value): access_mechanisms = { 'mov_write': self.__call_mov_write_access_mechanism, 'wrmsr': self.__call_wrmsr_access_mechanism, 'vmwrite': self.__call_vmwrite_access_mechanism, 'xsetbv': self.__call_xsetbv_access_mechansim, } if access_mechanism.name not in access_mechanisms: msg = "Access mechnism {am} is not supported using libpal" msg = msg.format(am=access_mechanism.name) logger.warn(msg) return access_mechanisms[access_mechanism.name](outfile, register, access_mechanism, value)
def call_writable_access_mechanism(self, outfile, register, access_mechanism, value): if access_mechanism.name == "mov_write": self._call_mov_write_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "wrmsr": self._call_wrmsr_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "vmwrite": self._call_vmwrite_access_mechanism(outfile, register, access_mechanism, value) elif access_mechanism.name == "xsetbv": self._call_xsetbv_access_mechanism(outfile, register, access_mechanism, value) else: msg = "Access mechnism {am} is not supported using " msg += "Intel x86_64 gas att assembler syntax" msg = msg.format(am=access_mechanism.name) logger.warn(msg)
def call_readable_access_mechanism(self, outfile, register, access_mechanism, result): access_mechanisms = { 'cpuid': self.__call_cpuid_access_mechanism, 'rdmsr': self.__call_rdmsr_access_mechanism, 'vmread': self.__call_vmread_access_mechanism, 'mov_read': self.__call_mov_read_access_mechanism, 'xgetbv': self.__call_xgetbv_read_access_mechanism, } if access_mechanism.name not in access_mechanisms: msg = "Access mechnism {am} is not supported using libpal" msg = msg.format(am=access_mechanism.name) logger.warn(msg) return access_mechanisms[access_mechanism.name](outfile, register, access_mechanism, result)
def _set_register_purpose(self, reg, reg_node): purpose_text_nodes = reg_node.findall("./reg_purpose/purpose_text") if len(purpose_text_nodes) == 1: purpose_node = reg_node.find("./reg_purpose/purpose_text/para") if purpose_node is not None: ET.strip_tags(purpose_node, "arm-defined-word", "register_link") reg.purpose = purpose_node.text reg.purpose = reg.purpose.replace("\n", " ") elif len(purpose_text_nodes) > 1: text = "See the ARMv8 architecture reference manual for a " text += "description of this register" reg.purpose = text reg.purpose = reg.purpose.replace("\n", " ") if reg.purpose is not None: logger.debug("purpose = " + reg.purpose) else: logger.warn(str(reg.name) + " purpose attribute not found")
def call_readable_access_mechanism(self, outfile, register, access_mechanism, result): if access_mechanism.name == "mov_read": self._call_mov_read_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "cpuid": self._call_cpuid_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "rdmsr": self._call_rdmsr_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "vmread": self._call_vmread_access_mechanism(outfile, register, access_mechanism, result) elif access_mechanism.name == "xgetbv": self._call_xgetbv_access_mechanism(outfile, register, access_mechanism, result) else: msg = "Access mechnism {am} is not supported using " msg += "Intel x86_64 gas att assembler syntax" msg = msg.format(am=access_mechanism.name) logger.warn(msg)
def declare_access_mechanism_dependencies(self, outfile, register, access_mechanism): access_mechanisms = { 'cpuid': self.__declare_cpuid_dependencies, 'rdmsr': self.__declare_rdmsr_dependencies, 'wrmsr': self.__declare_wrmsr_dependencies, 'vmread': self.__declare_vmread_dependencies, 'vmwrite': self.__declare_vmwrite_dependencies, 'mov_read': self.__declare_mov_read_dependencies, 'mov_write': self.__declare_mov_write_dependencies, 'xgetbv': self.__declare_xgetbv_dependencies, 'xsetbv': self.__declare_xsetbv_dependencies, } if access_mechanism.name not in access_mechanisms: msg = "Access mechnism {am} is not supported using libpal" msg = msg.format(am=access_mechanism.name) logger.warn(msg) return access_mechanisms[access_mechanism.name](outfile, register, access_mechanism)
def _set_register_access_mechanisms(self, reg, reg_node, n=0): # Memory mapped access mechanisms reg_address_nodes = reg_node.findall("./reg_address") offsets = set() for node in reg_address_nodes: component_node = node.find("./reg_component") component = str(component_node.text).lower() component = component.replace(" ", "_") component = component.replace(".", "_") offset_node = node.find("./reg_offset/hexnumber") offset = int(offset_node.text, 0) offsets.add(offset) access_type_nodes = node.findall( "./reg_access/reg_access_state/reg_access_type") access_types = set() for access_type_node in access_type_nodes: access_type = access_type_node.text access_types.add(access_type) readable_types = set( ["RO", "RW", "WI", "IMPDEF", "RO or RW", "RAZ/WI"]) writable_types = set(["RW", "WO", "RO or RW", "IMPDEF"]) if access_types & readable_types: am = pal.model.armv8a.access_mechanism.LDR(component, offset) reg.access_mechanisms["ldr"].append(am) if access_types & writable_types: am = pal.model.armv8a.access_mechanism.STR(component, offset) reg.access_mechanisms["str"].append(am) # Instruction based access mechanisms access_mechanism_nodes = reg_node.findall( "./access_mechanisms/access_mechanism") for access_mechanism_node in access_mechanism_nodes: try: accessor = str(access_mechanism_node.attrib["accessor"]) operation = accessor.split(' ', 1)[0] operand = accessor.split(' ', 1)[1] encoding_nodes = access_mechanism_node.findall("encoding/enc") encoding = {} for enc in encoding_nodes: name = str(enc.attrib["n"]).lower() try: val = int(enc.attrib["v"], 2) except ValueError: strval = str(enc.attrib["v"]) n_field_strings = re.findall("\[n:\d+:\d+]", strval) for n_string in n_field_strings: n_trim = n_string[1:-1] msb = int(n_trim.split(":")[1]) lsb = int(n_trim.split(":")[2]) replacement = "" for i in range(lsb, msb + 1): if ((1 << i) & n): replacement = "1" + replacement else: replacement = "0" + replacement strval = strval.replace(n_string, replacement) n_bit_strings = re.findall("\[n:\d+]", strval) for n_string in n_bit_strings: n_trim = n_string[1:-1] bit = int(n_trim.split(":")[1]) n_bit = bin(((1 << bit) & n) >> bit) strval = strval.replace(n_string, str(n_bit)[2:]) n_strings = re.findall("n+", strval) for n_string in n_strings: num_bits = len(n_string) replacement = "" for i in range(num_bits): if ((1 << i) & n): replacement = "1" + replacement else: replacement = "0" + replacement strval = strval.replace(n_string, replacement) val = int(strval, 2) operand = operand.replace("<n>", str(n)) encoding[name] = val if operation == "MSRregister": am = pal.model.armv8a.access_mechanism.MSRRegister( encoding["op0"], encoding["op1"], encoding["op2"], encoding["crn"], encoding["crm"], operand, rt=0b0) reg.access_mechanisms["msr_register"].append(am) elif operation == "MSRbanked": am = pal.model.armv8a.access_mechanism.MSRBanked( encoding["m"], encoding["r"], encoding["m1"], operand) reg.access_mechanisms["msr_banked"].append(am) elif operation == "MSRimmediate": am = pal.model.armv8a.access_mechanism.MSRImmediate( encoding["crn"], encoding["op0"], encoding["op1"], encoding["op2"], operand) reg.access_mechanisms["msr_immediate"].append(am) elif operation == "MRS": am = pal.model.armv8a.access_mechanism.MRSRegister( encoding["op0"], encoding["op1"], encoding["op2"], encoding["crn"], encoding["crm"], operand, rt=0b0) reg.access_mechanisms["mrs_register"].append(am) elif operation == "MRSbanked": am = pal.model.armv8a.access_mechanism.MRSBanked( encoding["m"], encoding["r"], encoding["m1"], operand) reg.access_mechanisms["mrs_banked"].append(am) elif operation == "MCR": am = pal.model.armv8a.access_mechanism.MCR( encoding["coproc"], encoding["opc1"], encoding["opc2"], encoding["crn"], encoding["crm"]) reg.access_mechanisms["mcr"].append(am) elif operation == "MCRR": am = pal.model.armv8a.access_mechanism.MCRR( encoding["coproc"], encoding["opc1"], encoding["crm"]) reg.access_mechanisms["mcrr"].append(am) elif operation == "MRC": am = pal.model.armv8a.access_mechanism.MRC( encoding["coproc"], encoding["opc1"], encoding["opc2"], encoding["crn"], encoding["crm"]) reg.access_mechanisms["mrc"].append(am) elif operation == "MRRC": am = pal.model.armv8a.access_mechanism.MRRC( encoding["coproc"], encoding["opc1"], encoding["crm"]) reg.access_mechanisms["mrrc"].append(am) elif operation == "VMRS": am = pal.model.armv8a.access_mechanism.VMRS( encoding["reg"], operand) reg.access_mechanisms["vmrs"].append(am) elif operation == "VMSR": am = pal.model.armv8a.access_mechanism.VMSR( encoding["reg"], operand) reg.access_mechanisms["vmsr"].append(am) else: msg = "Invalid operation " + operation msg += " in register " + reg.name raise PalParserException(msg) logger.debug(str(am)) except Exception as e: msg = "Could not parse " + str(operation) + " access mechanism" msg += " in register " + reg.name + ":" logger.warn(msg) logger.warn("\t" + str(e)) continue
def parse_file(self, path): registers = [] try: etree = ET.parse(path) root_node = etree.getroot() registers_node = root_node.findall("./registers/register") reg_count = len(registers_node) msg = type(self).__name__ + ": processing " + str(reg_count) msg += " register " if reg_count == 1 else " registers " msg += "from " + str(path) logger.debug(msg) for reg_node in registers_node: if (str(reg_node.attrib["is_register"]) == "True"): logger.debug("Register Attributes:") reg = pal.model.armv8a.ARMv8ARegister() self._set_register_name(reg, reg_node) if "<n>" in reg.name: array_start_node = reg_node.find( "./reg_array/reg_array_start") array_end_node = reg_node.find( "./reg_array/reg_array_end") if array_start_node is None or array_end_node is None: logger.warn("Unbounded n-index register " + str(reg.name)) continue array_start = int(array_start_node.text) array_end = int(array_end_node.text) # TODO: Figure out better way to identify long # memory-mapped registers and access them with an # offset if ((array_end - array_start) > 32): logger.warn("Excessively large n-index register " + str(reg.name)) continue for n in range(array_start, array_end + 1): n_reg = pal.model.armv8a.ARMv8ARegister() self._set_register_name(n_reg, reg_node) self._set_register_long_name(n_reg, reg_node) self._set_register_execution_state(n_reg, reg_node) self._set_register_attributes(n_reg, reg_node) self._set_register_access_mechanisms( n_reg, reg_node, n) self._set_register_purpose(n_reg, reg_node) self._set_register_size(n_reg, reg_node) self._set_register_fields(n_reg, reg_node) n_reg.name = n_reg.name.replace("<n>", str(n)) for fs in n_reg.fieldsets: for f in fs.fields: f.name = f.name.replace("<n>", str(n)) registers.append(n_reg) else: self._set_register_long_name(reg, reg_node) self._set_register_execution_state(reg, reg_node) self._set_register_attributes(reg, reg_node) self._set_register_access_mechanisms(reg, reg_node) self._set_register_purpose(reg, reg_node) self._set_register_size(reg, reg_node) self._set_register_fields(reg, reg_node) registers.append(reg) except Exception as e: msg = "Failed to parse register file " + str(path) msg += ": " + str(e) raise PalParserException(msg) return registers