, если вы хотите, чтобы вас ограничивали только одним пользователем, наиболее логичное решение - создать какой-либо именованный объект в общем глобальном пространстве имен (видимом для всех) и разрешить только конкретному пользователю Sid доступ к этому объекту.,в результате все экземпляры, запускаемые одним и тем же пользователем, могут открыть этот объект, когда другие пользователи получат другой Sid и могут не открыть его, если он уже существует.нам не обязательно нужен мьютекс.Лучшее использование именованного события - это самый простой и маленький объект.также необходимо использовать не CreateEvent
API, а CreateEventExW
, поскольку это позволяет указать dwDesiredAccess - это важно в случае, если какой-либо код будет выполняться как низкий / ненадежныйцелостность.он не может открыть существующий объект со всеми правами доступа (если мы не добавляем недоверенную метку к объекту), но он может открыть объект с универсальным доступом для чтения / общего исполнения.мы можем сказать, что запрос только SYNCHRONIZE
доступ к событию.код документа:
ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
volatile UCHAR guz = 0;
ULONG CanRun(BOOL& bCan)
{
bCan = false;
HANDLE hToken;
ULONG dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken));
if (dwError == NOERROR)
{
ULONG cb = 0, rcb = sizeof(TOKEN_USER) + GetSidLengthRequired(5);
PVOID stack = alloca(guz);
union {
PVOID buf;
PTOKEN_USER User;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
dwError = BOOL_TO_ERROR(GetTokenInformation(hToken, TokenUser, buf, cb, &rcb));
} while (dwError == ERROR_INSUFFICIENT_BUFFER);
CloseHandle(hToken);
if (dwError == NOERROR)
{
PSID UserSid = User->User.Sid;
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
PACL Dacl = (PACL)alloca(cb = sizeof(ACL) + FIELD_OFFSET(ACCESS_ALLOWED_ACE, SidStart) + GetLengthSid(UserSid));
if (
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) &&
InitializeAcl(Dacl, cb, ACL_REVISION) &&
AddAccessAllowedAce(Dacl, ACL_REVISION, GENERIC_ALL, UserSid) &&
SetSecurityDescriptorDacl(&sd, TRUE, Dacl, FALSE) &&
CreateEventExW(&sa, L"Global\\<Your Unique Name>", CREATE_EVENT_MANUAL_RESET, SYNCHRONIZE)
)
{
bCan = true;
}
else
{
switch (dwError = GetLastError())
{
case ERROR_ACCESS_DENIED:
dwError = NOERROR;
break;
}
}
}
}
return dwError;
}
, поэтому мы запрашиваем TOKEN_USER
из токена текущего процесса и создаем (или открываем) именованное событие "Global\\<Your Unique Name>"
.если даже еще не существует - мы создаем его и присваиваем SD , которые позволяют только тому же пользователю sid открыть его.любые другие пользователи (даже локальная система) не могут открыть это событие (без его изменения вначале) с помощью ERROR_ACCESS_DENIED
.
демонстрационное использование:
BOOL bCan;
ULONG dwError = CanRun(bCan);
if (dwError == NOERROR)
{
MessageBoxW(0, 0, bCan ? L"Yes" : L"No", bCan ? MB_ICONINFORMATION : MB_ICONWARNING);
}
else
{
PWSTR sz;
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, 0, (PWSTR)&sz, 0, 0))
{
MessageBoxW(0, sz, 0, MB_ICONHAND);
LocalFree(sz);
}
}