|
| 1 | +/* |
| 2 | + Created for Unicorn Engine by Glenn Baker <[email protected]>, 2024 |
| 3 | +*/ |
| 4 | + |
| 5 | +/* Sample code to demonstrate how to emulate AVR code */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include <string.h> |
| 9 | +#include <unicorn/unicorn.h> |
| 10 | + |
| 11 | +// Code to be emulated |
| 12 | +static const uint32_t CODE_BASE = 0x0000; |
| 13 | +static const uint8_t CODE[] = |
| 14 | + "\x86\x0f" // add r24, r22 |
| 15 | + "\x97\x1f" // adc r25, r23 |
| 16 | + "\x88\x0f" // add r24, r24 |
| 17 | + "\x99\x1f" // adc r25, r25 |
| 18 | + "\x01\x96" // adiw r24, 0x01 |
| 19 | + "\x08\x95" // ret |
| 20 | + ; |
| 21 | +static const uint32_t CODE_SIZE = sizeof(CODE) - 1; |
| 22 | +static const uint32_t CODE_SIZE_ALIGNED = (CODE_SIZE + 0xff) & -0x100; |
| 23 | + |
| 24 | +static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, |
| 25 | + void *user_data) |
| 26 | +{ |
| 27 | + printf(">>> Tracing basic block at 0x%" PRIx64 ", block size = 0x%x\n", |
| 28 | + address, size); |
| 29 | +} |
| 30 | + |
| 31 | +static void hook_code(uc_engine *uc, uint64_t address, uint32_t size, |
| 32 | + void *user_data) |
| 33 | +{ |
| 34 | + printf(">>> Tracing instruction at 0x%" PRIx64 |
| 35 | + ", instruction size = 0x%x\n", |
| 36 | + address, size); |
| 37 | +} |
| 38 | + |
| 39 | +static bool is_error(uc_err err, const char *what) |
| 40 | +{ |
| 41 | + if (err != UC_ERR_OK) { |
| 42 | + fprintf(stderr, "error: failed on %s() with error %u: %s\n", |
| 43 | + what, err, uc_strerror(err)); |
| 44 | + return true; |
| 45 | + } |
| 46 | + return false; |
| 47 | +} |
| 48 | + |
| 49 | +static bool test_avr(void) |
| 50 | +{ |
| 51 | + uc_engine *uc = NULL; |
| 52 | + uc_hook trace1, trace2; |
| 53 | + bool success = false; |
| 54 | + |
| 55 | + printf("Emulate AVR code\n"); |
| 56 | + do { |
| 57 | + // Initialize emulator in AVR mode |
| 58 | + uc_err err = uc_open(UC_ARCH_AVR, UC_MODE_LITTLE_ENDIAN, &uc); |
| 59 | + if (is_error(err, "uc_open")) |
| 60 | + break; |
| 61 | + |
| 62 | + // Map program code |
| 63 | + err = uc_mem_map(uc, CODE_BASE, CODE_SIZE_ALIGNED, UC_PROT_READ|UC_PROT_EXEC); |
| 64 | + if (is_error(err, "uc_mem_map")) |
| 65 | + break; |
| 66 | + |
| 67 | + // Write machine code to be emulated to memory |
| 68 | + err = uc_mem_write(uc, CODE_BASE, CODE, CODE_SIZE); |
| 69 | + if (is_error(err, "uc_mem_write")) |
| 70 | + break; |
| 71 | + |
| 72 | + // Tracing all basic blocks with customized callback |
| 73 | + err = uc_hook_add(uc, &trace1, UC_HOOK_BLOCK, hook_block, NULL, 1, 0); |
| 74 | + if (is_error(err, "uc_hook_add[UC_HOOK_BLOCK]")) |
| 75 | + break; |
| 76 | + |
| 77 | + // Tracing one instruction at CODE_BASE with customized callback |
| 78 | + err = uc_hook_add(uc, &trace2, UC_HOOK_CODE, hook_code, NULL, CODE_BASE, |
| 79 | + CODE_BASE + 1); |
| 80 | + if (is_error(err, "uc_hook_add[UC_HOOK_CODE]")) |
| 81 | + break; |
| 82 | + |
| 83 | + // Initialize registers |
| 84 | + uint8_t regs[32]; |
| 85 | + memset(regs, 0, sizeof(regs)); |
| 86 | + regs[25] = 0; regs[24] = 1; |
| 87 | + regs[23] = 0; regs[22] = 2; |
| 88 | + |
| 89 | + int reg_ids[32]; |
| 90 | + void *reg_vals[32]; |
| 91 | + for (unsigned i = 0; i < 4; i++) { |
| 92 | + reg_ids[i] = UC_AVR_REG_R0 + 22 + i; |
| 93 | + reg_vals[i] = ®s[22 + i]; |
| 94 | + } |
| 95 | + err = uc_reg_write_batch(uc, reg_ids, reg_vals, 4); |
| 96 | + if (is_error(err, "uc_reg_write_batch")) |
| 97 | + break; |
| 98 | + |
| 99 | + // Emulate machine code in infinite time (last param = 0), or |
| 100 | + // when finishing all the code. |
| 101 | + err = uc_emu_start(uc, CODE_BASE, CODE_BASE + 4, 0, 0); |
| 102 | + if (is_error(err, "uc_emu_start")) |
| 103 | + break; |
| 104 | + |
| 105 | + // now print out some registers |
| 106 | + printf(">>> Emulation done. Below is the CPU context\n"); |
| 107 | + |
| 108 | + uc_reg_read(uc, UC_AVR_REG_R25, ®s[25]); |
| 109 | + uc_reg_read(uc, UC_AVR_REG_R24, ®s[24]); |
| 110 | + uc_reg_read(uc, UC_AVR_REG_R23, ®s[23]); |
| 111 | + uc_reg_read(uc, UC_AVR_REG_R22, ®s[22]); |
| 112 | + printf(">>> r25,r24 = 0x%02x%02x\n", regs[25], regs[24]); |
| 113 | + if (regs[25] == 0 && regs[24] == 3 && regs[23] == 0 && regs[22] == 2) |
| 114 | + success = true; |
| 115 | + } while (0); |
| 116 | + |
| 117 | + if (uc) |
| 118 | + uc_close(uc); |
| 119 | + return success; |
| 120 | +} |
| 121 | + |
| 122 | +int main(int argc, char **argv, char **envp) |
| 123 | +{ |
| 124 | + if (!test_avr()) |
| 125 | + abort(); |
| 126 | + return 0; |
| 127 | +} |
0 commit comments