import binascii
import struct
import re
from idaapi import *
from idautils import *
from idc import *


def manual_decrypt(offset):

    key = "3B7397BE28A1C5F978B59AB2051D03958DF21305135685E5460321F2A9E6606065A8F68BE82962667C5FEB26A23B607EA9220EA63E6F5F" \
      "043DF1ECACB21F18690000000000000000"
    key = binascii.unhexlify(key)

    with open('quackbot_unpacked.bin', 'rb') as f:
        encrypted_data = f.read()[0xac98:0xac98 + 14139]

    idx = offset
    while encrypted_data[idx] != key[idx & 0x3f]:
        idx += 1

    size = idx - offset
    res = ""

    for idx in range(size):
        res += chr(key[(offset + idx) & 0x3f] ^ encrypted_data[offset + idx])

    return res


def decrypt_memory(base_addr):

        while True:
            # print(type(base_addr))
            # print('base_addr: [%08x]' % base_addr + 4)

            temp_data = get_dword(base_addr)
            # print('%08x' % temp_data)
            base_addr += 4
            # offset = struct.unpack('I', temp_data)[0]
            offset = temp_data
            
            if not offset:
                break
            
            if offset >= 0x373A:
                print('offset_%08x' % offset)
                while True:
                    temp_data = get_dword(base_addr)
                    # offset = struct.unpack('I', temp_data)[0]
                    offset = temp_data
                    if offset >= 0x373A or not offset:
                        break
                    # MakeName(base_addr, manual_decrypt(offset))
                    set_cmt(base_addr, manual_decrypt(offset), 0)
                    print('[%08x]: %s' % (offset, manual_decrypt(offset)))
                    base_addr += 4

            if not offset:
                break


def main():
    while True:
        try:
            offset = int(input())
            print(manual_decrypt(offset))
        except:
            break


def is_hexdigits(value):
    import string
    return all(k in string.hexdigits for k in value)


def format_value(value):
    if not value:
        return None

    # print('value before formatting:', value)
    if value[-1] == 'h':
        value = value[:-1]
    if is_hexdigits(value):
        value = int(value, 0x10)
        # print('value after formatting:', value)
        return value
    else:
        return None


def get_reasonable_value(current_address):
    # check for push instruction first
    # if don't find push then go for mov and some reasonable value

    for _ in range(10):
        current_address = prev_head(current_address)
        disasm = GetDisasm(current_address)
        disasm = re.split('\s+', disasm)
        if disasm[0] == 'push':
            return format_value(disasm[1])
        elif disasm[0] == 'mov':
            value = format_value(disasm[2])
            if value is not None:
                return value
    else:
        return None


def decrypt_function_call(base_addr):

    for xref in XrefsTo(base_addr):
        offset = get_reasonable_value(xref.frm)
        # print(xref.frm, offset)
        if offset is not None:
            decrypted_string = manual_decrypt(offset)
            print('[%08x] %s' % (xref.frm, decrypted_string))
            set_cmt(xref.frm, decrypted_string, 0)


def decrypt_function_calls():
    print('----- Starting Comment -----')
    decryption_func_addresses = [0x004064F7, 0x0040658B]
    for addr in decryption_func_addresses:
        decrypt_function_call(addr)
    print('----- End Comment -----')


def decrypt_memory_addresses():
    ea = 0x004051E1

    # for xref in XrefsTo(ea):
    #     offset = prev_head(xref.frm)
    #     # value = get_operand_value(xref.frm, 0)
    #     # print(type(value))
    #     # # print(value)
    #     # break
    #     try:
    #         disasm = GetDisasm(offset)
    #         offset_addr = int(re.split('\s+', disasm)[-1][4:], 0x10)
    #         print('[%08x]' % offset_addr)
    #         decrypt_memory(offset_addr)
    #     except:
    #         pass

    decrypt_memory(0x00410178)


if __name__ == '__main__':
    decrypt_memory_addresses()
