Files
2024-04-04 14:00:40 +07:00

137 lines
4.6 KiB
Python

import base64
import json
import os
import shutil
import sqlite3
from datetime import datetime, timedelta
import win32crypt # pip install pypiwin32
from Crypto.Cipher import AES # pip install pycryptodome
# This is Windows version
# You may want to edit these values to match your OS
local_state_path = os.path.join(os.environ["USERPROFILE"], "AppData", "Local", "Google", "Chrome", "User Data",
"Local State")
cookies_path = os.path.join(os.environ["USERPROFILE"], "AppData", "Local", "Google", "Chrome", "User Data", "Default",
"Network", "Cookies")
def convert_to_unix_time(expires_utc):
"""
:param expires_utc: microseconds diff since January, 1601
:return: epoch time in seconds
"""
if expires_utc == 0:
return 0
# Define the start date as January 1, 1601
start_date = datetime(1601, 1, 1)
# Calculate the timedelta from the start date to the expires_utc date
expires_date = start_date + timedelta(microseconds=expires_utc)
# Define the Unix epoch start date as January 1, 1970
epoch_start_date = datetime(1970, 1, 1)
# Calculate the timedelta from the Unix epoch start date to the expires_utc date
unix_time = (expires_date - epoch_start_date).total_seconds()
return unix_time
def convert_samesite(samesite):
samesite_mapping = {
-1: "unspecified",
0: "no_restriction",
1: "lax",
2: "strict",
3: "none",
}
return samesite_mapping.get(samesite, "unspecified")
def get_encryption_key():
working_local_state = "Local State"
if not os.path.isfile(working_local_state):
shutil.copyfile(local_state_path, working_local_state)
with open(working_local_state, "r", encoding="utf-8") as f:
local_state = f.read()
local_state = json.loads(local_state)
# decode the encryption key from Base64
key = base64.b64decode(local_state["os_crypt"]["encrypted_key"])
# remove 'DPAPI' str
key = key[5:]
# return decrypted key that was originally encrypted
# using a session key derived from current user's logon credentials
# doc: http://timgolden.me.uk/pywin32-docs/win32crypt.html
return win32crypt.CryptUnprotectData(key, None, None, None, 0)[1]
def decrypt_data(data, key):
try:
# get the initialization vector
iv = data[3:15]
data = data[15:]
# generate cipher
cipher = AES.new(key, AES.MODE_GCM, iv)
# decrypt password
return cipher.decrypt(data)[:-16].decode()
except:
try:
return str(win32crypt.CryptUnprotectData(data, None, None, None, 0)[1])
except:
# not supported
return ""
def main():
# local sqlite Chrome cookie database path
# copy the file to current directory
# as the database will be locked if chrome is currently open
working_cookies = "Cookies"
if not os.path.isfile(working_cookies):
shutil.copyfile(cookies_path, working_cookies)
# connect to the database
db = sqlite3.connect(working_cookies)
# ignore decoding errors
db.text_factory = lambda b: b.decode(errors="ignore")
cursor = db.cursor()
# get the cookies from `cookies` table
cursor.execute("""
SELECT host_key, name, value, encrypted_value, path, expires_utc, is_secure, is_httponly, has_expires,
is_persistent, samesite FROM cookies""")
encryption_key = get_encryption_key()
cookies_list = []
for i, (host_key, name, value, encrypted_value, path, expires_utc, is_secure, is_httponly, has_expires,
is_persistent, samesite) in enumerate(cursor.fetchall()):
if not value:
decrypted_value = decrypt_data(encrypted_value, encryption_key)
else:
# already decrypted
decrypted_value = value
cookies_list.append({
"domain": host_key,
"expirationDate": convert_to_unix_time(expires_utc),
"hostOnly": False, # This information is not available in the SQLite database
"httpOnly": bool(is_httponly),
"name": name,
"path": path,
"sameSite": convert_samesite(samesite),
"secure": bool(is_secure),
"session": not bool(is_persistent),
"storeId": "0", # This information is not available in the SQLite database
"value": decrypted_value,
"id": i + 1
})
# commit changes
db.commit()
# close connection
db.close()
with open('cookies.json', 'w') as f:
json.dump(cookies_list, f, indent=4)
if __name__ == "__main__":
main()