949 lines
26 KiB
C++
949 lines
26 KiB
C++
// Includes and Namespaces
|
|
#include <regex>
|
|
#include <iostream>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include <iphlpapi.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include "Crypto.h"
|
|
#include "Hash.h"
|
|
#include "KMSPID.h"
|
|
#include "KMSHWID.h"
|
|
#include "KMSServer.h"
|
|
#include "KMSServerLogic.h"
|
|
#include "KMSServerSettings.h"
|
|
using namespace std;
|
|
|
|
// Link Libraries
|
|
#pragma comment(lib, "rpcrt4.lib")
|
|
#pragma comment(lib, "iphlpapi.lib")
|
|
#pragma comment(lib, "ws2_32.lib")
|
|
|
|
// Hold KMS Server Settings
|
|
KMSServerSettings ServerSettings;
|
|
|
|
// Windows Service Parameters for Service Application
|
|
SERVICE_STATUS ServerServiceStatus;
|
|
SERVICE_STATUS_HANDLE ServerServiceHandle;
|
|
|
|
// Defined in Hash.cpp
|
|
extern HMAC_KEYBLOB Hmac_keyblob;
|
|
|
|
#pragma region Application Initialization Functions
|
|
// Load Application Parameters from Command-Line or Registry.
|
|
void LoadServerParameters(int argc, wchar_t* argv[], bool RunAsService)
|
|
{
|
|
// Initialize KMS Server Settings Object
|
|
ServerSettings.Initialize();
|
|
ServerSettings.RunAsService = RunAsService;
|
|
|
|
if (!RunAsService)
|
|
{
|
|
#pragma region Command-Line
|
|
// Handle Command-Line Arguments (KMS Port or Help)
|
|
if (argc >= 2)
|
|
{
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"/?", -1) == CSTR_EQUAL)
|
|
{
|
|
printf("Usage:\n");
|
|
printf("[KMS Port] [KMS PID] [KMS HWID] [Activation Interval] [Renewal Interval] [Kill Processes]\n\n");
|
|
|
|
printf("KMS Port:\n");
|
|
printf("\tDefaultPort:\t\tUse the built-in KMS Port.\n");
|
|
printf("\tInteger:\t\tNumber from 1 to 65535.\n\n");
|
|
|
|
printf("KMS PID:\n");
|
|
printf("\tRandomKMSPID:\t\tGenerate a random KMS PID.\n");
|
|
printf("\tString:\t\t\tA Microsoft Extended Product Key ID.\n\n");
|
|
|
|
printf("KMS HWID:\n");
|
|
printf("\tDefaultKMSHWID:\t\tUse the built-in KMS HWID.\n");
|
|
printf("\tString:\t\t\t16 Hex character KMS HWID (No 0x).\n\n");
|
|
|
|
printf("Activation Interval:\n");
|
|
printf("\tDefaultAI:\t\tUse the built-in Activation Interval.\n");
|
|
printf("\tInteger:\t\tNumber (in minutes) from 15 to 43200.\n\n");
|
|
|
|
printf("Renewal Interval:\n");
|
|
printf("\tDefaultRI:\t\tUse the built-in Renewal Interval.\n");
|
|
printf("\tInteger:\t\tNumber (in minutes) from 15 to 43200.\n\n");
|
|
|
|
printf("Kill Processes:\n");
|
|
printf("\tKillProcessOnPort:\tForce open the KMS Port if this is present.\n\n");
|
|
|
|
printf("Example:\n");
|
|
printf("\tC:\\>\"KMS Server.exe\" 1688 RandomKMSPID DefaultKMSHWID 43200 43200 KillProcessOnPort");
|
|
ServerWriteLogErrorTerminate(L"\n", -1);
|
|
}
|
|
else
|
|
{
|
|
ServerValidateKMSPort(argv[1]);
|
|
}
|
|
}
|
|
|
|
// Handle Command-Line Arguments (KMS PID)
|
|
if (argc >= 3)
|
|
{
|
|
ServerValidateKMSPID(argv[2]);
|
|
}
|
|
|
|
// Handle Command-Line Arguments (KMS HWID)
|
|
if (argc >= 4)
|
|
{
|
|
ServerValidateKMSHWID(argv[3]);
|
|
}
|
|
|
|
// Handle Command-Line Arguments (Activation Interval)
|
|
if (argc >= 5)
|
|
{
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, argv[4], -1, L"DefaultAI", -1) != CSTR_EQUAL)
|
|
{
|
|
// Check if Activation Interval is Valid Number
|
|
if (regex_match(argv[4], wregex(L"^([0-9]+)$")))
|
|
{
|
|
ServerValidateVLActivationInterval(_wtoi(argv[4]));
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Activation Interval! %s is not a valid argument.\n", argv[4]);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle Command-Line Arguments (Renewal Interval)
|
|
if (argc >= 6)
|
|
{
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, argv[5], -1, L"DefaultRI", -1) != CSTR_EQUAL)
|
|
{
|
|
// Check if Renewal Interval is Valid Number
|
|
if (regex_match(argv[5], wregex(L"^([0-9]+)$")))
|
|
{
|
|
ServerValidateVLRenewalInterval(_wtoi(argv[5]));
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Renewal Interval! %s is not a valid argument.\n", argv[5]);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle Command-Line Arguments (Free TCP/IP)
|
|
if (argc >= 7)
|
|
{
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, argv[6], -1, L"KillProcessOnPort", -1) == CSTR_EQUAL)
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.KillProcesses = true;
|
|
ServerWriteLogInformation(L"KMS Port Process Termination: Enabled \n");
|
|
}
|
|
else
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.KillProcesses = false;
|
|
ServerWriteLogInformation(L"KMS Port Process Termination: Disabled \n");
|
|
}
|
|
}
|
|
#pragma endregion
|
|
}
|
|
else
|
|
{
|
|
#pragma region Registry
|
|
// Create Buffers for Values
|
|
WCHAR KMSPID[256] = {0};
|
|
WCHAR KMSHWID[256] = {0};
|
|
WCHAR KMSPort[256] = {0};
|
|
DWORD VLActivationInterval = 0;
|
|
DWORD VLRenewalInterval = 0;
|
|
DWORD KillProcesses = 0;
|
|
|
|
// Open Registry Key
|
|
HKEY hKey;
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\KMSServerService\\Parameters", NULL, KEY_READ, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
// Buffer Size
|
|
DWORD bufferSize = 0;
|
|
|
|
// Handle Registry Parameters (KMS PID)
|
|
bufferSize = 256;
|
|
RegQueryValueExW(hKey, L"KMSPID", NULL, NULL, (LPBYTE)KMSPID, &bufferSize);
|
|
|
|
// Handle Registry Parameters (KMS HWID)
|
|
bufferSize = 256;
|
|
RegQueryValueExW(hKey, L"KMSHWID", NULL, NULL, (LPBYTE)KMSHWID, &bufferSize);
|
|
|
|
// Handle Registry Parameters (KMS Port)
|
|
bufferSize = 256;
|
|
RegQueryValueExW(hKey, L"KMSPort", NULL, NULL, (LPBYTE)KMSPort, &bufferSize);
|
|
|
|
// Handle Registry Parameters (Activation Interval)
|
|
bufferSize = sizeof(DWORD);
|
|
RegQueryValueExW(hKey, L"VLActivationInterval", NULL, NULL, (LPBYTE)&VLActivationInterval, &bufferSize);
|
|
|
|
// Handle Registry Parameters (Renewal Interval)
|
|
bufferSize = sizeof(DWORD);
|
|
RegQueryValueExW(hKey, L"VLRenewalInterval", NULL, NULL, (LPBYTE)&VLRenewalInterval, &bufferSize);
|
|
|
|
// Handle Registry Parameters (Free TCP/IP)
|
|
bufferSize = sizeof(DWORD);
|
|
RegQueryValueExW(hKey, L"KillProcessOnPort", NULL, NULL, (LPBYTE)&KillProcesses, &bufferSize);
|
|
|
|
// Close Registry Key
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
// Validate Values
|
|
ServerValidateKMSPort(KMSPort);
|
|
ServerValidateKMSPID(KMSPID);
|
|
ServerValidateKMSHWID(KMSHWID);
|
|
ServerValidateVLActivationInterval(VLActivationInterval);
|
|
ServerValidateVLRenewalInterval(VLRenewalInterval);
|
|
|
|
// Handle External Process Termination
|
|
if (KillProcesses > 0)
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.KillProcesses = true;
|
|
ServerWriteLogInformation(L"KMS Port Process Termination: Enabled \n");
|
|
}
|
|
else
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.KillProcesses = false;
|
|
ServerWriteLogInformation(L"KMS Port Process Termination: Disabled \n");
|
|
}
|
|
|
|
#pragma endregion
|
|
}
|
|
}
|
|
|
|
// Start RPC Connection and listen indefinitely for KMS Clients.
|
|
void StartKMSServer()
|
|
{
|
|
// Uses the protocol combined with the endpoint for receiving remote procedure calls.
|
|
RPC_STATUS status = RpcServerUseProtseqEp
|
|
(
|
|
// Use TCP/IP protocol.
|
|
(RPC_WSTR)L"ncacn_ip_tcp",
|
|
// Backlog queue length for TCP/IP.
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
// TCP/IP port to use.
|
|
(RPC_WSTR)ServerSettings.KMSPort,
|
|
// No security.
|
|
NULL
|
|
);
|
|
|
|
if (status)
|
|
{
|
|
// TCP/IP Port in Use
|
|
if (status == RPC_S_DUPLICATE_ENDPOINT && ServerSettings.KillProcesses)
|
|
{
|
|
// TCP Table and TCP Table Size
|
|
MIB_TCPTABLE_OWNER_PID *pTCPInfo;
|
|
DWORD dwSize = 0;
|
|
|
|
// Get TCP Table Size
|
|
GetExtendedTcpTable(NULL, &dwSize, true, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
|
|
|
|
// Allocate Memory for TCP Table
|
|
pTCPInfo = (MIB_TCPTABLE_OWNER_PID*)malloc(dwSize);
|
|
|
|
// Get TCP Table Data
|
|
GetExtendedTcpTable(pTCPInfo, &dwSize, true, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
|
|
|
|
// Read TCP Table Data
|
|
for (DWORD i = 0; i < pTCPInfo->dwNumEntries; i++)
|
|
{
|
|
// Get Process ID, Opened Port of the Process and KMS Port
|
|
DWORD processID = pTCPInfo->table[i].dwOwningPid;
|
|
DWORD kmsport = _wtoi(ServerSettings.KMSPort);
|
|
DWORD port = ntohs((u_short)pTCPInfo->table[i].dwLocalPort);
|
|
|
|
// Check if this Process has the KMS Port
|
|
if (port == kmsport)
|
|
{
|
|
|
|
// Enable Debug Privileges
|
|
HANDLE tokenHandle;
|
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenHandle);
|
|
TOKEN_PRIVILEGES privilegeToken;
|
|
LookupPrivilegeValue(0, SE_DEBUG_NAME, &privilegeToken.Privileges[0].Luid);
|
|
privilegeToken.PrivilegeCount = 1;
|
|
privilegeToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
AdjustTokenPrivileges(tokenHandle, 0, &privilegeToken, sizeof(TOKEN_PRIVILEGES), 0, 0);
|
|
CloseHandle(tokenHandle);
|
|
|
|
// Kill Process using the KMS Port
|
|
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, processID);
|
|
if (hProcess != NULL)
|
|
{
|
|
TerminateProcess(hProcess, 0);
|
|
CloseHandle(hProcess);
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Failed to get handle to kill Process ID: %i.\n", processID);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Connect on Port Again
|
|
Sleep(5000);
|
|
status = RpcServerUseProtseqEp
|
|
(
|
|
// Use TCP/IP protocol.
|
|
(RPC_WSTR)L"ncacn_ip_tcp",
|
|
// Backlog queue length for TCP/IP.
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
// TCP/IP port to use.
|
|
(RPC_WSTR)ServerSettings.KMSPort,
|
|
// No security.
|
|
NULL
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"RpcServerUseProtseqEp failed with code %i.\n", status);
|
|
ServerWriteLogErrorTerminate(Message, status);
|
|
}
|
|
}
|
|
|
|
// Registers the KMSServer interface.
|
|
status = RpcServerRegisterIfEx
|
|
(
|
|
// Interface to register.
|
|
KMSServer_v1_0_s_ifspec,
|
|
// Use the MIDL generated entry-point vector.
|
|
NULL,
|
|
// Use the MIDL generated entry-point vector.
|
|
NULL,
|
|
// Interface Registration Flags
|
|
RPC_IF_ALLOW_CALLBACKS_WITH_NO_AUTH | RPC_IF_AUTOLISTEN,
|
|
// Maximum Calls
|
|
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
|
|
// No Security Callback Function
|
|
NULL
|
|
);
|
|
|
|
if (status)
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"RpcServerRegisterIfEx failed with code %i.\n", status);
|
|
ServerWriteLogErrorTerminate(Message, status);
|
|
}
|
|
|
|
ServerWriteLogInformation(L"\nKMS Server Emulator started successfully.\n");
|
|
|
|
// Start to listen for remote procedure calls for all registered interfaces.
|
|
// This call will not return until RpcMgmtStopServerListening is called or the program is exited.
|
|
status = RpcServerListen
|
|
(
|
|
// Recommended minimum number of threads.
|
|
1,
|
|
// Recommended maximum number of threads.
|
|
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
|
|
// Start listening now.
|
|
0
|
|
);
|
|
|
|
if (status)
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"RpcServerListen failed with code %i.\n", status);
|
|
ServerWriteLogErrorTerminate(Message, status);
|
|
}
|
|
}
|
|
|
|
// Stop RPC Connection
|
|
void StopKMSServer()
|
|
{
|
|
RpcMgmtStopServerListening(NULL);
|
|
RpcServerUnregisterIf(NULL, NULL, FALSE);
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region RPC Interface Functions
|
|
// RPC Function to Build and Send a KMS Server Response.
|
|
int ActivationResponse(handle_t IDL_handle, int requestSize, unsigned char *request, int *responseSize, unsigned char **response)
|
|
{
|
|
// No Unreferenced Parameter Warnings
|
|
UNREFERENCED_PARAMETER(IDL_handle);
|
|
|
|
// Verify Request Size
|
|
if (requestSize < 92)
|
|
{
|
|
return RPC_S_INVALID_ENDPOINT_FORMAT;
|
|
}
|
|
|
|
// KMS Protocol Version
|
|
if (request[2] == 4 && request[0] == 0)
|
|
{
|
|
// Send Response to KMS Client
|
|
*response = CreateResponseV4((REQUEST_V4 *)request, responseSize);
|
|
|
|
// Log Sent Response
|
|
ServerWriteLogInformation(L"Activation response (KMS V4.0) sent.\n");
|
|
|
|
// Return to RPC Interface
|
|
return RPC_S_OK;
|
|
}
|
|
else if (request[2] == 5 && request[0] == 0)
|
|
{
|
|
// Send Response to KMS Client
|
|
*response = CreateResponseV5((REQUEST_V5 *)request, responseSize);
|
|
|
|
// Log Sent Response
|
|
ServerWriteLogInformation(L"Activation response (KMS V5.0) sent.\n");
|
|
|
|
// Return to RPC Interface
|
|
return RPC_S_OK;
|
|
}
|
|
else if (request[2] == 6 && request[0] == 0)
|
|
{
|
|
// Send Response to KMS Client
|
|
*response = CreateResponseV6((REQUEST_V6 *)request, responseSize);
|
|
|
|
// Log Sent Response
|
|
ServerWriteLogInformation(L"Activation response (KMS V6.0) sent.\n");
|
|
|
|
// Return to RPC Interface
|
|
return RPC_S_OK;
|
|
}
|
|
else
|
|
{
|
|
// Unsupported Protocol
|
|
return RPC_S_INVALID_VERS_OPTION;
|
|
}
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region KMS Server Response Generation Functions
|
|
// Create Base KMS Server Response Object and handle KMS PID and HWID.
|
|
DWORD CreateResponseBase(REQUEST* const Request, PBYTE const Response)
|
|
{
|
|
// Generate KMS PID or use Command Argument
|
|
WCHAR kmsPIDInstance[PID_BUFFER_SIZE] = { 0 };
|
|
|
|
// Try to Get Application Specific KMS PID and HWID if running as KMS Server Service
|
|
if (ServerSettings.RunAsService)
|
|
{
|
|
GetKMSPID(kmsPIDInstance, Request);
|
|
}
|
|
|
|
// No Specific KMS PID or not running as KMS Server Service
|
|
if (!regex_match(kmsPIDInstance, wregex(L"^([0-9]{5})-([0-9]{5})-([0-9]{3})-([0-9]{6})-([0-9]{2})-([0-9]{4,5})-([0-9]{4}).([0-9]{4})-([0-9]{7})$")))
|
|
{
|
|
if (ServerSettings.GenerateRandomKMSPID)
|
|
{
|
|
// Build Random KMS PID
|
|
CreateKMSPID(kmsPIDInstance, Request);
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"\nKMS PID Generated: %s \n", kmsPIDInstance);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
else
|
|
{
|
|
// Use Global KMS PID from Settings
|
|
StringCchCopyW(kmsPIDInstance, PID_BUFFER_SIZE, ServerSettings.KMSPID);
|
|
}
|
|
}
|
|
|
|
// Get KMS PID Length
|
|
DWORD kmsPIDLen = (DWORD)((wcslen(kmsPIDInstance) + 1) * sizeof(WCHAR));
|
|
|
|
// Current pointer
|
|
PBYTE current = Response;
|
|
|
|
// Set KMS Protocol Minor Version & Major Version
|
|
*(PDWORD)current = *(PDWORD)Request;
|
|
current += sizeof(DWORD);
|
|
|
|
// Set KMS PID Length
|
|
*(PDWORD)current = kmsPIDLen;
|
|
current += sizeof(DWORD);
|
|
|
|
// Copy KMS PID
|
|
memcpy(current, kmsPIDInstance, kmsPIDLen);
|
|
current += kmsPIDLen;
|
|
|
|
// Copy Client Machine ID
|
|
memcpy(current, &Request->ClientMachineId, sizeof(GUID));
|
|
current += sizeof(GUID);
|
|
|
|
// Copy KMS Client Request Timestamp
|
|
memcpy(current, &Request->RequestTime, sizeof(FILETIME));
|
|
current += sizeof(FILETIME);
|
|
|
|
// Set KMS Client Count Parameter
|
|
*(PDWORD)current = ServerSettings.CurrentClientCount;
|
|
current += sizeof(DWORD);
|
|
|
|
// Set KMS Activation Interval Parameter
|
|
*(PDWORD)current = ServerSettings.VLActivationInterval;
|
|
current += sizeof(DWORD);
|
|
|
|
// Set KMS Renewal Interval Parameter
|
|
*(PDWORD)current = ServerSettings.VLRenewalInterval;
|
|
current += sizeof(DWORD);
|
|
|
|
// Return ResponseBase Size
|
|
return (DWORD)(current - Response);
|
|
}
|
|
|
|
// Create Hashed KMS Server Response Data for KMS Protocol Version 4.
|
|
PBYTE CreateResponseV4(REQUEST_V4* const Request, int* const responseSize)
|
|
{
|
|
// Allocate KMS Server Response Buffer
|
|
BYTE buffer[MAX_RESPONSE_SIZE] = {0};
|
|
|
|
// Create KMS Server Response Base
|
|
int size = CreateResponseBase(&Request->RequestBase, buffer);
|
|
|
|
// Create Hash
|
|
GetHash(size, buffer, buffer + size);
|
|
|
|
// Create Proper Response with Hash
|
|
*responseSize = size + 16;
|
|
PBYTE response = (PBYTE)midl_user_allocate(*responseSize);
|
|
memcpy(response, buffer, *responseSize);
|
|
|
|
// Return Response
|
|
return response;
|
|
}
|
|
|
|
// Create Encrypted KMS Server Response Data for KMS Protocol Version 5.
|
|
PBYTE CreateResponseV5(REQUEST_V5* const Request, int* const responseSize)
|
|
{
|
|
// Allocate Response Buffer
|
|
BYTE buffer[MAX_RESPONSE_SIZE] = {0};
|
|
|
|
// Current pointer
|
|
PBYTE current = buffer;
|
|
|
|
// Set KMS Protocol Minor Version & Major Version
|
|
*(PDWORD)current = *(PDWORD)Request;
|
|
current += sizeof(DWORD);
|
|
|
|
// Store Response Salt pointer
|
|
PBYTE ResponseSalt = current;
|
|
|
|
// Copy Response Salt
|
|
memcpy(current, Request->Salt, 16);
|
|
current += 16;
|
|
|
|
// AES-128 Decrypt
|
|
DWORD DecryptSize = sizeof(Request->Salt) + sizeof(Request->RequestBase) + sizeof(Request->Pad);
|
|
AESDecryptMessage(Request->Salt, Request->Salt, &DecryptSize);
|
|
|
|
// Store Encrypt start pointer
|
|
PBYTE EncryptStart = current;
|
|
|
|
// Create Response Base Object
|
|
current += CreateResponseBase(&Request->RequestBase, current);
|
|
|
|
// Random Salt
|
|
BYTE RandomSalt[16];
|
|
|
|
// Seed Random Number Generator
|
|
srand(GetTickCount());
|
|
|
|
// Generate a Random Salt Key
|
|
for(int i = 0; i < 16; i++)
|
|
{
|
|
RandomSalt[i] = rand() % 256;
|
|
current[i] = Request->Salt[i] ^ ResponseSalt[i] ^ RandomSalt[i];
|
|
}
|
|
current += 16;
|
|
|
|
// Create Hash
|
|
GetHashSHA256(16, RandomSalt, current);
|
|
current += 32;
|
|
|
|
// Encrypted and Unencrypted size (KMS Protocol Major Version (2) + KMS Protocol Minor Version (2) + Salt (16))
|
|
DWORD UnencryptedSize = 20;
|
|
DWORD EncryptedSize = (DWORD)(current - EncryptStart);
|
|
|
|
// AES-128 Encrypt
|
|
AESEncryptMessage(ResponseSalt, EncryptStart, &EncryptedSize, MAX_RESPONSE_SIZE - UnencryptedSize);
|
|
|
|
// Make proper response with correct size
|
|
*responseSize = EncryptedSize + UnencryptedSize;
|
|
PBYTE response = (PBYTE)midl_user_allocate(*responseSize);
|
|
memcpy(response, buffer, *responseSize);
|
|
|
|
// Return Response
|
|
return response;
|
|
}
|
|
|
|
// Create Encrypted KMS Server Response Data for KMS Protocol Version 6.
|
|
PBYTE CreateResponseV6(REQUEST_V6* const Request, int* const responseSize)
|
|
{
|
|
// V6: AES key
|
|
BYTE AES_V6_KEY[16] = {0xA9, 0x4A, 0x41, 0x95, 0xE2, 0x01, 0x43, 0x2D, 0x9B, 0xCB, 0x46, 0x04, 0x05, 0xD8, 0x4A, 0x21};
|
|
|
|
// Allocate Response Buffer
|
|
BYTE buffer[MAX_RESPONSE_SIZE] = {0};
|
|
|
|
// Current pointer
|
|
PBYTE current = buffer;
|
|
|
|
// Set KMS Protocol Minor Version & Major Version
|
|
*(PDWORD)current = *(PDWORD)Request;
|
|
current += sizeof(DWORD);
|
|
|
|
// Store Response Salt pointer
|
|
PBYTE ResponseSalt = current;
|
|
|
|
// Copy Response Salt
|
|
memcpy(current, Request->Salt, 16);
|
|
current += 16;
|
|
|
|
// AES-128 Decrypt
|
|
DWORD DecryptSize = sizeof(Request->Salt) + sizeof(Request->RequestBase) + sizeof(Request->Pad);
|
|
|
|
// Modded AES-CBC Decrypt
|
|
AesInit(AES_TYPE_128, AES_MODE_CBC, 0x04, AES_V6_KEY, Request->Salt);
|
|
DecryptMessage(DecryptSize, Request->Salt);
|
|
AesClear();
|
|
|
|
// Store Encrypt start pointer
|
|
PBYTE EncryptStart = current;
|
|
|
|
// Create Response Base Object
|
|
current += CreateResponseBase(&Request->RequestBase, current);
|
|
|
|
// Random Salt
|
|
BYTE RandomSalt[16];
|
|
|
|
// Seed Random Number Generator
|
|
srand(GetTickCount());
|
|
|
|
// Generate a Random Salt Key
|
|
for(int i = 0; i < 16; i++)
|
|
{
|
|
RandomSalt[i] = rand() % 256;
|
|
current[i] = Request->Salt[i] ^ ResponseSalt[i] ^ RandomSalt[i];
|
|
}
|
|
current += 16;
|
|
|
|
// Create Hash
|
|
GetHashSHA256(16, RandomSalt, current);
|
|
current += 32;
|
|
|
|
#pragma region KMS HWID
|
|
// Try to Get Application Specific KMS HWID if running as KMS Server Service
|
|
QWORD kmsHWIDInstance = 0x0;
|
|
if (ServerSettings.RunAsService)
|
|
{
|
|
GetKMSHWID(&kmsHWIDInstance, &Request->RequestBase);
|
|
}
|
|
|
|
// No Specific KMS HWID or not running as KMS Server Service
|
|
if (kmsHWIDInstance == 0x0)
|
|
{
|
|
// Use Global KMS PID from Settings
|
|
kmsHWIDInstance = ServerSettings.KMSHWID;
|
|
}
|
|
|
|
// Machine Hardware Hash
|
|
*(PQWORD) current = kmsHWIDInstance;
|
|
current += 8;
|
|
#pragma endregion
|
|
|
|
// HMAC message buffer
|
|
BYTE Hmac_msg[256];
|
|
|
|
// Xor2 and Hmac_msg
|
|
for(int i = 0; i < 16; i++)
|
|
{
|
|
Hmac_msg[i] = current[i] = Request->Salt[i] ^ ResponseSalt[i];
|
|
}
|
|
current += 16;
|
|
|
|
// HMAC size
|
|
DWORD Hmac_size = current - EncryptStart;
|
|
memcpy(Hmac_msg + 16, EncryptStart, Hmac_size);
|
|
Hmac_size += 16;
|
|
|
|
// HMAC-SHA256
|
|
BYTE Hmac_hash[32];
|
|
GetHmacKey(&Request->RequestBase.RequestTime, Hmac_keyblob.rgbKeyData);
|
|
GetHmacSHA256(Hmac_size, Hmac_msg, Hmac_hash);
|
|
memcpy(current, Hmac_hash + 16, 16);
|
|
current += 16;
|
|
|
|
// Encrypted and Unencrypted size (KMS Protocol Major Version (2) + KMS Protocol Minor Version (2) + Salt (16))
|
|
DWORD EncryptSize = (DWORD)(current - EncryptStart);
|
|
|
|
// PKCS5 Padding
|
|
BYTE bPadding =(~EncryptSize + 1) & 0x0f;
|
|
if(bPadding == 0){
|
|
bPadding = 16;
|
|
}
|
|
|
|
memset(current, bPadding, bPadding);
|
|
EncryptSize += bPadding;
|
|
|
|
// Modded AES-CBC Encrypt
|
|
AesInit(AES_TYPE_128, AES_MODE_CBC, bPadding, AES_V6_KEY, ResponseSalt);
|
|
EncryptMessage(EncryptSize, EncryptStart);
|
|
AesClear();
|
|
|
|
// Make proper response with correct size
|
|
*responseSize = EncryptSize + 20;
|
|
PBYTE response = (PBYTE)midl_user_allocate(*responseSize);
|
|
memcpy(response, buffer, *responseSize);
|
|
|
|
// Return Response
|
|
return response;
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region Parameter Validation Functions
|
|
// Validate KMS Port Parameter
|
|
void ServerValidateKMSPort(WCHAR* KMSPort)
|
|
{
|
|
// Check if the KMS Port is not the Default
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, KMSPort, -1, L"DefaultPort", -1) != CSTR_EQUAL)
|
|
{
|
|
// Check if KMS Port is Valid Number
|
|
if (regex_match(KMSPort, wregex(L"^([0-9]+)$")))
|
|
{
|
|
// Check if KMS Port is Valid Range
|
|
if(_wtoi(KMSPort) < 1 || _wtoi(KMSPort) > 65535)
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Port Range! %s is not a valid argument.\n", KMSPort);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
else
|
|
{
|
|
// Apply Setting
|
|
StringCchCopyW(ServerSettings.KMSPort, PORT_BUFFER_SIZE, KMSPort);
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"KMS Port: %s \n", KMSPort);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Port! %s is not a valid argument.\n", KMSPort);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate KMS PID Parameter
|
|
void ServerValidateKMSPID(WCHAR* KMSPID)
|
|
{
|
|
// Check if KMS PID should be Random
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, KMSPID, -1, L"RandomKMSPID", -1) == CSTR_EQUAL)
|
|
{
|
|
ServerSettings.GenerateRandomKMSPID = true;
|
|
}
|
|
// Check if KMS PID is Valid
|
|
else if (regex_match(KMSPID, wregex(L"^([0-9]{5})-([0-9]{5})-([0-9]{3})-([0-9]{6})-([0-9]{2})-([0-9]{4,5})-([0-9]{4}).([0-9]{4})-([0-9]{7})$")))
|
|
{
|
|
// Apply Setting
|
|
StringCchCopyW(ServerSettings.KMSPID, PID_BUFFER_SIZE, KMSPID);
|
|
|
|
// Disable Random KMSPID Generation
|
|
ServerSettings.GenerateRandomKMSPID = false;
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"KMS PID: %s \n", KMSPID);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS PID! %s is not a valid argument.\n", KMSPID);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
}
|
|
|
|
// Validate KMS HWID Parameter
|
|
void ServerValidateKMSHWID(WCHAR* KMSHWID)
|
|
{
|
|
// Check if the KMS Port is not the Default
|
|
if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, KMSHWID, -1, L"DefaultKMSHWID", -1) != CSTR_EQUAL)
|
|
{
|
|
// Check if KMS HWID is Valid Hex String
|
|
if (regex_match(KMSHWID, wregex(L"^[a-fA-F0-9]{16}$")))
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.KMSHWID = wcstoull(KMSHWID, NULL, 16);
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"KMS HWID: %s \n", KMSHWID);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
else
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Hardware ID! %s is not a valid argument.\n", KMSHWID);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate KMS Activation Interval Parameter
|
|
void ServerValidateVLActivationInterval(int VLActivationInterval)
|
|
{
|
|
// Check if KMS Activation Interval is Valid Range
|
|
if (VLActivationInterval < 15 || VLActivationInterval > 43200)
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Activation Interval Range! %i is not a valid argument.\n", VLActivationInterval);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
else
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.VLActivationInterval = VLActivationInterval;
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"KMS Activation Interval: %i minutes \n", VLActivationInterval);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
}
|
|
|
|
// Validate KMS Activation Interval Parameter
|
|
void ServerValidateVLRenewalInterval(int VLRenewalInterval)
|
|
{
|
|
// Check if KMS Renewal Interval is Valid Range
|
|
if (VLRenewalInterval < 15 || VLRenewalInterval > 43200)
|
|
{
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"Invalid KMS Renewal Interval Range! %i is not a valid argument.\n", VLRenewalInterval);
|
|
ServerWriteLogErrorTerminate(Message, -1);
|
|
}
|
|
else
|
|
{
|
|
// Apply Setting
|
|
ServerSettings.VLRenewalInterval = VLRenewalInterval;
|
|
|
|
// Format Log Message
|
|
WCHAR Message[256];
|
|
swprintf_s(Message, 256, L"KMS Renewal Interval: %i minutes \n", VLRenewalInterval);
|
|
ServerWriteLogInformation(Message);
|
|
}
|
|
}
|
|
#pragma endregion
|
|
|
|
#pragma region Logging Functions
|
|
// Log an informational message to the Console or Windows Application Event Log.
|
|
void ServerWriteLogInformation(WCHAR* const Message)
|
|
{
|
|
// Log Message Buffer
|
|
WCHAR logMessage[256];
|
|
|
|
// Write Log Message to Buffer
|
|
StringCbPrintfW(logMessage, 256, Message);
|
|
|
|
// Choose Log Source
|
|
if (ServerSettings.RunAsService)
|
|
{
|
|
// Output to Windows Event Log: Qualifiers: 16384, EventId: 12290
|
|
ServerWriteEventLogEntry(0x40003002, logMessage, EVENTLOG_INFORMATION_TYPE);
|
|
}
|
|
else
|
|
{
|
|
// Output to Console
|
|
wprintf(L"%s", logMessage);
|
|
}
|
|
}
|
|
|
|
// Log an error message to the Console or Windows Application Event Log.
|
|
void ServerWriteLogError(WCHAR* const Message)
|
|
{
|
|
// Log Message Buffer
|
|
WCHAR logMessage[256];
|
|
|
|
// Write Log Message to Buffer
|
|
StringCbPrintfW(logMessage, 256, Message);
|
|
|
|
// Choose Log Source
|
|
if (ServerSettings.RunAsService)
|
|
{
|
|
// Output to Windows Event Log: Qualifiers: 16384, EventId: 902
|
|
ServerWriteEventLogEntry(0x40000386, logMessage, EVENTLOG_ERROR_TYPE);
|
|
}
|
|
else
|
|
{
|
|
// Output to Console
|
|
wprintf(L"%s", logMessage);
|
|
}
|
|
}
|
|
|
|
// Log an error message to the Console or Windows Application Event Log and terminate program.
|
|
void ServerWriteLogErrorTerminate(WCHAR* const Message, int ExitCode)
|
|
{
|
|
// Call ServerWriteLogError
|
|
ServerWriteLogError(Message);
|
|
|
|
if (!ServerSettings.RunAsService)
|
|
{
|
|
// Exit Program
|
|
exit(ExitCode);
|
|
}
|
|
else
|
|
{
|
|
// Exit Service
|
|
ServerServiceStatus.dwCurrentState = SERVICE_STOPPED;
|
|
ServerServiceStatus.dwWin32ExitCode = ExitCode;
|
|
SetServiceStatus(ServerServiceHandle, &ServerServiceStatus);
|
|
}
|
|
}
|
|
|
|
// Log a message to the Windows Application Event Log.
|
|
void ServerWriteEventLogEntry(DWORD dwIdentifier, WCHAR* const lpszMessage, WORD wType)
|
|
{
|
|
HANDLE hEventSource = OpenEventLogW(NULL, L"KmsRequests"); // Provider: KmsRequests or Microsoft-Windows-Security-SPP
|
|
|
|
if(hEventSource != NULL)
|
|
{
|
|
ReportEventW(
|
|
hEventSource, // Event log handle
|
|
wType, // Event type
|
|
NULL, // Event category
|
|
dwIdentifier, // Event identifier
|
|
NULL, // No security identifier
|
|
1, // Size of lpszStrings array
|
|
NULL, // No binary data
|
|
(LPCWSTR*)&lpszMessage, // Array of strings
|
|
NULL // No binary data
|
|
);
|
|
|
|
CloseEventLog(hEventSource);
|
|
}
|
|
}
|
|
#pragma endregion |