简体   繁体   中英

How to load registry hive for all users in a loop

With admin privileges, I need to enumerate all users on a Windows 7+ system (even ones that are logged off). Then I need to load the registry hive for each user and set a key.

NetUserEnum gives me the SID (I guess LsaEnumerateLogonSessions would as well). WTSEnumerateSessions followed by WTSQueryUserToken (to get a token) would be nice but it does not work for users who are not actively logged on.

So, my question, after calling NetUserEnum, how do I use the SID to load the registry for that user? Any recommended way of doing this?

Information about local user profiles is stored in this Registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

It is possible to enumerate it subkeys, where each subkey has a ProfileImagePath that points to the folder where ntuser.dat is located.

But, directly loading a user profile by RegLoadKey() is very bad. First, the profile may already be loaded. Second, it is possible that after you load the profile yourself, the system may also try loading the profile. Note the RefCount value. The system uses that value to load the profile if it is not already loaded, incrementing RefCount . And UnloadUserProfile() decrements RefCount and unloads the profile only when it become 0 by calling RegUnLoadKey() . So all profile load/unload operations must be synchronized.

There is only one correct way to load a profile - call LoadUserProfile() . (internally it performs a RPC call to profsvc.LoadUserProfileServer in svchost.exe -k netsvcs , where all synchronization is done).

So how do you get the user token for LoadUserProfile() ? I guess call LogonUser() , which you said you do not want to do (and cannot unless you have the user's password).

But, there does exist another way that works (I tested this), but it is undocumented . LoadUserProfile used only the user Sid from token (query for TOKEN_USER information with TokenUser iformation class) and then work with

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<Sid>

key

It is possible to create a token by calling ZwCreateToken() with any given SID, but for this call you need SE_CREATE_TOKEN_PRIVILEGE . This priviledge exists only in the lsass.exe process. So a possible solution is:

  1. open lsass.exe and get its token, or impersonate its thread.
  2. enable SE_CREATE_TOKEN_PRIVILEGE in the token, after impersonation
  3. enumerate HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList , and for each subkey query its Sid value, or (if Sid does not exist) convert the subkey name to a SID using ConvertStringSidToSid()
  4. create a token with that SID
  5. and finally call LoadUserProfile()

-------------- EDIT code example by request ----------------------------

code used ntdll export (which somebody here very not like) but as is

  1. we need got SE_CREATE_TOKEN_PRIVILEGE to create token by yourself in future

enum processes in the system, open token for every process, look are SE_CREATE_TOKEN_PRIVILEGE exist in token, if yes - duplicate this token and if need enable SE_CREATE_TOKEN_PRIVILEGE in it. finally impersonate with duplicated token

BOOL g_IsXP;// true if we on winXP, false otherwise
static volatile UCHAR guz;
static OBJECT_ATTRIBUTES zoa = { sizeof(zoa) };

NTSTATUS ImpersonateIfConformToken(HANDLE hToken)
{
    ULONG cb = 0, rcb = 0x200;
    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PTOKEN_PRIVILEGES ptp;
    };

    NTSTATUS status;
    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = ZwQueryInformationToken(hToken, TokenPrivileges, buf, cb, &rcb)))
        {
            if (ULONG PrivilegeCount = ptp->PrivilegeCount)
            {
                PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
                do 
                {
                    if (Privileges->Luid.LowPart == SE_CREATE_TOKEN_PRIVILEGE && !Privileges->Luid.HighPart)
                    {
                        static SECURITY_QUALITY_OF_SERVICE sqos = {
                            sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE
                        };

                        static OBJECT_ATTRIBUTES soa = { sizeof(soa), 0, 0, 0, 0, &sqos };

                        if (0 <= (status = ZwDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE, &soa, FALSE, TokenImpersonation, &hToken)))
                        {
                            if (Privileges->Attributes & SE_PRIVILEGE_ENABLED)
                            {
                                status = STATUS_SUCCESS;
                            }
                            else
                            {
                                static TOKEN_PRIVILEGES tp = {
                                    1, { { { SE_CREATE_TOKEN_PRIVILEGE }, SE_PRIVILEGE_ENABLED } }
                                };

                                status = ZwAdjustPrivilegesToken(hToken, FALSE, &tp, 0, 0, 0);
                            }

                            if (status == STATUS_SUCCESS)
                            {
                                status = ZwSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hToken, sizeof(HANDLE));
                            }

                            ZwClose(hToken);
                        }

                        return status;
                    }
                } while (Privileges++, --PrivilegeCount);
            }

            return STATUS_PRIVILEGE_NOT_HELD;
        }

    } while (status == STATUS_BUFFER_TOO_SMALL);

    return status;
}

NTSTATUS GetCreateTokenPrivilege()
{
    BOOLEAN b;
    RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &b);

    ULONG cb = 0, rcb = 0x10000;
    PVOID stack = alloca(guz);

    union {
        PVOID buf;
        PBYTE pb;
        PSYSTEM_PROCESS_INFORMATION pspi;
    };

    NTSTATUS status;
    do 
    {
        if (cb < rcb)
        {
            cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
        }

        if (0 <= (status = ZwQuerySystemInformation(SystemProcessInformation, buf, cb, &rcb)))
        {
            status = STATUS_UNSUCCESSFUL;

            ULONG NextEntryOffset = 0;
            do 
            {
                pb += NextEntryOffset;

                if (pspi->InheritedFromUniqueProcessId && pspi->UniqueProcessId)
                {
                    CLIENT_ID cid = { pspi->UniqueProcessId };

                    NTSTATUS s = STATUS_UNSUCCESSFUL;
                    HANDLE hProcess, hToken;

                    if (0 <= ZwOpenProcess(&hProcess, g_IsXP ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION, &zoa, &cid))
                    {
                        if (0 <= ZwOpenProcessToken(hProcess, TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
                        {
                            s = ImpersonateIfConformToken(hToken);

                            NtClose(hToken);
                        }

                        NtClose(hProcess);
                    }

                    if (s == STATUS_SUCCESS)
                    {
                        return STATUS_SUCCESS;
                    }
                }

            } while (NextEntryOffset = pspi->NextEntryOffset);

            return status;
        }

    } while (status == STATUS_INFO_LENGTH_MISMATCH);

    return STATUS_UNSUCCESSFUL;
}

if we have SE_CREATE_TOKEN_PRIVILEGE - we can create token !

NTSTATUS CreateUserToken(PHANDLE phToken, PSID Sid)
{
    HANDLE hToken;
    TOKEN_STATISTICS ts;
    NTSTATUS status = ZwOpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken);

    if (0 <= status)
    {
        if (0 <= (status = ZwQueryInformationToken(hToken, TokenStatistics, &ts, sizeof(ts), &ts.DynamicCharged)))
        {
            ULONG cb = 0, rcb = 0x200;
            PVOID stack = alloca(guz);

            union {
                PVOID buf;
                PTOKEN_PRIVILEGES ptp;
            };

            do 
            {
                if (cb < rcb)
                {
                    cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
                }

                if (0 <= (status = ZwQueryInformationToken(hToken, TokenPrivileges, buf, cb, &rcb)))
                {
                    TOKEN_USER User = { { Sid } };

                    static TOKEN_SOURCE Source = { {' ','U','s','e','r','3','2', ' '} };

                    static TOKEN_DEFAULT_DACL tdd;// 0 default DACL
                    static TOKEN_GROUPS Groups;// no groups

                    static SECURITY_QUALITY_OF_SERVICE sqos = {
                        sizeof sqos, SecurityImpersonation, SECURITY_DYNAMIC_TRACKING
                    };

                    static OBJECT_ATTRIBUTES oa = { 
                        sizeof oa, 0, 0, 0, 0, &sqos
                    };

                    status = ZwCreateToken(phToken, TOKEN_ALL_ACCESS, &oa, TokenPrimary, 
                        &ts.AuthenticationId, &ts.ExpirationTime, &User, &Groups, ptp, (PTOKEN_OWNER)&Sid,
                        (PTOKEN_PRIMARY_GROUP)&Sid, &tdd, &Source);

                    break;
                }

            } while (status == STATUS_BUFFER_TOO_SMALL);
        }

        ZwClose(hToken);
    }

    return status;
}

and finally enumerate and load/unload user profiles

void EnumProf()
{
    PROFILEINFO pi = { sizeof(pi), PI_NOUI };
    pi.lpUserName = L"*";

    STATIC_OBJECT_ATTRIBUTES(soa, "\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList");

    HANDLE hKey;
    if (0 <= ZwOpenKey(&hKey, KEY_READ, &soa))
    {
        PVOID stack = alloca(sizeof(WCHAR));

        union
        {
            PVOID buf;
            PKEY_BASIC_INFORMATION pkbi;
            PKEY_VALUE_PARTIAL_INFORMATION pkvpi;
        } u = {};

        DWORD cb = 0, rcb = 64;
        NTSTATUS status;
        ULONG Index = 0;

        do 
        {
            do 
            {
                if (cb < rcb)
                {
                    cb = RtlPointerToOffset(u.buf = alloca(rcb - cb), stack);
                }

                if (0 <= (status = ZwEnumerateKey(hKey, Index, KeyBasicInformation, u.buf, cb, &rcb)))
                {
                    *(PWSTR)RtlOffsetToPointer(u.pkbi->Name, u.pkbi->NameLength) = 0;

                    PSID Sid;
                    if (ConvertStringSidToSidW(u.pkbi->Name, &Sid))
                    {
                        HANDLE hToken;

                        if (0 <= CreateUserToken(&hToken, Sid))
                        {
                            if (LoadUserProfile(hToken, &pi))
                            {
                                UnloadUserProfile(hToken, pi.hProfile);
                            }

                            NtClose(hToken);
                        }
                        LocalFree(Sid);
                    }
                }

            } while (status == STATUS_BUFFER_OVERFLOW);

            Index++;

        } while (0 <= status);

        ZwClose(hKey);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM