В своём блоге я обещал рассказать, как определиться с флешкой, служащей ключом защиты приложения. Вначале флешку-ключ надо поименовать, как "HARD_KEY". Или присвоить другое имя, которое придумаете. Делается это с помощью проводника. Вставьте флешку, найдите её проводником, нажмите "Переименовать". Переименуйте и нажмите клавишу Enter. Удивительно, но мало кто знает, как переименовать флешку.
Фишка в том, что Windows от XP до «десятки» прописывает буквы примонтированных накопителей в системном реестре, чем и воспользуемся. Для начала приведу пару вспомогательных функций. Здесь и далее я использовал коды C++ системы разработки Embarcadero, но не беспокойтесь – ничего визуального, лишь пару классов – строки и списки строк. Можно было бы обойтись и без них, но, знаете ли, привычка.
Вспомогательная функция. Она определяет, есть ли признак флешки в байтовом буфере:
bool IsBufFS(char *Buf, int size)
{
int i,j;
bool res;
char* bf = new char[size/2+1];
for(i=0; i<size/2+1; i++)
bf[i]=0;
for(i=0,j=0; i<size; i++)
{
if(i%2==0)
bf[j++] = Buf[i];
}
String S = AnsiString(bf);
if( S.Pos("STOR")!=0 || S.Pos("USB")!=0)
res = true;
else
res = false;
delete[] bf;
return res;
}
Ещё одна вспомогательная функция. Она читает из системного реестра смонтированные флешки (именно флешки, а не все накопители) в глобальный строковый список letters_sl букв. Если возвращает true, то информация из реестра прочитана правильно.
bool fill_letters_sl(void)
{
bool res;
TRegIniFile *Reg = new TRegIniFile("SER_NUM");
TStringList *sl = new TStringList;
TStringList *dd_sl = new TStringList;
TStringList *s_sl = new TStringList;
TStringList *vs_sl = new TStringList;
int i,j,siz;
char Buf[512];
int bytes;
extern TStringList* letters_sl; // список букв смонтированных флешек, глобал
letters_sl->Clear();
{
Reg->RootKey = HKEY_LOCAL_MACHINE;
if (!Reg->OpenKey( "SYSTEM\\MountedDevices", false) )
res = false;
else
{
Reg->GetValueNames(sl);
for(i=0; i<sl->Count; i++)
{
if(sl->Strings[i].Pos("DosDevices"))
dd_sl->Add(sl->Strings[i]);
}
if(dd_sl->Count != 0)
{
for(i=0; i<dd_sl->Count; i++)
{
if( dd_sl->Strings[i].Pos("A:") )
continue;
if( dd_sl->Strings[i].Pos("B:") )
continue;
if( dd_sl->Strings[i].Pos("C:") )
continue;
s_sl->Add(dd_sl->Strings[i]); // подозрительные строки
}
}
if(s_sl->Count != 0)
{
// надо проверить: носитель -- флешка или нет?
for(i=0; i<s_sl->Count; i++)
{
siz = Reg->GetDataSize(s_sl->Strings[i]);
if(siz<32)
continue;
// читаем содержимое:
bytes = Reg->ReadBinaryData(s_sl->Strings[i], Buf, siz);
if( IsBufFS(Buf, bytes) )
vs_sl->Add(s_sl->Strings[i]);
}
}
if(vs_sl->Count != 0)
{
for(i=0; i<vs_sl->Count; i++)
letters_sl->Add(vs_sl->Strings[i].SubString(13,1));
}
}
}
catch(...)
{
res = false;
}
delete Reg;
delete sl;
delete dd_sl;
delete s_sl;
delete vs_sl;
if(letters_sl->Count > 0)
return true;
}
Ваша защищаемая софтина при запуске должна вызвать функцию SelectKeyLetterOnName() и запомнить в char KeyLetter[2], где находится флешка-ключ:
bool SelectKeyLetterOnName(void)
{
extern TStringList* letters_sl; // список букв смонтированных флешек
bool res;
if( !fill_letters_sl() )
return false;
if(letters_sl->Count == 0)
return false;
TStringList *search_sl = new TStringList;
DWORD SerialNum;
DWORD a, b;
char VolumeName[MAX_PATH];
char SysNameBuffer[MAX_PATH];
DWORD Result;
String Letter, RealKeyName;
char szRealKeyName[32];
KeyLetter[0] = 0;
KeyLetter[1] = 0; // предв. сброс буквы флешки-ключа
res = false;
for(int i=0; i<letters_sl->Count; i++)
{
Result = 0;
Letter = letters_sl->Strings[i]+":\\";
if (GetVolumeInformation(Letter.c_str(), VolumeName, sizeof(VolumeName),
&SerialNum, &a, &b, SysNameBuffer, sizeof(SysNameBuffer) ) )
Result = SerialNum;
if(Result==0)
continue;
// номер флешки прочитан, определяем её имя:
strcpy(szRealKeyName, VolumeName);
RealKeyName = AnsiString(szRealKeyName);
if(RealKeyName=="HARD_KEY")
{
strcpy(KeyLetter, letters_sl->Strings[i].c_str() );
search_sl->Add(letters_sl->Strings[i]);
}
}
if(search_sl->Count==1 && KeyLetter[0]!=0)
res = true;
delete search_sl;
return res;
}
Но, чтобы убедиться, что это ключ не фейк, по мере выполнения программы, читая стэмп, следует проверять его серийный номер. Это выполняет вот такая функция:
bool SelectKeyLetterOnNmr(void)
{
extern TStringList* letters_sl; // список букв смонтированных флешек (для поиска ключа)
bool res;
if( !fill_letters_sl() )
return false;
if(letters_sl->Count == 0)
return false;
TStringList *search_sl = new TStringList;
DWORD SerialNum;
DWORD a, b;
char VolumeName[MAX_PATH];
char SysNameBuffer[MAX_PATH];
DWORD Result;
String Letter;
KeyLetter[0] = 0;
KeyLetter[1] = 0; // предв. сброс буквы флешки-ключа
TIniFile* ini;
ini = new TIniFile(IniFileName);
DWORD KeyNmr = ini->ReadInteger("ApplAttr", "KeyNmr", 0);// прочитать номер ключа
delete ini;
res = false;
for(int i=0; i<letters_sl->Count; i++)
{
Result = 0;
Letter = letters_sl->Strings[i]+":\\";
if (GetVolumeInformation(Letter.c_str(), VolumeName, sizeof(VolumeName),
&SerialNum, &a, &b, SysNameBuffer, sizeof(SysNameBuffer) ) )
Result = SerialNum;
if(Result != 0 && Result == KeyNmr) // подставить !!!
{
strcpy(KeyLetter, letters_sl->Strings[i].c_str() );
search_sl->Add(letters_sl->Strings[i]);
}
}
if(search_sl->Count==1 && KeyLetter[0]!=0)
res = true;
delete search_sl;
return res;
}
В данном случае для иллюстрации "серийник" флешки-ключа тупо читается из ини-файла приложения. Так вообще-то не следует делать. Лучше "серийник" держать в реестре и держать не сам "серийник", а его хэш-код. Откуда он там возьмётся? При активации приложения.
Чтобы пользователь ничего не мог записать в флешку-ключ, забейте её случайными числами под завязку. А при активации запишите байты стэмпа железа, например, 1-ый байт – в 10-ый байт случайного содержимого, 2-ой – в 20-ый или в 21-ый, вообщем, проявите фантазию.
Копирование флешки-ключа ничего не даст: флешка содержит стэмп железа, на котором установлена ваша софтина. Скопировать-то можно, а толку? Ключ-копия сможет работать только на той машине, из которой был извлечён ключ-оригинал. В дальнейшем я расскажу, как сделать флешку-ключ нечитаемой. Ну не вообще, а всякими проводниками, коммандерами, приложениями и проч. Нечитаемой ничем, кроме вашего приложения. Подсказка: на флешке будет чужая для Windows файловая система.
Михаил Гурчик