Jump to content
Dave Nottage

Saving registry keys

Recommended Posts

Going (more) insane here.. I have the following test code:

 

uses
  System.Win.Registry;

function SetTokenPrivilege(const APrivilege: string; const AEnable: Boolean): Boolean;
var
  LToken: THandle;
  LTokenPriv: TOKEN_PRIVILEGES;
  LPrevTokenPriv: TOKEN_PRIVILEGES;
  LLength: Cardinal;
  LErrval: Cardinal;
begin
  Result := False;
  if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, LToken) then
  try
    // Get the locally unique identifier (LUID) .
    if LookupPrivilegeValue(nil, PChar(APrivilege), LTokenPriv.Privileges[0].Luid) then
    begin
      LTokenPriv.PrivilegeCount := 1; // one privilege to set
      case AEnable of
        True: LTokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;
        False: LTokenPriv.Privileges[0].Attributes := 0;
      end;
      LPrevTokenPriv := LTokenPriv;
      // Enable or disable the privilege
      Result := AdjustTokenPrivileges(LToken, False, LTokenPriv, SizeOf(LPrevTokenPriv), LPrevTokenPriv, LLength);
    end;
  finally
    CloseHandle(LToken);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  LReg: TRegistry;
begin
  if not SetTokenPrivilege('SeBackupPrivilege', True) then
    Exit; // <======
  LReg := TRegistry.Create(KEY_ALL_ACCESS);
  try
    LReg.RootKey := HKEY_CURRENT_USER;
    if not LReg.SaveKey('Software\Microsoft\Notepad', 'C:\Temp\Notepad.reg') then
      ShowMessage('Could not save');
  finally
    LReg.Free;
  end;
end;

Which results in showing the "Could not save" message. Am I missing something obvious?

Share this post


Link to post
56 minutes ago, Dave Nottage said:

"Could not save"

 

Works in Berlin when I change output to an existing folder: 'd:\Notepad.dat'

 

Plus this addition gives me the dialog below if I try to save into a dir which does not exist:

 ShowMessage('Could not save: ' + LReg.LastErrorMsg);

[Window Title]
Project1

[Content]
Could not save: The system cannot find the path specified

[OK]

Edited by FredS

Share this post


Link to post
2 hours ago, FredS said:

Could not save: The system cannot find the path specified

I receive:

 

[Window Title]
Project1

[Content]
Could not save: A required privilege is not held by the client

[OK]
 

Which is odd, since SetTokenPrivilege succeeds.

 

Using Delphi 10.3.2

Edited by Dave Nottage

Share this post


Link to post

Stupid question: Does Temp really exist (typo, wrong UTF8-character, etc.) ?

I usually put a FileExists before such things, to be sure.

'C:\Temp\Notepad.reg'
Edited by Rollo62

Share this post


Link to post
12 hours ago, Dave Nottage said:

A required privilege is not held by the client

 

I was using Berlin elevated when I ran that and get the same error msg while running non elevated.

 

My own wrapper around AdjustTokenPrivileges works because it checks the last error after that call regardless of the number of privileges adjusted.

In your demo (GetLastError=1300) while running non elevated.

 

This code behaves correctly:

      // Enable or disable the privilege
      Result := AdjustTokenPrivileges(LToken, False, LTokenPriv, SizeOf(LPrevTokenPriv), LPrevTokenPriv, LLength);
      Result := Result and (GetLastError = ERROR_SUCCESS);

 

Share this post


Link to post
12 hours ago, Dave Nottage said:

Could not save: A required privilege is not held by the client

 

Which is odd, since SetTokenPrivilege succeeds.

Per the AdjustTokenPrivileges() documentation:

Quote

If the function succeeds, the return value is nonzero. To determine whether the function adjusted all of the specified privileges, call GetLastError, which returns one of the following values when the function succeeds:

Return code Description
ERROR_SUCCESS
The function adjusted all specified privileges.
ERROR_NOT_ALL_ASSIGNED
The token does not have one or more of the privileges specified in the NewState parameter. The function may succeed with this error value even if no privileges were adjusted. The PreviousState parameter indicates the privileges that were adjusted.

Your code is not differentiating between ERROR_SUCCESS and ERROR_NOT_ALL_ASSIGNED if AdjustTokenPrivileges() returns non-zero.

Edited by Remy Lebeau

Share this post


Link to post
11 hours ago, Rollo62 said:

Stupid question: Does Temp really exist (typo, wrong UTF8-character, etc.) ?

I usually put a FileExists before such things, to be sure.


'C:\Temp\Notepad.reg'

TRegistry.SaveKey() uses the Win32 API RegSaveKey() function, which fails if the output file already exists, per documented behavior:

Quote

lpFile

The name of the file in which the specified key and subkeys are to be saved. If the file already exists, the function fails.

Perhaps you meant DirectoryExists() instead of FileExists()?  You could always call ForceDirectories() before SaveKey(), just in case.

  • Like 1

Share this post


Link to post
2 hours ago, Remy Lebeau said:

Your code is not differentiating between

 

Which should not be required for a single privilege.

 

Edited by FredS
Removed, ended up not working.
  • Like 1

Share this post


Link to post
3 hours ago, Remy Lebeau said:

TRegistry.SaveKey() uses the Win32 API RegSaveKey() function

Additionally, RegSaveKey doesn't save in .reg format but in registry hive format.

There is no API for saving in .reg format.

  • Like 1

Share this post


Link to post
6 hours ago, FredS said:

Which should not be required for a single privilege.

Enabling/disabling even a single privilege can still potentially report ERROR_NOT_ALL_ASSIGNED. It doesn't hurt to handle that condition, just in case.

  • Thanks 1

Share this post


Link to post

Hi Dave,

 

I prepared a sample that works for you.

a few days ago you solved my problem. now it's my turn  😉

 

uses
  System.Win.Registry;

function SetPrivilege(Privilege: PChar; EnablePrivilege: Boolean; out PreviousState: Boolean): DWORD;
var
  Token: THandle;
  NewState: TTokenPrivileges;
  Luid: TLargeInteger;
  PrevState: TTokenPrivileges;
  Return: DWORD;
begin
  PreviousState := True;
  if (GetVersion() > $80000000) then
    // Win9x
    Result := ERROR_SUCCESS
  else
  begin
    // WinNT
    if not OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, Token) then
      Result := GetLastError()
    else
      try
        if not LookupPrivilegeValue(nil, Privilege, Luid) then
          Result := GetLastError()
        else
        begin
          NewState.PrivilegeCount := 1;
          NewState.Privileges[0].Luid := Luid;
          if EnablePrivilege then
            NewState.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
          else
            NewState.Privileges[0].Attributes := 0;
          if not AdjustTokenPrivileges(Token, False, NewState, SizeOf(TTokenPrivileges), PrevState, Return) then
            Result := GetLastError()
          else
          begin
            Result := ERROR_SUCCESS;
            PreviousState := (PrevState.Privileges[0].Attributes and SE_PRIVILEGE_ENABLED <> 0);
          end;
        end;
      finally
        CloseHandle(Token);
      end;
  end;
end;

function RegSaveKeyToFile(Key: HKEY; const SubKey, FileName: string): DWORD;
const
  SE_BACKUP_NAME = 'SeBackupPrivilege';
var
  PreviousState: Boolean;
  KeyHandle: HKEY;
begin
  Result := SetPrivilege(SE_BACKUP_NAME, True, PreviousState);
  if (Result = ERROR_SUCCESS) then
    try
      KeyHandle := 0;
      Result := RegOpenKeyEx(Key, PChar(SubKey), 0, MAXIMUM_ALLOWED, KeyHandle);
      if (Result = ERROR_SUCCESS) then
        try
          // FIXME: Short Filename on Win9x!
          Result := RegSaveKey(KeyHandle, PChar(FileName), nil);
        finally
          RegCloseKey(KeyHandle);
        end;
    finally
      if (not PreviousState) then
        SetPrivilege(SE_BACKUP_NAME, PreviousState, PreviousState);
    end;
end;

function RegLoadKeyFromFile(Key: HKEY; const SubKey, FileName: string): DWORD;
const
  SE_BACKUP_NAME = 'SeBackupPrivilege';
  SE_RESTORE_NAME = 'SeRestorePrivilege';
var
  PrevBackup: Boolean;
  PrevRestore: Boolean;
  KeyHandle: HKEY;
  ShortName: array [0 .. MAX_PATH] of Char;
begin
  Result := SetPrivilege(SE_BACKUP_NAME, True, PrevBackup);
  if (Result = ERROR_SUCCESS) then
    try
      Result := SetPrivilege(SE_RESTORE_NAME, True, PrevRestore);
      if (Result = ERROR_SUCCESS) then
        try
          if (GetVersion() > $80000000) then
          begin // Win9x (FIXME: Test it! - and see RegReplaceKey)
            if (GetShortPathName(PChar(FileName), ShortName, MAX_PATH) = 0) then
              Result := GetLastError()
            else
              Result := RegLoadKey(Key, PChar(SubKey), ShortName);
          end
          else
          begin // WinNT (FIXME: Load RegRestoreKey dynamically!)
            KeyHandle := 0;
            Result := RegOpenKeyEx(Key, PChar(SubKey), 0, MAXIMUM_ALLOWED, KeyHandle);
            if (Result = ERROR_SUCCESS) then
              try
                Result := RegRestoreKey(KeyHandle, PChar(FileName), 0);
              finally
                RegCloseKey(KeyHandle);
              end;
          end;
        finally
          if (not PrevRestore) then
            SetPrivilege(SE_RESTORE_NAME, PrevRestore, PrevRestore);
        end;
    finally
      if (not PrevBackup) then
        SetPrivilege(SE_BACKUP_NAME, PrevBackup, PrevBackup);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Key = HKEY(HKEY_LOCAL_MACHINE);
  SubKey = 'Software\Microsoft\Notepad';
var
  FileName: string;
  ErrorCode: DWORD;
begin
  SetLength(FileName, MAX_PATH + 1);
  SetLength(FileName, GetTempPath(MAX_PATH, PChar(FileName)));
  FileName := 'C:\build\notepad.reg';
  ErrorCode := RegSaveKeyToFile(Key, SubKey, FileName);
  if (ErrorCode <> ERROR_SUCCESS) then
    ShowMessage('Save: ' + SysErrorMessage(ErrorCode))
  else
  begin
    ErrorCode := RegLoadKeyFromFile(Key, SubKey, FileName);
    if (ErrorCode <> ERROR_SUCCESS) then
      ShowMessage('Load: ' + SysErrorMessage(ErrorCode))
    else
      ShowMessage(IntToStr(42));
  end;
end;

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×