Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

How to Register a Program

3.95/5 (9 votes)
28 Jan 2009CPOL8 min read 63.6K   2K  
How to register a program on end-user machine only and without end-user interference.
PrgRegProj

Program registration tester

Introduction

How to hide values in the registry and use it for program registration!

Would it be possible to perform a program registration for a certain program (or other similar objects) and only keep the reg.info. hidden on the end-user machine - in a "pretty safe" way - so that the average user would/could not mess around with it? This is what my article tries to solve. Put simply: how to register a program.

I wouldn't say that my solution is anything state-of-the art, or even close to being elegant, but it works and it's safe to the extent that I feel that most users would not dare to look into how it is made and how it is avoided. Also, in writing this I hope to learn more from the feedback it may produce, especially if I unintentionally re-invent techniques or functions already known.

Background

Before I begin please allow me to first define what the article and code actually solves and exactly what I mean by saying "program registration" (it may not be obvoius). After that I'll go into more detail.

Definitions and Limits

  • A registration object is the entity provided to pinpoint (1) an official UTC*-timestamp and (2) a serial no. (SN) for the program being installed.
  • The code deals with the smallest possible set of information to establish such a registration entity and is by def. and for this demo. purpose (1) and (2).
  • The aticle does 'not' go into discussions about how a SN is made and how secure they are etc.
  • Except for time-services the code must not be dependend of any servers in the outside world.
  • The registration performed must be 100% free of charge for us.
*) Universal Time Coordinate.

Trivials

  • Although having a tiny Win-registry API the point is not to produce yet-another-such :-).

What is the idea?

The basic idea is relatively simple: Would it be possible to write something somewhere and then hide it for the user to see? And next, would it be possible to get the right time - and then let the 'something' be this time? If 'yes' to both it could be done. Or, in other words, could I hide a timestamp in a secret Harry-Potter-spot? Of course in the end we are forced to write our 'thingy' in a registry-branch/key already present so that the (advanced) user does not just remove an added (and visible) key - the key must exist to begin with - like "Console" or something!

That's the idea. Let's have some fun!

The Hidden Registry Value

One month ago I came across this:

Nowadays, everyone has access to the registry editor and can modify, delete or add registry keys. This is a great thing, as long as you know how and which registry values need to be modified. Creating an invisible registry key gives you the guarantee that your keys will remain what you set them, and the applications you develop won’t encounter any problems in reading wrong data. If you want to create an invisible registry folder, this is the solution for you!

The trick lies in a Windows flaw, a flaw regularly exploited by malicious applications (virus, spyware) to hide their traces. The flaw (trick) is that a registry value name can only have the length of 255, if a name longer than 255 characters is given to a registry value, windows will think the end of the key has been reached and all values in that key will be hidden from view. You can see them only with special software designed for this purpose.

Source: ReviewingIT

I tested it and to my surprice it worked. Is this generally known? I have to admit that I didn't know about it. So after reading this I came up with this tiny and simple win-registry-class:

C++
class CRegistryAccess
{
	private:
		// 0. Properties. (alphabetical).
		bool m_bHiddenName;
		HKEY m_hKey;
		CString m_cstrMainKey, 
		        m_cstrSubKey;
	protected:
		// 3. Methods. (alphabetical).
		static const bool 
			CloseKey(HKEY);
		static HKEY 
			CreateKey(const CString&);
		static BOOL WINAPI 
			EnableProcessPrivilege(LPCTSTR, BOOL);
		static const CString 
			FormatKey(const CString&, const CString&);
		const bool 
			GetDataDWORD(const CString&, DWORD&);
		const bool 
			GetDataSZ(const CString&, CString&);
		static const CString 
			GetLongName(const CString&);
		static HKEY 
			OpenKey(const CString&);
		const bool 
			SetDataDWORD(const CString&, CONST DWORD&);
		const bool 
			SetDataSZ(const CString&, const CString&);
	public:
		// 0. Properties. (alphabetical).
		struct SNameTypeData
		{
			CString m_cstrName;
			CString m_cstrType;
			CString m_cstrData;
		};
		// 1. Constructors. (alphabetical).
		CRegistryAccess(const CString&, const CString &cstrSubKey = _T(""),
                      const bool bHiddenKeyEx = false);
		~CRegistryAccess();
		// 3. Methods. (alphabetical).
		const bool 
			DataExist(const CString&);
		const bool 
			DeleteData(const CString&);
		const bool 
			DeleteData();
		const bool 
			DeleteKey();
		const void 
			GetAll(CArray<SNameTypeData, SNameTypeData>&);
		const void 
			GetAllNames(CStringArray&);
		const void 
			GetAllTypes(CStringArray&);
		const void 
			GetAllDatas(CStringArray&);
		const bool 
			GetData(const CString&, DWORD&);
		const bool 
			GetData(const CString&, CString&);
		const bool 
			GetData(const CString&, int&);
		const bool 
			GetData(const CString&, bool&);
		const bool 
			SetData(const CString&, CONST DWORD&);
		const bool 
			SetData(const CString&, const CString&);
		const bool 
			SetData(const CString&, const int&);
		const bool 
			SetData(const CString&, const bool&);
};

I prefer Set- and GetData(...) over Set- and GetValue(...) since this is what we do - setting/getting data - like the third column in regedit.exe suggests. For most purposes, at least for me right now, dealing with the registry is the same as working at one key (and ONE key only) at a time and then handling N data's under it. In that way I can concentrate on my one-and-only key opened (think of it as *this)! Hence the only method in this class dealing with a key is DeleteKey(...) and calling this is suicide - it removes the key and jumps up the tree one step making the parent node the open one. It fails if the key holds sub-keys BUT it wipes all data - so be careful. DeleteData(...) wipes all data under a key and DeleteData(const CString&) a specific. The group GetAll... is really just for enumerating the name(s)/type(s)/data(s) and the rest is overloaded get/set for the most important datatypes.

Also worth mentioning is: static BOOL WINAPI EnableProcessPrivilege(LPCTSTR, BOOL) which enables the owner of the object to have full access to the registry (got this from C Board and MS) AND the rest more or less are a protected by a thin layer over the win32 registry API.

The 007 stuff is bool m_bHiddenName;. If this is T the data is hidden under its key (hidden defaults to F in its constructor). 255 char's are padded in front of the name using:

C++
const CString CRegistryAccess::GetLongName(const CString &cstrName)
// Return long name (for invisible entries).
{
	TRY
	{
		// 0         0         0         0          = 40
		// 0123456789012345678901234567890123456789
		// MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG
		static const CString s_cstr255Chars(
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("MAGGIEBARTLISAMARGEHOMERSIMPSONSPIDERPIG")
				_T("ITCHYORSCRATCHY"));
		CString cstrNameLong(s_cstr255Chars + cstrName);
		return cstrNameLong;
	}
	CATCH_ALL(e)
	{
		WNDTRACEEXCEPTION_USERDLG("CRegistryAccess", "GetLongName", "", e);
		THROW_LAST();
	}
	END_CATCH_ALL
}

Test stub for the class...

registry_access_tester.jpg

...after a value added...

the_registry.jpg

Simple and straightforward. Next step is to get the right time.

What time is it?

windows_time_sync.jpg

Sync. to internet time (sorry for the Danish language)

Stupid question... but, chances are that the time sync. in Windows is turned off by the user making the registration worthless if we ask for his local PC-time. Therefore we need a time&date-server to ask instead, and luckily there is one such server on the internet, and has been for many years. They are called:

RegionHostName
Worldwidepool.ntp.org
Europeeurope.pool.ntp.org
Asiaasia.pool.ntp.org
North Americanorth-america.pool.ntp.org
Oceaniaoceania.pool.ntp.org
South Americasouth-america.pool.ntp.org

And there are many more. This host-list serves as the basis for my function inline const bool GetInternetTime(COleDateTimeEx &tNTP, const CString &cstrNTPServer = _T("")){...}. Calling this you'll have the current UTC! I was able to write this based on another CP article called CSNTPClient - An SNTP Implementation written by Mr. PJ Naughter.

After cutting some and adding some (mostly cut) I ended up with a set of classes and structs to retrieve the std. time as orig. defined by Mr. PJ Naughter. For this impl. we are only interested in the precision YYYYMMDD.

To make GetInternetTime(...) work we need a bunch of classes - defining the packet look, socket behaviour and some neat conversions etc. They are:

  • class SNTPTimePacket - a struct rep. seconds and fraction of s. as two DWORD's.
  • class CNTPTime - a class having SNTPTimePacket- and SYSTEMTIME cast operators etc.
  • class SNTPServerResponse - a cut down of a more advanced struct to just a CNTPTime for this purpose (so it could be removed all together).
  • class CNTPClient - class having const bool CNTPClient::GetServerTime(const CString &cstrHostName, SNTPServerResponse& sntpResponse, const int iPort).
  • class CNTPSocket - encapsulates native Win. sock calls like Close, Create and Connect.
  • class SNTPBasicInfo - struct holding the mandatory part of an NTP packet.
  • class SNTPAuthenticationInfo - struct holding the optional part of an NTP packet.
  • class SNTPFullPacket - struct rep. the two above.

I'll not go into all the time-code, but in short it's basically just setting up a Win32 UPD-socket (NTP uses UDP instead of the usual TCP) - ::WSAStartup(...) blah. blah. (see the books) and then create and connect it. GetServerTime(...) Sends and Recieves and after its success it goes: sntpResponse.m_ReceiveTime = sntpFullPacket.m_Basic.m_ReceiveTimestamp; after which we have a CNTPTime and following a SYSTEMTIME and in the end a COleDateTime. All hosts are found on port 123 (all that I know of at any rate).

C++
class CNTPClient : public CObject
{
	public:
		// 1. Constructors. (alphabetical).
		CNTPClient();
		// 3. Methods. (alphabetical).
		const bool GetServerTime(const CString&, SNTPServerResponse&,
                      const int iPort = 123);
	protected:
		// 0. Properties. (alphabetical).
		DWORD m_dwTimeout;
};

If GetInternetTime(...) fails it continues using the next server in the table until success. In the unlikely event that no time (actually date only) can be returned an appropriate exception is thrown.

Using the Code

When we want to register our program now, the class for that deals with a hidden name under a predefined reg. key named in the class - we pick "..\HKEY_CURRENT_USER\Console" for the purpose. So at time of creation it says:

C++
CProgramRegistration::CProgramRegistration(const CString &cstrAppName,
     const int iDaysToUseFreeOfCharge, const bool bHidden) : 
	m_cstrAppName(cstrAppName), 
	m_iDays2UseFreeOfCharge(iDaysToUseFreeOfCharge), 
	m_pregAccess(NULL)
	// Create.
	{
		try
		{
			// Get official UTC-time.
			if(!::GetInternetTime(m_dtNow))
			{
				// Abort.
				AfxThrowUserException();
			}
			// From now on m_dtNow are the official UTC time!
			m_pregAccess = new CRegistryAccess(_T("Console"), _T(""),
                               bHidden);
			VERIFY(m_pregAccess);
			m_cstrAppName.MakeUpper();
			// If first-time-run are present we read it back and if
                            // not we write it as m_dtNow.
			if(!GetFirstRunDate(m_dtFirstRun))
			{
				m_dtFirstRun = m_dtNow;
				SetFirstRunDate(m_dtFirstRun);
				UnRegister();
			}
			// Calc. expire date.
			m_dtExpire = m_dtFirstRun + COleDateTimeSpanEx(
                               m_iDays2UseFreeOfCharge, 0, 0, 0);
		}
		catch(CUserException *pe)
		{
			pe->ReportError();
			pe->Delete();
		}
		catch(CException *pe)
		{
			pe->ReportError();
		}
	}

To create a trial version of your program for 7 days:

C++
// Leave last arg. empty to hide it!
m_pReg = new CProgramRegistration(_T("My Program"), 7, false);

On new it UTC-timestamps and call UnRegister(...) after which the registry looks:

the_registry_after_trial_reg.jpg
Trial...

After registration using a random SN the registry looks:

the_registry_after_reg.jpg
Register...

Other methods of interest in CProgramRegistration are (bold text):

C++
class CProgramRegistration
{
	private:
		// 0. Properties. (alphabetical).
		static const CString sm_cstrSNDummy;
		static const CString sm_cstrFirstRunRegKeyPostfix;
		static const CString sm_cstrSNRegKeyPostfix;
		int m_iDays2UseFreeOfCharge;
		CString m_cstrAppName;
		COleDateTimeEx 
			m_dtNow, 
			m_dtExpire, 
			m_dtFirstRun;
		CRegistryAccess *m_pregAccess;
	protected:
		// 3. Methods. (alphabetical).
		inline const CString GetFirstRunRegName()
		{ return(m_cstrAppName + sm_cstrFirstRunRegKeyPostfix); }
		inline const CString GetSNRegName()
		{ return(m_cstrAppName + sm_cstrSNRegKeyPostfix); }
		const bool 
			SetFirstRunDate(const COleDateTimeEx&);
	public:
		// 1. Constructors. (alphabetical).
		CProgramRegistration(const CString&, const int,
                       const bool bHiddenEx = true);
		~CProgramRegistration();
		// 3. Methods. (alphabetical).
		const int 
                        // Return 7 the first day for "MyProgram" and 6 tomorrow etc.
			GetDaysLeft();
		static inline const CString&
                // Return "00000-00000-00000-00000-00000-00000".
			GetDummySerialNumber()
		{ return sm_cstrSNDummy; }
		const bool  
                        // Return "090123" for "MyProgram".
			GetFirstRunDate(COleDateTimeEx&);
		const bool 
                        // Return "uIkdx-1NNwc-xqvgd-cBGxR-Ju0ws-TTdsL" for "MyProgram".
			GetSerialNumber(CString&); 
		const bool 
                        // Return T if registred or in trial and F if not.
			IsLegalCopy(); 
		const bool 
                        // Return T if registred and F if not.
			IsRegistred(); 

		const bool 
                        // Write "uIkdx-1NNwc-xqvgd-cBGxR-Ju0ws-TTdsL" for "MyProgram".
			Register(const CString&); 
		const void 
                        // Delete MYPROGRAM_FIRST_RUN and MYPROGRAM_SN.
			RemoveRegistration(); 
		const bool 
                        // Write "00000-00000-00000-00000-00000-00000" for "MyProgram".
			UnRegister();
};

In real life situations use:

C++
// Last arg. is true to hide it (or empty)!
m_pReg = new CProgramRegistration(_T("My Program"), 7, true);

Also use:

C++
const bool CRegistryAccess::DeleteData()

with extreme caution!

Points of Interest

One downside of this is that hidden data can be seen if enumerated! Try running the RegistryAccessTester and press 'Get all data' - long names are among the possibilities. Also remember to remove the registration if the user uninstalls the program!

End note: Some of the ...XXXEx classes are not in the project - I work with one set of classes and if not relevant I only release the XXX version. Like for COleDateTimeEx and COleDateTimeSpanEx where only the MFC-bases are used. Open prgreg.sln - it holds the two projects.

History

  • 1.00: 09.01.28: Initial ver.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)