├── .gitignore ├── README.md ├── install.sh ├── main.py ├── requirements.txt └── test.inst /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arm64-emulator 2 | (Work in Progress) CLI arm64 emulator using unicorn 3 | 4 | ## Usage 5 | Edit the assembly code in `test.inst` and run `python main.py` 6 | 7 | Example output: 8 | 9 | ```bash 10 | >>> Tracing basic block at 0x10000, block size = 0x44 11 | >>> Tracing instruction at 0x10000, instruction size = 0x4 12 | >>> Emulation done. 13 | 14 | >>> Registers 15 | X0: 0x124 X1: 0x124 X2: 0x1 X3: 0x1 16 | X4: 0x123456789abcdef X5: 0x0 X6: 0x0 X7: 0x0 17 | X8: 0x0 X9: 0x0 X10: 0x0 X11: 0x0 18 | X12: 0x0 X13: 0x0 X14: 0x0 X15: 0x15 19 | X16: 0x0 X17: 0x0 X18: 0x0 X19: 0x0 20 | X20: 0x0 X21: 0x0 X22: 0x0 X23: 0x0 21 | X24: 0x0 X25: 0x0 X26: 0x0 X27: 0x0 22 | X28: 0x0 V0: 0x0 SP: 0x210000 PC: 0x10044 23 | 24 | >>> Stack 0x210000 25 | 00000000: EF CD AB 89 67 45 23 01 EF CD AB 89 67 45 23 01 ....gE#.....gE#. 26 | 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 27 | 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 28 | 00000030: EF CD AB 89 67 45 23 01 EF CD AB 89 67 45 23 01 ....gE#.....gE#. 29 | 00000040: EF CD AB 89 67 45 23 01 EF CD AB 89 67 45 23 01 ....gE#.....gE#. 30 | 00000050: EF CD AB 89 67 45 23 01 EF CD AB 89 67 45 23 01 ....gE#.....gE#. 31 | 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 32 | 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 33 | ``` 34 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | pip3 install -r requirements.txt 2 | #brew install unicorn 3 | #DYLD_LIBRARY_PATH=/usr/local/Cellar/unicorn/1.0.1 pip install unicorn --user 4 | #DYLD_LIBRARY_PATH=/usr/local/Cellar/keystone/0.9.1 pip install keystone-engine --user 5 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import frontmatter 3 | from hexdump import hexdump 4 | 5 | from unicorn import * 6 | from unicorn.arm64_const import * 7 | from keystone import * 8 | 9 | 10 | # Needs unicorn >= 1.0.2-rc1 11 | 12 | def get_register_for_key(key): 13 | return KEY_TO_REG[key] 14 | 15 | def get_key_for_reg(reg): 16 | for key, r in KEY_TO_REG.items(): 17 | if r == reg: 18 | return key 19 | 20 | def get_all_registers(): 21 | return KEY_TO_REG.values() 22 | 23 | KEY_TO_REG = { 24 | "X0": UC_ARM64_REG_X0, 25 | "X1": UC_ARM64_REG_X1, 26 | "X2": UC_ARM64_REG_X2, 27 | "X3": UC_ARM64_REG_X3, 28 | "X4": UC_ARM64_REG_X4, 29 | "X5": UC_ARM64_REG_X5, 30 | "X6": UC_ARM64_REG_X6, 31 | "X7": UC_ARM64_REG_X7, 32 | "X8": UC_ARM64_REG_X8, 33 | "X9": UC_ARM64_REG_X9, 34 | "X10": UC_ARM64_REG_X10, 35 | "X11": UC_ARM64_REG_X11, 36 | "X12": UC_ARM64_REG_X12, 37 | "X13": UC_ARM64_REG_X13, 38 | "X14": UC_ARM64_REG_X14, 39 | "X15": UC_ARM64_REG_X15, 40 | "X16": UC_ARM64_REG_X16, 41 | "X17": UC_ARM64_REG_X17, 42 | "X18": UC_ARM64_REG_X18, 43 | "X19": UC_ARM64_REG_X19, 44 | "X20": UC_ARM64_REG_X20, 45 | "X21": UC_ARM64_REG_X21, 46 | "X22": UC_ARM64_REG_X22, 47 | "X23": UC_ARM64_REG_X23, 48 | "X24": UC_ARM64_REG_X24, 49 | "X25": UC_ARM64_REG_X25, 50 | "X26": UC_ARM64_REG_X26, 51 | "X27": UC_ARM64_REG_X27, 52 | "X28": UC_ARM64_REG_X28, 53 | "V0": UC_ARM64_REG_V0, 54 | } 55 | 56 | # TODO use this 57 | def hook_block(uc, address, size, user_data): 58 | print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) 59 | 60 | def hook_code(uc, address, size, user_data): 61 | print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(address, size)) 62 | 63 | 64 | 65 | # memory address where emulation starts 66 | ADDRESS = 0x10000 67 | 68 | class Emulator(object): 69 | def __init__(self): 70 | self.mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM) 71 | self.ks = Ks(KS_ARCH_ARM64, KS_MODE_LITTLE_ENDIAN) 72 | 73 | self.registers = {} 74 | self.asm = ""; 75 | 76 | def load_instructions(self, path): 77 | data = frontmatter.load(path) 78 | for k,v in data.to_dict().items(): 79 | if k == "content": 80 | self._set_asm(v) 81 | else: 82 | self.registers[k] = v 83 | 84 | def _set_asm(self, asm): 85 | self.asm = asm 86 | encoding, count = self.ks.asm(asm) 87 | self.code = bytes(encoding) 88 | self.code_len = len(self.code) 89 | 90 | def _get_sp(self): 91 | return self.mu.reg_read(UC_ARM64_REG_SP) 92 | 93 | def print_state(self): 94 | print(">>> Registers") 95 | self.print_registers() 96 | print(">>> Stack 0x%x" % self._get_sp()) 97 | self.print_mem(self._get_sp()) 98 | 99 | def print_registers(self): 100 | CELL_WIDTH = 20 101 | register_rows = [(get_key_for_reg(x), self.mu.reg_read(x)) for x in sorted(get_all_registers())] 102 | register_rows.append(("SP",self.mu.reg_read(UC_ARM64_REG_SP))) 103 | register_rows.append(("PC",self.mu.reg_read(UC_ARM64_REG_PC))) 104 | s = "" 105 | for i in range(len(register_rows)): 106 | name, val = register_rows[i] 107 | cell = str(name) + ": " + hex(val) 108 | cell += (CELL_WIDTH - len(cell)) * ' ' 109 | s += cell + '\t' 110 | if i % 4 == 3: 111 | s += '\n' 112 | print (s) 113 | 114 | def print_mem(self, addr, size=128): 115 | mem = self.mu.mem_read(addr, size) 116 | mem = bytes(mem) 117 | hexdump(mem) 118 | 119 | def start(self): 120 | self.mu.mem_map(ADDRESS, 2 * 1024 * 1024) 121 | self.mu.mem_write(ADDRESS, self.code) 122 | [self.mu.reg_write(reg, 0) for reg in get_all_registers()] # TODO: is this needed? 123 | [self.mu.reg_write(get_register_for_key(reg), val) for reg,val in self.registers.items()] 124 | 125 | self.mu.mem_map(ADDRESS + 0x200000, 2 * 1024 * 1024) 126 | self.mu.reg_write(UC_ARM64_REG_SP, ADDRESS + 0x200000) 127 | 128 | self.mu.hook_add(UC_HOOK_BLOCK, hook_block) 129 | self.mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS) 130 | 131 | # https://github.com/unicorn-engine/unicorn/issues/940 132 | x = self.mu.reg_read(UC_ARM64_REG_CPACR_EL1) 133 | x |= 0x300000; # set FPEN bit 134 | self.mu.reg_write(UC_ARM64_REG_CPACR_EL1, x); 135 | 136 | try: 137 | self.mu.emu_start(ADDRESS, ADDRESS + self.code_len) 138 | except UcError as e: 139 | print("ERROR: %s" % e) 140 | 141 | print (">>> Emulation done.\n") 142 | self.print_state() 143 | 144 | 145 | def main(): 146 | e = Emulator() 147 | e.load_instructions('test.inst') 148 | e.start() 149 | 150 | if __name__ == '__main__': 151 | main() 152 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | unicorn>=1.0.2-rc1 2 | keystone 3 | hexdump 4 | python-frontmatter 5 | -------------------------------------------------------------------------------- /test.inst: -------------------------------------------------------------------------------- 1 | --- 2 | X0: 0x123 3 | X2: 0x1 4 | X3: 0x1 5 | X4: 0x123456789abcdef 6 | X15: 0x15 7 | --- 8 | add x0, x0, #1 9 | str x0, [sp, #0x8] 10 | ldr w1, [sp, #0x8] 11 | 12 | 13 | 14 | STUR X4, [SP] 15 | STUR X4, [SP,#0x8] 16 | STUR X4, [SP,#0x10] 17 | STUR X4, [SP,#0x18] 18 | STUR X4, [SP,#0x20] 19 | STUR X4, [SP,#0x28] 20 | STUR X4, [SP,#0x30] 21 | STUR X4, [SP,#0x38] 22 | STUR X4, [SP,#0x40] 23 | STUR X4, [SP,#0x48] 24 | STUR X4, [SP,#0x50] 25 | STUR X4, [SP,#0x58] 26 | 27 | MOVI V0.2D, #0 28 | STP Q0, Q0, [sp,#0x10] 29 | --------------------------------------------------------------------------------