How to manage a usage counter integrated in an app exec

Share your advanced PureBasic knowledge/code with the community.
fweil
Enthusiast
Enthusiast
Posts: 725
Joined: Thu Apr 22, 2004 5:56 pm
Location: France
Contact:

How to manage a usage counter integrated in an app exec

Post by fweil »

I wish some comments about this workaround.

I guess it answers several posts from here and the french forum.

Code: Select all

;
; Workaround for a program usage counter
; I guess it is interesting and well documented.
; This shows how to manage a usage counter encrypted in the executable itself.
; So you should save the source at a given place with a choosen name and compile it.
; For testing it properly, run the executable.
; As it is, this workaround will inform you of the license expiration after 3 runs.
;
; Read carefully comments and also I hope variable names are clear enough for understanding this.
;
; By extension, it is easy now to hard code ie the executable name in the same way and binary area, so that if
; the first executable name is not the same that the running, you can freeze the app, or do anything you want.
; ----------------------------------------------------
; Essai de cryptage d'un compteur d'utilisation d'un prog
; Je pense que c'est intéressant et bien documenté.
; Ce programme montre comment gérer un compteur d'utilisation encodé dans l'exécutable lui-même.
; Il faut donc sauver le source en lui donnant un nom et le compiler.
; Pour tester correctement le fonctionnement, lancer l'exécutable.
; Tel quel, vous devriez être informé que la licence est expirée après 3 lancements.
;
; Lisez bien les commentaires, en vous aidant des noms de variables pour une bonne compréhension.
;
; Par extensio, il est maintenant facile de coder en dur le nom de l'exécutable et de le comparer
; à lexécutable en cours pour geler l'application ou interagir comme vousl  le souhaitez.
;
; FWeil 20040707 : do not use this code without to inform me at fweil@internext.fr or on Purebasic forums.
;
; And you know what ? I am quite happy of this listing
;
;
; Set the size of the program messages array
; ----------------------------------------------------
; Fixe la taille du tableau des messages programme
;
#Max_Messages = 10

;
; Program messages array
; Tableau des messages du programme
;
Dim Messages.s(#Max_Messages)

;
; Procedure GetUserLanguage() : user language detection
; ----------------------------------------------------
; Procedure GetUserLanguage() : détection de la langue utilisateur
;
Procedure.s GetUserLanguage()
  Language.s
  GetSystemDefaultLangID = GetSystemDefaultLangID_()
  Select GetSystemDefaultLangID
    Case 1036   ; = French (Standard)
      Language = "FR"
    Default
      Language = "EN"
  EndSelect
  ProcedureReturn Language
EndProcedure

;
; Procedure Encrypt() : string encryption. The string can be decrypted using Decrypt()
; Both Encrypt() / Decrypt() procedures are symetrical, usable, but simple. You can change it by any encrypt / decrypt process you know or design.
; Just if you adapt this code, do not forget that the reserved space for the key is assigned by the second data of the KeyManagement: label in data section
; ----------------------------------------------------
; Procedure Encrypt() : encodage d'une chaine de caractères. La chaine peut être décodée avec Decrypt()
; Les procédures Encrypt() / Decrypt() sont symétriques, utilisables, mais simples. On peut les changer par tout procédé de codage / décodage connu ou personnalisé.
; Si ce code est changé, il faut simplement ne pas oublier que l'espace réservé pour la clé est défini par la deuxième donnée de la section data identifiée par KeyManagemen:
;
Procedure.s Encrypt(String.s)
  Result.s = ""
  For i = 1 To Len(String)
    Cod = Asc(Mid(String, i, 1))
    Result + Chr(((Cod & $F0) >> 4) + ($80 + Random($7F) & $F0)) + Chr((Cod & $0F) + ($80 + Random($7F) & $F0))
  Next
  ProcedureReturn Result
EndProcedure

;
; Procedure Decrypt() : string decryption using symmetrical algorythm in connection with Encrypt()
; ----------------------------------------------------
; Procedure Decrypt() : décodage d'une chaine de caractères en utilisant l'algorithme symétrique de Encrypt()
;
Procedure.s Decrypt(String.s)
  Result.s = ""
  For i = 1 To Len(string) Step 2
    Result + Chr(((Asc(Mid(String, i, 1)) & $0F) << 4) + (Asc(Mid(String, i + 1, 1)) & $0F))
  Next
  ProcedureReturn Result
EndProcedure

  ProgramName.s = Space(#MAX_PATH)
  GetModuleFileName_(0, @ProgramName, #MAX_PATH)
  ;
  ; User language detection and data section positioning to load program messages accordingly
  ; ----------------------------------------------------
  ; Détection de la langue utilisateur et placement approprié dans la zone data pour lire les messages du programme
  ;
  Select GetUserLanguage()
    Case "FR"
      Restore FR
    Default
      Restore EN
  EndSelect
  
  CurrentMessage = 0
  Repeat
    Read Message.s
    Messages(CurrentMessage) = Message
    CurrentMessage + 1
  Until Message = "EndDataSection"
  
  ;
  ; Data section positioning and read of the key to use to find the place of the usage counter
  ; ----------------------------------------------------
  ; Positionnement dans la zone data pour trouver la clé permettant d'accéder au compteur d'utilisation
  ;
  Restore KeyManagement
  Read Patch_Marker.s
  ;
  ; Program launch loading the encrypted 'usage counter' information
  ; This information is decrypted and parsed, and eventually updated information is encrypted again for saving in the file.
  ; ----------------------------------------------------
  ; Lancement du programme avec chargement de l'information 'compteur d'utilisation' encryptée
  ; Cette information est décryptée et analysée, puis mise à jour est ré-encryptée et enregistrée dans le fichier.
  ;
  If ReadFile(0, ProgramName)
      *Buffer = AllocateMemory(Lof())
      ReadData(*Buffer, Lof())
      For i = 0 To Lof()
        If PeekS(*Buffer + i, 6) = Patch_Marker
            CounterAddress = *Buffer + i + 6 ; CounterAddress is the address where the counter stays in the memory buffer
            Break
        EndIf
      Next
      If i => Lof()
          MessageRequester(Messages(0), Messages(1), #PB_MessageRequester_OK)
      EndIf
      CloseFile(0)
    Else
      MessageRequester(Messages(0), Messages(1), #PB_MessageRequester_OK)
  EndIf
  If CounterAddress
      Usage_Counter = Val(Decrypt(PeekS(CounterAddress, 6)))
      CounterAddress - *Buffer ; now the counter address is set to the file address, no more in the memory buffer
    Else
      Usage_Counter = 0
  EndIf

  FreeMemory(*Buffer)
  
  ;
  ; The counter value is placed in a buffer for editing in the file
  ; ----------------------------------------------------
  ; Le compteur est copié dans un buffer pour mise à jour du fichier
  ;
  *Key = AllocateMemory(6)
  Usage_Counter + 1
  PokeS(*Key, Encrypt(RSet(Str(Usage_Counter), 3, "0")))

  ;
  ; Here is the executable patch with the updated count
  ; ----------------------------------------------------
  ; Ici se trouve le patch du code exécutable avec le compteur à jour
  ;
  If OpenFile(0, ProgramName)
      FileSeek(CounterAddress)
      Debug WriteData(*Key, 6)
      CloseFile(0)
  EndIf
  
  ;
  ; In this skeleton I set the counter to 2. Don't forget to change this if you want to be smart
  ; ----------------------------------------------------
  ; Pour ce jeu d'essai j'ai mis le compteur à 2. N'oubliez pas de changer cette valeur pour être sympa dans vos applications
  ;
  If Usage_Counter > 2
      MessageRequester(Messages(0), Messages(2), #PB_MessageRequester_OK)
      Quit = #TRUE
    Else
      Quit = #FALSE
  EndIf

  ;
  ; From there the job is finished for the usage counter and you can place any regular app just using Quit #TRUE / #FALSE to do something or exit.
  ; ----------------------------------------------------
  ; A partir de là la gestion de compteur est terminée et on peut placer tout code de programme sous le contrôle éventuel de Quit #TRUE / #FALSE par exemple.
  ;
  If Quit = #FALSE
      MessageRequester("OK", "OK", #PB_MessageRequester_OK)
  EndIf
End

;
; Data section contains two data blocks corresponding to accepted languages
; ----------------------------------------------------
; La zone data contient les messages du programme avec un bloc pour chaque langue reconnue
;
DataSection

  FR:
  
  Data.s "Alerte"
  Data.s "Le programme ne peut être lancé : installation défaillante"
  Data.s "Le programme a été utilisé au delà de la licence accordée"
  Data.s "EndDataSection"
  
  EN:
  
  Data.s "Warning"
  Data.s "Can't run the program : bad installation"
  Data.s "Program used more than the license authorized"
  Data.s "EndDataSection"

EndDataSection

DataSection
  KeyManagement:
  ;
  ; Marker string to identify the position to use in the binary file
  ; ----------------------------------------------------
  ; Chaine marqueur pour identifier la position à utiliser dans le fichier binaire
  ;
  Data.s "SKBLZZ"
  ;
  ; Dummy string used as a reserved space in the binary file. This dummy string should not contain a valid number as Val(String) must equal 0.
  ; It is there only to lay in the binary code after compiling, and it will be changed each time the user will run the executable.
  ; ----------------------------------------------------
  ; Chaine factice utilisée comme réservation d'espace dans le fichier binaire. Cette chaine ne doit pas contenir une valeur numérique car la fonction
  ; Val de la chaine doit retourner 0. Cette chaine est placée de manière à se trouver à la bonne place dans le code généré par le compilateur, et
  ; sera changée à chaque lancement du programme par l'utilisateur.
  ;
  Data.s "TOTOTO"
EndDataSection
My avatar is a small copy of the 4x1.8m image I created and exposed at 'Le salon international du meuble à Paris' january 2004 in Matt Sindall's 'Shades' designers exhibition. The original laminated print was designed using a 150 dpi printout.
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Says OK.
Is that correct?
Not really sure what it's supposed to do
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
fweil
Enthusiast
Enthusiast
Posts: 725
Joined: Thu Apr 22, 2004 5:56 pm
Location: France
Contact:

Post by fweil »

geotrail ... are comments not clear enough ?

I will try to add some more explanations.

This source has the stuff to make possible when compiled to count the times the user runs the executable.

After a given count, the program will no more run.

It does not use the registry, nor an external file like a preferences file. The counter is integrated to the executable.

That's it.
My avatar is a small copy of the 4x1.8m image I created and exposed at 'Le salon international du meuble à Paris' january 2004 in Matt Sindall's 'Shades' designers exhibition. The original laminated print was designed using a 150 dpi printout.
User avatar
Rings
Moderator
Moderator
Posts: 1435
Joined: Sat Apr 26, 2003 1:11 am

Post by Rings »

hope you are sure that the counter is written in the file.:)
as far as i know you cannot write to a loaded(EXECUTABLE) file.
SPAMINATOR NR.1
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1285
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Post by Paul »

It does not use the registry, nor an external file like a preferences file
It also does not work ;)

You can't write to a file that is running.
Modify your code to this and see...

Code: Select all

  If OpenFile(0, ProgramName) 
      FileSeek(CounterAddress) 
      Debug WriteData(*Key, 6) 
      CloseFile(0)
      Else
      MessageRequester("Error","Can't write to open file")
  EndIf 
Error will always be thrown.
Image Image
fweil
Enthusiast
Enthusiast
Posts: 725
Joined: Thu Apr 22, 2004 5:56 pm
Location: France
Contact:

Post by fweil »

OK I was running in the editor and updating the exe which is different.

I am trying to find the way to have share rights with CreateFile_()

If I don't find the way today I will kill this post.
My avatar is a small copy of the 4x1.8m image I created and exposed at 'Le salon international du meuble à Paris' january 2004 in Matt Sindall's 'Shades' designers exhibition. The original laminated print was designed using a 150 dpi printout.
localmotion34
Enthusiast
Enthusiast
Posts: 665
Joined: Fri Sep 12, 2003 10:40 pm
Location: Tallahassee, Florida

Post by localmotion34 »

on my opinion, the message thing is great, but you have to CHECK and COMPARE the message to something. that usually means a "jump if equal to" or "jump if not equal to" in the ASM output. alot of crackers NOP out or change a JNE or JE in a reg check. they simply change the hex values at the right address. best advice is to use a while:wend expression or simple reg check multiple times inside the actual code.

EX:

Code: Select all


procedure secondaryregcheck()
;;code code code blah blah
if regcheck=registered
else
messagerequester("Nice Try","The program is not registered and you have bypassed the registration check.  BYE BYE"

END
endprocedure
select waitwindowevent()

  case #pb_event_menu
     select eventgadgetid()
         Case #menu_open
                 openfile---
                 secondaryregcheck()

  Case #pb_event_gadget
     select eventgadgetid()
        Case #whatever_gadget
               Dosomething()
                secondayregcheck()

Code: Select all

!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
User avatar
NoahPhense
Addict
Addict
Posts: 1999
Joined: Thu Oct 16, 2003 8:30 pm
Location: North Florida

..

Post by NoahPhense »

Just create a mandatory dll, that'll be your checker, put a procedure in
there that your app checks for every now an then.. also, you can change
the contents of that dll all day long..

As for hackers changing stuff in the exe's or dll's.. just create a fingerprint
of the files you need to monitor, have the app check that every now and
again.. if they don't match.. drop out..

- np
localmotion34
Enthusiast
Enthusiast
Posts: 665
Joined: Fri Sep 12, 2003 10:40 pm
Location: Tallahassee, Florida

Post by localmotion34 »

why create a DLL when the return from the DLL can be byte-patched in memory with a loader (AKA the infamous TRSH group who specializes in that). repeated uses of a procedure inside an exe would make it alot tougher to crack, especially if the procedure is called when a critical menu or button is clicked. fingerprinting isnt going to do any good, NOP out the jump if not equal to. and isnt like this the 500 millionth discussion on App protection, registrations? or is it more than that?

Code: Select all

!.WHILE status != dwPassedOut
! Invoke AllocateDrink, dwBeerAmount
!MOV Mug, Beer
!Invoke Drink, Mug, dwBeerAmount
!.endw
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Post by PB »

> The counter is integrated to the executable.

That's a very weak technique, for two reasons:

(1) The user can make the exe read-only.
(2) The user can use another copy of the exe.

Better to use the Registry to store the number of uses, IMO.
LuCiFeR[SD]
666
666
Posts: 1033
Joined: Mon Sep 01, 2003 2:33 pm

Post by LuCiFeR[SD] »

Simple rules to make the cracker pissed off enough to give up trying to steal your software.

(1) check every file you supply with your software by embedding watermarks/crc checksums etc. in images, mp3's, video or whatever and randomly check files at startup and shutdown :)... infact, you could check after every main operation (eg end of main loop)

(2) don't make it obvious that you have caught them trying to crack... give them several hours/days/weeks and then wallop them with a big flashing screen of "You naughty pirate" type messages :) and delete your software automagically... or something even more fun... replace their startup image when the load windows with something damn amusing :)

(3) No, I mean it... scatter stuff everywhere, multiple registry locations and compare the data from the registry with stuff in files.... use dll's to check the integrity of the main exe... but store the CRC's or whatever in external files that are randomly accessed. and just about any other dirty technique you can think off to add to this :). you have got to make it as difficult as possible, but obviously, if the person tying to crack is determined enough, no amount of protection is going to save you :)

just my few pennies worth on the subject. have fun :)
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

I like your way of thinking LuCiFeR[SD] :lol:
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
fweil
Enthusiast
Enthusiast
Posts: 725
Joined: Thu Apr 22, 2004 5:56 pm
Location: France
Contact:

Post by fweil »

I apologize to have missed some basics before making this post.

So I just changed some lines to make this program working (I guess it runs fine now), but using an external file.

I did not want to go in deep technics for program's safety (like splitting information between registry and multiple files). So I just create here a file in the system folder. A way to show what is possible to do.

The file contains a crypted counter, with a low quality encrypt / decrypt, but it shows how such things are possible to do.

Code: Select all

;
; Workaround for a program usage counter
; I guess it is interesting and well documented.
; This shows how to manage a usage counter encrypted in a key file placed in the system folder.
; As it is, this workaround will inform you of the license expiration after 3 runs.
;
; Read carefully comments and also I hope variable names are clear enough for understanding this.
;
; By extension, it is easy now to hard code information in the key file in the same way.
; ----------------------------------------------------
; Essai de cryptage d'un compteur d'utilisation d'un prog
; Je pense que c'est intéressant et bien documenté.
; Ce programme montre comment gérer un compteur d'utilisation encodé dans un fichier placé dans le répertoire sytème
; Tel quel, vous devriez être informé que la licence est expirée après 3 lancements.
;
; Lisez bien les commentaires, en vous aidant des noms de variables pour une bonne compréhension.
;
; Par extension, il est maintenant facile de coder en dur d'autres informations dans le fichier clé.
;
; FWeil 20040708 : do not use this code without to inform me at fweil@internext.fr or on Purebasic forums.
;
; And you know what ? I am quite happy of this listing
;

;
; Set the size of the program messages array
; ----------------------------------------------------
; Fixe la taille du tableau des messages programme
;
#Max_Messages = 10

;
; Program messages array
; Tableau des messages du programme
;
Dim Messages.s(#Max_Messages)

;
; Procedure GetUserLanguage() : user language detection
; ----------------------------------------------------
; Procedure GetUserLanguage() : détection de la langue utilisateur
;
Procedure.s GetUserLanguage()
  Language.s
  GetSystemDefaultLangID = GetSystemDefaultLangID_()
  Select GetSystemDefaultLangID & $FFFF
    Case 1036   ; = French (Standard)
      Language = "FR"
    Default
      Language = "EN"
  EndSelect
  ProcedureReturn Language
EndProcedure

;
; Procedure Encrypt() : string encryption. The string can be decrypted using Decrypt()
; Both Encrypt() / Decrypt() procedures are symetrical, usable, but simple. You can change it by any encrypt / decrypt process you know or design.
; ----------------------------------------------------
; Procedure Encrypt() : encodage d'une chaine de caractères. La chaine peut être décodée avec Decrypt()
; Les procédures Encrypt() / Decrypt() sont symétriques, utilisables, mais simples. On peut les changer par tout procédé de codage / décodage connu ou personnalisé.
;
Procedure.s Encrypt(String.s)
  Result.s = ""
  For i = 1 To Len(String)
    Cod = Asc(Mid(String, i, 1))
    Result + Chr(((Cod & $F0) >> 4) + ($80 + Random($7F) & $F0)) + Chr((Cod & $0F) + ($80 + Random($7F) & $F0))
  Next
  ProcedureReturn Result
EndProcedure

;
; Procedure Decrypt() : string decryption using symmetrical algorythm in connection with Encrypt()
; ----------------------------------------------------
; Procedure Decrypt() : décodage d'une chaine de caractères en utilisant l'algorithme symétrique de Encrypt()
;
Procedure.s Decrypt(String.s)
  Result.s = ""
  For i = 1 To Len(string) Step 2
    Result + Chr(((Asc(Mid(String, i, 1)) & $0F) << 4) + (Asc(Mid(String, i + 1, 1)) & $0F))
  Next
  ProcedureReturn Result
EndProcedure

  ;
  ; Look for the path of Windows system files
  ; ----------------------------------------------------
  ; Recherche du chemin des fichiers systèmes Windows
  ;
  WindowsDirectory.s = Space(#MAX_PATH)
  GetSystemDirectory_(WindowsDirectory.s, #MAX_PATH)
  
  ;
  ; Get the program filename
  ; ----------------------------------------------------
  ; Récupère le nom de fichier du programme
  ;
  ProgramName.s = Space(#MAX_PATH)
  GetModuleFileName_(0, @ProgramName, #MAX_PATH)
  
  ;
  ; Set the key filename
  ; ----------------------------------------------------
  ; Construit le nom du fichier clé
  ;
  KeyFilename.s = GetFilePart(ProgramName)
  KeyFilename = WindowsDirectory + "\" + Mid(KeyFilename, 1, FindString(KeyFilename, ".", 1) - 1) + ".key"
  
  ;
  ; User language detection and data section positioning to load program messages accordingly
  ; ----------------------------------------------------
  ; Détection de la langue utilisateur et placement approprié dans la zone data pour lire les messages du programme
  ;
  Select GetUserLanguage()
    Case "FR"
      Restore FR
    Default
      Restore EN
  EndSelect
  
  CurrentMessage = 0
  Repeat
    Read Message.s
    Messages(CurrentMessage) = Message
    CurrentMessage + 1
  Until Message = "EndDataSection"
  
  ;
  ; Open the key file to read the crypted content. If the file does not exist, the counter is set to 0
  ; ----------------------------------------------------
  ; Ouvre le fichier clé pour lire le contenu crypté. Si le fichier n'existe pas, le compteur est mis à 0
  ;
  If ReadFile(0, KeyFilename)
      LengthOfFile = Lof()
      *Buffer = AllocateMemory(LengthOfFile)
      ReadData(*Buffer, LengthOfFile)
      CloseFile(0)
      Key.s = PeekS(*Buffer)
      Usage_Counter = Val(Decrypt(Key)) + 1
      CloseFile(0)
    Else
      Usage_Counter = 0
  EndIf
  FreeMemory(*Buffer)
  
  ;
  ; Create a new key file with the updated encrypted counter
  ; ----------------------------------------------------
  ; Créé un nouveau fichier clé avec la nouvelle valeur encryptée du compteur
  ;
  If CreateFile(0, KeyFilename)
      WriteString(Encrypt(RSet(Str(Usage_Counter), 3, "0")))
      CloseFile(0)
    Else
      MessageRequester(Messages(0), Messages(1), #PB_MessageRequester_OK)
      End
  EndIf
  
  ;
  ; In this skeleton I set the counter to 2. Don't forget to change this if you want to be smart
  ; ----------------------------------------------------
  ; Pour ce jeu d'essai j'ai mis le compteur à 2. N'oubliez pas de changer cette valeur pour être sympa dans vos applications
  ;
  If Usage_Counter > 2
      MessageRequester(Messages(0), Messages(2), #PB_MessageRequester_OK)
      End
  EndIf

  ;
  ; From there the job is finished for the usage counter. Place your code after
  ; ----------------------------------------------------
  ; A partir de là la gestion de compteur est terminée. Placer votre code après
  ;
   MessageRequester("OK", Messages(3) + " " + Str(Usage_Counter) + " " + Messages(4), #PB_MessageRequester_OK)
End

;
; Data section contains two data blocks corresponding to accepted languages
; ----------------------------------------------------
; La zone data contient les messages du programme avec un bloc pour chaque langue reconnue
;
DataSection

  FR:
  
  Data.s "Alerte"
  Data.s "Le programme ne peut être lancé : installation défaillante"
  Data.s "Le programme a été utilisé au delà de la licence accordée"
  Data.s "Vous avez utilisé ce programme "
  Data.s "fois"
  Data.s "EndDataSection"
  
  EN:
  
  Data.s "Warning"
  Data.s "Can't run the program : bad installation"
  Data.s "Program used more than the license authorized"
  Data.s "You used this program "
  Data.s "time(s)"
  Data.s "EndDataSection"

EndDataSection
My avatar is a small copy of the 4x1.8m image I created and exposed at 'Le salon international du meuble à Paris' january 2004 in Matt Sindall's 'Shades' designers exhibition. The original laminated print was designed using a 150 dpi printout.
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1285
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Post by Paul »

Now as PB mentioned, this is a very weak technique since you can easily do a seach for files that have just changed... which shows you that a file called *.key has just been created in the system folder after you have run your program.

Simply make a little program (loader) that you would run instead of your app which first deletes this file and then runs the original app or make a copy of the key file and then have your loader replace the keyfile with your copy every time before running the original app.

Might as well put your key file in the same folder as your app and not clutter the harddrive since it really isn't protecting anything ;)
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Some more ideas:

Have some obvious checks and techniques that do the job (honeypots), and some you have hidden as best you can. Make the beggers work and hopefully make them frustrated as they crack it - and it don't crack. Repeatedly.

Don't do your protection checks inside a routine. Hard code it around the place, and use different coding approach for each, even if simple changes (to avoid patterns).

For those who monitor file accesses to see if config/preferences/etc files are updated, update these files often as you can with normal data, without degrading app performance too much. Let the security counters, et al, not be the first or last changes.

Don't use standard preferences file formats.

Reuse other often-used program control flags/switches, so that the value seems (because it is) part of the app. Also zapping the value zaps the app, and not zapping the value means the check stays.

Have an app that is not too popular and therefore not worth cracking. :)
@}--`--,-- A rose by any other name ..
Post Reply