65.9K
CodeProject 正在变化。 阅读更多。
Home

非常安全地保存和还原注册表的方法

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.18/5 (9投票s)

2004年8月24日

7分钟阅读

viewsIcon

75180

downloadIcon

990

本文提供了一种非常安全的保存和恢复注册表项的方法。它提供了命令行和图形用户界面(UI)模式下可直接使用的工具。

目录

引言

在先前的文章《如何保存和恢复注册表项》中,我提供了一个命令行工具,用于将注册表项保存/恢复到/从数据文件中。因此,如果您不熟悉此主题,我邀请您先阅读前一篇文章,以了解该主题。该文章中提供的工具存在一个问题,即从数据文件恢复注册表项根本不安全,除非我们确切知道以下两个条件都满足:

  1. 数据文件与我们要恢复的注册表项完全对应。
  2. 数据文件与保存时完全相同,即未被修改或损坏。

本文不仅满足了上述两个条件,还提供了一个更通用的工具,该工具可以在两种模式下使用:作为脚本中的命令行工具,以及如上图所示的 UI 模式。为了满足这两个条件,我们需要提供一个注册表配置文件(不要将其与后续内容中提到的程序配置文件混淆,它们不一定相同),在保存阶段,我们在其中添加两个重要信息:

  1. 数据文件位置与注册表项路径之间的对应关系。
  2. 与已保存数据文件对应的 CRC32 值(32 位数字的循环冗余校验和)。

在恢复数据时,我们需要检查数据完整性,即两项内容:

  1. 注册表项/文件对应关系:我们在配置文件中查找与我们要恢复的注册表项对应的文件(在保存阶段已完成)。
  2. 文件完整性检查:我们计算其 CRC32,并将其与报告的 CRC32(在保存阶段)进行比较。如果两个 CRC 相同,则继续执行恢复操作,否则不恢复数据文件。

基本上,项目中使用了两个 Windows API:RegSaveKeyRegRestoreKey。使用这两个 API 函数无法确保保存和恢复注册表项的安全性。MSDN 中有如下说明:

“如果 RegSaveKey 在其操作过程中途失败,则文件将损坏,后续对该文件的 RegLoadKeyRegReplaceKeyRegRestoreKey 调用将失败。”

要使用本文附带的工具,调用进程必须使用管理员组中的账户。该工具添加了保存和恢复注册表所需的必要权限,分别是 SE_BACKUP_NAMESeBackupPrivilege)或/和 SE_RESTORE_NAMESeRestorePrivilege)。如果在不属于管理员组的情况下完成这两项任务,将是一个很好的实际权限测试练习。

我在此感谢:

  • Brian Friesen,感谢他撰写的优秀文章《CRC32: Generating a checksum for a file》,我从中选取了所有函数来提供项目中使用的 CCRC32 类。
  • Pavel Antonov,感谢他撰写的优秀文章《Command line parser》,其中使用的 CCmdLineParser 类用于解析工具的命令行。

如何使用提供的工具

UI 模式 命令行模式
RegSR /UI RegSR /S|/R /H:ROOT /K:KEY /P:FILE [/C:CONFIG_FILE]

/S
/R
ROOT
KEY
文件
CONF_FILE

保存注册表项到文件。
从文件恢复注册表项。
取值范围包括 HKCU、HKLM、HKUSERS 或 HKCURCFG。
子项路径。
恢复模式下的输入文件或保存模式下的输出文件。
用于保护恢复数据的程序配置文件。其中还包含其他信息。

ROOT 值含义
HKCU HKEY_CURRENT_USER
HKLM HKEY_LOCAL_MACHINE
HKUSERS HKEY_USERS
HKCURCFG HKEY_CURRENT_CONFIG

注意:程序配置文件 CONF_FILE 是一个文件,它指示注册表保存/恢复配置文件应位于的服务器名称(作为 ServerPath 键的值)以及配置文件的名称(作为 ConfigFile 键的值)。以下是这类文件的两个示例:

示例 1 示例 2

[SETTINGS]
;服务器路径与程序 RegSR.exe 在同一目录下
ServerPath =.
;配置文件名
ConfigFile =RegConfig.ini

[SETTINGS]
;服务器路径位于名为 MyServer 的计算机上,共享名为 MyShare
ServerPath =\\MyServer\MyShare
;配置文件名
ConfigFile =MyRegConfigFile.ini


示例 1 中,程序会将注册表项和文件数据(包括文件的 CRC32 信息)保存在 .\RegConfig.ini 文件中。

示例 2 中,程序会将注册表项和文件数据(包括文件的 CRC32 信息)保存在 \\MyServer\MyShare\RegConfig.ini 文件中。

请注意,程序配置文件可以与注册表保存/恢复配置文件相同。在这种情况下,我们将参数 CONFIG_FILE 设置为 ServerPath\ConfigFile

何时可以使用提供的工具

提供的工具可以在许多场景中使用,例如:

  • 管理与注册表相关的用户配置文件。
  • 在任何时候配置软件与注册表相关的设置,不一定仅限于安装过程。
  • 配置硬件,如打印机、扫描仪等。

上下文使用

如果您在当前用户以外的上下文中运行该工具,例如在服务上下文中,则无法使用 HKCU(HKEY_CURRENT_USER),因为您无法访问已登录用户的注册表可见性。在这种情况下,您可以通过其 SID 的主键在 HKUSERS(HKEY_USERS)注册表蜂巢下访问 HKCU,前提是能够获取 SID 值。这是一个示例:

RegSr /R /H:HKUSERS /K:S-1-5-21-861567501-842925246-854245398-1004\Microsoft\Office /P:C:\Office.dat

在这里,我们将数据文件 C:\Office.dat 恢复到 SID 为 S-1-5-21-861567501-842925246-854245398-1004 的用户的 Microsoft\Office 注册表项。上面的示例等同于以下行,但在当前用户的上下文中:

RegSr /R /H:HKCU /K:Microsoft\Office /P:C:\Office.dat

如何使用提供的工具

有一个通用的 VBScript RegSr.vbs,如下所示,它使用提供的工具来保存/恢复注册表项。您可以根据您的需求对其进行自定义。它会在临时目录中生成一个日志文件 RegSR.log,其中包含程序 RegSR.exe 的退出代码。返回的代码包括:

1 无效参数
2 文件未找到
else 来自 RegSR.exe 的返回代码。也可能是 2。

要使用该脚本,您只需使用与工具相同的语法,即:

RegSR.vbs /S|/R /H:ROOT /K:KEY /P:FILE [/C:CONFIG_FILE]

示例: RegSR.vbs /S /H:HKCU /K:software\test /P:c:\test.dat

RegSr.vbs 列表

'////////////////////////////////////////////////////////
' Purpose:    Registry Save/Restore 
' Author:        A. YEZZA ' Date:        August 2004 
'//////////////////////////////////////////////////////// 

Option Explicit 

'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
'CONSTANTS 
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
Const LOG_FILE="RegSR.LOG" 
Const REG_SR="RegSR.exe" 
Const SEPARATOR="===================================================" 

'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
'Global variables 
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
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" 

'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
'Command-line Arguments variables 
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
Dim Op 
Dim Root 
Dim Key 
Dim InOutFile Dim ConfigFile '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
'MAIN 
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
Call MAIN()  


'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
'> PROCEDURES > 
'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
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

使用代码

该程序基于 WIN32 项目。它包含以下类:

CRegSRApp (RegSR.h) 应用程序类。我将留给读者提取一个不一定基于应用程序类(CRegSRApp)的类,该类可以在任何项目中用于保存/恢复注册表。
CMainDlg (MainDlg.h) UI 模式下的主对话框类。
CCRC32 (CRC32.h) 用于计算文件的 CRC32。

为了能够从对话框实现调用保存/恢复函数,诀窍在于使用中间的 CWinApp 成员 theApp,通过它可以调用唯一的非构造函数应用程序的两个公共函数:

void SetParams(CmdParams &P, int &PNum) 从命令行或从主对话框设置参数(如果程序以 UI 模式调用)。
void DoSaveRestore(DWORD &RetErr) 这个函数实际上执行了注册表保存或恢复操作。

我邀请读者在项目中查看有关这两个函数的详细信息。一个值得解释的私有函数是恢复阶段用于检查数据完整性的函数,在那里我们确保要恢复的数据是正确的数据,并且我们不会冒险损坏注册表。

//    This function checks 2 things:
//    1.The fact that the file from which we restore the key 
//      has the right CRC32 already reported to config file
//    2.The fact that the file corresponds exactly to the key 
//      to restore from the file
//    NOTE: These 2 information have been written in the save stage
//    Return TRUE if OKAY else FALSE
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) {
            //Compare calculated CRC32 and the reported one
            if (RetCRC32.CompareNoCase(SavResFile.CRC32)==0){
                //Get the corresponding key and compare
                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;
}

趣味点

本文展示了以下事实:

  • 如何在同一个项目中以最少的工作量提供命令行和 UI 工具。
  • 如何通过提供注册表配置文件,使注册表保存到数据文件,尤其是从数据文件恢复注册表的操作变得非常安全。

为了更好地利用这项工作,我们可以轻松提取一个与应用程序无关的类来保存/恢复注册表项。

历史

第一个版本:2004 年 8 月。

© . All rights reserved.