// rback.cpp : Defines the entry point for the console application.
//
//  This is a quick-and-dirty application I wrote as a sort of "restore point" for "Stop: 0xc0000218" errors that were plaguing me.  
//    Obviously, you'll need a way to get the hives back where they belong if you do eventually fail to boot; you might try:
//      * MS Recovery Console; need to change some stuff in secpol.msc, probably.
//      * The NTFS support in Linux (not sure if RW is still experimental)
//      * There are some options from Winternals you might try (supposed to be really good, but costly), like Remote Recover and NTFSDOS.
//
//  It backs up the accessible registry hives to an appropriate location.  This generally means that it won't touch "SECURITY", but
//    will grab the others.  If you run it in your own context, it will back up your ntuser.dat.  If you run it in the LOCALSYSTEM context,
//    it will grab the .DEFAULT user's ntuser.dat, which is basically useless, but it WILL grab "SECURITY".  I have not bothered to write
//    a workaround, and I have not tested it in with a non-administrative account.
//  The two main usages are:
//    rback.exe d:\tempback
//    psexec \\127.0.0.1 -s -i rback.exe d:\tempback
//  (Obviously, you'll provide paths to psexec and rback.exe, if necessary.  psexec.exe is available from sysinternals as part of PsTools.)
//  The former runs in your own context, the latter in the localsystem context with interactivity.  I run the latter at logon, but that's just me.
//
//  This program is provided AS-IS, with NO warranty WHATSOEVER.  I am responsible for nothing, period.
//  It's public domain.  Fix it yourself if you don't like it.  (Well, the SetPrivilege stuff is trimmed from MSDN, so I don't know what
//    their licensing is, but you can find it on their website, so I assume it's basically okay.)
//
//
// ------------------------------------------------------------------------------------------------------------------------------------------------
//  If you're curious about the problem I was having, I've been running Win2k adv srvr sp4 for some time now.  The SOFTWARE hive became corrupted
//    somehow.  Microsoft's CHKREG tool claimed to fix the problem, but didn't (six floppy disks later...geez).  I paid some guy on the Internet 
//    to repair it, and he sent me back two versions:  a .reg file that I was sorta able to use (no security settings, of course), after splitting 
//    it into parts and narrowing down which one wouldn't load, as well as a hive that wouldn't open when I tried to use it as a direct replacement.  
//    Oh well.  It was enough to reboot, load Nero, and back stuff up before reformatting.  Fun.
//  I disassembled CHKREG (see KB 830570) and found out it would've been terribly simple for Microsoft to forego the whole 6-floppy XP (yes, XP) setup
//    diskette procedure if they took into account the fact that many people can access their NTFS filesystem outside of Windows.  I keep a floppy drive in
//    each of my computers, but it doesn't mean I like using floppies.  The disks are notorious for developing surface problems, and the FAT12 system 
//    doesn't help matters any.  I can verify that I don't need most of the junk on those six disks anyway; it supports hardware I don't have.  What they
//    have you do is replace the recovery console on the sixth disk with the chkreg utility.  I also found a bug in the utility that looks like it could
//    require you to run it multiple times, which means loading all six disks multiple times....yeah, no, thanks.
//  Maybe it's been fixed since; the version I have is chkreg.exe, 5.2.3790.1007:
//    MD5         : C1EC9FBCEA9920B34E6572D86E902348
//    CRC-32      : 2047BD3E
//  I can't remember the exact chronology, but I think it was before I fixed another problem with my computer that may have caused it:  there's an 
//    apparent incompatibility between my ATi video card (actually, two that I tried, a 9800 and an x1600, both AGP 8x) and my Abit IC7-G, though
//    neither company would do anything.  I eventually found some performance settings (NOT the ones they talk about in the ATi FAQ) that fixed the 
//    problem; I think it was either the "PCI Write" or the "AGP Write".  Anyway, this error manifested itself as "Application failed to initialize 
//    properly (0xc0000142)", and the option in the ATi control panel fixed it, while the standard advice (increase SharedSection on csrss) had no effect.  
//    "No effect" is actually a quantitative statement:  I used the primitive shell capabilities of Windows to measure it.  (e.g., 
//    "for /L %i in (1,1,100) do notepad")  I could start about 220 notepads before installing the drivers, and about 60 afterwards, with declining
//    numbers as time wore on, until I had to reboot.  60 may sound like a lot, but they may have been equivalent to 2-3 IE6's, depending on circumstances.
//    The point is, if my memory of the timeline is correct (and I'm not sure it is), this incompatibility may have been the source of the corruption.
//    Sorry; it's been six months, and I fixed both of them within a month of each other.
// ------------------------------------------------------------------------------------------------------------------------------------------------

// Updates:  
//   12-26-2006 Fixed some Unicode bugs, added this header, and released the code.
//

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <assert.h>
#include <direct.h>
#include <io.h>
#include <errno.h>

void ReportError(LPCTSTR szMessage, unsigned uErrCode);

bool PerformBackup(HKEY hParentKey, LPCTSTR szSubkey, LPCTSTR szFilename) {
  HKEY hKey;
  static TCHAR szErr[512];
  LONG nRet;

  if(hParentKey!=HKEY_CURRENT_USER) {
	nRet=RegOpenKeyEx(hParentKey, szSubkey, 0, KEY_READ, &hKey);
	if(nRet!=ERROR_SUCCESS) {
	  _stprintf(szErr, _T("Unable to open \"%s\\%s\".\n"), 
	    hParentKey==HKEY_LOCAL_MACHINE?_T("HKLM"):hParentKey==HKEY_USERS?_T("HKU"):_T("???"), szSubkey);
	  ReportError(szErr, nRet);
	  return false;
	}
  } else
    hKey=HKEY_CURRENT_USER;

  //  nRet=RegSaveKeyEx(hKey, _T("c:\\bk1.hiv"), NULL, REG_STANDARD_FORMAT);
  nRet=RegSaveKey(hKey, szFilename, NULL);
  if(nRet!=ERROR_SUCCESS) {
    if(hKey==HKEY_CURRENT_USER)
	  _stprintf(szErr, _T("Unable to save \"HKEY_CURRENT_USER\".\n"));
	else
	  _stprintf(szErr, _T("Unable to save \"%s\\%s\".\n"), 
	    hParentKey==HKEY_LOCAL_MACHINE?_T("HKLM"):hParentKey==HKEY_USERS?_T("HKU"):_T("???"), szSubkey);
	ReportError(szErr, nRet);
	return false;
  }

  if(hParentKey!=HKEY_CURRENT_USER)
    RegCloseKey(hKey);
  return true;
}

bool SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    ) 
{
  TOKEN_PRIVILEGES tp;
  LUID luid;

  if ( !LookupPrivilegeValue( 
        NULL,            // lookup privilege on local system
        lpszPrivilege,   // privilege to lookup 
        &luid ) ) {      // receives LUID of privilege
    printf("LookupPrivilegeValue error: %u\n", GetLastError() ); 
    return false; 
  }

  tp.PrivilegeCount = 1;
  tp.Privileges[0].Luid = luid;
  if (bEnablePrivilege)
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  else
    tp.Privileges[0].Attributes = 0;

  // Enable the privilege or disable all privileges.

  AdjustTokenPrivileges(
       hToken, 
       FALSE, 
       &tp, 
       sizeof(TOKEN_PRIVILEGES), 
       (PTOKEN_PRIVILEGES) NULL, 
       (PDWORD) NULL); 
 
  // Call GetLastError to determine whether the function succeeded.

  if (GetLastError() != ERROR_SUCCESS) { 
      printf("AdjustTokenPrivileges failed: %u\n", GetLastError() ); 
      return false; 
  } 

  return true;
}


bool EnablePrivilege() {
  HANDLE hToken;
  if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
    printf("Unable to open process token, %d (0x%08X)\n", GetLastError(), GetLastError());
    return false;
  }

  if(!SetPrivilege(hToken, SE_BACKUP_NAME, TRUE)) {
    CloseHandle(hToken);
    return false;
  }

  CloseHandle(hToken);
  return true;
}


void ReportError(LPCSTR szMessage, unsigned uErrCode);
bool FindSuitableDirectory(int argc, TCHAR *argv0, TCHAR *argv1);
bool DoBackups();

TCHAR szDirectory[1024];

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) {
	if(!EnablePrivilege())
		return 2;
	if(!FindSuitableDirectory(argc, argv[0], argv[1]))
		return 3;
	if(!DoBackups())
		return 4;
	::MessageBox(NULL, _T("Successfully backed up the registry.\n"), _T("RBACK"), MB_OK);
	return 0;
}


void ReportError(LPCTSTR szMessage, unsigned uErrCode) {
	LPTSTR lpBuffer;
	TCHAR szTemp[512];

	if(!uErrCode ||
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 
		NULL, uErrCode, 0, (LPTSTR)&lpBuffer, 256, NULL)==0) {
		_sntprintf(szTemp, 511, _T("%s: %d (0x%08X)"), szMessage, uErrCode, uErrCode);
	} else {
		_sntprintf(szTemp, 511, _T("%s: %s"), szMessage, lpBuffer);
		LocalFree(lpBuffer);
	}

	::MessageBox(NULL, szTemp, _T("Error"), MB_OK);
}

bool FindSuitableDirectory(int argc, TCHAR *argv0, TCHAR *argv1) {
	TCHAR buf1[512];
	if(argc!=2) {
		_stprintf(buf1, _T("Usage:  %s <directory>\n  Where <directory> is the place under ")
			_T("which you want all your backups stored."), argv0);
		ReportError(buf1, 0);
		return false;
	}

	if(_taccess(argv1, 0)!=0) {
		if(_tmkdir(argv1)!=0) {
			ReportError(_T("Unable to create specified directory."), 0);
			return false;
		}
	}
	if(_taccess(argv1, 6)!=0) {
		ReportError(_T("Access to specified directory denied."), 0);
		return false;
	}
			
	if(_tchdir(argv1)!=0) {
		ReportError(_T("Unable to change to specified directory."), 0);
		return false;
	}

	SYSTEMTIME SystemTime;
	GetSystemTime(&SystemTime);
	TCHAR buf2[512];
	_stprintf(buf2, _T("%04d_%02d_%02d_%02d_%02d_%02d"),
		SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay,
		SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond);

	if(_taccess(buf2, 0)==0) {
		ReportError(_T("Unique directory already exists!"), 0);
		return false;
	}

	if(_tmkdir(buf2)!=0) {
		ReportError(_T("Unable to make unique directory."), 0);
		return false;
	}

	_stprintf(szDirectory, _T("%s\\%s"), argv1, buf2);
	if(_tchdir(szDirectory)!=0) {
		ReportError(_T("Unable to change to unique directory!"), 0);
		return false;
	}

	if(_taccess(szDirectory, 6)!=0) {
		ReportError(_T("Access denied to unique directory!"), 0);
		return false;
	}
	
	return true;
}

bool DoBackups() {
	TCHAR szTemp[1024];
	bool bSuccess=true;

	assert(_tchdir(szDirectory)==0);

	printf("Backing up SOFTWARE...");
	_sntprintf(szTemp, 1023, _T("%s\\SOFTWARE"), szDirectory);
	if(!PerformBackup(HKEY_LOCAL_MACHINE, _T("SOFTWARE"), szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");

	printf("Backing up SYSTEM...");
	_sntprintf(szTemp, 1023, _T("%s\\SYSTEM"), szDirectory);
	if(!PerformBackup(HKEY_LOCAL_MACHINE, _T("SYSTEM"), szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");
	
	printf("Backing up SAM...");
	_sntprintf(szTemp, 1023, _T("%s\\SAM"), szDirectory);
	if(!PerformBackup(HKEY_LOCAL_MACHINE, _T("SAM"), szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");

	printf("Backing up SECURITY...");
	_sntprintf(szTemp, 1023, _T("%s\\SECURITY"), szDirectory);
	if(!PerformBackup(HKEY_LOCAL_MACHINE, _T("SECURITY"), szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");

	printf("Backing up DEFAULT...");
	_sntprintf(szTemp, 1023, _T("%s\\DEFAULT"), szDirectory);
	if(!PerformBackup(HKEY_USERS, _T(".DEFAULT"), szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");

	printf("Backing up NTUSER.DAT...");
	_sntprintf(szTemp, 1023, _T("%s\\NTUSER.DAT"), szDirectory);
	if(!PerformBackup(HKEY_CURRENT_USER, NULL, szTemp)) {
		bSuccess=false;
		DeleteFile(szTemp);
		printf("failed! *********************************\n");
	} else printf("succeeded.\n");

	return bSuccess;
}