aboutsummaryrefslogtreecommitdiff
path: root/runtime/licensing_manager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/licensing_manager.cc')
-rw-r--r--runtime/licensing_manager.cc1135
1 files changed, 1135 insertions, 0 deletions
diff --git a/runtime/licensing_manager.cc b/runtime/licensing_manager.cc
new file mode 100644
index 0000000..bc1039f
--- /dev/null
+++ b/runtime/licensing_manager.cc
@@ -0,0 +1,1135 @@
+#include "common.h"
+#include "objects.h"
+#include "utils.h"
+#include "core.h"
+#include "crypto.h"
+#include "licensing_manager.h"
+#include "hwid.h"
+#include "loader.h"
+
+#if defined(__unix__)
+#include <sys/time.h>
+#include <curl/curl.h>
+#endif
+
+/**
+ * exported functions
+ */
+
+#ifdef VMP_GNU
+EXPORT_API int WINAPI ExportedSetSerialNumber(const char *serial) __asm__ ("ExportedSetSerialNumber");
+EXPORT_API int WINAPI ExportedGetSerialNumberState() __asm__ ("ExportedGetSerialNumberState");
+EXPORT_API bool WINAPI ExportedGetSerialNumberData(VMProtectSerialNumberData *data, int size) __asm__ ("ExportedGetSerialNumberData");
+EXPORT_API int WINAPI ExportedActivateLicense(const char *code, char *serial, int size) __asm__ ("ExportedActivateLicense");
+EXPORT_API int WINAPI ExportedDeactivateLicense(const char *serial) __asm__ ("ExportedDeactivateLicense");
+EXPORT_API int WINAPI ExportedGetOfflineActivationString(const char *code, char *buf, int size) __asm__ ("ExportedGetOfflineActivationString");
+EXPORT_API int WINAPI ExportedGetOfflineDeactivationString(const char *serial, char *buf, int size) __asm__ ("ExportedGetOfflineDeactivationString");
+EXPORT_API void WINAPI ExportedDecryptBuffer(uint8_t *buffer) __asm__ ("ExportedDecryptBuffer");
+#elif defined(USE_WININET)
+ #include <wininet.h>
+#else
+ #ifndef WIN_DRIVER
+ #include <winhttp.h>
+ #include <xstring>
+ #endif
+#endif
+
+int WINAPI ExportedSetSerialNumber(const char *serial)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->SetSerialNumber(serial) : SERIAL_STATE_FLAG_CORRUPTED;
+}
+
+int WINAPI ExportedGetSerialNumberState()
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->GetSerialNumberState() : SERIAL_STATE_FLAG_CORRUPTED;
+}
+
+bool WINAPI ExportedGetSerialNumberData(VMProtectSerialNumberData *data, int size)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->GetSerialNumberData(data, size) : false;
+}
+
+int WINAPI ExportedActivateLicense(const char *code, char *serial, int size)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->ActivateLicense(code, serial, size) : ACTIVATION_NOT_AVAILABLE;
+}
+
+int WINAPI ExportedDeactivateLicense(const char *serial)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->DeactivateLicense(serial) : ACTIVATION_NOT_AVAILABLE;
+}
+
+int WINAPI ExportedGetOfflineActivationString(const char *code, char *buf, int size)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->GetOfflineActivationString(code, buf, size) : ACTIVATION_NOT_AVAILABLE;
+}
+
+int WINAPI ExportedGetOfflineDeactivationString(const char *serial, char *buf, int size)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ return licensing_manager ? licensing_manager->GetOfflineDeactivationString(serial, buf, size) : ACTIVATION_NOT_AVAILABLE;
+}
+
+void WINAPI ExportedDecryptBuffer(uint8_t *buffer)
+{
+ LicensingManager *licensing_manager = Core::Instance()->licensing_manager();
+ if (licensing_manager)
+ licensing_manager->DecryptBuffer(buffer);
+}
+
+/**
+ * LicensingManager
+ */
+
+#ifdef __APPLE__
+
+#include "CFGregorianDateCreate.hpp"
+
+uint32_t GetTickCount()
+{
+ const int64_t one_million = 1000 * 1000;
+ mach_timebase_info_data_t timebase_info;
+ mach_timebase_info(&timebase_info);
+
+ // mach_absolute_time() returns billionth of seconds,
+ // so divide by one million to get milliseconds
+ return static_cast<uint32_t>((mach_absolute_time() * timebase_info.numer) / (one_million * timebase_info.denom));
+}
+#elif defined(WIN_DRIVER)
+uint32_t GetTickCount()
+{
+ LARGE_INTEGER tick_count;
+ KeQueryTickCount(&tick_count);
+ return static_cast<uint32_t>(tick_count.QuadPart * KeQueryTimeIncrement() / 10000);
+}
+#endif
+#ifdef __unix__
+unsigned long GetTickCount()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+#endif
+LicensingManager::LicensingManager(uint8_t *data, uint32_t size, uint8_t *key)
+ : start_(0), serial_(NULL)
+{
+ CriticalSection::Init(critical_section_);
+ session_key_ = 0 - static_cast<uint32_t>(loader_data->session_key());
+ license_data_ = new CryptoContainer(data, size, key);
+ start_tick_count_ = GetTickCount();
+ save_state(SERIAL_STATE_FLAG_INVALID);
+}
+
+LicensingManager::~LicensingManager()
+{
+ delete license_data_;
+ delete serial_;
+ CriticalSection::Free(critical_section_);
+}
+
+int LicensingManager::save_state(int new_state)
+{
+ if (new_state & (SERIAL_STATE_FLAG_CORRUPTED | SERIAL_STATE_FLAG_INVALID | SERIAL_STATE_FLAG_BLACKLISTED | SERIAL_STATE_FLAG_BAD_HWID)) {
+ product_code_ = 0;
+ if (serial_) {
+ delete serial_;
+ serial_ = NULL;
+ }
+ } else if (new_state & (SERIAL_STATE_FLAG_DATE_EXPIRED | SERIAL_STATE_FLAG_RUNNING_TIME_OVER | SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED)) {
+ if (state_ == SERIAL_STATE_FLAG_INVALID)
+ product_code_ = 0;
+ }
+ state_ = new_state;
+ return state_;
+}
+
+bool LicensingManager::CheckLicenseDataCRC() const
+{
+ size_t crc_pos = license_data_->GetDWord(FIELD_CRC_OFFSET * sizeof(uint32_t));
+ size_t size = crc_pos + 16;
+ if (size != license_data_->size())
+ return false; // bad key size
+
+ // CRC check
+ SHA1 hash;
+ hash.Input(license_data_->data(), crc_pos);
+ const uint8_t *p = hash.Result();
+ for (size_t i = crc_pos; i < size; i++) {
+ if (license_data_->GetByte(i) != p[i - crc_pos])
+ return false;
+ }
+
+ return true;
+}
+
+int LicensingManager::SetSerialNumber(const char *serial)
+{
+ CriticalSection cs(critical_section_);
+
+ save_state(SERIAL_STATE_FLAG_INVALID);
+
+ if (!serial)
+ return SERIAL_STATE_FLAG_INVALID; // the key is empty
+
+ size_t len = 0;
+ while (serial[len]) {
+ len++;
+ }
+ if (!len)
+ return SERIAL_STATE_FLAG_INVALID; // the key is empty
+
+ // decode serial number from base64
+ uint8_t *binary_serial = new uint8_t[len];
+ if (!Base64Decode(serial, len, binary_serial, len) || len < 16) {
+ delete [] binary_serial;
+ return SERIAL_STATE_FLAG_INVALID;
+ }
+
+ // check license data integrity
+ if (!CheckLicenseDataCRC()) {
+ delete [] binary_serial;
+ return save_state(SERIAL_STATE_FLAG_CORRUPTED);
+ }
+
+ // check serial by black list
+ size_t black_list_size = license_data_->GetDWord(FIELD_BLACKLIST_SIZE * sizeof(uint32_t));
+ if (black_list_size) {
+ size_t black_list_offset = license_data_->GetDWord(FIELD_BLACKLIST_OFFSET * sizeof(uint32_t));
+
+ SHA1 hash;
+ hash.Input(binary_serial, len);
+ const uint32_t *p = reinterpret_cast<const uint32_t *>(hash.Result());
+ int min = 0;
+ int max = (int)black_list_size / 20 - 1;
+ while (min <= max) {
+ int i = (min + max) / 2;
+ bool blocked = true;
+ for (size_t j = 0; j < 20 / sizeof(uint32_t); j++) {
+ uint32_t dw = license_data_->GetDWord(black_list_offset + i * 20 + j * sizeof(uint32_t));
+ if (dw == p[j])
+ continue;
+
+ if (__builtin_bswap32(dw) > __builtin_bswap32(p[j])) {
+ max = i - 1;
+ } else {
+ min = i + 1;
+ }
+ blocked = false;
+ break;
+ }
+ if (blocked) {
+ delete[] binary_serial;
+ return save_state(SERIAL_STATE_FLAG_BLACKLISTED);
+ }
+ }
+ }
+
+ // decode serial number
+ BigNumber x(binary_serial, len);
+ delete [] binary_serial;
+
+ serial_ = x.modpow(*license_data_,
+ license_data_->GetDWord(FIELD_PUBLIC_EXP_OFFSET * sizeof(uint32_t)), license_data_->GetDWord(FIELD_PUBLIC_EXP_SIZE * sizeof(uint32_t)),
+ license_data_->GetDWord(FIELD_MODULUS_OFFSET * sizeof(uint32_t)), license_data_->GetDWord(FIELD_MODULUS_SIZE * sizeof(uint32_t)));
+
+ if (!serial_)
+ return SERIAL_STATE_FLAG_INVALID;
+
+ if (serial_->GetByte(0) != 0 || serial_->GetByte(1) != 2)
+ return SERIAL_STATE_FLAG_INVALID;
+
+ size_t pos;
+ for (pos = 2; pos < serial_->size(); pos++) {
+ if (!serial_->GetByte(pos)) {
+ pos++;
+ break;
+ }
+ }
+ if (pos == serial_->size())
+ return SERIAL_STATE_FLAG_INVALID;
+
+ start_ = pos;
+ return ParseSerial(NULL);
+}
+
+uint32_t LicensingManager::GetCurrentDate()
+{
+ uint32_t cur_date;
+#ifdef VMP_GNU
+ time_t rawtime;
+ time(&rawtime);
+ struct tm local_tm;
+ tm *timeinfo = localtime_r(&rawtime, &local_tm);
+ cur_date = ((timeinfo->tm_year + 1900) << 16) + (static_cast<uint8_t>(timeinfo->tm_mon + 1) << 8) + static_cast<uint8_t>(timeinfo->tm_mday);
+#elif defined(WIN_DRIVER)
+ LARGE_INTEGER sys_time;
+ LARGE_INTEGER local_time;
+ TIME_FIELDS time_fields;
+
+ KeQuerySystemTime(&sys_time);
+ ExSystemTimeToLocalTime(&sys_time, &local_time);
+ RtlTimeToTimeFields(&local_time, &time_fields);
+ cur_date = (time_fields.Year << 16) + (static_cast<uint8_t>(time_fields.Month) << 8) + static_cast<uint8_t>(time_fields.Day);
+#else
+ typedef struct _KSYSTEM_TIME
+ {
+ ULONG LowPart;
+ LONG High1Time;
+ LONG High2Time;
+ } KSYSTEM_TIME, *PKSYSTEM_TIME;
+
+ typedef struct _KUSER_SHARED_DATA
+ {
+ ULONG TickCountLowDeprecated;
+ ULONG TickCountMultiplier;
+ KSYSTEM_TIME InterruptTime;
+ KSYSTEM_TIME SystemTime;
+ KSYSTEM_TIME TimeZoneBias;
+ //...
+ } KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;
+
+ PKUSER_SHARED_DATA user_shared_data = reinterpret_cast<PKUSER_SHARED_DATA>(0x7FFE0000);
+ LARGE_INTEGER sys_time, time_zone_bias, local_time;
+ while (true) {
+ sys_time.HighPart = user_shared_data->SystemTime.High1Time;
+ sys_time.LowPart = user_shared_data->SystemTime.LowPart;
+ if (sys_time.HighPart == user_shared_data->SystemTime.High2Time)
+ break;
+ }
+ while (true) {
+ time_zone_bias.HighPart = user_shared_data->TimeZoneBias.High1Time;
+ time_zone_bias.LowPart = user_shared_data->TimeZoneBias.LowPart;
+ if (time_zone_bias.HighPart == user_shared_data->TimeZoneBias.High2Time)
+ break;
+ }
+ local_time.QuadPart = sys_time.QuadPart - time_zone_bias.QuadPart;
+
+ __int64 total_days_since_1601 = local_time.QuadPart / 864000000000ull;
+ uint32_t number_of_400s = static_cast<uint32_t>(total_days_since_1601 / 146097);
+ total_days_since_1601 -= number_of_400s * 146097;
+ uint32_t number_of_100s = static_cast<uint32_t>((total_days_since_1601 * 100 + 75) / 3652425);
+ total_days_since_1601 -= number_of_100s * 36524;
+ uint32_t number_of_4s = static_cast<uint32_t>(total_days_since_1601 / 1461);
+ total_days_since_1601 -= number_of_4s * 1461;
+ uint16_t year = static_cast<uint16_t>((total_days_since_1601 * 100 + 75) / 36525);
+ total_days_since_1601 -= 365 * year;
+ year = static_cast<uint16_t>((number_of_400s * 400) + (number_of_100s * 100) + (number_of_4s * 4) + year + 1601);
+ uint16_t day = static_cast<uint16_t>(total_days_since_1601 + 1);
+ int days_in_month[] = {31, ((year % 400 == 0) || (year % 100 != 0) && (year % 4 == 0)) ? 29 : 28,
+ 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ uint8_t month = 1;
+ for (size_t i = 0; i < _countof(days_in_month); i++) {
+ if (day > days_in_month[i]) {
+ ++month;
+ day -= days_in_month[i];
+ } else
+ break;
+ }
+ assert(month <= 12);
+ cur_date = (year << 16) + (static_cast<uint8_t>(month) << 8) + static_cast<uint8_t>(day);
+#endif
+
+ return std::max(cur_date, loader_data->server_date());
+}
+
+int LicensingManager::ParseSerial(VMProtectSerialNumberData *data)
+{
+ if (!serial_)
+ return SERIAL_STATE_FLAG_INVALID;
+
+ int new_state = state_ & (SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED | SERIAL_STATE_FLAG_DATE_EXPIRED | SERIAL_STATE_FLAG_RUNNING_TIME_OVER);
+ size_t pos = start_;
+ while (pos < serial_->size()) {
+ uint8_t b = serial_->GetByte(pos++);
+ uint8_t s;
+ switch (b) {
+ case SERIAL_CHUNK_VERSION:
+ if (serial_->GetByte(pos) != 1)
+ return save_state(SERIAL_STATE_FLAG_INVALID);
+ pos += 1;
+ break;
+ case SERIAL_CHUNK_EXP_DATE:
+ uint32_t exp_date;
+ exp_date = serial_->GetDWord(pos);
+ if ((new_state & SERIAL_STATE_FLAG_DATE_EXPIRED) == 0) {
+ if (license_data_->GetDWord(FIELD_BUILD_DATE * sizeof(uint32_t)) > exp_date || GetCurrentDate() > exp_date)
+ new_state |= SERIAL_STATE_FLAG_DATE_EXPIRED;
+ }
+ if (data) {
+ data->dtExpire.wYear = exp_date >> 16;
+ data->dtExpire.bMonth = static_cast<uint8_t>(exp_date >> 8);
+ data->dtExpire.bDay = static_cast<uint8_t>(exp_date);
+ }
+ pos += 4;
+ break;
+ case SERIAL_CHUNK_RUNNING_TIME_LIMIT:
+ s = serial_->GetByte(pos);
+ if ((new_state & SERIAL_STATE_FLAG_RUNNING_TIME_OVER) == 0) {
+ uint32_t tick_count = GetTickCount();
+ size_t cur_time = (tick_count - start_tick_count_) / 1000 / 60;
+ if (cur_time > s)
+ new_state |= SERIAL_STATE_FLAG_RUNNING_TIME_OVER;
+ }
+ if (data)
+ data->bRunningTime = s;
+ pos += 1;
+ break;
+ case SERIAL_CHUNK_PRODUCT_CODE:
+ if (state_ == SERIAL_STATE_FLAG_INVALID)
+ product_code_ = serial_->GetQWord(pos);
+ pos += 8;
+ break;
+ case SERIAL_CHUNK_MAX_BUILD:
+ uint32_t max_build_date;
+ max_build_date = serial_->GetDWord(pos);
+ if ((new_state & SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED) == 0) {
+ if (license_data_->GetDWord(FIELD_BUILD_DATE * sizeof(uint32_t)) > max_build_date)
+ new_state |= SERIAL_STATE_FLAG_MAX_BUILD_EXPIRED;
+ }
+ if (data) {
+ data->dtMaxBuild.wYear = max_build_date >> 16;
+ data->dtMaxBuild.bMonth = static_cast<uint8_t>(max_build_date >> 8);
+ data->dtMaxBuild.bDay = static_cast<uint8_t>(max_build_date);
+ }
+ pos += 4;
+ break;
+ case SERIAL_CHUNK_USER_NAME:
+ s = serial_->GetByte(pos++);
+ if (data)
+ serial_->UTF8ToUnicode(pos, s, data->wUserName, _countof(data->wUserName));
+ pos += s;
+ break;
+ case SERIAL_CHUNK_EMAIL:
+ s = serial_->GetByte(pos++);
+ if (data)
+ serial_->UTF8ToUnicode(pos, s, data->wEMail, _countof(data->wEMail));
+ pos += s;
+ break;
+ case SERIAL_CHUNK_HWID:
+ s = serial_->GetByte(pos++);
+ if (state_ == SERIAL_STATE_FLAG_INVALID) {
+ HardwareID *hardware_id = Core::Instance()->hardware_id();
+ if (!hardware_id->IsCorrect(*serial_, pos, s))
+ return save_state(SERIAL_STATE_FLAG_BAD_HWID);
+ }
+ pos += s;
+ break;
+ case SERIAL_CHUNK_USER_DATA:
+ s = serial_->GetByte(pos++);
+ if (data) {
+ data->nUserDataLength = static_cast<uint8_t>(s);
+ for (size_t i = 0; i < s; i++) {
+ data->bUserData[i] = serial_->GetByte(pos + i);
+ }
+ }
+ pos += s;
+ break;
+ case SERIAL_CHUNK_END:
+ if (pos + 4 > serial_->size())
+ return save_state(SERIAL_STATE_FLAG_INVALID);
+
+ if (state_ == SERIAL_STATE_FLAG_INVALID) {
+ // calc hash without last chunk
+ SHA1 hash;
+ hash.Input(*serial_, start_, pos - start_ - 1);
+
+ // check CRC
+ const uint8_t *p = hash.Result();
+ for (size_t i = 0; i < 4; i++) {
+ if (serial_->GetByte(pos + i) != p[3 - i])
+ return save_state(SERIAL_STATE_FLAG_INVALID);
+ }
+ }
+
+ return save_state(new_state);
+ }
+ }
+
+ // SERIAL_CHUNK_END not found
+ return save_state(SERIAL_STATE_FLAG_INVALID);
+}
+
+int LicensingManager::GetSerialNumberState()
+{
+ CriticalSection cs(critical_section_);
+
+ if (state_ & (SERIAL_STATE_FLAG_CORRUPTED | SERIAL_STATE_FLAG_INVALID | SERIAL_STATE_FLAG_BLACKLISTED | SERIAL_STATE_FLAG_BAD_HWID))
+ return state_; // no reasons to continue
+
+ return ParseSerial(NULL);
+}
+
+bool LicensingManager::GetSerialNumberData(VMProtectSerialNumberData *data, int size)
+{
+ if (!data || size != sizeof(VMProtectSerialNumberData))
+ return false; // bad input
+
+ CriticalSection cs(critical_section_);
+
+ if (state_ == SERIAL_STATE_FLAG_CORRUPTED)
+ return false;
+
+ // clean memory
+ uint8_t *p = reinterpret_cast<uint8_t *>(data);
+ for (int i = 0; i < size; i++) {
+ p[i] = 0;
+ }
+
+ data->nState = (state_ & (SERIAL_STATE_FLAG_INVALID | SERIAL_STATE_FLAG_BLACKLISTED | SERIAL_STATE_FLAG_BAD_HWID)) ? state_ : ParseSerial(data);
+ return true;
+}
+
+int LicensingManager::ActivateLicense(const char *code, char *serial, int size) const
+{
+#ifdef WIN_DRIVER
+ return ACTIVATION_NOT_AVAILABLE;
+#else
+ if (!CheckLicenseDataCRC())
+ return ACTIVATION_CORRUPTED;
+
+ ActivationRequest request;
+ int res = request.Process(*license_data_, code, false);
+ if (res == ACTIVATION_OK) {
+ int need = static_cast<int>(strlen(request.serial()));
+ if (need > size - 1)
+ return ACTIVATION_SMALL_BUFFER;
+ strncpy_s(serial, size, request.serial(), need);
+ }
+ return res;
+#endif
+}
+
+int LicensingManager::DeactivateLicense(const char *serial) const
+{
+#ifdef WIN_DRIVER
+ return ACTIVATION_NOT_AVAILABLE;
+#else
+ if (!CheckLicenseDataCRC())
+ return ACTIVATION_CORRUPTED;
+
+ DeactivationRequest request;
+ return request.Process(*license_data_, serial, false);
+#endif
+}
+
+int LicensingManager::GetOfflineActivationString(const char *code, char *buf, int size) const
+{
+#ifdef WIN_DRIVER
+ return ACTIVATION_NOT_AVAILABLE;
+#else
+ if (!buf || size <= 0)
+ return ACTIVATION_SMALL_BUFFER;
+
+ if (!CheckLicenseDataCRC())
+ return ACTIVATION_CORRUPTED;
+
+ ActivationRequest request;
+ int res = request.Process(*license_data_, code, true);
+ if (res == ACTIVATION_OK) {
+ int need = static_cast<int>(strlen(request.url()));
+ if (need > size - 1)
+ return ACTIVATION_SMALL_BUFFER;
+ strncpy_s(buf, size, request.url(), need);
+ }
+ return res;
+#endif
+}
+
+int LicensingManager::GetOfflineDeactivationString(const char *serial, char *buf, int size) const
+{
+#ifdef WIN_DRIVER
+ return ACTIVATION_NOT_AVAILABLE;
+#else
+ if (!buf || size <= 0)
+ return ACTIVATION_SMALL_BUFFER;
+
+ if (!CheckLicenseDataCRC())
+ return ACTIVATION_CORRUPTED;
+
+ DeactivationRequest request;
+ int res = request.Process(*license_data_, serial, true);
+ if (res == ACTIVATION_OK) {
+ int need = static_cast<int>(strlen(request.url()));
+ if (need > size - 1)
+ return ACTIVATION_SMALL_BUFFER;
+ strncpy_s(buf, size, request.url(), need);
+ }
+ return res;
+#endif
+}
+
+void LicensingManager::DecryptBuffer(uint8_t *buffer)
+{
+ uint32_t key0 = static_cast<uint32_t>(product_code_);
+ uint32_t key1 = static_cast<uint32_t>(product_code_ >> 32) + session_key_;
+ uint32_t *p = reinterpret_cast<uint32_t*>(buffer);
+
+ p[0] = _rotl32((p[0] + session_key_) ^ key0, 7) + key1;
+ p[1] = _rotl32((p[1] + session_key_) ^ key0, 11) + key1;
+ p[2] = _rotl32((p[2] + session_key_) ^ key0, 17) + key1;
+ p[3] = _rotl32((p[3] + session_key_) ^ key0, 23) + key1;
+
+ if (p[0] + p[1] + p[2] + p[3] != session_key_ * 4) {
+ const VMP_CHAR *message;
+#ifdef VMP_GNU
+ message = VMProtectDecryptStringA(MESSAGE_SERIAL_NUMBER_REQUIRED_STR);
+#else
+ message = VMProtectDecryptStringW(MESSAGE_SERIAL_NUMBER_REQUIRED_STR);
+#endif
+ if (message[0])
+ ShowMessage(message);
+
+#if defined(VMP_GNU)
+ exit(0xDEADC0DE);
+#elif defined(WIN_DRIVER)
+ DbgBreakPointWithStatus(0xDEADC0DE);
+#else
+ TerminateProcess(GetCurrentProcess(), 0xDEADC0DE);
+#endif
+ }
+}
+
+#ifndef WIN_DRIVER
+
+/**
+ * BaseRequest
+ */
+
+BaseRequest::BaseRequest()
+ : response_(NULL)
+{
+ url_[0] = 0;
+}
+
+BaseRequest::~BaseRequest()
+{
+ delete [] response_;
+}
+
+bool BaseRequest::BuildUrl(const CryptoContainer &license_data)
+{
+ size_t url_size = license_data.GetDWord(FIELD_ACTIVATION_URL_SIZE * sizeof(uint32_t));
+ if (!url_size)
+ return false;
+
+ size_t url_offset = license_data.GetDWord(FIELD_ACTIVATION_URL_OFFSET * sizeof(uint32_t));
+ for (size_t i = 0; i < url_size; i++) {
+ url_[i] = license_data.GetByte(url_offset + i);
+ }
+ if (url_[url_size] != '/')
+ url_[url_size++] = '/';
+ url_[url_size] = 0;
+ return true;
+}
+
+#ifdef __unix__
+static size_t curl_write_data(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ size_t written = size * nmemb;
+
+ std::string *dest = (std::string *)stream;
+ *dest += std::string((char *)ptr, written);
+ return written;
+}
+static size_t curl_header(void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ size_t written = size * nmemb;
+
+ if(strncmp((char*)ptr, "Date: ", 6) == 0)
+ {
+ *((time_t *)stream) = curl_getdate((char *)ptr + 6, NULL);
+ }
+ return written;
+}
+#endif
+
+bool BaseRequest::Send()
+{
+ if (response_) {
+ delete [] response_;
+ response_ = NULL;
+ }
+
+#ifdef __APPLE__
+ CFStringRef str_ref = CFStringCreateWithCString(NULL, url_, kCFStringEncodingMacRoman);
+ CFURLRef url_ref = CFURLCreateWithString(kCFAllocatorDefault, str_ref, NULL);
+ CFHTTPMessageRef req_ref = CFHTTPMessageCreateRequest(kCFAllocatorDefault, CFSTR("GET"), url_ref, kCFHTTPVersion1_1);
+ CFReadStreamRef stream_ref = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, req_ref);
+
+ CFReadStreamOpen(stream_ref);
+ int current;
+ int size = 8096;
+ response_ = new char[size];
+ CFIndex read_size;
+ for (current = 0; current < size; current += read_size) {
+ read_size = CFReadStreamRead(stream_ref, reinterpret_cast<uint8_t *>(response_ + current), size - current);
+ if (read_size < 0)
+ break; // error
+ if (!read_size)
+ break; // end of data
+ }
+ if (current < size)
+ response_[current] = 0;
+ bool res = current > 0 && current < size;
+
+ CFHTTPMessageRef resp = (CFHTTPMessageRef)CFReadStreamCopyProperty(stream_ref, kCFStreamPropertyHTTPResponseHeader);
+ if(resp)
+ {
+ CFStringRef dateHeaderRef = CFHTTPMessageCopyHeaderFieldValue(resp, CFSTR("Date"));
+ if (dateHeaderRef != NULL)
+ {
+ CFGregorianDate gdate;
+ CFIndex count = _CFGregorianDateCreateWithString(kCFAllocatorDefault, dateHeaderRef, &gdate, NULL);
+ if (count != 0)
+ loader_data->set_server_date((gdate.year << 16) + (static_cast<uint8_t>(gdate.month) << 8) + static_cast<uint8_t>(gdate.day));
+ CFRelease(dateHeaderRef);
+ }
+ CFRelease(resp);
+ }
+ CFReadStreamClose(stream_ref);
+
+ CFRelease(stream_ref);
+ CFRelease(req_ref);
+ CFRelease(url_ref);
+ CFRelease(str_ref);
+
+ return res;
+#elif defined(__unix__)
+ CURL *curl = curl_easy_init();
+ if (curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, url_);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header);
+ std::string dest;
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &dest);
+ time_t file_time = (time_t)-1;
+ curl_easy_setopt(curl, CURLOPT_HEADERDATA, &file_time);
+ CURLcode c = curl_easy_perform(curl);
+ if (c == CURLE_OK) {
+ response_ = strdup(dest.c_str());
+ if (file_time != (time_t)-1) {
+ struct tm local_tm;
+ tm *t = localtime_r(&file_time, &local_tm);
+ if (t)
+ loader_data->set_server_date(((1900 + t->tm_year) << 16) + (static_cast<uint8_t>(t->tm_mon + 1) << 8) + static_cast<uint8_t>(t->tm_mday));
+ }
+ }
+ curl_easy_cleanup(curl);
+ return (c == CURLE_OK);
+ }
+ return false;
+#else
+ HMODULE dll = LoadLibraryA(VMProtectDecryptStringA("winhttp.dll"));
+ if (!dll)
+ return false;
+
+ typedef HINTERNET (WINAPI *HTTP_OPEN)(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags);
+ typedef BOOL (WINAPI *HTTP_CLOSE_HANDLE)(HINTERNET hInternet);
+ typedef BOOL (WINAPI *HTTP_READ_DATA)(HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead);
+ typedef BOOL (WINAPI *HTTP_CRACK_URL)(LPCWSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags, LPURL_COMPONENTS lpUrlComponents);
+ typedef HINTERNET (WINAPI *HTTP_CONNECT)(HINTERNET hInternet, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, DWORD dwReserved);
+ typedef BOOL (WINAPI *HTTP_SETCREDENTIALS)(HINTERNET hRequest, DWORD AuthTargets, DWORD AuthScheme, LPCWSTR pszUserName, LPCWSTR pszPassword, LPVOID pAuthParams);
+ typedef HINTERNET (WINAPI *HTTP_OPEN_REQUEST)(HINTERNET hConnect, LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, LPCWSTR lpszReferer, LPCWSTR *lplpszAcceptTypes, DWORD dwFlags);
+ typedef BOOL (WINAPI *HTTP_SEND_REQUEST)(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength, DWORD dwTotalLength, DWORD_PTR dwContext);
+ typedef BOOL (WINAPI *HTTP_RECEIVE_RESPONSE)(HINTERNET hRequest, LPVOID lpReserved);
+ typedef BOOL (WINAPI *HTTP_SET_OPTION)(HINTERNET hInternet, DWORD dwOption, LPVOID lpBuffer, DWORD dwBufferLength);
+ typedef BOOL (WINAPI *HTTP_GET_IE_PROXY_CONFIG)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *pProxyConfig);
+ typedef BOOL(WINAPI *HTTP_QUERY_HEADERS)(HINTERNET hRequest, DWORD dwInfoLevel, LPCWSTR pwszName, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex);
+
+ HTTP_OPEN http_open = reinterpret_cast<HTTP_OPEN>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpOpen")));
+ HTTP_READ_DATA http_read_data = reinterpret_cast<HTTP_READ_DATA>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpReadData")));
+ HTTP_CLOSE_HANDLE http_close_handle = reinterpret_cast<HTTP_CLOSE_HANDLE>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpCloseHandle")));
+ HTTP_CRACK_URL http_crack_url = reinterpret_cast<HTTP_CRACK_URL>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpCrackUrl")));
+ HTTP_CONNECT http_connect = reinterpret_cast<HTTP_CONNECT>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpConnect")));
+ HTTP_SETCREDENTIALS http_setcredentials = reinterpret_cast<HTTP_SETCREDENTIALS>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpSetCredentials")));
+ HTTP_OPEN_REQUEST http_open_request = reinterpret_cast<HTTP_OPEN_REQUEST>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpOpenRequest")));
+ HTTP_SEND_REQUEST http_send_request = reinterpret_cast<HTTP_SEND_REQUEST>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpSendRequest")));
+ HTTP_RECEIVE_RESPONSE http_receive_response = reinterpret_cast<HTTP_RECEIVE_RESPONSE>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpReceiveResponse")));
+ HTTP_SET_OPTION http_set_option = reinterpret_cast<HTTP_SET_OPTION>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpSetOption")));
+ HTTP_GET_IE_PROXY_CONFIG http_get_ie_proxy_config = reinterpret_cast<HTTP_GET_IE_PROXY_CONFIG>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpGetIEProxyConfigForCurrentUser")));
+ HTTP_QUERY_HEADERS http_query_headers = reinterpret_cast<HTTP_QUERY_HEADERS>(InternalGetProcAddress(dll, VMProtectDecryptStringA("WinHttpQueryHeaders")));
+
+ bool res = false;
+ if (http_open
+ && http_read_data
+ && http_close_handle
+ && http_crack_url
+ && http_connect
+ && http_setcredentials
+ && http_open_request
+ && http_send_request
+ && http_receive_response
+ && http_set_option
+ && http_query_headers) {
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ie_proxy_config = WINHTTP_CURRENT_USER_IE_PROXY_CONFIG();
+ if (http_get_ie_proxy_config)
+ http_get_ie_proxy_config(&ie_proxy_config);
+ // We are not using WPAD directly, it is buggy (OLE actively used and may hung).
+ DWORD dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; // default: settings from global registry, 8.1+ deprecated.
+#ifdef _WIN64
+ PEB64 *peb = reinterpret_cast<PEB64 *>(__readgsqword(0x60));
+#else
+ PEB32 *peb = reinterpret_cast<PEB32 *>(__readfsdword(0x30));
+#endif
+ uint16_t os_build_number = peb->OSBuildNumber;
+ if (os_build_number >= WINDOWS_8_1)
+ dwAccessType = 4; // WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY - 8.1+: smart from user IE config and/or global registry
+
+ if (ie_proxy_config.lpszProxy)
+ dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; // IE manual proxy setup
+ HINTERNET inet = http_open(VMProtectDecryptStringW(L"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"), dwAccessType, ie_proxy_config.lpszProxy, ie_proxy_config.lpszProxyBypass, 0);
+ if (inet) {
+ URL_COMPONENTS components = URL_COMPONENTS();
+
+ components.dwStructSize = sizeof(components);
+#define INTERNET_MAX_HOST_NAME_LENGTH 256
+#define INTERNET_MAX_USER_NAME_LENGTH 128
+#define INTERNET_MAX_PASSWORD_LENGTH 128
+#define INTERNET_MAX_PATH_LENGTH 2048
+ wchar_t url_host[INTERNET_MAX_HOST_NAME_LENGTH];
+ wchar_t url_path[INTERNET_MAX_PATH_LENGTH];
+ wchar_t url_user[INTERNET_MAX_USER_NAME_LENGTH];
+ wchar_t url_password[INTERNET_MAX_PASSWORD_LENGTH];
+ components.lpszHostName = url_host;
+ components.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
+ components.lpszUserName = url_user;
+ components.dwUserNameLength = INTERNET_MAX_USER_NAME_LENGTH;
+ components.lpszPassword = url_password;
+ components.dwPasswordLength = INTERNET_MAX_PASSWORD_LENGTH;
+ components.lpszUrlPath = url_path;
+ components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
+ http_crack_url(std::wstring(url_, url_ + strlen(url_)).c_str(), 0, 0, &components);
+
+ HINTERNET h = 0;
+ HINTERNET connect = http_connect(inet, components.lpszHostName, components.nPort, 0);
+ if (connect) {
+ h = http_open_request(connect, L"GET", components.lpszUrlPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_BYPASS_PROXY_CACHE | (
+ (components.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0
+ ));
+ if (h) {
+ //TODO: internet_setcredentials if need
+ if (components.nScheme == INTERNET_SCHEME_HTTPS) {
+ DWORD data = SECURITY_FLAG_IGNORE_UNKNOWN_CA
+ | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
+ | SECURITY_FLAG_IGNORE_CERT_CN_INVALID
+ /*| SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE*/;
+ http_set_option(h, WINHTTP_OPTION_SECURITY_FLAGS, &data, sizeof(data));
+ }
+ if (!http_send_request(h, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, NULL) || !http_receive_response( h, NULL)) {
+ http_close_handle(h);
+ h = 0;
+ }
+ }
+ }
+
+ if (h) {
+ DWORD current;
+ DWORD size = 8096;
+ response_ = new char[size];
+ DWORD read_size;
+ for (current = 0; current < size; current += read_size) {
+ if (http_read_data(h, response_ + current, size - current, &read_size) != TRUE)
+ break; // error
+ if (!read_size)
+ break; // end of data
+ }
+ if (current < size)
+ response_[current] = 0;
+ res = current > 0 && current < size;
+ SYSTEMTIME dtBuf;
+ DWORD dtBufLength = sizeof(dtBuf);
+ if(http_query_headers(h, WINHTTP_QUERY_DATE | WINHTTP_QUERY_FLAG_SYSTEMTIME, WINHTTP_HEADER_NAME_BY_INDEX, &dtBuf, &dtBufLength, WINHTTP_NO_HEADER_INDEX)) {
+ FILETIME ft;
+ SystemTimeToFileTime(&dtBuf, &ft);
+ FileTimeToSystemTime(&ft, &dtBuf);
+ loader_data->set_server_date((dtBuf.wYear << 16) + (static_cast<uint8_t>(dtBuf.wMonth) << 8) + static_cast<uint8_t>(dtBuf.wDay));
+ }
+ http_close_handle(h);
+ }
+ if (connect)
+ http_close_handle(connect);
+ http_close_handle(inet);
+ }
+
+ if (ie_proxy_config.lpszAutoConfigUrl)
+ GlobalFree(ie_proxy_config.lpszAutoConfigUrl);
+ if (ie_proxy_config.lpszProxy)
+ GlobalFree(ie_proxy_config.lpszProxy);
+ if (ie_proxy_config.lpszProxyBypass)
+ GlobalFree(ie_proxy_config.lpszProxyBypass);
+ }
+ FreeLibrary(dll);
+ return res;
+#endif
+}
+
+void BaseRequest::AppendUrlParam(const char *param, const char *value)
+{
+ AppendUrl(param, false);
+ AppendUrl(value, true);
+}
+
+void BaseRequest::AppendUrl(const char *str, bool escape)
+{
+ size_t pos = 0;
+ while (url_[pos]) {
+ pos++;
+ }
+
+ size_t size = _countof(url_);
+ if (escape) {
+ while (*str && pos < size - 1 - 3)
+ {
+ uint8_t c = *str;
+ if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
+ url_[pos++] = c;
+ } else if (c == ' ') {
+ url_[pos++] = '+';
+ } else {
+ const char *hex = "0123456789abcdef";
+ url_[pos++] = '%';
+ url_[pos++] = hex[c >> 4];
+ url_[pos++] = hex[c & 0x0f];
+ }
+ str++;
+ }
+ } else {
+ while (*str && pos < size) {
+ url_[pos++] = *str;
+ str++;
+ }
+ }
+ url_[pos] = 0;
+}
+
+void BaseRequest::EncodeUrl()
+{
+ char buf[2048];
+ strcpy_s(buf, url_);
+ size_t url_size = sizeof(url_);
+ Base64Encode(reinterpret_cast<const uint8_t *>(buf), strlen(buf), url_, url_size);
+ url_[url_size] = 0;
+}
+
+/**
+ * ActivationRequest
+ */
+
+ActivationRequest::ActivationRequest()
+ : BaseRequest(), serial_(NULL)
+{
+
+}
+
+bool ActivationRequest::VerifyCode(const char *code) const
+{
+ if (!code || !code[0])
+ return false;
+
+ static const char *alphabet = "0123456789abcdefghijklmnopqrstuvwxyz-";
+ for (const char *p = code; *p; p++) {
+ if (!strchr(alphabet, tolower(*p)))
+ return false;
+ if (p - code > 32)
+ return false; // too long
+ }
+ return true;
+}
+
+bool ActivationRequest::BuildUrl(const CryptoContainer &license_data, const char *code, bool offline)
+{
+ if (!offline) {
+ if (!BaseRequest::BuildUrl(license_data))
+ return false;
+ }
+
+ // hwid -> base64
+ char str_hwid[100];
+ {
+ uint8_t hwid[16 * sizeof(uint32_t)]; // HardwareID::MAX_BLOCKS
+
+ size_t hwid_size = Core::Instance()->hardware_id()->Copy(hwid, sizeof(hwid));
+ size_t dest_len = sizeof(str_hwid);
+ Base64Encode(hwid, hwid_size, str_hwid, dest_len);
+ str_hwid[dest_len] = 0;
+ }
+
+ // hash -> base64
+ char str_hash[64];
+ {
+ SHA1 sha;
+ sha.Input(license_data, license_data.GetDWord(FIELD_MODULUS_OFFSET * sizeof(uint32_t)), license_data.GetDWord(FIELD_MODULUS_SIZE * sizeof(uint32_t)));
+ size_t dest_len = sizeof(str_hash);
+ Base64Encode(sha.Result(), 20, str_hash, dest_len);
+ str_hash[dest_len] = 0;
+ }
+
+ // build url
+ if (offline) {
+ char str[] = {'t', 'y', 'p', 'e', '=', 'a', 'c', 't', 'i', 'v', 'a', 't', 'i', 'o', 'n', '&', 'c', 'o', 'd', 'e', '=', 0};
+ AppendUrlParam(str, code);
+ } else {
+ char str[] = {'a', 'c', 't', 'i', 'v', 'a', 't', 'i', 'o', 'n', '.', 'p', 'h', 'p', '?', 'c', 'o', 'd', 'e', '=', 0};
+ AppendUrlParam(str, code);
+ }
+ {
+ char str[] = {'&', 'h', 'w', 'i', 'd', '=', 0};
+ AppendUrlParam(str, str_hwid);
+ }
+ {
+ char str[] = {'&', 'h', 'a', 's', 'h', '=', 0};
+ AppendUrlParam(str, str_hash);
+ }
+
+ if (offline)
+ EncodeUrl();
+
+ return true;
+}
+
+int ActivationRequest::Process(const CryptoContainer &license_data, const char *code, bool offline)
+{
+ if (!VerifyCode(code))
+ return ACTIVATION_BAD_CODE;
+
+ if (!BuildUrl(license_data, code, offline))
+ return ACTIVATION_NOT_AVAILABLE;
+
+ if (offline)
+ return ACTIVATION_OK;
+
+ if (!Send())
+ return ACTIVATION_NO_CONNECTION;
+
+ const char *res = response();
+ if (!res || !res[0])
+ return ACTIVATION_BAD_REPLY;
+
+ // possible answers: OK, BAD, BANNED, USED, EXPIRED
+ // if OK - see the serial number below
+
+ if (res[0] == 'B' && res[1] == 'A' && res[2] == 'D' && res[3] == 0)
+ return ACTIVATION_BAD_CODE;
+
+ if (res[0] == 'B' && res[1] == 'A' && res[2] == 'N' && res[3] == 'N' && res[4] == 'E' && res[5] == 'D' && res[6] == 0)
+ return ACTIVATION_BANNED;
+
+ if (res[0] == 'U' && res[1] == 'S' && res[2] == 'E' && res[3] == 'D' && res[4] == 0)
+ return ACTIVATION_ALREADY_USED;
+
+ if (res[0] == 'E' && res[1] == 'X' && res[2] == 'P' && res[3] == 'I' && res[4] == 'R' && res[5] == 'E' && res[6] == 'D' && res[7] == 0)
+ return ACTIVATION_EXPIRED;
+
+ const char *endl = strchr(res, '\n');
+ if (!endl)
+ return ACTIVATION_BAD_REPLY;
+
+ if (endl - res != 2)
+ return ACTIVATION_BAD_REPLY;
+
+ if (res[0] != 'O' || res[1] != 'K')
+ return ACTIVATION_BAD_REPLY;
+
+ size_t len = strlen(res + 3);
+ if (len < 64)
+ return ACTIVATION_BAD_REPLY;
+
+ serial_ = res + 3;
+ return ACTIVATION_OK;
+}
+
+/**
+ * DeactivationRequest
+ */
+
+bool DeactivationRequest::VerifySerial(const char *serial) const
+{
+ if (!serial || !serial[0])
+ return false;
+
+ return true;
+}
+
+ bool DeactivationRequest::BuildUrl(const CryptoContainer &license_data, const char *serial, bool offline)
+{
+ if (!offline) {
+ if (!BaseRequest::BuildUrl(license_data))
+ return false;
+ }
+
+ size_t code_len = strlen(serial);
+ size_t len = code_len;
+ uint8_t *p = new uint8_t[len];
+ if (!Base64Decode(serial, code_len, p, len)) {
+ delete [] p;
+ return false;
+ }
+
+ // get binary hash
+ char str_hash[64];
+ {
+ SHA1 sha;
+ sha.Input(p, len);
+ size_t dst_len = sizeof(str_hash);
+ Base64Encode(sha.Result(), 20, str_hash, dst_len);
+ str_hash[dst_len] = 0;
+ }
+ delete [] p;
+
+ if (offline) {
+ char str[] = { 't', 'y', 'p', 'e', '=', 'd', 'e', 'a', 'c', 't', 'i', 'v', 'a', 't', 'i', 'o', 'n', '&', 'h', 'a', 's', 'h', '=', 0 };
+ AppendUrlParam(str, str_hash);
+ }
+ else {
+ char str[] = { 'd', 'e', 'a', 'c', 't', 'i', 'v', 'a', 't', 'i', 'o', 'n', '.', 'p', 'h', 'p', '?', 'h', 'a', 's', 'h', '=', 0 };
+ AppendUrlParam(str, str_hash);
+ }
+
+ if (offline)
+ EncodeUrl();
+
+ return true;
+}
+
+int DeactivationRequest::Process(const CryptoContainer &license_data, const char *serial, bool offline)
+{
+ if (!VerifySerial(serial))
+ return ACTIVATION_BAD_CODE;
+
+ if (!BuildUrl(license_data, serial, offline))
+ return ACTIVATION_NOT_AVAILABLE;
+
+ if (offline)
+ return ACTIVATION_OK;
+
+ if (!Send())
+ return ACTIVATION_NO_CONNECTION;
+
+ const char *res = response();
+ if (!res || !res[0])
+ return ACTIVATION_BAD_REPLY;
+
+ if (res[0] == 'O' && res[1] == 'K' && res[2] == 0)
+ return ACTIVATION_OK;
+
+ if (res[0] == 'E' && res[1] == 'R' && res[2] == 'R' && res[3] == 'O' && res[4] == 'R' && res[5] == 0)
+ return ACTIVATION_CORRUPTED;
+
+ if (res[0] == 'U' && res[1] == 'N' && res[2] == 'K' && res[3] == 'N' && res[4] == 'O' && res[5] == 'W' && res[6] == 'N' && res[7] == 0)
+ return ACTIVATION_SERIAL_UNKNOWN;
+
+ return ACTIVATION_BAD_REPLY;
+};
+
+#endif \ No newline at end of file