Click here to Skip to main content
Click here to Skip to main content

Phone Home: An Approach to Software Activation

, 19 Sep 2006
Rate this:
Please Sign up or sign in to vote.
This article describes an easy approach to keeping track of each installation of an application. The intent of the example is to demonstrate the use of a simple web service to collect and store information about each user and machine running the application.

Introduction

This article describes an easy approach to keeping track of each installation of an application. The intent of the example is to demonstrate the use of a simple web service to collect and store information about each user and machine running the application.

I was originally driven to coming up with something like this in response to working for an employer that operated strictly on the honor system. By that, I mean that the company would sell its software by the seat to individual clients, but with no licensing or other restrictions imposed or enforced by the software that might prevent the installation of additional copies of that software; for example, if a client purchased five seats of the product, it was assumed by the company that the client would only install it five times.

In one particular sale of the product, I felt that it was likely that the client would install additional copies beyond what they had purchased (given the magnitude of work they had to perform, five seats seemed woefully inadequate), and I had asked for approval to implement a licensing scheme in the code; the company declined to do this for whatever reason. They did agree for me to devise a method for tracking installations without requiring a license. The code in this project is a derivation of what I did there, mainly because, in the original approach, I was not permitted to terminate the application if it were not registered; I was only able to collect information on the installations. What we discovered was that the five purchased seats were installed over 100 times by the client (at about a $380,000 loss in revenue to the company in this one instance).

When I wrote this example version, I did it a little differently in that I create a bogus registry entry that is used to determine whether or not the application was installed and registered through the web service. If the product is not registered, I prevent the application from being used. When I terminate the application, I inform the user that the registration process is incomplete and that the process requires an active connection to the internet. This works much like what is seen when you are required to, for example, activate a copy of Microsoft Word through an internet connection. The registry key is checked each time the application is executed; if the registry key exists and its value indicates that the product is registered, then nothing else happens; if that is not the case, the application will not run until the registration occurs. If a person were to X-copy the application onto another machine, the application will still check for the key the first time it is executed, and therefore it will not run until it is also registered.

I did not use it in this example, but you could use the original installation date and allow the user to use the product for some set period of days prior to locking them out, which could be useful if you are authoring shareware with a time limited trial period. Here in this example, I just lock them out from the beginning. This approach does not prevent the client from over-installing the product; it merely forces each installation to be registered through the web service. You could easily modify the web service to check and see if an individual license has been registered and prevent additional installations of that license, but for the purposes of this example, I am only demonstrating the capture of each installation through the service.

As it stands, the example is not the be-all, end-all solution to securing your application against theft. It does, however, offer an approach for determining the extent of abuse you may be encountering with a release of custom software to a client company. That information may be useful in making an argument for building a more robust approach to licensing your distributions.

Getting Started

To get started, unzip the source code included with this project. In the code example, you will find a web service and a WinForms application. Open the Web Service up, and make any changes necessary to get it to operate on your machine. The project containing the web service has an MS Access database included, and the database contains a single table used to collect the installation information. In reality, you’d probably configure a database and table in SQL Server or Oracle to contain the data, but for the purposes of this demonstration, it was easier to pack it up in Access. Aside from the MS Access database, you will note that the web service project only contains a single service entitled, “RegisterInstallation”.

Once you’ve configured the web service, run it and check it with the proxy to validate that it is updating the database correctly:

Figure 1: Run the Web Service

With the web service running, select the link to “RegisterInstallation”:

Figure 2: Testing the “RegisterInstallation” Web Service

Once you’ve keyed in some sample values as indicated in Figure 2, click on the “Invoke” button and view the response; if successful, you should see something like what is shown in Figure 3:

Figure 3: The Web Service returns “true” if the installation information is saved to the database

Given the web service has returned a true, take a look at the database to confirm that the values entered have been stored in the table:

Figure 4: The AppInstallers table updated by the Web Service

Having confirmed that the web service is up and running, open the application solution in Visual Studio 2005. Examine the contents of the project in the Solution Explorer, you should see the following:

Figure 5: RegTest application solution content

Within the solution, note that the registration service has been added as a web reference; you will likely need to update this reference. The app.config file is the default version, and can be ignored here. The classes worthy of note are frmMain and RegistrationCheck. The main form class (frmMain) represents your application, and the registration check class is used to capture information about the machine containing the application and package up for submittal to the registration web service.

Assuming that the web service and the project references check out, we can now take a look at the code.

Code: Web Service Project – Registration Service

Locate the RegistrationService.asmx file contained in the “RegistrationSvc” web service solution, and open it up in the IDE. You will note that this is a very simple service, and in general, all that it does is expose a function entitled “RegisterInstallation”. This function receives a collection of arguments containing the information about the installer, and formats and executes an INSERT query against the MS Access database used to contain the installer information.

The imports section of the code in this class has been amended to include the portions of the System.Data library that pertain to the use of an OLEDB data source:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Data
Imports System.Data.OleDb


<WebService(Namespace:="http://localhost/RegistrationService/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Service
    Inherits System.Web.Services.WebService

The class declaration is cleverly called “Service”, and the attributes applied to the class are also in the default configuration, with the exception of the updated namespace. The only function in the service exposed as a web method is the RegisterInstallation function; its code is as follows:

<WebMethod()> _
Public Function RegisterInstallation(ByVal strDomainAndUser As String, _
                                     ByVal strComputerName As String, _
                                     ByVal strApplication As String, _
                                     ByVal strVersion As String, _
                                     ByVal strAppID As String, _
                                     ByVal strInstallDate As String) As  
                                     Boolean

   Dim conn As OleDbConnection = New _
       OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;" & _
       "Data Source=c:\inetpub\wwwroot\RegistrationSvc\Registration.mdb;" & _
       "User ID=;Password=;")

    Try

        Dim strBuilder As New System.Text.StringBuilder
        strBuilder.Append("INSERT INTO AppInstallers ")
        strBuilder.Append("(DomainAndUser, ComputerName, _
                          Application, Version, AppID, _
                          InstallDate) VALUES(")
        strBuilder.Append("'" & strDomainAndUser & "', ")
        strBuilder.Append("'" & strComputerName & "', ")
        strBuilder.Append("'" & strApplication & "', ")
        strBuilder.Append("'" & strVersion & "', ")
        strBuilder.Append("'" & strAppID & "', ")
        strBuilder.Append("'" & strInstallDate & "')")

        Dim sSQL As String = strBuilder.ToString()
        Dim cmd As OleDbCommand = New OleDbCommand(sSQL, conn)

        Try
            conn.Open()
            cmd.ExecuteNonQuery()
            conn.Close()
        Catch ex As Exception
            conn.Close()
            Return False
        End Try

    Catch ex As Exception
        conn.Close()
        Return False
    End Try

    conn.Close()
    Return True

End Function

This function is pretty straightforward; the arguments detailing the installer’s information are passed as arguments to the function. The function then takes the arguments directly, and passes them to an INSERT query against the MS Access database. Once the execution of the INSERT statement has completed, the function returns a Boolean set to True if the data is passed to the database, or False if the operation fails to update the database.

That is all there is to the web service; the calling application will gather the user’s information and, if appropriate, connect to the web service and post the data to the database used to house the installer information.

One point to make here is with regards to the setting of the installation date; in this example, the value is passed into the web method from the client. If you are interested in creating a grace period for using the application (e.g., allow the user to use the application for 30 days without registering it), you may wish to set this value on the server side instead of the client. This will prevent the user from changing the date on their machine to gain additional, free use of the application.

Next, open the “RegTest” solution in the IDE, and we can take a look at that.

Code: RegistrationCheck Class

First off, open the “RegistrationCheck” class in the code window. You will note that the class contains two subroutines and one function. The Imports statements and the class declaration are as follows:

Imports System
Imports System.Windows.Forms.SystemInformation

Public Class RegistrationCheck

By importing the “SystemInformation” library, we are able to capture the information used to identify the machine and the user running the application. This will provide us with the source for the arguments passed to the web service.

Now, take a look at the function, “CheckProductRegistration”. This function is Public, and is the means by which the application checks for prior registrations and then gathers and submits data to the web service during the initialization of an unregistered version of the application. The code for this function is as follows:

Public Function CheckProductRegistration() As Boolean

    ' Check registry for key, 
    ' create the key if it does not exist
    CheckForOrMakeRegKey()

    ' Check the key to see if application has been registered
    ' the key DA\SOMLA\1.0 is nonsense; it is just
    ' there to confuse anyone that tries to 
    ' defeat the registration process. (actually,
    ' SOMLA is some software I used in the
    ' mid 1980's to test aircraft crash effects
    ' upon a seated aircraft occupant, but you
    ' can put whatever you want in there, use Bullwinkle
    ' or cDc if you don't like SOMLA)
    ' Set this boolean upon determining whether or not
    ' the product has been registered
    Dim blnIsRegistered As Boolean = False
    Dim key As Microsoft.Win32.RegistryKey

    Try

        key = _
         Microsoft.Win32.Registry.CurrentUser.
         OpenSubKey("SOFTWARE\\DA\\SOMLA\\1.0")

        If (Not key Is Nothing) Then

            Dim intVal As Integer = _
                CType(key.GetValue("dval"), Integer)

            If intVal = 0 Then
                blnIsRegistered = False
            ElseIf intVal = 1 Then
                blnIsRegistered = True
                Return True
                ' it is already registered so just return true 
                ' and exit out of function
            End If

        Else

            ' The key does not exist, the application
            ' has not been registered
            blnIsRegistered = False

        End If

        key.Close()

    Catch
        ' do nothing here, we want this to be pretty quiet...
    End Try


    ' Registration has not occurred, so try to register it
    If blnIsRegistered = False Then

        ' Obtain the user name currently logged on to this computer
        Dim strUser As String
        strUser = _
        System.Windows.Forms.SystemInformation.UserName.ToString()

        ' Obtain the user's domain name for the current session
        Dim strDomain As String
        strDomain = _
        System.Windows.Forms.SystemInformation.UserDomainName.ToString()

        ' Combine the user and domain names into a single entry
        Dim strDomainAndUser As String = strDomain & "\" & strUser

        ' Get the name of the computer the application 
        ' is installed on
        Dim strComputerName As String
        strComputerName = _
        System.Windows.Forms.SystemInformation.ComputerName.ToString()

        ' Create a local instance of the web service
        Dim ws As New RegistrationService.Service

        ' The web service returns a boolean to indicate 
        ' success or failure in execution
        Dim blnTest As Boolean = False

        ' Run the web service and load the installer's 
        ' information into the database
        Try

            blnTest = _
              ws.RegisterInstallation(strDomainAndUser, _
              strComputerName, _
              "RegTest", "1.0", _
              "{376FEDC1-BC65-4c6a-B3C2-49CD57BFD127}", _
              Now.ToLongDateString())

        Catch

            ' Create registry entry and set it to false
            CreateKeyAndSetValue(0)

        End Try

        'NOTE: the GUID is canned as is meant to mark the install 
        '      as a specific copy of the application
        '      create new guids and registry keys for other customers
        '      and applications - this will allow you to track who bought 
        '      the software versus where it ends up getting installed.

        If blnTest = True Then

            ' Create registry entry and set it to 1 - registered
            CreateKeyAndSetValue(1) ' IS registered
            Return True

        Else
            ' Create registry entry and set it to 0 - not registered
            CreateKeyAndSetValue(0) ' NOT registered
            Return False

        End If
    End If
End Function

The first thing this function does is call a private subroutine used to check for the existence of the registry key used to check for prior registrations of the application. If the key does not exist (first use), the key will be created in the subroutine. The key used, "SOFTWARE\\DA\\SOMLA\\1.0", is just some nonsense used to throw someone off if they were to try to find and manually edit the key value used to determine whether or not the application has been registered. As I mention in the comments, SOMLA is an application dating back to the 1980’s; it was used to run analysis of the effects of a crash upon the human spine. “DA” is for district attorney. You can change these values to whatever you want, but you should make some effort to conceal the purpose of the key and its value.

Next, the function gets the value of the key used to mark the application as having successfully been submitted to the registration web service; if the value indicates that the application has not been registered or activated through the service, it sets a Boolean to False. The Boolean is then tested, and if it is False, the function will attempt to gather the user’s information and submit it to the web service for registration. If the product is then registered, the calling method is notified of the success, and the registry value is updated to indicate that the product has been activated through the service. If the Boolean returns False, the user is booted out of the application, else the user is granted access to the application.

The two subroutines in this class are merely used to support the function by creating or confirming the existence of a registry key, and by allowing the function to set the key value associated with the registration of the application. The code for these two subroutines is as follows:

Private Sub CheckForOrMakeRegKey()

    Try
        Dim regVersion As Microsoft.Win32.RegistryKey
        regVersion = Microsoft.Win32.Registry.CurrentUser.OpenSubKey _
                    ("SOFTWARE\\DA\\SOMLA\\1.0", True)

        If regVersion Is Nothing Then
            regVersion = _
            Microsoft.Win32.Registry.CurrentUser.CreateSubKey _
            ("SOFTWARE\\DA\\SOMLA\\1.0")
        End If

        regVersion.Close()

    Catch

        'do nothing

    End Try

End Sub

Private Sub CreateKeyAndSetValue(ByVal intVal As Integer)

    Try

        ' Declare a registry key variable and set it to
        ' be the fake SOMLA key
        Dim regVersionKey As Microsoft.Win32.RegistryKey
        regVersionKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey _
                       ("SOFTWARE\\DA\\SOMLA\\1.0", True)

        ' If the key exists, then set its dval key value
        ' to the integer passed into this subroutine
        If (Not regVersionKey Is Nothing) Then
            regVersionKey.SetValue("dval", intVal)
            regVersionKey.Close()
        End If

    Catch

        'do nothing

    End Try

End Sub

That concludes the discussion of the contents of this class. Next, open up the main form (frmMain.vb) class into the code window. The only item worthy of note in the main form’s code is the handler for the Load event:

Private Sub frmMain_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load

    ' Each time the application is loaded,
    ' we check for the existence of 
    ' the registry entry:
    Dim rc As New RegistrationCheck
    Dim bln As Boolean

    bln = rc.CheckProductRegistration()

    ' If the bln indicates that the application
    ' is not registered (and therefore
    ' could not be registered, then tell them
    ' Goodbye and exit the application.

    If bln = False Then

        MessageBox.Show("You have not registered this " & _ 
                        "product. Registration requires " & _ 
                        "internet access.", "Registration Failed", _
                        MessageBoxButtons.OK)
        Application.Exit()

    End If

End Sub

This code is pretty simple; it merely creates an instance of the “RegistrationCheck” class, sets a Boolean variable to capture the results of the attempt to register the product, and then processes the evaluation of the returned Boolean. If the Boolean is False, the user is notified that product activation requires an internet connection, and then the application is terminated if activation has not occurred. The user will continue to be met with this response until they permit the application to connect to the internet and subsequently activate the product.

Naturally, you can modify what occurs in response to a failed activation. For example, if you want to let the user use the product for thirty days after the original installation, you could amend the web service to return the initial installation date, and compare that date to today’s date and then decide whether or not to allow them to use the application.

Summary

The sample application and web service are meant to convey an approach to activating a product. This is not a particularly robust way of doing it, and it certainly is not foolproof. The approach could easily be compromised if the user were able to locate and manually edit the registry value. The approach does show you a general approach to product activation, and is a good way of collecting specific information about who is using the software, and determining which uniquely marked copy of the software the installer/user is running on a specific machine.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

salysle
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
GeneralNice PinmemberAnthony Collins15-May-07 14:55 
GeneralRe: Nice Pinmembersalysle17-May-07 4:41 
GeneralFoolproof Pinmemberbalazs_hideghety19-Sep-06 20:56 
It's a nice example. Instead of putting a key inside the registry i would use hashed (or keyed-hashing) datetime values.
 
Also i thing it's better to use the HDD number of the registration rather than the domain/user name + of course some company (buyer) id (special hash/checksum value).
 
I would add an additional field: last on-line check. So i could trace (with weekly/monthly interval) if the given instalation is in use. For instances for that the online registration check was not performed for an given time, we can assume that the instance is not in use Smile | :) Also the application should do these checks often - and if the period after last succesfully check exceedes a given value, application should not start...
 
Smile | :)
 
C#, ASPX, SQL

GeneralSQL Injection PinmemberChristopher G. Lewis19-Sep-06 10:33 
GeneralRe: SQL Injection Pinmembersalysle19-Sep-06 15:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.141015.1 | Last Updated 19 Sep 2006
Article Copyright 2006 by salysle
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid