From e093c21ed880ac3eb72119be15093ee04f8ce299 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 5 Mar 2024 19:50:13 -0500 Subject: Move architecture modules into the API repo --- arch/armv7/thumb2_disasm/arch_thumb2.cpp | 1710 ++++++++++++++++++++++++++++++ 1 file changed, 1710 insertions(+) create mode 100644 arch/armv7/thumb2_disasm/arch_thumb2.cpp (limited to 'arch/armv7/thumb2_disasm/arch_thumb2.cpp') diff --git a/arch/armv7/thumb2_disasm/arch_thumb2.cpp b/arch/armv7/thumb2_disasm/arch_thumb2.cpp new file mode 100644 index 00000000..f27a86f7 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arch_thumb2.cpp @@ -0,0 +1,1710 @@ +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +#include "binaryninjaapi.h" + +// registers, etc. +#include "arch_armv7.h" +#include "spec.h" +#include "disassembler.h" +#include "il.h" + +using namespace BinaryNinja; +using namespace armv7; +using namespace std; + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + +/* class Architecture from binaryninjaapi.h */ +class Thumb2Architecture: public ArmCommonArchitecture +{ +protected: + virtual std::string GetAssemblerTriple() override + { + if(m_endian == BigEndian) + return "thumbv7eb-none-none"; + + return "thumbv7-none-none"; + } + + void populateDecomposeRequest(decomp_request *req, const uint8_t *data, size_t len, + uint64_t addr, int inIfThen, int inIfThenLast) + { + req->instr_word16 = 0; + req->instr_word32 = 0; + if(m_endian == LittleEndian) { + req->instr_word16 = *(uint16_t *)data; + if(len >= 4) { + req->instr_word32 = ((*(uint16_t *)data)<<16) | *(uint16_t *)(data + 2); + } + } + else { + req->instr_word16 = (data[0] << 8) | data[1]; + if(len >= 4) { + req->instr_word32 = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + } + } + + req->arch = ARCH_ARMv7; + req->instrSet = INSTRSET_THUMB2; + req->inIfThen = inIfThen; + req->inIfThenLast = inIfThenLast; + req->carry_in = 0; + req->addr = (uint32_t)addr; + } + + virtual bool Disassemble(const uint8_t* data, uint64_t addr, size_t maxLen, decomp_result& result) + { + (void)addr; + (void)maxLen; + decomp_request request; + populateDecomposeRequest(&request, data, maxLen, addr, IFTHEN_UNKNOWN, IFTHENLAST_UNKNOWN); + + memset(&result, 0, sizeof(result)); + if (thumb_decompose(&request, &result) != STATUS_OK) + return false; + return true; + } + +public: + /* initialization list */ + Thumb2Architecture(const char* name, BNEndianness endian): ArmCommonArchitecture(name, endian) + { + } + + /*************************************************************************/ + + virtual size_t GetMaxInstructionLength() const override + { + return 18; // IT blocks can have up to four following associated instructions + } + + virtual size_t GetInstructionAlignment() const override + { + return 2; + } + + virtual size_t GetOpcodeDisplayLength() const override + { + return 4; + } + + /* think "GetInstructionBranchBehavior()" + + populates struct Instruction Info (api/binaryninjaapi.h) + which extends struct BNInstructionInfo (core/binaryninjacore.h) + + tasks: + 1) set the length + 2) invoke AddBranch() for every non-sequential execution possibility + + */ + virtual bool GetInstructionInfo(const uint8_t* data, uint64_t addr, + size_t maxLen, InstructionInfo& result) override + { + decomp_request request; + decomp_result decomp; + + populateDecomposeRequest(&request, data, maxLen, addr, IFTHEN_UNKNOWN, IFTHENLAST_UNKNOWN); + + if (thumb_decompose(&request, &decomp) != STATUS_OK) + return false; + if ((decomp.instrSize / 8) > maxLen) + return false; + if ((decomp.status & STATUS_UNDEFINED) || (!decomp.format)) + return false; + + result.length = decomp.instrSize / 8; + + switch (decomp.mnem) + { + case armv7::ARMV7_LDR: + if ((decomp.format->operands[0].type == OPERAND_FORMAT_REG) && (decomp.fields[decomp.format->operands[0].field0] == 15)) + { + result.AddBranch(UnresolvedBranch); + result.archTransitionByTargetAddr = true; + } + break; + + case ARMV7_LDM: + case ARMV7_LDMDA: + case ARMV7_LDMDB: + case ARMV7_LDMIA: // defaults to ARMV7_LDM + case ARMV7_LDMIB: + if ((decomp.format->operands[0].type == OPERAND_FORMAT_REG) && (decomp.fields[decomp.format->operands[0].field0] == 15)) + { + result.AddBranch(UnresolvedBranch); + result.archTransitionByTargetAddr = true; + } + break; + case armv7::ARMV7_ADD: + case armv7::ARMV7_ADC: + case armv7::ARMV7_EOR: + case armv7::ARMV7_SUB: + case armv7::ARMV7_SBC: + case armv7::ARMV7_RSB: + case armv7::ARMV7_RSC: + case armv7::ARMV7_BIC: + case armv7::ARMV7_ORR: + case armv7::ARMV7_LSL: + case armv7::ARMV7_LSR: + case armv7::ARMV7_ASR: + case armv7::ARMV7_ROR: + case armv7::ARMV7_RRX: + case armv7::ARMV7_MOV: + case armv7::ARMV7_MVN: + case armv7::ARMV7_MOVW: + case armv7::ARMV7_MOVT: + case armv7::ARMV7_LDRT: + case armv7::ARMV7_LDRH: + case armv7::ARMV7_LDRHT: + case armv7::ARMV7_LDRB: + case armv7::ARMV7_LDRBT: + case armv7::ARMV7_LDRSH: + case armv7::ARMV7_LDRSHT: + case armv7::ARMV7_LDRSB: + case armv7::ARMV7_LDRSBT: + case armv7::ARMV7_LDRD: + case armv7::ARMV7_ADR: + case armv7::ARMV7_UBFX: + case armv7::ARMV7_UXTAB: + case armv7::ARMV7_UXTB: + case armv7::ARMV7_UXTH: + case armv7::ARMV7_MUL: + case armv7::ARMV7_SDIV: + case armv7::ARMV7_UDIV: + case armv7::ARMV7_SBFX: + case armv7::ARMV7_SXTB: + case armv7::ARMV7_SXTH: + case armv7::ARMV7_BFC: + case armv7::ARMV7_BFI: + case armv7::ARMV7_CLZ: + if ((decomp.format->operands[0].type == OPERAND_FORMAT_REG) && (decomp.fields[decomp.format->operands[0].field0] == 15)) + result.AddBranch(UnresolvedBranch); + break; + + case armv7::ARMV7_B: + if ((!(decomp.format->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL)) || + (decomp.fields[FIELD_cond] == COND_AL)) { + result.AddBranch(UnconditionalBranch, (decomp.fields[decomp.format->operands[0].field0] + + 4 + addr) & 0xffffffffLL, this); + } else { + result.AddBranch(TrueBranch, (decomp.fields[decomp.format->operands[0].field0] + + 4 + addr) & 0xffffffffLL, this); + result.AddBranch(FalseBranch, (addr + result.length) & 0xffffffffLL, this); + } + break; + + case armv7::ARMV7_BX: + if ((!(decomp.format->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL)) || + (decomp.fields[FIELD_cond] == COND_AL)) { + if ((decomp.format->operands[0].type == OPERAND_FORMAT_LR) || + ((decomp.format->operands[0].type == OPERAND_FORMAT_REG) && + (decomp.fields[decomp.format->operands[0].field0] == 14))) { + result.AddBranch(FunctionReturn); + } else { + result.AddBranch(UnresolvedBranch); + result.archTransitionByTargetAddr = true; + } + } + break; + + case armv7::ARMV7_BL: + if ((!(decomp.format->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL)) || + (decomp.fields[FIELD_cond] == COND_AL)) { + result.AddBranch(CallDestination, (decomp.fields[decomp.format->operands[0].field0] + + 4 + addr) & 0xffffffffLL, this); + } + break; + + case armv7::ARMV7_BLX: + if ((!(decomp.format->operationFlags & INSTR_FORMAT_FLAG_CONDITIONAL)) || + (decomp.fields[FIELD_cond] == COND_AL)) { + if (decomp.format->operands[0].type == OPERAND_FORMAT_IMM) { + uint64_t target; + if (addr & 2) + target = (decomp.fields[decomp.format->operands[0].field0] + 2 + addr) & 0xffffffffLL; + else + target = (decomp.fields[decomp.format->operands[0].field0] + 4 + addr) & 0xffffffffLL; + result.AddBranch(CallDestination, target, m_armArch); + } else if ((decomp.format->operands[0].type == OPERAND_FORMAT_LR) || + ((decomp.format->operands[0].type == OPERAND_FORMAT_REG) && + (decomp.fields[decomp.format->operands[0].field0] == 14))) { + result.AddBranch(FunctionReturn); // initially indicate "blx lr" as a return since this is common and conservative; subsequent analysis determines if it's a function call + } + result.archTransitionByTargetAddr = true; + } + break; + + case armv7::ARMV7_CBNZ: + case armv7::ARMV7_CBZ: + result.AddBranch(TrueBranch, (decomp.fields[decomp.format->operands[1].field0] + addr) & 0xffffffffLL, this); + result.AddBranch(FalseBranch, (addr + result.length) & 0xffffffffLL, this); + break; + + case armv7::ARMV7_POP: + if ((decomp.format->operands[0].type == OPERAND_FORMAT_REGISTERS) && + (decomp.fields[FIELD_registers] & (1 << 15))) { + result.AddBranch(FunctionReturn); + } + break; + + case armv7::ARMV7_SVC: + result.AddBranch(SystemCall); + break; + + case armv7::ARMV7_TBB: + case armv7::ARMV7_TBH: + result.AddBranch(UnresolvedBranch); + break; + + case armv7::ARMV7_UDF: + result.AddBranch(ExceptionBranch); + break; + + default: + break; + } + + return true; + } + + /* populate the vector result with InstructionTextToken + + */ + virtual bool GetInstructionText(const uint8_t* data, uint64_t addr, size_t& len, vector& result) override + { + decomp_request request; + decomp_result decomp; + + populateDecomposeRequest(&request, data, len, addr, IFTHEN_UNKNOWN, IFTHENLAST_UNKNOWN); + + if (thumb_decompose(&request, &decomp) != STATUS_OK) + return false; + + if (decomp.status & STATUS_UNDEFINED) { + len = decomp.instrSize / 8; + result.emplace_back(InstructionToken, "undefined"); + return true; + } + + if ((decomp.instrSize / 8) > len) + return false; + + if (!decomp.format) + return false; + + char padding[9]; + memset(padding, 0x20, sizeof(padding)); + string operation = get_thumb_operation_name(&decomp); + size_t operationLen = operation.size(); + if (operationLen < 8) + { + padding[8-operationLen] = '\0'; + } + else + padding[1] = '\0'; + + result.emplace_back(InstructionToken, operation); + if (decomp.format->operandCount > 0) + result.emplace_back(TextToken, padding); + + for (size_t i = 0; i < decomp.format->operandCount; i++) + { + int j; + const instruction_operand_format& operand = decomp.format->operands[i]; + uint32_t value, r, bits, shift_t, shift_n, add, imm32, reg; + char buf[16]; + char offset[32]; + char regname[16]; + char secondname[16]; + bool first; + + switch (operand.type) + { + case OPERAND_FORMAT_MEMORY_ONE_REG: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_ONE_REG_IMM: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(value) { + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "#"); + + if (value < 10) + snprintf(offset, sizeof(offset), "%d", value); + else + snprintf(offset, sizeof(offset), "0x%x", value); + result.emplace_back(IntegerToken, offset, value); + } + + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_ONE_REG_NEG_IMM: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "#"); + + value = decomp.fields[operand.field1]; + if (value < 10) + snprintf(offset, sizeof(offset), "-%d", value); + else + snprintf(offset, sizeof(offset), "-0x%x", value); + result.emplace_back(IntegerToken, offset, -(int64_t)value); + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_ONE_REG_ADD_IMM: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) + strcpy(regname, "undefined"); + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(decomp.fields[FIELD_add] && value==0) { + /* omit the case where we are adding 0 */ + while(0); + } + else { + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "#"); + + const char *fmt; + if(decomp.fields[FIELD_add]) + fmt = (value < 10) ? "%d":"0x%x"; + else + fmt = (value < 10) ? "-%d":"-0x%x"; + + snprintf(offset, sizeof(offset), fmt, value); + result.emplace_back(IntegerToken, offset, -(int64_t)value); + } + + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_ONE_REG_OPTIONAL_IMM: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if (value != 0) { + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "#"); + if (value < 10) + snprintf(offset, sizeof(offset), "%d", value); + else + snprintf(offset, sizeof(offset), "0x%x", value); + result.emplace_back(IntegerToken, offset, value); + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_ONE_REG_OPTIONAL_ADD_IMM: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(!(decomp.fields[FIELD_add] && value == 0)) { + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "#"); + if(decomp.fields[FIELD_add]) { + if (value < 10) + snprintf(offset, sizeof(offset), "%d", value); + else + snprintf(offset, sizeof(offset), "0x%x", value); + result.emplace_back(IntegerToken, offset, value); + } else { + if (value < 10) + snprintf(offset, sizeof(offset), "-%d", value); + else + snprintf(offset, sizeof(offset), "-0x%x", value); + result.emplace_back(IntegerToken, offset, -(int64_t)value); + } + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_TWO_REG: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(0 != get_reg_name(value, secondname)) { + strcpy(secondname, "undefined"); + } + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(RegisterToken, secondname); + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_TWO_REG_SHIFT: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(0 != get_reg_name(value, secondname)) { + strcpy(secondname, "undefined"); + } + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(RegisterToken, secondname); + + shift_t = decomp.fields[FIELD_shift_t]; + shift_n = decomp.fields[FIELD_shift_n]; + + if(shift_n != 0) { + result.emplace_back(OperandSeparatorToken, ", "); + if(shift_t == SRType_LSL) { + snprintf(offset, sizeof(offset), "%d", shift_n); + result.emplace_back(TextToken, "lsl #"); + result.emplace_back(IntegerToken, offset, shift_n); + } else if(shift_t == SRType_LSR) { + snprintf(offset, sizeof(offset), "%d", shift_n); + result.emplace_back(TextToken, "lsr #"); + result.emplace_back(IntegerToken, offset, shift_n); + } else if(shift_t == SRType_ASR) { + snprintf(offset, sizeof(offset), "%d", shift_n); + result.emplace_back(TextToken, "asr #"); + result.emplace_back(IntegerToken, offset, shift_n); + } else if(shift_t == SRType_RRX) { + if(shift_n != 1) { + snprintf(offset, sizeof(offset), "%d", shift_n); + result.emplace_back(TextToken, "rrx #"); + result.emplace_back(IntegerToken, offset, shift_n); + } + else { + result.emplace_back(TextToken, "rrx"); + } + } else if(shift_t == SRType_ROR) { + snprintf(offset, sizeof(offset), "%d", shift_n); + result.emplace_back(TextToken, "ror #"); + result.emplace_back(IntegerToken, offset, shift_n); + } else { + result.emplace_back(TextToken, "undefined"); + } + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_ROTATION: + value = decomp.fields[FIELD_rotation]; + + if(value) { + if(i>0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "ror #"); + snprintf(buf, sizeof(buf), "%d", value); + result.emplace_back(IntegerToken, buf, 1); + } + + break; + + case OPERAND_FORMAT_MEMORY_TWO_REG_LSL_ONE: + value = decomp.fields[operand.field0]; + if(0 != get_reg_name(value, regname)) { + strcpy(regname, "undefined"); + } + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, regname); + + value = decomp.fields[operand.field1]; + if(0 != get_reg_name(value, secondname)) { + strcpy(secondname, "undefined"); + } + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(RegisterToken, secondname); + result.emplace_back(TextToken, ", lsl #"); + result.emplace_back(IntegerToken, "1", 1); + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_SP_IMM: + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, "sp"); + + value = decomp.fields[operand.field0]; + if(value) { + result.emplace_back(TextToken, ", #"); + + if (value < 10) + snprintf(offset, sizeof(offset), "%d", value); + else + snprintf(offset, sizeof(offset), "0x%x", value); + result.emplace_back(IntegerToken, offset, value); + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_SP_OPTIONAL_IMM: + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(BeginMemoryOperandToken, "["); + result.emplace_back(RegisterToken, "sp"); + + value = decomp.fields[operand.field0]; + if (value != 0) { + result.emplace_back(TextToken, ", #"); + if (value < 10) + snprintf(offset, sizeof(offset), "%d", value); + else + snprintf(offset, sizeof(offset), "0x%x", value); + result.emplace_back(IntegerToken, offset, value); + } + result.emplace_back(EndMemoryOperandToken, "]"); + break; + + case OPERAND_FORMAT_MEMORY_PC: + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + result.emplace_back(TextToken, "["); + result.emplace_back(RegisterToken, "pc"); + result.emplace_back(TextToken, "]"); + break; + + case OPERAND_FORMAT_IMM64: /* 64 bit immediate fields */ + if (i > 0) + result.emplace_back(OperandSeparatorToken, ", "); + if(IS_FIELD_PRESENT(&decomp, FIELD_imm64h) && IS_FIELD_PRESENT(&decomp, FIELD_imm64l)){ + uint64_t imm64 = 0; + imm64 |= decomp.fields[FIELD_imm64h]; + imm64 <<= 32; + imm64 |= decomp.fields[FIELD_imm64l]; + /* this will be '#' for lone numerals, 'p' for coprocessor, etc. */ + if (operand.prefix[0] != 0) + result.emplace_back(TextToken, operand.prefix); + + if(imm64 < 10) + snprintf(offset, sizeof(offset), "%" PRIu64, imm64); + else + snprintf(offset, sizeof(offset), "0x%" PRIx64, imm64); + result.emplace_back(IntegerToken, offset, imm64); + } + /* could be closing '}' for stuff like coprocessor {