├── hd.npy ├── .gitignore ├── imgs ├── smoothed-sbox.png └── smoothed-ttable.png ├── .gitmodules ├── LICENSE ├── xprint.py ├── CMakeLists.txt ├── aes-sbox-dec-test.c ├── aes-dec-test.c ├── hd-plot.ipynb ├── README.md ├── unicorn-trace-w-reg-diffs-example.py └── emusca.py /hd.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jevinskie/emusca/HEAD/hd.npy -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build-mac/ 3 | .ipynb_checkpoints/ 4 | *.pyc 5 | -------------------------------------------------------------------------------- /imgs/smoothed-sbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jevinskie/emusca/HEAD/imgs/smoothed-sbox.png -------------------------------------------------------------------------------- /imgs/smoothed-ttable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jevinskie/emusca/HEAD/imgs/smoothed-ttable.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mbedtls"] 2 | path = mbedtls 3 | branch = emusca 4 | url = https://github.com/jevinskie/mbedtls 5 | [submodule "tiny-AES-c"] 6 | path = tiny-AES-c 7 | branch = emusca 8 | url = https://github.com/jevinskie/tiny-AES-c 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <''year''> <''copyright holders''> 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. -------------------------------------------------------------------------------- /xprint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Capstone Python bindings, by Nguyen Anh Quynnh 3 | 4 | from __future__ import print_function 5 | import sys 6 | _python3 = sys.version_info.major == 3 7 | 8 | 9 | def to_hex(s, prefix_0x = True): 10 | if _python3: 11 | if prefix_0x: 12 | return " ".join("0x{0:02x}".format(c) for c in s) # <-- Python 3 is OK 13 | else: 14 | return " ".join("{0:02x}".format(c) for c in s) # <-- Python 3 is OK 15 | else: 16 | if prefix_0x: 17 | return " ".join("0x{0:02x}".format(ord(c)) for c in s) 18 | else: 19 | return " ".join("{0:02x}".format(ord(c)) for c in s) 20 | 21 | def to_hex2(s): 22 | if _python3: 23 | r = "".join("{0:02x}".format(c) for c in s) # <-- Python 3 is OK 24 | else: 25 | r = "".join("{0:02x}".format(ord(c)) for c in s) 26 | while r[0] == '0': r = r[1:] 27 | return r 28 | 29 | def to_x(s): 30 | from struct import pack 31 | if not s: return '0' 32 | x = pack(">q", s) 33 | while x[0] in ('\0', 0): x = x[1:] 34 | return to_hex2(x) 35 | 36 | def to_x_32(s): 37 | from struct import pack 38 | if not s: return '0' 39 | x = pack(">i", s) 40 | while x[0] in ('\0', 0): x = x[1:] 41 | return to_hex2(x) 42 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(emusca C) 4 | 5 | option(TARGET_ARM_BAREMETAL "Target ARM baremetal using arm-none-eabi-gcc toolchain" ON) 6 | 7 | if(TARGET_ARM_BAREMETAL) 8 | set(CMAKE_EXECUTABLE_SUFFIX ".elf") 9 | set(CMAKE_TOOLCHAIN_FILE $ENV{KSDK_ROOT}/tools/cmake_toolchain_files/armgcc.cmake) 10 | set(CFLAGS -g -Os -mthumb -ffunction-sections -fdata-sections -DMBEDTLS_NO_PLATFORM_ENTROPY -UMBEDTLS_TIMING_C) 11 | set(LDFLAGS "-Wl,--gc-sections,-e,aes_do_dec_wrapped,-E") 12 | set(CMAKE_C_STANDARD_LIBRARIES "-Wl,--start-group -lgcc -lc_nano -lm -Wl,--end-group") 13 | set(ARCH_OPTS 14 | -mcpu=cortex-m4 15 | -mfpu=fpv4-sp-d16 16 | -mfloat-abi=hard 17 | -mthumb 18 | ) 19 | else() 20 | set(CFLAGS -g -Os) 21 | set(LDFLAGS -Wl,-dead_strip) 22 | endif() 23 | 24 | add_subdirectory(mbedtls EXCLUDE_FROM_ALL) 25 | 26 | add_executable(aes-dec-test aes-dec-test.c) 27 | target_link_libraries(aes-dec-test mbedcrypto) 28 | target_include_directories(aes-dec-test PRIVATE ${CMAKE_SOURCE_DIR}/mbedtls/include) 29 | target_compile_options(mbedcrypto PRIVATE ${CFLAGS}) 30 | target_compile_options(aes-dec-test PRIVATE ${CFLAGS}) 31 | 32 | add_executable(aes-sbox-dec-test aes-sbox-dec-test.c tiny-AES-c/aes.c) 33 | target_include_directories(aes-sbox-dec-test PRIVATE ${CMAKE_SOURCE_DIR}/tiny-AES-c) 34 | target_compile_options(aes-sbox-dec-test PRIVATE ${CFLAGS}) 35 | 36 | set_target_properties( 37 | aes-dec-test aes-sbox-dec-test 38 | PROPERTIES 39 | C_STANDARD 11 40 | C_EXTENSIONS ON 41 | LINK_FLAGS "${LDFLAGS}" 42 | ) 43 | -------------------------------------------------------------------------------- /aes-sbox-dec-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "aes.h" 8 | 9 | void read_hex(const char *hex_str, uint8_t *out_buf, size_t len) { 10 | for (size_t i = 0; i < len; ++i) { 11 | assert(sscanf(hex_str, "%02hhx", &out_buf[i]) == 1); 12 | hex_str += 2; 13 | } 14 | } 15 | 16 | void print_hex(const uint8_t *buf, size_t len) { 17 | for (size_t i = 0; i < len; ++i) { 18 | printf("%02hhx", buf[i]); 19 | } 20 | } 21 | 22 | uint8_t key[0x10]; 23 | uint8_t iv[0x10]; 24 | uint8_t ct[0x10]; 25 | uint8_t pt[0x10]; 26 | 27 | void aes_do_dec(const uint8_t *key_in, size_t key_sz, uint8_t *iv_in, const uint8_t *ct_in, uint8_t *pt_out, size_t sz_in) { 28 | struct AES_ctx ctx; 29 | memcpy(pt_out, ct_in, sz_in); 30 | AES_init_ctx_iv(&ctx, key, iv); 31 | AES_CBC_decrypt_buffer(&ctx, pt_out, sz_in); 32 | return; 33 | } 34 | 35 | __attribute__((noinline)) 36 | void aes_init_marker(void) { 37 | asm volatile("nop\n\t"); 38 | return; 39 | } 40 | 41 | void aes_do_dec_wrapped(void) { 42 | static int done_init = 0; 43 | if (!done_init) { 44 | aes_init_marker(); 45 | } 46 | aes_do_dec(key, sizeof(key), iv, ct, pt, sizeof(ct)); 47 | #ifdef __arm__ 48 | _exit(243); 49 | #endif 50 | } 51 | 52 | int main(int argc, const char **argv) { 53 | _Static_assert(sizeof(ct) == sizeof(pt), "ct and pt size differ"); 54 | if (argc != 4) { 55 | printf("Usage: %s \n", argv[0]); 56 | return -1; 57 | } 58 | read_hex(argv[1], key, sizeof(key)); 59 | read_hex(argv[2], iv, sizeof(iv)); 60 | read_hex(argv[3], ct, sizeof(pt)); 61 | // aes_do_dec(key, sizeof(key), iv, ct, pt, sizeof(ct)); 62 | aes_do_dec_wrapped(); 63 | print_hex(pt, sizeof(pt)); 64 | puts(""); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /aes-dec-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "mbedtls/aes.h" 7 | 8 | void read_hex(const char *hex_str, uint8_t *out_buf, size_t len) { 9 | for (size_t i = 0; i < len; ++i) { 10 | assert(sscanf(hex_str, "%02hhx", &out_buf[i]) == 1); 11 | hex_str += 2; 12 | } 13 | } 14 | 15 | void print_hex(const uint8_t *buf, size_t len) { 16 | for (size_t i = 0; i < len; ++i) { 17 | printf("%02hhx", buf[i]); 18 | } 19 | } 20 | 21 | uint8_t key[0x10]; 22 | uint8_t iv[0x10]; 23 | uint8_t ct[0x10]; 24 | uint8_t pt[0x10]; 25 | 26 | uint8_t init_key[0x10]; 27 | uint8_t init_iv[0x10]; 28 | uint8_t init_ct[0x10]; 29 | uint8_t init_pt[0x10]; 30 | 31 | void aes_do_dec(const uint8_t *key_in, size_t key_sz, uint8_t *iv_in, const uint8_t *ct_in, uint8_t *pt_out, size_t sz_in) { 32 | mbedtls_aes_context ctx; 33 | mbedtls_aes_setkey_dec(&ctx, key_in, key_sz*8); 34 | mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, sz_in, iv_in, ct_in, pt_out); 35 | return; 36 | } 37 | 38 | __attribute__((noinline)) 39 | void aes_init_marker(void) { 40 | asm volatile("nop\n\t"); 41 | return; 42 | } 43 | 44 | void aes_do_dec_wrapped(void) { 45 | static int done_init = 0; 46 | if (!done_init) { 47 | aes_do_dec(init_key, sizeof(init_key), init_iv, init_ct, init_pt, sizeof(init_ct)); 48 | done_init = 1; 49 | aes_init_marker(); 50 | } 51 | aes_do_dec(key, sizeof(key), iv, ct, pt, sizeof(ct)); 52 | #ifdef __arm__ 53 | _exit(243); 54 | #endif 55 | } 56 | 57 | int main(int argc, const char **argv) { 58 | _Static_assert(sizeof(ct) == sizeof(pt), "ct and pt size differ"); 59 | if (argc != 4) { 60 | printf("Usage: %s \n", argv[0]); 61 | return -1; 62 | } 63 | read_hex(argv[1], key, sizeof(key)); 64 | read_hex(argv[2], iv, sizeof(iv)); 65 | read_hex(argv[3], ct, sizeof(pt)); 66 | // aes_do_dec(key, sizeof(key), iv, ct, pt, sizeof(ct)); 67 | aes_do_dec_wrapped(); 68 | print_hex(pt, sizeof(pt)); 69 | puts(""); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /hd-plot.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%matplotlib notebook\n", 10 | "import matplotlib.pyplot as plt\n", 11 | "import numpy as np\n", 12 | "from scipy.signal import savgol_filter" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "hd_list = np.load('foo.npy')" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "plt.close()\n", 31 | "plt.plot(hd_list)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "hd_listp1 = savgol_filter(hd_list, 11, 3)\n", 41 | "hd_listp2 = savgol_filter(hd_list, 51, 3)\n", 42 | "hd_listp3 = savgol_filter(hd_list, 101, 3)\n", 43 | "hd_listp4 = savgol_filter(hd_list, 251, 3)\n", 44 | "hd_listp5 = savgol_filter(hd_list, 501, 3)" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "plt.close()\n", 54 | "plt.plot(hd_listp1)\n", 55 | "plt.plot(hd_listp2)\n", 56 | "plt.plot(hd_listp3)\n", 57 | "plt.plot(hd_listp4)\n", 58 | "plt.plot(hd_listp5)" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "plt.close()\n", 68 | "hd_listp2 = savgol_filter(hd_list, 501, 3)\n", 69 | "plt.plot(hd_listp2)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "hd_listp6 = savgol_filter(hd_list, 11, 3)\n", 79 | "plt.close()\n", 80 | "plt.plot(hd_listp6[4500:])" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "plt.close()\n", 90 | "plt.plot(hd_listp6[935:1112])" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [] 99 | } 100 | ], 101 | "metadata": { 102 | "kernelspec": { 103 | "display_name": "Python 3", 104 | "language": "python", 105 | "name": "python3" 106 | }, 107 | "language_info": { 108 | "codemirror_mode": { 109 | "name": "ipython", 110 | "version": 3 111 | }, 112 | "file_extension": ".py", 113 | "mimetype": "text/x-python", 114 | "name": "python", 115 | "nbconvert_exporter": "python", 116 | "pygments_lexer": "ipython3", 117 | "version": "3.7.2" 118 | } 119 | }, 120 | "nbformat": 4, 121 | "nbformat_minor": 2 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | emusca - Power trace simulator using Unicorn Engine 2 | ======================== 3 | 4 | This project uses [Unicorn] to emulate a binary. Every instruction is traced 5 | and the hamming distance between every register before and after the instruction 6 | is calculated and stored. The target binary is run over many input ciphertexts (or plaintexts) 7 | to generate simulated power traces (simply the hamming distance for now) and [Daredevil] input, 8 | output, trace, and config file are generated. Marker functions consisting of NOPs are added to the 9 | target binary and the instruction hook records when they are hit. This is to facilitate finding 10 | the boundaries of the rounds of AES. [deco] is used for easy multiprocessing to speed up trace generation. 11 | 12 | Example Traces 13 | -------------- 14 | AES S-Box based implementation simulated power trace. 15 | The different colored plots are the same trace smoothed at different levels. 16 | The nine humps are the inner full rounds of AES. 17 | ![S-Box Trace](imgs/smoothed-sbox.png "S-Box Trace") 18 | 19 | AES T-Table based implementation simulated power trace. 20 | The plateau on the right hand side are the rounds of AES. They are more difficult to distinguish compared 21 | to the S-Box trace since they take approximately 10 times fewer instructions per round. 22 | ![T-Table Trace](imgs/smoothed-ttable.png "T-Table Trace") 23 | 24 | The activity on the left hand side in both plots is key expansion. 25 | 26 | Other Notes 27 | ------------ 28 | Before I realized the `MBEDTLS_AES_ROM_TABLES` option existed for mbedTLS, I was frustrated by 29 | the time wasted on generating the AES tables for every trace emulation. Thus I wrote a hacky save/restore 30 | system for Unicorn that runs a dummy encryption to make sure the tables are initialized, dumps the mapped 31 | memory to a global cache and then uses that global cache when creating the Unicorn instances for 32 | each trace simulation. I thought it might be useful later so I left it in. Registers are not saved/restored 33 | but could be thanks to the save/restore context API that the [angr] project got upstreamed to Unicorn. 34 | 35 | LIEF is used to map the ELF binary into Unicorn and to find various symbols for marker tracing and crypto input/output setting/getting. 36 | 37 | This may be rewritten in C++ in the future to see if that appreciably improves the performance. The Python version will always exist since it is so easy to hack on. 38 | 39 | Build/Run notes: 40 | ----------- 41 | The ARM build assumes an NXP toolchain is present. I need to switch to using devkitARM or something easier to install. 42 | 43 | This needs my [jevmaster branch of unicorn] for the batch register read Python bindings. Remind me to create a PR for Unicorn to upstream this. 44 | 45 | TODO 46 | ------------ 47 | Write up tutorial for building, collecting traces, and running CPA on them. 48 | 49 | Lots of code cleanup. 50 | 51 | [Unicorn]: https://github.com/unicorn-engine/unicorn 52 | [Daredevil]: https://github.com/SideChannelMarvels/Daredevil 53 | [deco]: https://www.peterbe.com/plog/deco 54 | [LIEF]: https://lief.quarkslab.com 55 | [angr]: http://angr.io 56 | [jevmaster branch of unicorn]: https://github.com/jevinskie/unicorn/tree/jevmaster 57 | -------------------------------------------------------------------------------- /unicorn-trace-w-reg-diffs-example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from __future__ import print_function 4 | 5 | import binascii 6 | import struct 7 | import sys 8 | 9 | from unicorn import * 10 | from unicorn.arm_const import * 11 | 12 | from capstone import * 13 | from capstone.arm import * 14 | from xprint import to_hex, to_x_32 15 | 16 | cs_arm = Cs(CS_ARCH_ARM, CS_MODE_ARM) 17 | cs_arm.detail = True 18 | cs_thumb = Cs(CS_ARCH_ARM, CS_MODE_THUMB) 19 | cs_thumb.detail = True 20 | 21 | IRAM_START = 0x40000000 22 | IRAM_SIZE = 256 * 1024 23 | RCM_PAYLOAD_ADDR = 0x4000A000 24 | 25 | USB_SEND_ADDR = 0xfff05092 26 | NvOsWaitUS_ADDR = 0xFFF00B9A 27 | 28 | PMC_BASE = 0x7000E400 29 | PMC_SCRATCH0 = 0x50 30 | PMC_SCRATCH1 = 0x54 31 | 32 | FOURK_ALIGN_MASK = 0xFFFFF000 33 | 34 | min_sp = 0xFFFFFFFF 35 | 36 | reg_ids = (UC_ARM_REG_APSR, UC_ARM_REG_APSR_NZCV, UC_ARM_REG_CPSR, UC_ARM_REG_LR, UC_ARM_REG_PC, UC_ARM_REG_SP, UC_ARM_REG_SPSR, UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7, UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12) 37 | 38 | reg_names = { 39 | UC_ARM_REG_APSR: 'APSR', 40 | UC_ARM_REG_APSR_NZCV: 'APSR_NZCV', 41 | UC_ARM_REG_CPSR: 'CPSR', 42 | UC_ARM_REG_LR: 'LR', 43 | UC_ARM_REG_PC: 'PC', 44 | UC_ARM_REG_SP: 'SP', 45 | UC_ARM_REG_SPSR: 'SPSR', 46 | UC_ARM_REG_R0: 'R0', 47 | UC_ARM_REG_R1: 'R1', 48 | UC_ARM_REG_R2: 'R2', 49 | UC_ARM_REG_R3: 'R3', 50 | UC_ARM_REG_R4: 'R4', 51 | UC_ARM_REG_R5: 'R5', 52 | UC_ARM_REG_R6: 'R6', 53 | UC_ARM_REG_R7: 'R7', 54 | UC_ARM_REG_R8: 'R8', 55 | UC_ARM_REG_R9: 'R9', 56 | UC_ARM_REG_R10: 'R10', 57 | UC_ARM_REG_R11: 'R11', 58 | UC_ARM_REG_R12: 'R12', 59 | } 60 | 61 | saved_regs = None 62 | 63 | def dump_regs(regs): 64 | for k in regs.keys(): 65 | print("\t%s:\t0x%08x" % (reg_names[k], regs[k])) 66 | 67 | def dump_regs_changed(regs): 68 | for k in regs.keys(): 69 | print("\t%s:\t0x%08x -> 0x%08x" % (reg_names[k], regs[k]['old'], regs[k]['new'])) 70 | 71 | def all_regs(uc): 72 | reg_vals = uc.reg_read_batch(reg_ids) 73 | regs = {} 74 | for i, reg_val in enumerate(reg_vals): 75 | regs[reg_ids[i]] = reg_val 76 | return regs 77 | 78 | def changed_regs(old_regs, new_regs): 79 | regs = {} 80 | for k in old_regs.keys(): 81 | if old_regs[k] != new_regs[k]: 82 | regs[k] = {'old': old_regs[k], 'new': new_regs[k]} 83 | # regs[k] = new_regs[k] 84 | return regs 85 | 86 | def print_insn_detail(insn, insn_bytes): 87 | # print address, insn bytes, mnemonic and operands 88 | insn_bytes_str = binascii.hexlify(insn_bytes) 89 | insn_bytes_str = ' '.join(insn_bytes_str[i:i+2] for i in range(0, len(insn_bytes_str), 2)) 90 | print("0x%x: %s\t%s\t%s" % (insn.address, insn_bytes_str, insn.mnemonic, insn.op_str)) 91 | 92 | return 93 | 94 | # "data" instruction generated by SKIPDATA option has no detail 95 | if insn.id == 0: 96 | return 97 | 98 | if len(insn.operands) > 0: 99 | print("\top_count: %u" % len(insn.operands)) 100 | c = 0 101 | for i in insn.operands: 102 | if i.type == ARM_OP_REG: 103 | print("\t\toperands[%u].type: REG = %s" % (c, insn.reg_name(i.reg))) 104 | if i.type == ARM_OP_IMM: 105 | print("\t\toperands[%u].type: IMM = 0x%s" % (c, to_x_32(i.imm))) 106 | if i.type == ARM_OP_PIMM: 107 | print("\t\toperands[%u].type: P-IMM = %u" % (c, i.imm)) 108 | if i.type == ARM_OP_CIMM: 109 | print("\t\toperands[%u].type: C-IMM = %u" % (c, i.imm)) 110 | if i.type == ARM_OP_FP: 111 | print("\t\toperands[%u].type: FP = %f" % (c, i.fp)) 112 | if i.type == ARM_OP_SYSREG: 113 | print("\t\toperands[%u].type: SYSREG = %u" % (c, i.reg)) 114 | if i.type == ARM_OP_SETEND: 115 | if i.setend == ARM_SETEND_BE: 116 | print("\t\toperands[%u].type: SETEND = be" % c) 117 | else: 118 | print("\t\toperands[%u].type: SETEND = le" % c) 119 | if i.type == ARM_OP_MEM: 120 | print("\t\toperands[%u].type: MEM" % c) 121 | if i.mem.base != 0: 122 | print("\t\t\toperands[%u].mem.base: REG = %s" \ 123 | % (c, insn.reg_name(i.mem.base))) 124 | if i.mem.index != 0: 125 | print("\t\t\toperands[%u].mem.index: REG = %s" \ 126 | % (c, insn.reg_name(i.mem.index))) 127 | if i.mem.scale != 1: 128 | print("\t\t\toperands[%u].mem.scale: %u" \ 129 | % (c, i.mem.scale)) 130 | if i.mem.disp != 0: 131 | print("\t\t\toperands[%u].mem.disp: 0x%s" \ 132 | % (c, to_x_32(i.mem.disp))) 133 | if i.mem.lshift != 0: 134 | print("\t\t\toperands[%u].mem.lshift: 0x%s" \ 135 | % (c, to_x_32(i.mem.lshift))) 136 | 137 | if i.neon_lane != -1: 138 | print("\t\toperands[%u].neon_lane = %u" % (c, i.neon_lane)) 139 | 140 | if i.access == CS_AC_READ: 141 | print("\t\toperands[%u].access: READ\n" % (c)) 142 | elif i.access == CS_AC_WRITE: 143 | print("\t\toperands[%u].access: WRITE\n" % (c)) 144 | elif i.access == CS_AC_READ | CS_AC_WRITE: 145 | print("\t\toperands[%u].access: READ | WRITE\n" % (c)) 146 | 147 | if i.shift.type != ARM_SFT_INVALID and i.shift.value: 148 | print("\t\t\tShift: %u = %u" \ 149 | % (i.shift.type, i.shift.value)) 150 | if i.vector_index != -1: 151 | print("\t\t\toperands[%u].vector_index = %u" %(c, i.vector_index)) 152 | if i.subtracted: 153 | print("\t\t\toperands[%u].subtracted = True" %c) 154 | 155 | c += 1 156 | 157 | if insn.update_flags: 158 | print("\tUpdate-flags: True") 159 | if insn.writeback: 160 | print("\tWrite-back: True") 161 | if not insn.cc in [ARM_CC_AL, ARM_CC_INVALID]: 162 | print("\tCode condition: %u" % insn.cc) 163 | if insn.cps_mode: 164 | print("\tCPSI-mode: %u" %(insn.cps_mode)) 165 | if insn.cps_flag: 166 | print("\tCPSI-flag: %u" %(insn.cps_flag)) 167 | if insn.vector_data: 168 | print("\tVector-data: %u" %(insn.vector_data)) 169 | if insn.vector_size: 170 | print("\tVector-size: %u" %(insn.vector_size)) 171 | if insn.usermode: 172 | print("\tUser-mode: True") 173 | if insn.mem_barrier: 174 | print("\tMemory-barrier: %u" %(insn.mem_barrier)) 175 | 176 | (regs_read, regs_write) = insn.regs_access() 177 | 178 | if len(regs_read) > 0: 179 | print("\tRegisters read:", end="") 180 | for r in regs_read: 181 | print(" %s" %(insn.reg_name(r)), end="") 182 | print("") 183 | 184 | if len(regs_write) > 0: 185 | print("\tRegisters modified:", end="") 186 | for r in regs_write: 187 | print(" %s" %(insn.reg_name(r)), end="") 188 | print("") 189 | 190 | # callback for tracing basic blocks 191 | def hook_block(uc, address, size, user_data): 192 | print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) 193 | 194 | # callback for tracing instructions 195 | def hook_code(uc, address, size, user_data): 196 | global saved_regs, min_sp 197 | new_regs = all_regs(uc) 198 | min_sp = min(min_sp, new_regs[UC_ARM_REG_SP]) 199 | ch_regs = changed_regs(saved_regs, new_regs) 200 | ch_regs.pop(UC_ARM_REG_PC, None) 201 | dump_regs_changed(ch_regs) 202 | is_thumb = new_regs[UC_ARM_REG_CPSR] & (1 << 5) != 0 203 | mode_str = None 204 | if is_thumb: 205 | mode_str = "Thumb" 206 | else: 207 | mode_str = "ARM" 208 | print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, mode: %s" % (address, size, mode_str)) 209 | pc = new_regs[UC_ARM_REG_PC] 210 | insn_bytes = uc.mem_read(pc, size) 211 | insn = None 212 | if is_thumb: 213 | insn = list(cs_thumb.disasm(insn_bytes, pc))[0] 214 | else: 215 | insn = list(cs_arm.disasm(insn_bytes, pc))[0] 216 | print_insn_detail(insn, insn_bytes) 217 | saved_regs = new_regs 218 | 219 | def hook_usb_send(uc, address, size, user_data): 220 | global saved_regs 221 | print(">>> Hooking usb_send") 222 | (buf_ptr, size, size_sent_ptr, lr) = uc.reg_read_batch((UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_LR)) 223 | print(">>> \tbuf_ptr: 0x%08x, size: 0x%08x, size_sent_ptr: 0x%08x lr: 0x%08x" % (buf_ptr, size, size_sent_ptr, lr)) 224 | buf = uc.mem_read(buf_ptr, size) 225 | try: 226 | buf_str = buf.decode('utf-8') 227 | print(">>> \tbuf:\t'%s' %s" % (buf_str, binascii.hexlify(buf))) 228 | except UnicodeError: 229 | print(">>> \tbuf:\t%s" % binascii.hexlify(buf)) 230 | size_sent = struct.pack('>> Hooking NvOsWaitUS") 243 | (us, lr) = uc.reg_read_batch((UC_ARM_REG_R0, UC_ARM_REG_LR)) 244 | print(">>> \tus: %d lr: 0x%08x" % (us, lr)) 245 | uc.reg_write(UC_ARM_REG_PC, lr) 246 | 247 | # callback for tracing invalid memory access (READ or WRITE) 248 | def hook_mem_unmapped(uc, access, address, size, value, user_data): 249 | if access == UC_MEM_WRITE_UNMAPPED: 250 | print(">>> Missing memory is being WRITE at 0x%08x, data size = %u, data value = 0x%08x" \ 251 | %(address, size, value)) 252 | else: 253 | print(">>> Missing memory is being READ at 0x%08x, data size = %u, data value = 0x%08x" \ 254 | %(address, size, value)) 255 | # return False to indicate we want to stop emulation 256 | return False 257 | 258 | # callback for tracing memory access (READ or WRITE) 259 | def hook_mem_access(uc, access, address, size, value, user_data): 260 | old_val_bytes = uc.mem_read(address, size) 261 | old_val_bytes = uc.mem_read(address, size) 262 | struct_fmt = {1: '>> Memory is being WRITE at 0x%08x, data size = %u, data value = 0x%08x, old value = 0x%08x" \ 266 | %(address, size, value, old_val)) 267 | if address == PMC_BASE and value & (1 << 4) != 0: 268 | print(">>> PMC reset issued, stopping emu") 269 | uc.emu_stop() 270 | if address == PMC_BASE + PMC_SCRATCH0: 271 | print(">>> PMC_SCRATCH0 written") 272 | if address == PMC_BASE + PMC_SCRATCH1: 273 | print(">>> PMC_SCRATCH1 written") 274 | else: # READ 275 | print(">>> Memory is being READ at 0x%08x, data size = %u, data value = 0x%08x" \ 276 | %(address, size, old_val)) 277 | 278 | def main(argv): 279 | global saved_regs 280 | 281 | payload_buf = None 282 | with open(sys.argv[1], "rb") as f: 283 | payload_buf = f.read() 284 | 285 | binbuf = payload_buf 286 | 287 | # memory address where emulation starts 288 | base_addr = IRAM_START 289 | mem_size = IRAM_SIZE 290 | top_addr = base_addr + mem_size 291 | stack_top = RCM_PAYLOAD_ADDR - 0x10 292 | 293 | print("Emulate arm32 code") 294 | try: 295 | # Initialize emulator in ARMz mode 296 | uc = Uc(UC_ARCH_ARM, UC_MODE_ARM) 297 | 298 | # map IRAM memory for this emulation 299 | uc.mem_map(base_addr, mem_size) 300 | uc.mem_map(USB_SEND_ADDR & FOURK_ALIGN_MASK, 4096) 301 | uc.mem_write(USB_SEND_ADDR, "\xc0\x46\xc0\x46") # thumb nop nop 302 | uc.mem_map(PMC_BASE & FOURK_ALIGN_MASK, 4096) 303 | uc.mem_map(NvOsWaitUS_ADDR & FOURK_ALIGN_MASK, 4096) 304 | uc.mem_write(NvOsWaitUS_ADDR, "\xc0\x46\xc0\x46") 305 | 306 | uc.reg_write(UC_ARM_REG_SP, stack_top) 307 | uc.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on 308 | 309 | saved_regs = all_regs(uc) 310 | 311 | uc.hook_add(UC_HOOK_BLOCK, hook_block) 312 | uc.hook_add(UC_HOOK_CODE, hook_code) 313 | uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, hook_mem_access) 314 | uc.hook_add(UC_HOOK_MEM_UNMAPPED, hook_mem_unmapped) 315 | 316 | uc.hook_add(UC_HOOK_CODE, hook_usb_send, begin=USB_SEND_ADDR, end=USB_SEND_ADDR+4) 317 | uc.hook_add(UC_HOOK_CODE, hook_NvOsWaitUS, begin=NvOsWaitUS_ADDR, end=NvOsWaitUS_ADDR+4) 318 | 319 | # write machine code to be emulated to memory 320 | uc.mem_write(RCM_PAYLOAD_ADDR, binbuf) 321 | 322 | uc.emu_start(RCM_PAYLOAD_ADDR, len(binbuf)) 323 | 324 | print("Minimum SP: 0x%08x" % min_sp) 325 | 326 | except UcError as e: 327 | print("ERROR: %s" % e) 328 | 329 | if __name__ == "__main__": 330 | sys.exit(main(sys.argv)) 331 | -------------------------------------------------------------------------------- /emusca.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from __future__ import print_function 4 | 5 | import binascii as ba 6 | import os 7 | import struct 8 | import sys 9 | from multiprocessing import Pool 10 | 11 | from unicorn import * 12 | from unicorn.arm_const import * 13 | 14 | from capstone import * 15 | from capstone.arm import * 16 | from xprint import to_hex, to_x_32 17 | 18 | import lief 19 | 20 | import intervaltree as itree 21 | 22 | import numpy as np 23 | 24 | import gmpy2 25 | 26 | from deco import concurrent, synchronized 27 | 28 | cs_arm = Cs(CS_ARCH_ARM, CS_MODE_ARM) 29 | cs_arm.detail = True 30 | cs_thumb = Cs(CS_ARCH_ARM, CS_MODE_THUMB) 31 | cs_thumb.detail = True 32 | 33 | IRAM_START = 0x40000000 34 | IRAM_SIZE = 256 * 1024 35 | RCM_PAYLOAD_ADDR = 0x4000A000 36 | 37 | USB_SEND_ADDR = 0xfff05092 38 | NvOsWaitUS_ADDR = 0xFFF00B9A 39 | 40 | PMC_BASE = 0x7000E400 41 | PMC_SCRATCH0 = 0x50 42 | PMC_SCRATCH1 = 0x54 43 | 44 | FOURK_ALIGN_MASK = 0xFFFFF000 45 | EVEN_MASK = 0xFFFFFFFE 46 | 47 | def roundup(x, m): 48 | return x if x % m == 0 else x + m - x % m 49 | 50 | def rounddown(x, m): 51 | return x if x % m == 0 else x - x % m 52 | 53 | reg_ids = (UC_ARM_REG_APSR, UC_ARM_REG_APSR_NZCV, UC_ARM_REG_CPSR, UC_ARM_REG_LR, UC_ARM_REG_PC, UC_ARM_REG_SP, UC_ARM_REG_SPSR, UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3, UC_ARM_REG_R4, UC_ARM_REG_R5, UC_ARM_REG_R6, UC_ARM_REG_R7, UC_ARM_REG_R8, UC_ARM_REG_R9, UC_ARM_REG_R10, UC_ARM_REG_R11, UC_ARM_REG_R12) 54 | 55 | reg_names = { 56 | UC_ARM_REG_APSR: 'APSR', 57 | UC_ARM_REG_APSR_NZCV: 'APSR_NZCV', 58 | UC_ARM_REG_CPSR: 'CPSR', 59 | UC_ARM_REG_LR: 'LR', 60 | UC_ARM_REG_PC: 'PC', 61 | UC_ARM_REG_SP: 'SP', 62 | UC_ARM_REG_SPSR: 'SPSR', 63 | UC_ARM_REG_R0: 'R0', 64 | UC_ARM_REG_R1: 'R1', 65 | UC_ARM_REG_R2: 'R2', 66 | UC_ARM_REG_R3: 'R3', 67 | UC_ARM_REG_R4: 'R4', 68 | UC_ARM_REG_R5: 'R5', 69 | UC_ARM_REG_R6: 'R6', 70 | UC_ARM_REG_R7: 'R7', 71 | UC_ARM_REG_R8: 'R8', 72 | UC_ARM_REG_R9: 'R9', 73 | UC_ARM_REG_R10: 'R10', 74 | UC_ARM_REG_R11: 'R11', 75 | UC_ARM_REG_R12: 'R12', 76 | } 77 | 78 | def print_keystuff(uc): 79 | key = uc.mem_read(uc.key_sym.value, uc.key_sym.size) 80 | iv = uc.mem_read(uc.iv_sym.value, uc.iv_sym.size) 81 | ct = uc.mem_read(uc.ct_sym.value, uc.ct_sym.size) 82 | pt = uc.mem_read(uc.pt_sym.value, uc.pt_sym.size) 83 | # init_key = uc.mem_read(uc.init_key_sym.value, uc.init_key_sym.size) 84 | # init_iv = uc.mem_read(uc.init_iv_sym.value, uc.init_iv_sym.size) 85 | # init_ct = uc.mem_read(uc.init_ct_sym.value, uc.init_ct_sym.size) 86 | # init_pt = uc.mem_read(uc.init_pt_sym.value, uc.init_pt_sym.size) 87 | # aes_init_done = uc.mem_read(uc.aes_init_done_sym.value, uc.aes_init_done_sym.size) 88 | print("\tkey: {}".format(ba.hexlify(key))) 89 | print("\tiv: {}".format(ba.hexlify(iv))) 90 | print("\tct: {}".format(ba.hexlify(ct))) 91 | print("\tpt: {}".format(ba.hexlify(pt))) 92 | # print("\tinit_key: {}".format(ba.hexlify(init_key))) 93 | # print("\tinit_iv: {}".format(ba.hexlify(init_iv))) 94 | # print("\tinit_ct: {}".format(ba.hexlify(init_ct))) 95 | # print("\tinit_pt: {}".format(ba.hexlify(init_pt))) 96 | # print("\taes_init_done: {}".format(ba.hexlify(aes_init_done))) 97 | 98 | def dump_regs(regs): 99 | for k in regs.keys(): 100 | print("\t%s:\t0x%08x" % (reg_names[k], regs[k])) 101 | 102 | def dump_regs_changed(regs): 103 | regs.pop(UC_ARM_REG_PC, None) 104 | for k in regs.keys(): 105 | print("\t%s:\t0x%08x -> 0x%08x" % (reg_names[k], regs[k]['old'], regs[k]['new'])) 106 | 107 | def all_regs(uc): 108 | reg_vals = uc.reg_read_batch(reg_ids) 109 | regs = {} 110 | for i, reg_val in enumerate(reg_vals): 111 | regs[reg_ids[i]] = reg_val 112 | return regs 113 | 114 | def changed_regs(old_regs, new_regs): 115 | regs = {} 116 | for k in old_regs.keys(): 117 | if old_regs[k] != new_regs[k]: 118 | regs[k] = {'old': old_regs[k], 'new': new_regs[k]} 119 | # regs[k] = new_regs[k] 120 | return regs 121 | 122 | def hamming_distance_pair(old_regs, new_regs): 123 | pass 124 | 125 | def hamming_distance_changed(changed_regs): 126 | keys = changed_regs.keys() 127 | old = map(lambda r: changed_regs[r]['old'], keys) 128 | new = map(lambda r: changed_regs[r]['new'], keys) 129 | # print("old: {} new: {}".format(old, new)) 130 | # xor = np.bitwise_xor(old, new) 131 | # print("xor: {}".format(xor)) 132 | # popcnt = np.bincount(xor.transpose()) 133 | # print("popcnt: {}".format(popcnt)) 134 | hd = sum(map(lambda p: gmpy2.hamdist(p[0], p[1]), zip(old, new))) 135 | # print("hd: {}".format(hd)) 136 | return hd 137 | 138 | 139 | def print_insn_detail(insn, insn_bytes): 140 | # print address, insn bytes, mnemonic and operands 141 | insn_bytes_str = ba.hexlify(insn_bytes) 142 | insn_bytes_str = ' '.join(insn_bytes_str[i:i+2] for i in range(0, len(insn_bytes_str), 2)) 143 | print("0x%x: %s\t%s\t%s" % (insn.address, insn_bytes_str, insn.mnemonic, insn.op_str)) 144 | 145 | return 146 | 147 | # "data" instruction generated by SKIPDATA option has no detail 148 | if insn.id == 0: 149 | return 150 | 151 | if len(insn.operands) > 0: 152 | print("\top_count: %u" % len(insn.operands)) 153 | c = 0 154 | for i in insn.operands: 155 | if i.type == ARM_OP_REG: 156 | print("\t\toperands[%u].type: REG = %s" % (c, insn.reg_name(i.reg))) 157 | if i.type == ARM_OP_IMM: 158 | print("\t\toperands[%u].type: IMM = 0x%s" % (c, to_x_32(i.imm))) 159 | if i.type == ARM_OP_PIMM: 160 | print("\t\toperands[%u].type: P-IMM = %u" % (c, i.imm)) 161 | if i.type == ARM_OP_CIMM: 162 | print("\t\toperands[%u].type: C-IMM = %u" % (c, i.imm)) 163 | if i.type == ARM_OP_FP: 164 | print("\t\toperands[%u].type: FP = %f" % (c, i.fp)) 165 | if i.type == ARM_OP_SYSREG: 166 | print("\t\toperands[%u].type: SYSREG = %u" % (c, i.reg)) 167 | if i.type == ARM_OP_SETEND: 168 | if i.setend == ARM_SETEND_BE: 169 | print("\t\toperands[%u].type: SETEND = be" % c) 170 | else: 171 | print("\t\toperands[%u].type: SETEND = le" % c) 172 | if i.type == ARM_OP_MEM: 173 | print("\t\toperands[%u].type: MEM" % c) 174 | if i.mem.base != 0: 175 | print("\t\t\toperands[%u].mem.base: REG = %s" \ 176 | % (c, insn.reg_name(i.mem.base))) 177 | if i.mem.index != 0: 178 | print("\t\t\toperands[%u].mem.index: REG = %s" \ 179 | % (c, insn.reg_name(i.mem.index))) 180 | if i.mem.scale != 1: 181 | print("\t\t\toperands[%u].mem.scale: %u" \ 182 | % (c, i.mem.scale)) 183 | if i.mem.disp != 0: 184 | print("\t\t\toperands[%u].mem.disp: 0x%s" \ 185 | % (c, to_x_32(i.mem.disp))) 186 | if i.mem.lshift != 0: 187 | print("\t\t\toperands[%u].mem.lshift: 0x%s" \ 188 | % (c, to_x_32(i.mem.lshift))) 189 | 190 | if i.neon_lane != -1: 191 | print("\t\toperands[%u].neon_lane = %u" % (c, i.neon_lane)) 192 | 193 | if i.access == CS_AC_READ: 194 | print("\t\toperands[%u].access: READ\n" % (c)) 195 | elif i.access == CS_AC_WRITE: 196 | print("\t\toperands[%u].access: WRITE\n" % (c)) 197 | elif i.access == CS_AC_READ | CS_AC_WRITE: 198 | print("\t\toperands[%u].access: READ | WRITE\n" % (c)) 199 | 200 | if i.shift.type != ARM_SFT_INVALID and i.shift.value: 201 | print("\t\t\tShift: %u = %u" \ 202 | % (i.shift.type, i.shift.value)) 203 | if i.vector_index != -1: 204 | print("\t\t\toperands[%u].vector_index = %u" %(c, i.vector_index)) 205 | if i.subtracted: 206 | print("\t\t\toperands[%u].subtracted = True" %c) 207 | 208 | c += 1 209 | 210 | if insn.update_flags: 211 | print("\tUpdate-flags: True") 212 | if insn.writeback: 213 | print("\tWrite-back: True") 214 | if not insn.cc in [ARM_CC_AL, ARM_CC_INVALID]: 215 | print("\tCode condition: %u" % insn.cc) 216 | if insn.cps_mode: 217 | print("\tCPSI-mode: %u" %(insn.cps_mode)) 218 | if insn.cps_flag: 219 | print("\tCPSI-flag: %u" %(insn.cps_flag)) 220 | if insn.vector_data: 221 | print("\tVector-data: %u" %(insn.vector_data)) 222 | if insn.vector_size: 223 | print("\tVector-size: %u" %(insn.vector_size)) 224 | if insn.usermode: 225 | print("\tUser-mode: True") 226 | if insn.mem_barrier: 227 | print("\tMemory-barrier: %u" %(insn.mem_barrier)) 228 | 229 | (regs_read, regs_write) = insn.regs_access() 230 | 231 | if len(regs_read) > 0: 232 | print("\tRegisters read:", end="") 233 | for r in regs_read: 234 | print(" %s" %(insn.reg_name(r)), end="") 235 | print("") 236 | 237 | if len(regs_write) > 0: 238 | print("\tRegisters modified:", end="") 239 | for r in regs_write: 240 | print(" %s" %(insn.reg_name(r)), end="") 241 | print("") 242 | 243 | def findsym(elf, name): 244 | return next(s for s in elf.symbols if s.name == name) 245 | 246 | # callback for tracing basic blocks 247 | def hook_block(uc, address, size, user_data): 248 | print(">>> Tracing basic block at 0x%x, block size = 0x%x" %(address, size)) 249 | 250 | # callback for tracing instructions 251 | def hook_code(uc, address, size, user_data): 252 | new_regs = all_regs(uc) 253 | ch_regs = changed_regs(uc.saved_regs, new_regs) 254 | # ch_regs.pop(UC_ARM_REG_PC, None) 255 | hamming_distance_changed(ch_regs) 256 | dump_regs_changed(ch_regs) 257 | is_thumb = new_regs[UC_ARM_REG_CPSR] & (1 << 5) != 0 258 | mode_str = None 259 | if is_thumb: 260 | mode_str = "Thumb" 261 | else: 262 | mode_str = "ARM" 263 | print(">>> Tracing instruction at 0x%x, instruction size = 0x%x, mode: %s" % (address, size, mode_str)) 264 | pc = new_regs[UC_ARM_REG_PC] 265 | insn_bytes = uc.mem_read(pc, size) 266 | insn = None 267 | if is_thumb: 268 | insn = list(cs_thumb.disasm(insn_bytes, pc))[0] 269 | else: 270 | insn = list(cs_arm.disasm(insn_bytes, pc))[0] 271 | print_insn_detail(insn, insn_bytes) 272 | uc.saved_regs = new_regs 273 | 274 | def hook_code_hamming_distance(uc, address, size, user_data): 275 | new_regs = all_regs(uc) 276 | ch_regs = changed_regs(uc.saved_regs, new_regs) 277 | # ch_regs.pop(UC_ARM_REG_PC, None) 278 | hd = hamming_distance_changed(ch_regs) 279 | if address == uc.aes_round_marker_addr: 280 | uc.aes_round_marks.append(uc.insn_count) 281 | uc.hd_list.append(hd) 282 | uc.saved_regs = new_regs 283 | uc.insn_count += 1 284 | 285 | def hook_exit(uc, address, size, user_data): 286 | # print(">>> Hooking _exit") 287 | (retval, lr, pc) = uc.reg_read_batch((UC_ARM_REG_R0, UC_ARM_REG_LR, UC_ARM_REG_PC)) 288 | # print(">>> \tretval: %d lr: 0x%08x address: 0x%08X pc: 0x%08x size: %d" % (retval, lr, address, pc, size)) 289 | # print_keystuff(uc) 290 | uc.emu_stop() 291 | 292 | # callback for tracing invalid memory access (READ or WRITE) 293 | def hook_mem_unmapped(uc, access, address, size, value, user_data): 294 | if access == UC_MEM_WRITE_UNMAPPED: 295 | print(">>> Missing memory is being WRITE at 0x%08x, data size = %u, data value = 0x%08x" \ 296 | %(address, size, value)) 297 | else: 298 | print(">>> Missing memory is being READ at 0x%08x, data size = %u, data value = 0x%08x" \ 299 | %(address, size, value)) 300 | # return False to indicate we want to stop emulation 301 | return False 302 | 303 | # callback for tracing memory access (READ or WRITE) 304 | def hook_mem_access(uc, access, address, size, value, user_data): 305 | old_val_bytes = uc.mem_read(address, size) 306 | old_val_bytes = uc.mem_read(address, size) 307 | struct_fmt = {1: '>> Memory is being WRITE at 0x%08x, data size = %u, data value = 0x%08x, old value = 0x%08x" \ 311 | %(address, size, value, old_val)) 312 | if address == PMC_BASE and value & (1 << 4) != 0: 313 | print(">>> PMC reset issued, stopping emu") 314 | uc.emu_stop() 315 | if address == PMC_BASE + PMC_SCRATCH0: 316 | print(">>> PMC_SCRATCH0 written") 317 | if address == PMC_BASE + PMC_SCRATCH1: 318 | print(">>> PMC_SCRATCH1 written") 319 | else: # READ 320 | print(">>> Memory is being READ at 0x%08x, data size = %u, data value = 0x%08x" \ 321 | %(address, size, old_val)) 322 | 323 | 324 | uc_inited_ctx = None 325 | uc_elf = None 326 | uc_stack_top = None 327 | uc_maxaddr = None 328 | def get_inited_uc(elf_path, key, iv, ct): 329 | global uc_inited_ctx, uc_elf, uc_stack_top, uc_maxaddr 330 | if uc_inited_ctx is None: 331 | elf = lief.parse(elf_path) 332 | uc_elf = elf 333 | tree = itree.IntervalTree() 334 | for seg in elf.segments: 335 | rstart = rounddown(seg.virtual_address, 4096) 336 | rend = roundup(seg.virtual_address + seg.virtual_size, 4096) 337 | rsize_content = roundup(seg.virtual_size, 4096) 338 | rsize_range = rend - rstart 339 | zpad_front = b'\x00' * (seg.virtual_address - rstart) 340 | zpad_back = b'\x00' * (rend - (seg.virtual_address + seg.virtual_size)) 341 | # print("rstart: 0x{:08x} rend: 0x{:08x} rsize_content: 0x{:08x} rsize_content: 0x{:08x}".format(rstart, rend, rsize_content, rsize_range)) 342 | assert(not tree.overlaps(rstart, rend)) 343 | tree[rstart:rend] = zpad_front + bytes(bytearray(seg.content)) + zpad_back 344 | minaddr = tree.begin() 345 | maxaddr = tree.end() 346 | uc_maxaddr = maxaddr 347 | # print("minaddr: 0x{:08x} maxaddr: 0x{:08x}".format(minaddr, maxaddr)) 348 | stack_gap = 1024*1024 349 | stack_size = 1024*1024 350 | stack_begin = tree.end() + stack_gap 351 | stack_end = stack_begin + stack_size 352 | tree[stack_begin:stack_end] = b'\x00' * stack_size 353 | stack_top = stack_end 354 | uc_stack_top = stack_top 355 | # print("tree: {}".format(tree)) 356 | minaddr = tree.begin() 357 | maxaddr = tree.end() 358 | # print("minaddr: 0x{:08x} maxaddr: 0x{:08x}".format(minaddr, maxaddr)) 359 | 360 | # print("Emulate thumb code") 361 | try: 362 | # Initialize emulator in thumb mode 363 | uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB) 364 | uc.elf = elf 365 | uc.maxaddr = maxaddr 366 | 367 | for seg in tree: 368 | uc.mem_map(seg.begin, seg.end - seg.begin) 369 | uc.mem_write(seg.begin, seg.data) 370 | 371 | uc.reg_write(UC_ARM_REG_SP, stack_top) 372 | uc.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on 373 | 374 | def hook_aes_init_marker(uc, address, size, user_data): 375 | uc.emu_stop() 376 | 377 | uc.key_sym = findsym(uc.elf, 'key') 378 | uc.iv_sym = findsym(uc.elf, 'iv') 379 | uc.ct_sym = findsym(uc.elf, 'ct') 380 | uc.pt_sym = findsym(uc.elf, 'pt') 381 | # uc.init_key_sym = findsym(uc.elf, 'init_key') 382 | # uc.init_iv_sym = findsym(uc.elf, 'init_iv') 383 | # uc.init_ct_sym = findsym(uc.elf, 'init_ct') 384 | # uc.init_pt_sym = findsym(uc.elf, 'init_pt') 385 | # uc.aes_init_done_sym = findsym(uc.elf, 'aes_init_done') 386 | uc.exit_sym = findsym(uc.elf, '_exit') 387 | 388 | aes_init_marker_sym = findsym(uc.elf, 'aes_init_marker') 389 | aes_init_marker_addr = aes_init_marker_sym.value & EVEN_MASK 390 | 391 | uc.hook_add(UC_HOOK_CODE, hook_aes_init_marker, begin=aes_init_marker_addr & EVEN_MASK, end=aes_init_marker_addr + 2) 392 | 393 | # print_keystuff(uc) 394 | uc.emu_start(uc.elf.entrypoint, uc.maxaddr) 395 | # print_keystuff(uc) 396 | mem_ctx = [] 397 | for region in uc.mem_regions(): 398 | mem_ctx.append({'addr': region[0], 'sz': region[1] - region[0] + 1, 'data': bytes(uc.mem_read(region[0], region[1] - region[0] + 1))}) 399 | uc_inited_ctx = mem_ctx 400 | except UcError as e: 401 | print("ERROR: %s" % e) 402 | 403 | elf = uc_elf 404 | uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB) 405 | uc.elf = elf 406 | uc.insn_count = 0 407 | uc.maxaddr = uc_maxaddr 408 | 409 | for seg in uc_inited_ctx: 410 | uc.mem_map(seg['addr'], seg['sz']) 411 | uc.mem_write(seg['addr'], seg['data']) 412 | 413 | uc.reg_write(UC_ARM_REG_SP, uc_stack_top) 414 | uc.reg_write(UC_ARM_REG_APSR, 0xFFFFFFFF) #All application flags turned on 415 | 416 | uc.saved_regs = all_regs(uc) 417 | 418 | uc.hook_add(UC_HOOK_CODE, hook_code_hamming_distance) 419 | 420 | uc.key_sym = findsym(uc.elf, 'key') 421 | uc.iv_sym = findsym(uc.elf, 'iv') 422 | uc.ct_sym = findsym(uc.elf, 'ct') 423 | uc.pt_sym = findsym(uc.elf, 'pt') 424 | # uc.init_key_sym = findsym(uc.elf, 'init_key') 425 | # uc.init_iv_sym = findsym(uc.elf, 'init_iv') 426 | # uc.init_ct_sym = findsym(uc.elf, 'init_ct') 427 | # uc.init_pt_sym = findsym(uc.elf, 'init_pt') 428 | # uc.aes_init_done_sym = findsym(uc.elf, 'aes_init_done') 429 | uc.exit_sym = findsym(uc.elf, '_exit') 430 | uc.aes_round_marker_sym = findsym(uc.elf, 'aes_round_marker') 431 | uc.aes_round_marker_addr = uc.aes_round_marker_sym.value & EVEN_MASK 432 | uc.aes_round_marks = [] 433 | uc.hook_add(UC_HOOK_CODE, hook_exit, begin=uc.exit_sym.value & EVEN_MASK, end=(uc.exit_sym.value & EVEN_MASK) + 2) 434 | 435 | assert(len(key) == 16) 436 | assert(len(iv) == 16) 437 | assert(len(ct) == 16) 438 | # print_keystuff(uc) 439 | uc.mem_write(uc.key_sym.value, key) 440 | uc.mem_write(uc.iv_sym.value, iv) 441 | uc.mem_write(uc.ct_sym.value, ct) 442 | # print_keystuff(uc) 443 | 444 | uc.hd_list = [] 445 | return uc 446 | 447 | @concurrent 448 | def gen_traces(elf_path, key, iv, ct): 449 | try: 450 | uc = get_inited_uc(elf_path, key, iv, ct) 451 | uc.emu_start(uc.elf.entrypoint, uc.maxaddr) 452 | pt = uc.mem_read(uc.pt_sym.value, uc.pt_sym.size) 453 | uc.hd_list = np.asarray(uc.hd_list, dtype=np.dtype(np.uint16)) 454 | # print_keystuff(uc) 455 | # print("pt: {}".format(ba.hexlify(pt))) 456 | # print("len(hd_list): {}".format(len(uc.hd_list))) 457 | return (pt, uc.aes_round_marks, uc.hd_list) 458 | 459 | except UcError as e: 460 | print("ERROR: %s" % e) 461 | 462 | def write_daredevil(key, cts, pts, hd_lists): 463 | prefix = 'foo' 464 | with open(prefix + '_in_daredevil.bin', 'wb') as in_f, open(prefix + '_out_daredevil.bin', 'wb') as out_f, open(prefix + '_trace_daredevil.bin', 'wb') as trace_f: 465 | for i in range(len(cts)): 466 | indata = cts[i] 467 | outdata = pts[i] 468 | tracedata = struct.pack("={}f".format(len(hd_lists[i])), *hd_lists[i]) 469 | in_f.write(indata) 470 | out_f.write(outdata) 471 | trace_f.write(tracedata) 472 | with open(prefix + '_daredevil.cfg', 'w') as cfgf: 473 | cfgf.write( 474 | """ 475 | [Traces] 476 | files=1 477 | trace_type={format} 478 | transpose=true 479 | index=0 480 | nsamples={nsamples} 481 | trace={traces_filename} {ntraces} {nsamples} 482 | 483 | [Guesses] 484 | files=1 485 | guess_type=u 486 | transpose=true 487 | guess={input_filename} {ntraces} 16 488 | 489 | [General] 490 | threads=8 491 | order=1 492 | return_type=double 493 | algorithm=AES 494 | position=LUT/AES_AFTER_SBOXINV 495 | round=0 496 | bitnum=none 497 | bytenum=all 498 | correct_key=0x{key} 499 | memory=4G 500 | top=20 501 | """.format(format='f', ntraces=len(hd_lists), nsamples=len(hd_lists[0]), traces_filename=os.getcwd() + '/' + prefix + '_trace_daredevil.bin', input_filename=os.getcwd() + '/' + prefix + '_in_daredevil.bin', key=ba.hexlify(key))) 502 | 503 | def main(argv): 504 | get_inited_uc(argv[1], b'\x00' * 16, b'\x00' * 16, b'\x00' * 16) # cache init 505 | key = ba.unhexlify('00112233445566778899aabbccddeeff') 506 | # key = b'\x00' * 16 507 | iv = b'\x00' * 16 508 | res = [] 509 | for i in range(4096): 510 | ct = os.urandom(16) 511 | # ct = b'\x00' * 16 512 | # print("main ct: {}".format(ba.hexlify(ct))) 513 | res.append({'ct': ct, 'res': gen_traces(argv[1], key, iv, ct)}) 514 | gen_traces.wait() 515 | cts = [] 516 | pts = [] 517 | hd_lists = [] 518 | round_marks = [] 519 | for r in res: 520 | ct = r['ct'] 521 | (pt, aes_round_marks, hd_list) = r['res'].result() 522 | cts.append(ct) 523 | pts.append(pt) 524 | round_marks.append(aes_round_marks) 525 | hd_lists.append(hd_list) 526 | # np.save('foo.npy', hd_list) 527 | write_daredevil(key, cts, pts, hd_lists) 528 | print("round_marks: {}".format(round_marks[0])) 529 | last = 0 530 | diffs = [] 531 | for n in round_marks[0]: 532 | diffs.append(n - last) 533 | last = n 534 | print("diffs: {}".format(diffs)) 535 | 536 | if __name__ == "__main__": 537 | sys.exit(main(sys.argv)) 538 | --------------------------------------------------------------------------------