import unittest import struct from functools import wraps from manticore.core.cpu.arm import Armv7Cpu as Cpu, Mask, Interruption from manticore.core.memory import Memory32 from capstone.arm import * from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM ks = Ks(KS_ARCH_ARM, KS_MODE_ARM) import logging logger = logging.getLogger("ARM_TESTS") def assemble(asm): ords = ks.asm(asm)[0] if not ords: raise Exception('bad assembly: {}'.format(asm)) return ''.join(map(chr, ords)) class Armv7CpuTest(unittest.TestCase): def setUp(self): self.c = Cpu(Memory32()) self.rf = self.c.regfile self._setupStack() def _setupStack(self): self.stack = self.c.memory.mmap(0xf000, 0x1000, 'rw') self.rf.write('SP', self.stack + 0x1000) def test_rd(self): self.assertEqual(self.rf.read('R0'), 0) def test_rd2(self): self.c.STACK = 0x1337 self.assertEqual(self.rf.read('SP'), 0x1337) def test_stack_set_get(self): self.c.STACK = 0x1337 self.assertEqual(self.c.STACK, 0x1337) def test_rd3(self): self.c.STACK = 0x1337 - 1 self.assertEqual(self.rf.read('SP'), 0x1336) def test_rd4(self): self.c.STACK = 0x1337 + 1 self.assertEqual(self.rf.read('SP'), 0x1338) def test_stack_push(self): self.c.stack_push(42) self.c.stack_push(44) self.assertItemsEqual(self.c.read(self.c.STACK, 4), '\x2c\x00\x00\x00') self.assertItemsEqual(self.c.read(self.c.STACK + 4, 4), '\x2a\x00\x00\x00') def test_stack_pop(self): v = 0x55 v_bytes = struct.pack('> 3)) @itest_custom("add r3, r1, r2, asr #3") def test_add_reg_sft_asr(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x80000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0xf0000000) @itest_custom("add r3, r1, r2, asr #3") def test_add_reg_sft_asr2(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x40000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) @itest_custom("add r3, r1, r2, ror #3") def test_add_reg_sft_ror_norm(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x8) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0x1) @itest_custom("add r3, r1, r2, ror #3") def test_add_reg_sft_ror(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x3) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0x60000000) @itest_custom("adc r3, r1, r2") @itest_setregs("R1=1", "R2=2", "APSR_C=1") def test_adc_basic(self): self.cpu.execute() self.assertEqual(self.rf.read('R3'), 4) @itest_custom("adc r3, r1, r2, ror #3") @itest_setregs("R1=1", "R2=2", "APSR_C=1") def test_adc_reg_sft_ror(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x3) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0x60000001) # TODO what is shifter_carry_out in the manual, A8-291? it gets set to # Bit[0] presumably, but i have no clue what it is. Not mentioned again in # manual. @itest_custom("add r3, r1, r2, rrx") def test_add_reg_sft_rrx(self): self.rf.write('APSR_C', 0x0) self.rf.write('R1', 0x0) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) @itest_custom("add r3, r1, r2, rrx") def test_add_reg_sft_rrx2(self): self.rf.write('APSR_C', 0x1) self.rf.write('R1', 0x0) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) @itest_custom("add r3, r1, r2, lsl r4") def test_add_reg_sft_lsl_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (1 << 3)) @itest_custom("add r3, r1, r2, lsr r4") def test_add_reg_sft_lsr_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x8) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (0x8 >> 3)) @itest_custom("add r3, r1, r2, asr r4") def test_add_reg_sft_asr_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x80000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0xf0000000) @itest_custom("add r3, r1, r2, asr r4") def test_add_reg_sft_asr2_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x40000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) @itest_custom("add r3, r1, r2, ror r4") def test_add_reg_sft_ror_norm_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x8) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 1) @itest_custom("add r3, r1, r2, ror r4") def test_add_reg_sft_ror_reg(self): self.rf.write('R1', 0x0) self.rf.write('R4', 0x3) self.rf.write('R2', 0x3) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0x60000000) @itest_custom("add r3, r1, r2, rrx") def test_add_reg_sft_rrx_reg(self): self.rf.write('R1', 0x0) self.rf.write('APSR_C', 0x0) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) @itest_custom("add r3, r1, r2, rrx") def test_add_reg_sft_rrx2_reg(self): self.rf.write('R1', 0x0) self.rf.write('APSR_C', 0x1) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) # ADDS @itest_custom("adds r3, r1, 55") def test_adds_imm_norm(self): self.rf.write('R1', 44) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 99) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, 0x100") def test_adds_imm_mod_imm_min(self): self.rf.write('R1', 44) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 44 + 0x100) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, 0xff000000") def test_adds_imm_mod_imm_max(self): self.rf.write('R1', 44) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) self._checkFlagsNZCV(1, 0, 0, 0) @itest_custom("adds r3, r1, 0x1000000") def test_adds_imm_carry(self): self.rf.write('R1', 0xff000001) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 1) self._checkFlagsNZCV(0, 0, 1, 0) @itest_custom("adds r3, r1, 0x80000000") def test_adds_imm_carry_overflow(self): self.rf.write('R1', 0x80000001) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 1) self._checkFlagsNZCV(0, 0, 1, 1) @itest_custom("adds r3, r1, 0x1") def test_adds_imm_overflow(self): self.rf.write('R1', (2 ** 31 - 1)) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0x80000000) self._checkFlagsNZCV(1, 0, 0, 1) @itest_custom("adds r3, r3, 0x0") def test_adds_imm_zf(self): self.rf.write('R3', 0) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0) self._checkFlagsNZCV(0, 1, 0, 0) @itest_custom("adds r3, r1, r2") def test_adds_reg_norm(self): self.rf.write('R1', 44) self.rf.write('R2', 55) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 99) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, r2") def test_adds_reg_mod_imm_min(self): self.rf.write('R1', 44) self.rf.write('R2', 0x100) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 44 + 0x100) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, r2") def test_adds_reg_mod_imm_max(self): self.rf.write('R1', 44) self.rf.write('R2', 0xff000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 44 + 0xff000000) self._checkFlagsNZCV(1, 0, 0, 0) @itest_custom("adds r3, r1, r2") def test_adds_reg_carry(self): self.rf.write('R1', 0x1000000) self.rf.write('R2', 0xff000001) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 1) self._checkFlagsNZCV(0, 0, 1, 0) @itest_custom("adds r3, r1, r2") def test_adds_reg_overflow(self): self.rf.write('R1', (2 ** 31 - 1)) self.rf.write('R2', 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (1 << 31)) self._checkFlagsNZCV(1, 0, 0, 1) @itest_custom("adds r3, r1, r2") def test_adds_reg_carry_overflow(self): self.rf.write('R1', 0x80000001) self.rf.write('R2', 0x80000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 1) self._checkFlagsNZCV(0, 0, 1, 1) @itest_custom("adds r3, r1, r2") def test_adds_reg_zf(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x0) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0) self._checkFlagsNZCV(0, 1, 0, 0) @itest_custom("adds r3, r1, r2, asr #3") def test_adds_reg_sft_asr(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x80000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 0xf0000000) self._checkFlagsNZCV(1, 0, 0, 0) @itest_custom("adds r3, r1, r2, asr #3") def test_adds_reg_sft_asr2(self): self.rf.write('R1', 0x0) self.rf.write('R2', 0x40000000) self.cpu.execute() self.assertEqual(self.rf.read('R3'), (0x40000000 >> 3)) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, r2, rrx") def test_adds_reg_sft_rrx(self): self.rf.write('APSR_C', 0x0) self.rf.write('R1', 0x0) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 31 - 1) self._checkFlagsNZCV(0, 0, 0, 0) @itest_custom("adds r3, r1, r2, rrx") def test_adds_reg_sft_rrx2(self): self.rf.write('APSR_C', 0x1) self.rf.write('R1', 0x0) self.rf.write('R2', 2 ** 32 - 1) self.cpu.execute() self.assertEqual(self.rf.read('R3'), 2 ** 32 - 1) self._checkFlagsNZCV(1, 0, 0, 0) # LDR imm @itest_custom("ldr r1, [sp]") def test_ldr_imm_off_none(self): self.cpu.stack_push(42) self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) @itest_custom("ldr r1, [sp, #4]") def test_ldr_imm_off_pos(self): self.cpu.stack_push(42) self.cpu.stack_push(41) self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) @itest_custom("ldr r1, [sp, #-4]") def test_ldr_imm_off_neg(self): self.cpu.stack_push(42) self.cpu.stack_push(41) self.cpu.STACK += 4 self.cpu.execute() self.assertEqual(self.rf.read('R1'), 41) @itest_custom("ldr r1, [sp, #4]!") def test_ldr_imm_preind_pos(self): self.cpu.stack_push(42) self.cpu.stack_push(41) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack + 4) @itest_custom("ldr r1, [sp, #-4]!") def test_ldr_imm_preind_neg(self): self.cpu.stack_push(42) self.cpu.stack_push(41) self.cpu.STACK += 4 pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 41) self.assertEqual(self.rf.read('SP'), pre_stack - 4) @itest_custom("ldr r1, [sp], #5") def test_ldr_imm_postind_pos(self): self.cpu.stack_push(42) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack + 5) @itest_custom("ldr r1, [sp], #-5") def test_ldr_imm_postind_neg(self): self.cpu.stack_push(42) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack - 5) # LDR reg @itest_custom("ldr r1, [sp, r2]") def test_ldr_reg_off(self): self.cpu.regfile.write('R2', 4) self.cpu.stack_push(42) self.cpu.stack_push(48) self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) @itest_custom("ldr r1, [sp, -r2]") def test_ldr_reg_off_neg(self): self.cpu.regfile.write('R2', 4) self.cpu.stack_push(42) self.cpu.stack_push(48) self.cpu.STACK += 4 self.cpu.execute() self.assertEqual(self.rf.read('R1'), 48) @itest_custom("ldr r1, [sp, r2, lsl #3]") def test_ldr_reg_off_shift(self): self.cpu.regfile.write('R2', 1) self.cpu.stack_push(42) self.cpu.stack_push(48) self.cpu.stack_push(40) self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) @itest_custom("ldr r1, [sp, -r2, lsl #3]") def test_ldr_reg_off_neg_shift(self): self.cpu.regfile.write('R2', 1) self.cpu.stack_push(42) self.cpu.stack_push(48) self.cpu.STACK += 8 self.cpu.execute() self.assertEqual(self.rf.read('R1'), 48) @itest_custom("ldr r1, [sp, r2]!") def test_ldr_reg_preind(self): self.cpu.regfile.write('R2', 4) self.cpu.stack_push(42) self.cpu.stack_push(48) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack + 4) @itest_custom("ldr r1, [sp, -r2, lsl #3]!") def test_ldr_reg_preind_shift(self): self.cpu.regfile.write('R2', 1) self.cpu.stack_push(42) self.cpu.stack_push(48) self.cpu.STACK += 8 pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 48) self.assertEqual(self.rf.read('SP'), pre_stack - 8) @itest_custom("ldr r1, [sp], r2") def test_ldr_reg_postind(self): self.cpu.regfile.write('R2', 4) self.cpu.stack_push(42) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack + 4) @itest_custom("ldr r1, [sp], -r2, lsl #3") def test_ldr_reg_postind_neg_shift(self): self.cpu.regfile.write('R2', 1) self.cpu.stack_push(42) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 42) self.assertEqual(self.rf.read('SP'), pre_stack - 8) @itest_custom("pop {r1}") def test_pop_one_reg(self): self.cpu.stack_push(0x55) pre_stack = self.cpu.STACK self.cpu.execute() self.assertEqual(self.rf.read('R1'), 0x55) self.assertEqual(self.rf.read('SP'), pre_stack + 4) @itest_custom("pop {r1, r2, r3}") def test_pop_multops(self): vals = [0x01, 0x55, 0xAA] for v in vals: self.cpu.stack_push(v) self.cpu.execute() self.assertEqual(self.rf.read('R1'), 0xAA) self.assertEqual(self.rf.read('R2'), 0x55) self.assertEqual(self.rf.read('R3'), 0x01) @itest_custom("push {r1}") @itest_setregs("R1=3") def test_push_one_reg(self): self.cpu.execute() self.assertItemsEqual(self.cpu.stack_peek(), struct.pack('> 3) @itest_setregs("R0=0x1000") @itest("lsr r0, r0, #3") def test_lsr_reg_imm(self): self.assertEqual(self.rf.read('R0'), 0x1000 >> 3) @itest_setregs("R2=29") @itest("RSB r2, r2, #31") def test_rsb_imm(self): # Diverging instruction from trace self.assertEqual(self.rf.read('R2'), 2) def test_flag_state_continuity(self): '''If an instruction only partially updates flags, cpu.setFlags should ensure unupdated flags are preserved. For example: r1 = 2**31 - 1 add r2, r1, 0x1 // overflow = 1 mov r1, 1 mov r3, 0 tst r3, r1 // does not change overflow flag // ovf should still be 1 ''' self.rf.write('R1', (2 ** 31 - 1)) self._setupCpu("adds r2, r1, #0x1") self.cpu.execute() self.rf.write('R1', 1) self.rf.write('R3', 0) self.mem.write(self.cpu.PC, assemble("tst r3, r1")) self.cpu.execute() self._checkFlagsNZCV(0, 1, 0, 1) @itest_setregs("R1=30", "R2=10") @itest("MUL R1, R2") def test_mul_reg(self): self.assertEqual(self.rf.read('R1'), 300) @itest_setregs("R1=30", "R2=10") @itest("MUL R3, R1, R2") def test_mul_reg_w_dest(self): self.assertEqual(self.rf.read('R3'), 300) @itest_setregs("R2=10", "R3=15", "R4=7") @itest("MLA R1, R2, R3, R4") def test_mla_reg(self): self.assertEqual(self.rf.read('R1'), 157) @itest_setregs("R1=0xFF") @itest("BIC R2, R1, #0x10") def test_bic_reg_imm(self): self.assertEqual(self.rf.read('R2'), 0xEF) @itest_setregs("R1=0x1008") @itest("BLX R1") def test_blx_reg(self): self.assertEqual(self.rf.read('PC'), 0x1008) self.assertEqual(self.rf.read('LR'), 0x1008) @itest_setregs("R1=0x1009") @itest("BLX R1") def test_blx_reg_thumb(self): self.assertEqual(self.rf.read('PC'), 0x1008) self.assertEqual(self.rf.read('LR'), 0x1008) @itest_setregs("R1=0xffffffff", "R2=2") @itest("UMULLS R1, R2, R1, R2") def test_umull(self): mul = 0xffffffff * 2 pre_c = self.rf.read('APSR_C') pre_v = self.rf.read('APSR_V') self.assertEqual(self.rf.read('R1'), mul & Mask(32)) self.assertEqual(self.rf.read('R2'), mul >> 32) self._checkFlagsNZCV(0, 0, pre_c, pre_v) @itest_setregs("R1=2", "R2=2") @itest("UMULLS R1, R2, R1, R2") def test_umull_still32(self): mul = 2 * 2 pre_c = self.rf.read('APSR_C') pre_v = self.rf.read('APSR_V') self.assertEqual(self.rf.read('R1'), mul & Mask(32)) self.assertEqual(self.rf.read('R2'), mul >> 32) self._checkFlagsNZCV(0, 0, pre_c, pre_v) @itest_setregs("R1=0xfffffffe", "R2=0xfffffffe") @itest("UMULLS R1, R2, R1, R2") def test_umull_max(self): mul = 0xfffffffe ** 2 pre_c = self.rf.read('APSR_C') pre_v = self.rf.read('APSR_V') self.assertEqual(self.rf.read('R1'), mul & Mask(32)) self.assertEqual(self.rf.read('R2'), mul >> 32) self._checkFlagsNZCV(1, 0, pre_c, pre_v) @itest_setregs("R1=3", "R2=0") @itest("UMULLS R1, R2, R1, R2") def test_umull_z(self): mul = 3 * 0 pre_c = self.rf.read('APSR_C') pre_v = self.rf.read('APSR_V') self.assertEqual(self.rf.read('R1'), mul & Mask(32)) self.assertEqual(self.rf.read('R2'), (mul >> 32) & Mask(32)) self._checkFlagsNZCV(0, 1, pre_c, pre_v) @itest("dmb ish") def test_dmb(self): # This is a nop, ensure that the instruction exists self.assertTrue(True)