Contents
Introduction
In a previous article: How to save and restore Registry Keys, I have provided a command-line tool to save/restore registry keys to/from data files. That's why I invite you if you are not familiar with this topic, to read first the previous article to have an idea about the subject. The provided tool in that article suffers from the fact that restoring keys from data files is not secure at all, except if we know exactly that the following two conditions are met:
- The data file corresponds exactly to the key we want to restore.
- The data file is exactly the same as the one that has been saved, i.e., has not been modified or corrupted.
The present article does not only respect the previous two conditions, but also provides a more general tool that can be used in two modes: in scripts as a command-line tool, and in UI mode as shown in the image above. In order to respond to these two conditions, we have to provide a registry configuration file (don't confuse with the program configuration file as indicated in the sequel, they are not necessarily the same) in which we add during saving stage, two important pieces of information:
- The correspondence between the data file location and the registry key path.
- A CRC32 value (Cyclic Redundancy Checksum in a 32-bit number) corresponding to the saved data file.
When it's time to restore data, we have to check data integrity, i.e., two things:
- Correspondence Key/File: we look for the data file in the configuration file corresponding to the key we want to restore (already done in save stage).
- File integrity check: we compute its CRC32, we compare it with the reported CRC32 (in save stage). If the two CRCs are the same, then we proceed to the restore action, otherwise the data file is not restored.
Basically, two Windows APIs have been used in the project: RegSaveKey
and RegRestoreKey
. There is no way by using these two API functions to ensure that saving and restoring registry keys is secure. It is said in MSDN:
"If RegSaveKey
fails part way through its operation, the file will be corrupt, and subsequent calls to RegLoadKey
, RegReplaceKey
, or RegRestoreKey
for the file will fail."
In order to use the tool accompanying this article, the calling process has to use an account in the Administrators group. The tool adds necessary privileges for saving and restoring registry, namely SE_BACKUP_NAME
(SeBackupPrivilege
) or/and SE_RESTORE_NAME
(SeRestorePrivilege
) respectively. It will be a good exercise to test for real privileges to achieve these two tasks without being in the Administrators group.
I have to express my thanks to:
- Brian Friesen for his excellent article CRC32: Generating a checksum for a file from which I have picked all functions to provide
CCRC32
class used in the project.
- Pavel Antonov for his excellent parser class
CCmdLineParser
in his article Command line parser used to parse the tool command-line.
How to use the provided tool
UI mode |
Command-line mode |
RegSR /UI |
RegSR /S|/R /H:ROOT /K:KEY /P:FILE [/C:CONFIG_FILE] |
|
/S /R ROOT KEY FILE CONF_FILE |
To save a registry key to file. To restore a registry key from file. Take HKCU, HKLM, HKUSERS, or HKCURCFG. Subkey path. Input file in restore mode or output file in save mode. Program configuration file used to secure restoring data. It contains also other information. ROOT values meaning:
HKCU |
HKEY_CURRENT_USER |
HKLM |
HKEY_LOCAL_MACHINE |
HKUSERS |
HKEY_USERS |
HKCURCFG |
HKEY_CURRENT_CONFIG | |
Remark: The program configuration file CONF_FILE
is the file indicating the server name where the registry save/restore configuration file should be located as the value of ServerPath
key and the configuration file name as the value of ConfigFile
key. Following are two examples of such a file:
Example 1 |
Example 2 |
[SETTINGS] ;The Server path is in the same directory as the program RegSR.exe ServerPath =. ;The config file name ConfigFile =RegConfig.ini
|
[SETTINGS] ;The Server path is on the machine whose name MyServer in the share called MyShare ServerPath =\\MyServer\MyShare ;The config file name ConfigFile =MyRegConfigFile.ini
|
In Example 1, the program will save key and file data including file CRC32 information in the file .\RegConfig.ini.
In Example 2, the program will save key and file data including file CRC32 information in the file \\MyServer\MyShare\RegConfig.ini.
Note that the program configuration file may be the same as the registry save/restore configuration file. In this case, we have to make the parameter CONFIG_FILE
to be the same as ServerPath\ConfigFile
.
When can we use the provided tool
The provided tool can be used in many contexts such as:
- To manage users profiles with respect to the registry.
- To configure software with respect to the registry at any time not necessarily during installation process.
- To configure hardware as printers, scanners, etc.
Context use
If you are running the tool in a a different context than the current user, like as in a service context, you cannot use HKCU (HKEY_CURRENT_USER) since you don't have the registry visibility of the logged-on user. In such a case, you can accede HKCU by the main of its SID under the registry hive HKUSERS (HKEY_USERS) with the condition that the SID value can be obtained. This is an example:
RegSr /R /H:HKUSERS /K:S-1-5-21-861567501-842925246-854245398-1004\Microsoft\Office /P:C:\Office.dat
Here, we restore the data file C:\Office.dat to the user whose SID=S-1-5-21-861567501-842925246-854245398-1004 to its key Microsoft\Office. The example above is equivalent to the following line but in the context of the current user:
RegSr /R /H:HKCU /K:Microsoft\Office /P:C:\Office.dat
How can we use the provided tool
There is a generic VBScript RegSr.vbs as shown below using the provided tool to save/restore registry keys. You can customize it with respect to your requirements. It generates a log file RegSR.log in the temporary directory containing the exit code of the program RegSR.exe. The returned codes are:
1 |
Non valid arguments |
2 |
File not found |
else |
Returned code from RegSR.exe. It can be also 2. |
To use the script, you have just to use the same syntax as that of the tool, i.e.,
RegSR.vbs /S|/R /H:ROOT /K:KEY /P:FILE [/C:CONFIG_FILE]
Example: RegSR.vbs /S /H:HKCU /K:software\test /P:c:\test.dat
RegSr.vbs listing:
Option Explicit
Const LOG_FILE="RegSR.LOG"
Const REG_SR="RegSR.exe"
Const SEPARATOR="==================================================="
Dim ScriptArgs: Set ScriptArgs = WScript.Arguments Dim WSHShell : _
Set WSHShell = WScript.CreateObject("WScript.Shell")
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
Dim EnvObject: Set EnvObject = WshShell.Environment("PROCESS")
Dim TempDir: TEMPDir = EnvObject.Item("TEMP")
Dim WScriptJet: WScriptJet = _
EnvObject.Item("WINDIR") &"\System32\WScript.exe"
Dim Op
Dim Root
Dim Key
Dim InOutFile Dim ConfigFile
Call MAIN()
Sub MAIN()
Dim Ret WriteToFile LOG_FILE,vbcrlf+SEPARATOR+vbcrlf+"BEGIN:<"
If GetParameters()=False Then
ExitScript(1)
Else
If ConfigFile<>"" Then
WriteToFile LOG_FILE,"Launching : "&_
CurrentDir()& REG_SR & " "& _
Op&" /H:"&Root&" /K:"&Key&_
" /P:"&InOutFile&" /C:" & _
ConfigFile Ret=LaunchEXE(CurrentDir() _
& REG_SR, Op&" /H:"&Root&_
" /K:"&Key& " /P:"&InOutFile&_
" /C:"&ConfigFile,True)
Else
WriteToFile LOG_FILE,"Launching : "& _
CurrentDir() & REG_SR & " "& _
Op&" /H:"&Root&" /K:"&Key&_
" /P:" & InOutFile Ret=LaunchEXE(CurrentDir() _
& REG_SR, Op&" /H:"&Root&_
" /K:"&Key& " /P:"&InOutFile,True)
End If
End If ExitScript(Ret)
End Sub
Function CurrentDir() CurrentDir=Mid(WScript.ScriptFullName,_
1,Len(WScript.ScriptFullName)-Len(WScript.ScriptName))
End Function
Sub ExitScript(ErrCode)
if ErrCode=0 Then WriteToFile LOG_FILE, _
"END:>Normal termination"+vbcrlf+_
SEPARATOR Else WriteToFile LOG_FILE, _
"END:>Error Code: " & CStr(ErrCode)+_
vbcrlf+SEPARATOR End If
Set fso=Nothing
Set WSHShell=Nothing
Set EnvObject=Nothing WScript.Quit(ErrCode)
End Sub
Sub WriteToFile(File, Text)
Dim TextFile File=TEMPDir+"\"+File
If Not IsFileExist(File) Then
Set TextFile=fso.CreateTextFile(File, True)
Else
Set TextFile=fso.OpenTextFile(File, 8)
End If
TextFile.WriteLine(Text)
TextFile.Close
Set TextFile=Nothing
End Sub
Function IsFileExist(File) IsFileExist=fso.FileExists(File)
End Function
Function IsDirExist(Fldr) IsDirExist=fso.FolderExists(Fldr)
End Function
Function LaunchEXE(EXE, Args, IsWait)
If (IsFileExist(EXE)) Then
If InStr(1, EXE, " ")>1 Then
LaunchEXE=WshShell.Run (Chr(34)& EXE _
& " " & Args&Chr(34), 1, IsWait)
Else
LaunchEXE=WshShell.Run (EXE & _
" " & Args, 1, IsWait)
End If
Else MsgBox(EXE & " Not found.")
ExitScript(2)
End If
End Function
Function GetArgument(Arg,TheSwitch,Value) _
GetArgument=False Arg=LCase(Arg):TheSwitch=LCase(TheSwitch)
If Mid(Arg,1,Len(TheSwitch))=TheSwitch Then
Value=Mid(Arg,Len(TheSwitch)+1,Len(Arg))
Value=Trim(Value) GetArgument=True
End If
End Function
Function GetParameters() GetParameters=False
Dim Value
If ScriptArgs.Count >=4 Then
'Get operation (save or restore)
If GetArgument(ScriptArgs(0),"/R",Value)=True Then
Op="/R"
ElseIf GetArgument(ScriptArgs(0), "/S",Value)=True Then
Op="/S"
Else GetParameters=False: Exit Function
End If
'Get Root
If GetArgument(ScriptArgs(1),"/H:",Value)=True Then
Root=Value
Else GetParameters=False: Exit Function
End If
'Get Key path
If GetArgument(ScriptArgs(2),"/K:",Value)=True Then
Key=Value
Else
GetParameters=False: Exit Function
End If
'Get InOutFile
If GetArgument(ScriptArgs(3),"/P:",Value)=True Then
InOutFile=Value
Else
GetParameters=False: Exit Function
End If
GetParameters=True
'Get ConfigFile
If ScriptArgs.Count=5 Then
If GetArgument(ScriptArgs(4),"/C:",Value)=True Then
ConfigFile=Value
End If
End If
End If
End Function
Using the code
The program is based on a WIN32 project. It contains the following classes:
CRegSRApp (RegSR.h) |
Application class. I leave to the reader to extract a class not necessarily based on the application class (CRegSRApp ) which can be used in any project to save/restore registry. |
CMainDlg (MainDlg.h) |
Main dialog class shown in UI mode. |
CCRC32 (CRC32.h) |
Used to compute files' CRC32. |
The trick to be able to call save/restore functions from the dialog implementation is to use the intermediate CWinApp
member theApp
by which we can call the unique non-constructor application two public functions:
void SetParams(CmdParams &P, int &PNum) |
Set parameters from command-line or from main dialog if the program is called in UI mode. |
void DoSaveRestore(DWORD &RetErr) |
That's the function which really does the registry save or restore job. |
I invite the reader to see inside the project for details about these two functions in particular. One private function worth to be explained is the function called to check for data integrity in restore stage, that's where we ensure that the data to restore is the right data and we don't risk shattering the registry.
BOOL CRegSRApp::CheckIntegrity(CString &Root, CString &SubKey,
CString &InFile)
{
BOOL ret=TRUE;
CString KeyPath=AddBackSlash(Root)+SubKey;
if (Get_CRC32()!=FALSE) {
CString RetCRC32;
DWORD dw=GetPrivateProfileString(SEC_CRC32,
SavResFile.FileName,
"", RetCRC32.GetBuffer(128), 128,
AddBackSlash(ServerPath)+ConfigFile);
RetCRC32.ReleaseBuffer();
if (dw>0) {
if (RetCRC32.CompareNoCase(SavResFile.CRC32)==0){
CString strKeyPath;
DWORD dw=GetPrivateProfileString(
SEC_KEYS_FILES, SavResFile.FileName,
"", strKeyPath.GetBuffer(255), 255,
AddBackSlash(ServerPath)+ConfigFile);
strKeyPath.ReleaseBuffer();
ret=(strKeyPath.CompareNoCase(KeyPath)==0)
?TRUE:FALSE;
} else ret=FALSE;
} else ret=FALSE;
} else ret=FALSE;
return ret;
}
Points of Interest
This article has shown the following facts:
- How to provide in one project a command-line and UI tool with minimum work.
- How to make saving registry to data file and especially restoring registry from data file very safe, by providing a registry configuration file.
In order to take more advantage of this work, we can easily extract an application-independent class to save/restore registry keys.
History
First version: August 2004.