1#! /usr/bin/env python 2## -*- coding: utf-8 -*- 3 4from __future__ import print_function 5from triton import * 6 7import random 8import string 9import sys 10import lief 11import os 12 13 14DEBUG = False 15# DEBUG = True 16TARGET = os.path.join(os.path.dirname(__file__), './bin/crypto_test-nothumb-O0.bin') 17STOP_ADDR = None 18MAX_INSTRS = 100000 19 20 21# The debug function 22def debug(s): 23 if DEBUG: print(s) 24 25 26def print_state(ctx): 27 print('[+] r0 (r0): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r0))) 28 print('[+] r1 (r1): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r1))) 29 print('[+] r2 (r2): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r2))) 30 print('[+] r3 (r3): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r3))) 31 print('[+] r4 (r4): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r4))) 32 print('[+] r5 (r5): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r5))) 33 print('[+] r6 (r6): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r6))) 34 print('[+] r6 (r6): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r6))) 35 print('[+] r7 (r7): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r7))) 36 print('[+] r8 (r8): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r8))) 37 print('[+] r9 (r9): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r9))) 38 print('[+] r10 (r10): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r10))) 39 print('[+] r11 (fp): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r11))) 40 print('[+] r12 (ip): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r12))) 41 print('[+] r13 (sp): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.sp))) 42 print('[+] r14 (lr): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r14))) 43 print('[+] r15 (pc): {:08x}'.format(ctx.getConcreteRegisterValue(ctx.registers.pc))) 44 45 46# Memory mapping 47BASE_PLT = 0x10000000 48BASE_ARGV = 0x20000000 49BASE_STACK = 0x9fffffff 50 51 52def getMemoryString(ctx, addr): 53 s = str() 54 index = 0 55 56 while ctx.getConcreteMemoryValue(addr+index): 57 c = chr(ctx.getConcreteMemoryValue(addr+index)) 58 if c not in string.printable: c = "" 59 s += c 60 index += 1 61 62 return s 63 64 65def getFormatString(ctx, addr): 66 return getMemoryString(ctx, addr) \ 67 .replace("%s", "{}").replace("%d", "{:d}").replace("%#02x", "{:#02x}") \ 68 .replace("%#x", "{:#x}").replace("%x", "{:x}").replace("%02X", "{:02x}") \ 69 .replace("%c", "{:c}").replace("%02x", "{:02x}").replace("%ld", "{:d}") \ 70 .replace("%*s", "").replace("%lX", "{:x}").replace("%08x", "{:08x}") \ 71 .replace("%u", "{:d}").replace("%lu", "{:d}") \ 72 73 74# Simulate the aeabi_memclr() function 75def aeabi_memclrHandler(ctx): 76 print('[+] __aeabi_memclr hooked') 77 78 # Get arguments 79 arg1 = ctx.getConcreteRegisterValue(ctx.registers.r0) 80 arg2 = ctx.getConcreteRegisterValue(ctx.registers.r1) 81 82 print('\targ1: {:x}'.format(arg1)) 83 print('\targ2: {:x}'.format(arg2)) 84 85 for i in range(0, arg2): 86 ctx.setConcreteMemoryValue(arg1 + i, 0) 87 88 89# Simulate the __aeabi_memcpy() function 90def aeabi_memcpyHandler(ctx): 91 print('[+] __aeabi_memcpy hooked') 92 93 # Get arguments 94 arg1 = ctx.getConcreteRegisterValue(ctx.registers.r0) 95 arg2 = ctx.getConcreteRegisterValue(ctx.registers.r1) 96 arg3 = ctx.getConcreteRegisterValue(ctx.registers.r2) 97 98 print('\targ1: {:x}'.format(arg1)) 99 print('\targ2: {:x}'.format(arg2)) 100 print('\targ3: {:x}'.format(arg3)) 101 102 for i in range(0, arg3): 103 b = ctx.getConcreteMemoryValue(arg2 + i) 104 ctx.setConcreteMemoryValue(arg1 + i, b) 105 106 107# Simulate the memcpy() function 108def memcpyHandler(ctx): 109 print('[+] memcpy hooked') 110 111 # Get arguments 112 arg1 = ctx.getConcreteRegisterValue(ctx.registers.r0) 113 arg2 = ctx.getConcreteRegisterValue(ctx.registers.r1) 114 arg3 = ctx.getConcreteRegisterValue(ctx.registers.r2) 115 116 print('\targ1: {:x}'.format(arg1)) 117 print('\targ2: {:x}'.format(arg2)) 118 print('\targ3: {:x}'.format(arg3)) 119 120 for i in range(0, arg3): 121 b = ctx.getConcreteMemoryValue(arg2 + i) 122 ctx.setConcreteMemoryValue(arg1 + i, b) 123 124 # Return value 125 return arg1 126 127 128# Simulate the memset() function 129def memsetHandler(ctx): 130 print('[+] memset hooked') 131 132 # Get arguments 133 arg1 = ctx.getConcreteRegisterValue(ctx.registers.r0) 134 arg2 = ctx.getConcreteRegisterValue(ctx.registers.r1) 135 arg3 = ctx.getConcreteRegisterValue(ctx.registers.r2) 136 137 print('\targ1: {:x}'.format(arg1)) 138 print('\targ2: {:x}'.format(arg2)) 139 print('\targ3: {:x}'.format(arg3)) 140 141 for i in range(0, arg3): 142 ctx.setConcreteMemoryValue(arg1 + i, arg2) 143 144 # Return value 145 return arg1 146 147 148# Simulate the strlen() function 149def strlenHandler(ctx): 150 print('[+] strlen hooked') 151 152 # Get arguments 153 arg1 = getMemoryString(ctx, ctx.getConcreteRegisterValue(ctx.registers.r0)) 154 155 print('\targ1: "{}" ({:08x})'.format(arg1, ctx.getConcreteRegisterValue(ctx.registers.r0))) 156 157 # Return value 158 return len(arg1) 159 160 161# Simulate the printf() function 162def printfHandler(ctx): 163 print('[+] printf hooked') 164 165 # Get arguments 166 arg1 = getFormatString(ctx, ctx.getConcreteRegisterValue(ctx.registers.r0)) 167 arg2 = ctx.getConcreteRegisterValue(ctx.registers.r1) 168 arg3 = ctx.getConcreteRegisterValue(ctx.registers.r2) 169 arg4 = ctx.getConcreteRegisterValue(ctx.registers.r3) 170 arg5 = ctx.getConcreteRegisterValue(ctx.registers.r4) 171 arg6 = ctx.getConcreteRegisterValue(ctx.registers.r5) 172 nbArgs = arg1.count("{") 173 args = [arg2, arg3, arg4, arg5, arg6][:nbArgs] 174 s = arg1.format(*args) 175 176 sys.stdout.write(s) 177 178 # Return value 179 return len(s) 180 181 182def libcInitHandler(ctx): 183 global STOP_ADDR 184 185 print('[+] __libc_init hooked') 186 187 # Get return address of __libc_init 188 STOP_ADDR = ctx.getConcreteRegisterValue(ctx.registers.r14) 189 190 print('[+] Set stop address to: {:x}'.format(STOP_ADDR)) 191 192 # Get address of main function. 193 main_addr = ctx.getConcreteRegisterValue(ctx.registers.r2) 194 195 debug('[+] Address of main function: {:x}'.format(main_addr)) 196 197 # Setup argc / argv 198 argvs = [ 199 bytes(TARGET.encode('utf-8')), # argv[0] 200 ] 201 202 # Define argc / argv 203 base = BASE_ARGV 204 addrs = list() 205 206 index = 0 207 for argv in argvs: 208 addrs.append(base) 209 ctx.setConcreteMemoryAreaValue(base, argv+b'\x00') 210 debug('[+] argv[%d] = %s' %(index, argv)) 211 base += len(argv)+1 212 index += 1 213 214 argc = len(argvs) 215 argv = base 216 for addr in addrs: 217 ctx.setConcreteMemoryValue(MemoryAccess(base, CPUSIZE.DWORD), addr) 218 base += CPUSIZE.DWORD 219 220 ctx.setConcreteRegisterValue(ctx.registers.r0, argc) 221 ctx.setConcreteRegisterValue(ctx.registers.r1, argv) 222 223 # Simulate call to main 224 debug('[+] Simulating call to main...') 225 ctx.setConcreteRegisterValue(ctx.registers.sp, ctx.getConcreteRegisterValue(ctx.registers.sp)-CPUSIZE.DWORD) 226 push_addr = MemoryAccess(ctx.getConcreteRegisterValue(ctx.registers.sp), CPUSIZE.DWORD) 227 ctx.setConcreteMemoryValue(push_addr, main_addr) 228 debug(' Pushing {:x} at {:x}'.format(main_addr, ctx.getConcreteRegisterValue(ctx.registers.sp))) 229 230 return None 231 232 233# Functions to emulate 234customRelocation = [ 235 ('__libc_init', libcInitHandler, BASE_PLT + 0 * 8), 236 ('strlen', strlenHandler, BASE_PLT + 1 * 8), 237 ('printf', printfHandler, BASE_PLT + 2 * 8), 238 ('memset', memsetHandler, BASE_PLT + 3 * 8), 239 ('memcpy', memcpyHandler, BASE_PLT + 4 * 8), 240 ('__aeabi_memclr', aeabi_memclrHandler, BASE_PLT + 5 * 8), 241 ('__aeabi_memcpy', aeabi_memcpyHandler, BASE_PLT + 6 * 8), 242] 243 244 245def hookingHandler(ctx): 246 pc = ctx.getConcreteRegisterValue(ctx.registers.pc) 247 for rel in customRelocation: 248 if rel[2] == pc: 249 # Simulate push {lr} 250 debug('[+] Simulating "push {lr}"') 251 ctx.setConcreteRegisterValue(ctx.registers.sp, ctx.getConcreteRegisterValue(ctx.registers.sp)-CPUSIZE.DWORD) 252 push_addr = MemoryAccess(ctx.getConcreteRegisterValue(ctx.registers.sp), CPUSIZE.DWORD) 253 ctx.setConcreteMemoryValue(push_addr, ctx.getConcreteRegisterValue(ctx.registers.r14)) 254 debug(' lr : {:x}'.format(ctx.getConcreteRegisterValue(ctx.registers.r14))) 255 256 # Emulate the routine and the return value 257 ret_value = rel[1](ctx) 258 if ret_value is not None: 259 ctx.setConcreteRegisterValue(ctx.registers.r0, ret_value) 260 261 # Simulate pop {lr} 262 debug('[+] Simulating "pop {pc}"') 263 pop_addr = MemoryAccess(ctx.getConcreteRegisterValue(ctx.registers.sp), CPUSIZE.DWORD) 264 pc = ctx.getConcreteMemoryValue(pop_addr) 265 ctx.setConcreteRegisterValue(ctx.registers.sp, ctx.getConcreteRegisterValue(ctx.registers.sp)+CPUSIZE.DWORD) 266 debug(" pc : {:x}".format(pc)) 267 268 # Update PC 269 ctx.setConcreteRegisterValue(ctx.registers.pc, pc) 270 return 271 272 273# Emulate the binary. 274def emulate(ctx, pc): 275 ctx.setConcreteRegisterValue(ctx.registers.pc, pc) 276 277 ret_value = None 278 count = 0 279 while pc and count < MAX_INSTRS: 280 if STOP_ADDR and pc == STOP_ADDR: 281 debug("[+] Emulation reached stop address...") 282 break 283 284 if count >= MAX_INSTRS: 285 print("[-] Emulation exceeded max number of instructions!") 286 break 287 288 if pc == 0xa68: 289 r0 = ctx.getConcreteRegisterValue(ctx.registers.r0) 290 debug("[+] Return value: {:#x}".format(r0)) 291 ret_value = r0 292 293 # Fetch opcodes 294 opcodes = ctx.getConcreteMemoryAreaValue(pc, 4) 295 296 # Create the Triton instruction 297 instruction = Instruction() 298 instruction.setOpcode(opcodes) 299 instruction.setAddress(pc) 300 301 # Process 302 if ctx.processing(instruction) == False: 303 opcodes_str = " ".join(["{:02x}".format(b) for b in bytearray(instruction.getOpcode())]) 304 debug('[-] Instruction not supported: %s\t%s' %(opcodes_str, str(instruction))) 305 break 306 307 opcodes_str = " ".join(["{:02x}".format(b) for b in bytearray(instruction.getOpcode())]) 308 309 debug('{}\t{}'.format(opcodes_str, instruction)) 310 311 # print_state(ctx) 312 313 # Inc the number of instructions executed 314 count += 1 315 316 # Simulate routines 317 hookingHandler(ctx) 318 319 # Next 320 pc = ctx.getConcreteRegisterValue(ctx.registers.pc) 321 322 debug('[+] Instruction executed: %d' %(count)) 323 324 if ret_value == None: 325 raise Exception("Invalid return code.") 326 327 return ret_value 328 329 330def loadBinary(ctx, binary): 331 # Map the binary into the memory 332 phdrs = binary.segments 333 for phdr in phdrs: 334 size = phdr.physical_size 335 vaddr = phdr.virtual_address 336 debug('[+] Loading 0x%06x - 0x%06x' %(vaddr, vaddr+size)) 337 ctx.setConcreteMemoryAreaValue(vaddr, phdr.content) 338 return 339 340 341def makeRelocation(ctx, binary): 342 # Perform our own relocations 343 try: 344 for rel in binary.pltgot_relocations: 345 symbolName = rel.symbol.name 346 symbolRelo = rel.address 347 for crel in customRelocation: 348 if symbolName == crel[0]: 349 debug('[+] Hooking %s' %(symbolName)) 350 debug(' {:x} : {:x}'.format(symbolRelo, crel[2])) 351 ctx.setConcreteMemoryValue(MemoryAccess(symbolRelo, CPUSIZE.DWORD), crel[2]) 352 except: 353 pass 354 355 # Perform our own relocations 356 try: 357 for rel in binary.dynamic_relocations: 358 symbolName = rel.symbol.name 359 symbolRelo = rel.address 360 for crel in customRelocation: 361 if symbolName == crel[0]: 362 debug('[+] Hooking %s' %(symbolName)) 363 debug(' {:x} : {:x}'.format(symbolRelo, crel[2])) 364 ctx.setConcreteMemoryValue(MemoryAccess(symbolRelo, CPUSIZE.DWORD), crel[2]) 365 except: 366 pass 367 return 368 369 370def run(ctx, binary): 371 # Concretize previous context 372 ctx.concretizeAllMemory() 373 ctx.concretizeAllRegister() 374 375 # Define a fake stack 376 ctx.setConcreteRegisterValue(ctx.registers.sp, BASE_STACK) 377 378 # Let's emulate the binary from the entry point 379 debug('[+] Starting emulation.') 380 emulate(ctx, binary.entrypoint) 381 debug('[+] Emulation done.') 382 return 383 384 385def main(): 386 # Get a Triton context 387 ctx = TritonContext() 388 389 # Set the architecture 390 ctx.setArchitecture(ARCH.ARM32) 391 392 # Parse the binary 393 binary = lief.parse(TARGET) 394 395 # Load the binary 396 loadBinary(ctx, binary) 397 398 # Perform our own relocations 399 makeRelocation(ctx, binary) 400 401 # Run emulation 402 return run(ctx, binary) 403 404 405if __name__ == '__main__': 406 retValue = main() 407 sys.exit(retValue) 408