1 (edited by AD1408 2017-02-15 11:49:51)

Topic: Decent protection system for trial apps

I have been looking into how to protect an app created with MVD fairly good.


The issue is been discussed on the following topic with some interesting suggestions.
http://myvisualdatabase.com/forum/viewtopic.php?id=932


Dmitry kindly provides registry based protection. However, as far as I understand this method doesn't prevent a users distributing the application with their registration details to others to run it their own computers without registration.


I found one interesting suggestion by Rob in the topic above:

Another way is to provide the user a mechanism to provide you, the license provider, a code that is generated based on their NIC or motherboard sn.  The license key that is provided to the user uses this code value as part of the generated key.  Therefore, if they try to install the software on another machine, the key will not work for that new machine.
Rob

Here is a project in line of Robs suggestion. . "Generating a “unique” hardware ID using Delphi and the WMI" - by Rodrigo. He provides the code.
https://theroadtodelphi.com/2010/12/02/ … d-the-wmi/


Now the question is could Dmitry or somebody convert the delphi code into MVD?
I understand that some functions of Delphi may not be supported in MVD. I hope something similar can be done.


How I think it may work is as follows:
1. When user installs the trial app, unique virtual machine ID generated and displayed on custom About window of trial app.
2. User sends his/her virtual machine ID to developer and developer compiles app with user's unique virtual machine ID and sends to user. It'd be hard coded activation. Therefore it will only run on specific machine.


Unique virtual machine ID should include multiple hardware IDs/serial numbers. User may change their hard disk, motherboard etc. App will run if x numbers of specified hardware is present rather than requiring all specified hardware be present. This would help to reduce issues arising with hardware changes on the user machine.


Rodrigo's Delphi code:

program WMIHardwareID;
 
{$APPTYPE CONSOLE}
{$DEFINE Use_Jwscl} //necessary to obtain a hash of the data using md2, md4, md5 or sha1
 
uses
  {$IFDEF Use_Jwscl}
  JwsclTypes,
  JwsclCryptProvider,
  {$ENDIF}
  Classes,
  SysUtils,
  ActiveX,
  ComObj,
  Variants;
 
type
   TMotherBoardInfo   = (Mb_SerialNumber,Mb_Manufacturer,Mb_Product,Mb_Model);
   TMotherBoardInfoSet= set of TMotherBoardInfo;
   TProcessorInfo     = (Pr_Description,Pr_Manufacturer,Pr_Name,Pr_ProcessorId,Pr_UniqueId);
   TProcessorInfoSet  = set of TProcessorInfo;
   TBIOSInfo          = (Bs_BIOSVersion,Bs_BuildNumber,Bs_Description,Bs_Manufacturer,Bs_Name,Bs_SerialNumber,Bs_Version);
   TBIOSInfoSet       = set of TBIOSInfo;
   TOSInfo            = (Os_BuildNumber,Os_BuildType,Os_Manufacturer,Os_Name,Os_SerialNumber,Os_Version);
   TOSInfoSet         = set of TOSInfo;
 
const //properties names to get the data
   MotherBoardInfoArr: array[TMotherBoardInfo] of AnsiString =
                        ('SerialNumber','Manufacturer','Product','Model');
 
   OsInfoArr         : array[TOSInfo] of AnsiString =
                        ('BuildNumber','BuildType','Manufacturer','Name','SerialNumber','Version');
 
   BiosInfoArr       : array[TBIOSInfo] of AnsiString =
                        ('BIOSVersion','BuildNumber','Description','Manufacturer','Name','SerialNumber','Version');
 
   ProcessorInfoArr  : array[TProcessorInfo] of AnsiString =
                        ('Description','Manufacturer','Name','ProcessorId','UniqueId');
 
type
   THardwareId  = class
   private
    FOSInfo         : TOSInfoSet;
    FBIOSInfo       : TBIOSInfoSet;
    FProcessorInfo  : TProcessorInfoSet;
    FMotherBoardInfo: TMotherBoardInfoSet;
    FBuffer         : AnsiString;
    function GetHardwareIdHex: AnsiString;
  {$IFDEF Use_Jwscl}
    function GetHashString(Algorithm: TJwHashAlgorithm; Buffer : Pointer;Size:Integer) : AnsiString;
    function GetHardwareIdMd5: AnsiString;
    function GetHardwareIdMd2: AnsiString;
    function GetHardwareIdMd4: AnsiString;
    function GetHardwareIdSHA: AnsiString;
  {$ENDIF}
   public
     //Set the properties to  be used in the generation of the hardware id
    property  MotherBoardInfo : TMotherBoardInfoSet read FMotherBoardInfo write FMotherBoardInfo;
    property  ProcessorInfo : TProcessorInfoSet read FProcessorInfo write FProcessorInfo;
    property  BIOSInfo: TBIOSInfoSet read FBIOSInfo write FBIOSInfo;
    property  OSInfo  : TOSInfoSet read FOSInfo write FOSInfo;
    property  Buffer : AnsiString read FBuffer; //return the content of the data collected in the system
    property  HardwareIdHex : AnsiString read GetHardwareIdHex; //get a hexadecimal represntation of the data collected
  {$IFDEF Use_Jwscl}
    property  HardwareIdMd2  : AnsiString read GetHardwareIdMd2; //get a Md2 hash of the data collected
    property  HardwareIdMd4  : AnsiString read GetHardwareIdMd4; //get a Md4 hash of the data collected
    property  HardwareIdMd5  : AnsiString read GetHardwareIdMd5; //get a Md5 hash of the data collected
    property  HardwareIdSHA  : AnsiString read GetHardwareIdSHA; //get a SHA1 hash of the data collected
  {$ENDIF}
    procedure GenerateHardwareId; //calculate the hardware id
    constructor  Create(Generate:Boolean=True); overload;
    Destructor  Destroy; override;
   end;
 
function VarArrayToStr(const vArray: variant): AnsiString;
 
  function _VarToStr(const V: variant): AnsiString;
  var
  Vt: integer;
  begin
   Vt := VarType(V);
      case Vt of
        varSmallint,
        varInteger  : Result := AnsiString(IntToStr(integer(V)));
        varSingle,
        varDouble,
        varCurrency : Result := AnsiString(FloatToStr(Double(V)));
        varDate     : Result := AnsiString(VarToStr(V));
        varOleStr   : Result := AnsiString(WideString(V));
        varBoolean  : Result := AnsiString(VarToStr(V));
        varVariant  : Result := AnsiString(VarToStr(Variant(V)));
        varByte     : Result := AnsiChar(byte(V));
        varString   : Result := AnsiString(V);
        varArray    : Result := VarArrayToStr(Variant(V));
      end;
  end;
 
var
i : integer;
begin
    Result := '[';
    if (VarType(vArray) and VarArray)=0 then
       Result := _VarToStr(vArray)
    else
    for i := VarArrayLowBound(vArray, 1) to VarArrayHighBound(vArray, 1) do
     if i=VarArrayLowBound(vArray, 1)  then
      Result := Result+_VarToStr(vArray[i])
     else
      Result := Result+'|'+_VarToStr(vArray[i]);
 
    Result:=Result+']';
end;
 
function VarStrNull(const V:OleVariant):AnsiString; //avoid problems with null strings
begin
  Result:='';
  if not VarIsNull(V) then
  begin
    if VarIsArray(V) then
       Result:=VarArrayToStr(V)
    else
    Result:=AnsiString(VarToStr(V));
  end;
end;
 
{ THardwareId }
 
constructor THardwareId.Create(Generate:Boolean=True);
begin
   inherited Create;
   CoInitialize(nil);
   FBuffer          :='';
   //Set the propeties to be used in the hardware id generation
   FMotherBoardInfo :=[Mb_SerialNumber,Mb_Manufacturer,Mb_Product,Mb_Model];
   FOSInfo          :=[Os_BuildNumber,Os_BuildType,Os_Manufacturer,Os_Name,Os_SerialNumber,Os_Version];
   FBIOSInfo        :=[Bs_BIOSVersion,Bs_BuildNumber,Bs_Description,Bs_Manufacturer,Bs_Name,Bs_SerialNumber,Bs_Version];
   FProcessorInfo   :=[];//including the processor info is expensive [Pr_Description,Pr_Manufacturer,Pr_Name,Pr_ProcessorId,Pr_UniqueId];
   if Generate then
    GenerateHardwareId;
end;
 
destructor THardwareId.Destroy;
begin
  CoUninitialize;
  inherited;
end;
 
//Main function which collect the system data.
procedure THardwareId.GenerateHardwareId;
var
  objSWbemLocator : OLEVariant;
  objWMIService   : OLEVariant;
  objWbemObjectSet: OLEVariant;
  oWmiObject      : OLEVariant;
  oEnum           : IEnumvariant;
  iValue          : LongWord;
  SDummy          : AnsiString;
  Mb              : TMotherBoardInfo;
  Os              : TOSInfo;
  Bs              : TBIOSInfo;
  Pr              : TProcessorInfo;
begin;
  objSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  objWMIService   := objSWbemLocator.ConnectServer('localhost','root\cimv2', '','');
 
  if FMotherBoardInfo<>[] then //MotherBoard info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_BaseBoard','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Mb := Low(TMotherBoardInfo) to High(TMotherBoardInfo) do
       if Mb in FMotherBoardInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(MotherBoardInfoArr[Mb]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;
 
  if FOSInfo<>[] then//Windows info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_OperatingSystem','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Os := Low(TOSInfo) to High(TOSInfo) do
       if Os in FOSInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(OsInfoArr[Os]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;
 
  if FBIOSInfo<>[] then//BIOS info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_BIOS','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Bs := Low(TBIOSInfo) to High(TBIOSInfo) do
       if Bs in FBIOSInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(BiosInfoArr[Bs]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;
 
  if FProcessorInfo<>[] then//CPU info
  begin
    objWbemObjectSet:= objWMIService.ExecQuery('SELECT * FROM Win32_Processor','WQL',0);
    oEnum           := IUnknown(objWbemObjectSet._NewEnum) as IEnumVariant;
    while oEnum.Next(1, oWmiObject, iValue) = 0 do
    begin
      for Pr := Low(TProcessorInfo) to High(TProcessorInfo) do
       if Pr in FProcessorInfo then
       begin
          SDummy:=VarStrNull(oWmiObject.Properties_.Item(ProcessorInfoArr[Pr]).Value);
          FBuffer:=FBuffer+SDummy;
       end;
       oWmiObject:=Unassigned;
    end;
  end;
 
end;
 
function THardwareId.GetHardwareIdHex: AnsiString;
begin
    SetLength(Result,Length(FBuffer)*2);
    BinToHex(PAnsiChar(FBuffer),PAnsiChar(Result),Length(FBuffer));
end;
 
{$IFDEF Use_Jwscl}
function THardwareId.GetHashString(Algorithm: TJwHashAlgorithm; Buffer : Pointer;Size:Integer) : AnsiString;
var
  Hash: TJwHash;
  HashSize: Cardinal;
  HashData: Pointer;
begin
  Hash := TJwHash.Create(Algorithm);
  try
    Hash.HashData(Buffer,Size);
    HashData := Hash.RetrieveHash(HashSize);
    try
        SetLength(Result,HashSize*2);
        BinToHex(PAnsiChar(HashData),PAnsiChar(Result),HashSize);
    finally
      TJwHash.FreeBuffer(HashData);
    end;
  finally
    Hash.Free;
  end;
end;
 
function THardwareId.GetHardwareIdMd2: AnsiString;
begin
   Result:=GetHashString(haMD2,@FBuffer[1],Length(FBuffer));
end;
 
function THardwareId.GetHardwareIdMd4: AnsiString;
begin
   Result:=GetHashString(haMD4,@FBuffer[1],Length(FBuffer));
end;
 
function THardwareId.GetHardwareIdMd5: AnsiString;
begin
   Result:=GetHashString(haMD5,@FBuffer[1],Length(FBuffer));
end;
 
function THardwareId.GetHardwareIdSHA: AnsiString;
begin
   Result:=GetHashString(haSHA,@FBuffer[1],Length(FBuffer));
end;
 
{$ENDIF}
 
//testing the THardwareId object
var
  HWID : THardwareId;
  dt   : TDateTime;
begin
 try
    HWID:=THardwareId.Create(False);
    try
       dt := Now;
       HWID.GenerateHardwareId;
       dt := now - dt;
       Writeln(Format('Hardware Id Generated in %s',[FormatDateTime('hh:mm:nn.zzz',dt)]));
       Writeln(Format('%s %s',['Buffer ',HWID.Buffer]));
       Writeln('');
       Writeln(Format('%s %s',['Hex  ',HWID.HardwareIdHex]));
      {$IFDEF Use_Jwscl}
       Writeln(Format('%s %s',['Md2  ',HWID.HardwareIdMd2]));
       Writeln(Format('%s %s',['Md4  ',HWID.HardwareIdMd4]));
       Writeln(Format('%s %s',['Md5  ',HWID.HardwareIdMd5]));
       Writeln(Format('%s %s',['SHA1 ',HWID.HardwareIdSHA]));
      {$ENDIF}
      Readln;
    finally
     HWID.Free;
    end;                                                   
 except
    on E:Exception do
    begin
        Writeln(E.Classname, ':', E.Message);
        Readln;
    end;
  end;
end.
Adam
God... please help me become the person my dog thinks I am.

Re: Decent protection system for trial apps

My 2cents on it:

Creating a perfect copy protection is a dream. Even Microsoft know this.
You can make it difficult for easy end users, but exactly at the moment where your application becomes interesting for some people they will break it and your developing time for the copy protection was wasted time because to much people will break whatever you have created.
You can purchase some third party tools for copy protection who are working as wrappers for the software, but it would be also only a matter of time until it has been broken.
If you need something heuristic safe I can suggest something which must be coded as script. I write it here more understandable and not as computer code:

PART 1 (Customer create it):
randomized value 1 + randomized value 2 + number from hdd (MVD can read it in Version 3.2) = Text String
hex value of Text string (sample here in forum) = your machine code

PART 2 (You create it):
Serial No and - if you want - customer name + Part 1 + Software Version and Product ID + something else additional which is also in the software but not visible = Text String
hex value of Text string (sample here in forum) = your Activation code

Part 3 (Customer check it):
Part 1 + Serial No and - if you want - customer name + Software Version and Product ID + something else additional which is also in the software but not visible = Text String
hex value of Text string (sample here in forum) must be identical to Activation code

Customer must give you Machine Code
You give customer serial No + Activation code
If it fits it is activated.
No need for Mainboard ID, etc. because every activation system works more or less like this sample.
Sometimes the Serial No is included in the Activation code as a prefix or suffix.
The only way to make it more save would be a full encryption with AES or something else. But here you could have issues with export regulations from your country where you live.
Have fun with coding.

Re: Decent protection system for trial apps

How to get logical serial number of C:\

ShowMessage(GetHardDiskSerial('c'));

But this number will change if user reinstall Windows.

Dmitry.

Re: Decent protection system for trial apps

Hi lostdb,
Thanks a lot  for the info........ When you say "sample here in forum" what do you mean?


Dmitry,
What other parts Serial / ID of a computer MVD can read beside hdd?

Adam
God... please help me become the person my dog thinks I am.

5 (edited by rjkantor 2017-02-17 04:18:25)

Re: Decent protection system for trial apps

DriveSoft wrote:

How to get logical serial number of C:\

ShowMessage(GetHardDiskSerial('c'));

But this number will change if user reinstall Windows.

I would implement a license key system to validate the user and their computer.

Upon the user registering you would ask the user to provide the hard drive serial based on the method shown above.

You would enter this, plus their name and email address as components of the license key (plus anything else  you want to include in the license generation). 
The resulting code, would be given to the user and they would enter the license key which would validate that the computer matches the serial and you would display their name and email in the about screen. 

If you wanted to support multiple computers, you would just add these multiple serials codes into the key generator.  (See image)

Robb

Post's attachments

Attachment icon 2017-02-16_22-16-26.png 5.05 kb, 115 downloads since 2017-02-17 

Re: Decent protection system for trial apps

Thanks rj kantor..... do you have have something working that you can share?


Dmitry,
What other parts Serial / ID of a computer MVD can read beside hdd?

Adam
God... please help me become the person my dog thinks I am.

Re: Decent protection system for trial apps

Dear rjkantor,

According to you attached project how will the program identify that 365 days have past?

Re: Decent protection system for trial apps

bemorhona-qt wrote:

Dear rjkantor,

According to you attached project how will the program identify that 365 days have past?

One of two ways,
1) the date of the key has to be encoded into the string and the number of days support
2) the calculated expiration date of support needs to be encoded into the string

Then that is compared to the current date.

Robb

Re: Decent protection system for trial apps

AD1408 wrote:

Dmitry,
What other parts Serial / ID of a computer MVD can read beside hdd?

There is no more functions to read computer's ID, only GetHardDiskSerial

Dmitry.

10 (edited by rjkantor 2017-02-17 17:01:00)

Re: Decent protection system for trial apps

Another option, for a trial App and/or protecting your deployed application.

Engima Virtual Box is a free option that you can use to package all files into one application, if you include the sqlite.db into the package all changes will be reverted upon exiting.

http://enigmaprotector.com/en/aboutvb.html

If you want to protect your files but want to retain data changes, you would create a package including all files but the sqlite.db and put that in the same folder as the packaged files.  (see image) 

For the most security, you will want to ensure your db only contains user data and not any system required information.

Post's attachments

Attachment icon 2017-02-17_10-58-18.png 6.58 kb, 91 downloads since 2017-02-17 

Re: Decent protection system for trial apps

AD1408 wrote:

Hi lostdb,
Thanks a lot  for the info........ When you say "sample here in forum" what do you mean?

Seach for Text to HEX. There is a sample how you can convert a text to a HEX String and vice versa.

This can be used to obscure the licence text.

rjkantor wrote:

Another option, for a trial App and/or protecting your deployed application.

Engima Virtual Box is a free option that you can use to package all files into one application, if you include the sqlite.db into the package all changes will be reverted upon exiting.

http://enigmaprotector.com/en/aboutvb.html

If you want to protect your files but want to retain data changes, you would create a package including all files but the sqlite.db and put that in the same folder as the packaged files.  (see image)

For the most security, you will want to ensure your db only contains user data and not any system required information.

Question 1: Is the free version also free for commercial usage or is the paid version needed.
Question 2: Does it make release of updates more difficult or not?

DriveSoft wrote:

There is no more functions to read computer's ID, only GetHardDiskSerial

If you could include the libraries for network connections via network stack, it should be possible to access the MAC ID from the Network adapters. They are also unique. Hot to create a script should be the programmers work. Just publish the possible API commands.

Re: Decent protection system for trial apps

DriveSoft wrote:

How to get logical serial number of C:\

ShowMessage(GetHardDiskSerial('c'));

But this number will change if user reinstall Windows.


Sorry to reply on old thread, but I want to know if I clone the pc hard drive, is the logical serial number will be the same or cloned on both pc?

Thank you!

Re: Decent protection system for trial apps

Unfortunately I don't know.

Dmitry.

Re: Decent protection system for trial apps

Why this project not accepted the serial key, for a specific time, I download the latest version instead of 1.44 MVD but still not working.

Post's attachments

Attachment icon Your App Serial.zip 6.95 kb, 82 downloads since 2019-08-12 

JUST LEARNING, O GOD HELP ME.

Re: Decent protection system for trial apps

Asifmute,
Your app serial project works fine. When you enter in the app serial, only use the first 19 characters of the key within the script. The other 3 characters are the number of days till expiration. See below.

https://i.postimg.cc/Ghr1JpMy/Keys.jpg

Re: Decent protection system for trial apps

@ehwagner
Thanks for your intension, really appreciated.
Thanks once again.

JUST LEARNING, O GOD HELP ME.