gpt4 book ai didi

c++ - 从 SID 创建用户 token ,在用户上下文中扩展环境变量

转载 作者:可可西里 更新时间:2023-11-01 13:26:29 25 4
gpt4 key购买 nike

我有一个正在运行的服务,并且想访问像启动这样的普通用户文件夹。为此我想为系统上的每个用户(包括注销)扩展环境变量,如 %APPDATA%。我可以获得已登录用户的 session ID 并从中创建一个 token ,然后调用 ExpandEnvironmentStringsForUser()。但是注销的用户呢?他们不会有 session 。我唯一能为他们得到的是帐户名(使用 NetUserEnum()NetQueryDisplayInformation()) 和来自注册表的 SID (HKLM\software\Microst\Windows NT\current Version\Profile List)我能否从 SID 获取用户 token 或使用 SID 模拟用户,或者是否有某种方法可以使用 SID 扩展环境变量。

编辑:我需要从所有用户的启动位置删除一些文件。为此,我需要在每个用户的上下文中展开 %APPDATA%%USERPROFILE%,无论是登录还是不是。

编辑 2:问题归结为为不同的用户扩展环境变量,如 %APPDATA% 而没有该用户的 token 。

最佳答案

从任何给定的 SID 创建 token 是可能的,但不是简单的。存在用于创建 token 的未记录的系统 api:

extern "C" NTSYSCALLAPI NTSTATUS NTAPI NtCreateToken(
_Out_ PHANDLE TokenHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_ TOKEN_TYPE TokenType,
_In_ PLUID AuthenticationId,
_In_ PLARGE_INTEGER ExpirationTime,
_In_ PTOKEN_USER User,
_In_ PTOKEN_GROUPS Groups,
_In_ PTOKEN_PRIVILEGES Privileges,
_In_opt_ PTOKEN_OWNER Owner,
_In_ PTOKEN_PRIMARY_GROUP PrimaryGroup,
_In_opt_ PTOKEN_DEFAULT_DACL DefaultDacl,
_In_ PTOKEN_SOURCE TokenSource
);

这里 AuthenticationId 必须是一些有效的登录 session ID,否则我们会收到 STATUS_NO_SUCH_LOGON_SESSION 错误。例如,我们可以从当前流程 token 中获取此值。所有其他参数,通常可以是任何有效的感知数据。所以可以用下一种方式创建 token :

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

if (0 <= status)
{
TOKEN_STATISTICS ts;

status = NtQueryInformationToken(hToken, TokenStatistics, &ts, sizeof(ts), &ts.DynamicCharged);

NtClose(hToken);

if (0 <= status)
{
TOKEN_PRIMARY_GROUP tpg = { Sid };
TOKEN_USER User = { { Sid } };

static TOKEN_SOURCE Source = { { "User32 "} };

static TOKEN_DEFAULT_DACL tdd;
static _SID EveryOne = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, { SECURITY_WORLD_RID } };
static TOKEN_GROUPS Groups = { 1, { { &EveryOne, SE_GROUP_ENABLED|SE_GROUP_MANDATORY } } };

struct TOKEN_PRIVILEGES_3 {
ULONG PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[3];
} Privileges = {
3, {
{ { SE_BACKUP_PRIVILEGE }, SE_PRIVILEGE_ENABLED|SE_PRIVILEGE_ENABLED_BY_DEFAULT },
{ { SE_RESTORE_PRIVILEGE }, SE_PRIVILEGE_ENABLED|SE_PRIVILEGE_ENABLED_BY_DEFAULT },
{ { SE_CHANGE_NOTIFY_PRIVILEGE }, SE_PRIVILEGE_ENABLED|SE_PRIVILEGE_ENABLED_BY_DEFAULT }
}
};

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

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

status = NtCreateToken(phToken, TOKEN_ALL_ACCESS, &oa, TokenImpersonation,
&ts.AuthenticationId, &ts.ExpirationTime, &User, &Groups, (PTOKEN_PRIVILEGES)&Privileges, 0,
&tpg, &tdd, &Source);
}
}

return status;
}

此 token 将被赋予 SID 作为 token 用户 sid,3 权限(SE_BACKUP_PRIVILEGESE_RESTORE_PRIVILEGE - 这需要调用 LoadUserProfile api 和 SE_CHANGE_NOTIFY_PRIVILEGE 具有遍历权限)和一组 - 所有人 (s-1-1-0)

但是对于调用 NtCreateToken 我们必须有 SE_CREATE_TOKEN_PRIVILEGE 权限否则我们会得到错误 STATUS_PRIVILEGE_NOT_HELD。大多数系统进程都没有。只有少数(如 lsass.exe)。说 services.exe 和所有服务 - 没有这个特权。所以一开始我们必须得到它。这可以通过枚举进程来完成,看看 - 它具有此特权,从该进程获取 token ,并用它模拟:

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

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

union {
PVOID buf;
PTOKEN_PRIVILEGES ptp;
};

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

if (0 <= (status = NtQueryInformationToken(hToken, TokenPrivileges, buf, cb, &rcb)))
{
if (ULONG PrivilegeCount = ptp->PrivilegeCount)
{
ULONG n = 1;
BOOL bNeedAdjust = FALSE;

PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
do
{
if (!Privileges->Luid.HighPart)
{
switch (Privileges->Luid.LowPart)
{
case SE_CREATE_TOKEN_PRIVILEGE:
if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
{
Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
bNeedAdjust = TRUE;
}

if (!--n)
{
static SECURITY_QUALITY_OF_SERVICE sqos = {
sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE
};

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

if (0 <= (status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE, &soa, FALSE, TokenImpersonation, &hToken)))
{
if (bNeedAdjust)
{
status = NtAdjustPrivilegesToken(hToken, FALSE, ptp, 0, 0, 0);
}

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

NtClose(hToken);
}

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

return STATUS_PRIVILEGE_NOT_HELD;
}

} while (status == STATUS_BUFFER_TOO_SMALL);

return status;
}

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

ULONG cb = 0x10000;

do
{
status = STATUS_INSUFF_SERVER_RESOURCES;

if (PVOID buf = LocalAlloc(0, cb))
{
if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
{
status = STATUS_UNSUCCESSFUL;

ULONG NextEntryOffset = 0;

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

pv = buf;

do
{
pb += NextEntryOffset;

HANDLE hProcess, hToken;

if (pspi->UniqueProcessId && pspi->NumberOfThreads)
{
NTSTATUS s = NtOpenProcess(&hProcess,
g_xp ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION,
&zoa, &pspi->TH->ClientId);

if (0 <= s)
{
s = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE|TOKEN_QUERY, &hToken);

NtClose(hProcess);

if (0 <= s)
{
s = ImpersonateIfConformToken(hToken);

NtClose(hToken);

if (0 <= s)
{
status = STATUS_SUCCESS;

break;
}
}
}
}

} while (NextEntryOffset = pspi->NextEntryOffset);
}
LocalFree(buf);
}

} while (status == STATUS_INFO_LENGTH_MISMATCH);

return status;
}

在我们获得 SE_CREATE_TOKEN_PRIVILEGE 权限后,我们可以通过这种方式获得一些已知的文件夹路径:

HRESULT GetGetKnownFolderPathBySid(REFKNOWNFOLDERID rfid, PSID Sid, PWSTR *ppszPath)
{
PROFILEINFO pi = { sizeof(pi), PI_NOUI };
pi.lpUserName = L"*";

HANDLE hToken;

NTSTATUS status = CreateUserToken(&hToken, Sid);

if (0 <= status)
{
if (LoadUserProfile(hToken, &pi))
{
status = SHGetKnownFolderPath(rfid, 0, hToken, ppszPath);

UnloadUserProfile(hToken, pi.hProfile);
}
else
{
status = HRESULT_FROM_WIN32(GetLastError());
}

CloseHandle(hToken);
}
else
{
status = HRESULT_FROM_NT(status);
}

return status;
}

例如获取 %AppData%

void PrintAppDataBySid(PSID Sid)
{
PWSTR path, szSid;

if (S_OK == GetGetKnownFolderPathBySid(FOLDERID_RoamingAppData, Sid, &path))
{
if (ConvertSidToStringSidW(Sid, &szSid))
{
DbgPrint("%S %S\n", szSid, path);
LocalFree(szSid);
}
CoTaskMemFree(path);
}
}

最后我们可以枚举本地用户配置文件并为每个找到的 sid 获取它的 appdata 路径:

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

UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };

if (0 <= ZwOpenKey(&oa.RootDirectory, KEY_READ, &soa))
{
PVOID stack = alloca(sizeof(WCHAR));

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

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

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

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

PSID _Sid, Sid = 0;

BOOL fOk = ConvertStringSidToSidW(pkbi->Name, &_Sid);

if (fOk)
{
Sid = _Sid;
}

ObjectName.Buffer = pkbi->Name;
ObjectName.Length = (USHORT)pkbi->NameLength;
HANDLE hKey;

if (0 <= ZwOpenKey(&hKey, KEY_READ, &oa))
{
rcb = 64;

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

STATIC_UNICODE_STRING(usSid, "Sid");

if (0 <= (s = ZwQueryValueKey(hKey, &usSid, KeyValuePartialInformation, buf, cb, &rcb)))
{
if (pkvpi->DataLength >= sizeof(_SID) &&
IsValidSid(pkvpi->Data) &&
GetLengthSid(pkvpi->Data) == pkvpi->DataLength)
{
Sid = pkvpi->Data;
}
}

} while (s == STATUS_BUFFER_OVERFLOW);

NtClose(hKey);
}

if (Sid)
{
PrintAppDataBySid(Sid);
}

if (fOk)
{
LocalFree(_Sid);
}
}

} while (status == STATUS_BUFFER_OVERFLOW);

Index++;

} while (0 <= status);

NtClose(oa.RootDirectory);
}
}

例如我得到下一个结果:

S-1-5-18 C:\Windows\system32\config\systemprofile\AppData\Roaming
S-1-5-19 C:\Windows\ServiceProfiles\LocalService\AppData\Roaming
S-1-5-20 C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming
S-1-5-21-*-1000 C:\Users\defaultuser0\AppData\Roaming
S-1-5-21-*-1001 C:\Users\<user>\AppData\Roaming

关于c++ - 从 SID 创建用户 token ,在用户上下文中扩展环境变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47412590/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com