mirror of
https://github.com/Leseratte10/acsm-calibre-plugin.git
synced 2024-12-23 01:34:36 +06:00
Move all win32 logic to wine executable
Actions like reading the registry and serial number were being done in python even though the final decryption was done in wine. This commit moves all windows logic except architecture detection into the exe ran under wine to simplify the architecture.
This commit is contained in:
parent
61a03fe988
commit
0cb13b9d38
@ -5,307 +5,7 @@
|
|||||||
|
|
||||||
import sys, binascii
|
import sys, binascii
|
||||||
|
|
||||||
def unfuck(user):
|
def GetMasterKey(wineprefix):
|
||||||
# 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 sys.version_info[0] == 2:
|
|
||||||
char = ord(char)
|
|
||||||
|
|
||||||
if char == ord('\\'):
|
|
||||||
# Get next char:
|
|
||||||
i += 1
|
|
||||||
char = user[i][0].encode("latin-1")[0]
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
char = ord(char)
|
|
||||||
|
|
||||||
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 sys.version_info[0] == 2:
|
|
||||||
char = ord(char)
|
|
||||||
if char not in hex_char_list:
|
|
||||||
user_new.append(ord('x'))
|
|
||||||
# This seems to be fallback code.
|
|
||||||
# Subtract 1 so the next char (the one that's not a hex char)
|
|
||||||
# is handled normally.
|
|
||||||
i -= 1
|
|
||||||
else:
|
|
||||||
ival = "" + chr(char)
|
|
||||||
|
|
||||||
# Read up to 3 more chars
|
|
||||||
next = user[i + 1][0].encode("latin-1")[0]
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
next = ord(next)
|
|
||||||
|
|
||||||
if next in hex_char_list:
|
|
||||||
ival += chr(next)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
next = user[i + 1][0].encode("latin-1")[0]
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
next = ord(next)
|
|
||||||
|
|
||||||
if next in hex_char_list:
|
|
||||||
ival += chr(next)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
next = user[i + 1][0].encode("latin-1")[0]
|
|
||||||
if sys.version_info[0] == 2:
|
|
||||||
next = ord(next)
|
|
||||||
|
|
||||||
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 sys.version_info[0] == 2:
|
|
||||||
next = ord(next)
|
|
||||||
|
|
||||||
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 sys.version_info[0] == 2:
|
|
||||||
next = ord(next)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
verbose_logging = False
|
|
||||||
try:
|
|
||||||
import calibre_plugins.deacsm.prefs as prefs
|
|
||||||
deacsmprefs = prefs.DeACSM_Prefs()
|
|
||||||
verbose_logging = deacsmprefs["detailed_logging"]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
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 usually use a default serial number of "0".
|
|
||||||
# There are some edge cases where Wine uses a different serial number even when that
|
|
||||||
# .windows-serial file is not present.
|
|
||||||
serial = 0
|
|
||||||
|
|
||||||
if (verbose_logging):
|
|
||||||
print("Serial: {}".format(serial))
|
|
||||||
|
|
||||||
cpu = cpuid.CPUID()
|
|
||||||
_, b, c, d = cpu(0)
|
|
||||||
vendor = struct.pack("III", b, d, c)
|
|
||||||
|
|
||||||
if (verbose_logging):
|
|
||||||
print("Vendor: {}".format(vendor))
|
|
||||||
|
|
||||||
signature, _, _, _ = cpu(1)
|
|
||||||
signature = struct.pack('>I', signature)[1:]
|
|
||||||
|
|
||||||
if (verbose_logging):
|
|
||||||
print("Signature: {}".format(binascii.hexlify(signature)))
|
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
|
||||||
# Comes as bytearray
|
|
||||||
if sys.version_info[0] == 3:
|
|
||||||
user = bytes(user)
|
|
||||||
else:
|
|
||||||
user = str(user)
|
|
||||||
|
|
||||||
if verbose_logging:
|
|
||||||
print("Username: {}".format(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('\r', '').replace('\n', '').replace(' ', '').replace(',', '')
|
|
||||||
key_line = binascii.unhexlify(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
|
|
||||||
|
|
||||||
if key_line is None:
|
|
||||||
print("No ADE activation found ...")
|
|
||||||
return None
|
|
||||||
|
|
||||||
if verbose_logging:
|
|
||||||
print("Encrypted key: {}".format(binascii.hexlify(key_line)))
|
|
||||||
|
|
||||||
# These should all be "bytes" (Py3) or "str" (Py2)
|
|
||||||
# print(type(vendor))
|
|
||||||
# print(type(signature))
|
|
||||||
# print(type(user))
|
|
||||||
|
|
||||||
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
|
|
||||||
|
|
||||||
if verbose_logging:
|
|
||||||
print("Entropy: {}".format(binascii.hexlify(entropy)))
|
|
||||||
|
|
||||||
# We would now call CryptUnprotectData to decrypt the stuff,
|
|
||||||
# but unfortunately there's no working Linux implementation
|
|
||||||
# for that.
|
|
||||||
#
|
|
||||||
# The plan was to handle everything in Python so we don't have
|
|
||||||
# to interact with Wine - that's why we're doing all the registry
|
|
||||||
# handling ourselves.
|
|
||||||
# Unfortunately, that doesn't work for the actual decryption.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
if verbose_logging:
|
|
||||||
print("Key: {}".format(binascii.hexlify(keykey)))
|
|
||||||
return keykey
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Error number: {}".format(data))
|
|
||||||
if data == 13: # WINError ERROR_INVALID_DATA
|
|
||||||
print("Could not decrypt data with the given key. Did the entropy change?")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def CryptUnprotectDataExecuteWine(wineprefix, data, entropy):
|
|
||||||
import subprocess, os, re
|
import subprocess, os, re
|
||||||
|
|
||||||
verbose_logging = False
|
verbose_logging = False
|
||||||
@ -319,7 +19,7 @@ def CryptUnprotectDataExecuteWine(wineprefix, data, entropy):
|
|||||||
print("Asking WINE to decrypt encrypted key for us ...")
|
print("Asking WINE to decrypt encrypted key for us ...")
|
||||||
|
|
||||||
if wineprefix == "" or not os.path.exists(wineprefix):
|
if wineprefix == "" or not os.path.exists(wineprefix):
|
||||||
print("Wineprefix not found!!")
|
print("Wineprefix not found!")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -334,27 +34,21 @@ def CryptUnprotectDataExecuteWine(wineprefix, data, entropy):
|
|||||||
if not line:
|
if not line:
|
||||||
break
|
break
|
||||||
|
|
||||||
stuff = re.match(r'#arch=(win32|win64)', line)
|
archkey = re.match(r'#arch=(win32|win64)', line)
|
||||||
if (stuff):
|
if (archkey):
|
||||||
winearch = stuff.groups()[0]
|
winearch = archkey.groups()[0]
|
||||||
break
|
break
|
||||||
regfile.close()
|
regfile.close()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Execute!
|
|
||||||
|
|
||||||
env_dict = os.environ
|
env_dict = os.environ
|
||||||
env_dict["PYTHONPATH"] = ""
|
env_dict["PYTHONPATH"] = ""
|
||||||
env_dict["WINEPREFIX"] = wineprefix
|
env_dict["WINEPREFIX"] = wineprefix
|
||||||
#env_dict["WINEDEBUG"] = "-all,+crypt"
|
#env_dict["WINEDEBUG"] = "-all,+crypt"
|
||||||
env_dict["WINEDEBUG"] = "+err,+fixme"
|
env_dict["WINEDEBUG"] = "+err,+fixme"
|
||||||
|
|
||||||
# Use environment variables to get the input data to the application.
|
|
||||||
env_dict["X_DECRYPT_DATA"] = binascii.hexlify(data).decode("utf-8")
|
|
||||||
env_dict["X_DECRYPT_ENTROPY"] = binascii.hexlify(entropy).decode("utf-8")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from calibre.utils.config import config_dir
|
from calibre.utils.config import config_dir
|
||||||
pluginsdir = os.path.join(config_dir,"plugins")
|
pluginsdir = os.path.join(config_dir,"plugins")
|
||||||
@ -366,37 +60,26 @@ def CryptUnprotectDataExecuteWine(wineprefix, data, entropy):
|
|||||||
|
|
||||||
# calls decrypt_win32.exe or decrypt_win64.exe
|
# calls decrypt_win32.exe or decrypt_win64.exe
|
||||||
proc = subprocess.Popen(["wine", "decrypt_" + winearch + ".exe"], shell=False, cwd=moddir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
proc = subprocess.Popen(["wine", "decrypt_" + winearch + ".exe"], shell=False, cwd=moddir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
prog_output, prog_stderr = proc.communicate()
|
prog_stdout, prog_stderr = proc.communicate()
|
||||||
stdout = prog_output.decode("utf-8")
|
|
||||||
stderr = prog_stderr.decode("utf-8")
|
|
||||||
|
|
||||||
if stdout.startswith("PROGOUTPUT:0:"):
|
|
||||||
key_string = stdout.split(':')[2]
|
|
||||||
if verbose_logging:
|
if verbose_logging:
|
||||||
print("Successfully got encryption key from WINE: {}".format(key_string))
|
print("Stderr log:\n{}".format(prog_stderr.decode("utf-8")))
|
||||||
|
print("Stdout log: {}".format(prog_stdout.decode("utf-8")))
|
||||||
|
print("Exit code: {}".format(proc.returncode))
|
||||||
|
|
||||||
|
if proc.returncode == 0:
|
||||||
|
if verbose_logging:
|
||||||
|
print("Successfully got encryption key from WINE: {}".format(prog_stdout.decode("utf-8")))
|
||||||
else:
|
else:
|
||||||
print("Successfully got encryption key from WINE.")
|
print("Successfully got encryption key from WINE.")
|
||||||
master_key = binascii.unhexlify(key_string)
|
master_key = binascii.unhexlify(prog_stdout)
|
||||||
return True, master_key
|
return master_key
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("Huh. That didn't work. ")
|
print("Failed to extract encryption key from WINE.")
|
||||||
try:
|
print("Exit code: {}".format(proc.returncode))
|
||||||
err = int(stdout.split(':')[1])
|
|
||||||
if err == -4:
|
|
||||||
err = int(stdout.split(':')[2])
|
|
||||||
new_serial = int(stdout.split(':')[3])
|
|
||||||
if verbose_logging:
|
|
||||||
print("New serial: {}".format(new_serial))
|
|
||||||
except:
|
|
||||||
err = None
|
|
||||||
|
|
||||||
if verbose_logging:
|
|
||||||
# print("Stderr log:\n{}".format(stderr))
|
|
||||||
print("Program output: {}".format(stdout))
|
|
||||||
|
|
||||||
return False, err
|
return None
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -5,10 +5,10 @@ clean:
|
|||||||
rm decrypt_win32.exe decrypt_win64.exe 2>/dev/null || /bin/true
|
rm decrypt_win32.exe decrypt_win64.exe 2>/dev/null || /bin/true
|
||||||
|
|
||||||
decrypt_win32.exe: main.c Makefile
|
decrypt_win32.exe: main.c Makefile
|
||||||
i686-w64-mingw32-gcc main.c -Os -o decrypt_win32.exe -lcrypt32
|
i686-w64-mingw32-gcc main.c -Os -o decrypt_win32.exe -lcrypt32 -lwsock32
|
||||||
i686-w64-mingw32-strip decrypt_win32.exe
|
i686-w64-mingw32-strip decrypt_win32.exe
|
||||||
|
|
||||||
decrypt_win64.exe: main.c Makefile
|
decrypt_win64.exe: main.c Makefile
|
||||||
x86_64-w64-mingw32-gcc main.c -Os -o decrypt_win64.exe -lcrypt32
|
x86_64-w64-mingw32-gcc main.c -Os -o decrypt_win64.exe -lcrypt32 -lwsock32
|
||||||
x86_64-w64-mingw32-strip decrypt_win64.exe
|
x86_64-w64-mingw32-strip decrypt_win64.exe
|
||||||
|
|
||||||
|
@ -1,196 +1,112 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <cpuid.h>
|
||||||
#include <string.h>
|
#include <intsafe.h>
|
||||||
#include <windows.h>
|
|
||||||
#include <wincrypt.h>
|
|
||||||
#include <dpapi.h>
|
|
||||||
#include <fileapi.h>
|
|
||||||
#include <direct.h>
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
union CPUIDVendor {
|
||||||
#undef DEBUG
|
unsigned int reg[3];
|
||||||
#endif
|
char vendor[13];
|
||||||
|
};
|
||||||
|
|
||||||
int char2int(char input) {
|
struct EncEntropy {
|
||||||
if (input >= '0' && input <= '9')
|
unsigned int serial;
|
||||||
return input - '0';
|
char vendor[12];
|
||||||
if (input >= 'A' && input <= 'F')
|
char signature[3];
|
||||||
return input - 'A' + 10;
|
char user[13];
|
||||||
if (input >= 'a' && input <= 'f')
|
};
|
||||||
return input - 'a' + 10;
|
|
||||||
|
|
||||||
fputs("PROGOUTPUT:-3", stdout);
|
|
||||||
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) fprintf (stderr, "%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) fprintf (stderr, " %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 get_serial() {
|
|
||||||
DWORD serial = 0;
|
|
||||||
int retval = GetVolumeInformation("c:\\\\", NULL, 0, &serial, NULL, NULL, NULL, 0);
|
|
||||||
if (retval == 0) {
|
|
||||||
fprintf(stderr, "Error with GetVolumeInformation: %d\n", GetLastError());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
char * var_data = "X_DECRYPT_DATA";
|
// Get disk serial
|
||||||
char * var_entropy = "X_DECRYPT_ENTROPY";
|
DWORD serial;
|
||||||
|
if (GetVolumeInformation("c:\\\\", NULL, 0, &serial, NULL, NULL, NULL, 0) == 0) {
|
||||||
char * data_hex = getenv(var_data);
|
DWORD err = GetLastError();
|
||||||
char * entropy_hex = getenv(var_entropy);
|
fprintf(stderr, "Error with GetVolumeInformation: %ld\n", err);
|
||||||
|
return err;
|
||||||
if (data_hex == NULL || entropy_hex == NULL) {
|
|
||||||
fputs("PROGOUTPUT:-1", stdout);
|
|
||||||
exit(-1);
|
|
||||||
}
|
}
|
||||||
|
DWORD be_serial = htonl(serial);
|
||||||
|
fprintf(stderr, "Disk serial (hex): %08lx\n", serial);
|
||||||
|
|
||||||
char * data_bytes = malloc((strlen(data_hex) / 2));
|
|
||||||
char * entropy_bytes = malloc((strlen(entropy_hex) / 2));
|
|
||||||
|
|
||||||
if (data_bytes == NULL || entropy_bytes == NULL) {
|
unsigned int eax, ebx, ecx, edx;
|
||||||
fputs("PROGOUTPUT:-2", stdout);
|
// Get CPUID vendor string
|
||||||
exit(-2);
|
union CPUIDVendor cpu_vendor;
|
||||||
|
if (__get_cpuid(0, &eax, &cpu_vendor.reg[0], &cpu_vendor.reg[2], &cpu_vendor.reg[1]) == 0) {
|
||||||
|
fprintf(stderr, "Error: cpuid(0) not supported");
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
cpu_vendor.vendor[12] = '\0';
|
||||||
hex2bin(data_hex, data_bytes);
|
fprintf(stderr, "CPUID Vendor: %s\n", cpu_vendor.vendor);
|
||||||
hex2bin(entropy_hex, entropy_bytes);
|
// Get CPUID "signature" (eax of CPUID(1))
|
||||||
|
unsigned int signature;
|
||||||
#ifdef DEBUG
|
if (__get_cpuid(1, &signature, &ebx, &ecx, &edx) == 0) {
|
||||||
hexDump("data", data_bytes, strlen(data_hex)/2, 16);
|
fprintf(stderr, "Error: cpuid(1) not supported");
|
||||||
hexDump("entropy", entropy_bytes, strlen(entropy_hex)/2, 16);
|
return 1;
|
||||||
#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
|
unsigned int be_signature = htonl(signature);
|
||||||
fputs("PROGOUTPUT:0:", stdout);
|
fprintf(stderr, "CPUID Signature (hex): %08x\n", signature);
|
||||||
|
|
||||||
|
|
||||||
|
// Get windows user
|
||||||
|
#define USERBUFSIZE 512
|
||||||
|
TCHAR user[USERBUFSIZE];
|
||||||
|
memset(&user, 0, sizeof(user)); // GetUserName only sets bytes as needed for length of username, but we need null bytes to fill the rest
|
||||||
|
DWORD bufsize = USERBUFSIZE ;
|
||||||
|
if (GetUserName(user, &bufsize) == 0) {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
fprintf(stderr, "Error with GetUserName: %ld\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Username: %s\n", user);
|
||||||
|
|
||||||
|
|
||||||
|
// Get Encrypted adobe key
|
||||||
|
#define KEYBUFSIZE 180 // As measured
|
||||||
|
BYTE key[KEYBUFSIZE];
|
||||||
|
DWORD regkeysize = KEYBUFSIZE;
|
||||||
|
LSTATUS retval = RegGetValue(HKEY_CURRENT_USER, "Software\\Adobe\\Adept\\Device", "key", RRF_RT_REG_BINARY, NULL, &key, ®keysize);
|
||||||
|
if (retval != ERROR_SUCCESS) {
|
||||||
|
fprintf(stderr, "Error with RegGetValue: %ld\n", retval);
|
||||||
|
fprintf(stderr, "regkeysize: %ld\n", regkeysize);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Encrypted key (hex): ");
|
||||||
|
for (size_t i = 0; i < KEYBUFSIZE; i++ )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%02x", key[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
|
||||||
|
// Assemble "entropy" (passphrase) for key
|
||||||
|
struct EncEntropy entropy;
|
||||||
|
memcpy(&entropy.serial, &be_serial, sizeof(entropy.serial));
|
||||||
|
memcpy(&entropy.vendor, &cpu_vendor.vendor, sizeof(entropy.vendor));
|
||||||
|
memcpy(&entropy.signature, ((char*)(&be_signature))+1, sizeof(entropy.signature)); // Only the last 3 bytes are needed, hence the +1 ptr
|
||||||
|
memcpy(&entropy.user, &user, sizeof(entropy.user));
|
||||||
|
|
||||||
|
// Print entropy byte by byte in hex
|
||||||
|
fprintf(stderr, "Entropy: ");
|
||||||
|
for (size_t i = 0; i < sizeof(entropy); i++ )
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%02x", ((unsigned char*)&entropy)[i]);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
|
||||||
|
// Run decryption API
|
||||||
|
DATA_BLOB ciphertext_data, entropy_data, plaintext_data;
|
||||||
|
ciphertext_data.pbData = key;
|
||||||
|
ciphertext_data.cbData = sizeof(key);
|
||||||
|
entropy_data.pbData = (BYTE*)(&entropy);
|
||||||
|
entropy_data.cbData = sizeof(entropy);
|
||||||
|
if (CryptUnprotectData(&ciphertext_data, NULL, &entropy_data, NULL, NULL, 0, &plaintext_data) != TRUE) {
|
||||||
|
DWORD err = GetLastError();
|
||||||
|
fprintf(stderr, "Error with CryptUnprotectData: %ld\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Decrypted key length: %lu\n", plaintext_data.cbData);
|
||||||
|
|
||||||
|
// Print decrypted key to stdout
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
printf("%02x", output_data.pbData[i]);
|
printf("%02x", plaintext_data.pbData[i]);
|
||||||
}
|
}
|
||||||
exit(0);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
|
||||||
// Apparently Wine has issues with the volume serial code sometimes
|
|
||||||
// so the code on the Linux side detects the wrong serial number.
|
|
||||||
// Thus, if the decryption fails, we read the serial number that Wine
|
|
||||||
// (and ADE) sees back to the Linux side for another attempt.
|
|
||||||
|
|
||||||
int err = GetLastError();
|
|
||||||
|
|
||||||
printf("PROGOUTPUT:-4:%d:%08x", err, get_serial());
|
|
||||||
|
|
||||||
exit(-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user