Bunch of code to extract keys from WINE

This commit is contained in:
Florian Bach 2021-11-20 14:14:59 +01:00
parent ed9e6d534a
commit 4c3ee827f0
7 changed files with 870 additions and 0 deletions

View File

@ -10,6 +10,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install compilers
run: |
sudo apt update ; sudo apt install -y gcc-mingw-w64-i686 gcc-mingw-w64-x86-64
- name: Compile
run: |
./bundle_calibre_plugin.sh

View File

@ -7,6 +7,12 @@
[ ! -f calibre-plugin/pyasn1.zip ] && ./package_modules.sh
pushd calibre-plugin
pushd key-wine
# Compile:
make
popd
zip -r ../calibre-plugin.zip *

View File

@ -0,0 +1,14 @@
all: decrypt_win32.exe decrypt_win64.exe
.PHONY: clean
clean:
rm decrypt_win32.exe decrypt_win64.exe 2>/dev/null || /bin/true
decrypt_win32.exe: main.c Makefile
i686-w64-mingw32-gcc main.c -Os -o decrypt_win32.exe -lcrypt32
i686-w64-mingw32-strip decrypt_win32.exe
decrypt_win64.exe: main.c Makefile
x86_64-w64-mingw32-gcc main.c -Os -o decrypt_win64.exe -lcrypt32
x86_64-w64-mingw32-strip decrypt_win64.exe

View File

@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018 Anders Høst
#
# Licensed under MIT
# See https://github.com/flababah/cpuid.py/blob/master/cpuid.py
from __future__ import print_function
import platform
import os
import ctypes
from ctypes import c_uint32, c_int, c_long, c_ulong, c_size_t, c_void_p, POINTER, CFUNCTYPE
# Posix x86_64:
# Three first call registers : RDI, RSI, RDX
# Volatile registers : RAX, RCX, RDX, RSI, RDI, R8-11
# Windows x86_64:
# Three first call registers : RCX, RDX, R8
# Volatile registers : RAX, RCX, RDX, R8-11
# cdecl 32 bit:
# Three first call registers : Stack (%esp)
# Volatile registers : EAX, ECX, EDX
_POSIX_64_OPC = [
0x53, # push %rbx
0x89, 0xf0, # mov %esi,%eax
0x89, 0xd1, # mov %edx,%ecx
0x0f, 0xa2, # cpuid
0x89, 0x07, # mov %eax,(%rdi)
0x89, 0x5f, 0x04, # mov %ebx,0x4(%rdi)
0x89, 0x4f, 0x08, # mov %ecx,0x8(%rdi)
0x89, 0x57, 0x0c, # mov %edx,0xc(%rdi)
0x5b, # pop %rbx
0xc3 # retq
]
_WINDOWS_64_OPC = [
0x53, # push %rbx
0x89, 0xd0, # mov %edx,%eax
0x49, 0x89, 0xc9, # mov %rcx,%r9
0x44, 0x89, 0xc1, # mov %r8d,%ecx
0x0f, 0xa2, # cpuid
0x41, 0x89, 0x01, # mov %eax,(%r9)
0x41, 0x89, 0x59, 0x04, # mov %ebx,0x4(%r9)
0x41, 0x89, 0x49, 0x08, # mov %ecx,0x8(%r9)
0x41, 0x89, 0x51, 0x0c, # mov %edx,0xc(%r9)
0x5b, # pop %rbx
0xc3 # retq
]
_CDECL_32_OPC = [
0x53, # push %ebx
0x57, # push %edi
0x8b, 0x7c, 0x24, 0x0c, # mov 0xc(%esp),%edi
0x8b, 0x44, 0x24, 0x10, # mov 0x10(%esp),%eax
0x8b, 0x4c, 0x24, 0x14, # mov 0x14(%esp),%ecx
0x0f, 0xa2, # cpuid
0x89, 0x07, # mov %eax,(%edi)
0x89, 0x5f, 0x04, # mov %ebx,0x4(%edi)
0x89, 0x4f, 0x08, # mov %ecx,0x8(%edi)
0x89, 0x57, 0x0c, # mov %edx,0xc(%edi)
0x5f, # pop %edi
0x5b, # pop %ebx
0xc3 # ret
]
is_windows = os.name == "nt"
is_64bit = ctypes.sizeof(ctypes.c_voidp) == 8
class CPUID_struct(ctypes.Structure):
_fields_ = [(r, c_uint32) for r in ("eax", "ebx", "ecx", "edx")]
class CPUID(object):
def __init__(self):
if platform.machine() not in ("AMD64", "x86_64", "x86", "i686"):
raise SystemError("Only available for x86")
if is_windows:
if is_64bit:
# VirtualAlloc seems to fail under some weird
# circumstances when ctypes.windll.kernel32 is
# used under 64 bit Python. CDLL fixes this.
self.win = ctypes.CDLL("kernel32.dll")
opc = _WINDOWS_64_OPC
else:
# Here ctypes.windll.kernel32 is needed to get the
# right DLL. Otherwise it will fail when running
# 32 bit Python on 64 bit Windows.
self.win = ctypes.windll.kernel32
opc = _CDECL_32_OPC
else:
opc = _POSIX_64_OPC if is_64bit else _CDECL_32_OPC
size = len(opc)
code = (ctypes.c_ubyte * size)(*opc)
if is_windows:
self.win.VirtualAlloc.restype = c_void_p
self.win.VirtualAlloc.argtypes = [ctypes.c_void_p, ctypes.c_size_t, ctypes.c_ulong, ctypes.c_ulong]
self.addr = self.win.VirtualAlloc(None, size, 0x1000, 0x40)
if not self.addr:
raise MemoryError("Could not allocate RWX memory")
else:
self.libc = ctypes.cdll.LoadLibrary(None)
self.libc.valloc.restype = ctypes.c_void_p
self.libc.valloc.argtypes = [ctypes.c_size_t]
self.addr = self.libc.valloc(size)
if not self.addr:
raise MemoryError("Could not allocate memory")
self.libc.mprotect.restype = c_int
self.libc.mprotect.argtypes = [c_void_p, c_size_t, c_int]
ret = self.libc.mprotect(self.addr, size, 1 | 2 | 4)
if ret != 0:
raise OSError("Failed to set RWX")
ctypes.memmove(self.addr, code, size)
func_type = CFUNCTYPE(None, POINTER(CPUID_struct), c_uint32, c_uint32)
self.func_ptr = func_type(self.addr)
def __call__(self, eax, ecx=0):
struct = CPUID_struct()
self.func_ptr(struct, eax, ecx)
return struct.eax, struct.ebx, struct.ecx, struct.edx
def __del__(self):
if is_windows:
self.win.VirtualFree.restype = c_long
self.win.VirtualFree.argtypes = [c_void_p, c_size_t, c_ulong]
self.win.VirtualFree(self.addr, 0, 0x8000)
elif self.libc:
# Seems to throw exception when the program ends and
# libc is cleaned up before the object?
self.libc.free.restype = None
self.libc.free.argtypes = [c_void_p]
self.libc.free(self.addr)
if __name__ == "__main__":
def valid_inputs():
cpuid = CPUID()
for eax in (0x0, 0x80000000):
highest, _, _, _ = cpuid(eax)
while eax <= highest:
regs = cpuid(eax)
yield (eax, regs)
eax += 1
print(" ".join(x.ljust(8) for x in ("CPUID", "A", "B", "C", "D")).strip())
for eax, regs in valid_inputs():
print("%08x" % eax, " ".join("%08x" % reg for reg in regs))

View File

@ -0,0 +1,326 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def unfuck(user):
# Wine uses a pretty nonstandard encoding in their registry file.
# I haven't found any existing Python implementation for that,
# so I looked at the C code and wrote my own.
# This implementation doesn't support multi-byte UTF-8 chars,
# but a standard-conforming Wine registry won't contain
# these anyways, so who cares.
hex_char_list = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 97, 98, 99, 100, 101, 102]
# Remove the quotation marks at beginning and end:
user = user.strip()[1:-1]
user_new = bytearray()
i = 0
while i < len(user):
# Convert string of len 1 to a byte
char = user[i][0].encode("latin-1")[0]
if char == ord('\\'):
# Get next char:
i += 1
char = user[i][0].encode("latin-1")[0]
if (char == ord('a')):
user_new.append(0x07)
elif (char == ord('b')):
user_new.append(0x08)
elif (char == ord('e')):
user_new.append(0x1b)
elif (char == ord('f')):
user_new.append(0x0c)
elif (char == ord('n')):
user_new.append(0x0a)
elif (char == ord('r')):
user_new.append(0x0d)
elif (char == ord('t')):
user_new.append(0x09)
elif (char == ord('v')):
user_new.append(0x0b)
elif (char == ord('x')):
# Get next char
i += 1
char = user[i][0].encode("latin-1")[0]
if char not in hex_char_list:
user_new.append(ord('x'))
# This seems to be fallback code.
# Probably a bug in wine: We should also add "char" - but Wine doesn't either ...
else:
ival = "" + chr(char)
# Read up to 3 more chars
next = user[i + 1][0].encode("latin-1")[0]
if next in hex_char_list:
ival += chr(next)
i += 1
next = user[i + 1][0].encode("latin-1")[0]
if next in hex_char_list:
ival += chr(next)
i += 1
next = user[i + 1][0].encode("latin-1")[0]
if next in hex_char_list:
ival += chr(next)
i += 1
# ival now contains "00e9". Convert to int ...
ival = int(ival, 16)
# then drop everything except the lowest byte
ival = ival & 0xFF
# then add it to the array
user_new.append(ival)
elif (char >= ord('0') and char <= ord('9') ):
octal = char - ord('0')
# Read up to 2 more chars
next = user[i + 1][0].encode("latin-1")[0]
if next >= ord('0') and next <= ord('9'):
octal = (octal * 8) + (next - ord('0'))
i += 1
next = user[i + 1][0].encode("latin-1")[0]
if next >= ord('0') and next <= ord('9'):
octal = (octal * 8) + (next - ord('0'))
i += 1
else:
if (char < 0x80):
user_new.append(char)
else:
print("Multi-Byte UTF-8, not supported")
print("This should never happen in a standard-conform Wine registry ...")
return False
# Parse next char
i += 1
return user_new
def GetMasterKey(path_to_wine_prefix):
import os
if os.name == 'nt':
print("Hey! This is for Linux!")
return
import cpuid
import struct
try:
# Linux / Wine code just assumes that the system drive is C:\
serial_file = open(os.path.join(path_to_wine_prefix, "drive_c", ".windows-serial"), "r")
serial = serial_file.read()
serial_file.close()
serial = int(serial, 16)
except:
# If this file is not present, Wine will use a default serial number of "0".
serial = 0
print("Serial: " + str(serial))
cpu = cpuid.CPUID()
_, b, c, d = cpu(0)
vendor = struct.pack("III", b, d, c)
print("Vendor: " + vendor.decode("utf-8"))
signature, _, _, _ = cpu(1)
signature = struct.pack('>I', signature)[1:]
print("Signature: " + str(signature.hex()))
# Search for the username in the registry:
user = None
# Linux - loop through the Wine registry file to find the "username" attribute
try:
registry_file = open(os.path.join(path_to_wine_prefix, "user.reg"))
waiting_for_username = False
while True:
line = registry_file.readline()
if not line:
break
if waiting_for_username:
if (not line.lower().startswith("\"username\"=")):
continue
# If we end up here, we have the username.
user = line.split('=', 1)[1].strip()
user = unfuck(user)
break
else:
if (line.startswith("[Software\\\\Adobe\\\\Adept\\\\Device]")):
waiting_for_username = True
if (line.startswith("[Volatile Environment]")):
waiting_for_username = True
registry_file.close()
except:
# There was an error hunting through the registry.
raise
pass
if (user is None):
print("Error while determining username ...")
exit()
print("Username: " + str(user))
# Find the value we want to decrypt from the registry. loop through the Wine registry file to find the "key" attribute
try:
registry_file = open(os.path.join(path_to_wine_prefix, "user.reg"))
waiting_for_key = False
key_line = None
while True:
line = registry_file.readline()
if not line:
break
if waiting_for_key:
if (not line.lower().startswith("\"key\"=")):
continue
# If we end up here, we have the key.
key_line = line
while (key_line.strip().endswith('\\')):
key_line = key_line.strip()[:-1] + registry_file.readline()
# Now parse ...
key_line = key_line.split(':', 1)[1]
key_line = key_line.replace('\t', '').replace(' ', '').replace(',', '')
key_line = bytes.fromhex(key_line)
else:
if (line.startswith("[Software\\\\Adobe\\\\Adept\\\\Device]")):
waiting_for_key = True
registry_file.close()
except:
# There was an error hunting through the registry.
raise
pass
print("Encrypted key: " + str(key_line))
import hexdump
hexdump.hexdump(key_line)
# These should all be "bytes" or "bytearray"
#print(type(vendor))
#print(type(signature))
#print(type(user))
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
print("Entropy: " + str(entropy))
# We would now call CryptUnprotectData to decrypt the stuff,
# but unfortunately there's no working Linux implementation
# for that. This means we have to call a Windows binary through
# Wine just for this one single decryption call ...
success, data = CryptUnprotectDataExecuteWine(path_to_wine_prefix, key_line, entropy)
if (success):
keykey = data
print(keykey)
return keykey
else:
print("Error number: " + str(data))
if data == 13: # WINError ERROR_INVALID_DATA
print("Could not decrypt data with the given key. Did the Wine username change?")
return None
def CryptUnprotectDataExecuteWine(wineprefix, data, entropy):
import subprocess, os, re
print("Asking WINE to decrypt encrypted key for us ...")
if wineprefix == "" or not os.path.exists(wineprefix):
print("Wineprefix not found!!")
return None
# Default to win32 binary, unless we find arch in registry
winearch = "win32"
try:
system_registry_path = os.path.join(wineprefix, "system.reg")
regfile = open(system_registry_path, "r")
while True:
line = regfile.readline()
if not line:
break
stuff = re.match(r'#arch=(win32|win64)', line)
if (stuff):
winearch = stuff.groups()[0]
break
regfile.close()
except:
pass
# Execute!
env_dict = os.environ
env_dict["PYTHONPATH"] = ""
env_dict["WINEPREFIX"] = wineprefix
env_dict["WINEDEBUG"] = "-all,+crypt"
import base64
env_dict["X_DECRYPT_DATA"] = data.hex()
env_dict["X_DECRYPT_ENTROPY"] = entropy.hex()
try:
from calibre.utils.config import config_dir
pluginsdir = os.path.join(config_dir,"plugins")
maindir = os.path.join(pluginsdir,"DeACSM")
moddir = os.path.join(maindir,"modules")
except:
import os
moddir = os.path.dirname(os.path.abspath(__file__))
proc = subprocess.Popen(["wine", "decrypt_" + winearch + ".exe" ], shell=False, cwd=moddir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
prog_output, prog_stderr = proc.communicate()
# calls decrypt_win32.exe or decrypt_win64.exe
if prog_output.decode("utf-8").startswith("PROGOUTPUT:0:"):
key_string = prog_output.decode("utf-8").split(':')[2]
print("Successfully got encryption key from WINE: " + key_string)
master_key = bytes.fromhex(key_string)
return True, master_key
else:
print("Huh. That didn't work. ")
err = int(prog_output.decode("utf-8").split(':')[1])
if err == -4:
err = int(prog_output.decode("utf-8").split(':')[2])
print("Program reported: " + prog_output.decode("utf-8"))
print("Debug log: ")
print(prog_stderr.decode("utf-8"))
return False, err
GetMasterKey()

View File

@ -0,0 +1,190 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Most of the code in this file has been taken from adobekey.pyw written by i♥cabbages
# adobekey.pyw, version 7.0
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
try:
from ctypes import windll
except ImportError:
import os
if os.name != 'nt':
print("This script is for Windows!")
exit()
else:
raise
def GetSystemDirectory():
from ctypes import windll, c_wchar_p, c_uint, create_unicode_buffer
MAX_PATH = 255
kernel32 = windll.kernel32
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
from ctypes import windll, c_wchar_p, c_uint, POINTER, byref
kernel32 = windll.kernel32
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(
path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserNameWINAPI():
from ctypes import windll, c_wchar_p, c_uint, POINTER, byref, create_unicode_buffer
advapi32 = windll.advapi32
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
# Yes, it's actually implemented like that. Encode in UTF16 but only take the lowest byte of each character.
return buffer.value.encode('utf-16-le')[::2]
def GetUserNameREG():
try:
import winreg
except ImportError:
import _winreg as winreg
try:
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH)
# Yes, it's actually implemented like that. Encode in UTF16 but only take the lowest byte of each character.
userREG = winreg.QueryValueEx(regkey, 'username')[0].encode('utf-16-le')[::2]
return userREG
except:
return None
from ctypes import Structure, c_uint, c_void_p, POINTER
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def CryptUnprotectData(indata, entropy):
from ctypes import windll, c_wchar_p, c_uint, byref, cast, create_string_buffer, string_at
crypt32 = windll.crypt32
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
return None
return string_at(outdata.pbData, outdata.cbData)
def GetMasterKey():
import os
if os.name != 'nt':
print("This script is for Windows!")
# Get serial number of root drive
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
print("Serial: " + str(serial))
# Get CPU vendor:
import cpuid, struct
cpu = cpuid.CPUID()
_, b, c, d = cpu(0)
vendor = struct.pack("III", b, d, c)
print("Vendor: " + vendor.decode("utf-8"))
signature, _, _, _ = cpu(1)
signature = struct.pack('>I', signature)[1:]
print("Signature: " + str(signature.hex()))
# Search for the username in the registry:
user = None
user_from_registry = GetUserNameREG()
current_user_name = GetUserNameWINAPI()
if (user_from_registry is not None):
# Found entry
user = user_from_registry
else:
user = current_user_name
if (user_from_registry is not None and user_from_registry != current_user_name):
print("Username: {0}/{1} mismatch, using {0}".format(str(user_from_registry), str(current_user_name)))
elif (user_from_registry is not None):
print("Username: {0} (Registry)".format(str(user_from_registry)))
else:
print("Username: {0} (WinAPI)".format(str(current_user_name)))
# Find the value we want to decrypt from the registry:
try:
import winreg
except ImportError:
import _winreg as winreg
try:
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
regkey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, DEVICE_KEY_PATH)
device = winreg.QueryValueEx(regkey, 'key')[0]
except:
print("Can't find encrypted device key.")
print("Encrypted key: " + str(device))
# These three must all be bytes.
#print(type(vendor))
#print(type(signature))
#print(type(user))
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
print("Entropy: " + str(entropy))
keykey = CryptUnprotectData(device, entropy)
if (keykey is None):
print("Couldn't decrypt key!")
return None
print("Decrypted key: " + str(keykey))
return keykey
GetMasterKey()

View File

@ -0,0 +1,173 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <wincrypt.h>
#include <dpapi.h>
#ifdef DEBUG
#undef DEBUG
#endif
int char2int(char input) {
if (input >= '0' && input <= '9')
return input - '0';
if (input >= 'A' && input <= 'F')
return input - 'A' + 10;
if (input >= 'a' && input <= 'f')
return input - 'a' + 10;
printf("PROGOUTPUT:-3");
exit(-3);
}
void hex2bin(const char * src, char * dst) {
while (*src && src[1]) {
*(dst++) = char2int(*src) * 16 + char2int(src[1]);
src += 2;
}
}
#ifdef DEBUG
void hexDump (
const char * desc,
const void * addr,
const int len,
int perLine
) {
// Silently ignore silly per-line values.
if (perLine < 4 || perLine > 64) perLine = 16;
int i;
unsigned char buff[perLine+1];
const unsigned char * pc = (const unsigned char *)addr;
// Output description if given.
if (desc != NULL) printf ("%s:\n", desc);
// Length checks.
if (len == 0) {
fprintf(stderr, " ZERO LENGTH\n");
return;
}
if (len < 0) {
fprintf(stderr, " NEGATIVE LENGTH: %d\n", len);
return;
}
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of perLine means new or first line (with line offset).
if ((i % perLine) == 0) {
// Only print previous-line ASCII buffer for lines beyond first.
if (i != 0) printf (" %s\n", buff);
// Output the offset of current line.
fprintf (stderr, " %04x ", i);
}
// Now the hex code for the specific character.
fprintf (stderr, " %02x", pc[i]);
// And buffer a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) // isprint() may be better.
buff[i % perLine] = '.';
else
buff[i % perLine] = pc[i];
buff[(i % perLine) + 1] = '\0';
}
// Pad out last line if not exactly perLine characters.
while ((i % perLine) != 0) {
fprintf (stderr, " ");
i++;
}
// And print the final ASCII buffer.
fprintf (stderr, " %s\n", buff);
}
#endif
int main() {
char * var_data = "X_DECRYPT_DATA";
char * var_entropy = "X_DECRYPT_ENTROPY";
char * data_hex = getenv(var_data);
char * entropy_hex = getenv(var_entropy);
if (data_hex == NULL || entropy_hex == NULL) {
printf("PROGOUTPUT:-1");
exit(-1);
}
char * data_bytes = malloc((strlen(data_hex) / 2));
char * entropy_bytes = malloc((strlen(entropy_hex) / 2));
if (data_bytes == NULL || entropy_bytes == NULL) {
printf("PROGOUTPUT:-2");
exit(-2);
}
hex2bin(data_hex, data_bytes);
hex2bin(entropy_hex, entropy_bytes);
#ifdef DEBUG
hexDump("data", data_bytes, strlen(data_hex)/2, 16);
hexDump("entropy", entropy_bytes, strlen(entropy_hex)/2, 16);
#endif
DATA_BLOB input_data;
DATA_BLOB entropy_data;
DATA_BLOB output_data;
input_data.pbData = data_bytes;
input_data.cbData = strlen(data_hex)/2;
entropy_data.pbData = entropy_bytes;
entropy_data.cbData = strlen(entropy_hex)/2;
int ret = CryptUnprotectData(
&input_data,
NULL,
&entropy_data,
NULL,
NULL,
0,
&output_data);
if (ret) {
if (output_data.cbData != 16) {
printf("PROGOUTPUT:-5:%d", output_data.cbData);
exit(-5);
}
// Success! Return decrypted data
printf("PROGOUTPUT:0:");
for (int i = 0; i < 16; i++) {
printf("%02x", output_data.pbData[i]);
}
exit(0);
}
else {
printf("PROGOUTPUT:-4:%d", GetLastError());
exit(-4);
}
}