Delphi - Hogyan védjük alkalmazásainkat az illetéktelen felhasználástól?

forráskód letöltése
A ma bemutatásra kerülő cikkben egy olyan komponenst készítünk, mellyel alkalmazásunknak adhatunk különféle védelmet. A komponens ötféle funkciót egyesít magában. Így készíthetünk vele idő- és dátumkorlátos, egyszer futtatható, illetve csak megadott lemezről futtatható alkalmazásokat. Használatával könnyen készíthetünk shareware programokat is.
A mellékelt példaprogram megnyitása előtt az AppProtector.pas-ban lévő komponenst telepítenie kell a Delphi alá. Ehhez válassza a Component - Install Component menüpontot.
Nézzük sorban az egyes funkciókat. Nagyon sok helyen elterjedt megoldás a dátumkorlátos program, mely egy bizonyos időponton túl nem indul el, hanem helyette egy figyelmeztető üzenetet ad. Esetünkben kétféle dátumkorlátos funkciót is megvalósítottunk. Az Expire függvény megvizsgálja a mai dátumot, majd ha az ExpirationDate property-ben megadott dátumnál nagyobb, egy üzenettel tér vissza. A vizsgálat csak akkor hajtódik végre, ha a SetExpire property-t True-ra állítottuk. Ne felejtsük el a FormName-ben megadni annak a Form-nak a nevét, mely először jön létre a programunkban!
procedure TAppProtector.Expire;
begin
  if FSetExpire then
    begin
      if (Date >= FExpireDate) then
        begin
          ShowMessage('Letelt az időkorlátozás!');
          FFormName.Close;
        end;
    end;
end;
A másik megoldásban előre megadható a futtatás kezdeti és végső dátuma. Így akár előre beprogramozhatjuk alkalmazásunk korlátlan futtatásának időpontját. Amennyiben a TrialStartDate property-be beírjuk a kezdeti dátumot, és a TrialDatesNo-ban megadjuk, hogy programunk hány napig legyen futtatható, majd engedélyezzük a SetTrialDates property-t, a végső dátum automatikusan beírásra kerül a TrialStartEnd-be. Külön-külön megvizsgáljuk, hogy az aktuális dátum a beállított intervallum előtt vagy után van-e, és ennek megfelelő üzenetet íratunk ki.
procedure TAppProtector.TrialDates;
begin
  if FSetTrialDatesNo then
    begin
      if (Date<FTrialStart) then
      begin
        ShowMessage('A program csak '+DateToStr(FTrialStart)+'-'+DateToStr(FTrialEnd)+' között használható!');
        FFormName.Close;
      end;
      if (Date>FTrialEnd) then
      begin
        ShowMessage('A(z) '+IntToStr(FTrialDatesNo)+' napos kipróbálási idő lejárt!');
        FFormName.Close;
      end;
    end;
end;
Alkalmazásunkat nemcsak dátum, hanem időkorláttal is elláthatjuk. Példánkban másodperces pontossággal beállítható, hogy mennyi ideig legyen a programunk futtatható. Mindehhez a CountTimeSec property-be be kell írnunk a kívánt értéket, majd engedélyezni a SetCountTime-ot. Ez a funkció tulajdonképpen négy részből áll. A komponens létrejöttekor inicializálunk egy Timer nevű időzítőt, melynek intervallumát 1 másodpercre állítjuk, és az OnTimer eseményéhez hozzárendeljük a DoTimer függényt.
constructor TAppProtector.Create(AOwner : TComponent);
begin
  inherited Create(AOwner);
  Timer:=TTimer.Create(self);
  i:=0;
  with Timer do
  begin
    OnTimer:=DoTimer;
    Interval:=1000;
    Enabled:=False;
  end;
end;
A Timer engedélyezésekor lefut a DoTimer függvény. Ebben egy i nevű változót növelünk minden egyes másodpercben. Amikor az eléri az FCountTime-ban megadott értéket, az időzítést leállítjuk, és meghívjuk a CountTime függvényt.
procedure TAppProtector.DoTimer;
begin
  inc(i);
  if FCountTime=i then
  begin
    Timer.Enabled:=False;
    CountTime;
  end;
end;
Ebben a függvényben megvizsgáljuk, hogy a SetCountType property engedélyezve van-e. Amennyiben igen, egy üzenetet íratunk ki, majd lezárjuk az alkalmazást.
procedure TAppProtector.CountTime;
begin
  if FSetCountTime then
    begin
      ShowMessage('A program használatának ideje lejárt!');
      FFormName.Close;
    end;
end;
A programunkból az időzítést a StartTimer függvény meghívásával indíthatjuk el.
procedure TAppProtector.StartTimer;
begin
  Timer.Enabled:=True;
end;
Lehetőségünk van arra is, hogy a programunknak egy session-ön belül csak egyszer engedélyezzük a futtatását. Mindezt a RunAtOnce függvény meghívásával tehetjük meg, amennyiben a komponens SetRunAtOnce property-jét igazra állítjuk.
procedure TAppProtector.RunAtOnce;
begin
  if FSetRunAtOnce then
    begin
      if GlobalFindAtom('PROGRAM_AT_ONCE')=0 then
        GlobalAddAtom('PROGRAM_AT_ONCE')
      else
      begin
        ShowMessage('     A program egy session alatt csak egyszer futtatható!'#13#10+'Amennyiben újra futtatni szeretné, indítsa újra a Windows-t!');
        FFormName.Close;
      end;
    end;
end;
A programunkat mindaddig nem tudjuk újra futtatni, míg a Windows-t újra nem indítottuk.
Végül a DiskSerialNo függvény meghívásával megvizsgálhatjuk azt, hogy az alkalmazásunkat az általunk megadott sorozatszámú lemezről futtatják-e. Ezzel megelőzhetjük az illetéktelen másolatok készítését. Ez akkor jöhet a segítségünkre, ha például CD lemezen terjesztjük a programunkat. A komponens DiskSerialNumber property-jében meg kell adnunk annak a lemeznek a sorozatszámát, melyre engedélyezni szeretnénk a program futtatását. A vizsgálat csak akkor megy végbe, ha a SetDiskSerialNumber-t igazra állítjuk. Az aktuális meghajtó sorozatszámát a GetVolumeInformation függvény segítségével kérdezzük le, majd összehasonlítjuk azt az FDiskSerialNo változóban tárolt értékkel. Amennyiben a kettő nem egyezik meg, hibaüzenettel térünk vissza, majd lezárjuk az alkalmazást.
procedure TAppProtector.DiskSerialNo;
var
  lpRootPathName,
  lpVolumeNameBuffer,
  lpFileSystemNameBuffer: array[0..63] of char;
  lpMaximumComponentLength, lpVolumeNumber,
  lpFileSystemFlags: DWORD;
  TmpStr, CurrentDrive: string;
begin
  if FSetDiskSerialNumber then
  begin
    TmpStr:=GetCurrentDir;
    CurrentDrive:=Copy(TmpStr,1,3);
    StrPCopy(lpRootPathName, CurrentDrive);
    GetVolumeInformation(
      lpRootPathName,
      lpVolumeNameBuffer,
      64,
      @lpVolumeNumber,
      lpMaximumComponentLength,
      lpFileSystemFlags,
      lpFileSystemNameBuffer,
      64
    );
    if (FDiskSerialNO<>IntToHex(lpVolumeNumber, 8)) then
    begin
      ShowMessage('A programot nem a megfelelő lemezről indította el!');
      FFormName.Close;
    end;
 end;
end;
Természetesen a fent bemutatott módszereken kívül még számtalan módja van a programunk megvédésének. Azonban minden alkalmazáshoz meg kell találni a megfelelő módot. Így például játékprogramokhoz célszerű az időlimites, míg egyéb irodai szoftverhez a dátumkorlátos megoldást választani. Természetesen jelszavak és jogok használatával még biztonságosabbá tehetjük programunkat.