Перейти к содержанию
Fire Monkey от А до Я
  • 0

Простая защита программы


d7d1cd

Вопрос

Привет всем. Есть необходимость сделать защиту программы так, чтобы она выполнялась только на одном компьютере. Думаю привязать ее к серийному номеру материнской платы. Сначала покупатель с помощью моей утилиты пришлет мне номер, а потом я его внедрю в продаваемую программу. Вопрос: как программно узнать этот номер?

 

P.S. Давайте только не будем обсуждать то, что эту защиту обойдет даже начинающий хакер.

Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Модераторы

Возможно, это https://msdn.microsoft.com/en-us/library/aa394204.aspx вам поможет

procedure GetWMIInfo(mem: TMemo);
// uses Winapi.ActiveX, System.Win.ComObj;
const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator: OLEVariant;
  FWMIService: OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject: OLEVariant;
  oEnum: IEnumvariant;
  iValue: LongWord;
begin
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet := FWMIService.ExecQuery('Select * from Win32_MotherboardDevice', 'WQL', wbemFlagForwardOnly);

  oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumvariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    if not VarIsNull(FWbemObject.DeviceID) then
      mem.Lines.Add(VarToStr(FWbemObject.DeviceID));
    if not VarIsNull(FWbemObject.PrimaryBusType) then
      mem.Lines.Add(VarToStr(FWbemObject.PrimaryBusType));
    if not VarIsNull(FWbemObject.SecondaryBusType) then
      mem.Lines.Add(VarToStr(FWbemObject.SecondaryBusType));
    FWbemObject := Unassigned;
  end;
end;

что вы там собрались брать?

 

Результат:

Motherboard

PCI

ISA

 

я могу вам предложить вот такой вариант

замените этими строками код выше, вы получите все устройства доступные на ПК

  FWbemObjectSet := FWMIService.ExecQuery('Select * from CIM_LogicalDevice', 'WQL', wbemFlagForwardOnly);
.........
  if not(VarIsNull(FWbemObject.Name) and VarIsNull(FWbemObject.DeviceID)) then
    mem.Lines.Add(VarToStr(FWbemObject.Name) + ' - ' + VarToStr(FWbemObject.DeviceID));

или использовать защиту по MAC адресу

  FWbemObjectSet := FWMIService.ExecQuery
    ('SELECT Description,MACAddress,IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE', 'WQL',
    wbemFlagForwardOnly);
..............
    if not(VarIsNull(FWbemObject.MacAddress) and VarIsNull(FWbemObject.IpAddress)) then
      mem.Lines.Add(VarToStr(FWbemObject.MacAddress) + ' - ' + VarToStr(FWbemObject.IpAddress[0]));

Изменено пользователем ZuBy
Ссылка на комментарий
  • 0
  • Модераторы

Слово "Возможно" не просто так стоит. Знаете способ, говорите.

Вот только раньше, не на всех материнках можно было получить серийник, у некоторых его просто не было. Не знаю как сейчас дела обстоят с этим...

Ссылка на комментарий
  • 0
  • Модераторы

Слово "Возможно" не просто так стоит. Знаете способ, говорите.

Вот только раньше, не на всех материнках можно было получить серийник, у некоторых его просто не было. Не знаю как сейчас дела обстоят с этим...

уже дал ответ выше

 

WMI поддерживается с Windows Vista, для ранних версии нужно что нить другое думать (если вообще есть смысл)

Ссылка на комментарий
  • 0

лучше сделать активацию через интернет, проще будет, если пользователь решит пользоваться другим компьютером.

а при активации сохранять в облаке, например серийный номер Windows, если пользователь произведет активацию на другом компьютере, то аннулировать прежнюю активацию!

как то так....

Ссылка на комментарий
  • 0

Zuby, спасибо за код. Буду пробовать.

 

Как не бился, так и не смог перевести код на С++. Объект, который создается функцией CreateOleObject не содержит функции ConnectServer  :( . Что я не так делаю?

Изменено пользователем d7d1cd
Ссылка на комментарий
  • 0

проще привязаться к серийнику винта, он редко меняется. 

Как это сделать?

 

И вообще, может быть у кого-то есть решение как сделать простую защиту программы не используя UUID и интернет?

Изменено пользователем d7d1cd
Ссылка на комментарий
  • 0

Самое простое (да, это просто обходится любым,кто знаком с ProcessMonitor):

программа-"подготовщик" запускается с правами админа и записывает в HKLM случайно сгенерированное довольно длинное значение. Она же выдает его пользователю в читабельном виде (например - через Base64).

Рабочее приложение читает это значение (на чтение HKLM доступен) и сравнивает с эталоном, который хранится "внутри" нее.

Ссылка на комментарий
  • 0

В свое время я даже делал шифрование кода в ходе "предпродажной" подготовки и динамическую его расшифровку в процессе выполнения по части такого ключа.

Правда, антивирусы стали ругаться :)

Ссылка на комментарий
  • 0

Самое простое (да, это просто обходится любым,кто знаком с ProcessMonitor):

программа-"подготовщик" запускается с правами админа и записывает в HKLM случайно сгенерированное довольно длинное значение. Она же выдает его пользователю в читабельном виде (например - через Base64).

Рабочее приложение читает это значение (на чтение HKLM доступен) и сравнивает с эталоном, который хранится "внутри" нее.

Но ведь пользователь может запустить программу-"подготовщик" на любом количестве компьютеров и потом запускать там рабочее приложение. Какая же это защита?

Ссылка на комментарий
  • 0

Подготовщик пишет в реестр случайную последовательность. Каждый раз - случайную (если он не видит записанное ранее значение).

Эту же случайную последовательность в закодированном виде пользователь передает вам.

Вы вносите в код программы полученное от пользователя значение.

Программа проверяет совпадение того, что в реестре с тем, что зашито в коде.

 

Если подготовщик запустить на другом компьютере - в реестре будет другая последовательность, отличающаяся от зашитой в программе.

 

Какая же это не-защита?

Изменено пользователем kami
Ссылка на комментарий
  • 0
  • Модераторы

OFFTOP: то что хранится в программе можно вытащить, заменить в реестре на это значение

 

P.S. Давайте только не будем обсуждать то, что эту защиту обойдет даже начинающий хакер.

обход защиты не обсуждаем, не превращайте тему в свалку

Ссылка на комментарий
  • 0

Подготовщик пишет в реестр случайную последовательность. Каждый раз - случайную (если он не видит записанное ранее значение).

Эту же случайную последовательность в закодированном виде пользователь передает вам.

Вы вносите в код программы полученное от пользователя значение.

Программа проверяет совпадение того, что в реестре с тем, что зашито в коде.

 

Если подготовщик запустить на другом компьютере - в реестре будет другая последовательность, отличающаяся от зашитой в программе.

 

Какая же это не-защита?

 

kami, прошу прощения. Сразу не внимательно прочитал. А что, Ваш вариант мне подходит. Видимо его и буду использовать.

 

ZuBy, Ваш код я бы использовал, но я кодирую на С++, а перевести с Паскаля у меня его так и не получилось...

Ссылка на комментарий
  • 0
  • Модераторы

 

ZuBy, Ваш код я бы использовал, но я кодирую на С++, а перевести с Паскаля у меня его так и не получилось...

 

почитайте тут

http://stackoverflow.com/questions/20062151/c-builder-wmi-invalid-query

Ссылка на комментарий
  • 0

Нашел готовый код:

void GetWmiInfo(TStrings *lpList, wchar_t* wsQuery)
{
    HRESULT hres;
    // устанавливаем контекст бехопасности
    hres =  CoInitializeSecurity(
            NULL,
            -1,                          // COM authentication
            NULL,                        // Authentication services
            NULL,                        // Reserved
            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication
            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
            NULL,                        // Authentication info
            EOAC_NONE,                   // Additional capabilities
            NULL                         // Reserved
    );

    IWbemLocator *pWbemLocator = NULL;    // провайдер WMI
    // инициализируем провайдера
    if(CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&pWbemLocator) == S_OK) {
        IWbemServices *pWbemServices = NULL;    // обьект, которому будем скармливать запрос (целевая машина)
        wchar_t* wsNamespace = (L"ROOT\\CIMV2"); // пространство имён
        // подключаемся к целевой машине
        if(pWbemLocator->ConnectServer(wsNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pWbemServices) == S_OK){
            IEnumWbemClassObject *pEnumClassObject = NULL; // энумератор записей
            wchar_t* wsWQL=(L"WQL"); // язык запросов WQL
            //wchar_t* wsQuery=(L"select * from WIN32_diskdrive");//+wsClass; // сам запрос к классу
            // выполняем запрос
            if(pWbemServices->ExecQuery(wsWQL, wsQuery, WBEM_FLAG_RETURN_IMMEDIATELY,NULL, &pEnumClassObject) == S_OK){
                // параметры для перебора коллекции записей
                IWbemClassObject *pClassObject = NULL;
                ULONG uCount = 1, uReturned = 0;
                if(pEnumClassObject->Reset() == S_OK) { // ?????????
                    int iEnumIdx = 0;
                    hres = pEnumClassObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
                    // перебираем колекцию
                    while( hres == S_OK) {
                        lpList->Add("---------------- ["+IntToStr(iEnumIdx)+"] -----------------");
                        SAFEARRAY *pvNames = NULL;
                        if(pClassObject->GetNames(NULL, WBEM_FLAG_ALWAYS | WBEM_MASK_CONDITION_ORIGIN, NULL, &pvNames) == S_OK) {
                            long vbl, vbu;
                            SafeArrayGetLBound(pvNames, 1, &vbl);
                            SafeArrayGetUBound(pvNames, 1, &vbu);
                            for(long idx=vbl; idx<=vbu; idx++) {
                                long aidx = idx;
                                wchar_t *wsName = 0;
                                VARIANT vValue;
                                VariantInit(&vValue);
                                SafeArrayGetElement(pvNames, &aidx, &wsName);
                                BSTR bs = SysAllocString(wsName);
                                HRESULT hRes = pClassObject->Get(bs, 0, &vValue, NULL, 0);
                                SysFreeString(bs);
                                if(hRes == S_OK) {
                                    AnsiString s;
                                    Variant v = *(Variant*)&vValue;
                                    if(v.IsArray()){
                                        for(int i=v.ArrayLowBound(); i<=v.ArrayHighBound(); i++){
                                            Variant a = v.GetElement(i);
                                            if(!s.IsEmpty()) s+=", ";
                                            s+=VarToStr(a);
                                        }
                                    } else {
                                        s = VarToStr(v);
                                    }
                                    lpList->Add(AnsiString(wsName)+"="+s);
                                }
                                VariantClear(&vValue);
                                SysFreeString(wsName);
                            }
                        }
                        if(pvNames)SafeArrayDestroy(pvNames);
                        iEnumIdx++;
                        hres = pEnumClassObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
                    } // конец по WHILE
                    if(hres != S_OK) lpList->Add((hres&0xFFFF));
                } // конец по pEnumClassObject->Reset()
                if(pClassObject)pClassObject->Release();
            } // конец по выполнению запроса
            if(pEnumClassObject)pEnumClassObject->Release();
        } // конец по подключению
        if(pWbemServices)pWbemServices->Release();
    } // конец инициализации провайдера
    if(pWbemLocator)pWbemLocator->Release();
}





void __fastcall TForm1::Button1Click(TObject *Sender)
{
  Memo1->Lines->Add("================== [WMI WQL] =================");
	wchar_t* ss=(L"Select * from Win32_MotherboardDevice");
    GetWmiInfo(Memo1->Lines, ss);
    Memo1->Lines->Add("");
}

Данный код выдает следующее:

 

================== [WMI WQL] =================

---------------- [0] -----------------
Availability=
Caption=Материнская плата
ConfigManagerErrorCode=
ConfigManagerUserConfig=
CreationClassName=Win32_MotherBoardDevice
Description=Материнская плата
DeviceID=Motherboard
ErrorCleared=
ErrorDescription=
InstallDate=
LastErrorCode=
Name=Материнская плата
PNPDeviceID=
PowerManagementCapabilities=
PowerManagementSupported=
PrimaryBusType=PCI
RevisionNumber=
SecondaryBusType=ISA
Status=
StatusInfo=
SystemCreationClassName=Win32_ComputerSystem
SystemName=EG-TA-CHSN
1
 

Так и не смог понять где тут UUID...

Ссылка на комментарий
  • 0
  • Модераторы
Так и не смог понять где тут UUID...

так его там и нету, используйте вот этот запрос

wchar_t* ss=(L"SELECT Description,MACAddress,IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE");

вернётся Mac/IP адрес

Ссылка на комментарий
  • 0

Как я понимаю, MAC адрес будет только в том случае, если в компьютере есть сетевая карта. А мне надо сделать привязку к такому оборудованию, которое гарантированно есть в компьютере. Например к процессору. У него есть уникальный номер?

Ссылка на комментарий
  • 0

Нет такого оборудования, которое уникально и доступно на компе. Задача программной защиты обычно использует совокупность хардварных идентификатров и алгоритмы, которые допускают частичное изменение их. Но это для тиражных продуктов, если вам не сильно критично сильная защита - то проще всего ориентироваться на дату установки Windows. 

Ссылка на комментарий
  • 0

Ну это Вы махнули, никакой уникальности у ID вичестера нет. Меняется этот номер программно на раз, поэтому любому молодому "хакеру" только и нужно будет узнать правильный ID, что тоже очень просто.

Ссылка на комментарий
  • 0

Прошу сильно не пинать, но вдруг понадобится:

 

Вопрос к серверу

	IdHTTP1->Request->UserAgent= "Mozilla/5.0 (Windows NT 6.1; rv:16.0) Gecko/20100101 Firefox/16.0";
	IdHTTP1->Request->Accept= "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
	IdHTTP1->Request->AcceptLanguage= "ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3";
	IdHTTP1->Request->Connection= "keep-alive";
	IdHTTP1->Request->Host= "store.steampowered.com";

	TStrings* ToServer;            // чего отправляем
	TStringStream* Response;       // чего получаем

	ToServer = new TStringList();
	Response = new TStringStream();


	ToServer->Add("param=" + Edit1->Text);
	IdHTTP1->HandleRedirects = true;
	IdHTTP1->Post("http://test.ru/test.php",ToServer, Response);
	Memo1->Lines->Add(Response->DataString);
	delete Response;
	delete ToServer;

На сервере лежит обычный PHP скрипт, для теста:

<?php
$param = $_REQUEST['param']; 	
$fd = fopen("text.txt", "w");
fwrite($fd, $param);
fclose($fd);
echo $param;
?>

Берет ваш "param", записывает в лежащий там-же текстовый файл, и возвращает. Не забыть про разрешения для файла (для PHP не критично, а вот текстовик должен иметь право на чтение и запись, в данном случае).

PHP прост, и написать сверку с ключами, лежащими в текстовом файле очень просто. И еще - не до конца понял, как работает, но PHP имеет доступ 0400 - но при этом работает.

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...