static void print_insn_detail(cs_insn *ins) { cs_arm64 *arm64; int i; cs_regs regs_read, regs_write; uint8_t regs_read_count, regs_write_count; // detail can be NULL if SKIPDATA option is turned ON if (ins->detail == NULL) return; arm64 = &(ins->detail->arm64); if (arm64->op_count) printf("\top_count: %u\n", arm64->op_count); for (i = 0; i < arm64->op_count; i++) { cs_arm64_op *op = &(arm64->operands[i]); switch(op->type) { default: break; case ARM64_OP_REG: printf("\t\toperands[%u].type: REG = %s\n", i, cs_reg_name(handle, op->reg)); break; case ARM64_OP_IMM: printf("\t\toperands[%u].type: IMM = 0x%"PRIx64 "\n", i, op->imm); break; case ARM64_OP_FP: printf("\t\toperands[%u].type: FP = %f\n", i, op->fp); break; case ARM64_OP_MEM: printf("\t\toperands[%u].type: MEM\n", i); if (op->mem.base != ARM64_REG_INVALID) printf("\t\t\toperands[%u].mem.base: REG = %s\n", i, cs_reg_name(handle, op->mem.base)); if (op->mem.index != ARM64_REG_INVALID) printf("\t\t\toperands[%u].mem.index: REG = %s\n", i, cs_reg_name(handle, op->mem.index)); if (op->mem.disp != 0) printf("\t\t\toperands[%u].mem.disp: 0x%x\n", i, op->mem.disp); break; case ARM64_OP_CIMM: printf("\t\toperands[%u].type: C-IMM = %u\n", i, (int)op->imm); break; case ARM64_OP_REG_MRS: printf("\t\toperands[%u].type: REG_MRS = 0x%x\n", i, op->reg); break; case ARM64_OP_REG_MSR: printf("\t\toperands[%u].type: REG_MSR = 0x%x\n", i, op->reg); break; case ARM64_OP_PSTATE: printf("\t\toperands[%u].type: PSTATE = 0x%x\n", i, op->pstate); break; case ARM64_OP_SYS: printf("\t\toperands[%u].type: SYS = 0x%x\n", i, op->sys); break; case ARM64_OP_PREFETCH: printf("\t\toperands[%u].type: PREFETCH = 0x%x\n", i, op->prefetch); break; case ARM64_OP_BARRIER: printf("\t\toperands[%u].type: BARRIER = 0x%x\n", i, op->barrier); break; } uint8_t access = op->access; switch(access) { default: break; case CS_AC_READ: printf("\t\toperands[%u].access: READ\n", i); break; case CS_AC_WRITE: printf("\t\toperands[%u].access: WRITE\n", i); break; case CS_AC_READ | CS_AC_WRITE: printf("\t\toperands[%u].access: READ | WRITE\n", i); break; } if (op->shift.type != ARM64_SFT_INVALID && op->shift.value) printf("\t\t\tShift: type = %u, value = %u\n", op->shift.type, op->shift.value); if (op->ext != ARM64_EXT_INVALID) printf("\t\t\tExt: %u\n", op->ext); if (op->vas != ARM64_VAS_INVALID) printf("\t\t\tVector Arrangement Specifier: 0x%x\n", op->vas); if (op->vess != ARM64_VESS_INVALID) printf("\t\t\tVector Element Size Specifier: %u\n", op->vess); if (op->vector_index != -1) printf("\t\t\tVector Index: %u\n", op->vector_index); } if (arm64->update_flags) printf("\tUpdate-flags: True\n"); if (arm64->writeback) printf("\tWrite-back: True\n"); if (arm64->cc) printf("\tCode-condition: %u\n", arm64->cc); // Print out all registers accessed by this instruction (either implicit or explicit) if (!cs_regs_access(handle, ins, regs_read, ®s_read_count, regs_write, ®s_write_count)) { if (regs_read_count) { printf("\tRegisters read:"); for(i = 0; i < regs_read_count; i++) { printf(" %s", cs_reg_name(handle, regs_read[i])); } printf("\n"); } if (regs_write_count) { printf("\tRegisters modified:"); for(i = 0; i < regs_write_count; i++) { printf(" %s", cs_reg_name(handle, regs_write[i])); } printf("\n"); } } printf("\n"); }
void emulate_constructor(dmp_ctx_t *ctx, uint64_t constructor_address, struct hierarchy_entry_head *head) { if (!ctx) return; if (!ctx->kimage_mh || !ctx->map || !ctx->reg_map) return; uint64_t kimage_base = find_kimage_base(ctx->kimage_mh); uint64_t kimage_os_metaclass_constructor = find_kimage_os_metaclass_constructor(ctx->kimage_mh, kimage_base); csh handle; cs_insn *insn; size_t count; cs_regs regs_read = {0}, regs_write = {0}; uint8_t read_count, write_count; if (cs_open(CS_ARCH_ARM64, CS_MODE_ARM, &handle) != CS_ERR_OK) return; cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); uint8_t *constructor_code = (uint8_t *)KERNEL_ADDR_TO_MAP(ctx->kimage_mh, kimage_base, constructor_address); uint64_t constructor_size = get_constructor_size(ctx->kimage_mh, constructor_address, kimage_base); count = cs_disasm(handle, constructor_code, constructor_size, constructor_address, 0, &insn); if (count > 0) { size_t j; for (j = 0; j < count; j++) { if (cs_regs_access(handle, &insn[j], regs_read, &read_count, regs_write, &write_count) == 0) { switch (insn[j].id) { case ARM64_INS_ADR: { uint64_t adr_addr = 0; int is_adrp = 0; uint32_t rd = 0; int32_t off = 0; if (aarch64_decode_adr(*(uint32_t *)(&insn[j].bytes), &is_adrp, &rd, &off)) { adr_addr = insn[j].address + off; } get_ctx_reg_map_reg(ctx, regs_write[0]) = adr_addr; break; } case ARM64_INS_ADRP: { uint64_t adr_addr = 0; int is_adrp = 0; uint32_t rd = 0; int32_t off = 0; if (aarch64_decode_adr(*(uint32_t *)(&insn[j].bytes), &is_adrp, &rd, &off)) { adr_addr = ((insn[j].address + off) >> 12) << 12; } get_ctx_reg_map_reg(ctx, regs_write[0]) = adr_addr; break; } case ARM64_INS_LDR: { uint64_t ldr_addr = 0; int is_w = 0; int is64 = 0; uint32_t rt = 0; int32_t s_off = 0; uint32_t u_off = 0; if (aarch64_decode_ldr_literal(*(uint32_t *)(&insn[j].bytes), &is_w, &is64, &rt, &s_off)) { ldr_addr = insn[j].address + s_off; uint64_t ldr_deref = *(uint64_t *)KERNEL_ADDR_TO_MAP(ctx->kimage_mh, kimage_base, ldr_addr); get_ctx_reg_map_reg(ctx, regs_write[0]) = ldr_deref; } else if (aarch64_decode_ldr_immediate(*(uint32_t *)(&insn[j].bytes), &u_off)) { uint64_t op1 = get_ctx_reg_map_reg(ctx, regs_read[0]); op1 += u_off; uint64_t ldr_addr = *(uint64_t *)KERNEL_ADDR_TO_MAP(ctx->kimage_mh, kimage_base, op1); get_ctx_reg_map_reg(ctx, regs_write[0]) = ldr_addr; } break; } case ARM64_INS_ADD: { uint32_t off = 0; if (aarch64_decode_add(*(uint32_t *)(&insn[j].bytes), &off)) { uint64_t op1 = get_ctx_reg_map_reg(ctx, regs_read[0]); op1 += off; get_ctx_reg_map_reg(ctx, regs_write[0]) = op1; } break; } case ARM64_INS_MOV: { uint64_t op1 = get_ctx_reg_map_reg(ctx, regs_read[0]); get_ctx_reg_map_reg(ctx, regs_write[0]) = op1; break; } /* ugly. libdump should be independent of iokitdumper, but I was lazy and this was the quickest way to map constructors lmao */ case ARM64_INS_BL: { int is_bl = 0; int32_t off = 0; if (aarch64_decode_b(*(uint32_t *)(&insn[j].bytes), &is_bl, &off)) { if (insn[j].address + off == kimage_os_metaclass_constructor) { const char *kext_name = get_kext_name(ctx->map, ctx->kimage_mh); struct hierarchy_entry *entry = malloc(sizeof(struct hierarchy_entry)); strncpy(entry->class_name, (char *)KERNEL_ADDR_TO_MAP(ctx->kimage_mh, kimage_base, get_ctx_reg_map_reg(ctx, REG_X1)), sizeof(entry->class_name)); if (kext_name) strncpy(entry->kext_name, kext_name, sizeof(entry->kext_name)); else { if (((void *)ctx->map->map_data == (void *)ctx->kimage_mh)) { strncpy(entry->kext_name, "kernel", sizeof(entry->kext_name)); } else { strncpy(entry->kext_name, "unknown", sizeof(entry->kext_name)); } } struct hierarchy_regs_set set = {0}; set.reg_x0 = get_ctx_reg_map_reg(ctx, REG_X0); set.reg_x1 = get_ctx_reg_map_reg(ctx, REG_X1); set.reg_x2 = get_ctx_reg_map_reg(ctx, REG_X2); entry->set = set; if (head) { if (SLIST_EMPTY(head)) { SLIST_INSERT_HEAD(head, entry, entries); prev = entry; } else { SLIST_INSERT_AFTER(prev, entry, entries); prev = entry; } } else { free(entry); } } } break; } } } }