Click here to Skip to main content
15,886,110 members
Articles / Desktop Programming / MFC

Open source software protection - Part 2

Rate me:
Please Sign up or sign in to vote.
3.28/5 (31 votes)
23 Sep 20038 min read 163.4K   14.9K   119  
This is a better alternative to my previous soulution
/*
Loader32 module v2.0 updated on 20th April 2003
the program now tries to get the program location from the registry and if not found, then only
asks the user to find the executable
*/
#include "stdafx.h"
#include "Loader32.h"
#include "Loader32Dlg.h"
#include <fstream.h>
#include <string.h>
#include "BlowFish.h"

//////////////////////////////
#include "HashLibProper.h"
#pragma comment(lib,"HashLibProper.lib")
#include <imagehlp.h>
#pragma comment(lib,"imagehlp.lib")
#pragma comment (lib,"Advapi32.lib") //For registry reading
//////////////////////////////

BOOL InitializeUs(DWORD*,DWORD*);
char szMyFileName[MAX_PATH+1];

//////////////////////This section needs to be changed program to program///////////////////////
#define CODE_DUMP "code_sec.dat"
#define FILE_TO_LOAD "Gta3.EXE"
#define nNumberOfRVAsToPatch 1
#define ROOT_KEY HKEY_LOCAL_MACHINE
#define REGKEY_PATH "Software\\Microsoft\\Microsoft Games\\Age of Empires II: The Conquerors Expansion\\1.0" //Registry sub-key where we will find REGKEY_STRING
#define REGKEY_STRING "EXE Path" //The key where we will find the program path
//////////////////////////////////////////////////////////////


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

struct PatchInfo
{
 char *RVA;//Relative virtual address
 unsigned long int nNumberOfPatches;//number of bytes to patch
 unsigned char *szPatchData;//If above bytes matched, these will be written over them.
 PatchInfo()
 {
	 RVA=NULL;
	 nNumberOfPatches=0;
	 szPatchData=NULL;
 }
 
};
//////////Patch Info data ///////////////////////
PatchInfo *PatchBytes=NULL;
STARTUPINFO startupinfo;
PROCESS_INFORMATION processinfo;
////////////////////////////////////
 void InitializeSP() {
	 memset(&startupinfo,0,sizeof(startupinfo));//fill startupinfo with NULL
	 startupinfo.cb = sizeof(startupinfo);
 }
/////////////////////////////////////
BOOL PatchProcess(PatchInfo*,int );
/////////////////////////////////////

BOOL LoadCrackedFile(LPCTSTR lpApplicationName,LPSTR  lpCmdLine)
{
	InitializeSP();
	BOOL bWhat=FALSE;
	CreateProcessA(lpApplicationName,lpCmdLine,0,0,0,CREATE_SUSPENDED,0,0,&startupinfo,&processinfo);//Create the thread as supended
	for(int i=0;i<nNumberOfRVAsToPatch;i++) bWhat=PatchProcess(PatchBytes,i);//process to be patched
	if(bWhat) ResumeThread(processinfo.hThread);
	else TerminateProcess(processinfo.hProcess,-1);
	return (bWhat);
}
  
BOOL PatchProcess(PatchInfo *PatchBytes,int index)
{
 BOOL ReturnVal=FALSE;
 DWORD BytesWritten=0;
 HANDLE ProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,FALSE,processinfo.dwProcessId);
 if (ProcessHandle)
 {
		if(WriteProcessMemory(ProcessHandle,(PatchBytes+index)->RVA,(PatchBytes+index)->szPatchData,(PatchBytes+index)->nNumberOfPatches,&BytesWritten)) ReturnVal = TRUE;
	
  CloseHandle(ProcessHandle);
 }
 return ReturnVal;
}

BOOL InitializeUs(DWORD *nEntryPoint,DWORD *nPreferredLoadAddress)
{
	LOADED_IMAGE li;
	if (MapAndLoad(szMyFileName,0,&li,FALSE,TRUE)) 
	{
		*nEntryPoint=li.FileHeader->OptionalHeader.AddressOfEntryPoint;
		*nPreferredLoadAddress=li.FileHeader->OptionalHeader.ImageBase;
	}
    else
    {
        return FALSE;
    }
	UnMapAndLoad( &li );
	return TRUE;
}

CString GetPathToLoadFile(HKEY hkPreDefined,LPCTSTR lpRegistryKey,LPSTR strFileName)
{
	/* strFileName will be a fully qualified filename (path, drive letter et al)*/
	HKEY hkeyresult;
    TCHAR PathName[MAX_PATH+1];
	DWORD dwSize;
    if(RegOpenKey(hkPreDefined,lpRegistryKey,&hkeyresult)!=ERROR_SUCCESS) return CString("");//The specified key could not be opened
    if(RegQueryValueEx(hkeyresult,REGKEY_STRING,NULL,NULL,(LPBYTE)PathName,&dwSize)!=ERROR_SUCCESS) return CString("");;
	RegCloseKey(hkeyresult);
	/*Now let's see if the registry key already has the filename, else append the filename to the above obtained value*/
	//But first, we convert PathName and strFileName to uppercase
	if(strstr(strupr(PathName),strupr(strFileName))!=NULL)	return CString(PathName);
	else
	{
	//The registry key did NOT have our filename.See if the path finishes with a "\\", if not add it
		if(strcmp((PathName+strlen(PathName)-2),"\\")!=0) strcat(PathName,"\\");
		strcat(PathName,strFileName);
		return CString(PathName);
	}
	return CString("");//Some other thing I have not thought about ?    
}
CString CLoader32Dlg::AskTheUserToGetTheFile(LPCTSTR strLoadEXE)
{
		//If the read from registry was unsucessfull or the user likes to do things himself
		CString sFilter="Fearless Force SiPe Release(*.EXE)|";
		sFilter+=FILE_TO_LOAD;
		sFilter+="||";
		CFileDialog dlg(TRUE,NULL,strLoadEXE,OFN_FILEMUSTEXIST,(LPCTSTR)sFilter,this);
		if(dlg.DoModal()==IDOK) return CString(dlg.GetPathName());
		return CString("");
}
/////////////////////////////////////////////////////////////////////////////
// CLoader32Dlg dialog

CLoader32Dlg::CLoader32Dlg(CWnd* pParent /*=NULL*/)
	: CDialog(CLoader32Dlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CLoader32Dlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CLoader32Dlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CLoader32Dlg)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CLoader32Dlg, CDialog)
	//{{AFX_MSG_MAP(CLoader32Dlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, OnLoad)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CLoader32Dlg message handlers

BOOL CLoader32Dlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CLoader32Dlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CLoader32Dlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

void CLoader32Dlg::OnLoad() 
{
	DWORD dwSize=NULL,dwByteCount=NULL;
	/*
	First of all, let's set the Current Directory to our own and NOT of the calling process.
	Else ReadFile() etc will not find the associated files if we call this program form anywhere else
	*/
	char *lpszCurrentDirectory=new char[MAX_PATH+1]; //We will be needing this once only
	GetModuleFileName(NULL,lpszCurrentDirectory,MAX_PATH);
	*(strrchr(lpszCurrentDirectory,int('\\')))='\0';
	SetCurrentDirectory(lpszCurrentDirectory); //Check the returned BOOL value
	delete lpszCurrentDirectory;
	/*	Done setting the current directory to our very comfortable abode ;)	*/
	//////////////////////////Open file contaning section data///////////////////
	HANDLE hFile=CreateFile(CODE_DUMP,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
	if(hFile==INVALID_HANDLE_VALUE) {
		MessageBox("\nCode section file missing ?");
		return;
	}
	dwSize=GetFileSize(hFile,NULL);
	//Create a dynamic array
	PatchBytes=new PatchInfo[1];
	PatchBytes->szPatchData=new unsigned char[dwSize];
	if(PatchBytes==NULL&&PatchBytes->szPatchData==NULL) 
	{
		MessageBox("\nFailed to create dynamic Memory buffer ! Exiting ....");
		exit(0);
	}
	//Fill the array up with CODE_DUMP contents and initialize 
	ReadFile(hFile,PatchBytes->szPatchData,dwSize,&dwByteCount,NULL);//szPatchData now contains encrypted data
	CloseHandle(hFile);
	///////////////////////////////////////////////////////////////////////////////////////////
	//Decrypt it
	llCD llCd;
	char temp1[256],temp2[256];
	llCd.GetMeDrives(3);//GetMeDrives of type DRIVE_FIXED (HDD)
	llCd.validate();
	llCd.CheckMachine(temp1,temp2);//temp2 has the MD5
	BlowFishEnc encryption(temp2);
	encryption.decryptStream((char *)PatchBytes->szPatchData,dwSize,(char *)PatchBytes->szPatchData);
	///////////////////////////////////////////////////////////////////////////////////////////
	DWORD nEntryPoint=-1,nPreferredLoadAddress=-1;
	//Fill up other PatchBytes data..
	//A quick hack - will read the real number of bytes to patch/overwrite from the 8 byte padded blowfish stream from Patch.dat
	ifstream f("Patch.dat");
	f>>PatchBytes->nNumberOfPatches;
	f.close();
	////////////////////////////////////////////////////
	/*
	Now let's try to read the registry for the required data, and ONLY pester the user if 
	the data could not be found. However, we shall ask the user if we should do that
	*/
	CString strLoadEXE;
	int nButt=MessageBox("\nShould I try to find the program file automatically or shall you browse for it ?","File detection dialog",MB_YESNO|MB_ICONQUESTION);
	if(nButt==IDYES) strLoadEXE=GetPathToLoadFile(ROOT_KEY,REGKEY_PATH,FILE_TO_LOAD);
	////////////////////////////////////////////////////
	if(strcmp((LPCTSTR)strLoadEXE,"")==0)
	{
		strLoadEXE=AskTheUserToGetTheFile((LPCTSTR)strLoadEXE);
	}
	//MessageBox(strLoadEXE);//Debug
	
	hFile=CreateFile(strLoadEXE,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);
    if(hFile==INVALID_HANDLE_VALUE) {
		MessageBox("\nThere was an error while trying to load the program.\nPlease make sure that the file is not in use\n\nReinstalling the program may help.","Error during file validation",MB_OK|MB_ICONINFORMATION);
		return;
	}
	CloseHandle(hFile);
	//////////////////////////////////////////////////////
	strcpy(szMyFileName,strLoadEXE);
	InitializeUs(&nEntryPoint,&nPreferredLoadAddress);
	///////////////////////////////////////////////////////
	PatchBytes->RVA=(char *)(nEntryPoint+nPreferredLoadAddress);//AddressOfEntryPoint of program
	char szMsg[512];
	if(dwByteCount!=dwSize) 
	{
		wsprintf(szMsg,"\n\nThere was an error reading the file to memory !\n(Bytes read = %d Bytes to be read = %d)\n",dwByteCount,dwSize);
		MessageBox(szMsg);
		return;
	}
	//else printf("\nRead file sucessfully into memory !");
	//Call the loader
	if(!LoadCrackedFile((LPCTSTR)strLoadEXE,"")) MessageBox("\nSorry ! Program could not be patched! Will terminate thread.");
    //Clean up our dynamic array
	delete[] PatchBytes->szPatchData;
	delete[] PatchBytes;
	exit(0);//
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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


Written By
Web Developer
United States United States
Kamal Shankar is a programming freak (or so he feels).He currently lives in the Salt Lake City and loves doing what he has been since 1990 - coding horribly Wink | ;)

Comments and Discussions