mirror of https://github.com/IoTcat/vlmcsd.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1142 lines
32 KiB
1142 lines
32 KiB
#ifndef CONFIG |
|
#define CONFIG "config.h" |
|
#endif // CONFIG |
|
#include CONFIG |
|
|
|
#ifndef _GNU_SOURCE |
|
#define _GNU_SOURCE |
|
#endif |
|
|
|
#ifndef _CRT_SECURE_NO_WARNINGS |
|
#define _CRT_SECURE_NO_WARNINGS |
|
#endif |
|
|
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <stdint.h> |
|
#include <ctype.h> |
|
#include <time.h> |
|
#if !defined(_WIN32) |
|
#if !__ANDROID__ |
|
#include <sys/shm.h> |
|
#endif // !__ANDROID__ |
|
#include <sys/socket.h> |
|
#include <sys/ipc.h> |
|
#endif // !defined(_WIN32) |
|
|
|
#include "output.h" |
|
#include "crypto.h" |
|
#include "endian.h" |
|
#include "kms.h" |
|
#include "shared_globals.h" |
|
#include "helpers.h" |
|
|
|
#define FRIENDLY_NAME_WINDOWS "Windows" |
|
#define FRIENDLY_NAME_OFFICE2010 "Office 2010" |
|
#define FRIENDLY_NAME_OFFICE2013 "Office 2013+" |
|
|
|
#ifndef IS_LIBRARY |
|
|
|
#ifdef NO_LOG |
|
#define LOGTEXT(x) "" |
|
#else //!NO_LOG |
|
#define LOGTEXT(x) x |
|
#endif // !NO_LOG |
|
|
|
int32_t getProductIndex(const GUID* guid, const PVlmcsdData_t list, const int32_t count, char** name, char** ePid) |
|
{ |
|
int i; |
|
|
|
for (i = count - 1; i >= 0; i--) |
|
{ |
|
if (IsEqualGUID(guid, &list[i].Guid)) |
|
{ |
|
if (name) *name = list[i].Name; |
|
if (ePid) *ePid = KmsData->CsvlkData[list[i].EPidIndex].EPid; |
|
return i; |
|
} |
|
} |
|
|
|
if (name) *name = "Unknown"; |
|
if (ePid) *ePid = KmsData->CsvlkData[EPID_INDEX_WINDOWS].EPid; |
|
return i; |
|
} |
|
|
|
#endif // IS_LIBRARY |
|
|
|
|
|
#ifndef NO_RANDOM_EPID |
|
// HostType and OSBuild |
|
static const struct KMSHostOS { uint16_t Type; uint16_t Build; } HostOS[] = |
|
{ |
|
{ 55041, 6002 }, // Windows Server 2008 SP2 |
|
{ 55041, 7601 }, // Windows Server 2008 R2 SP1 |
|
{ 5426, 9200 }, // Windows Server 2012 |
|
{ 6401, 9600 }, // Windows Server 2012 R2 |
|
{ 3612, 14393 }, // Windows Server 2016 |
|
}; |
|
|
|
|
|
// Valid language identifiers to be used in the ePID |
|
static const uint16_t LcidList[] = { |
|
1078, 1052, 1025, 2049, 3073, 4097, 5121, 6145, 7169, 8193, 9217, 10241, 11265, 12289, 13313, 14337, 15361, 16385, |
|
1067, 1068, 2092, 1069, 1059, 1093, 5146, 1026, 1027, 1028, 2052, 3076, 4100, 5124, 1050, 4122, 1029, 1030, 1125, 1043, 2067, |
|
1033, 2057, 3081, 4105, 5129, 6153, 7177, 8201, 9225, 10249, 11273, 12297, 13321, 1061, 1080, 1065, 1035, 1036, 2060, |
|
3084, 4108, 5132, 6156, 1079, 1110, 1031, 2055, 3079, 4103, 5127, 1032, 1095, 1037, 1081, 1038, 1039, 1057, 1040, 2064, 1041, 1099, |
|
1087, 1111, 1042, 1088, 1062, 1063, 1071, 1086, 2110, 1100, 1082, 1153, 1102, 1104, 1044, 2068, 1045, 1046, 2070, |
|
1094, 1131, 2155, 3179, 1048, 1049, 9275, 4155, 5179, 3131, 1083, 2107, 8251, 6203, 7227, 1103, 2074, 6170, 3098, |
|
7194, 1051, 1060, 1034, 2058, 3082, 4106, 5130, 6154, 7178, 8202, 9226, 10250, 11274, 12298, 13322, 14346, 15370, 16394, |
|
17418, 18442, 19466, 20490, 1089, 1053, 2077, 1114, 1097, 1092, 1098, 1054, 1074, 1058, 1056, 1091, 2115, 1066, 1106, 1076, 1077 |
|
}; |
|
|
|
|
|
#ifdef _PEDANTIC |
|
uint16_t IsValidLcid(const uint16_t Lcid) |
|
{ |
|
uint16_t i; |
|
|
|
for (i = 0; i < vlmcsd_countof(LcidList); i++) |
|
{ |
|
if (Lcid == LcidList[i]) return Lcid; |
|
} |
|
|
|
return 0; |
|
} |
|
#endif // _PEDANTIC |
|
#endif // NO_RANDOM_EPID |
|
|
|
|
|
// Unix time is seconds from 1970-01-01. Should be 64 bits to avoid year 2038 overflow bug. |
|
// FILETIME is 100 nanoseconds from 1601-01-01. Must be 64 bits. |
|
void getUnixTimeAsFileTime(FILETIME *const ts) |
|
{ |
|
int64_t unixtime = (int64_t)time(NULL); |
|
int64_t *filetime = (int64_t*)ts; |
|
|
|
PUT_UA64LE(filetime, (unixtime + 11644473600LL) * 10000000LL); |
|
} |
|
|
|
__pure int64_t fileTimeToUnixTime(const FILETIME *const ts) |
|
{ |
|
return GET_UA64LE(ts) / 10000000LL - 11644473600LL; |
|
} |
|
|
|
|
|
#ifndef NO_STRICT_MODES |
|
#ifndef NO_CLIENT_LIST |
|
|
|
static PClientList_t ClientLists; |
|
static BYTE ZeroGuid[16] = { 0 }; |
|
|
|
#if !defined(_WIN32) && !defined(__CYGWIN__) |
|
pthread_mutex_t* mutex; |
|
#define mutex_size (((sizeof(pthread_mutex_t)+7)>>3)<<3) |
|
#else |
|
CRITICAL_SECTION* mutex; |
|
#define mutex_size (((sizeof(CRITICAL_SECTION)+7)>>3)<<3) |
|
#endif // _WIN32 |
|
|
|
#ifndef USE_THREADS |
|
static int shmid_clients = -1; |
|
#endif // USE_THREADS |
|
|
|
#if !defined(_WIN32) && !defined(__CYGWIN__) |
|
#define lock_client_lists() pthread_mutex_lock(mutex) |
|
#define unlock_client_lists() pthread_mutex_unlock(mutex) |
|
#define mutex_t pthread_mutex_t |
|
#else |
|
#define lock_client_lists() EnterCriticalSection(mutex) |
|
#define unlock_client_lists() LeaveCriticalSection(mutex) |
|
#define mutex_t CRITICAL_SECTION |
|
#endif |
|
|
|
void CleanUpClientLists() |
|
{ |
|
# ifndef USE_THREADS |
|
shmctl(shmid_clients, IPC_RMID, NULL); |
|
# endif // !USE_THREADS |
|
} |
|
|
|
void InitializeClientLists() |
|
{ |
|
int_fast8_t i; |
|
int_fast16_t j; |
|
|
|
# ifndef USE_THREADS |
|
if ( |
|
(shmid_clients = shmget(IPC_PRIVATE, sizeof(ClientList_t) * KmsData->AppItemCount + mutex_size, IPC_CREAT | 0600)) < 0 || |
|
(mutex = (mutex_t*)shmat(shmid_clients, NULL, 0)) == (mutex_t*)-1 |
|
) |
|
{ |
|
int errno_save = errno; |
|
printerrorf("Warning: CMID lists disabled. Could not create shared memory: %s\n", vlmcsd_strerror(errno_save)); |
|
if (shmid_clients >= 0) shmctl(shmid_clients, IPC_RMID, NULL); |
|
MaintainClients = FALSE; |
|
return; |
|
} |
|
|
|
ClientLists = (PClientList_t)((BYTE*)mutex + mutex_size); |
|
|
|
# if __CYGWIN__ |
|
InitializeCriticalSection(mutex); |
|
# else // !__CYGWIN__ |
|
pthread_mutexattr_t mutex_attr; |
|
pthread_mutexattr_init(&mutex_attr); |
|
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); |
|
pthread_mutex_init(mutex, &mutex_attr); |
|
|
|
# endif // !__CYGWIN__ |
|
|
|
# else // USE_THREADS |
|
|
|
ClientLists = (PClientList_t)vlmcsd_malloc(sizeof(ClientList_t) * KmsData->AppItemCount); |
|
mutex = (mutex_t*)vlmcsd_malloc(sizeof(mutex_t)); |
|
|
|
# if !_WIN32 && !__CYGWIN__ |
|
pthread_mutex_init(mutex, NULL); |
|
# else //_WIN32 || __CYGWIN__ |
|
InitializeCriticalSection(mutex); |
|
# endif //_WIN32 || __CYGWIN__ |
|
|
|
# endif // USE_THREADS |
|
|
|
memset(ClientLists, 0, sizeof(ClientList_t) * KmsData->AppItemCount); |
|
|
|
if (!StartEmpty) |
|
{ |
|
for (i = 0; i < KmsData->AppItemCount; i++) |
|
{ |
|
uint8_t maxCount = KmsData->AppItemList[i].NCountPolicy; |
|
ClientLists[i].CurrentCount = (maxCount >> 1) - 1; |
|
ClientLists[i].MaxCount = maxCount; |
|
|
|
for (j = 0; j < (maxCount >> 1) - 1; j++) |
|
{ |
|
get16RandomBytes(&ClientLists[i].Guid[j]); |
|
} |
|
} |
|
} |
|
} |
|
#endif // NO_CLIENT_LIST |
|
#endif // !NO_STRICT_MODES |
|
|
|
#ifndef NO_RANDOM_EPID |
|
// formats an int with a fixed number of digits with leading zeros (helper for ePID generation) |
|
static char* itoc(char *const c, const int i, uint_fast8_t digits) |
|
{ |
|
char formatString[8]; |
|
if (digits > 9) digits = 0; |
|
strcpy(formatString, "%"); |
|
|
|
if (digits) |
|
{ |
|
formatString[1] = '0'; |
|
formatString[2] = digits | 0x30; |
|
formatString[3] = 0; |
|
} |
|
|
|
strcat(formatString, "u"); |
|
sprintf(c, formatString, i); |
|
return c; |
|
} |
|
|
|
static int getRandomServerType() |
|
{ |
|
# if defined(USE_MSRPC) || defined(SIMPLE_RPC) |
|
|
|
return rand() % (int)vlmcsd_countof(HostOS); |
|
|
|
# else // !defined(USE_MSRPC) && !defined(SIMPLE_RPC) |
|
if (!UseServerRpcBTFN) |
|
{ |
|
// This isn't possible at all, e.g. KMS host on XP |
|
return rand() % (int)vlmcsd_countof(HostOS); |
|
} |
|
|
|
// return 9200/9600/14393 if NDR64 is in use, otherwise 6002/7601 |
|
if (UseServerRpcNDR64) return (rand() % 3) + 2; |
|
return (rand() % 2); |
|
|
|
# endif // !defined(USE_MSRPC) && !defined(SIMPLE_RPC) |
|
} |
|
|
|
|
|
/* |
|
* Generates a random ePID |
|
*/ |
|
static void generateRandomPid(int index, char *const szPid, int serverType, int16_t lang) |
|
{ |
|
char numberBuffer[12]; |
|
|
|
if (serverType < 0 || serverType >= (int)vlmcsd_countof(HostOS)) |
|
{ |
|
serverType = getRandomServerType(); |
|
} |
|
|
|
strcpy(szPid, itoc(numberBuffer, HostOS[serverType].Type, 5)); |
|
strcat(szPid, "-"); |
|
|
|
//if (index > 3) index = 0; |
|
|
|
PCsvlkData_t csvlkData = &KmsData->CsvlkData[index]; |
|
strcat(szPid, itoc(numberBuffer, csvlkData->GroupId, 5)); |
|
strcat(szPid, "-"); |
|
|
|
int keyId = (rand32() % (csvlkData->MaxKeyId - csvlkData->MinKeyId)) + csvlkData->MinKeyId; |
|
strcat(szPid, itoc(numberBuffer, keyId / 1000000, 3)); |
|
strcat(szPid, "-"); |
|
strcat(szPid, itoc(numberBuffer, keyId % 1000000, 6)); |
|
strcat(szPid, "-03-"); |
|
|
|
if (lang < 0) lang = LcidList[rand() % vlmcsd_countof(LcidList)]; |
|
strcat(szPid, itoc(numberBuffer, lang, 0)); |
|
strcat(szPid, "-"); |
|
|
|
strcat(szPid, itoc(numberBuffer, HostOS[serverType].Build, 0)); |
|
strcat(szPid, ".0000-"); |
|
|
|
# define minTime ((time_t)1470175200) /* Release Date Win 2016 */ |
|
|
|
time_t maxTime, kmsTime; |
|
time(&maxTime); |
|
|
|
# ifndef BUILD_TIME |
|
# define BUILD_TIME 1481079869 |
|
# endif |
|
|
|
if (maxTime < (time_t)BUILD_TIME) // Just in case the system time is < 10/17/2013 1:00 pm |
|
maxTime = (time_t)BUILD_TIME; |
|
|
|
kmsTime = (rand32() % (maxTime - minTime)) + minTime; |
|
|
|
struct tm *pidTime; |
|
pidTime = gmtime(&kmsTime); |
|
|
|
strcat(szPid, itoc(numberBuffer, pidTime->tm_yday, 3)); |
|
strcat(szPid, itoc(numberBuffer, pidTime->tm_year + 1900, 4)); |
|
} |
|
|
|
|
|
/* |
|
* Generates random ePIDs and stores them if not already read from ini file. |
|
* For use with randomization level 1 |
|
*/ |
|
void randomPidInit() |
|
{ |
|
uint32_t i; |
|
|
|
int serverType = getRandomServerType(); |
|
int16_t lang = Lcid ? Lcid : LcidList[rand() % vlmcsd_countof(LcidList)]; |
|
|
|
for (i = 0; i < KmsData->CsvlkCount; i++) |
|
{ |
|
if (KmsResponseParameters[i].Epid) continue; |
|
|
|
char Epid[PID_BUFFER_SIZE]; |
|
|
|
generateRandomPid(i, Epid, serverType, lang); |
|
KmsResponseParameters[i].Epid = (const char*)vlmcsd_strdup(Epid); |
|
|
|
#ifndef NO_LOG |
|
KmsResponseParameters[i].EpidSource = "randomized at program start"; |
|
#endif // NO_LOG |
|
} |
|
} |
|
|
|
#endif // NO_RANDOM_EPID |
|
|
|
|
|
#ifndef NO_LOG |
|
static int32_t getProductIndexFromAllLists(const GUID* guid, char** productName) |
|
{ |
|
return getProductIndex(guid, KmsData->AppItemList, KmsData->AppItemCount + KmsData->KmsItemCount + KmsData->SkuItemCount, productName, NULL); |
|
} |
|
|
|
/* |
|
* Logs a Request |
|
*/ |
|
static void logRequest(const REQUEST *const baseRequest) |
|
{ |
|
#ifndef NO_VERBOSE_LOG |
|
if (logverbose) |
|
{ |
|
logger("<<< Incoming KMS request\n"); |
|
logRequestVerbose(baseRequest, &logger); |
|
return; |
|
} |
|
#endif // NO_VERBOSE_LOG |
|
|
|
char *productName; |
|
char clientname[64]; |
|
|
|
int32_t index = getProductIndexFromAllLists(&baseRequest->ActID, &productName); |
|
if (index < 0) index = getProductIndexFromAllLists(&baseRequest->KMSID, &productName); |
|
if (index < 0) index = getProductIndexFromAllLists(&baseRequest->AppID, &productName); |
|
|
|
if (index < 0 || !strcasecmp(productName, "Unknown")) |
|
{ |
|
productName = (char*)alloca(GUID_STRING_LENGTH + 1); |
|
uuid2StringLE(&baseRequest->ActID, productName); |
|
} |
|
|
|
ucs2_to_utf8(baseRequest->WorkstationName, clientname, 64, 64); |
|
logger("KMS v%i.%i request from %s for %s\n", LE16(baseRequest->MajorVer), LE16(baseRequest->MinorVer), clientname, productName); |
|
} |
|
#endif // NO_LOG |
|
|
|
|
|
/* |
|
* Converts a utf-8 ePID string to UCS-2 and writes it to a RESPONSE struct |
|
*/ |
|
#ifndef IS_LIBRARY |
|
static void getEpidFromString(RESPONSE *const Response, const char *const pid) |
|
{ |
|
size_t length = utf8_to_ucs2(Response->KmsPID, pid, PID_BUFFER_SIZE, PID_BUFFER_SIZE * 3); |
|
Response->PIDSize = LE32(((unsigned int)length + 1) << 1); |
|
} |
|
|
|
|
|
/* |
|
* get ePID from appropriate source |
|
*/ |
|
static void getEpid(RESPONSE *const baseResponse, const char** EpidSource, const int32_t index, BYTE *const HwId, const char* defaultEPid) |
|
{ |
|
#if !defined(NO_RANDOM_EPID) || !defined(NO_CL_PIDS) || !defined(NO_INI_FILE) |
|
const char* pid; |
|
if (KmsResponseParameters[index].Epid == NULL) |
|
{ |
|
#ifndef NO_RANDOM_EPID |
|
if (RandomizationLevel == 2) |
|
{ |
|
char szPid[PID_BUFFER_SIZE]; |
|
generateRandomPid(index, szPid, -1, Lcid ? Lcid : -1); |
|
pid = szPid; |
|
|
|
#ifndef NO_LOG |
|
*EpidSource = "randomized on every request"; |
|
#endif // NO_LOG |
|
} |
|
else |
|
#endif // NO_RANDOM_EPID |
|
{ |
|
pid = defaultEPid; |
|
#ifndef NO_LOG |
|
*EpidSource = "vlmcsd default"; |
|
#endif // NO_LOG |
|
} |
|
} |
|
else |
|
{ |
|
pid = KmsResponseParameters[index].Epid; |
|
|
|
if (HwId && KmsResponseParameters[index].HwId != NULL) |
|
memcpy(HwId, KmsResponseParameters[index].HwId, sizeof(((RESPONSE_V6 *)0)->HwId)); |
|
|
|
#ifndef NO_LOG |
|
*EpidSource = KmsResponseParameters[index].EpidSource; |
|
#endif // NO_LOG |
|
} |
|
|
|
getEpidFromString(baseResponse, pid); |
|
|
|
#else // defined(NO_RANDOM_EPID) && defined(NO_CL_PIDS) && !defined(NO_INI_FILE) |
|
|
|
getEpidFromString(baseResponse, defaultEPid); |
|
|
|
# ifndef NO_LOG |
|
*EpidSource = "vlmcsd default"; |
|
# endif // NO_LOG |
|
|
|
#endif // defined(NO_RANDOM_EPID) && defined(NO_CL_PIDS) && !defined(NO_INI_FILE) |
|
} |
|
#endif // IS_LIBRARY |
|
|
|
|
|
#if !defined(NO_LOG) && defined(_PEDANTIC) |
|
static BOOL CheckVersion4Uuid(const GUID *const guid, const char *const szGuidName) |
|
{ |
|
if (LE16(guid->Data3) >> 12 != 4 || guid->Data4[0] >> 6 != 2) |
|
{ |
|
logger("Warning: %s does not conform to version 4 UUID according to RFC 4122\n", szGuidName); |
|
return FALSE; |
|
} |
|
return TRUE; |
|
} |
|
|
|
|
|
static void CheckRequest(const REQUEST *const Request) |
|
{ |
|
CheckVersion4Uuid(&Request->CMID, "Client machine ID"); |
|
CheckVersion4Uuid(&Request->AppID, "Application ID"); |
|
CheckVersion4Uuid(&Request->KMSID, "Server SKU ID"); |
|
CheckVersion4Uuid(&Request->ActID, "Client SKU ID"); |
|
|
|
if (LE32(Request->IsClientVM) > 1) |
|
logger("Warning: Virtual Machine field in request must be 0 or 1 but is %u\n", LE32(Request->IsClientVM)); |
|
|
|
if (LE32(Request->LicenseStatus) > 6) |
|
logger("Warning: License status must be between 0 and 6 but is %u\n", LE32(Request->LicenseStatus)); |
|
} |
|
#endif // !defined(NO_LOG) && defined(_PEDANTIC) |
|
|
|
|
|
#ifndef NO_LOG |
|
/* |
|
* Logs the Response |
|
*/ |
|
static void logResponse(const RESPONSE *const baseResponse, const BYTE *const hwId, const char *const EpidSource) |
|
{ |
|
char utf8pid[PID_BUFFER_SIZE * 3]; |
|
ucs2_to_utf8(baseResponse->KmsPID, utf8pid, PID_BUFFER_SIZE, PID_BUFFER_SIZE * 3); |
|
|
|
#ifndef NO_VERBOSE_LOG |
|
if (!logverbose) |
|
{ |
|
#endif // NO_VERBOSE_LOG |
|
logger("Sending ePID (%s): %s\n", EpidSource, utf8pid); |
|
#ifndef NO_VERBOSE_LOG |
|
} |
|
else |
|
{ |
|
logger(">>> Sending response, ePID source = %s\n", EpidSource); |
|
logResponseVerbose(utf8pid, hwId, baseResponse, &logger); |
|
} |
|
#endif // NO_VERBOSE_LOG |
|
|
|
} |
|
#endif |
|
|
|
|
|
#if __UCLIBC__ && !defined(NO_STRICT_MODES) |
|
long long int llabs(long long int j); |
|
#endif |
|
|
|
|
|
/* |
|
* Creates the unencrypted base response |
|
*/ |
|
#ifndef IS_LIBRARY |
|
static HRESULT __stdcall CreateResponseBaseCallback(const REQUEST *const baseRequest, RESPONSE *const baseResponse, BYTE *const hwId, const char* const ipstr) |
|
{ |
|
const char* EpidSource; |
|
#ifndef NO_LOG |
|
logRequest(baseRequest); |
|
#ifdef _PEDANTIC |
|
CheckRequest(baseRequest); |
|
#endif // _PEDANTIC |
|
#endif // NO_LOG |
|
|
|
char* ePid; |
|
DWORD minClients = LE32(baseRequest->N_Policy); |
|
DWORD required_clients = minClients < 1 ? 1 : minClients << 1; |
|
|
|
int32_t index = getProductIndex(&baseRequest->KMSID, KmsData->KmsItemList, KmsData->KmsItemCount, NULL, &ePid); |
|
|
|
# ifndef NO_STRICT_MODES |
|
|
|
if (required_clients > 2000) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Rejecting request with more than 1000 minimum clients (0x8007000D)\n"); |
|
# endif |
|
|
|
return 0x8007000D; |
|
} |
|
|
|
if (CheckClientTime) |
|
{ |
|
time_t requestTime = (time_t)fileTimeToUnixTime(&baseRequest->ClientTime); |
|
|
|
if (llabs(requestTime - time(NULL)) > 60 * 60 * 4) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Client time differs more than 4 hours from system time (0xC004F06C)\n"); |
|
# endif // !NO_LOG |
|
|
|
return 0xC004F06C; |
|
} |
|
} |
|
|
|
if (WhitelistingLevel & 2) |
|
{ |
|
if (index >= 0 && (KmsData->KmsItemList[index].IsPreview || KmsData->KmsItemList[index].IsRetail)) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Refusing retail or beta product (0xC004F042)\n"); |
|
# endif // !NO_LOG |
|
|
|
return 0xC004F042; |
|
} |
|
} |
|
|
|
if ((WhitelistingLevel & 1) && index < 0) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Refusing unknown product (0xC004F042)\n"); |
|
# endif // !NO_LOG |
|
|
|
return 0xC004F042; |
|
} |
|
|
|
# ifndef NO_CLIENT_LIST |
|
int32_t appIndex = index < 0 ? 0 : KmsData->KmsItemList[index].AppIndex; |
|
# endif // NO_CLIENT_LIST |
|
|
|
# endif // !NO_STRICT_MODES |
|
|
|
int32_t ePidIndex = index < 0 ? 0 : KmsData->KmsItemList[index].EPidIndex; |
|
|
|
# if !defined(NO_STRICT_MODES) |
|
|
|
if ((WhitelistingLevel & 1) && index >= 0 && !IsEqualGUID(&KmsData->AppItemList[KmsData->KmsItemList[index].AppIndex].Guid, &baseRequest->AppID)) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Refusing product with incorrect Application ID (0xC004F042)\n"); |
|
# endif // NO_LOG |
|
return 0xC004F042; |
|
} |
|
|
|
# ifndef NO_CLIENT_LIST |
|
if (MaintainClients) |
|
{ |
|
lock_client_lists(); |
|
|
|
int_fast16_t i; |
|
int_fast8_t isKnownClient = FALSE; |
|
|
|
if (required_clients > (DWORD)ClientLists[appIndex].MaxCount) ClientLists[appIndex].MaxCount = required_clients; |
|
|
|
for (i = 0; i < ClientLists[appIndex].MaxCount; i++) |
|
{ |
|
if (IsEqualGUID(&ClientLists[appIndex].Guid[i], &baseRequest->CMID)) |
|
{ |
|
isKnownClient = TRUE; |
|
break; |
|
} |
|
} |
|
|
|
if (isKnownClient) |
|
{ |
|
baseResponse->Count = LE32(ClientLists[appIndex].CurrentCount); |
|
} |
|
else |
|
{ |
|
for (i = 0; i < ClientLists[appIndex].MaxCount; i++) |
|
{ |
|
if (IsEqualGUID(ZeroGuid, &ClientLists[appIndex].Guid[i])) |
|
{ |
|
if (ClientLists[appIndex].CurrentCount >= MAX_CLIENTS) |
|
{ |
|
# ifndef NO_LOG |
|
logger("Rejecting more than 671 clients (0xC004D104)\n"); |
|
# endif // !NO_LOG |
|
|
|
unlock_client_lists(); |
|
return 0xC004D104; |
|
} |
|
|
|
baseResponse->Count = LE32(++ClientLists[appIndex].CurrentCount); |
|
memcpy(&ClientLists[appIndex].Guid[i], &baseRequest->CMID, sizeof(GUID)); |
|
break; |
|
} |
|
} |
|
|
|
if (i >= ClientLists[appIndex].MaxCount) |
|
{ |
|
memcpy(&ClientLists[appIndex].Guid[ClientLists[appIndex].CurrentPosition], &baseRequest->CMID, sizeof(GUID)); |
|
ClientLists[appIndex].CurrentPosition = (ClientLists[appIndex].CurrentPosition + 1) % (ClientLists[appIndex].MaxCount > MAX_CLIENTS ? MAX_CLIENTS : ClientLists[appIndex].MaxCount); |
|
baseResponse->Count = LE32(ClientLists[appIndex].CurrentCount); |
|
} |
|
} |
|
|
|
unlock_client_lists(); |
|
} |
|
else |
|
# endif // !NO_CLIENT_LIST |
|
# endif // !defined(NO_STRICT_MODES) |
|
{ |
|
uint8_t minimum_answer_clients = (uint8_t)KmsData->CsvlkData[ePidIndex].MinActiveClients; |
|
baseResponse->Count = LE32(required_clients > minimum_answer_clients ? required_clients : minimum_answer_clients); |
|
//if (LE32(baseRequest->N_Policy) > LE32(baseResponse->Count)) baseResponse->Count = LE32(LE32(baseRequest->N_Policy) << 1); |
|
} |
|
|
|
getEpid(baseResponse, &EpidSource, ePidIndex, hwId, ePid); |
|
|
|
baseResponse->Version = baseRequest->Version; |
|
|
|
memcpy(&baseResponse->CMID, &baseRequest->CMID, sizeof(GUID)); |
|
memcpy(&baseResponse->ClientTime, &baseRequest->ClientTime, sizeof(FILETIME)); |
|
|
|
baseResponse->VLActivationInterval = LE32(VLActivationInterval); |
|
baseResponse->VLRenewalInterval = LE32(VLRenewalInterval); |
|
|
|
#ifndef NO_LOG |
|
logResponse(baseResponse, hwId, EpidSource); |
|
#endif // NO_LOG |
|
|
|
return S_OK; |
|
} |
|
|
|
RequestCallback_t CreateResponseBase = &CreateResponseBaseCallback; |
|
|
|
#else // IS_LIBRARY |
|
|
|
RequestCallback_t CreateResponseBase = NULL; |
|
|
|
#endif // IS_LIBRARY |
|
|
|
|
|
////TODO: Move to helpers.c |
|
void get16RandomBytes(void* ptr) |
|
{ |
|
int i; |
|
for (i = 0; i < 4; i++) ((DWORD*)ptr)[i] = rand32(); |
|
} |
|
|
|
/* |
|
* Creates v4 response |
|
*/ |
|
size_t CreateResponseV4(REQUEST_V4 *const request_v4, BYTE *const responseBuffer, const char* const ipstr) |
|
{ |
|
RESPONSE_V4* Response = (RESPONSE_V4*)responseBuffer; |
|
|
|
HRESULT hResult; |
|
if (FAILED(hResult = CreateResponseBase(&request_v4->RequestBase, &Response->ResponseBase, NULL, ipstr))) return hResult; |
|
|
|
DWORD pidSize = LE32(Response->ResponseBase.PIDSize); |
|
BYTE* postEpidPtr = responseBuffer + V4_PRE_EPID_SIZE + pidSize; |
|
memmove(postEpidPtr, &Response->ResponseBase.CMID, V4_POST_EPID_SIZE); |
|
|
|
size_t encryptSize = V4_PRE_EPID_SIZE + V4_POST_EPID_SIZE + pidSize; |
|
AesCmacV4(responseBuffer, encryptSize, responseBuffer + encryptSize); |
|
|
|
return encryptSize + sizeof(Response->MAC); |
|
} |
|
|
|
/* |
|
// Workaround for buggy GCC 4.2/4.3 |
|
#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ < 4) |
|
__attribute__((noinline)) |
|
#endif |
|
__pure static uint64_t TimestampInterval(void *ts) |
|
{ |
|
return ( GET_UA64LE(ts) / TIME_C1 ) * TIME_C2 + TIME_C3; |
|
}*/ |
|
|
|
|
|
/* |
|
* Creates the HMAC for v6 |
|
*/ |
|
static int_fast8_t CreateV6Hmac(BYTE *const encrypt_start, const size_t encryptSize, int_fast8_t tolerance) |
|
{ |
|
BYTE hash[32]; |
|
# define halfHashSize (sizeof(hash) >> 1) |
|
uint64_t timeSlot; |
|
BYTE *responseEnd = encrypt_start + encryptSize; |
|
|
|
// This is the time from the response |
|
FILETIME* ft = (FILETIME*)(responseEnd - V6_POST_EPID_SIZE + sizeof(((RESPONSE*)0)->CMID)); |
|
|
|
// Generate a time slot that changes every 4.11 hours. |
|
// Request and repsonse time must match +/- 1 slot. |
|
// When generating a response tolerance must be 0. |
|
// If verifying the hash, try tolerance -1, 0 and +1. One of them must match. |
|
|
|
timeSlot = LE64((GET_UA64LE(ft) / TIME_C1 * TIME_C2 + TIME_C3) + (tolerance * TIME_C1)); |
|
|
|
// The time slot is hashed with SHA256 so it is not so obvious that it is time |
|
Sha256((BYTE*)&timeSlot, sizeof(timeSlot), hash); |
|
|
|
// The last 16 bytes of the hashed time slot are the actual HMAC key |
|
if (!Sha256Hmac |
|
( |
|
hash + halfHashSize, // Use last 16 bytes of SHA256 as HMAC key |
|
encrypt_start, // hash only the encrypted part of the v6 response |
|
(DWORD)(encryptSize - sizeof(((RESPONSE_V6*)0)->HMAC)), // encryptSize minus the HMAC itself |
|
hash // use same buffer for resulting hash where the key came from |
|
)) |
|
{ |
|
return FALSE; |
|
} |
|
|
|
memcpy(responseEnd - sizeof(((RESPONSE_V6*)0)->HMAC), hash + halfHashSize, halfHashSize); |
|
return TRUE; |
|
# undef halfHashSize |
|
} |
|
|
|
|
|
/* |
|
* Creates v5 or v6 response |
|
*/ |
|
size_t CreateResponseV6(REQUEST_V6 *restrict request_v6, BYTE *const responseBuffer, const char* const ipstr) |
|
{ |
|
// The response will be created in a fixed sized struct to |
|
// avoid unaligned access macros and packed structs on RISC systems |
|
// which largely increase code size. |
|
// |
|
// The fixed sized struct with 64 WCHARs for the ePID will be converted |
|
// to a variable sized struct later and requires unaligned access macros. |
|
|
|
RESPONSE_V6* Response = (RESPONSE_V6*)responseBuffer; |
|
RESPONSE* baseResponse = &Response->ResponseBase; |
|
|
|
#ifdef _DEBUG |
|
// ReSharper disable once CppEntityNeverUsed |
|
RESPONSE_V6_DEBUG* xxx = (RESPONSE_V6_DEBUG*)responseBuffer; |
|
#endif |
|
|
|
static const BYTE DefaultHwid[8] = { HWID }; |
|
int_fast8_t v6 = LE16(request_v6->MajorVer) > 5; |
|
AesCtx aesCtx; |
|
|
|
AesInitKey(&aesCtx, v6 ? AesKeyV6 : AesKeyV5, v6, AES_KEY_BYTES); |
|
AesDecryptCbc(&aesCtx, NULL, request_v6->IV, V6_DECRYPT_SIZE); |
|
|
|
// get random salt and SHA256 it |
|
get16RandomBytes(Response->RandomXoredIVs); |
|
Sha256(Response->RandomXoredIVs, sizeof(Response->RandomXoredIVs), Response->Hash); |
|
|
|
if (v6) // V6 specific stuff |
|
{ |
|
// In v6 a random IV is generated |
|
Response->Version = request_v6->Version; |
|
get16RandomBytes(Response->IV); |
|
|
|
// pre-fill with default HwId (not required for v5) |
|
memcpy(Response->HwId, DefaultHwid, sizeof(Response->HwId)); |
|
|
|
// Just copy decrypted request IV (using Null IV) here. Note this is identical |
|
// to XORing non-decrypted request and reponse IVs |
|
memcpy(Response->XoredIVs, request_v6->IV, sizeof(Response->XoredIVs)); |
|
} |
|
else // V5 specific stuff |
|
{ |
|
// In v5 IVs of request and response must be identical (MS client checks this) |
|
// The following memcpy copies Version and IVs at once |
|
memcpy(Response, request_v6, V6_UNENCRYPTED_SIZE); |
|
} |
|
|
|
// Xor Random bytes with decrypted request IV |
|
XorBlock(request_v6->IV, Response->RandomXoredIVs); |
|
|
|
// Get the base response |
|
HRESULT hResult; |
|
if (FAILED(hResult = CreateResponseBase(&request_v6->RequestBase, baseResponse, Response->HwId, ipstr))) return hResult; |
|
|
|
// Convert the fixed sized struct into variable sized |
|
DWORD pidSize = LE32(baseResponse->PIDSize); |
|
BYTE* postEpidPtr = responseBuffer + V6_PRE_EPID_SIZE + pidSize; |
|
size_t post_epid_size = v6 ? V6_POST_EPID_SIZE : V5_POST_EPID_SIZE; |
|
|
|
memmove(postEpidPtr, &baseResponse->CMID, post_epid_size); |
|
|
|
// number of bytes to encrypt |
|
size_t encryptSize = |
|
V6_PRE_EPID_SIZE |
|
- sizeof(Response->Version) |
|
+ pidSize |
|
+ post_epid_size; |
|
|
|
//AesDecryptBlock(&aesCtx, Response->IV); |
|
if (v6 && !CreateV6Hmac(Response->IV, encryptSize, 0)) return 0; |
|
|
|
// Padding auto handled by encryption func |
|
AesEncryptCbc(&aesCtx, NULL, Response->IV, &encryptSize); |
|
|
|
return encryptSize + sizeof(Response->Version); |
|
} |
|
|
|
|
|
// Create Hashed KMS Client Request Data for KMS Protocol Version 4 |
|
BYTE *CreateRequestV4(size_t *size, const REQUEST* requestBase) |
|
{ |
|
*size = sizeof(REQUEST_V4); |
|
|
|
// Build a proper KMS client request data |
|
BYTE *request = (BYTE *)vlmcsd_malloc(sizeof(REQUEST_V4)); |
|
|
|
// Temporary Pointer for access to REQUEST_V4 structure |
|
REQUEST_V4 *request_v4 = (REQUEST_V4 *)request; |
|
|
|
// Set KMS Client Request Base |
|
memcpy(&request_v4->RequestBase, requestBase, sizeof(REQUEST)); |
|
|
|
// Generate Hash Signature |
|
AesCmacV4(request, sizeof(REQUEST), request_v4->MAC); |
|
|
|
// Return Request Data |
|
return request; |
|
} |
|
|
|
|
|
// Create Encrypted KMS Client Request Data for KMS Protocol Version 6 |
|
BYTE* CreateRequestV6(size_t *size, const REQUEST* requestBase) |
|
{ |
|
*size = sizeof(REQUEST_V6); |
|
|
|
// Temporary Pointer for access to REQUEST_V5 structure |
|
REQUEST_V6 *request = (REQUEST_V6 *)vlmcsd_malloc(sizeof(REQUEST_V6)); |
|
|
|
// KMS Protocol Version |
|
request->Version = requestBase->Version; |
|
|
|
// Initialize the IV |
|
get16RandomBytes(request->IV); |
|
|
|
// Set KMS Client Request Base |
|
memcpy(&request->RequestBase, requestBase, sizeof(REQUEST)); |
|
|
|
// Encrypt KMS Client Request |
|
size_t encryptSize = sizeof(request->RequestBase); |
|
AesCtx Ctx; |
|
int_fast8_t v6 = LE16(request->MajorVer) > 5; |
|
AesInitKey(&Ctx, v6 ? AesKeyV6 : AesKeyV5, v6, 16); |
|
AesEncryptCbc(&Ctx, request->IV, (BYTE*)(&request->RequestBase), &encryptSize); |
|
|
|
// Return Proper Request Data |
|
return (BYTE*)request; |
|
} |
|
|
|
|
|
/* |
|
* Checks whether Length of ePID is valid |
|
*/ |
|
static uint8_t checkPidLength(const RESPONSE *const responseBase) |
|
{ |
|
unsigned int i; |
|
|
|
if (LE32(responseBase->PIDSize) > (PID_BUFFER_SIZE << 1)) return FALSE; |
|
if (responseBase->KmsPID[(LE32(responseBase->PIDSize) >> 1) - 1]) return FALSE; |
|
|
|
for (i = 0; i < (LE32(responseBase->PIDSize) >> 1) - 2; i++) |
|
{ |
|
if (!responseBase->KmsPID[i]) return FALSE; |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
|
|
/* |
|
* "Decrypts" a KMS v4 response. Actually just copies to a fixed size buffer |
|
*/ |
|
RESPONSE_RESULT DecryptResponseV4(RESPONSE_V4* response_v4, const int responseSize, BYTE* const rawResponse, const BYTE* const rawRequest) |
|
{ |
|
int copySize = |
|
V4_PRE_EPID_SIZE + |
|
(LE32(((RESPONSE_V4*)rawResponse)->ResponseBase.PIDSize) <= PID_BUFFER_SIZE << 1 ? |
|
LE32(((RESPONSE_V4*)rawResponse)->ResponseBase.PIDSize) : |
|
PID_BUFFER_SIZE << 1); |
|
|
|
int messageSize = copySize + V4_POST_EPID_SIZE; |
|
|
|
memcpy(response_v4, rawResponse, copySize); |
|
memcpy(&response_v4->ResponseBase.CMID, rawResponse + copySize, responseSize - copySize); |
|
|
|
// ensure PID is null terminated |
|
response_v4->ResponseBase.KmsPID[PID_BUFFER_SIZE - 1] = 0; |
|
|
|
uint8_t* mac = rawResponse + messageSize; |
|
AesCmacV4(rawResponse, messageSize, mac); |
|
|
|
REQUEST_V4* request_v4 = (REQUEST_V4*)rawRequest; |
|
RESPONSE_RESULT result; |
|
|
|
result.mask = (DWORD)~0; |
|
result.PidLengthOK = checkPidLength((RESPONSE*)rawResponse); |
|
result.VersionOK = response_v4->ResponseBase.Version == request_v4->RequestBase.Version; |
|
result.HashOK = !memcmp(&response_v4->MAC, mac, sizeof(response_v4->MAC)); |
|
result.TimeStampOK = !memcmp(&response_v4->ResponseBase.ClientTime, &request_v4->RequestBase.ClientTime, sizeof(FILETIME)); |
|
result.ClientMachineIDOK = !memcmp(&response_v4->ResponseBase.CMID, &request_v4->RequestBase.CMID, sizeof(GUID)); |
|
result.effectiveResponseSize = responseSize; |
|
result.correctResponseSize = sizeof(RESPONSE_V4) - sizeof(response_v4->ResponseBase.KmsPID) + LE32(response_v4->ResponseBase.PIDSize); |
|
|
|
return result; |
|
} |
|
|
|
|
|
static RESPONSE_RESULT VerifyResponseV6(RESPONSE_RESULT result, const AesCtx* Ctx, RESPONSE_V6* response_v6, REQUEST_V6* request_v6, BYTE* const rawResponse) |
|
{ |
|
// Check IVs |
|
result.IVsOK = !memcmp // In V6 the XoredIV is actually the request IV |
|
( |
|
response_v6->XoredIVs, |
|
request_v6->IV, |
|
sizeof(response_v6->XoredIVs) |
|
); |
|
|
|
result.IVnotSuspicious = !!memcmp // If IVs are identical, it is obviously an emulator |
|
( |
|
request_v6->IV, |
|
response_v6->IV, |
|
sizeof(request_v6->IV) |
|
); |
|
|
|
// Check Hmac |
|
int_fast8_t tolerance; |
|
BYTE OldHmac[sizeof(response_v6->HMAC)]; |
|
|
|
result.HmacSha256OK = FALSE; |
|
|
|
memcpy // Save received HMAC to compare with calculated HMAC later |
|
( |
|
OldHmac, |
|
response_v6->HMAC, |
|
sizeof(response_v6->HMAC) |
|
); |
|
|
|
//AesEncryptBlock(Ctx, Response_v6->IV); // CreateV6Hmac needs original IV as received over the network |
|
|
|
for (tolerance = -1; tolerance < 2; tolerance++) |
|
{ |
|
CreateV6Hmac |
|
( |
|
rawResponse + sizeof(response_v6->Version), // Pointer to start of the encrypted part of the response |
|
(size_t)result.correctResponseSize - sizeof(response_v6->Version), // size of the encrypted part |
|
tolerance // tolerance -1, 0, or +1 |
|
); |
|
|
|
result.HmacSha256OK = !memcmp // Compare both HMACs |
|
( |
|
OldHmac, |
|
rawResponse + (size_t)result.correctResponseSize - sizeof(response_v6->HMAC), |
|
sizeof(OldHmac) |
|
); |
|
|
|
if (result.HmacSha256OK) break; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
static RESPONSE_RESULT VerifyResponseV5(RESPONSE_RESULT result, REQUEST_V5* request_v5, RESPONSE_V5* response_v5) |
|
{ |
|
// Check IVs: in V5 (and only v5) request and response IVs must match |
|
result.IVsOK = !memcmp(request_v5->IV, response_v5->IV, sizeof(request_v5->IV)); |
|
|
|
// V5 has no Hmac, always set to TRUE |
|
result.HmacSha256OK = TRUE; |
|
|
|
return result; |
|
} |
|
|
|
|
|
/* |
|
* Decrypts a KMS v5 or v6 response received from a server. |
|
* hwid must supply a valid 16 byte buffer for v6. hwid is ignored in v5 |
|
*/ |
|
RESPONSE_RESULT DecryptResponseV6(RESPONSE_V6* response_v6, int responseSize, BYTE* const response, const BYTE* const rawRequest, BYTE* hwid) |
|
{ |
|
RESPONSE_RESULT result; |
|
result.mask = ~0; // Set all bits in the results mask to 1. Assume success first. |
|
result.effectiveResponseSize = responseSize; |
|
|
|
int copySize1 = |
|
sizeof(response_v6->Version); |
|
|
|
// Decrypt KMS Server Response (encrypted part starts after RequestIV) |
|
responseSize -= copySize1; |
|
|
|
AesCtx Ctx; |
|
int_fast8_t v6 = LE16(((RESPONSE_V6*)response)->MajorVer) > 5; |
|
|
|
AesInitKey(&Ctx, v6 ? AesKeyV6 : AesKeyV5, v6, AES_KEY_BYTES); |
|
AesDecryptCbc(&Ctx, NULL, response + copySize1, responseSize); |
|
|
|
// Check padding |
|
BYTE* lastPadByte = response + (size_t)result.effectiveResponseSize - 1; |
|
|
|
// Must be from 1 to 16 |
|
if (!*lastPadByte || *lastPadByte > AES_BLOCK_BYTES) |
|
{ |
|
result.DecryptSuccess = FALSE; |
|
return result; |
|
} |
|
|
|
// Check if pad bytes are all the same |
|
BYTE* padByte; |
|
for (padByte = lastPadByte - *lastPadByte + 1; padByte < lastPadByte; padByte++) |
|
{ |
|
if (*padByte != *lastPadByte) |
|
{ |
|
result.DecryptSuccess = FALSE; |
|
return result; |
|
} |
|
} |
|
|
|
// Add size of Version, KmsPIDLen and variable size PID |
|
DWORD pidSize = LE32(((RESPONSE_V6*)response)->ResponseBase.PIDSize); |
|
|
|
copySize1 += |
|
V6_UNENCRYPTED_SIZE + |
|
sizeof(response_v6->ResponseBase.PIDSize) + |
|
(pidSize <= PID_BUFFER_SIZE << 1 ? pidSize : PID_BUFFER_SIZE << 1); |
|
|
|
// Copy part 1 of response up to variable sized PID |
|
memcpy(response_v6, response, copySize1); |
|
|
|
// ensure PID is null terminated |
|
response_v6->ResponseBase.KmsPID[PID_BUFFER_SIZE - 1] = 0; |
|
|
|
// Copy part 2 |
|
size_t copySize2 = v6 ? V6_POST_EPID_SIZE : V5_POST_EPID_SIZE; |
|
memcpy(&response_v6->ResponseBase.CMID, response + copySize1, copySize2); |
|
|
|
// Decrypting the response is finished here. Now we check the results for validity |
|
// A basic client doesn't need the stuff below this comment but we want to use vlmcs |
|
// as a debug tool for KMS emulators. |
|
|
|
REQUEST_V6* request_v6 = (REQUEST_V6*)rawRequest; |
|
DWORD decryptSize = sizeof(request_v6->IV) + sizeof(request_v6->RequestBase) + sizeof(request_v6->Pad); |
|
|
|
AesDecryptCbc(&Ctx, NULL, request_v6->IV, decryptSize); |
|
|
|
// Check that all version informations are the same |
|
result.VersionOK = |
|
request_v6->Version == response_v6->ResponseBase.Version && |
|
request_v6->Version == response_v6->Version && |
|
request_v6->Version == request_v6->RequestBase.Version; |
|
|
|
// Check Base Request |
|
result.PidLengthOK = checkPidLength(&((RESPONSE_V6*)response)->ResponseBase); |
|
result.TimeStampOK = !memcmp(&response_v6->ResponseBase.ClientTime, &request_v6->RequestBase.ClientTime, sizeof(FILETIME)); |
|
result.ClientMachineIDOK = IsEqualGUID(&response_v6->ResponseBase.CMID, &request_v6->RequestBase.CMID); |
|
|
|
// Rebuild Random Key and Sha256 Hash |
|
BYTE HashVerify[sizeof(response_v6->Hash)]; |
|
BYTE RandomKey[sizeof(response_v6->RandomXoredIVs)]; |
|
|
|
memcpy(RandomKey, request_v6->IV, sizeof(RandomKey)); |
|
XorBlock(response_v6->RandomXoredIVs, RandomKey); |
|
Sha256(RandomKey, sizeof(RandomKey), HashVerify); |
|
|
|
result.HashOK = !memcmp(response_v6->Hash, HashVerify, sizeof(HashVerify)); |
|
|
|
// size before encryption (padding not included) |
|
result.correctResponseSize = |
|
(v6 ? sizeof(RESPONSE_V6) : sizeof(RESPONSE_V5)) |
|
- sizeof(response_v6->ResponseBase.KmsPID) |
|
+ LE32(response_v6->ResponseBase.PIDSize); |
|
|
|
// Version specific stuff |
|
if (v6) |
|
{ |
|
// Copy the HwId |
|
memcpy(hwid, response_v6->HwId, sizeof(response_v6->HwId)); |
|
|
|
// Verify the V6 specific part of the response |
|
result = VerifyResponseV6(result, &Ctx, response_v6, request_v6, response); |
|
} |
|
else // V5 |
|
{ |
|
// Verify the V5 specific part of the response |
|
result = VerifyResponseV5(result, request_v6, (RESPONSE_V5*)response_v6); |
|
} |
|
|
|
// padded size after encryption |
|
result.correctResponseSize += (~(result.correctResponseSize - sizeof(response_v6->ResponseBase.Version)) & 0xf) + 1; |
|
|
|
return result; |
|
} |
|
|
|
|