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