如何为循环中的所有用户加载注册表配置单元

How to load registry hive for all users in a loop

使用管理员权限,我需要枚举 Windows 7+ 系统上的所有用户(甚至是已注销的用户)。然后我需要为每个用户加载注册表配置单元并设置一个键。

NetUserEnum 给了我 SID(我想 LsaEnumerateLogonSessions 也会)。 WTSEnumerateSessions 后跟 WTSQueryUserToken(以获取令牌)会很好,但它不适用于未主动登录的用户。

所以,我的问题是,在调用 NetUserEnum 之后,如何使用 SID 为该用户加载注册表?有什么推荐的方法吗?

有关本地用户配置文件的信息存储在此注册表项中:

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

可以枚举它的子项,其中每个子项都有一个 ProfileImagePath 指向 ntuser.dat 所在的文件夹。

但是,直接通过 RegLoadKey() 加载用户配置文件是非常糟糕的。首先,配置文件可能已经加载。其次,有可能在您自己加载配置文件后,系统也可能会尝试加载配置文件。注意 RefCount 值。如果尚未加载,系统将使用该值加载配置文件,递增 RefCount。并且 UnloadUserProfile() 递减 RefCount 并仅在通过调用 RegUnLoadKey() 变为 0 时卸载配置文件。因此必须同步所有配置文件 load/unload 操作。

只有一种加载配置文件的正确方法 - 调用 LoadUserProfile()。 (在内部,它在 svchost.exe -k netsvcs 中执行对 profsvc.LoadUserProfileServer 的 RPC 调用,所有同步都已完成)。

那么如何获得 LoadUserProfile() 的用户令牌呢?我想打电话给 LogonUser(),你说你不想这样做(除非你有用户的密码,否则不能)。

但是,确实存在另一种可行的方法(我对此进行了测试),但它未记录LoadUserProfile 仅使用令牌中的用户 Sid(查询 TOKEN_USER information with TokenUser 信息 class),然后使用

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

关键

可以通过使用任何给定的 SID 调用 ZwCreateToken() 来创建令牌,但对于此调用,您需要 SE_CREATE_TOKEN_PRIVILEGE。此权限仅存在于 lsass.exe 进程中。所以一个可能的解决方案是:

  1. 打开 lsass.exe 并获取其令牌,或模拟其线程。
  2. 在令牌中启用 SE_CREATE_TOKEN_PRIVILEGE,模拟后
  3. 枚举 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList,并为每个子项查询其 Sid 值,或者(如果 Sid 不存在)使用 ConvertStringSidToSid() 将子项名称转换为 SID
  4. 使用该 SID 创建令牌
  5. 最后调用LoadUserProfile()

---------------- 按要求编辑代码示例-------------------- ------

代码使用 ntdll 导出(这里有人非常不喜欢)但原样

  1. 我们需要 SE_CREATE_TOKEN_PRIVILEGE 来自己创建令牌 未来

枚举系统中的进程,为每个进程打开令牌,查看令牌中是否存在SE_CREATE_TOKEN_PRIVILEGE,如果是 - 复制此令牌,如果需要则在其中启用SE_CREATE_TOKEN_PRIVILEGE。最后用重复的令牌模拟

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;
}

如果我们有 SE_CREATE_TOKEN_PRIVILEGE - 我们可以创建令牌!

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;
}

最后枚举和 load/unload 用户配置文件

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);
    }
}