Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Токен же хранит внутри себя множество различных SID, среди которых можно выделить основные:
Достаточно сложно, правда ведь? Но можно провести простую аналогию. Токен — карточка сотрудника компании. SID пользователя — имя на этой карточке, SID группы — напечатанная должность. Система смотрит на эту карточку каждый раз, когда мы начинаем с ней взаимодействовать.
В Windows есть процессы, а есть потоки. Говоря простыми словами, это некие объекты, обладающие собственным виртуальным адресным пространством. Потоком называют ход выполнения программы. Поток выполняется в рамках владеющего им процесса, или, как говорят, в контексте процесса. Любое запущенное приложение представляет собой процесс, в контексте которого выполняется по крайней мере один поток.
У процесса есть токен. Чаще всего используется токен пользователя, запустившего процесс. Когда процесс порождает другие процессы, все они используют этот же токен.
Если нам требуется выполнить одну задачу с токеном одного пользователя, а другую с токеном другого пользователя, запускать новый процесс как‑то не очень удобно. Поэтому токен можно применить и к определенному потоку процесса.
Существует несколько функций для получения токена. Для работы с процессами и потоками можно использовать следующие варианты.
Вариант 1: получить токен определенного процесса.
BOOL OpenProcessToken(
[in] HANDLE ProcessHandle,
[in] DWORD DesiredAccess,
[out] PHANDLE TokenHandle
);
Вариант 2: получить токен определенного потока.
BOOL OpenThreadToken(
[in] HANDLE ThreadHandle,
[in] DWORD DesiredAccess,
[in] BOOL OpenAsSelf,
[out] PHANDLE TokenHandle
);
Переписывать MSDN и объяснять каждый параметр как‑то неправильно. Если вдруг ты незнаком с WinAPI, то можешь написать мне, скину материал для изучения. Предлагаю обратить внимание лишь на второй параметр — DesiredAccess
.
Здесь ты должен указать, какой тип доступа к токену хочешь получить. Это значение преобразуется в маску доступа, на основе которой Windows определяет, можно выдавать токен или нельзя. WinAPI предоставляет для такой маски некоторые стандартные значения.
Обрати внимание, что просто так засунуть TOKEN_ALL_ACCESS
нельзя: система банально не выдаст токен, так как в эту маску входит и TOKEN_ADJUST_SESSIONID
, который требует наличие привилегии SeTcbPrivilege
. Такой привилегией обладает лишь система.
При этом данную ошибку допускают очень часто. Например, лишь в версии 4.7 инструмента Cobalt Strike был исправлен этот недочет.
Чаще всего для наших задач мы будем указывать привилегию TOKEN_DUPLICATE
, чтобы использовать функцию DuplicateTokenEx(
, которую мы разберем позже.
Вариант 3: запросить токен пользователя, если мы знаем его логин и пароль.
BOOL LogonUserA(
[in]LPCSTR lpszUsername,
[in, optional] LPCSTR lpszDomain,
[in, optional] LPCSTR lpszPassword,
[in]DWORD dwLogonType,
[in]DWORD dwLogonProvider,
[out]PHANDLE phToken
);
Токен также содержит информацию о привилегиях пользователя. У самих привилегий в Windows есть два представления:
Act as part of the operating system
;SE_TCB_NAME
.Для проверки можно использовать следующую функцию:
BOOL PrivilegeCheck(
[in]HANDLEClientToken,
[in, out] PPRIVILEGE_SET RequiredPrivileges,
[out] LPBOOLpfResult
);
Сам код может быть примерно следующий (принимает токен, в котором надо проверить наличие привилегии, и ее имя. Допустим, SE_DEBUG_NAME
):
bool IsPrivilegeEnabled(HANDLE hToken, PCWSTR name) {
PRIVILEGE_SET set{};
set.PrivilegeCount = 1;
if (!::LookupPrivilegeValue(nullptr, name, &set.Privilege[0].Luid)) return false;
BOOL result;
return ::PrivilegeCheck(hToken, &set, &result) && result;
}
Допустимые изменения делятся на две группы:
Для большинства ситуаций можно воспользоваться этой функцией:
BOOL SetTokenInformation(
[in] HANDLETokenHandle,
[in] TOKEN_INFORMATION_CLASS TokenInformationClass,
[in] LPVOIDTokenInformation,
[in] DWORDTokenInformationLength
);
Конечно же, в токене возможно изменить далеко не все параметры. Ниже описаны допустимые классы информации для SetTokenInformation(
, а также привилегии и маски доступа, которые для этого требуются.
Например, чтобы включить виртуализацию UAC, используй следующий код:
// hProcess имеет PROCESS_QUERY_INFORMATION
HANDLE hToken;
OpenProcessToken(hProcess, TOKEN_ADJUST_DEFAULT, &hToken);
ULONG enable = 1;
SetTokenInformation(hToken, TokenVirtualizationEnabled,&enable, sizeof(enable));
При этом мы можем изменить и привилегии, содержащиеся в токене! Но требуется знать, как получить из программного имени привилегии ее LUID. Это позволяет сделать следующая функция:
BOOL LookupPrivilegeValueA(
[in, optional] LPCSTR lpSystemName,
[in]LPCSTR lpName,
[out]PLUID lpLuid
);
Следующим шагом мы должны вызвать AdjustTokenPrivilege(
:
BOOL AdjustTokenPrivileges(
[in]HANDLETokenHandle,
[in]BOOLDisableAllPrivileges,
[in, optional] PTOKEN_PRIVILEGES NewState,
[in]DWORDBufferLength,
[out, optional] PTOKEN_PRIVILEGES PreviousState,
[out, optional] PDWORDReturnLength
);
Эта функция может как включить привилегии, так и отключить их. Не знаю, почему Microsoft не реализовала что‑нибудь подобное:
BOOL EnableTokenPrivilege(HANDLE hToken, LPTSTR szPriv, BOOL bEnabled) {
TOKEN_PRIVILEGES tp;
LUID luid;
BOOL bRet = FALSE;
__try {
// Ищем уникальный для системы LUID привилегии
if (!LookupPrivilegeValue(NULL, szPriv /*SE_DEBUG_NAME*/, &luid)) {
// Если имя фиктивное
__leave;
}
// Создаем массив привилегий нашего маркера (в данном случае массив из одного элемента)
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = bEnabled ? SE_PRIVILEGE_ENABLED : 0;
// Изменяем состояние привилегий маркера, включая или отключая привилегии из нашего массива
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
__leave;
}
bRet = TRUE;
}
__finally {};
return(bRet);
}
Этой функции требуется передать токен, программное имя привилегии и булево значение, TRUE
или FALSE
, то есть включить привилегию или выключить ее. При этом токен должен иметь маску TOKEN_ADJUST_PRIVILEGES
.
|
|