Solved

HKEY_USERS

Posted on 2006-11-10
10
3,562 Views
Last Modified: 2012-08-14
I have an application that uses registry to store user preferances (windows user id based). each user can have different preferances and those are created when the user runs the application. So for each user the settings are available in HKEY_CURRENT_USER.

Now when the application is uninstalled, it clears registry only for the current user and the registry for other users remains as is. The thing I want is to clear the registry for all users.

I believe, that it should be possible by iterating through the number of entries available under HKEY_USERS. But not sure whether windows security would allow me to do this or not. if not how to get access.

Any ideas?
0
Comment
Question by:atul_parmar
  • 6
  • 3
10 Comments
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility

Yes, very possible and not too difficult either.

>> I believe, that it should be possible by iterating through the number of entries available under HKEY_USERS

Wrong. Unless you have fast user switching and the user is currently logged on, then the user's hive will NOT be loaded. If the user is logged on though, its easy enough to test for by checking for ERROR_SHARING_VIOLATION when attempting to RegLoadKey.

As to performing RegLoadKey, you will need to hold the SE_BACKUP_NAME / SE_RESTORE_NAME privileges, and have them enabled (you can find code on EE to do this, or download the privilege unit from my site). Then its a matter of loading the users hive, doing whatever you need to do (removal of setting values, etc), then unloading the hive.

Regards,
Russell

----

Example assuming we had a user called "foobar"

var
  dwError:       Integer;
  phKey:         HKEY;

begin

  // Create privilege wrapper
  with TUserPrivileges.Create do
  begin
     // Resource protection
     try
        // Check held privs
        if HoldsPrivilege(SE_BACKUP_NAME) and HoldsPrivilege(SE_RESTORE_NAME) then
        begin
           // Enable privs
           PrivilegeByName(SE_BACKUP_NAME).Enabled:=True;
           PrivilegeByName(SE_RESTORE_NAME).Enabled:=True;
           // Load the desired registry hive for user "foobar". You can use whatever subkey name you want so long as its
           // a) a valid reg key name and b) does not conflict with a current subkey name
           dwError:=RegLoadKey(HKEY_USERS, 'foobar', 'c:\documents and settings\foobar\ntuser.dat');
           // Check error code
           if (dwError = ERROR_SUCCESS) then
           begin
              // Resource protection
              try
                 // Open a key for testing
                 if (RegOpenKeyEx(HKEY_USERS, 'foobar\Software\Microsoft\Windows\CurrentVersion', 0, KEY_ALL_ACCESS, phKey) = ERROR_SUCCESS) then
                 begin
                    // Close the key
                    RegCloseKey(phKey);
                    // Display success
                    MessageBox(0, 'Loaded the hive for "foobar" and opened key!', nil, MB_OK);
                 end;
              finally
                 // Unload the registry hive
                 RegUnloadKey(HKEY_USERS, 'foobar');
              end;
           end
           else
              // Display error
              MessageBox(0, PChar(SysErrorMessage(dwError)), nil, MB_OK);
        end;
     finally
        // Free privilege wrapper
        Free;
     end;
  end;

end;

0
 
LVL 10

Author Comment

by:atul_parmar
Comment Utility
Russell, very thanks for this.

However my specific concern is what about the other users whose user ids are not known to me and I want to change their preferances.
0
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility
So what is the question then?

- Determining all user accounts on the system?
- Getting the SID authority value for a user account?
- Loading the reg hive for a user?
- All of the above?

You need to be a little more specific

Russell
0
 
LVL 10

Author Comment

by:atul_parmar
Comment Utility
I m sorry if it confused.

I am logged as USER1.
I opened the registry editor using regedit.exe and expanded the HKEY_USERS
It will show the users SID
e.g.
S-1-5-21-62671851-149427659-1843927889-6146
...

As per my assumption, it shows SID for all users (whose user ID is known to me)
I want to change the following registry for all users
S-1-5-21-62671851-149427659-1843927889-6146
+Software
  +MyApp
    +Settings -> I want to change this for all each user of this machine
0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 500 total points
Comment Utility

As I already stated, your assumption is **WRONG**. the HKEY_USERS root shows:

- your user account
- the local system account
- possible network service account
- any users that (please read carefully) ARE LOGGED ON.

If a user account is defined on the system, eg "foobar", but the account is not logged on, then the HKEY_USERS root key will NOT display the SID subkey for the user (foobar). For users that are not logged on, you will have to do what I already gave you code for.

- Load the reg hive and map into whatever name you want, then open the desired key, make the changes, then unload the reg hive.

here is further code that may help you to that end.

////////////////////////////////////////////////////////////////////////////
// Getting a list of all user accounts defined on the local computer
////////////////////////////////////////////////////////////////////////////
const
  MAX_PREFERRED_LENGTH       =  DWORD(-1);

type
  NET_API_STATUS             =  DWORD;

  _USER_INFO_0               =  packed record
     usri0_name:             LPWSTR;
  end;
  USER_INFO_0                =  _USER_INFO_0;
  PUSER_INFO_0               =  ^USER_INFO_0;
  LPUSER_INFO_0              =  PUSER_INFO_0;
  TUserInfo0                 =  _USER_INFO_0;
  PUserInfo0                 =  ^TUserInfo0;

function   NetUserEnum(ServerName: LPWSTR; Level, Filter: DWORD; var BufPtr: Pointer; PrefMaxLen: DWORD; EntriesRead: PDWORD; TotalEntries: PDWORD; ResumeHandle: PDWORD): NET_API_STATUS; stdcall; external 'netapi32.dll';
function   NetApiBufferFree(pBuf: Pointer): NET_API_STATUS; stdcall; external 'netapi32.dll';

procedure GetLocalUsers(List: TStrings);
var  lpBuffer:      PUserInfo0;
     lpUser:        PUserInfo0;
     dwRead:        DWORD;
     dwTotal:       DWORD;
     dwDomain:      DWORD;
     dwResume:      DWORD;
     dwError:       DWORD;
     dwIndex:       Integer;
begin

  // Check list
  if Assigned(List) then
  begin
     // Lock list
     List.BeginUpdate;
     try
        // Clear the list
        List.Clear;
        // Set starting resume handle
        dwResume:=0;
        // Get the user accounts on this system
        dwError:=NetUserEnum(nil, 0, 0, Pointer(lpBuffer), MAX_PREFERRED_LENGTH, @dwRead, @dwTotal, @dwResume);
        // Check result
        while (dwError = ERROR_SUCCESS) or (dwError = ERROR_MORE_DATA) do
        begin
           // Resource protection
           try
              // Save start of pointer
              lpUser:=lpBuffer;
              // Enumerate the groups
              for dwIndex:=0 to Pred(dwRead) do
              begin
                 // Copy the user account name
                 List.Add(WideCharToString(lpUser^.usri0_name));
                 // Push next group name
                 Inc(lpUser);
              end;
           finally
              // Free the allocated buffer
              NetApiBufferFree(lpBuffer);
           end;
           // Check for continue
           if (dwError = ERROR_MORE_DATA) and (dwResume > 0) then
              //  Continue enumeration
              dwError:=NetUserEnum(nil, 0, 0, Pointer(lpBuffer), MAX_PREFERRED_LENGTH, @dwRead, @dwTotal, @dwResume)
           else
              // Done processing
              break;
        end;
     finally
        // Unlock the list
        List.EndUpdate;
     end;
  end;

end;

////////////////////////////////////////////////////////////////////////////
// Converting a user account name into a SID string
////////////////////////////////////////////////////////////////////////////

function   ConvertSidToStringSid(SID: PSID; var lpszString: PChar): BOOL; stdcall; external 'advapi32.dll' name 'ConvertSidToStringSidA';

function GetUserSID(UserAccount: String): String;
var  lpszAccount:   Array [0..MAX_BUFFER] of Char;
     lpszDomain:    Array [0..MAX_BUFFER] of Char;
     lpszSID:       PChar;
     snUse:         SID_NAME_USE;
     lpSID:         PSID;
     dwDomain:      DWORD;
     dwSize:        DWORD;
     dwSID:         DWORD;
     dwIndex:       Integer;
begin

  // Set default result
  SetLength(result, 0);

  // Allocate memory for SID
  lpSID:=AllocMem(MAX_PATH);

  // Resource protection
  try
     // Set SID buffer size
     dwSID:=MAX_PATH;
     // Set domain size
     dwDomain:=MAX_PATH;
     // Clear domain buffer
     FillChar(lpszDomain, dwDomain, 0);
     // Account name handling
     StrPCopy(@lpszAccount, UserAccount);
     // Lookup the account name
     if LookupAccountName(nil, @lpszAccount, lpSID, dwSID, @lpszDomain, dwDomain, snUse) then
     begin
        // Convert SID into a string
        if ConvertSidToStringSid(lpSID, lpszSID) then
        begin
           // Resource protection
           try
              // Set result
              result:=lpszSID;
           finally
              // Free allocated memory
              LocalFree(Integer(lpszSID));
           end;
        end;
     end;
  finally
     // Free SID memory
     FreeMem(lpSID);
  end;

end;
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility
Btw, if your looking for a Win2k/XP only solution, then I can probably come up with some code that does the majority of the grunt work for you, and passes the desired key back in a callback function.

eg

type
 TRegEnumUserKeyCallback = function(Key: HKEY): Boolean;

// Enumeration function
procedure RegEnumUsersKey(SubKey: String; Callback: TRegEnumUserKeyCallback);

----

Russell
0
 
LVL 9

Expert Comment

by:alkisg
Comment Utility
If you're distributing with .MSI or NSIS technology, it is possible to do this on the uninstaller level, without Delphi code.
I can provide more info if you're interested in this...
0
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility
For those that may find this useful...

Russell

-----

Example usage:

function RegEnumProc(Key: HKEY; SIDAuthority: String): Boolean;
begin

  // Resource protection
  try
     // Do something with the key, eg delete values, set values, etc
     ShowMessage(Format('Key %d is opened (%s)', [Key, SIDAuthority]));
  finally
     // Return true to keep enumerating
     result:=True;
  end;

end;


  // Enumerate this registry key for all user accounts (profiles)
  RegEnumUserKey('Software\Microsoft\Windows', KEY_ALL_ACCESS, @RegEnumProc);




--- Source ---
unit RegUser;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  RegUser
//   Author      :  rllibby
//   Date        :  11.10.2006
//
//   Description :  Utility unit for Win2k / XP / 2003 for programmatically
//                  enumerating the opening of a specific registry subkey key
//                  for all user profiles on a local system. The current user
//                  as well as logged on / logged off users will be enumerated
//                  by this process. The code also handles old style profile
//                  locations, such as those from NT4 upgrades.
//
//   Requires    :  Privilege.pas
//                  http://users.adelphia.net/~rllibby/downloads/privilege.zip
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Privilege;

////////////////////////////////////////////////////////////////////////////////
//   Constants
////////////////////////////////////////////////////////////////////////////////
const
  RU_MINOSVERSION   =  5;
  RU_OLDPROFILE     =  '%systemroot%\Profiles\*.*';
  RU_NEWPROFILE     =  '%systemdrive%\Documents And Settings\*.*';
  RU_HIVENAME       =  'ntuser.dat';
  RU_IGNOREUSER:    Array [0..1] of String = ('All Users', 'Default User');

////////////////////////////////////////////////////////////////////////////////
//   Types
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
//   Imported functions
////////////////////////////////////////////////////////////////////////////////
function   ConvertSidToStringSid(SID: PSID; var lpszString: PChar): BOOL; stdcall; external 'advapi32.dll' name 'ConvertSidToStringSidA';

////////////////////////////////////////////////////////////////////////////////
//   Enumeration callback function prototype
////////////////////////////////////////////////////////////////////////////////
type
  TRegUserEnumProc  =  function(Key: HKEY; SIDAuthority: String): Boolean;

////////////////////////////////////////////////////////////////////////////////
//   Enumeration function
////////////////////////////////////////////////////////////////////////////////
function   RegEnumUserKey(SubKey: String; samDesired: DWORD; EnumProc: TRegUserEnumProc): Boolean;

////////////////////////////////////////////////////////////////////////////////
//   Utility functions
////////////////////////////////////////////////////////////////////////////////
function   GetCurrentUserSID: String;
function   GetUserSID(UserAccount: String): String;
function   IgnoreAccount(UserAccount: String): Boolean;
function   RequiredVersionCheck: Integer;

implementation

////////////////////////////////////////////////////////////////////////////////
//   Protected variables
////////////////////////////////////////////////////////////////////////////////
var
  lpPrivs:          TUserPrivileges;

//// Enumeration Function //////////////////////////////////////////////////////
function RegEnumUserKey(SubKey: String; samDesired: DWORD; EnumProc: TRegUserEnumProc): Boolean;
var  lpszProfiles:  Array [0..1, 0..MAX_PATH] of Char;
     lpFind:        TWin32FindData;
     szUserSID:     String;
     szSID:         String;
     szHive:        String;
     hFind:         THandle;
     hkUser:        HKEY;
     dwError:       Integer;
     dwIndex:       Integer;
     bContinue:     Boolean;
begin

  // Set default result
  result:=False;

  // Check the callback function and subkey
  if not(Assigned(EnumProc)) or (Length(SubKey) = 0)then
     // No callback specififed or subkey is null
     SetLastError(ERROR_INVALID_PARAMETER)
  // Check held privileges for those we need
  else if not(lpPrivs.HoldsPrivilege(SE_BACKUP_NAME) and lpPrivs.HoldsPrivilege(SE_RESTORE_NAME)) then
     // User does not hold required privileges
     SetLastError(ERROR_ACCESS_DENIED)
  // Perform windows check
  else if (RequiredVersionCheck = ERROR_SUCCESS) then
  begin
     // Switch default result
     result:=True;
     // Set continue flag
     bContinue:=True;
     // Set desired privileges
     lpPrivs.PrivilegeByName(SE_BACKUP_NAME).Enabled:=True;
     lpPrivs.PrivilegeByName(SE_RESTORE_NAME).Enabled:=True;
     // Set current user SID
     szUserSID:=GetCurrentUserSID;
     // Attempt to open the current user key
     if (RegOpenKeyEx(HKEY_CURRENT_USER, PChar(SubKey), 0, samDesired, hkUser) = ERROR_SUCCESS) then
     begin
        // Resource protection
        try
           // Perform the callback
           bContinue:=EnumProc(hkUser, szUserSID);
        finally
           // Close the key
           RegCloseKey(hkUser);
        end;
     end;
     // Can we continue?
     if bContinue then
     begin
        // Handle both new style and old style profile locations
        ExpandEnvironmentStrings(RU_OLDPROFILE, @lpszProfiles[0], MAX_PATH);
        ExpandEnvironmentStrings(RU_NEWPROFILE, @lpszProfiles[1], MAX_PATH);
        // Walk both profile locations
        for dwIndex:=0 to 1 do
        begin
           // Break if continue flag is no longer set
           if not(bContinue) then break;
           // Enumerate the profile path
           hFind:=FindFirstFile(@lpszProfiles[dwIndex], lpFind);
           // Check find handle
           if not(hFind = 0) then
           begin
              // Resource protection
              try
                 // Repeat while items to enumerate and continuation flag is set
                 repeat
                    // Check for path
                    if ((lpFind.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = FILE_ATTRIBUTE_DIRECTORY) then
                    begin
                       // Skip the '.' and '..' path root name
                       if not((lpFind.cFileName[0] = '.') and (lpFind.cFileName[1] in [#0, '.'])) then
                       begin
                          // Build hive filename
                          szHive:=IncludeTrailingBackslash(ExtractFilePath(lpszProfiles[dwIndex])) + lpFind.cFileName + '\' + RU_HIVENAME;
                          // Check the path for ntuser.dat and make sure we don't need to ignore the account
                          if FileExists(szHive) and not(IgnoreAccount(lpFind.cFileName)) then
                          begin
                             // Get the SID for the use account
                             szSID:=GetUserSID(lpFind.cFileName);
                             // Make sure we resolved  the SID and its not the current user's SID
                             if (Length(szSID) > 0) and not(CompareText(szSID, szUserSID) = 0) then
                             begin
                                // Attempt to load the hive. This will work correctly
                                // even for logged on users due to us using the SID
                                // authority of the user (eg its already mapped in)
                                dwError:=RegLoadKey(HKEY_USERS, PChar(szSID), PChar(szHive));
                                // Check error result
                                if (dwError = ERROR_SUCCESS) then
                                begin
                                   // Resource protection
                                   try
                                      // Attempt to open the user's key
                                      if (RegOpenKeyEx(HKEY_CURRENT_USER, PChar(SubKey), 0, samDesired, hkUser) = ERROR_SUCCESS) then
                                      begin
                                         // Resource protection
                                         try
                                            // Perform the callback
                                            bContinue:=EnumProc(hkUser, szSID);
                                         finally
                                            // Close the key
                                            RegCloseKey(hkUser);
                                         end;
                                      end;
                                   finally
                                      // Unload the hive
                                      RegUnloadKey(HKEY_USERS, PChar(szSID));
                                   end;
                                end
                                // Check for hive being loaded (failsafe code)
                                else if (dwError = ERROR_SHARING_VIOLATION) then
                                begin
                                   // Attempt to open the key using the user's SID
                                   if (RegOpenKeyEx(HKEY_USERS, PChar(szSID + '\' + SubKey), 0, samDesired, hkUser) = ERROR_SUCCESS) then
                                   begin
                                      // Resource protection
                                      try
                                         // Perform the callback
                                         bContinue:=EnumProc(hkUser, szSID);
                                      finally
                                         // Close the key
                                         RegCloseKey(hkUser);
                                      end;
                                   end;
                                end;
                             end;
                          end;
                       end;
                    end;
                 until not(FindNextFile(hFind, lpFind)) or not(bContinue);
              finally
                 // Close the find handle
                 Windows.FindClose(hFind);
              end;
           end;
        end;
     end;
  end;

end;

//// Utility functions /////////////////////////////////////////////////////////
function IgnoreAccount(UserAccount: String): Boolean;
var  dwIndex:       Integer;
begin

  // Set default result
  result:=False;

  // Check ignore list
  for dwIndex:=Low(RU_IGNOREUSER) to High(RU_IGNOREUSER) do
  begin
     // Check user account
     if (CompareText(RU_IGNOREUSER[dwIndex], UserAccount) = 0) then
     begin
        // Ignore
        result:=True;
        // Done processing
        break;
     end;
  end;

end;

function GetUserSID(UserAccount: String): String;
var  lpszDomain:    Array [0..MAX_PATH] of Char;
     snUse:         SID_NAME_USE;
     szUser:        String;
     szDomain:      String;
     lpSID:         PSID;
     lpszSID:       PChar;
     dwDomain:      DWORD;
     dwSID:         DWORD;
begin

  // Set default result
  SetLength(result, 0);

  // Save the account name
  szUser:=UserAccount;

  // If the user name is the format of account.domain then the last part is the domain name
  dwDomain:=LastDelimiter('.', szUser);

  // Check domain index
  if (dwDomain = 0) then
     // Make sure domain name is empty
     SetLength(szDomain, 0)
  else
  begin
     // Get the domain name
     szDomain:=Copy(szUser, Succ(dwDomain), MaxInt);
     // Remove the domain from the user name
     SetLength(szUser, Pred(dwDomain));
  end;

  // Allocate memory for SID
  lpSID:=AllocMem(MAX_PATH);

  // Resource protection
  try
     // Set SID buffer size
     dwSID:=MAX_PATH;
     // Set domain size
     dwDomain:=MAX_PATH;
     // Clear domain buffer
     FillChar(lpszDomain, dwDomain, 0);
     // Lookup the account name (if domain is blank, local account is used)
     if LookupAccountName(Pointer(szDomain), Pointer(szUser), lpSID, dwSID, @lpszDomain, dwDomain, snUse) then
     begin
        // Convert SID into a string
        if ConvertSidToStringSid(lpSID, lpszSID) then
        begin
           // Resource protection
           try
              // Set result
              result:=lpszSID;
           finally
              // Free allocated memory
              LocalFree(Integer(lpszSID));
           end;
        end;
     end;
  finally
     // Free SID memory
     FreeMem(lpSID);
  end;

end;

function GetCurrentUserSID: String;
var  hToken:        THandle;
     lpToken:       PTokenUser;
     lpszSID:       PChar;
     dwSize:        DWORD;
begin

  // Clear the passed buffer
  SetLength(result, 0);

  // Check result
  if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, hToken) then
  begin
     // Resource protection
     try
        // Get size for user's token
        GetTokenInformation(hToken, TokenUser, nil, 0, dwSize);
        // Allocate memory to get user's SID and attributes
        lpToken:=AllocMem(dwSize);
        // Resource protection
        try
           // Get user's token SID
           if GetTokenInformation(hToken, TokenUser, lpToken, dwSize, dwSize) then
           begin
              // Convert SID into a string
              if ConvertSidToStringSid(lpToken^.User.Sid, lpszSID) then
              begin
                 // Resource protection
                 try
                    // Set result
                    result:=lpszSID;
                 finally
                    // Free allocated memory
                    LocalFree(Integer(lpszSID));
                 end;
              end;
           end;
        finally
           // Free token memory
           FreeMem(lpToken);
        end;
     finally
        // Close token handle
        CloseHandle(hToken);
     end;
  end;

end;

function RequiredVersionCheck: Integer;
var  lpOSVersion:   TOSVersionInfo;
begin

  // Set default result
  result:=ERROR_SUCCESS;

  // Set buffer size
  lpOSVersion.dwOSVersionInfoSize:=SizeOf(TOSVersionInfo);

  // Resource protection
  try
     // Get the version info
     if GetVersionEx(lpOSVersion) then
     begin
        // Check the major version (Win 2000 and up)
        if (lpOSVersion.dwMajorVersion < RU_MINOSVERSION) then
           // Invalid version of windows
           result:=ERROR_OLD_WIN_VERSION
        else
           // Success
           result:=ERROR_SUCCESS;
     end
     else
        // Return last error
        result:=GetLastError;
  finally
     // Set the last error
     SetLastError(result);
  end;

end;

initialization

  // Create privileges wrapper
  lpPrivs:=TUserPrivileges.Create;

finalization

  // Free the privileges wrapper
  lpPrivs.Free;

end.
0
 
LVL 10

Author Comment

by:atul_parmar
Comment Utility
Russell, many thanks.
0
 
LVL 26

Expert Comment

by:Russell Libby
Comment Utility
Very welcome.
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now