Files
safetynet/apps/inspector/dpapi.py
T

39 lines
1.9 KiB
Python

"""Current-user Windows capability protection; no browser profile crypto state."""
import base64
import ctypes
import sys
class Blob(ctypes.Structure):
_fields_=[('size',ctypes.c_uint32),('data',ctypes.POINTER(ctypes.c_ubyte))]
def convert(data,protect):
crypt=ctypes.WinDLL('crypt32',use_last_error=True)
kernel=ctypes.WinDLL('kernel32',use_last_error=True)
allocation=(ctypes.c_ubyte*len(data)).from_buffer_copy(data)
source=Blob(len(data),allocation);result=Blob()
function=crypt.CryptProtectData if protect else crypt.CryptUnprotectData
function.argtypes=[ctypes.POINTER(Blob),ctypes.c_void_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.c_uint32,ctypes.POINTER(Blob)]
function.restype=ctypes.c_int
# UI_FORBIDDEN; omit LOCAL_MACHINE so only the signed-in Windows user can decrypt.
if not function(ctypes.byref(source),None,None,None,None,1,ctypes.byref(result)):
raise ValueError('DPAPI rejected capability')
kernel.LocalFree.argtypes=[ctypes.c_void_p];kernel.LocalFree.restype=ctypes.c_void_p
try:return ctypes.string_at(result.data,result.size)
finally:kernel.LocalFree(result.data)
if __name__=='__main__':
try:
if sys.platform!='win32' or len(sys.argv)!=2 or sys.argv[1] not in ['protect','unprotect']: raise ValueError()
limit=32768 if sys.argv[1]=='protect' else 65536
data=sys.stdin.buffer.read(limit+1)
if not data or len(data)>limit:raise ValueError()
if sys.argv[1]=='protect':output=base64.b64encode(convert(data,True))
else:
decoded=base64.b64decode(data,validate=True)
if base64.b64encode(decoded)!=data:raise ValueError()
output=convert(decoded,False)
if len(output)>(65536 if sys.argv[1]=='protect' else 32768):raise ValueError()
sys.stdout.buffer.write(output)
except Exception:
sys.stderr.write('Windows capability protection failed.\n');sys.exit(1)