#!/usr/bin/env python3
## -*- coding: utf-8 -*-
##
##  Jonathan Salwan - 2018-10-26
##
##  Description: Solution of the unbreakable challenge from the Google 2016 CTF.
##  In this solution, we fully emulate the binary and we solve each branch
##  to go through the good path.
##
##  Output:
##
##  $ time python3 ./solve.py
##  [+] Loading 0x400040 - 0x400200
##  [+] Loading 0x400200 - 0x40021c
##  [+] Loading 0x400000 - 0x403df4
##  [+] Loading 0x604000 - 0x604258
##  [+] Loading 0x604018 - 0x6041e8
##  [+] Loading 0x40021c - 0x400260
##  [+] Loading 0x403590 - 0x40378c
##  [+] Loading 0x000000 - 0x000000
##  [+] Hooking strncpy
##  [+] Hooking puts
##  [+] Hooking printf
##  [+] Hooking __libc_start_main
##  [+] Hooking exit
##  [+] Starting emulation.
##  [+] __libc_start_main hooked
##  [+] argv[0] = ./unbreakable-enterprise-product-activation
##  [+] argv[1] = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
##  [+] strncpy hooked
##  [+] puts hooked
##  Thank you - product activated!
##  [+] exit hooked
##  Flag: CTF{0The1Quick2Brown3Fox4Jumped5Over6The7Lazy8Fox9}
##  python3 solve.py  8.04s user 0.02s system 99% cpu 8.060 total
##

from __future__ import print_function
from triton     import *

import random
import string
import sys
import lief
import os

TARGET = os.path.join(os.path.dirname(__file__), 'unbreakable-enterprise-product-activation')
DEBUG  = True

# The debug function
def debug(s):
    if DEBUG: print(s)

# Memory mapping
BASE_PLT   = 0x10000000
BASE_ARGV  = 0x20000000
BASE_STACK = 0x9fffffff

# These instruction conditions must set zf to 1.
conditions = [
    0x402819,
    0x402859,
    0x4028A3,
    0x4028F3,
    0x402927,
    0x402969,
    0x4029A9,
    0x4029E0,
    0x402A1F,
    0x402A56,
    0x402A99,
    0x402AD9,
    0x402B07,
    0x402B37,
    0x402B79,
    0x402BA7,
    0x402BD7,
    0x402C22,
    0x402C69,
    0x402CA9,
    0x402CD7,
    0x402D22,
    0x402D73,
    0x402DB0,
    0x402DF9,
    0x402E43,
    0x402E89,
    0x402EC9,
    0x402EF7,
    0x402F30,
    0x402F79,
    0x402FB9,
    0x402FF9,
    0x403039,
    0x403079,
    0x4030C5,
    0x403109,
    0x403149,
    0x403189,
    0x4031B7,
    0x4031F9,
    0x403239,
    0x403270,
    0x4032B0,
    0x403302,
    0x403337,
    0x403379,
    0x4033B9,
    0x4033F0,
    0x403427,
    0x403472,
]


def getMemoryString(ctx, addr):
    s = str()
    index = 0

    while ctx.getConcreteMemoryValue(addr+index):
        c = chr(ctx.getConcreteMemoryValue(addr+index))
        if c not in string.printable: c = ""
        s += c
        index  += 1

    return s


def getFormatString(ctx, addr):
    return getMemoryString(ctx, addr)                                               \
           .replace("%s", "{}").replace("%d", "{:d}").replace("%#02x", "{:#02x}")   \
           .replace("%#x", "{:#x}").replace("%x", "{:x}").replace("%02X", "{:02x}") \
           .replace("%c", "{:c}").replace("%02x", "{:02x}").replace("%ld", "{:d}")  \