Декодирование ключа на Linux

Issues related to VMProtect
Post Reply
orgulut
Posts: 5
Joined: Wed Mar 28, 2012 2:00 am

Декодирование ключа на Linux

Post by orgulut »

Добрый день.

Я использую vmprotect для защиты Windows версии своего продукта. Linux версия готовится к выпуску и я хотел бы использовать те же лицезионные ключи что и на Windows.

Мне требуется только базовый функционал - декодировать информацию из ключа (имя пользователя, max build date, etc.), защита не нужна.

Есть ли пример-исходник как сделать декодирование информации из ключа? Еще лучше - уже портированный для Linux.
Admin
Site Admin
Posts: 2586
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Декодирование ключа на Linux

Post by Admin »

А в чем у вас возникла сложность? Генерация серийника полностью есть в исходных кодах (\Keygen\DLL\Sources\), на основе этого кода декрипт серийника пишется без проблем в том числе и для Linux.
orgulut
Posts: 5
Joined: Wed Mar 28, 2012 2:00 am

Re: Декодирование ключа на Linux

Post by orgulut »

Проблем нет, разбираться в исходниках генерации ключа, потом самому писать декодирование, ловить баги и т.д. - непродуктивно.
Именно поэтому люди собственно и платят деньги за готовые решения (особенно выбирают Ultimate версии) - сэкономить себе время.

Т.к. алгоритм декодирования ключа не составляет секрета (его можно восстановить по кодированию, как вы говорите), то расшарьте его код, по крайней мере для клиентов. Номер моего ордера: 523814, мой мейл вам известен, copy-paste труда не составит.

Тем более что он уже есть в недрах VMProtect - это сэкономит тучу моего времени и добавит шансов на продление лицензии, в том числе и тем клиентам, которых я привел к вам.
Admin
Site Admin
Posts: 2586
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Декодирование ключа на Linux

Post by Admin »

Дело в том, что в нашем рантайме используется переделанная RSA-ная библиотека, работающая с открытым ключем через криптоконтейнеры, поэтому наш кусок кода все равно придется переделывать под библиотеку из кейгена. Я постараюсь в ближайшее время написать разбор ключа на библиотеке из кейгена.
orgulut
Posts: 5
Joined: Wed Mar 28, 2012 2:00 am

Re: Декодирование ключа на Linux

Post by orgulut »

Буду очень благодарен. Пока откладываю эту задачу до вашего ответа.
Admin
Site Admin
Posts: 2586
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Декодирование ключа на Linux

Post by Admin »

Ну вот как-то так:

Code: Select all

// DecodeSerial.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

std::vector<byte> Base64ToVector(const char *p)
{
	if (!p || !p[0]) return std::vector<byte>(); // empty vector for empty string

	size_t nSrcLen = strlen(p);
	std::vector<byte>	res;
	base64_decode(p, nSrcLen, res);
	return res;
}

std::wstring FromUTF8(const char *src, size_t len)
{
	int nLength = static_cast<int>(len);
	int nSize = MultiByteToWideChar(CP_UTF8, 0, src, nLength, NULL, 0);
	std::wstring dest;
	dest.resize(nSize);
	int nSize2 = MultiByteToWideChar(CP_UTF8, 0, src, nLength, (LPWSTR)dest.c_str(), nSize);
	return dest;
}

size_t g_nBits = 2048;
const char *g_vModulus = "rnK4CvdHf4d3DTtS7B7Fyu5kzT16BTWlecKTZcjw5Ji9BTLfhjgIxSyY6uqD2wNxd+qTzNY4PHvAf8lwxsLE9ir7sDhrDgzLp5PR5EW5nuZgHC+QKpXoaXH9Q73i6KcfDOUSHquQmRNtDHqyublT9/yv1pLuA+mCC+/gWZhgdAGZbwA0Ek61mGH1YFOUY+qLGyiJwGSMBXDWgEAm36wUHyTjZhN2003JuJwtpT8IWKiR5sy+vNQdtj+QEeyADbTFuYv5RHXUkUVvc0RUXecFLAQuE/1i1AJDAd5s4oDKUizZ10nCsHawSnHn4pgdQFzvhxnNTEVcjhcWMW0+/Zvfhw==";
const char *g_vPrivate = "qA/5YwcsKJ63gVXf438aCxF0p45Mmew/CXj9TBg0i+biY7nJQqOFP4BhqiBY+4zrqh2iWrse3pZStEm7jqFh488xP2p7KMQHB+EPWx4ZYA6OFVl1SLG3gK7C0pqDmqQ289NUi5u56N+gNV+YA/mZ/dXBCbXv/HDgSuPEfkygz+CG20uRWoKbF+JKF6dHLbQHnn7l9nu1ht7+6NeL32UV8sEu8E508qF4cEni4RPu4cvnp98t8FF5e10JFpCE5wjkg47GQ+vUB5z+Z2AzuQdW8EHPvWwn+TNvcIh2tHEPWT39IeZ2X2KbfLCqdW4T2wwHHesiNwhqOwpNnxzHmDyaAQ==";
const char *g_vPublic = "AAEAAQ==";

enum eChunks 
{
	SERIAL_CHUNK_VERSION				= 0x01,	//	1 byte of data - version
	SERIAL_CHUNK_USER_NAME				= 0x02,	//	1 + N bytes - length + N bytes of customer's name (without enging \0).
	SERIAL_CHUNK_EMAIL					= 0x03,	//	1 + N bytes - length + N bytes of customer's email (without ending \0).
	SERIAL_CHUNK_HWID					= 0x04,	//	1 + N bytes - length + N bytes of hardware id (N % 4 == 0)
	SERIAL_CHUNK_EXP_DATE				= 0x05,	//	4 bytes - (year << 16) + (month << 8) + (day)
	SERIAL_CHUNK_RUNNING_TIME_LIMIT		= 0x06,	//	1 byte - number of minutes
	SERIAL_CHUNK_PRODUCT_CODE			= 0x07,	//	8 bytes - used for decrypting some parts of exe-file
	SERIAL_CHUNK_USER_DATA				= 0x08,	//	1 + N bytes - length + N bytes of user data
	SERIAL_CHUNK_MAX_BUILD				= 0x09,	//	4 bytes - (year << 16) + (month << 8) + (day)

	SERIAL_CHUNK_END					= 0xFF	//	4 bytes - checksum: the first four bytes of sha-1 hash from the data before that chunk
};

#pragma pack(push, 1)
typedef struct
{
	WORD			wYear;
	BYTE			bMonth;
	BYTE			bDay;
} VMProtectDate;
typedef struct
{
	INT				nState;				// VMProtectSerialStateFlags
	wchar_t			wUserName[256];		// user name
	wchar_t			wEMail[256];		// email
	VMProtectDate	dtExpire;			// date of serial number expiration
	VMProtectDate	dtMaxBuild;			// max date of build, that will accept this key
	INT				bRunningTime;		// running time in minutes
	BYTE			nUserDataLength;	// length of user data in bUserData
	BYTE			bUserData[255];		// up to 255 bytes of user data
} VMProtectSerialNumberData;

bool DecodeSerial(const char *serial, VMProtectSerialNumberData *data)
{
	std::vector<byte> sn = Base64ToVector(serial);
	if (sn.size() < 16)
		return false;

	std::vector<byte> modulus = Base64ToVector(g_vModulus);
	std::vector<byte> exp = Base64ToVector(g_vPublic);

	Bignum e = bignum_from_bytes(&exp[0], exp.size());
	Bignum n = bignum_from_bytes(&modulus[0], modulus.size());
	Bignum x = bignum_from_bytes(&sn[0], sn.size());

	// the second check of data length, data is too long to crypt
	if (bignum_cmp(n, x) < 0) 
		return false;

	Bignum y = modpow(x, e, n);
	int nBytes;
	byte *pRes = bignum_to_bytes(y, &nBytes);
	sn.clear();
	sn.insert(sn.end(), pRes, pRes + nBytes);
	delete [] pRes;

	if (sn[0] != 0 && sn[1] != 2)
		return false;

	// skip padding
	size_t pos = 2;
	for (pos = 2; pos < sn.size(); pos++) {
		if (sn[pos] == 0) {
			pos++;
			break;
		}
	}
	if (pos == sn.size())
		return false;

	size_t start = pos;
	while (pos < sn.size()) {
		byte b = sn[pos];
		byte sz;
		std::wstring str;
		pos++;
		switch (b) {
			case SERIAL_CHUNK_VERSION:
				if (sn[pos] != 1)
					return false;
				pos += 1;
				break;
			case SERIAL_CHUNK_EXP_DATE:
				memcpy(&data->dtExpire, &sn[pos], sizeof(data->dtExpire));
				pos += 4;
				break;
			case SERIAL_CHUNK_RUNNING_TIME_LIMIT:
				data->bRunningTime = sn[pos];
				pos += 1;
				break;
			case SERIAL_CHUNK_PRODUCT_CODE:
				pos += 8;
				break;
			case SERIAL_CHUNK_MAX_BUILD:
				memcpy(&data->dtMaxBuild, &sn[pos], sizeof(data->dtMaxBuild));
				pos += 4;
				break;
			case SERIAL_CHUNK_USER_NAME:
				sz = sn[pos++];
				str = FromUTF8(reinterpret_cast<const char *>(&sn[pos]), sz);
				memcpy(&data->wUserName, &str[0], str.size() * sizeof(wchar_t));
				pos += sz;
				break;
			case SERIAL_CHUNK_EMAIL:
				sz = sn[pos++];
				str = FromUTF8(reinterpret_cast<const char *>(&sn[pos]), sz);
				memcpy(&data->wEMail, &str[0], str.size() * sizeof(wchar_t));
				pos += sz;
				break;
			case SERIAL_CHUNK_HWID:
				sz = sn[pos++];
				// read HWID
				pos += sz;
				break;
			case SERIAL_CHUNK_USER_DATA:
				sz = sn[pos++];
				data->nUserDataLength = sz;
				memcpy(&data->bUserData, &sn[pos], sz);
				pos += sz;
				break;
			case SERIAL_CHUNK_END:
				if (sn.size() - pos < 3)
					return false;

				// compute hash
				SHA1Context ctx;
				SHA1Reset(&ctx);
				SHA1Input(&ctx, &sn[start], pos - start - 1);
				SHA1Result(&ctx);

				// check CRC
				const byte *p = (const byte *)&ctx.Message_Digest;
				for (size_t i = 0; i < 4; i++) {
					if (sn[pos + i] != p[i])
						return false;
				}

				pos  = sn.size();
				break;
		}
	}

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	VMProtectSerialNumberData data = {0};

	DecodeSerial("IOtjdo0yTQFhExs0hoDu7Y6O3jQgsJqSu2eytTmlsFI1+XJdPXdhRJmSkqzld/RSGes7wqxmxtFQUakrHxkAruXPgOPRZX1Mr/d717LlpDW1DvJJ7ndD/fAziYcKGiQ1HfWjwXWAzjM/A1zT0X333E8zCYmGrWHPC0u94UqjabJ2EF4Wu5K+6zZX8Gy+msV8BarrW1VdGCcEIMA/wVD5t1nrhU4PMAsqzZHkmXuH9RT8AWCBz2n1RWqnk3YOCNFJ8Oywi7YBjVnyzTTHTOojBXo77xmMFoncxUoUzFA6P5653KK14nZ2A4yXb4t2Ia5XOFMcfEQ4HOfLK9dnD2BeGA==", &data);

	return 0;
}
в stdafx.h добавить:

Code: Select all

#include <windows.h>

#include <vector>

#ifndef byte
typedef unsigned char byte;
#endif

#include "b64.h"
#include "sha-1.h"
#include "sshbn.h"
все запчасти можно взять из %VMProtect%\Keygen\DLL\Sources\
orgulut
Posts: 5
Joined: Wed Mar 28, 2012 2:00 am

Re: Декодирование ключа на Linux

Post by orgulut »

Большое спасибо!

В течении пары дней посмотрю-поразбираюсь, задам вопросы если возникнут.
orgulut
Posts: 5
Joined: Wed Mar 28, 2012 2:00 am

Re: Декодирование ключа на Linux

Post by orgulut »

Доброго дня.

Все моменты понятны, кроме одно - где взять мой публичный ключ?

VMProtect экспортирует только g_vPrivate, g_vModulus и g_vProductCode.
Имеется ввиду что g_vProductCode и есть публичный ключ?
Admin
Site Admin
Posts: 2586
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Декодирование ключа на Linux

Post by Admin »

Публичный ключ можно взять из VMP файла. Для кейгена он не экспортируется.
Post Reply