Last active
May 24, 2024 13:45
-
-
Save AltoRetrato/05f6957756931b12dc7ff8a87c2fe835 to your computer and use it in GitHub Desktop.
This Python script tests the CryptProtectData() Windows function. It might help test and solve issues that can affect many Windows programs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# crypt32_test.py | |
# | |
# 2024-05-24 - 1st version published as a gist at | |
# https://gist.github.com/AltoRetrato/05f6957756931b12dc7ff8a87c2fe835 | |
# TL;DR: This script helps you test the CryptProtectData() Windows function | |
# to possibly find and solve issues that can affect many programs. | |
# ---------------------------------------------------------------------------- | |
# Is Google Chrome pausing sync each time you (re)start the browser? | |
# | |
# Does Steam always ask for login even when selecting | |
# "Remember my password on this device"? | |
# | |
# I investigated these and other issues in Chromium Embedded Framework (CEF) | |
# apps on my PC by starting those apps with the command-line argument | |
# --enable-logging | |
# | |
# One thing they all had in common (Chrome, Steam, Evernote, ...) was an error | |
# similar to "Failed to encrypt: Access is denied. (0x5)" appearing in the log. | |
# | |
# After some debugging, I found that the Windows function CryptProtectData() | |
# was returning error code 5 (access denied) instead of 0 (no error) on my PC. | |
# So I wrote this script to reproduce the issue faster and test on other PCs, | |
# where it worked fine. I just converted this C++ code to Python: | |
# https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata#examples | |
# | |
# Using Process Monitor, I captured events while running the Python script | |
# and found that lsass.exe encountered an "ACCESS DENIED" error | |
# when trying to access %APPDATA%\Microsoft\Protect\CREDHIST. | |
# | |
# SOLUTION / CONCLUSION | |
# ===================== | |
# | |
# Though CREDHIST file permissions were correct, I noticed that the file | |
# was set to "read-only". Removing the "read-only" attribute solved the issue! | |
# You can check and change it from a command prompt using the "attrib" command, | |
# or by right-clicking the file in Windows Explorer and selecting "Properties". | |
# | |
# As for what changed the attribute, I have no idea. | |
# | |
# From a quick web search, this doesn't seem to be a common issue. | |
# Still, I think the process I used to find and fix the issue might help | |
# other users to find other issues as well. | |
# | |
# Some possibly related Chromium issues: | |
# Logout and Sync Pause Issue | |
# https://issues.chromium.org/issues/40281493 | |
# | |
# Chrome Sync is paused after closing and opening Chrome | |
# https://issues.chromium.org/issues/40936509 | |
# | |
# Google not saving my passwords, or prompting me to save pw even though I have the option selected | |
# https://issues.chromium.org/issues/337476657 | |
import ctypes | |
from ctypes import POINTER, byref, c_void_p, c_wchar_p, c_char_p, c_ulong, c_int, c_char | |
# Define required structures | |
class DATA_BLOB(ctypes.Structure): | |
_fields_ = [("cbData", c_ulong), | |
("pbData", POINTER(c_char))] | |
# Load CryptProtectData from Crypt32 | |
crypt32 = ctypes.WinDLL('Crypt32.dll') | |
CryptProtectData = crypt32.CryptProtectData | |
CryptProtectData.argtypes = [POINTER(DATA_BLOB), c_wchar_p, c_void_p, c_void_p, c_void_p, c_ulong, POINTER(DATA_BLOB)] | |
CryptProtectData.restype = c_int | |
# Initialize variables | |
pbDataInput = b"Hello world of data protection." | |
cbDataInput = len(pbDataInput) + 1 | |
# Initialize the DataIn structure | |
DataIn = DATA_BLOB() | |
DataIn.pbData = ctypes.cast(pbDataInput, POINTER(c_char)) | |
DataIn.cbData = cbDataInput | |
# Call CryptProtectData | |
DataOut = DATA_BLOB() | |
if CryptProtectData(byref(DataIn), None, None, None, None, 0, byref(DataOut)): | |
print("The encryption phase worked.") | |
# Free memory allocated by CryptProtectData | |
ctypes.windll.kernel32.LocalFree(DataOut.pbData) | |
else: | |
print("Encryption error using CryptProtectData.") | |
print(f"Error number {ctypes.GetLastError()}.") | |
input("[ENTER] to terminate.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment