Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

SW copy protection

  • Please log in to reply
305 replies to this topic
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Here is a "proof of concept" script providing SW copy protection. It needs the SafeSW.ini file with the user information and an unlock code, without it the script request registration and exits. The unlock code is computed by the same process at the provider of the SW (AuthData), and it has to be emailed back to the user.

1. To protect the code the AHK script has to be compiled to an executable with ahk2exe, which is part of the AHK distribution. To prevent de-compilation a (good) password has to be provided. This way the program runs normally, but an attacker would have difficulties modifying the program or getting out secrets (keys).

2. Collect some data specific to the target machine. For simplicity we use the following environment variables:


AHK built in variables:


These should not change at OS updates or changing the network environment.

In addition one can use disk volume identifiers by running the consol command VOL > info.txt:

Volume in drive C has no label.
Volume Serial Number is 24B5-2345

The MAC (Media Access Controller) address of the network cards can be used, too, by Getmac /NH >> info.txt:

00-20-30-F0-F1-F2 Media disconnected

(To be safe only the first 17 characters should be used, the MAC of the first network card.)

Instead of saving this data in a temporary file, one can use CMDret - return output from console programs [DLL version]

3. Having the machine/user specific data, we hash them all (Message Authentication Code = MAC with key 0), so no confidential data can be retrieved from it (for privacy concerns) and everybody can verify that no user secret is hidden in there. This is called the PC fingerprint. We ask the user to register the program via email, with the PC fingerprint and with a username and valid return email address, where we would send the unlock code.

4. We generate an unlock code from the data and send the whole SafeSW.ini file back to the user. S/he saves it into the script's working directory. At startup the program verifies if the user info, the PC fingerprint and the unlock code match. If not, it asks for registering the SW, otherwise it starts working.

Fingerprint = MAC(0_key; PC_data)
Registration_data = Username, Email, Fingerprint → emailed for registration
Authentication_data = Username, Email, MAC(secret_key, Username, Email, Fingerprint) ← emailed back from registration site

5. Operation

- If no or invalid format Authentication_data: Request registration, Exit
- Retrieve PC_data, Username, Email (from stored Authentication_data)
- Compute MAC(secret_key; Username, Email, Fingerprint)
- If invalid Authentication_data in the SafeSW.ini file: Request registration, Exit
- Else: display Username, Email; Start working
- Periodically re-compute Fingerprint, at change: Exit.

6. SafeSW.ini

It contains the following information:

User = Laszlo Hars
Email = <!-- e --><a href="mailto:[email protected]">[email protected]</a><!-- e -->
UnlockCode = d0e8fd5f48edbc00

7. Notes

A malicious user could change the environment variables before the program starts, but it prevents normal operation, so he has to change it back. The periodic check of the PC fingerprint should make this attack more difficult.

MAC was used with key 0 for the Fingerprint, so users could verify the information sent for registration does not contain their secrets. A general, un-keyed hash is safer, as it is harder in the wrong PC to modify just one environmental variable to get a desired Fingerprint. We used MAC for simplicity.

There is more information that can be included in the machine specific data, like the physical serial number of the primary disk, the BIOS version, checksum, etc. You can make the sample script arbitrarily complex.

Some random, secret info can be written at installation somewhere hidden on the disk (e.g. in the registry) and it could be read back. (It is easy to catch the writing action, so this does not look very secure.) This random number could modify the executable itself, but it is complicated by the need of re-encrypting the code.

It is still possible to disassemble the program, and read the secret keys, but it is hard. Stronger protection is possible, but things would soon get very complicated.
; AutoHotkey Version: 1.0.39+
; Language:  English
; Platform:  Win2000/XP
; Author:    Laszlo Hars <www.Hars.US>
; Function:  SW copy protection

k0 = 0x11111111                  ; 128-bit secret key (example)
k1 = 0x22222222
k2 = 0x33333333
k3 = 0x44444444

l0 = 0x12345678                  ; 64- bit 2nd secret key (example)
l1 = 0x12345678

m0 = 0x87654321                  ; 64- bit 3rd secret key (example)
m1 = 0x87654321

IniFile = SafeSW.ini

GoSub    CheckAuth
SetTimer CheckAuth,1000
MsgBox,,,This SW is registered to`n%User% at %Email%,4

MsgBox OK   ; add your code here
Sleep 10000 ; add your code here


;---- End autoexecute secsion ----;

   IniRead User, %IniFile%, Registration, User
   IniRead Email,%IniFile%, Registration, Email
   IniRead Code, %IniFile%, Registration, UnlockCode
   PCdata = %PCdata%%PROCESSOR_LEVEL%%PROCESSOR_REVISION%%A_OSType%%A_OSVersion%%Language%
   Fingerprint := XCBC(Hex(PCdata,StrLen(PCdata)), 0,0, 0,0,0,0, 1,1, 2,2)
   Together = %User%%Email%%Fingerprint%
   AuthData := XCBC(Hex(Together,StrLen(Together)), 0,0, k0,k1,k2,k3, l0,l1, m0,m1)
   If (User="Error" || Email="Error" || Code <> AuthData)
      S =
      (  LTrim
         To: [email protected]
         Username = <enter your full name here>
         Your email address = <where you want the unlock code sent>
         PC Fingerprint = %Fingerprint%
      ClipBoard = %S%
      MsgBox Please Register! Email the following information`n`n%S%`n`n(it has been copied to the ClipBoard)

;---- Crypto functions ----;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TEA cipher ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Block encryption with the TEA cipher
; [y,z] = 64-bit I/0 block
; [k0,k1,k2,k3] = 128-bit key

TEA(ByRef y,ByRef z, k0,k1,k2,k3)
{                                   ; need  SetFormat Integer, D
   s = 0
   d = 0x9E3779B9
   Loop 32                          ; could be reduced to 8 for speed
      k := "k" . s & 3              ; indexing the key
      y := 0xFFFFFFFF & (y + ((z << 4 ^ z >> 5) + z  ^  s + %k%))
      s := 0xFFFFFFFF & (s + d)  ; simulate 32 bit operations
      k := "k" . s >> 11 & 3
      z := 0xFFFFFFFF & (z + ((y << 4 ^ y >> 5) + y  ^  s + %k%))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; XCBC-MAC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; x  = long hex string input
; [u,v] = 64-bit initial value (0,0)
; [k0,k1,k2,k3] = 128-bit key
; [l0,l1] = 64-bit key for not padded last block
; [m0,m1] = 64-bit key for padded last block
; Return 16 hex digits (64 bits) digest

XCBC(x, u,v, k0,k1,k2,k3, l0,l1, m0,m1)
   Loop % Ceil(StrLen(x)/16)-1   ; full length intermediate message blocks
      XCBCstep(u, v, x, k0,k1,k2,k3)

   If (StrLen(x) = 16)              ; full length last message block
      u := u ^ l0                   ; l-key modifies last state
      v := v ^ l1
      XCBCstep(u, v, x, k0,k1,k2,k3)
   Else {                           ; padded last message block
      u := u ^ m0                   ; m-key modifies last state
      v := v ^ m1
      x = %x%100000000000000
      XCBCstep(u, v, x, k0,k1,k2,k3)
   Return Hex8(u) . Hex8(v)         ; 16 hex digits returned

XCBCstep(ByRef u, ByRef v, ByRef x, k0,k1,k2,k3)
   StringLeft  p, x, 8              ; Msg blocks
   StringMid   q, x, 9, 8
   StringTrimLeft x, x, 16
   p = 0x%p%
   q = 0x%q%
   u := u ^ p
   v := v ^ q

Hex8(i)                             ; 32-bit integer -> 8 hex digits
   format = %A_FormatInteger%       ; save original integer format
   SetFormat Integer, Hex
   i += 0x100000000                 ; convert to hex, set MS bit
   StringTrimLeft i, i, 3           ; remove leading 0x1
   SetFormat Integer, %format%      ; restore original format
   Return i

Hex(ByRef b, n=0)                   ; n bytes data -> stream of 2-digit hex
{                                   ; n = 0: all (SetCapacity can be larger than used!)
   format = %A_FormatInteger%       ; save original integer format
   SetFormat Integer, Hex           ; for converting bytes to hex

   m := VarSetCapacity(b)
   If (n < 1 or n > m)
       n := m
   Loop %n%
      x := 256 + *(&b+A_Index-1)    ; get byte in hex, set 17th bit
      StringTrimLeft x, x, 3        ; remove 0x1
      h = %h%%x%
   SetFormat Integer, %format%      ; restore original format
   Return h

  • Members
  • 124 posts
  • Last active: Nov 13 2006 09:18 PM
  • Joined: 02 Sep 2004
Weird. I did kind of a similar thing just two days ago. I really wanted to prevent a users ini file that contained some info about their account (not passwords or anything like that) from being used by someone else. One of the things that I did was save some info about the users environment to the ini file (computer name was one of them), but in encrypted format. This prevents anyone from knowing what it is in plain text. Then when the program starts up, it verifies that its running on the machine that it was first authenticated on.

What you've done looks pretty cool. I know that I will be using it soon.
I am he of whom he speaks!

  • Members
  • 35 posts
  • Last active: May 20 2007 09:18 AM
  • Joined: 04 Oct 2005
I hate to ask, simply because this code is so far advanced from anything I could ever make, but could you please write a short script for creating the unlock code?

also, in this line there is a spelling error:

If (User="Error" || Eamil="Error" || Code <> AuthData)

I think I speak for everyone when I say... wow.. what a great contribution for the community. Thanks!

  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
I fixed in the original post a couple of bugs. Thanks, JohnL!

To generate the SafeSW.ini file replace the code between the line IniFile = ... and ExitApp with the following:
InputBox User, User,  Enter the User's name,,   220,140
InputBox Email,Email, Enter the User's Email,,  220,140
InputBox FGprt,FGprt, Enter the PC Fingerprint,,220,140
Together = %User%%Email%%FGprt%
Auth := XCBC(Hex(Together,StrLen(Together)), 0,0, k0,k1,k2,k3, l0,l1, m0,m1)
IniWrite %User%, %IniFile%, Registration, User
IniWrite %Email%,%IniFile%, Registration, Email
IniWrite %Auth%, %IniFile%, Registration, UnlockCode
You can remove the CheckAuth subroutine, too, but it does not matter.

  • Guests
  • Last active:
  • Joined: --

All very cool, but I would skip the Hard Drive test... of all the computers I've owned, every single one of them, had a new hard drive installed at one point.

I would be very annoyed (as an end customer), if my Software didn't work, after a clean install.

Just my 2 cents.

  • Members
  • 641 posts
  • Last active: Jan 02 2017 12:17 PM
  • Joined: 12 Jul 2005
I think that's the post I was looking for!
I didn't read the code, only description but it looks like that what I need!

Big thanks to Laszlo (and to Bonzo who put the topic on top :D )!

I think this way of protecting is very personal, so it wouldn't be hard to tell the user that he has to re-register the programm after changing hardware!


  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
The more information on the user's system we include in the PC fingerprint, the more often s/he needs to re-register the SW. Network cards get also replaced or new adapters added, the original disk could become secondary, when a larger one is installed, BIOS can be updated, etc. Using fewer pieces of configuration data makes the copy protection easier to break. I think the information used in the original post represent a good security/convenience tradeoff, and also high speed, simple code.

Another variation of the theme is when the user first receives (downloads) the compiled script, without the code doing the useful work. In response of the registration we send back the fully functional program with the information User, Email and UnlockCode hard coded. The original setup program then can be deleted. This way there is no need for the .ini file, but our email response has to contain a large attachment.

  • Members
  • 10 posts
  • Last active: Nov 21 2005 01:20 PM
  • Joined: 07 Nov 2005
Laszlo, thanks for this great solution - you are a genious!
I am developing a somewhat bigger program and want to share it.
But I definitely cannot give away the source by now, only the exe file (too much nights without getting sleep and too much justified complaints
by my family members). I would like to know, how many people still use it after a period of 90 days.

So I want to ask your permission to integrate this solution in my script.
Of course I could give credits to you, in whatever form you want
(in the registration-Gui, in the email with the code, in the documentaion, ...)

  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Whatever I post in the AHK forum is FREE, you can use it without any restriction, but I cannot guarantee anything about the scripts, either. I would be glad if you give me credit for the copy protection scheme in the documentation (my website is in my AHK profile).

  • Members
  • 10 posts
  • Last active: Nov 21 2005 01:20 PM
  • Joined: 07 Nov 2005
Thank you!
I expected an answer like that, but anyway, I felt bad to use it without your
explicit permission.

The credits will be given, and I hope, my program will be of some use for you too, so I can give something back.

  • Guests
  • Last active:
  • Joined: --
so when i get the persons info sent to my email

do i edit the .ini file or something please explian how i edit it

  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
As described above, you need a second version of the script, with the replacement code. You run it, enter (copy & paste) the user information you received via email, and it creates the ini file for you. Its path\name is assigned to "IniFile".

  • Members
  • 403 posts
  • Last active: Feb 15 2012 12:07 PM
  • Joined: 18 Jul 2006
got it thanks nice work

  • Members
  • 179 posts
  • Last active: Jul 31 2013 12:49 AM
  • Joined: 18 Jun 2007
I'm just not understanding the 2nd part of this, how do i create the ini file and the location of it, lol?

  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
This post explains how you generate the personalized ini file.