Информация о локальных профилях пользователей хранятся в этом ключе реестра:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
Можно перечислить его подразделов, где каждый подключ имеет ProfileImagePath
, что указывает на папку, где находится ntuser.dat
.
Но, непосредственно загружая профиль пользователя RegLoadKey()
, очень плохо. Во-первых, профиль уже может быть загружен. Во-вторых, возможно, что после загрузки профиля самостоятельно система может также попробовать загрузить профиль. Обратите внимание на значение RefCount
. Система использует это значение для загрузки профиля, если он еще не загружен, приращение RefCount
. И UnloadUserProfile()
уменьшается RefCount
и выгружает профиль, только когда он становится 0, вызывая RegUnLoadKey()
. Поэтому все операции загрузки/выгрузки профиля должны быть синхронизированы.
Существует только один правильный способ загрузки профиля - звонок LoadUserProfile()
. (внутренне он выполняет вызов RPC до profsvc.LoadUserProfileServer
в svchost.exe -k netsvcs
, где выполняется вся синхронизация).
Итак, как вы получаете токен пользователя для LoadUserProfile()
? Я думаю, звоните LogonUser()
, о котором вы сказали, что не хотите делать (и не можете, если у вас нет пароля пользователя).
Но существует другой способ, который работает (я тестировал это), но это недокументированный. LoadUserProfile
используется только пользователь Sid
из маркеров (запроса для TOKEN_USER
информации с TokenUser
классом iformation), а затем работать с
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<Sid>
ключ
Можно создать маркер, вызвав ZwCreateToken()
с любой заданной SID, но для этого звонка вам нужно SE_CREATE_TOKEN_PRIVILEGE
. Эта привилегия существует только в процессе lsass.exe
. Таким образом, возможным решением является:
- открыть
lsass.exe
и получить свой токен или выдавать себя за нить.
- позволяет
SE_CREATE_TOKEN_PRIVILEGE
в маркере, после того, как олицетворения
- перечислить
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
, и для каждого запроса подключ его значение Sid
, или (если Sid
не существует) преобразовать имя подраздела в SID с помощью ConvertStringSidToSid()
- создать токен что SID
- и, наконец, называют
LoadUserProfile()
-------------- EDIT пример кода по запросу --------------- -------------
код, используемый Ntdll экспорт (что кто-то здесь очень не нравится), но как
- нам нужно есть
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;
}
и, наконец, перечислить и пользовательские загрузки/выгрузки профилей
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);
}
}
'NetUserEnum()' может возвращать имена пользователей. Не совпадают ли эти имена пользователей с подпапками в вашей папке «Пользователи»? В этом случае вы можете просто перечислить подпапки 'Users' и не использовать' NetUserEnum() 'вообще. Или даже перечислите разделы «HKEY_USERS» в реестре напрямую. –
Если профиль не загружен, он не отображается в HKEY_USERS, поэтому требуется RegLoadKey или LoadUserProfile. Я не хочу использовать пароль, поэтому LogonUser не работает. – pcunite