it would not use a hardware-detection. Sometimes hardware breaks or is replaced (bigger ssd for example) and your programm will not run. I would add a very simple check. a License-file encoded with AES and a inbuild key. Of course a hacker could find the aes-key in the app, but when he can do this, he could also remove the check code at all.
When you want a time-limited-license, you can't trust the local date. You can try to validate the current date with the internet - or if not possible, simple check all files in the Temp-Folder and check the date. Also you can save the current-date when the program quits and check on start, if the date is older than the quit-date.
Why the temp-folder? A user can change the current date to an earlier date to bypass the date check. But he must set the date back to valid date (for example when he want to use a browser - or all https-sides will be invalid). And because many programms create Temp-files, there must be at least one file created with the correct date.
With an aes-encrypted license-file and a date check you should prevent 99.99% of all basic users to freely copy your program. And don't forget to add a "licensed to user"-message in the info-box.
here is my example:
Code: Select all
EnableExplicit
Procedure ValidateDate()
Protected dir
Protected date, mdate
;Get the date of the all entries in the Temp-Folder
dir = ExamineDirectory(#PB_Any, GetTemporaryDirectory(), "*.*")
If dir
While NextDirectoryEntry(dir)
mdate = DirectoryEntryDate(dir, #PB_Date_Modified)
If mdate > date
date = mdate
EndIf
mdate = DirectoryEntryDate(dir, #PB_Date_Accessed)
If mdate > date
date = mdate
EndIf
mdate = DirectoryEntryDate(dir, #PB_Date_Created)
If mdate > date
date = mdate
EndIf
Wend
FinishDirectory(dir)
EndIf
; current date older than Temp-Files?
mdate = date - Date()
If mdate > 60*60*24 ; little security-gab
ProcedureReturn #Null
EndIf
; older than compile-Time?
If Date() < #PB_Compiler_Date
ProcedureReturn #Null
EndIf
; it seems, that everything is all right
ProcedureReturn Date()
EndProcedure
Structure License_File
Serial.s
UserName.s
StartDate.l
EndDate.l
EndStructure
;Generate Serial
; for guid-code see https://www.purebasic.fr/english/viewtopic.php?f=12&t=74015
; here is only the windows-code
Structure UUID
data1.l
data2.w
data3.w
data4.b[8]
EndStructure
Procedure.s GenerateSerial()
Protected.uuid guid
Protected.i pst
Protected.s st
If UuidCreate_(guid) = #RPC_S_OK
If UuidToString_(guid, @pst) = #RPC_S_OK
If pst
st = PeekS(pst, -1, #PB_Unicode)
RpcStringFree_(@pst)
EndIf
EndIf
EndIf
ProcedureReturn st
EndProcedure
Procedure CreateLicenseFile(filename.s, *key, Name.s, Serial.s, startDate.l, endDate.l)
Protected.License_File license_Data
;Generate License-Structure
license_Data\Serial = Serial
license_Data\UserName = Name
license_Data\StartDate = startDate
license_Data\EndDate = endDate
;Transfer into json and generate a string from the structure
Protected json, composed.s
json = CreateJSON(#PB_Any)
If json = 0
ProcedureReturn #False
EndIf
InsertJSONStructure( JSONValue(json), license_Data, License_File)
ComposeJSON(json)
composed.s = ComposeJSON(json)
;encrypt structure
Protected ByteLen = StringByteLength( composed + "*" ) ; "*" = Null-terminator
Protected *out = AllocateMemory(ByteLen)
If *out = #Null
ProcedureReturn #False
EndIf
AESEncoder(@composed, *out, ByteLen, *key, 256, #Null, #PB_Cipher_ECB)
;savefile
Protected fout
fout = CreateFile(#PB_Any,filename)
If Not fout
FreeMemory(*out)
ProcedureReturn #False
EndIf
WriteData(fout, *out, ByteLen)
CloseFile(fout)
FreeMemory(*out)
ProcedureReturn #True
EndProcedure
Procedure OpenLicenseFile(filename.s, *key, *ret.License_File)
; load file
Protected fin
Protected ByteLen
Protected *in, *out
fin = ReadFile(#PB_Any, filename)
If Not fin
ProcedureReturn #False
EndIf
ByteLen = Lof(fin)
*in = AllocateMemory(ByteLen)
If *in = #Null
CloseFile(fin)
ProcedureReturn #False
EndIf
*out = AllocateMemory(ByteLen)
If *out = #Null
FreeMemory(*in)
CloseFile(fin)
ProcedureReturn #False
EndIf
ReadData(fin, *in, ByteLen)
CloseFile(fin)
; decode
AESDecoder(*in, *out, ByteLen, *key, 256, #Null, #PB_Cipher_ECB)
FreeMemory(*in)
Protected.s str = PeekS(*out,ByteLen)
FreeMemory(*out)
; Create JSON
Protected json = ParseJSON(#PB_Any,str)
If Not json
ProcedureReturn #False
EndIf
ExtractJSONStructure(JSONValue(json),*ret, License_File)
EndProcedure
;we create a license-file
CreateLicenseFile("demo.lic", ?key, "Otto", GenerateSerial(), Date(), Date()+10000)
; now we read it
Define.License_File license
OpenLicenseFile("demo.lic", ?key, license)
Debug "User:"+license\UserName
Debug "Serial:"+license\Serial
Debug "Start:"+FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", license\StartDate)
Debug "End:"+FormatDate("%dd.%mm.%yyyy %hh:%ii:%ss", license\EndDate)
; check if the date is valid
; additional-check: Store the current date when the programms quits and check on start if the date is newer than the quit-date
Define date = ValidateDate()
If date >= license\StartDate And date <= license\EndDate
Debug "Date is valid!"
Else
Debug "-- rejected - go to demo-mode"
EndIf
DataSection
Key:; 256-bit-key - should be changed
Data.b $06, $a9, $21, $40, $36, $b8, $a1, $5b, $51, $2e, $03, $d5, $34, $12, $00, $06
Data.b $06, $a9, $21, $40, $36, $b8, $a1, $5b, $51, $2e, $03, $d5, $34, $12, $00, $06
EndDataSection