├── README.md ├── input_client_ida.py ├── juniEmul.py ├── sample_arm_code_binary └── screenshots └── juniEmul.png /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### Overview 3 | 4 | **JuniEmu** is an emulator interface designed for ARM 32-bit. 5 | 6 | JuniEmu is using Unicorn framework for emulation, Capstone disassembly framework and Python's standard GUI package - Tkinter. 7 | 8 | The goal of the project was to visually emulate (single step, breakpoint, memory view\edit) raw ARM binaries (bootloaders, embedded firmware, standalone functions, etc). 9 | 10 | ![](./screenshots/juniEmul.png) 11 | 12 | ## Features 13 | 14 | - Single steeping and breakpoint support 15 | - Memory view and edit 16 | - Registers view and edit 17 | - Instruction highlighting 18 | 19 | ## Commandline cmd 20 | 21 | `help` - print help 22 | 23 | `d ` - dump memory to current view windows e.g. `d 0x1030` 24 | 25 | `u ` - unassemble address or register e.g. `u pc`, `u 0x1030`, `u 0x1031` (disasm 0x1030 in Thumb mode) 26 | 27 | `bp ` - set breakpoint 28 | 29 | `bc ` - clear breakpoint 30 | 31 | `bl` - list breakpoints 32 | 33 | `r ` - change register value e.g. `r r0 0x101` 34 | 35 | `rr` - print registers 36 | 37 | `?` - calc expression 38 | 39 | `dump "PATH"` - dump memory range to a file e.g. `dump 0x1030 100 "/Users/bob/Desktop/dump.bin"` 40 | 41 | ## Question 42 | 43 | - How to change program counter register to e.g. 0x1030 (ARM mode) ? 44 | 45 | `r pc 0x1030` 46 | 47 | - How to change program counter to e.g. 0x1030 (Thumb mode) ? 48 | 49 | To switch into thumb mode provide unaligned address (add +1 to the destination address) 50 | 51 | `r pc 0x1031` 52 | 53 | ### Issues 54 | 55 | - Not tested with Python 3 56 | - Not fully tested on Windows, MacOS 57 | - Input server will freeze the UI until until receiving bytes from the client 58 | 59 | ### Dependencies (Python bindings) 60 | 61 | - python TkInter (`sudo apt-get install python-tk`) 62 | - Capstone https://github.com/aquynh/capstone 63 | - Unicorn https://github.com/unicorn-engine/unicorn 64 | -------------------------------------------------------------------------------- /input_client_ida.py: -------------------------------------------------------------------------------- 1 | # 2 | # simple IDAPython script to send bytes from address, lenght to JuniEmu input server 3 | # 4 | # *Important* IS_THUMB_MODE var must be set to 0 or 1 in the JuniEmu.py to indicate starting mode 5 | # 6 | 7 | import socket 8 | import struct 9 | 10 | buff = b"" 11 | 12 | code_start = 0x0010200 13 | code_len = 0x100 14 | 15 | for ptr in range(code_start, code_start + code_len): 16 | b = Byte(ptr) 17 | buff += struct.pack('B',b) 18 | 19 | host = socket.gethostname() 20 | port = 5559 # The same port as used by the server 21 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 22 | s.connect((host, port)) 23 | s.sendall(buff) 24 | s.close() 25 | -------------------------------------------------------------------------------- /juniEmul.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | ''' 4 | JuniEmu - emulator interface for ARM 32-bit. 5 | 6 | Using Unicorn framework for emulation, Capstone disassembly framework and 7 | Python's standard GUI (Graphical User Interface) package - Tkinter. 8 | 9 | ''' 10 | 11 | import sys 12 | import os 13 | from collections import namedtuple 14 | import collections 15 | import threading 16 | import time 17 | import signal 18 | import struct 19 | from capstone.arm import * 20 | import binascii 21 | import pdb 22 | import platform 23 | import socket 24 | import multiprocessing 25 | 26 | from unicorn import * 27 | from unicorn.arm_const import * 28 | from unicorn.unicorn_const import * 29 | 30 | if sys.version_info[0] < 3: 31 | from capstone import * 32 | 33 | if sys.version_info[0] < 3: 34 | import Tkinter as tk 35 | import ttk as ttk 36 | from tkFileDialog import askopenfilename 37 | import tkFont 38 | else: 39 | import tkinter as tk 40 | from tkinter import filedialog 41 | import tkinter.ttk as ttk 42 | import tkinter.messagebox as msg 43 | import tkinter.font as tkFont 44 | 45 | BACKGROUND_COLOR = "black" 46 | TEXT_COLOR = "#C0C0C0" 47 | FONT_FAMILY = "Courier New" 48 | FONT_SIZE = 9 49 | 50 | print ("Unicorn version: {}.{}.{}".format(UC_VERSION_MAJOR, UC_VERSION_MINOR, UC_VERSION_EXTRA)) 51 | 52 | ################################################################################################## 53 | # CONFIG 54 | ################################################################################################## 55 | 56 | IMAGEBASE = 0x00020000 57 | ENTRYPOINT = 0x0 58 | 59 | IS_THUMB_MODE = 0 # start mode 60 | 61 | # leave empty if you want to choose file via open dialog 62 | TEST_FILENAME = "" 63 | 64 | ################################################################################################## 65 | # Registers start value 66 | ################################################################################################## 67 | 68 | INITIALIZE_REG_R0 = 0 69 | INITIALIZE_REG_R1 = 0 70 | INITIALIZE_REG_R2 = 0 71 | INITIALIZE_REG_R3 = 0 72 | INITIALIZE_REG_R4 = 0 73 | INITIALIZE_REG_R5 = 0 74 | INITIALIZE_REG_R6 = 0 75 | INITIALIZE_REG_R7 = 0 76 | INITIALIZE_REG_R8 = 0 77 | INITIALIZE_REG_R9 = 0 78 | INITIALIZE_REG_R10 = 0 79 | INITIALIZE_REG_R11 = 0 80 | INITIALIZE_REG_R12 = 0 81 | 82 | ################################################################################################## 83 | # 84 | ################################################################################################## 85 | 86 | MAX_INSTR_SIZE = 4 87 | VIEW_MEMORY_DUMP_RANGE = 16 88 | PRINT_TO_CONSOL = 0 89 | PRINT_DASM_DECOMPOSED_PC = 0 90 | PRINT_EMULATION_LOG = 0 91 | 92 | program_exit = False 93 | g_current_memory_view_frame_number = 0 94 | 95 | TEST_CODE = "\x00\x00\x00\x00\x00\x00\x00\x01" 96 | 97 | EMUL_FUNCTION_NOP = 0 98 | EMUL_FUNCTION_STRLEN = 1 99 | EMUL_FUNCTION_AES_CRYPT = 2 100 | 101 | EMUL_ADDR_FUNCTIONS_GLOBAL_TAB = {} 102 | # EMUL_ADDR_FUNCTIONS_GLOBAL_TAB[0xFFFFFFFFF] = EMUL_FUNCTION_STRLEN 103 | 104 | ################################################################################################## 105 | # .CODE 106 | ################################################################################################## 107 | 108 | IN_UC_ARCH = UC_ARCH_ARM 109 | IN_CS_MODE = CS_ARCH_ARM 110 | 111 | if IS_THUMB_MODE == 0: 112 | IN_UC_ARCH_MODE = UC_MODE_ARM 113 | IN_CS_ARCH_MODE = CS_MODE_ARM 114 | print("Using ARM - ARM mode") 115 | else: 116 | IN_UC_ARCH_MODE = UC_MODE_THUMB 117 | IN_CS_ARCH_MODE = CS_MODE_THUMB 118 | print("Using ARM - THUMB mode") 119 | 120 | print("Imagebase: 0x" + '{:08X}'.format(IMAGEBASE) + " Entrypoint: 0x" + '{:08X}'.format(ENTRYPOINT)) 121 | 122 | lock = threading.Lock() 123 | 124 | _python3 = sys.version_info.major == 3 125 | 126 | 127 | def to_hex(s): 128 | if _python3: 129 | return " ".join("0x{0:02x}".format(c) for c in s) 130 | else: 131 | return " ".join("0x{0:02x}".format(ord(c)) for c in s) 132 | 133 | 134 | def to_hex2(s): 135 | if _python3: 136 | r = "".join("{0:02x}".format(c) for c in s) 137 | else: 138 | r = "".join("{0:02x}".format(ord(c)) for c in s) 139 | while r[0] == '0': r = r[1:] 140 | return r 141 | 142 | 143 | def to_x(s): 144 | from struct import pack 145 | if not s: return '0' 146 | x = pack(">q", s) 147 | while x[0] in ('\0', 0): x = x[1:] 148 | return to_hex2(x) 149 | 150 | 151 | def to_x_32(s): 152 | from struct import pack 153 | if not s: return '0' 154 | x = pack(">i", s) 155 | while x[0] in ('\0', 0): x = x[1:] 156 | return to_hex2(x) 157 | 158 | 159 | def BREAK_HERE(): 160 | pdb.set_trace() 161 | return 162 | 163 | 164 | class Dasm(object): 165 | def __init__(self): 166 | self.abc = "" 167 | self._set_dasm_mode() 168 | 169 | def _set_dasm_mode(self): 170 | self.md_lite = Cs(IN_CS_MODE, IN_CS_ARCH_MODE) 171 | self.md_lite.detail = True 172 | self.md_lite.imm_unsigned = True 173 | self.md_lite.syntax = CS_OPT_SYNTAX_NOREGNAME 174 | 175 | self.md_full = Cs(IN_CS_MODE, IN_CS_ARCH_MODE) 176 | self.md_full.detail = True 177 | self.md_full.imm_unsigned = True 178 | self.md_full.syntax = CS_OPT_SYNTAX_NOREGNAME 179 | 180 | def print_insn_detail(self, insn): 181 | print("0x%x:\t%s\t%s" % (insn.address, insn.mnemonic, insn.op_str)) 182 | 183 | def print_detail(self, insn): 184 | # print address, mnemonic and operands 185 | print("0x%x:\t%s\t%s" % (insn.address, insn.mnemonic, insn.op_str)) 186 | 187 | # "data" instruction generated by SKIPDATA option has no detail 188 | if insn.id == 0: 189 | return 190 | 191 | # 192 | # example from http://www.capstone-engine.org/lang_python.html 193 | # 194 | if len(insn.regs_read) > 0: 195 | print("\tImplicit registers read: "), 196 | for r in insn.regs_read: 197 | print("%s \n" % insn.reg_name(r)) 198 | 199 | if len(insn.regs_write) > 0: 200 | print("\tImplicit registers write: "), 201 | for r in insn.regs_write: 202 | print("%s \n" % insn.reg_name(r)) 203 | 204 | if len(insn.operands) > 0: 205 | print("\top_count: %u" % len(insn.operands)) 206 | c = 0 207 | for i in insn.operands: 208 | if i.type == ARM_OP_REG: 209 | print("\t\toperands[%u].type: REG = %s" % (c, insn.reg_name(i.reg))) 210 | if i.type == ARM_OP_IMM: 211 | print("\t\toperands[%u].type: IMM = 0x%s" % (c, to_x_32(i.imm))) 212 | if i.type == ARM_OP_PIMM: 213 | print("\t\toperands[%u].type: P-IMM = %u" % (c, i.imm)) 214 | if i.type == ARM_OP_CIMM: 215 | print("\t\toperands[%u].type: C-IMM = %u" % (c, i.imm)) 216 | if i.type == ARM_OP_FP: 217 | print("\t\toperands[%u].type: FP = %f" % (c, i.fp)) 218 | if i.type == ARM_OP_SYSREG: 219 | print("\t\toperands[%u].type: SYSREG = %u" % (c, i.reg)) 220 | if i.type == ARM_OP_SETEND: 221 | if i.setend == ARM_SETEND_BE: 222 | print("\t\toperands[%u].type: SETEND = be" % c) 223 | else: 224 | print("\t\toperands[%u].type: SETEND = le" % c) 225 | if i.type == ARM_OP_MEM: 226 | print("\t\toperands[%u].type: MEM" % c) 227 | if i.mem.base != 0: 228 | print("\t\t\toperands[%u].mem.base: REG = %s" % (c, insn.reg_name(i.mem.base))) 229 | if i.mem.index != 0: 230 | print("\t\t\toperands[%u].mem.index: REG = %s" % (c, insn.reg_name(i.mem.index))) 231 | if i.mem.scale != 1: 232 | print("\t\t\toperands[%u].mem.scale: %u" % (c, i.mem.scale)) 233 | if i.mem.disp != 0: 234 | print("\t\t\toperands[%u].mem.disp: 0x%s" % (c, to_x_32(i.mem.disp))) 235 | 236 | if i.shift.type != ARM_SFT_INVALID and i.shift.value: 237 | print("\t\t\tShift: %u = %u" % (i.shift.type, i.shift.value)) 238 | if i.vector_index != -1: 239 | print("\t\t\toperands[%u].vector_index = %u" % (c, i.vector_index)) 240 | if i.subtracted: 241 | print("\t\t\toperands[%u].subtracted = True" % c) 242 | 243 | c += 1 244 | 245 | if insn.update_flags: 246 | print("\tUpdate-flags: True") 247 | if insn.writeback: 248 | print("\tWrite-back: True") 249 | if not insn.cc in [ARM_CC_AL, ARM_CC_INVALID]: 250 | print("\tCode condition: %u" % insn.cc) 251 | if insn.cps_mode: 252 | print("\tCPSI-mode: %u" % (insn.cps_mode)) 253 | if insn.cps_flag: 254 | print("\tCPSI-flag: %u" % (insn.cps_flag)) 255 | if insn.vector_data: 256 | print("\tVector-data: %u" % (insn.vector_data)) 257 | if insn.vector_size: 258 | print("\tVector-size: %u" % (insn.vector_size)) 259 | if insn.usermode: 260 | print("\tUser-mode: True") 261 | if insn.mem_barrier: 262 | print("\tMemory-barrier: %u" % (insn.mem_barrier)) 263 | 264 | def disasm_buffer_light(self, base_addr, buf, dasm_mode): 265 | self.md_lite.mode = dasm_mode 266 | address = size = mnemonic = op_str = 0 267 | try: 268 | for (address, size, mnemonic, op_str) in self.md_lite.disasm_lite(buf, base_addr, 1): 269 | break 270 | except CsError as e: 271 | assert (False) 272 | print("ERROR: %s" % e) 273 | 274 | return address, size, mnemonic, op_str 275 | 276 | def disasm_buffer_full(self, base_addr, buf, dasm_mode): 277 | self.md_full.mode = dasm_mode 278 | try: 279 | for insn in self.md_full.disasm(buf, base_addr, 1): 280 | self.print_detail(insn) 281 | except CsError as e: 282 | assert (False) 283 | print("ERROR: %s" % e) 284 | return 285 | 286 | 287 | class EmulatorCore(object): 288 | def __init__(self): 289 | self.filename = "" 290 | self.last_error = "" 291 | self.disasm_number_of_lines = 0 292 | self.file_data = b'' 293 | self.threads = [] 294 | self._create_objects() 295 | self.emul_data_waiting = False 296 | self.update_mem_view = False 297 | self.update_dasm_view = False 298 | self.update_register_view = False 299 | self.last_emul_ip = 0 300 | self.tracking_curr_instr_ip = 0 301 | self.highlight_line_num = -1 302 | self.wait_for_user_input = False 303 | self.go_emul = False 304 | self.emulator_running = False 305 | self.break_run = False 306 | self.mem_view1_addr = 0 307 | self.mem_view1_range = 0 308 | self.mem_view1_buffer = b"" 309 | self.mem_view2_addr = 0 310 | self.mem_view2_range = 0 311 | self.mem_view2_buffer = b"" 312 | self.run_counter = 0 313 | self.bp_settings_dict = {} 314 | self.dasm_listing_dict = collections.OrderedDict() 315 | self.last_hook_regs_data = {} 316 | self.mem_map_region_rwx = [] 317 | self.disasm_mode = None 318 | 319 | def set_disasm_number_of_lines(self, ln_count): 320 | self.disasm_number_of_lines = ln_count 321 | 322 | def _create_objects(self): 323 | self.dasm = Dasm() 324 | 325 | def load_binary_stream(self, bin_stream): 326 | self.file_data = bin_stream 327 | return len(bin_stream) 328 | 329 | def load_input_file(self, fname): 330 | if fname == "": 331 | return 0 332 | 333 | with open(fname, 'rb') as f: 334 | self.file_data = f.read() 335 | 336 | return len(self.file_data) 337 | 338 | def fetch_org_bytes(self, offset, fetch_limit): 339 | if (offset + fetch_limit) < len(self.file_data): 340 | return self.file_data[offset: offset + fetch_limit] 341 | elif offset < len(self.file_data): 342 | rest = len(self.file_data) - offset 343 | return self.file_data[offset: offset + rest] 344 | else: 345 | return "" 346 | 347 | def is_emul_data_waiting(self): 348 | global lock 349 | with lock: 350 | return self.emul_data_waiting 351 | 352 | def reset_emul_data_waiting(self): 353 | global lock 354 | with lock: 355 | self.emul_data_waiting = False 356 | 357 | def set_emul_data_waiting(self): 358 | global lock 359 | with lock: 360 | self.emul_data_waiting = True 361 | 362 | def set_new_memory_view_addr_on_window_number(self, addr, window_view): 363 | global lock 364 | with lock: 365 | if window_view == 0: 366 | self.mem_view1_addr = addr 367 | elif window_view == 1: 368 | self.mem_view2_addr = addr 369 | 370 | def set_mem_view_range(self, r, frame_num): 371 | if frame_num == 0: 372 | self.mem_view1_range = r 373 | elif frame_num == 1: 374 | self.mem_view2_range = r 375 | else: 376 | assert (False) 377 | 378 | def get_mem_view1_addr_data(self): 379 | global lock 380 | with lock: 381 | return self.mem_view1_addr, self.mem_view1_buffer 382 | 383 | def get_mem_view2_addr_data(self): 384 | global lock 385 | with lock: 386 | return self.mem_view2_addr, self.mem_view2_buffer 387 | 388 | def get_disasm_listing_dict(self): 389 | return self.dasm_listing_dict 390 | 391 | def get_highlight_line_num(self): 392 | if self.highlight_line_num == -1: 393 | return -1 394 | # text line number start from 1 395 | return self.highlight_line_num + 1 396 | 397 | def is_wait_for_user_input(self): 398 | global lock 399 | with lock: 400 | return self.wait_for_user_input 401 | 402 | def reset_wait_for_user_input(self): 403 | global lock 404 | with lock: 405 | self.wait_for_user_input = False 406 | 407 | def set_wait_for_user_input(self): 408 | global lock 409 | with lock: 410 | self.wait_for_user_input = True 411 | 412 | def is_update_mem_view(self): 413 | global lock 414 | with lock: 415 | return self.update_mem_view 416 | 417 | def reset_update_mem_view(self): 418 | global lock 419 | with lock: 420 | self.update_mem_view = False 421 | 422 | def set_update_mem_view(self): 423 | global lock 424 | with lock: 425 | self.update_mem_view = True 426 | 427 | def is_dasm_view_update(self): 428 | global lock 429 | with lock: 430 | return self.update_dasm_view 431 | 432 | def reset_dasm_view_update(self): 433 | global lock 434 | with lock: 435 | self.update_dasm_view = False 436 | 437 | def set_dasm_view_update(self): 438 | global lock 439 | with lock: 440 | self.update_dasm_view = True 441 | 442 | def is_register_view_update(self): 443 | global lock 444 | with lock: 445 | return self.update_register_view 446 | 447 | def reset_update_register_view(self): 448 | global lock 449 | with lock: 450 | self.update_register_view = False 451 | 452 | def set_update_register_view(self): 453 | global lock 454 | with lock: 455 | self.update_register_view = True 456 | 457 | def dasm_listing_index_to_addr(self, i): 458 | global lock 459 | with lock: 460 | j = 0 461 | for addr_key in self.dasm_listing_dict: 462 | if i == j: 463 | return True, addr_key 464 | j = j + 1 465 | return False, 0 466 | 467 | def reset_run_counter(self): 468 | self.run_counter = 0 469 | 470 | def inc_run_counter(self): 471 | self.run_counter = self.run_counter + 1 472 | 473 | def set_register_data(self, reg_name, reg_val): 474 | if not self.is_wait_for_user_input(): 475 | return False 476 | 477 | if reg_name == 'r0': 478 | self.uc.reg_write(UC_ARM_REG_R0, reg_val) 479 | return True 480 | elif reg_name == 'r1': 481 | self.uc.reg_write(UC_ARM_REG_R1, reg_val) 482 | return True 483 | elif reg_name == 'r2': 484 | self.uc.reg_write(UC_ARM_REG_R2, reg_val) 485 | return True 486 | elif reg_name == 'r3': 487 | self.uc.reg_write(UC_ARM_REG_R3, reg_val) 488 | return True 489 | elif reg_name == 'r4': 490 | self.uc.reg_write(UC_ARM_REG_R4, reg_val) 491 | return True 492 | elif reg_name == 'r5': 493 | self.uc.reg_write(UC_ARM_REG_R5, reg_val) 494 | return True 495 | elif reg_name == 'r6': 496 | self.uc.reg_write(UC_ARM_REG_R6, reg_val) 497 | return True 498 | elif reg_name == 'r7': 499 | self.uc.reg_write(UC_ARM_REG_R7, reg_val) 500 | return True 501 | elif reg_name == 'r8': 502 | self.uc.reg_write(UC_ARM_REG_R8, reg_val) 503 | return True 504 | elif reg_name == 'r9': 505 | self.uc.reg_write(UC_ARM_REG_R9, reg_val) 506 | return True 507 | elif reg_name == 'r10' or reg_name == 'sl': 508 | self.uc.reg_write(UC_ARM_REG_R10, reg_val) 509 | return True 510 | elif reg_name == 'r11': 511 | self.uc.reg_write(UC_ARM_REG_R11, reg_val) 512 | return True 513 | elif reg_name == 'r12': 514 | self.uc.reg_write(UC_ARM_REG_R12, reg_val) 515 | return True 516 | elif reg_name == 'r13' or reg_name == 'sp': 517 | self.uc.reg_write(UC_ARM_REG_R13, reg_val) 518 | return True 519 | elif reg_name == 'r14' or reg_name == 'lr': 520 | self.uc.reg_write(UC_ARM_REG_R14, reg_val) 521 | return True 522 | elif reg_name == 'r15' or reg_name == 'pc': 523 | self.uc.reg_write(UC_ARM_REG_R15, reg_val) 524 | return True 525 | elif reg_name == 'psr': 526 | self.uc.reg_write(UC_ARM_REG_CPSR, reg_val) 527 | return True 528 | 529 | return False 530 | 531 | def get_register_last_data(self): 532 | return self.last_hook_regs_data 533 | 534 | def _save_register_last_data(self): 535 | assert (self.is_wait_for_user_input() == False) 536 | 537 | r0 = self.uc.reg_read(UC_ARM_REG_R0) 538 | r1 = self.uc.reg_read(UC_ARM_REG_R1) 539 | r2 = self.uc.reg_read(UC_ARM_REG_R2) 540 | r3 = self.uc.reg_read(UC_ARM_REG_R3) 541 | r4 = self.uc.reg_read(UC_ARM_REG_R4) 542 | r5 = self.uc.reg_read(UC_ARM_REG_R5) 543 | r6 = self.uc.reg_read(UC_ARM_REG_R6) 544 | r7 = self.uc.reg_read(UC_ARM_REG_R7) 545 | r8 = self.uc.reg_read(UC_ARM_REG_R8) 546 | r9 = self.uc.reg_read(UC_ARM_REG_R9) 547 | sl = r10 = self.uc.reg_read(UC_ARM_REG_R10) 548 | fp = r11 = self.uc.reg_read(UC_ARM_REG_R11) 549 | ip = r12 = self.uc.reg_read(UC_ARM_REG_R12) 550 | sp = r13 = self.uc.reg_read(UC_ARM_REG_R13) 551 | lr = r14 = self.uc.reg_read(UC_ARM_REG_R14) 552 | pc = r15 = self.uc.reg_read(UC_ARM_REG_R15) 553 | psr = self.uc.reg_read(UC_ARM_REG_CPSR) 554 | 555 | self.last_hook_regs_data = {} 556 | self.last_hook_regs_data['r0'] = r0 557 | self.last_hook_regs_data['r1'] = r1 558 | self.last_hook_regs_data['r2'] = r2 559 | self.last_hook_regs_data['r3'] = r3 560 | self.last_hook_regs_data['r4'] = r4 561 | self.last_hook_regs_data['r5'] = r5 562 | self.last_hook_regs_data['r6'] = r6 563 | self.last_hook_regs_data['r7'] = r7 564 | self.last_hook_regs_data['r8'] = r8 565 | self.last_hook_regs_data['r9'] = r9 566 | self.last_hook_regs_data['r10'] = r10 567 | self.last_hook_regs_data['r11'] = r11 568 | self.last_hook_regs_data['r12'] = r12 569 | self.last_hook_regs_data['sp'] = sp 570 | self.last_hook_regs_data['lr'] = lr 571 | self.last_hook_regs_data['pc'] = pc 572 | self.last_hook_regs_data['psr'] = psr 573 | 574 | def get_register_data(self): 575 | reg_data = {} 576 | if not self.is_wait_for_user_input(): 577 | return reg_data 578 | 579 | r0 = self.uc.reg_read(UC_ARM_REG_R0) 580 | r1 = self.uc.reg_read(UC_ARM_REG_R1) 581 | r2 = self.uc.reg_read(UC_ARM_REG_R2) 582 | r3 = self.uc.reg_read(UC_ARM_REG_R3) 583 | r4 = self.uc.reg_read(UC_ARM_REG_R4) 584 | r5 = self.uc.reg_read(UC_ARM_REG_R5) 585 | r6 = self.uc.reg_read(UC_ARM_REG_R6) 586 | r7 = self.uc.reg_read(UC_ARM_REG_R7) 587 | r8 = self.uc.reg_read(UC_ARM_REG_R8) 588 | r9 = self.uc.reg_read(UC_ARM_REG_R9) 589 | sl = r10 = self.uc.reg_read(UC_ARM_REG_R10) 590 | fp = r11 = self.uc.reg_read(UC_ARM_REG_R11) 591 | ip = r12 = self.uc.reg_read(UC_ARM_REG_R12) 592 | sp = r13 = self.uc.reg_read(UC_ARM_REG_R13) 593 | lr = r14 = self.uc.reg_read(UC_ARM_REG_R14) 594 | pc = r15 = self.uc.reg_read(UC_ARM_REG_R15) 595 | psr = self.uc.reg_read(UC_ARM_REG_CPSR) 596 | 597 | reg_data['r0'] = r0 598 | reg_data['r1'] = r1 599 | reg_data['r2'] = r2 600 | reg_data['r3'] = r3 601 | reg_data['r4'] = r4 602 | reg_data['r5'] = r5 603 | reg_data['r6'] = r6 604 | reg_data['r7'] = r7 605 | reg_data['r8'] = r8 606 | reg_data['r9'] = r9 607 | reg_data['r10'] = r10 608 | reg_data['r11'] = r11 609 | reg_data['r12'] = r12 610 | reg_data['sp'] = sp 611 | reg_data['lr'] = lr 612 | reg_data['pc'] = pc 613 | reg_data['psr'] = psr 614 | return reg_data 615 | 616 | def set_break_run(self): 617 | if self.is_emulator_running() and self.is_wait_for_user_input() == False: 618 | self.break_run = True 619 | 620 | def is_emulator_running(self): 621 | return self.emulator_running 622 | 623 | def stop_emulator(self): 624 | if self.is_emulator_running() and not self.is_emul_data_waiting() and self.is_wait_for_user_input(): 625 | self.uc.emu_stop() 626 | self.reset_wait_for_user_input() 627 | 628 | def gogo_emulator(self): 629 | if self.is_emulator_running() and not self.is_emul_data_waiting() and self.is_wait_for_user_input(): 630 | self.go_emul = True 631 | self.reset_wait_for_user_input() 632 | 633 | def single_step_emulator(self): 634 | if self.is_emulator_running() and not self.is_emul_data_waiting() and self.is_wait_for_user_input(): 635 | self.reset_wait_for_user_input() 636 | 637 | def worker(self): 638 | print ("Emulator start: %s" % (time.ctime(time.time()))) 639 | self.set_emul_syscall_address_table() 640 | self.run_emulator() 641 | self.reset_emul() 642 | 643 | def reset_emul(self): 644 | self.emulator_running = False 645 | self.threads = [] 646 | self.reset_emul_data_waiting() 647 | self.reset_update_mem_view() 648 | self.reset_dasm_view_update() 649 | self.reset_update_register_view() 650 | self.last_emul_ip = 0 651 | self.tracking_curr_instr_ip = 0 652 | self.highlight_line_num = -1 653 | self.go_emul = False 654 | self.break_run = False 655 | self.mem_view1_addr = 0 656 | self.mem_view1_buffer = b"" 657 | self.mem_view2_addr = 0 658 | self.mem_view2_buffer = b"" 659 | self.dasm_listing_dict = collections.OrderedDict() 660 | self.mem_map_region_rwx = [] 661 | 662 | def start_emul_thread(self): 663 | self.emulator_running = True 664 | self.last_error = "" 665 | 666 | t = threading.Thread(target=self.worker) 667 | self.threads.append(t) 668 | t.start() 669 | 670 | def read_emul_memory_range(self, address, mem_size): 671 | emul_mem = self.uc.mem_read(address, mem_size) 672 | return emul_mem 673 | 674 | def write_test_data(self): 675 | if self.is_wait_for_user_input(): 676 | pass 677 | # bemem = b"\x00" 678 | # self.uc.mem_write(self.stack_address, bemem) 679 | # self.uc.mem_write(IMAGEBASE, b"\x31\x22") 680 | 681 | def get_dump_memory(self, dump_begin, dump_range): 682 | 683 | if not self.is_wait_for_user_input(): 684 | return 685 | 686 | buffer = b"" 687 | ba = bytearray() 688 | if dump_range == 0: 689 | return "" 690 | try: 691 | ba = self.read_emul_memory_range(dump_begin, dump_range) 692 | except UcError as e: 693 | if e.errno == UC_ERR_FETCH_UNMAPPED: 694 | print("get_dump_memory error") 695 | 696 | for i in range(len(ba)): 697 | b = ba[i] 698 | buffer += struct.pack('B', b) 699 | 700 | return buffer 701 | 702 | def write_byte_at_memory_addr(self, target_addr, nibble, mask): 703 | if self.is_wait_for_user_input(): 704 | mem_ba = self.uc.mem_read(target_addr, 1) 705 | if len(mem_ba) == 0: 706 | assert (False) 707 | return 708 | 709 | b = mem_ba[0] 710 | if mask == 0x0F: 711 | nn = (nibble << 4) 712 | print (hex(b)) 713 | b = nn | (b & 0x0F) 714 | elif mask == 0xF0: 715 | b = (b & 0xF0) | (nibble & 0x0F) 716 | else: 717 | return 718 | s = b"" 719 | s += struct.pack('B', b) 720 | self.uc.mem_write(target_addr, s) 721 | 722 | def reload_dasm_listing(self): 723 | global program_exit 724 | if program_exit: 725 | return False 726 | if not self.is_wait_for_user_input(): 727 | return False 728 | 729 | dasm_listing_top_addr = 0 730 | if len(self.dasm_listing_dict): 731 | for key in self.dasm_listing_dict: 732 | dasm_listing_top_addr = key 733 | break 734 | else: 735 | return False 736 | 737 | self.update_dasm_listing(dasm_listing_top_addr, self.last_emul_ip, self.disasm_mode) 738 | return True 739 | 740 | def get_dasm_listing_first_addr(self): 741 | first_addr = None 742 | for key in self.dasm_listing_dict: 743 | first_addr = key 744 | break 745 | return first_addr 746 | 747 | def get_dasm_listing_last_addr(self): 748 | last_addr = None 749 | for key in self.dasm_listing_dict: 750 | last_addr = key 751 | return last_addr 752 | 753 | def update_buffer_view_memory1(self): 754 | self.mem_view1_buffer = b"" 755 | ba = bytearray() 756 | if self.mem_view1_range == 0: 757 | return 758 | try: 759 | ba = self.read_emul_memory_range(self.mem_view1_addr, self.mem_view1_range) 760 | except UcError as e: 761 | if e.errno == UC_ERR_FETCH_UNMAPPED: 762 | print("update_view_memory1 error") 763 | 764 | for i in range(len(ba)): 765 | b = ba[i] 766 | self.mem_view1_buffer += struct.pack('B', b) 767 | 768 | def update_buffer_view_memory2(self): 769 | self.mem_view2_buffer = b"" 770 | ba = bytearray() 771 | if self.mem_view2_range == 0: 772 | return 773 | try: 774 | ba = self.read_emul_memory_range(self.mem_view2_addr, self.mem_view2_range) 775 | except UcError as e: 776 | if e.errno == UC_ERR_FETCH_UNMAPPED: 777 | print("update_view_memory2 error") 778 | 779 | for i in range(len(ba)): 780 | b = ba[i] 781 | self.mem_view2_buffer += struct.pack('B', b) 782 | 783 | def update_dasm_listing(self, dasm_listing_view_top_first_addr, ip_address, dasm_mode): 784 | global PRINT_TO_CONSOL 785 | buffer = self.read_emul_memory_range(dasm_listing_view_top_first_addr, 786 | self.disasm_number_of_lines * MAX_INSTR_SIZE) 787 | buffer_str = b"" 788 | for i in range(len(buffer)): 789 | b = buffer[i] 790 | buffer_str += struct.pack('B', b) 791 | 792 | self.dasm_listing_dict = collections.OrderedDict() 793 | curr_addr = dasm_listing_view_top_first_addr 794 | offset = 0 795 | self.highlight_line_num = -1 796 | 797 | for ln in range(self.disasm_number_of_lines): 798 | t = self.dasm.disasm_buffer_light(curr_addr, buffer_str[offset:], dasm_mode) 799 | (ln_dasm_address, inst_size, mnemonic, op_str) = t 800 | if inst_size == 0: 801 | if len(buffer_str[offset:]) >= 4: 802 | inst_size = 4 803 | ln_dasm_address = curr_addr 804 | mnemonic = "" 805 | op_str = "" 806 | else: 807 | break 808 | 809 | if ln_dasm_address == ip_address: 810 | self.highlight_line_num = ln 811 | if PRINT_DASM_DECOMPOSED_PC: 812 | self.dasm.disasm_buffer_full(curr_addr, buffer_str[offset:], dasm_mode) 813 | 814 | hex_str = "" 815 | for j in range(inst_size): 816 | b = buffer_str[offset + j] 817 | byte_str = '{:02X}'.format(ord(b)) 818 | hex_str += byte_str 819 | inst_str = "" 820 | inst_str = "{0}{1}{2}".format(mnemonic, str(" "), op_str) 821 | 822 | # pdb.set_trace() 823 | 824 | addr_str = '{:08X}'.format(ln_dasm_address) 825 | line = addr_str + " " + hex_str.ljust(8) + " " + inst_str 826 | 827 | if PRINT_TO_CONSOL: 828 | print(line) 829 | 830 | # replace address with syscall name 831 | emul_syscall_str = self.addr_to_emul_syscall_instr_str(ln_dasm_address) 832 | if emul_syscall_str != "": 833 | addr_str = '{:08X}'.format(ln_dasm_address) 834 | line = addr_str + " " + hex_str.ljust(8) + " " + "bl " + emul_syscall_str 835 | 836 | self.dasm_listing_dict[curr_addr] = line 837 | 838 | offset += inst_size 839 | curr_addr += inst_size 840 | 841 | def get_last_error(self): 842 | return self.last_error 843 | 844 | def toggle_bp_settings(self, bp_addr, bp_state): 845 | global lock 846 | with lock: 847 | self.bp_settings_dict[bp_addr] = bp_state 848 | 849 | def get_bp_settings(self): 850 | global lock 851 | with lock: 852 | return self.bp_settings_dict 853 | 854 | def get_bp_settings_for_addr(self, bp_addr): 855 | global lock 856 | with lock: 857 | for key_addr in self.bp_settings_dict: 858 | if key_addr == bp_addr: 859 | return True, self.bp_settings_dict[key_addr] 860 | return False, 0 861 | 862 | def del_bp_addr(self, bp_addr): 863 | global lock 864 | with lock: 865 | for key in self.bp_settings_dict: 866 | if bp_addr == key: 867 | self.bp_settings_dict.pop(key, None) 868 | return 1 869 | return 0 870 | 871 | def del_all_bp_addr(self): 872 | l = len(self.bp_settings_dict) 873 | self.bp_settings_dict = {} 874 | return l 875 | 876 | def reload_dasm_listing_with_unassemble_from_addr(self, unasm_begin): 877 | self.update_dasm_listing(unasm_begin, self.tracking_curr_instr_ip, self.disasm_mode) 878 | 879 | def is_mem_region_mapped(self, mem_addr_test): 880 | for i in range(len(self.mem_map_region_rwx)): 881 | (mem_begin, mem_range) = self.mem_map_region_rwx[i] 882 | if mem_begin <= mem_addr_test < (mem_begin + mem_range): 883 | return True 884 | return False 885 | 886 | def find_prev_mem_begin_region(self, curr_addr): 887 | ll = self.mem_map_region_rwx 888 | ll = sorted(ll) 889 | for i in range(len(ll)): 890 | (mem_begin, mem_range) = ll[i] 891 | if mem_begin <= curr_addr < (mem_begin + mem_range): 892 | if i == 0: 893 | (mem_begin, mem_range) = ll[i] 894 | return mem_begin 895 | else: 896 | (mem_begin, mem_range) = ll[i - 1] 897 | return mem_begin 898 | return None 899 | 900 | def find_mem_region_from_addr(self, curr_addr): 901 | ll = self.mem_map_region_rwx 902 | ll = sorted(ll) 903 | for i in range(len(ll)): 904 | (mem_begin, mem_range) = ll[i] 905 | if mem_begin <= curr_addr < (mem_begin + mem_range): 906 | (mem_begin, mem_range) = ll[i] 907 | return mem_begin, mem_range 908 | return None, None 909 | 910 | # callback for tracing instructions 911 | def all_instr_hook_code(self, uc, ip_address, size, user_data): 912 | global program_exit 913 | if program_exit: 914 | return False 915 | 916 | self.tracking_curr_instr_ip = ip_address 917 | self._save_register_last_data() 918 | 919 | # use Arm mode by default 920 | self.disasm_mode = CS_MODE_ARM 921 | if self.uc.query(UC_QUERY_MODE) == UC_MODE_THUMB: 922 | self.disasm_mode = CS_MODE_THUMB 923 | if PRINT_EMULATION_LOG: 924 | print(">> THUMB mode on") 925 | 926 | if ip_address in self.bp_settings_dict: 927 | # BREAK_HERE() 928 | print(">>> Breakpoint hit at 0x%x, instruction size = %u" % (ip_address, size)) 929 | self.go_emul = False 930 | 931 | elif self.break_run: 932 | self.go_emul = False 933 | self.break_run = False 934 | 935 | elif self.go_emul: 936 | if PRINT_EMULATION_LOG: 937 | print(">>> Tracing instruction at 0x%x, instruction size = %u" % (ip_address, size)) 938 | self.last_emul_ip = ip_address 939 | return True 940 | 941 | if PRINT_EMULATION_LOG: 942 | print(">>> Tracing instruction at 0x%x, instruction size = %u" % (ip_address, size)) 943 | 944 | dasm_listing_top_addr = 0 945 | if ip_address in self.dasm_listing_dict: 946 | for key in self.dasm_listing_dict: 947 | dasm_listing_top_addr = key 948 | break 949 | else: 950 | dasm_listing_top_addr = ip_address 951 | 952 | # 953 | # 954 | # 955 | self.update_dasm_listing(dasm_listing_top_addr, ip_address, self.disasm_mode) 956 | self.update_buffer_view_memory1() 957 | self.update_buffer_view_memory2() 958 | 959 | self.set_wait_for_user_input() 960 | self.set_emul_data_waiting() 961 | while self.is_emul_data_waiting(): 962 | time.sleep(0.01) 963 | 964 | # 965 | # wait for user input 966 | # 967 | self.last_emul_ip = ip_address 968 | 969 | while self.is_wait_for_user_input(): 970 | time.sleep(0.01) 971 | if program_exit: 972 | return False 973 | 974 | self.emul_syscall(ip_address) 975 | 976 | return True 977 | 978 | def emul_syscall(self, ip_address): 979 | 980 | if ip_address in self.emul_syscall_addr_table: 981 | syscall_number = self.emul_syscall_addr_table[ip_address] 982 | if syscall_number == EMUL_FUNCTION_NOP: 983 | pass 984 | 985 | elif syscall_number == EMUL_FUNCTION_STRLEN: 986 | self.emul_function_strlen() 987 | 988 | elif syscall_number == EMUL_FUNCTION_AES_CRYPT: 989 | assert False 990 | 991 | else: 992 | assert False 993 | 994 | self.skip_current_instruction() 995 | 996 | def emul_function_strlen(self): 997 | r0 = self.uc.reg_read(UC_ARM_REG_R0) 998 | if not self.is_mem_region_mapped(r0): 999 | assert False 1000 | return 1001 | 1002 | (mem_begin, mem_range) = self.find_mem_region_from_addr(r0) 1003 | if mem_begin is None and mem_range is None: 1004 | assert False 1005 | return 1006 | 1007 | rest_size = (mem_begin + mem_range) - r0 1008 | buff = self.read_emul_memory_range(r0, rest_size) 1009 | i = 0 1010 | for i in range(len(buff)): 1011 | c = buff[i] 1012 | if c == 0x0: 1013 | break 1014 | i += 1 1015 | self.uc.reg_write(UC_ARM_REG_R0, i) 1016 | 1017 | def skip_current_instruction(self): 1018 | pc = r15 = self.uc.reg_read(UC_ARM_REG_R15) 1019 | if self.uc.query(UC_QUERY_MODE) == UC_MODE_THUMB: 1020 | pc += 2 1021 | else: 1022 | pc += 4 1023 | self.uc.reg_write(UC_ARM_REG_R15, pc) 1024 | 1025 | # callback for tracing basic blocks 1026 | def basic_block_hook_block(self, uc, address, size, user_data): 1027 | if PRINT_EMULATION_LOG: 1028 | print(">>> Tracing basic block at 0x%x, block size = 0x%x" % (address, size)) 1029 | 1030 | # callback for tracing memory access (READ or WRITE) 1031 | def hook_mem_access(self, uc, access, address, size, value, user_data): 1032 | if access == UC_MEM_WRITE: 1033 | if PRINT_EMULATION_LOG: 1034 | print(">>> Memory is being WRITE at 0x%x, data size = %u, data value = 0x%x" % (address, size, value)) 1035 | else: # READ 1036 | if PRINT_EMULATION_LOG: 1037 | print(">>> Memory is being READ at 0x%x, data size = %u" % (address, size)) 1038 | 1039 | def hook_mem_read_unmapped(self, uc, access, address, size, value, user_data): 1040 | print("UC_HOOK_MEM_READ_UNMAPPED at 0x%x access %d" % (address, access)) 1041 | return False 1042 | 1043 | # callback for tracing invalid memory access (READ/WRITE/EXEC) 1044 | def hook_mem_invalid(self, uc, access, address, size, value, user_data): 1045 | access_memory_str = "" 1046 | if access == UC_MEM_READ_UNMAPPED: 1047 | print("!!! Invalid Memory is being READ at 0x%x, data size = %u" % (address, size)) 1048 | access_memory_str = "UC_MEM_READ_UNMAPPED" 1049 | elif access == UC_MEM_WRITE_UNMAPPED: 1050 | access_memory_str = "UC_MEM_WRITE_UNMAPPED" 1051 | elif access == UC_MEM_FETCH_PROT: 1052 | access_memory_str = "UC_MEM_FETCH_PROT" 1053 | elif access == UC_MEM_WRITE_PROT: 1054 | access_memory_str = "UC_MEM_WRITE_PROT" 1055 | elif access == UC_MEM_READ_PROT: 1056 | access_memory_str = "UC_MEM_READ_PROT" 1057 | 1058 | print("!!! Invalid Memory is being at 0x%x, data size = %u" % (address, size)) 1059 | print (access_memory_str) 1060 | # pdb.set_trace() 1061 | self.last_error = "UC_HOOK_MEM_INVALID at 0x%x access %d" % (address, access) 1062 | print (self.last_error) 1063 | return False 1064 | 1065 | # callback for tracing Linux interrupt 1066 | def hook_intr(self, uc, intno, user_data): 1067 | # only handle Linux syscall 1068 | rip = self.uc.reg_read(UC_ARM_REG_IP) 1069 | if intno != 0x80: 1070 | print("=== 0x%x: got interrupt %x, quit" % (rip, intno)) 1071 | self.uc.emu_stop() 1072 | return 1073 | eax = self.uc.reg_read(UC_ARM_REG_R0) 1074 | print(">>> 0x%x: interrupt 0x%x, R0 = 0x%x" % (rip, intno, eax)) 1075 | 1076 | def addr_to_emul_syscall_instr_str(self, ip_address): 1077 | if ip_address in self.emul_syscall_addr_table: 1078 | syscall_number = self.emul_syscall_addr_table[ip_address] 1079 | if syscall_number == EMUL_FUNCTION_NOP: 1080 | return "emul_nop" 1081 | elif syscall_number == EMUL_FUNCTION_STRLEN: 1082 | return "emul_strlen" 1083 | elif syscall_number == EMUL_FUNCTION_AES_CRYPT: 1084 | return "aes_crypt" 1085 | else: 1086 | assert (False) 1087 | return "" 1088 | 1089 | def set_emul_syscall_address_table(self): 1090 | global EMUL_ADDR_FUNCTIONS_GLOBAL_TAB 1091 | 1092 | self.emul_syscall_addr_table = {} 1093 | for key, value in EMUL_ADDR_FUNCTIONS_GLOBAL_TAB.iteritems(): 1094 | self.emul_syscall_addr_table[key] = value 1095 | 1096 | def run_emulator(self): 1097 | try: 1098 | 1099 | self.mem_map_region_rwx = [] 1100 | self.inc_run_counter() 1101 | 1102 | # Initialize emulator in ARM mode 1103 | self.uc = Uc(IN_UC_ARCH, IN_UC_ARCH_MODE) 1104 | 1105 | self.mem_size = 0 1106 | i = len(self.file_data) // (1024 * 1024) 1107 | self.mem_size = (1024 * 1024) + (i * (1024 * 1024)) 1108 | 1109 | # map memory for this emulation 1110 | self.uc.mem_map(IMAGEBASE, self.mem_size) 1111 | 1112 | # map stack memory 1113 | self.stack_address = IMAGEBASE + self.mem_size 1114 | self.stack_size = (1024 * 1024) * 2 1115 | self.uc.mem_map(self.stack_address, self.stack_size) 1116 | 1117 | # append mapped regions to the list (addr, range) 1118 | self.mem_map_region_rwx.append((IMAGEBASE, self.mem_size)) 1119 | self.mem_map_region_rwx.append((self.stack_address, self.stack_size)) 1120 | 1121 | # write machine code to be emulated to memory 1122 | self.uc.mem_write(IMAGEBASE, self.file_data) 1123 | 1124 | # initialize machine registers 1125 | self.uc.reg_write(UC_ARM_REG_R0, INITIALIZE_REG_R0) 1126 | self.uc.reg_write(UC_ARM_REG_R1, INITIALIZE_REG_R1) 1127 | self.uc.reg_write(UC_ARM_REG_R2, INITIALIZE_REG_R2) 1128 | self.uc.reg_write(UC_ARM_REG_R3, INITIALIZE_REG_R3) 1129 | self.uc.reg_write(UC_ARM_REG_R4, INITIALIZE_REG_R4) 1130 | self.uc.reg_write(UC_ARM_REG_R5, INITIALIZE_REG_R5) 1131 | self.uc.reg_write(UC_ARM_REG_R6, INITIALIZE_REG_R6) 1132 | self.uc.reg_write(UC_ARM_REG_R7, INITIALIZE_REG_R7) 1133 | self.uc.reg_write(UC_ARM_REG_R8, INITIALIZE_REG_R8) 1134 | self.uc.reg_write(UC_ARM_REG_R9, INITIALIZE_REG_R9) 1135 | self.uc.reg_write(UC_ARM_REG_R10, INITIALIZE_REG_R10) 1136 | self.uc.reg_write(UC_ARM_REG_R11, INITIALIZE_REG_R11) 1137 | self.uc.reg_write(UC_ARM_REG_R12, INITIALIZE_REG_R12) 1138 | 1139 | self.uc.reg_write(UC_ARM_REG_SP, self.stack_address) 1140 | 1141 | # tracing all basic blocks with customized callback 1142 | self.uc.hook_add(UC_HOOK_BLOCK, self.basic_block_hook_block) 1143 | 1144 | # tracing all instructions with customized callback 1145 | self.uc.hook_add(UC_HOOK_CODE, self.all_instr_hook_code) 1146 | self.uc.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, self.hook_mem_access) 1147 | self.uc.hook_add(UC_HOOK_MEM_INVALID, self.hook_mem_invalid) 1148 | # self.uc.hook_add(UC_HOOK_MEM_READ_UNMAPPED, self.hook_mem_read_unmapped) 1149 | self.uc.hook_add(UC_HOOK_INTR, self.hook_intr) 1150 | 1151 | self.mem_view1_addr = IMAGEBASE 1152 | self.mem_view2_addr = self.stack_address 1153 | 1154 | thumbCorrect = 0 1155 | if IS_THUMB_MODE: 1156 | thumbCorrect = 1 1157 | print("Entrypoint adjusted to thumb") 1158 | 1159 | # emulate machine code in infinite time 1160 | self.uc.emu_start(IMAGEBASE + ENTRYPOINT + thumbCorrect, IMAGEBASE + self.mem_size - 1) 1161 | except UcError as e: 1162 | if e.errno == UC_ERR_FETCH_UNMAPPED: 1163 | print("!!! about to bail due to bad fetch... here's the data at PC:") 1164 | print("Last IP at 0x%x" % self.last_emul_ip) 1165 | 1166 | print(binascii.hexlify(self.uc.mem_read(self.uc.reg_read(UC_ARM_REG_IP), 0x8))) 1167 | print("IP at 0x%x" % (self.uc.reg_read(UC_ARM_REG_IP))) 1168 | return 1169 | 1170 | # print ("Emulator finished") 1171 | # last_ip = '{:08X}'.format(self.uc.reg_read(UC_ARM_REG_IP)) 1172 | # print(last_ip) 1173 | 1174 | 1175 | # 1176 | # Command Line 1177 | # 1178 | class CommandLineWindow(tk.Frame): 1179 | def __init__(self, master=None): 1180 | self.parent = master 1181 | tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken") 1182 | self._create_settings() 1183 | self._create_layout() 1184 | self._create_bindings() 1185 | 1186 | def _create_settings(self): 1187 | self.history_list = [] 1188 | self.history_index = 0 1189 | self.emulator_core = None 1190 | self.font = tkFont.Font(family=FONT_FAMILY, size=FONT_SIZE) 1191 | 1192 | def _create_layout(self): 1193 | self.textArea = tk.Text(self, wrap=tk.NONE, undo=True, 1194 | width=1, height=1, 1195 | relief=tk.SUNKEN, borderwidth=2, 1196 | highlightthickness=0, insertbackground="white") 1197 | self.textArea.config(font=self.font) 1198 | self.textArea.configure(bg="black", fg=TEXT_COLOR) 1199 | 1200 | self.cmdEdit = tk.Text(self, wrap=tk.NONE, undo=True, 1201 | width=1, height=1, 1202 | relief=tk.SUNKEN, borderwidth=4, 1203 | highlightthickness=0, insertbackground="white") 1204 | self.cmdEdit.config(font=self.font) 1205 | self.cmdEdit.configure(bg="black", fg=TEXT_COLOR) 1206 | 1207 | self.scrollbarY = tk.Scrollbar(self, orient=tk.VERTICAL) 1208 | self.scrollbarX = tk.Scrollbar(self, orient=tk.HORIZONTAL) 1209 | 1210 | self.textArea.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E)) 1211 | self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=1) 1212 | self.cmdEdit.grid(row=1, column=0, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E)) 1213 | 1214 | self.rowconfigure(0, weight=1) 1215 | self.columnconfigure(0, weight=1) 1216 | 1217 | self.textArea.configure(yscrollcommand=self.scrollbarY.set) 1218 | 1219 | self.scrollbarY.config(command=self.textArea.yview) 1220 | 1221 | def _create_bindings(self): 1222 | self.cmdEdit.bind('', self.on_cmd_line_return) 1223 | self.cmdEdit.bind('', self.on_cmd_key_up) 1224 | self.cmdEdit.bind('', self.on_cmd_key_down) 1225 | 1226 | def on_cmd_key_down(self, event): 1227 | prev_cmd = self.get_history_text(self.history_index + 1) 1228 | self.set_text_on_cmd_edit(prev_cmd) 1229 | 1230 | def on_cmd_key_up(self, event): 1231 | next_cmd = self.get_history_text(self.history_index - 1) 1232 | self.set_text_on_cmd_edit(next_cmd) 1233 | 1234 | def set_text_on_cmd_edit(self, cmd_text): 1235 | if len(cmd_text) != 0: 1236 | self.cmdEdit.delete(0.0, "end") 1237 | self.cmdEdit.insert(0.0, cmd_text) 1238 | 1239 | def on_focus_cmd_line(self): 1240 | self.cmdEdit.focus_set() 1241 | 1242 | def get_history_text(self, index): 1243 | if len(self.history_list) == False: 1244 | return "" 1245 | if index == 0: 1246 | self.history_index = 0 1247 | return self.history_list[0] 1248 | elif index > 0 and index < len(self.history_list): 1249 | self.history_index = index 1250 | return self.history_list[index] 1251 | else: 1252 | return "" 1253 | 1254 | def add_emulator_core_access(self, emul_uc): 1255 | self.emulator_core = emul_uc 1256 | 1257 | def cmd_str_to_hex_addr(self, s): 1258 | try: 1259 | s = s.rstrip() 1260 | s = s.lstrip() 1261 | reg_data_dict = self.emulator_core.get_register_data() 1262 | if s in reg_data_dict: 1263 | peek_addr = reg_data_dict[s] 1264 | else: 1265 | if len(s) <= 8: 1266 | peek_addr = int(s, 16) 1267 | else: 1268 | peek_addr = eval(s) 1269 | except: 1270 | return (0, False) 1271 | 1272 | return (peek_addr, True) 1273 | 1274 | def exec_user_cmd_line(self, cmd_text=""): 1275 | if cmd_text.startswith("d "): 1276 | (dump_expr, ok) = self.cmd_str_to_hex_addr(cmd_text[2:]) 1277 | if not ok: 1278 | return 1279 | # 1280 | # update UI 1281 | # 1282 | # pdb.set_trace() 1283 | global g_current_memory_view_frame_number 1284 | self.emulator_core.set_new_memory_view_addr_on_window_number(dump_expr, g_current_memory_view_frame_number) 1285 | if self.emulator_core.is_wait_for_user_input() and not self.emulator_core.is_emul_data_waiting(): 1286 | self.emulator_core.set_update_mem_view() 1287 | 1288 | elif cmd_text.startswith("u "): 1289 | (dump_expr, ok) = self.cmd_str_to_hex_addr(cmd_text[2:]) 1290 | if not ok: 1291 | return 1292 | 1293 | if self.emulator_core.is_wait_for_user_input(): 1294 | self.emulator_core.reload_dasm_listing_with_unassemble_from_addr(dump_expr) 1295 | self.emulator_core.set_dasm_view_update() 1296 | 1297 | elif cmd_text.startswith("bp "): # set breakpoint 1298 | (peek_addr, ok) = self.cmd_str_to_hex_addr(cmd_text[2:]) 1299 | if not ok: 1300 | return 1301 | 1302 | self.emulator_core.toggle_bp_settings(peek_addr, 1) 1303 | 1304 | if self.emulator_core.is_wait_for_user_input(): 1305 | self.emulator_core.set_dasm_view_update() 1306 | 1307 | elif cmd_text.startswith("bc *"): # clear ALL breakpoint 1308 | if self.emulator_core.del_all_bp_addr() != 0: 1309 | if self.emulator_core.is_wait_for_user_input(): 1310 | self.emulator_core.set_dasm_view_update() 1311 | 1312 | elif cmd_text.startswith("bc "): # clear breakpoint 1313 | (peek_addr, ok) = self.cmd_str_to_hex_addr(cmd_text[2:]) 1314 | if not ok: 1315 | return False 1316 | 1317 | if self.emulator_core.del_bp_addr(peek_addr) != 0: 1318 | if self.emulator_core.is_wait_for_user_input(): 1319 | self.emulator_core.set_dasm_view_update() 1320 | 1321 | elif cmd_text.startswith("bl"): 1322 | text_pile = "" 1323 | bp_list = self.emulator_core.get_bp_settings() 1324 | for key in bp_list: 1325 | bp_ip_str = '0x{:08X}'.format(key) 1326 | bp_state_str = format(bp_list[key]) 1327 | l = bp_ip_str + " - state: " + bp_state_str 1328 | text_pile += l + "\n" 1329 | self.append_TextArea(text_pile) 1330 | 1331 | elif cmd_text.startswith("r "): 1332 | words = cmd_text[2:].split() 1333 | if len(words) == 2: 1334 | reg_data_dict = self.emulator_core.get_register_data() 1335 | if words[0] in reg_data_dict: 1336 | val = int(words[1], 16) 1337 | if self.emulator_core.is_wait_for_user_input() and self.emulator_core.set_register_data(words[0], 1338 | val) == True: 1339 | self.emulator_core.set_update_register_view() 1340 | 1341 | elif cmd_text == "rr": 1342 | if self.emulator_core.is_wait_for_user_input(): 1343 | reg_data_dict = self.emulator_core.get_register_data() 1344 | self.print_emul_exit_context(reg_data_dict) 1345 | 1346 | elif cmd_text.startswith("dump "): 1347 | words = cmd_text[4:].split() 1348 | if len(words) < 2: 1349 | return False 1350 | (dump_addr, ok) = self.cmd_str_to_hex_addr(words[0]) 1351 | if ok == False: 1352 | return False 1353 | dump_range = eval(words[1]) 1354 | path_begin = cmd_text.find('"') 1355 | if path_begin == - 1: 1356 | return False 1357 | path_begin += 1 1358 | path_end = cmd_text[path_begin:].find('"') 1359 | if path_end == - 1: 1360 | return False 1361 | path_end += path_begin 1362 | 1363 | dump_filename = cmd_text[path_begin:path_end] 1364 | dump_mem = self.emulator_core.get_dump_memory(dump_addr, dump_range) 1365 | if len(dump_mem): 1366 | dump_file = open(dump_filename, 'wb') 1367 | dump_file.write(dump_mem) 1368 | dump_file.close() 1369 | print(dump_addr, dump_range, dump_filename) 1370 | 1371 | elif cmd_text.startswith("? "): 1372 | s = cmd_text[1:] 1373 | s = s.rstrip() 1374 | s = s.lstrip() 1375 | txt = str(eval(s)) 1376 | self.append_TextArea(txt) 1377 | 1378 | elif cmd_text.startswith("hex(") and cmd_text.endswith(')'): 1379 | s = cmd_text[4:-1] 1380 | s = s.rstrip() 1381 | s = s.lstrip() 1382 | txt = hex(eval(s)) 1383 | self.append_TextArea(txt) 1384 | 1385 | elif cmd_text == "help": 1386 | commands = "" 1387 | commands += "d - dump memory\n" 1388 | commands += "u - unassemble memory\n" 1389 | commands += "bp - set breakpoint\n" 1390 | commands += "bc - clear breakpoint\n" 1391 | commands += "bl - list breakpoints\n" 1392 | commands += "r - change register value\n" 1393 | commands += "rr - print registers\n" 1394 | commands += "? - calc expression\n" 1395 | commands += "dump \"PATH\" - dump memory range to a file\n" 1396 | commands += "hex("") - calc expression format to hex\n" 1397 | self.append_TextArea(commands) 1398 | 1399 | else: 1400 | return False 1401 | return True 1402 | 1403 | def append_TextArea(self, append_txt): 1404 | if len(append_txt) == 0: 1405 | return 1406 | txt = self.textArea.get('1.0', tk.END) 1407 | self.textArea.insert(tk.END, "\n" + append_txt) 1408 | self.textArea.see(tk.END) 1409 | 1410 | def on_cmd_line_return(self, event): 1411 | cmd_text = str(self.cmdEdit.get(0.0, "end")) 1412 | cmd_text = cmd_text.lstrip('\n') 1413 | cmd_text = cmd_text.rstrip('\n') 1414 | 1415 | if not self.exec_user_cmd_line(cmd_text): 1416 | print ("Command line parse error") 1417 | 1418 | if len(self.history_list) > 8: 1419 | del self.history_list[0] 1420 | 1421 | self.history_list.append(cmd_text) 1422 | self.history_index = len(self.history_list) 1423 | 1424 | self.cmdEdit.delete(0.0, "end") 1425 | pass 1426 | 1427 | def print_emul_exit_context(self, reg_data): 1428 | self.textArea.delete('1.0', tk.END) 1429 | i = 0 1430 | lines = "" 1431 | for reg_name in sorted(reg_data): 1432 | if i > 0 and (i % 4) == 0: 1433 | lines += "\n" 1434 | reg_val = reg_data[reg_name] 1435 | i += 1 1436 | lines += reg_name.ljust(3) + " : " + '{:08X}'.format(reg_val) + " " 1437 | 1438 | self.textArea.insert(tk.END, lines) 1439 | 1440 | 1441 | # 1442 | # RegisterWindow 1443 | # 1444 | class RegisterWindow(tk.Frame): 1445 | def __init__(self, master=None): 1446 | self.parent = master 1447 | tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken") 1448 | 1449 | self._create_settings() 1450 | self._create_layout() 1451 | 1452 | self._create_token_objects() 1453 | self._draw_data_on_canvas() 1454 | self._create_bindings() 1455 | 1456 | def _create_layout(self): 1457 | self.canvas = tk.Canvas(self, bg=BACKGROUND_COLOR, relief=tk.SUNKEN) 1458 | self.canvas.config(width=20, height=10) 1459 | # self.canv.config(scrollregion=(0,0,300, 1000)) 1460 | self.canvas.config(highlightthickness=0) 1461 | 1462 | self.sbar = tk.Scrollbar(self, orient=tk.VERTICAL) 1463 | # self.sbar.config(command=self.canv.yview) 1464 | # self.canv.config(yscrollcommand=self.sbar.set) 1465 | self.sbar.pack(side=tk.RIGHT, fill=tk.Y) 1466 | self.canvas.pack(side=tk.LEFT, expand="YES", fill=tk.BOTH) 1467 | 1468 | self.height = self.canvas.winfo_width() 1469 | self.width = self.canvas.winfo_height() 1470 | 1471 | def _create_settings(self): 1472 | self.mouse_x, self.mouse_y = 0, 0 1473 | 1474 | self.reg_names_list = ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8"] 1475 | self.reg_names_list += ["r9", "r10", "r11", "r12", "sp", "lr", "pc", "psr"] 1476 | 1477 | # this data is used to keep track of an 1478 | # item being dragged 1479 | self._drag_data = {"x": 0, "y": 0, "item_text": "", "item": None} 1480 | 1481 | self.refreshPending = False 1482 | self.curr_memory_addr = 0 1483 | self.font = tkFont.Font(family=FONT_FAMILY, size=FONT_SIZE) 1484 | (self.font_width, self.font_height) = (self.font.measure("W"), self.font.metrics("linespace")) 1485 | self.prev_reg_data = {} 1486 | 1487 | def on_resize(self, event): 1488 | # determine the ratio of old width/height to new width/height 1489 | wscale = float(event.width) / self.width 1490 | hscale = float(event.height) / self.height 1491 | self.width = event.width 1492 | self.height = event.height 1493 | 1494 | # resize the canvas 1495 | self.config(width=self.width, height=self.height) 1496 | 1497 | def mouse_click(self, event): 1498 | self.mouse_x, self.mouse_y = event.x, event.y 1499 | 1500 | canv_click_id = self.canvas.find_withtag(tk.CURRENT) 1501 | tags = self.canvas.gettags(canv_click_id) 1502 | 1503 | def _create_bindings(self): 1504 | self.bind("", self.on_resize) 1505 | self.canvas.bind("", self.mouse_click) 1506 | 1507 | # add bindings for clicking, dragging and releasing over 1508 | # any object with the "token" tag 1509 | self.canvas.tag_bind("token", "", self.on_token_button_press) 1510 | self.canvas.tag_bind("token", "", self.on_token_button_release) 1511 | self.canvas.tag_bind("token", "", self.on_token_motion) 1512 | # self.canvas.bind_all("", self.on_mousewheel_del) 1513 | 1514 | def on_token_button_press(self, event): 1515 | # Being drag of an object record the item and its location 1516 | self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0] 1517 | self._drag_data["x"] = event.x 1518 | self._drag_data["y"] = event.y 1519 | self._drag_data["item_text"] = event.widget.itemcget(tk.CURRENT, "text") 1520 | # coords = self.canvas.coords(tk.CURRENT) 1521 | 1522 | def on_token_button_release(self, event): 1523 | # End drag of an object reset the drag information 1524 | self._drag_data["item"] = None 1525 | self._drag_data["item_text"] = "" 1526 | self._drag_data["x"] = 0 1527 | self._drag_data["y"] = 0 1528 | 1529 | def on_token_motion(self, event): 1530 | # Handle dragging of an object compute how much this object has moved 1531 | delta_x = event.x - self._drag_data["x"] 1532 | delta_y = event.y - self._drag_data["y"] 1533 | # move the object the appropriate amount 1534 | self.canvas.move(self._drag_data["item"], delta_x, delta_y) 1535 | # record the new position 1536 | self._drag_data["x"] = event.x 1537 | self._drag_data["y"] = event.y 1538 | 1539 | def _create_token_objects(self): 1540 | # create a couple movable objects 1541 | margin = 5 1542 | for i in range(len(self.reg_names_list)): 1543 | reg_name = self.reg_names_list[i] 1544 | reg_label = reg_name.ljust(3) + " : " + '{:08X}'.format(0) 1545 | token_y = i * (self.font_height + 2) 1546 | self._create_token((margin, token_y), reg_label, "token") 1547 | 1548 | def _create_token(self, coord, reg_name, token_name): 1549 | # Create a token at the given coordinate in the given color 1550 | 1551 | (x, y) = coord 1552 | self.canvas.create_text(x, y, tags=token_name, text=reg_name, activefill='#99FF00', font=self.font, 1553 | fill=TEXT_COLOR, anchor="nw") 1554 | 1555 | def _draw_data_on_canvas(self): 1556 | pass 1557 | 1558 | def update_register_view(self, curr_reg_data): 1559 | 1560 | # self.canvas.delete(tk.ALL) # remove all items 1561 | if len(curr_reg_data) == 0: 1562 | return 1563 | 1564 | reg_tokens = self.canvas.find_withtag("token") 1565 | 1566 | for i in range(len(self.reg_names_list)): 1567 | reg_color = TEXT_COLOR 1568 | reg_name = self.reg_names_list[i] 1569 | if reg_name in self.prev_reg_data and curr_reg_data[reg_name] != self.prev_reg_data[reg_name]: 1570 | reg_color = "yellow" 1571 | token_text = reg_name.ljust(3) + " : " + '{:08X}'.format(curr_reg_data[reg_name]) 1572 | self.canvas.itemconfig(reg_tokens[i], fill=reg_color, text=token_text) 1573 | 1574 | self.prev_reg_data = curr_reg_data 1575 | 1576 | 1577 | # 1578 | # MemoryView 1579 | # 1580 | class MemoryViewWindow(tk.Frame): 1581 | def __init__(self, master=None): 1582 | self.parent = master 1583 | tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken") 1584 | 1585 | self._create_settings() 1586 | self._create_layout() 1587 | self._create_bindings() 1588 | self._draw_text_data_on_canvas(self.current_dump_addr, self.current_dump_buffer) 1589 | 1590 | def _create_layout(self): 1591 | self.canvas = tk.Canvas(self, bg=BACKGROUND_COLOR, relief=tk.SUNKEN) 1592 | self.canvas.config(width=20, height=10) 1593 | # self.canv.config(scrollregion=(0,0,300, 1000)) 1594 | self.canvas.config(highlightthickness=0) 1595 | 1596 | self.sbar = tk.Scrollbar(self, orient=tk.VERTICAL) 1597 | # self.sbar.config(command=self.canv.yview) 1598 | # self.canv.config(yscrollcommand=self.sbar.set) 1599 | self.sbar.pack(side=tk.RIGHT, fill=tk.Y) 1600 | self.canvas.pack(side=tk.LEFT, expand="YES", fill=tk.BOTH) 1601 | 1602 | self.height = self.canvas.winfo_width() 1603 | self.width = self.canvas.winfo_height() 1604 | 1605 | def _create_settings(self): 1606 | self.current_dump_addr = 0 1607 | self.current_dump_buffer = b"" 1608 | self.mouse_x, self.mouse_y = 0, 0 1609 | self.number_of_line = 0 1610 | self.font = tkFont.Font(family=FONT_FAMILY, size=FONT_SIZE) 1611 | (self.font_char_width, self.font_char_height) = (self.font.measure('0'), self.font.metrics("linespace")) 1612 | self.emulator_core = None 1613 | self.highlight_byte_addr_list = [] 1614 | self.view_frame_number = -1 1615 | self.cursor_edit_addr = 0 1616 | self.edit_addr_mask = 0x00 1617 | self.hex_tag_prefix = "hex_tag_" 1618 | self.caret_rect_tag = "caret_rect_tag" 1619 | self.address_line_tag = "address_tag_0x" 1620 | self.mouse_click_tags = () 1621 | 1622 | def set_view_number(self, v): 1623 | self.view_frame_number = v 1624 | 1625 | def _create_bindings(self): 1626 | self.bind("", self.on_resize) 1627 | self.canvas.bind("", self.click) 1628 | self.bind("", self.mouse_pointer_enter) 1629 | self.bind("", self.mouse_pointer_leave) 1630 | 1631 | def on_mousewheel_scroll_up(self, event=None): 1632 | if not self.emulator_core.is_wait_for_user_input(): 1633 | return 1634 | prev_addr = self.current_dump_addr 1635 | prev_addr -= (VIEW_MEMORY_DUMP_RANGE * 3) 1636 | if not self.emulator_core.is_mem_region_mapped(prev_addr): 1637 | return 1638 | self.emulator_core.set_new_memory_view_addr_on_window_number(prev_addr, self.view_frame_number) 1639 | if self.emulator_core.is_wait_for_user_input() and not self.emulator_core.is_emul_data_waiting(): 1640 | self.emulator_core.set_update_mem_view() 1641 | 1642 | def on_mousewheel_scroll_down(self, event=None): 1643 | if not self.emulator_core.is_wait_for_user_input(): 1644 | return 1645 | next_addr = self.current_dump_addr 1646 | next_addr += (VIEW_MEMORY_DUMP_RANGE * 3) 1647 | if not self.emulator_core.is_mem_region_mapped(next_addr): 1648 | return 1649 | self.emulator_core.set_new_memory_view_addr_on_window_number(next_addr, self.view_frame_number) 1650 | 1651 | if self.emulator_core.is_wait_for_user_input() and not self.emulator_core.is_emul_data_waiting(): 1652 | self.emulator_core.set_update_mem_view() 1653 | 1654 | def on_mousewheel(self, event=None): 1655 | delta = (event.delta / 120) 1656 | if delta > 0: 1657 | self.on_mousewheel_scroll_up() 1658 | else: 1659 | self.on_mousewheel_scroll_down() 1660 | 1661 | def mouse_pointer_enter(self, event): 1662 | self.cursor_edit_addr = 0 1663 | self.edit_addr_mask = 0x00 1664 | # self.focus_set() #will be set on mouse click 1665 | self.bind("", self.key_press_event) 1666 | 1667 | os = platform.system() 1668 | if os == "Linux": 1669 | self.canvas.bind('<4>', self.on_mousewheel_scroll_up) 1670 | self.canvas.bind('<5>', self.on_mousewheel_scroll_down) 1671 | else: 1672 | # Windows and MacOS 1673 | self.bind("", self.on_mousewheel) 1674 | 1675 | def mouse_pointer_leave(self, event): 1676 | self.cursor_edit_addr = 0 1677 | self.edit_addr_mask = 0x00 1678 | self.unbind("") 1679 | 1680 | self.mouse_click_tags = () 1681 | caret_tag_id = self.canvas.find_withtag(self.caret_rect_tag) 1682 | if len(caret_tag_id) > 0: 1683 | self.canvas.delete(caret_tag_id) 1684 | 1685 | os = platform.system() 1686 | if os == "Linux": 1687 | self.canvas.unbind('<4>') 1688 | self.canvas.unbind('<5>') 1689 | else: 1690 | # Windows and MacOS 1691 | self.unbind("") 1692 | 1693 | def key_press_event(self, event): 1694 | if event.keysym_num == 0xffc4: # F7 1695 | return 1696 | if len(self.mouse_click_tags) == 0: 1697 | return 1698 | 1699 | tag_name = self.mouse_click_tags[0] 1700 | hex_edit_mode = False 1701 | is_valid_char = False 1702 | if tag_name.startswith(self.hex_tag_prefix): 1703 | hex_edit_mode = True 1704 | if ('0' <= event.char <= '9') or \ 1705 | ('a' <= event.char <= 'f') or \ 1706 | ('A' <= event.char <= 'F'): 1707 | is_valid_char = True 1708 | 1709 | if hex_edit_mode and is_valid_char: 1710 | s = tag_name[len(self.hex_tag_prefix):] 1711 | eb_addr = int(s, 16) 1712 | 1713 | eb_val = ord(event.char) 1714 | if eb_val >= ord('0') and eb_val <= ord('9'): 1715 | eb_val -= ord('0') 1716 | elif eb_val >= ord('a') and eb_val <= ord('f'): 1717 | eb_val = (eb_val - ord('a')) + 0x0A 1718 | elif eb_val >= ord('A') and eb_val <= ord('F'): 1719 | eb_val = (eb_val - ord('A')) + 0x0A 1720 | else: 1721 | assert (False) 1722 | return 1723 | 1724 | if self.edit_addr_mask == 0x00: 1725 | mask = 0x0F 1726 | elif self.edit_addr_mask == 0x0F: 1727 | mask = 0xF0 1728 | else: 1729 | assert (False) 1730 | return 1731 | 1732 | self.emulator_core.write_byte_at_memory_addr(eb_addr, eb_val, mask) 1733 | self.edit_addr_mask = mask 1734 | 1735 | if self.edit_addr_mask == 0xF0: 1736 | self.reload_view_on_mem_edit_keypress() 1737 | self.move_caret_to_next_addr() 1738 | self.edit_addr_mask = 0x00 1739 | 1740 | def reload_view_on_mem_edit_keypress(self): 1741 | if self.view_frame_number == 0: 1742 | self.emulator_core.update_buffer_view_memory1() 1743 | view_addr, view_buffer = self.emulator_core.get_mem_view1_addr_data() 1744 | elif self.view_frame_number == 1: 1745 | self.emulator_core.update_buffer_view_memory2() 1746 | view_addr, view_buffer = self.emulator_core.get_mem_view2_addr_data() 1747 | else: 1748 | assert (False) 1749 | return 1750 | 1751 | self.display_memory_from_single_step(view_addr, view_buffer) 1752 | self.emulator_core.set_dasm_view_update() 1753 | 1754 | def move_caret_to_next_addr(self): 1755 | if len(self.mouse_click_tags) == 0: 1756 | return 1757 | tag_name = self.mouse_click_tags[0] 1758 | if not tag_name.startswith(self.hex_tag_prefix): 1759 | return 1760 | s = tag_name[len(self.hex_tag_prefix):] 1761 | next_addr = int(s, 16) + 1 1762 | next_tag = self.hex_byte_addr_to_hex_tag(next_addr) 1763 | tag_id = self.canvas.find_withtag(next_tag) 1764 | if len(tag_id) == 0: 1765 | return 1766 | 1767 | caret_tag_id = self.canvas.find_withtag(self.caret_rect_tag) 1768 | if len(caret_tag_id) > 0: 1769 | self.canvas.delete(caret_tag_id) 1770 | 1771 | r = self.canvas.create_rectangle(self.canvas.bbox(tag_id[0]), fill="#193300", tags='caret_rect_tag') 1772 | self.canvas.tag_lower(r) 1773 | 1774 | tags = self.canvas.gettags(tag_id) 1775 | self.mouse_click_tags = tags 1776 | 1777 | def add_emulator_core_access(self, emul_uc): 1778 | self.emulator_core = emul_uc 1779 | 1780 | def draw_addr_area(self, dump_addr): 1781 | 1782 | mem_addr_str = '{:08X}'.format(dump_addr) 1783 | 1784 | self.addr_rect_pix_width = self.font_char_width * len(mem_addr_str) 1785 | # self.canvas.create_rectangle(0, 0, self.addr_rect_pix_width, self.height, fill=BACKGROUND_COLOR) 1786 | 1787 | self.number_of_line = self.height // self.font_char_height 1788 | 1789 | for i in range(self.number_of_line): 1790 | x = dump_addr + (i * VIEW_MEMORY_DUMP_RANGE) 1791 | s = '{:08X}'.format(x) 1792 | self.canvas.create_text(0, self.font_char_height * i, font=self.font, tags=self.address_line_tag + s, 1793 | text=s, 1794 | activefill='#33FF33', 1795 | fill=TEXT_COLOR, anchor="nw") 1796 | 1797 | def _draw_dword_separate_line(self): 1798 | self.draw_data_base_x = self.addr_rect_pix_width + (self.font_char_width * 1) 1799 | self.dword_block_width = 4 * (self.font_char_width * 3) 1800 | self.dword_block_width = self.dword_block_width + self.font_char_width # space after before end line | e.g. FF | 1801 | 1802 | for i in range(5): 1803 | x = self.draw_data_base_x + (self.dword_block_width * i) 1804 | if x > self.width: 1805 | break 1806 | self.canvas.create_line(x, 0, x, self.height, fill=TEXT_COLOR) 1807 | 1808 | def get_raw_byte_data_from_memory_buffer(self, i, dump_buffer): 1809 | rr = "" 1810 | if i < len(dump_buffer): 1811 | rr = ord(dump_buffer[i:i + 1]) 1812 | return True, rr 1813 | 1814 | return False, 0x0 1815 | 1816 | def get_formated_byte_data_from_memory_buffer(self, i, dump_buffer): 1817 | c = "" 1818 | if i < len(dump_buffer): 1819 | c = binascii.hexlify(dump_buffer[i]) 1820 | else: 1821 | c = "??" 1822 | return c 1823 | 1824 | def hex_byte_addr_to_hex_tag(self, addr): 1825 | name_tag = '{:08X}'.format(addr) 1826 | name_tag = self.hex_tag_prefix + name_tag 1827 | return name_tag 1828 | 1829 | def hex_byte_addr_to_ascii_tag(self, addr): 1830 | ascii_tag = 'ascii_tag_{:08X}'.format(addr) 1831 | return ascii_tag 1832 | 1833 | def hex_value_to_printable_ascii_chr(self, v): 1834 | s = "" 1835 | if 0x20 < v < 0x7F: 1836 | s += chr(v) 1837 | else: 1838 | s += "." 1839 | return s 1840 | 1841 | def _draw_memory_data(self, dump_addr, dump_buffer): 1842 | 1843 | data_draw_area = self.width - self.draw_data_base_x 1844 | if data_draw_area < (self.font_char_width * 3): 1845 | return 1846 | 1847 | data_size = self.number_of_line * (self.font_char_width * 3) 1848 | progress = (self.font_char_width * 3) 1849 | 1850 | nr_element_in_line = 4 1851 | 1852 | hex_id = 0 1853 | ascii_id = 0 1854 | text_x = 0 1855 | base_y = 0 1856 | for i in range(self.number_of_line): 1857 | base_y = i * self.font_char_height 1858 | 1859 | for j in range(nr_element_in_line): 1860 | text_x = (self.draw_data_base_x + self.font_char_width) + (j * self.dword_block_width) 1861 | for k in range(4): 1862 | dump_byte = self.get_formated_byte_data_from_memory_buffer( 1863 | (i * VIEW_MEMORY_DUMP_RANGE) + (j * 4) + k, dump_buffer) 1864 | hex_tag = self.hex_byte_addr_to_hex_tag(dump_addr + hex_id) 1865 | 1866 | item = self.canvas.create_text(text_x, base_y, font=self.font, tags=hex_tag, text=dump_byte, 1867 | activefill='#33FF33', 1868 | fill=TEXT_COLOR, anchor="nw") 1869 | 1870 | hex_id += 1 1871 | text_x += (self.font_char_width * 3) 1872 | 1873 | text_x += (self.font_char_width * 1) 1874 | for l in range(4 * 4): 1875 | tag_name = self.hex_byte_addr_to_ascii_tag(dump_addr + ascii_id) 1876 | 1877 | (read_success, raw_byte) = self.get_raw_byte_data_from_memory_buffer(ascii_id, dump_buffer) 1878 | if read_success: 1879 | show_char = self.hex_value_to_printable_ascii_chr(raw_byte) 1880 | else: 1881 | show_char = "??" 1882 | 1883 | item = self.canvas.create_text(text_x, base_y, font=self.font, tags=tag_name, text=show_char, 1884 | activefill='#33FF33', fill=TEXT_COLOR, anchor="nw") 1885 | text_x += (self.font_char_width * 1) 1886 | ascii_id += 1 1887 | 1888 | def _draw_cursor_caret(self): 1889 | pass 1890 | 1891 | def _draw_text_data_on_canvas(self, dump_addr, dump_buffer): 1892 | self.canvas.delete("all") 1893 | self.draw_addr_area(dump_addr) 1894 | self._draw_dword_separate_line() 1895 | self._draw_memory_data(dump_addr, dump_buffer) 1896 | # self._draw_cursor_caret() 1897 | 1898 | (w, h) = (self.font.measure("W"), self.font.metrics("linespace")) 1899 | 1900 | def click(self, event): 1901 | self.focus_set() 1902 | self.mouse_x, self.mouse_y = event.x, event.y 1903 | self.set_current_as_main_view_frame() 1904 | 1905 | canv_click_id = self.canvas.find_withtag(tk.CURRENT) 1906 | tags = self.canvas.gettags(canv_click_id) 1907 | self.mouse_click_tags = tags 1908 | 1909 | if len(tags) > 1: 1910 | caret_tag_id = self.canvas.find_withtag(self.caret_rect_tag) 1911 | if len(caret_tag_id) > 0: 1912 | self.canvas.delete(caret_tag_id) 1913 | 1914 | r = self.canvas.create_rectangle(self.canvas.bbox(tags[0]), fill="#193300", tags='caret_rect_tag') 1915 | self.canvas.tag_lower(r) 1916 | 1917 | return 1918 | 1919 | def set_current_as_main_view_frame(self): 1920 | global g_current_memory_view_frame_number 1921 | g_current_memory_view_frame_number = self.view_frame_number 1922 | 1923 | def on_resize(self, event): 1924 | self.width = event.width 1925 | self.height = event.height 1926 | self.number_of_line = self.height // self.font_char_height 1927 | # resize the canvas 1928 | self.config(width=self.width, height=self.height) 1929 | 1930 | # do it only for the first frame the second is the same 1931 | self.emulator_core.set_mem_view_range(self.number_of_line * VIEW_MEMORY_DUMP_RANGE, self.view_frame_number) 1932 | 1933 | if self.emulator_core.is_wait_for_user_input(): 1934 | if self.view_frame_number == 0: 1935 | self.emulator_core.update_buffer_view_memory1() 1936 | view1_addr, view1_buffer = self.emulator_core.get_mem_view1_addr_data() 1937 | self.display_memory_from_browsing(view1_addr, view1_buffer) 1938 | elif self.view_frame_number == 1: 1939 | self.emulator_core.update_buffer_view_memory2() 1940 | view2_addr, view2_buffer = self.emulator_core.get_mem_view2_addr_data() 1941 | self.display_memory_from_browsing(view2_addr, view2_buffer) 1942 | 1943 | def get_dump_memory_data_range(self): 1944 | return self.number_of_line * (self.font_char_width * 3) * 16 1945 | 1946 | def update_hex_tag_value(self, dump_addr, buffer): 1947 | 1948 | for i in range(len(buffer)): 1949 | hex_tag = self.hex_byte_addr_to_hex_tag(dump_addr + i) 1950 | item = self.canvas.find_withtag(hex_tag) 1951 | 1952 | if len(item) == 0: 1953 | assert False 1954 | return 1955 | assert (len(item) == 1) 1956 | item = item[0] 1957 | hex_color = TEXT_COLOR 1958 | item_text = binascii.hexlify(buffer[i]) 1959 | self.canvas.itemconfig(item, tags=hex_tag, fill=hex_color, text=item_text) 1960 | 1961 | def update_ascii_tag_value(self, dump_addr, buffer): 1962 | 1963 | for i in range(len(buffer)): 1964 | ascii_char_tag = self.hex_byte_addr_to_ascii_tag(dump_addr + i) 1965 | item = self.canvas.find_withtag(ascii_char_tag) 1966 | 1967 | if len(item) == 0: 1968 | assert (False) 1969 | return 1970 | assert (len(item) == 1) 1971 | item = item[0] 1972 | 1973 | (read_success, raw_byte) = self.get_raw_byte_data_from_memory_buffer(i, buffer) 1974 | show_char = self.hex_value_to_printable_ascii_chr(raw_byte) 1975 | 1976 | hex_color = TEXT_COLOR 1977 | self.canvas.itemconfig(item, tags=ascii_char_tag, activefill='#33FF33', fill=TEXT_COLOR, text=show_char) 1978 | 1979 | def get_changed_byte_addr_list(self, dump_addr, buffer, prev_dump_addr, prev_dump_buffer): 1980 | assert (dump_addr == prev_dump_addr) 1981 | assert (len(buffer) == len(prev_dump_buffer)) 1982 | 1983 | addr_list = [] 1984 | for i in range(len(buffer)): 1985 | hex_tag = self.hex_byte_addr_to_hex_tag(dump_addr + i) 1986 | item = self.canvas.find_withtag(hex_tag) 1987 | 1988 | if len(item) == 0: 1989 | assert (False) 1990 | return 1991 | assert (len(item) == 1) 1992 | item = item[0] 1993 | if buffer[i] != prev_dump_buffer[i]: 1994 | addr_list.append(dump_addr + i) 1995 | return addr_list 1996 | 1997 | def mark_changed_bytes(self, addr_list): 1998 | 1999 | for i in range(len(addr_list)): 2000 | addr = addr_list[i] 2001 | hex_tag = self.hex_byte_addr_to_hex_tag(addr) 2002 | item = self.canvas.find_withtag(hex_tag) 2003 | if len(item) == 0: 2004 | continue 2005 | assert (len(item) == 1) 2006 | item = item[0] 2007 | hex_color = "#00ffff" 2008 | self.canvas.itemconfig(item, tags=hex_tag, fill=hex_color) 2009 | 2010 | return 2011 | 2012 | def display_memory_from_browsing(self, dump_addr, dump_buffer): 2013 | 2014 | # everything the same do nothing 2015 | if dump_addr == self.current_dump_addr and \ 2016 | len(dump_buffer) == len(self.current_dump_buffer) and dump_buffer == self.current_dump_buffer: 2017 | if not self.highlight_byte_addr_list: 2018 | return 2019 | 2020 | # buffer change need to update tag values 2021 | if dump_addr == self.current_dump_addr and \ 2022 | len(dump_buffer) == len(self.current_dump_buffer) and \ 2023 | dump_buffer != self.current_dump_buffer: 2024 | self.update_hex_tag_value(dump_addr, dump_buffer) 2025 | self.update_ascii_tag_value(dump_addr, dump_buffer) 2026 | 2027 | else: 2028 | self._draw_text_data_on_canvas(dump_addr, dump_buffer) 2029 | self.current_dump_addr = dump_addr 2030 | self.current_dump_buffer = dump_buffer 2031 | 2032 | self.mark_changed_bytes(self.highlight_byte_addr_list) 2033 | 2034 | def display_memory_from_single_step(self, addr, buf): 2035 | """ 2036 | Display new memory data and snapshot it to compare with the next single step data 2037 | """ 2038 | 2039 | # pdb.set_trace() 2040 | dump_addr = addr 2041 | dump_buffer = buf 2042 | 2043 | # everything the same do nothing 2044 | if dump_addr == self.current_dump_addr and \ 2045 | len(dump_buffer) == len(self.current_dump_buffer) and dump_buffer == self.current_dump_buffer: 2046 | if not self.highlight_byte_addr_list: 2047 | return 2048 | 2049 | self.highlight_byte_addr_list = [] # reset list 2050 | 2051 | # buffer change need to update tag values 2052 | if dump_addr == self.current_dump_addr and \ 2053 | len(dump_buffer) == len(self.current_dump_buffer) and \ 2054 | dump_buffer != self.current_dump_buffer: 2055 | 2056 | self.update_hex_tag_value(dump_addr, dump_buffer) 2057 | self.update_ascii_tag_value(dump_addr, dump_buffer) 2058 | 2059 | changed_bytes_list = self.get_changed_byte_addr_list(dump_addr, dump_buffer, self.current_dump_addr, 2060 | self.current_dump_buffer) 2061 | self.mark_changed_bytes(changed_bytes_list) 2062 | 2063 | self.current_dump_addr = dump_addr 2064 | self.current_dump_buffer = dump_buffer 2065 | self.highlight_byte_addr_list = changed_bytes_list 2066 | else: 2067 | self._draw_text_data_on_canvas(dump_addr, dump_buffer) 2068 | 2069 | self.current_dump_addr = dump_addr 2070 | self.current_dump_buffer = dump_buffer 2071 | 2072 | def clear_mem_view(self): 2073 | self.current_dump_buffer = "" 2074 | self.current_dump_addr = IMAGEBASE 2075 | self.canvas.delete("all") 2076 | 2077 | 2078 | # 2079 | # Disassembly window 2080 | # 2081 | class DisassemblyWindow(tk.Frame): 2082 | def __init__(self, master=None): 2083 | self.parent = master 2084 | tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken") 2085 | self._create_layout() 2086 | self._create_settings() 2087 | self._create_bindings() 2088 | 2089 | def _create_settings(self): 2090 | self.emulator_core = None 2091 | self.disasm_number_of_lines = 0 2092 | self.disasm_edit_area.tag_configure("ip_ln_highlight_tag", foreground="#e9e9e9", background="#808080") 2093 | self.disasm_edit_area.tag_configure("bp_active_ln_highlight_tag", foreground="#FFFFFF", background="#CC0000") 2094 | self.disasm_edit_area.tag_configure("cpu_registers_tag", foreground="#DAA520", background=BACKGROUND_COLOR) 2095 | self.disasm_edit_area.tag_configure("branch_instr_tag", foreground="#FFCC00", background=BACKGROUND_COLOR) 2096 | self.disasm_edit_area.tag_configure("word_under_cur_tag", foreground=BACKGROUND_COLOR, background="#FFFF00") 2097 | self.word_under_cursor = "" 2098 | self.prev_cursor_position_str = "" 2099 | 2100 | def _create_layout(self): 2101 | self.font = tkFont.Font(family=FONT_FAMILY, size=FONT_SIZE) 2102 | (self.font_width, self.font_height) = (self.font.measure("W"), self.font.metrics("linespace")) 2103 | 2104 | self.disasm_edit_area = tk.Text(self, wrap=tk.NONE, undo=True, 2105 | width=1, height=1, 2106 | relief=tk.SUNKEN, borderwidth=5, 2107 | highlightthickness=0, insertbackground="white") 2108 | self.disasm_edit_area.config(font=self.font) 2109 | self.disasm_edit_area.configure(bg="black", fg=TEXT_COLOR) 2110 | 2111 | self.scrollbarY = tk.Scrollbar(self, orient=tk.VERTICAL) 2112 | self.scrollbarX = tk.Scrollbar(self, orient=tk.HORIZONTAL) 2113 | 2114 | self.status_label_text = tk.StringVar() 2115 | self.status_label_text.set("") 2116 | 2117 | self.status = tk.Label(self, text="", textvariable=self.status_label_text, bd=1, relief=tk.SUNKEN, 2118 | anchor=tk.W, bg='lightgray') 2119 | 2120 | self.disasm_edit_area.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E)) 2121 | self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=2) 2122 | self.scrollbarX.grid(row=1, column=0, sticky=(tk.W, tk.E)) 2123 | self.status.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), columnspan=2) 2124 | 2125 | self.rowconfigure(0, weight=1) 2126 | self.columnconfigure(0, weight=1) 2127 | 2128 | self.disasm_edit_area.configure(yscrollcommand=self.scrollbarY.set, xscrollcommand=self.scrollbarX.set) 2129 | 2130 | self.scrollbarY.config(command=self.disasm_edit_area.yview) 2131 | self.scrollbarX.config(command=self.disasm_edit_area.xview) 2132 | 2133 | def add_emulator_core_access(self, emul_uc): 2134 | self.emulator_core = emul_uc 2135 | 2136 | def _create_bindings(self): 2137 | self.disasm_edit_area.bind("", self.on_dasm_view_resize) 2138 | self.disasm_edit_area.bind('', self.mouse_double_click) 2139 | self.disasm_edit_area.bind('', self.on_toggle_line_breakpoint_key_pressed) 2140 | 2141 | os = platform.system() 2142 | if os == "Linux": 2143 | self.disasm_edit_area.bind('<4>', self.on_mousewheel_scroll_up) 2144 | self.disasm_edit_area.bind('<5>', self.on_mousewheel_scroll_down) 2145 | else: 2146 | # Windows and MacOS 2147 | self.disasm_edit_area.bind("", self.on_mousewheel) 2148 | 2149 | def on_mousewheel_scroll_up(self, event=None): 2150 | if not self.emulator_core.is_wait_for_user_input(): 2151 | return 2152 | curr_addr = prev_addr = self.emulator_core.get_dasm_listing_first_addr() 2153 | if None == prev_addr: 2154 | return 2155 | prev_addr -= (4 * 3) 2156 | if not self.emulator_core.is_mem_region_mapped(prev_addr): 2157 | prev_addr = self.emulator_core.find_prev_mem_begin_region(curr_addr) 2158 | if prev_addr == None: 2159 | return 2160 | self.emulator_core.reload_dasm_listing_with_unassemble_from_addr(prev_addr) 2161 | self.emulator_core.set_dasm_view_update() 2162 | 2163 | def on_mousewheel_scroll_down(self, event=None): 2164 | if not self.emulator_core.is_wait_for_user_input(): 2165 | return 2166 | next_addr = self.emulator_core.get_dasm_listing_first_addr() 2167 | if None == next_addr: 2168 | return 2169 | next_addr += (4 * 3) 2170 | if not self.emulator_core.is_mem_region_mapped(next_addr): 2171 | return 2172 | self.emulator_core.reload_dasm_listing_with_unassemble_from_addr(next_addr) 2173 | self.emulator_core.set_dasm_view_update() 2174 | 2175 | def on_mousewheel(self, event=None): 2176 | delta = (event.delta / 120) 2177 | if delta > 0: 2178 | self.on_mousewheel_scroll_up() 2179 | else: 2180 | self.on_mousewheel_scroll_down() 2181 | 2182 | def on_toggle_line_breakpoint_key_pressed(self, event=None): 2183 | pos_str = self.disasm_edit_area.index("insert linestart") 2184 | # print (pos_str) 2185 | dot_offset = pos_str.find('.') 2186 | index = 0 2187 | if dot_offset == -1: 2188 | return 2189 | try: 2190 | index = int(pos_str[0:dot_offset], 10) 2191 | except ValueError: 2192 | pass 2193 | 2194 | index = index - 1 # addr index starts from 0 2195 | if self.emulator_core.is_wait_for_user_input(): 2196 | (ok, addr) = self.emulator_core.dasm_listing_index_to_addr(index) 2197 | if ok: 2198 | (bp_found, state) = self.emulator_core.get_bp_settings_for_addr(addr) 2199 | if bp_found: 2200 | self.emulator_core.del_bp_addr(addr) 2201 | else: 2202 | self.emulator_core.toggle_bp_settings(addr, 1) 2203 | self.prev_cursor_position_str = pos_str 2204 | self.emulator_core.set_dasm_view_update() 2205 | 2206 | def highlight_word_under_cursor(self): 2207 | 2208 | self.disasm_edit_area.tag_remove("word_under_cur_tag", "1.0", tk.END) 2209 | 2210 | if self.disasm_edit_area.tag_ranges("sel"): 2211 | try: 2212 | tmp_under_cur_text = self.disasm_edit_area.get(tk.SEL_FIRST, tk.SEL_LAST) 2213 | tmp_under_cur_text = tmp_under_cur_text.strip() 2214 | if tmp_under_cur_text.startswith(", "): 2215 | self.word_under_cursor = "" 2216 | return 2217 | elif len(tmp_under_cur_text): 2218 | self.highlight_keyword(tmp_under_cur_text, "word_under_cur_tag") 2219 | 2220 | # 2221 | # pad a string len 8 e.g. 0x1000 to 00001000 and highlight address (if found in the dasm listing) 2222 | # 2223 | if tmp_under_cur_text.startswith("0x"): 2224 | try: 2225 | imm = int(tmp_under_cur_text, 16) 2226 | addr_str = '{:08X}'.format(imm) 2227 | self.highlight_keyword(addr_str, "word_under_cur_tag") 2228 | except ValueError: 2229 | pass 2230 | elif tmp_under_cur_text.startswith("#0x"): 2231 | try: 2232 | imm = int(tmp_under_cur_text[1:], 16) 2233 | addr_str = '{:08X}'.format(imm) 2234 | self.highlight_keyword(addr_str, "word_under_cur_tag") 2235 | except ValueError: 2236 | pass 2237 | 2238 | self.word_under_cursor = tmp_under_cur_text 2239 | except tk.TclError: 2240 | self.word_under_cursor = "" 2241 | pass 2242 | 2243 | def mouse_double_click(self, event): 2244 | self.after(100, self.highlight_word_under_cursor) 2245 | 2246 | def on_dasm_view_resize(self, event): 2247 | self.disasm_number_of_lines = event.height // self.font_height 2248 | if self.disasm_number_of_lines > 0: 2249 | self.disasm_number_of_lines -= 1 2250 | else: 2251 | self.disasm_number_of_lines = 1 2252 | return 2253 | 2254 | if event.height <= self.font_height * 4: 2255 | return 2256 | 2257 | if not self.emulator_core.is_dasm_view_update(): 2258 | self.emulator_core.set_disasm_number_of_lines(self.disasm_number_of_lines) 2259 | 2260 | if self.emulator_core.is_wait_for_user_input() and self.emulator_core.is_dasm_view_update() == False: 2261 | self.emulator_core.set_dasm_view_update() 2262 | 2263 | def set_status_label_text(self, txt): 2264 | self.status_label_text.set(txt) 2265 | 2266 | def highlight_keyword(self, keyword, tag): 2267 | if len(keyword) == 0: 2268 | return 2269 | pos = '1.0' 2270 | while True: 2271 | idx = self.disasm_edit_area.search(keyword, pos, tk.END) 2272 | if not idx: 2273 | break 2274 | pos = '{}+{}c'.format(idx, len(keyword)) 2275 | self.disasm_edit_area.tag_add(tag, idx, pos) 2276 | 2277 | def clear_dasm_view_edit_area(self): 2278 | self.disasm_edit_area.delete('1.0', tk.END) 2279 | 2280 | def update_dasm_view_edit_area(self, dasm_listing_dict, ip_highlight_ln): 2281 | if not dasm_listing_dict: 2282 | return 2283 | 2284 | bo_settings = self.emulator_core.get_bp_settings() 2285 | self.disasm_edit_area.delete('1.0', tk.END) 2286 | bp_highlight_ln = [] 2287 | 2288 | curr_text_line = 1 2289 | for key_addr in dasm_listing_dict: 2290 | 2291 | if curr_text_line > 1: 2292 | self.disasm_edit_area.insert(tk.END, "\n") 2293 | 2294 | if key_addr in bo_settings: 2295 | if ip_highlight_ln != curr_text_line: 2296 | bp_highlight_ln.append(curr_text_line) 2297 | 2298 | dasm_text_line = dasm_listing_dict[key_addr] 2299 | self.disasm_edit_area.insert(tk.END, dasm_text_line) 2300 | curr_text_line += 1 2301 | 2302 | self.highlight_keyword("r0", "cpu_registers_tag") 2303 | self.highlight_keyword("r1", "cpu_registers_tag") 2304 | self.highlight_keyword("r2", "cpu_registers_tag") 2305 | self.highlight_keyword("r3", "cpu_registers_tag") 2306 | self.highlight_keyword("r4", "cpu_registers_tag") 2307 | self.highlight_keyword("r5", "cpu_registers_tag") 2308 | self.highlight_keyword("r6", "cpu_registers_tag") 2309 | self.highlight_keyword("r7", "cpu_registers_tag") 2310 | self.highlight_keyword("r8", "cpu_registers_tag") 2311 | self.highlight_keyword("r9", "cpu_registers_tag") 2312 | self.highlight_keyword("r10", "cpu_registers_tag") 2313 | self.highlight_keyword("r11", "cpu_registers_tag") 2314 | self.highlight_keyword("r12", "cpu_registers_tag") 2315 | self.highlight_keyword("r13", "cpu_registers_tag") 2316 | self.highlight_keyword("r14", "cpu_registers_tag") 2317 | self.highlight_keyword("pc", "cpu_registers_tag") 2318 | 2319 | self.highlight_keyword(" bne ", "branch_instr_tag") 2320 | self.highlight_keyword(" bl ", "branch_instr_tag") 2321 | self.highlight_keyword(" b ", "branch_instr_tag") 2322 | self.highlight_keyword(" bls ", "branch_instr_tag") 2323 | self.highlight_keyword(" beq ", "branch_instr_tag") 2324 | self.highlight_keyword(" bxeq ", "branch_instr_tag") 2325 | 2326 | self.disasm_edit_area.tag_lower('cpu_registers_tag') 2327 | self.disasm_edit_area.tag_lower('branch_instr_tag') 2328 | 2329 | if ip_highlight_ln != -1 and ip_highlight_ln <= (self.disasm_number_of_lines): 2330 | # highlight IP/PC line 2331 | begin = str(ip_highlight_ln) + ".0" 2332 | end = str(ip_highlight_ln) + ".end+1c" 2333 | self.disasm_edit_area.tag_add("ip_ln_highlight_tag", begin, end) 2334 | 2335 | for i in range(len(bp_highlight_ln)): 2336 | ln = bp_highlight_ln[i] 2337 | begin = str(ln) + ".0" 2338 | end = str(ln) + ".end+1c" 2339 | self.disasm_edit_area.tag_add("bp_active_ln_highlight_tag", begin, end) 2340 | 2341 | if len(self.prev_cursor_position_str) != 0: 2342 | self.disasm_edit_area.mark_set("insert", self.prev_cursor_position_str) 2343 | self.prev_cursor_position_str = "" 2344 | 2345 | def go_continue_run(self): 2346 | if self.emulator_core.is_emulator_running() and self.emulator_core.is_wait_for_user_input(): 2347 | self.disasm_edit_area.tag_remove("ip_ln_highlight_tag", "1.0", 'end') 2348 | self.emulator_core.gogo_emulator() 2349 | 2350 | 2351 | # 2352 | # MainWindow 2353 | # 2354 | class MainWindow(tk.Frame): 2355 | def __init__(self, master=None): 2356 | self.parent = master 2357 | self.load_file_name = "" 2358 | tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken") 2359 | self._create_objects() 2360 | self._create_layout() 2361 | self._create_menu() 2362 | self._create_coupling() 2363 | self._create_settings() 2364 | self._create_bindings() 2365 | self._on_time_shot() 2366 | 2367 | def _create_bindings(self): 2368 | self.bind("", self.main_frame_resize_event) 2369 | 2370 | def main_frame_resize_event(self, event): 2371 | w, h = event.width, event.height 2372 | if self.emulator_core.is_emulator_running(): 2373 | print ("resize windows") 2374 | 2375 | def _create_settings(self): 2376 | self.mem_view_frame1.set_view_number(0) 2377 | self.mem_view_frame2.set_view_number(1) 2378 | 2379 | def _create_coupling(self): 2380 | self.dasm_frame.add_emulator_core_access(self.emulator_core) 2381 | self.command_line_frame.add_emulator_core_access(self.emulator_core) 2382 | self.mem_view_frame1.add_emulator_core_access(self.emulator_core) 2383 | self.mem_view_frame2.add_emulator_core_access(self.emulator_core) 2384 | 2385 | def _create_objects(self): 2386 | self.emulator_core = EmulatorCore() 2387 | 2388 | def _create_layout(self): 2389 | self.dasm_frame = DisassemblyWindow(self) 2390 | self.register_frame = RegisterWindow(self) 2391 | self.mem_view_frame1 = MemoryViewWindow(self) 2392 | self.mem_view_frame2 = MemoryViewWindow(self) 2393 | self.command_line_frame = CommandLineWindow(self) 2394 | 2395 | self.dasm_frame.grid(row=0, column=0, rowspan=1, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E)) 2396 | self.register_frame.grid(row=0, column=8, rowspan=1, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E)) 2397 | self.mem_view_frame1.grid(row=1, column=0, rowspan=1, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E)) 2398 | self.mem_view_frame2.grid(row=1, column=5, rowspan=1, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E)) 2399 | self.command_line_frame.grid(row=2, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E)) 2400 | 2401 | self.rowconfigure(0, weight=4) 2402 | self.rowconfigure(1, weight=2) 2403 | self.rowconfigure(2, weight=1) 2404 | 2405 | for c in range(10): 2406 | self.columnconfigure(c, weight=1) 2407 | 2408 | def close_quit(self, event=None): 2409 | global program_exit 2410 | program_exit = True 2411 | 2412 | self.parent.destroy() 2413 | 2414 | def _create_menu(self): 2415 | self.menubar = tk.Menu(self.parent) 2416 | 2417 | self.filemenu = tk.Menu(self.menubar, tearoff=0) 2418 | self.filemenu.add_command(label="Open", command=self.open_file) 2419 | self.filemenu.add_separator() 2420 | self.filemenu.add_command(label="Start input server", command=self.start_input_server) 2421 | self.filemenu.add_command(label="Save memory dump as...", command=self.save_binary_dump) 2422 | self.filemenu.add_separator() 2423 | 2424 | self.filemenu.add_command(label="Exit", command=self.close_quit) 2425 | self.menubar.add_cascade(label="File", menu=self.filemenu) 2426 | 2427 | self.emulmenu = tk.Menu(self.menubar, tearoff=0) 2428 | self.emulmenu.add_command(label="Single step", accelerator="F7", command=self.on_single_step_from_menu) 2429 | self.emulmenu.add_command(label="Go", accelerator="F5", command=self.on_emul_gogo) 2430 | self.emulmenu.add_command(label="Break", command=self.break_emul_execution) 2431 | self.emulmenu.add_command(label="Stop emulator", command=self.on_emul_stop) 2432 | self.emulmenu.add_command(label="Toggle breakpoint", accelerator="F2", command=self.toggle_breakpoint_from_menu) 2433 | self.emulmenu.add_command(label="Restart", command=self.restart_file_load) 2434 | self.menubar.add_cascade(label="Emulator", menu=self.emulmenu) 2435 | 2436 | self.helpmenu = tk.Menu(self.menubar, tearoff=0) 2437 | self.helpmenu.add_command(label="About...", command=self.dummy_func) 2438 | self.menubar.add_cascade(label="Help", menu=self.helpmenu) 2439 | 2440 | self.parent.bind('', self.on_single_step) 2441 | self.parent.bind('', self.on_focus_cmd_line) 2442 | self.parent.bind('', self.on_emul_gogo) 2443 | 2444 | self.parent.config(menu=self.menubar) 2445 | 2446 | def dummy_func(self): 2447 | self.emulator_core.write_test_data() 2448 | print("dummy") 2449 | pass 2450 | 2451 | def update_all_memory_windows_browsing_dump_data(self): 2452 | 2453 | self.emulator_core.update_buffer_view_memory1() 2454 | self.emulator_core.update_buffer_view_memory2() 2455 | 2456 | view1_addr, view1_buffer = self.emulator_core.get_mem_view1_addr_data() 2457 | view2_addr, view2_buffer = self.emulator_core.get_mem_view2_addr_data() 2458 | 2459 | self.mem_view_frame1.display_memory_from_browsing(view1_addr, view1_buffer) 2460 | self.mem_view_frame2.display_memory_from_browsing(view2_addr, view2_buffer) 2461 | 2462 | def update_all_memory_windows_single_step_dump_data(self): 2463 | view1_addr, view1_buffer = self.emulator_core.get_mem_view1_addr_data() 2464 | view2_addr, view2_buffer = self.emulator_core.get_mem_view2_addr_data() 2465 | 2466 | self.mem_view_frame1.display_memory_from_single_step(view1_addr, view1_buffer) 2467 | self.mem_view_frame2.display_memory_from_single_step(view2_addr, view2_buffer) 2468 | 2469 | def set_load_file_name(self, fname): 2470 | self.load_file_name = fname 2471 | 2472 | def load_input_stream_wrapper(self, binary_stream): 2473 | print("[!] load_input_stream_wrapper") 2474 | self.after(5000, self.load_input_stream(binary_stream)) 2475 | 2476 | def load_input_stream(self, binary_stream): 2477 | global ENTRYPOINT 2478 | global IMAGEBASE 2479 | global IS_THUMB_MODE 2480 | 2481 | if len(binary_stream) == 0: 2482 | return 2483 | self.emulator_core.reset_run_counter() 2484 | 2485 | ENTRYPOINT = 0 2486 | IMAGEBASE = 0 2487 | 2488 | print("[i] Loading code from stream client") 2489 | print("Imagebase: 0x" + '{:08X}'.format(IMAGEBASE) + " Entrypoint: 0x" + '{:08X}'.format(ENTRYPOINT)) 2490 | 2491 | binary_stream_size = self.emulator_core.load_binary_stream(binary_stream) 2492 | if binary_stream_size != 0: 2493 | print("Loaded binary stream: size: 0x{:04x}".format(binary_stream_size)) 2494 | self.parent.title("Stream from client") 2495 | self.emulator_core.start_emul_thread() 2496 | 2497 | self.dasm_frame.set_status_label_text("") 2498 | self.after(10, self.emul_even_worker_loop) 2499 | 2500 | def load_input_file(self): 2501 | if self.load_file_name == "": 2502 | return 2503 | 2504 | self.emulator_core.reset_run_counter() 2505 | file_size = self.emulator_core.load_input_file(self.load_file_name) 2506 | if file_size != 0: 2507 | print("Loaded file: {:s} - file size: 0x{:04x}".format(self.load_file_name, file_size)) 2508 | self.parent.title(self.load_file_name) 2509 | self.emulator_core.start_emul_thread() 2510 | 2511 | self.dasm_frame.set_status_label_text("") 2512 | self.after(10, self.emul_even_worker_loop) 2513 | 2514 | else: 2515 | print("Load file error %s" % self.load_file_name) 2516 | 2517 | # 2518 | # Emul thread event loop 2519 | # 2520 | def emul_even_worker_loop(self): 2521 | global program_exit 2522 | 2523 | if program_exit == True or self.emulator_core.is_emulator_running() == False: 2524 | self.after(100, self.emul_even_worker_finished()) 2525 | return 2526 | 2527 | if self.emulator_core.is_emul_data_waiting(): 2528 | self.update_all_frames_with_new_emul_data() 2529 | 2530 | elif self.emulator_core.is_wait_for_user_input() and self.emulator_core.is_dasm_view_update(): 2531 | self.update_dasm_view_window() 2532 | 2533 | elif self.emulator_core.is_wait_for_user_input() and self.emulator_core.is_register_view_update(): 2534 | self.update_register_view_window() 2535 | 2536 | elif self.emulator_core.is_wait_for_user_input() and self.emulator_core.is_update_mem_view(): 2537 | self.update_mem_frames_with_new_emul_data() 2538 | 2539 | self.after(10, self.emul_even_worker_loop) 2540 | return 2541 | 2542 | def emul_even_worker_finished(self): 2543 | last_error = self.emulator_core.get_last_error() 2544 | self.dasm_frame.set_status_label_text(last_error) 2545 | self.dasm_frame.clear_dasm_view_edit_area() 2546 | self.mem_view_frame1.clear_mem_view() 2547 | self.mem_view_frame2.clear_mem_view() 2548 | self.update_register_view_window_with_last_reg_data() 2549 | 2550 | print ("Emulator thread finished: %s" % (time.ctime(time.time()))) 2551 | 2552 | def update_register_view_window_with_last_reg_data(self): 2553 | last_reg_data = self.emulator_core.get_register_last_data() 2554 | self.register_frame.update_register_view(last_reg_data) 2555 | 2556 | self.command_line_frame.print_emul_exit_context(last_reg_data) 2557 | 2558 | def update_register_view_window(self): 2559 | reg_data = self.emulator_core.get_register_data() 2560 | self.register_frame.update_register_view(reg_data) 2561 | self.emulator_core.reset_update_register_view() 2562 | pass 2563 | 2564 | def update_dasm_view_window(self): 2565 | if self.emulator_core.reload_dasm_listing(): 2566 | dasm_lines = self.emulator_core.get_disasm_listing_dict() 2567 | if dasm_lines: 2568 | highlight_ln = self.emulator_core.get_highlight_line_num() 2569 | self.dasm_frame.update_dasm_view_edit_area(dasm_lines, highlight_ln) 2570 | self.emulator_core.reset_dasm_view_update() 2571 | 2572 | def update_mem_frames_with_new_emul_data(self): 2573 | self.update_all_memory_windows_browsing_dump_data() 2574 | self.emulator_core.reset_update_mem_view() 2575 | 2576 | def update_all_frames_with_new_emul_data(self): 2577 | dasm_listing_dict = self.emulator_core.get_disasm_listing_dict() 2578 | highlight_ln = self.emulator_core.get_highlight_line_num() 2579 | reg_data = self.emulator_core.get_register_data() 2580 | 2581 | # pdb.set_trace() 2582 | 2583 | self.dasm_frame.update_dasm_view_edit_area(dasm_listing_dict, highlight_ln) 2584 | self.register_frame.update_register_view(reg_data) 2585 | self.update_all_memory_windows_single_step_dump_data() 2586 | 2587 | assert (self.emulator_core.is_emul_data_waiting() == True) 2588 | self.emulator_core.reset_emul_data_waiting() 2589 | 2590 | def _on_time_shot(self): 2591 | self.after(100, self.load_input_file) 2592 | 2593 | def get_filename_from_user(self): 2594 | filename = "" 2595 | if sys.version_info[0] < 3: 2596 | filename = askopenfilename(title="Open file", filetypes=[]) 2597 | else: 2598 | filename = filedialog.askopenfilename(title="Open file", filetypes=[]) 2599 | return filename 2600 | 2601 | def start_input_server(self): 2602 | if self.emulator_core.is_emulator_running(): 2603 | print("[!] Emulator running") 2604 | return 2605 | 2606 | # FIXME: this is a bit buggy since it will block UI 2607 | self.after(100, self.spawn_byte_stream_server_worker()) 2608 | 2609 | def spawn_byte_stream_server_worker(self): 2610 | print("[i] Starting stream server") 2611 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 2612 | host = socket.gethostname() 2613 | port = 5559 2614 | 2615 | s.bind((host, port)) 2616 | s.listen(1) 2617 | conn, addr = s.accept() 2618 | bin_stream = b"" 2619 | print('Connected by', addr) 2620 | while True: 2621 | data = conn.recv(1024) 2622 | if not data: 2623 | break 2624 | bin_stream += data 2625 | 2626 | conn.close() 2627 | self.after(100, self.load_input_stream(bin_stream)) 2628 | 2629 | def open_file(self): 2630 | if self.emulator_core.is_emulator_running(): 2631 | print("[!] Emulator running") 2632 | return 2633 | 2634 | filename = TEST_FILENAME 2635 | if len(filename) == 0: 2636 | filename = self.get_filename_from_user() 2637 | if len(filename): 2638 | self.preload_input_file_start_emul(filename) 2639 | else: 2640 | self.preload_input_file_start_emul(filename) 2641 | 2642 | def preload_input_file_start_emul(self, filename): 2643 | self.set_load_file_name(filename) 2644 | self.after(100, self.load_input_file) 2645 | 2646 | def restart_file_load(self): 2647 | if self.emulator_core.is_emulator_running(): 2648 | print("[!] Emulator running") 2649 | return 2650 | if len(self.load_file_name): 2651 | self.after(100, self.load_input_file) 2652 | 2653 | def save_binary_dump(self): 2654 | pass 2655 | 2656 | def on_single_step_from_menu(self): 2657 | self.on_single_step(self) 2658 | 2659 | def on_emul_stop(self): 2660 | self.emulator_core.stop_emulator() 2661 | 2662 | def on_emul_gogo(self, event=None): 2663 | self.dasm_frame.go_continue_run() 2664 | 2665 | def break_emul_execution(self): 2666 | self.emulator_core.set_break_run() 2667 | 2668 | def toggle_breakpoint_from_menu(self): 2669 | self.dasm_frame.on_toggle_line_breakpoint_key_pressed() 2670 | 2671 | # @staticmethod 2672 | def on_single_step(self, event): 2673 | self.emulator_core.single_step_emulator() 2674 | 2675 | def on_focus_cmd_line(self, event): 2676 | self.command_line_frame.on_focus_cmd_line() 2677 | 2678 | def is_emul_running(self): 2679 | return self.emulator_core.is_emulator_running() 2680 | 2681 | 2682 | def maximize_window(root): 2683 | w, h = root.winfo_screenwidth(), root.winfo_screenheight() 2684 | root.geometry("%dx%d+0+0" % (w, h)) 2685 | 2686 | 2687 | # 2688 | # MAIN 2689 | # 2690 | def main(): 2691 | load_file_name = "" 2692 | if len(sys.argv) > 1: 2693 | load_file_name = str(sys.argv[1]) 2694 | 2695 | root = tk.Tk() 2696 | 2697 | root.title("JuniEmul") 2698 | root.geometry("850x500+525+300") 2699 | # maximize_window(root) 2700 | 2701 | try: 2702 | path = os.path.join(os.path.dirname(__file__), "images/") 2703 | print (path) 2704 | if sys.platform.startswith("win"): 2705 | icon = path + "interest.ico" 2706 | else: 2707 | icon = "@" + path + "interest.xbm" 2708 | # application.iconbitmap(icon) 2709 | except: 2710 | pass 2711 | 2712 | root.configure(background="#808080") 2713 | root.option_add("*font", ("Courier New", 9, "normal")) 2714 | 2715 | window = MainWindow(master=root) 2716 | window.pack(side="top", fill="both", expand=True) 2717 | 2718 | root.protocol("WM_DELETE_WINDOW", window.close_quit) 2719 | root.mainloop() 2720 | 2721 | if window.is_emul_running(): 2722 | print("Emulator force exit") 2723 | os._exit(-1) 2724 | 2725 | 2726 | if __name__ == '__main__': 2727 | main() 2728 | 2729 | -------------------------------------------------------------------------------- /sample_arm_code_binary: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkup/juniEmu/ad5f4446915554ca768afbd9b9338ab9406f7e88/sample_arm_code_binary -------------------------------------------------------------------------------- /screenshots/juniEmul.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkup/juniEmu/ad5f4446915554ca768afbd9b9338ab9406f7e88/screenshots/juniEmul.png --------------------------------------------------------------------------------