..продолжим первую часть статьи, где были представлены общие сведения о системных объектах Win. Чтобы не потерять логическую нить, предыдущая рекомендуется к прочтению, поскольку здесь ставка делается на то, что вы уже знакомы с понятиями: идентификатор пользователя SID, дескриптор безопасности объекта и его список DACL. Организовать качественную подсистему безопасности можно лишь в том случае, когда есть чёткая модель того, что нужно защищать и кому/что разрешается делать. В этой области инженерами Microsoft проделан огромный объём работы, так-что сконцентрируем своё внимание исключительно на основных моментах. За роялем сегодня токен юзера, его права и привилегии.
Содержание:
1. Аутентификация пользователей в системе;
2. Маркер доступа к объектам (токен);
3. Права и привилегии пользователей;
4. Практика – информация о токенах и дескрипторах(SD) процесса;
5. Подводим итоги.
------------------------------------------------
1. Регистрация пользователя в системе
Ядром подсистемы безопасности является служба Lsass.exe, или "Local Security Authority Sub-System". В автозагрузке Win лежит процесс входа пользователей Winlogon.exe, который как швейцар проводит фейс-контроль вновь прибывших и открывает им двери в систему. Winlogon является критическим важным процессом (если убить, то рухнет вся ОС), поэтому провайдер аутентификации Authui.dll и интерфейсное окно входа в систему запускаются внутри дочернего процесса Winlogon, под названием LogonUI.exe (см.папку system32). После того-как пользователь вводит свои учётные данные, процесс LogonUI завершается, а Winlogon остаётся активным, чтобы в фоне отлавливать комбинацию клавиш юзера [Ctrl+Alt+Delete] для смены пользователей. При обнаружении таковой, Winlogon опять зовёт LogonUI и так в бесконечном цикле.
На следующем этапе, Winlogon передаёт имя и пароль юзера в службу безопасности LSA, которая обращается к Authui.dll для аутентификации пользователя. Если всё в порядке и юзер с таким именем существует, провайдер вычисляет NTLM-хеш введённого пасворда и сравнивает его с дайджестом (хешем) из базы данных "диспетчера учётных записей" SAM – Security Account Manager. Если проверка подтвердит пользователя и не найдёт оснований, чтобы выпроводить его за порог, провайдер формирует и возвращает в Lsass уже знакомый нам идентификатор безопасности пользователя SID (Security Identifier). В противном случае, на территории охранной зоны включается ревун, и на незадачливого юзверя направляются сразу несколько ярких прожекторов.
Теперь получив SID, подсистема Lsass вызывает функцию NtCreateToken() в мониторе безопасности SRM, чтобы создать т.н. маркер-доступа клиента "AccessToken". На системах х86-32, структура токена имеет размер 480-байт и в неё заложен профиль безопасности пользователя. Помимо прочего, он содержит в себе SID'ы юзера и группы в которую он входит, а так-же его привилегии. Остальные поля в токене носят информационный характер и в данном случае не представляют для нас интереса. Примерно так выглядит взаимодействие субъектов в этом сообществе:
Обратите внимание, что создавать токены доступа к объектам может не только служба безопасности Lsass, но и мы и с вами, как рядовые пользователи. Благодаря именно этой возможности консольной утилите "runas.exe" удаётся запускать процессы выдавая себя за админа системы. Она использует функцию из библиотеки advapi32.dll LogonUser(), которая пытается зарегать нового юзверя на локальном узле. При удачных обстоятельствах, функция создаёт токен с настройками по умолчанию, и теперь он будет представлять новорождённого юзера во-всех его начинаниях, то как старт процессов и прочее. Если функция нарвётся на шипы подсистемы безопасности и по какой-либо причине не сможет выполнить свою задачу, то в EAX вернёт логический нуль. Вот её прототип:
Далее, установив в этом токене нужные привилегии, можно будет вызывать процессы от чужого имени, посредством функции CreateProcessAsUser(). В качестве альтернативы, имеется ещё одна обёртка, сочетающая в себе сразу две функции выше под названием CreateProcessWithLogonW(). Одним выстрелом дробью она и создаёт нового юзера, и сразу запускает указанный в аргументе процесс. В общем выбор у нас широкий, правда в клинических случаях нам как "художникам" самим потребуются некоторые привилегии, но это уже дело техники и всегда можно найти забытый инженерами люк (например использовать не первичный токен, а токен олицетворения, или ограниченный токен).
2. Маркер доступа к объектам "AccessToken"
Токен сам является системным объектом, который хранит SID и привилегии пользователя. Он создаётся локальным органом безопасности LSA после того-как идентификация юзера успешно пройдёт цензуру. В последующем, каждый процесс, который так или иначе запускается от имени данного пользователя, сопровождается копией его токена. Токен используется системой исключительно для предоставления процессу доступа к защищённым объектам, и в зависимости от указанных в нём привилегий позволяет выполнять различные системные операции. Уже упоминалось, что защищённые объекты ожидают пользовательский SID в своих списках DACL, который как-раз и берётся из токена пользователя. В отличии от имени и пароля в базе реестра SAM, токен нигде не сохраняется, а при каждой загрузке системы создаётся вновь.
Алгоритм проверки прав доступа к объекту со стороны нашего процесса выглядит так:
Обсуждать коня в вакууме как-то не комильфо, поэтому проведём небольшой эксперимент в отладчике WinDbg, чтобы посмотреть на токен реального приложения. Значит даём команду
Остаётся запросить токен командой
А что, если нужно просмотреть не все вложенные структуры, а в избирательном порядке одну.., к примеру первую "TOKEN --> TOKEN_SOURCE"? Для этого, в конце команды указываем имя интересующего нас поля, и завершаем команду жирной точкой. Можно даже связывать отображение вложенных структур по типу:
2.1. Типы токенов
Обратите внимание на поле по смещению 0x0a4 под названием "TokenType" – это тип токена. Здесь оно хранит константу(1) = TOKEN_PRIMARY. Дело в том, что в природе мирно сосуществуют две разновидности токенов: первичный и наследуемый.
• Первичный токен: Token_Primary = 1
Это основной токен процесса, и создается он только ядром Win. Содержимое представляет информацию о безопасности по умолчанию. Функция UserLogon() и подсистема Lsass всегда создаёт только первичный токен, хэндл которого можно получить вызовом OpenProcessToken(). Ещё одна функция CreateRestrictedToken() тоже создаёт его, но только урезанный в привилегиях вариант, о чём собственно и говорит название "Restricted" (ограниченный). На вход ей нужно подать первичный токен, а на выходе получим только шелуху от него. Такие токены Lsass обычно "дарит" гостям системы.
• Токен олицетворения: Token_Impersonation = 2
Токен данного типа заимствует права первичного токена, чтобы олицетворять клиентский процесс на сервере. К примеру токены олицетворения передаются от процессов к каждому из его потоков, так-что получить дескриптор можно функцией OpenThreadToken(). Однако в полной мере достоинства наследуемых токенов раскрываются в клиент-серверных приложениях, когда серверы предоставляют клиентам такие расшаренные объекты как: файлы, принтеры, базы-данных и прочее.
Получив запрос сервер должен убедиться, что у клиента есть разрешение на доступ к запрашиваемому объекту – для этого ему нужен SID учётной записи клиента, который и передаётся в токене олицетворения механизмом заимствования прав. Меняет тип токена с первичного на токен-олицетворения функция ImpersonateLoggedOnUser(), а обратную операцию проводят RevertToSelf() или DuplicateTokenEx().
3. Привилегии пользователей
Рассмотрим некоторые вопросы лингвистики..
Знаете-ли вы, какая разница между привилегией и правом пользователя? На сайте MSDN привилегией называют расширенные права юзеров, что собственно ничуть не проясняет ситуацию. Однако если уйти в доки с головой, то найти тонкую грань между двумя этими терминами всё-таки можно.
• Права "Rights" (так-же известные как разрешения) всегда связаны с определенным объектом системы, который выдаёт нам право на открытие файла, запись, чтение из него и прочее. Монитор безопасности SRM разграничивает права пользователей на доступ к объекту, просматривая его список DACL. Обычно DACL'ы и их записи АСЕ сохраняются вместе с объектом. Например файловая запись АСЕ лежит прямо рядом с ним на диске – атрибут безопасности файла в NTFS равен 50h (см.спеки Ntfs):
• Привилегии-же связаны с определенными действиями в системе, и предоставляются пользователям, а не объектам. Они позволяют переопределять права на доступ к объекту, поэтому с их назначением мы (как админы) должны быть очень осторожны. Например юзер с привилегией
Добавляет привилегий в токен функция AdjustTokenPrivileges(), но перед этим не помешало-бы узнать полный их список в системе. Проблема в том, что за время своего существования Win не однократно редактировала этот лист и может получиться так, что поддержка требуемой нам привилегии просто прекратилась и её место заняла какая-нибудь новая. Например начиная с Win-7, широко известной привилегии SeTcbPrivilege с идентификатором(7) уже не существует, и можно долго гадать, почему-же она не выставляется.
Функции для работы с подсистемой безопасности Lsass сосредоточены в библиотеке пользовательского уровня Advapi32.dll (advanced, расширенные API). Среди них имеются две интересные функции Set/GetTokenInformation(), которые как и следует из названий оперируют информацией в токене. Вот их прототип:
Значит в первый аргумент передаём дескриптор токена, который возвращает OpenProcessToken(). Во-втором аргументе нужно указать класс/тип запрашиваемой информации – поскольку мы планируем получить список системных привилегий, то ставим константу(3) "TokenPrivileges". Для этих функции всего предусмотрено 48 разнообразных классов, и соответственно такое-же количество информации о токене они способны вернуть. В следующих аргументах указываем адрес и размер буфера, а так-же адрес переменной, куда функция вернёт требуемый размер буфа для конкретного типа инфы.
Если функция отработает успешно, вернёт в EAX логическую единицу и в буфере получим данные, которые описывает структура "TOKEN_PRIVILEGES". Первое поле в ней "PrivilegeCount" – это общее число поддерживаемых привилегий, а дальше следует массив вложенных структур "LUID_AND_ATTRIBUTES" с описанием каждой привилегии в отдельности. Таким образом, первое поле хранит кол-во вложенных структур, что совпадает с кол-вом зареганных в системе привилегий. Чтобы узнать, какие из общего списка назначены нам, нужно будет проверить поле с атрибутом во-вложенных структурах.
Так мы получим в буфере идентификаторы LUID всех привилегий (Local Identifier), и чтобы привести их в дружелюбный текстовый вид, нужно будет позвать функцию LookupPrivilegeName(). Она ожидает на входе идентификатор, и возвращает соответствующую ему строку. Родственная ей функция LookupPrivilegeValue() проводит обратную операцию, т.е. принимая имя, возвращает LUID. Завернём всё вышесказанное в код:
Если верить функции GetTokenInformation() (а не верить ей у нас нет причин), то семёрка поддерживает всего 24 привилегии, причём по-умолчанию у меня (как входящего в группу админов) включены только три из них. "ChangeNotify" имеют все пользователи, "Impersonate" позволяет олицетворять себя с другими лицами, а привилегию "CreateGlobal" система назначает только админам, что даём им право создавать глобальные объекты в пространстве имён. Список всех привилегий и заложенный в них глубокий смысл можно найти в репозитории MSDN
Обратите внимание на идентификаторы – первые четыре привилегии(0,1,2,3) под семёркой уже отправлены в утиль, причём и дальше порядок не последовательный. Более того, содержимое листа привязано и к версии операционной системы. Так, запустив этот код на своём буке с Win-10 я обнаружил, что в ней пропала привилегия с идентификатором(4), зато появилась новая с LUID=36, под громким названием "SeDelegateSessionUserImpersonatePrivilege". На сайте RSDN есть статья
3.1. Включение привилегий в токене
Ладно, лист поддержки получили.. теперь попробуем повысить себя в ранге и добавить парочку привилегий в токен функцией AdjustTokenPrivileges(). Она может как включать, так и отключать ненужные привилегии. Данная операция требует, чтобы токен был открыт с доступом
Если во-второй аргумент запихать логическую единицу, функция превратит токен в ограниченный "Resticted", удалив из него все привилегии кроме "SeChangeNotify". Особого внимания заслуживает здесь аргумент "NewState", который является указателем на структуру "TOKEN_PRIVILEGES". Как упоминалось выше, в первом поле этой структуры лежит счётчик вложенных структур "LUID_AND_ATTRIBUTES", за которым следует и сам массив. Значит чтобы добавить в токен, например, четыре какие-нибудь дополнительные привилегии, можно воспользоваться такой конструкцией в секции-данных программы:
Теперь у нас есть структура, в которую необходимо занести LUID требуемых привилегий – их возвращает функция LookupPrivilegeValue(), которой мы передаём имя привилегии, а она возвращает LUID. Подправим немного предыдущий исходник и посмотрим, что из этого выйдет:
Привилегии, которые мы назначаем сами должны иметь атрибут(2), а которые идут по-умолчанию(1). На случай, если нам нужно наоборот отключить какую-нибудь привилегию оставив все остальные, предусмотрен атрибут(4) "SE_PRIVILEGE_REMOVED". Так они представлены в сишном хидере Winnt.h:
Теперь мой процесс может без проблем копировать системные файлы привилегией "SeBackup" (даже если этого запрещает DACL файла, поскольку привилегии имеют приоритет над правом), производить в них запись посредством "SeRestore", и захватывать чужие процессы, для чего и предназначена "SeDebug". Следуя этикету нужно отметить, после того-как мы выполним задачу, нужно восстановить свои дефолтные привилегии (см.аргумент PreviousState в AdjustTokenPrivileges). В противном случае, даже после закрытия софта установленные привилегии остаются в нашем токене, и будут действительны вплоть до окончания текущего сеанса (т.е. до следующей перезагрузки системы). В некоторых случаях это может создать проблемы, например оставив лазейку для хакеров.
4. Сбор информации о токенах, и списках DACL
В примере ниже я только беру информацию из токена, хотя при родственной Set можно и редактировать его. Вот функции, которые решают следующие задачи:
• GetTokenInformation() – запрашивает 48-типов информации из токена;
• LookupPrivilegeName() – принимая LUID возвращает привилегию в виде строки;
• ConvertSidToStringSid() – конвертирует двоичный SID в строку.
Можно было вывести намного больше данных, но лист не вместился уже в окно консоли, поэтому минимум.:
5. Под занавес..
Информация на уровне токенов позволяет нам приобретать права и привилегии. Здесь играет роль каждый бит, который мы указываем в масках доступа, поэтому знать их назначение наша обязанность. Практика ничто, перед теорией, и можно долго ломиться в открытые двери, даже не подразумевая, что ключ лежит в единственном бите. К сожалению, здесь не было возможности в полной мере освятить эту тему, т.к. говорить о ней можно бесконечно. В результате у меня осталось впечатление, что тема не раскрыта всё-таки до конца, и будем считать ей отправной точкой. В скрепке готовые примеры и инклуд. Всем желаю удачи, и до следующего..
Содержание:
1. Аутентификация пользователей в системе;
2. Маркер доступа к объектам (токен);
3. Права и привилегии пользователей;
4. Практика – информация о токенах и дескрипторах(SD) процесса;
5. Подводим итоги.
------------------------------------------------
1. Регистрация пользователя в системе
Ядром подсистемы безопасности является служба Lsass.exe, или "Local Security Authority Sub-System". В автозагрузке Win лежит процесс входа пользователей Winlogon.exe, который как швейцар проводит фейс-контроль вновь прибывших и открывает им двери в систему. Winlogon является критическим важным процессом (если убить, то рухнет вся ОС), поэтому провайдер аутентификации Authui.dll и интерфейсное окно входа в систему запускаются внутри дочернего процесса Winlogon, под названием LogonUI.exe (см.папку system32). После того-как пользователь вводит свои учётные данные, процесс LogonUI завершается, а Winlogon остаётся активным, чтобы в фоне отлавливать комбинацию клавиш юзера [Ctrl+Alt+Delete] для смены пользователей. При обнаружении таковой, Winlogon опять зовёт LogonUI и так в бесконечном цикле.
На следующем этапе, Winlogon передаёт имя и пароль юзера в службу безопасности LSA, которая обращается к Authui.dll для аутентификации пользователя. Если всё в порядке и юзер с таким именем существует, провайдер вычисляет NTLM-хеш введённого пасворда и сравнивает его с дайджестом (хешем) из базы данных "диспетчера учётных записей" SAM – Security Account Manager. Если проверка подтвердит пользователя и не найдёт оснований, чтобы выпроводить его за порог, провайдер формирует и возвращает в Lsass уже знакомый нам идентификатор безопасности пользователя SID (Security Identifier). В противном случае, на территории охранной зоны включается ревун, и на незадачливого юзверя направляются сразу несколько ярких прожекторов.
Теперь получив SID, подсистема Lsass вызывает функцию NtCreateToken() в мониторе безопасности SRM, чтобы создать т.н. маркер-доступа клиента "AccessToken". На системах х86-32, структура токена имеет размер 480-байт и в неё заложен профиль безопасности пользователя. Помимо прочего, он содержит в себе SID'ы юзера и группы в которую он входит, а так-же его привилегии. Остальные поля в токене носят информационный характер и в данном случае не представляют для нас интереса. Примерно так выглядит взаимодействие субъектов в этом сообществе:
Обратите внимание, что создавать токены доступа к объектам может не только служба безопасности Lsass, но и мы и с вами, как рядовые пользователи. Благодаря именно этой возможности консольной утилите "runas.exe" удаётся запускать процессы выдавая себя за админа системы. Она использует функцию из библиотеки advapi32.dll LogonUser(), которая пытается зарегать нового юзверя на локальном узле. При удачных обстоятельствах, функция создаёт токен с настройками по умолчанию, и теперь он будет представлять новорождённого юзера во-всех его начинаниях, то как старт процессов и прочее. Если функция нарвётся на шипы подсистемы безопасности и по какой-либо причине не сможет выполнить свою задачу, то в EAX вернёт логический нуль. Вот её прототип:
C-подобный:
BOOL LogonUserA(
LPCSTR lpszUsername, ;// имя юзера = произвольное
LPCSTR lpszDomain, ;// имя домена/сервера = GetComputerName()
LPCSTR lpszPassword, ;// пароль юзера = 0
DWORD dwLogonType, ;// тип входа в систему = LOGON32_LOGON_NEW_CREDENTIALS
DWORD dwLogonProvider, ;// провайдер обеспечения входа = LOGON32_PROVIDER_DEFAULT (Authui.dll)
PHANDLE phToken ) ;// сюда вернётся дескриптор токена нового пользователя!
Далее, установив в этом токене нужные привилегии, можно будет вызывать процессы от чужого имени, посредством функции CreateProcessAsUser(). В качестве альтернативы, имеется ещё одна обёртка, сочетающая в себе сразу две функции выше под названием CreateProcessWithLogonW(). Одним выстрелом дробью она и создаёт нового юзера, и сразу запускает указанный в аргументе процесс. В общем выбор у нас широкий, правда в клинических случаях нам как "художникам" самим потребуются некоторые привилегии, но это уже дело техники и всегда можно найти забытый инженерами люк (например использовать не первичный токен, а токен олицетворения, или ограниченный токен).
2. Маркер доступа к объектам "AccessToken"
Токен сам является системным объектом, который хранит SID и привилегии пользователя. Он создаётся локальным органом безопасности LSA после того-как идентификация юзера успешно пройдёт цензуру. В последующем, каждый процесс, который так или иначе запускается от имени данного пользователя, сопровождается копией его токена. Токен используется системой исключительно для предоставления процессу доступа к защищённым объектам, и в зависимости от указанных в нём привилегий позволяет выполнять различные системные операции. Уже упоминалось, что защищённые объекты ожидают пользовательский SID в своих списках DACL, который как-раз и берётся из токена пользователя. В отличии от имени и пароля в базе реестра SAM, токен нигде не сохраняется, а при каждой загрузке системы создаётся вновь.
Алгоритм проверки прав доступа к объекту со стороны нашего процесса выглядит так:
1. Если SID из токена не совпадает с SID в элементе ACE объекта, то осуществляется переход к следующему ACE в цепочке, иначе переход к п.2.
2. Если элемент ACE запрещает доступ к объекту обладателю данного SID, но он является владельцем объекта (и запрос только на чтение), доступ к объекту разрешается, иначе переход к п.3.
3. Если достигнут конец списка DACL – попытка доступа отклоняется.
4. Если DACL объекта пуст, доступ к нему запрещён всем субъектам, за исключением владельца, которому разрешены чтение и изменение списка DACL (право WRITE_DAC).
5. Если у объекта нет дескриптора безопасности SD (например, у файлов на диске FAT), то все могут получить любые права-доступа к данному объекту.
Обсуждать коня в вакууме как-то не комильфо, поэтому проведём небольшой эксперимент в отладчике WinDbg, чтобы посмотреть на токен реального приложения. Значит даём команду
!process 0 0
для сбора инфы об активных процессах, и находим среди них адрес окружения своего – я получил 0x8a058d20
. Теперь передаю этот адрес той-же команде !process, чтобы она отфильтровала лог и вернула информацию только о моём процессе, забросив в топку все остальные. Объём выводимой инфы можно контролировать параметром в диапазоне 0-7, где нуль минимум, а 7 макс.инфы – нам достаточно единицы. В выхлопе отладчика будет красоваться адрес структуры принадлежащего нам токена = 0xa5e41030
:
C-подобный:
lkd> !process 8a058d20 1
PROCESS 8a058d20 SessionId: 1 Cid: 0ab0 Peb: 7ffdf000 ParentCid : 0abc
DirBase: 5f524a40 ObjectTable: a8c2b388 HandleCount: 33.
Image: EXAMPLE.EXE
Token a5e41030 ;//<------ Адрес структуры _TOKEN
ElapsedTime 00:00:34.834
QuotaPoolUsage[PagedPool] 0
Working Set Sizes (now,min,max) (387, 50, 345) (1548KB, 200KB, 1380KB)
PeakWorkingSetSize 387
VirtualSize 10 Mb
PageFaultCount 383
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 82
Остаётся запросить токен командой
dt
(DisplayType), и в качестве аргумента передать ей полученный на предыдущем этапе линк 0xa5e41030
. Как видим, основная структура "TOKEN" имеет не только переменные, но и множество вложенных структур, где могут быть припрятаны весьма интересные вещи. Для их отображения в WinDbg предусмотрен специальный ключ(-r), который подразумевает рекурсию. Добавьте его в хвост команды ниже, и отладчик вернёт лог полностью в развёрнутом виде, вместе со-значениями всех вложенных структур. Кстати этому ключу можно передавать и уровень рекурсии в числовом виде, например –r1
или –r2
. Наиболее важные поля я выделил здесь стрелками:
C-подобный:
lkd> dt _token a5e41030
nt!_TOKEN ;//<---//------------------ размер 480 байт = 0x1e0
+0x000 TokenSource : _TOKEN_SOURCE
+0x010 TokenId : _LUID
+0x018 AuthenticationId : _LUID
+0x020 ParentTokenId : _LUID
+0x028 ExpirationTime : _LARGE_INTEGER 0x7fffffff`ffffffff
+0x030 TokenLock : 0x826d2860 _ERESOURCE
+0x034 ModifiedId : _LUID
+0x040 Privileges : _SEP_TOKEN_PRIVILEGES ;//<-----------
+0x058 AuditPolicy : _SEP_AUDIT_POLICY
+0x074 SessionId : 1
+0x078 UserAndGroupCount : 0x10
+0x07c RestrictedSidCount : 0
+0x080 VariableLength : 0x1fc
+0x084 DynamicCharged : 0x400
+0x088 DynamicAvailable : 0
+0x08c DefaultOwnerIndex : 5
+0x090 UserAndGroups : 0xa5e41210 _SID_AND_ATTRIBUTES ;//<-----------
+0x094 RestrictedSids : (null)
+0x098 PrimaryGroup : 0xb08ff468 Void ;//<-----------
+0x09c DynamicPart : 0xb08ff468 -> 0x501
+0x0a0 DefaultDacl : 0xb08ff484 _ACL ;//<-----------
+0x0a4 TokenType : 1 ( TokenPrimary ) ;//<-----------
+0x0a8 ImpersonationLevel : 0 ( SecurityAnonymous )
+0x0ac TokenFlags : 0x2000
+0x0b0 TokenInUse : 0x1 ''
+0x0b4 IntegrityLevelIndex : 0xf
+0x0b8 MandatoryPolicy : 3
+0x0bc LogonSession : 0x8df64310 _SEP_LOGON_SESSION_REFERENCES
+0x0c0 OriginLogonSession : _LUID
+0x0c8 SidHash : _SID_AND_ATTRIBUTES_HASH ;//<-----------
+0x150 RestrictedSidHash : _SID_AND_ATTRIBUTES_HASH
+0x1d8 pSecurityAttributes : 0xae185238 _AUTHZBASEP_SECURITY_ATTRIBUTES_INFORMATION
+0x1dc SessionObject : 0x86628c68 Void
+0x1e0 VariablePart : 0xa5e41290
А что, если нужно просмотреть не все вложенные структуры, а в избирательном порядке одну.., к примеру первую "TOKEN --> TOKEN_SOURCE"? Для этого, в конце команды указываем имя интересующего нас поля, и завершаем команду жирной точкой. Можно даже связывать отображение вложенных структур по типу:
dt _token a5e41030 TokenSource.SourceIdentifier.
(отобразит локальный идентификатор LUID):
C-подобный:
lkd> dt _token a5e41030 TokenSource.
nt!_TOKEN
+0x000 TokenSource :
+0x000 SourceName : [8] "User32 "
+0x008 SourceIdentifier : _LUID
2.1. Типы токенов
Обратите внимание на поле по смещению 0x0a4 под названием "TokenType" – это тип токена. Здесь оно хранит константу(1) = TOKEN_PRIMARY. Дело в том, что в природе мирно сосуществуют две разновидности токенов: первичный и наследуемый.
• Первичный токен: Token_Primary = 1
Это основной токен процесса, и создается он только ядром Win. Содержимое представляет информацию о безопасности по умолчанию. Функция UserLogon() и подсистема Lsass всегда создаёт только первичный токен, хэндл которого можно получить вызовом OpenProcessToken(). Ещё одна функция CreateRestrictedToken() тоже создаёт его, но только урезанный в привилегиях вариант, о чём собственно и говорит название "Restricted" (ограниченный). На вход ей нужно подать первичный токен, а на выходе получим только шелуху от него. Такие токены Lsass обычно "дарит" гостям системы.
• Токен олицетворения: Token_Impersonation = 2
Токен данного типа заимствует права первичного токена, чтобы олицетворять клиентский процесс на сервере. К примеру токены олицетворения передаются от процессов к каждому из его потоков, так-что получить дескриптор можно функцией OpenThreadToken(). Однако в полной мере достоинства наследуемых токенов раскрываются в клиент-серверных приложениях, когда серверы предоставляют клиентам такие расшаренные объекты как: файлы, принтеры, базы-данных и прочее.
Получив запрос сервер должен убедиться, что у клиента есть разрешение на доступ к запрашиваемому объекту – для этого ему нужен SID учётной записи клиента, который и передаётся в токене олицетворения механизмом заимствования прав. Меняет тип токена с первичного на токен-олицетворения функция ImpersonateLoggedOnUser(), а обратную операцию проводят RevertToSelf() или DuplicateTokenEx().
3. Привилегии пользователей
Рассмотрим некоторые вопросы лингвистики..
Знаете-ли вы, какая разница между привилегией и правом пользователя? На сайте MSDN привилегией называют расширенные права юзеров, что собственно ничуть не проясняет ситуацию. Однако если уйти в доки с головой, то найти тонкую грань между двумя этими терминами всё-таки можно.
• Права "Rights" (так-же известные как разрешения) всегда связаны с определенным объектом системы, который выдаёт нам право на открытие файла, запись, чтение из него и прочее. Монитор безопасности SRM разграничивает права пользователей на доступ к объекту, просматривая его список DACL. Обычно DACL'ы и их записи АСЕ сохраняются вместе с объектом. Например файловая запись АСЕ лежит прямо рядом с ним на диске – атрибут безопасности файла в NTFS равен 50h (см.спеки Ntfs):
• Привилегии-же связаны с определенными действиями в системе, и предоставляются пользователям, а не объектам. Они позволяют переопределять права на доступ к объекту, поэтому с их назначением мы (как админы) должны быть очень осторожны. Например юзер с привилегией
SE_RESTORE
может без проблем перезаписать практически любой файл в системе, а привилегия SE_BACKUP
наоборот позволяет считать защищённый системный файл.Добавляет привилегий в токен функция AdjustTokenPrivileges(), но перед этим не помешало-бы узнать полный их список в системе. Проблема в том, что за время своего существования Win не однократно редактировала этот лист и может получиться так, что поддержка требуемой нам привилегии просто прекратилась и её место заняла какая-нибудь новая. Например начиная с Win-7, широко известной привилегии SeTcbPrivilege с идентификатором(7) уже не существует, и можно долго гадать, почему-же она не выставляется.
Функции для работы с подсистемой безопасности Lsass сосредоточены в библиотеке пользовательского уровня Advapi32.dll (advanced, расширенные API). Среди них имеются две интересные функции Set/GetTokenInformation(), которые как и следует из названий оперируют информацией в токене. Вот их прототип:
C-подобный:
BOOL GetTokenInformation
TokenHandle dd 0 ;// дескриптор токена
TokenInformationClass dd 0 ;// класс запрашиваемой информации
TokenInformation dd 0 ;// линк на буфер под инфу
TokenInformationLength dd 0 ;// размер буфера
ReturnLength dd 0 ;// переменная, куда вернётся кол-во байт
Значит в первый аргумент передаём дескриптор токена, который возвращает OpenProcessToken(). Во-втором аргументе нужно указать класс/тип запрашиваемой информации – поскольку мы планируем получить список системных привилегий, то ставим константу(3) "TokenPrivileges". Для этих функции всего предусмотрено 48 разнообразных классов, и соответственно такое-же количество информации о токене они способны вернуть. В следующих аргументах указываем адрес и размер буфера, а так-же адрес переменной, куда функция вернёт требуемый размер буфа для конкретного типа инфы.
C-подобный:
;//--- Класс запросов для Set/Get TokenInformation() -----
;// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-token_information_class
TokenUser = 1 ;// SID пользователя (атрибуты не определены)
TokenGroups = 2 ;// массив SID групп (и их атрибуты)
TokenPrivileges = 3 ;// массив LUID привилегий (и их атрибуты)
TokenOwner = 4 ;// владелец
TokenPrimaryGroup = 5 ;// группа пользователя токена
TokenDefaultDacl = 6 ;// DACL, который будет назначен всем объектам, использующим этот токен
TokenSource = 7 ;// текстовый (и числовой) ID создателя токена
TokenType = 8 ;// тип
TokenImpersonationLevel = 9 ;// уровень олицетворения
TokenStatistics = 10 ;// общая информация о токене
TokenRestrictedSids = 11 ;// ограниченный SID
TokenSessionId = 12 ;// SID сессии Logon
TokenGroupsAndPrivileges = 13
TokenSessionReference = 14
TokenSandBoxInert = 15
TokenAuditPolicy = 16
TokenOrigin = 17
TokenElevationType = 18
TokenLinkedToken = 19
TokenElevation = 20
TokenHasRestrictions = 21
TokenAccessInformation = 22
TokenVirtualizationAllowed = 23
TokenVirtualizationEnabled = 24
TokenIntegrityLevel = 25
TokenUIAccess = 26
TokenMandatoryPolicy = 27
TokenLogonSid = 28
TokenIsAppContainer = 29
TokenCapabilities = 30
TokenAppContainerSid = 31
TokenAppContainerNumber = 32
TokenUserClaimAttributes = 33
TokenDeviceClaimAttributes = 34
TokenRestrictedUserClaimAttributes = 35
TokenRestrictedDeviceClaimAttributes = 36
TokenDeviceGroups = 37
TokenRestrictedDeviceGroups = 38
TokenSecurityAttributes = 39
TokenIsRestricted = 40
TokenProcessTrustLevel = 41
TokenPrivateNameSpace = 42
TokenSingletonAttributes = 43
TokenBnoIsolation = 44
TokenChildProcessFlags = 45
TokenIsLessPrivilegedAppContainer = 46
TokenIsSandboxed = 47
MaxTokenInfoClass = 48
Если функция отработает успешно, вернёт в EAX логическую единицу и в буфере получим данные, которые описывает структура "TOKEN_PRIVILEGES". Первое поле в ней "PrivilegeCount" – это общее число поддерживаемых привилегий, а дальше следует массив вложенных структур "LUID_AND_ATTRIBUTES" с описанием каждой привилегии в отдельности. Таким образом, первое поле хранит кол-во вложенных структур, что совпадает с кол-вом зареганных в системе привилегий. Чтобы узнать, какие из общего списка назначены нам, нужно будет проверить поле с атрибутом во-вложенных структурах.
C-подобный:
struct TOKEN_PRIVILEGES
PrivilegeCount dd 0
Privileges LUID_AND_ATTRIBUTES
ends
;//--------------------
struct LUID_AND_ATTRIBUTES
Luid dq 0 ;// ID привилегии (8-байт)
Attributes dd 0 ;// откл(0), вкл.в дефолте(1), вкл.программно(2)
ends
Так мы получим в буфере идентификаторы LUID всех привилегий (Local Identifier), и чтобы привести их в дружелюбный текстовый вид, нужно будет позвать функцию LookupPrivilegeName(). Она ожидает на входе идентификатор, и возвращает соответствующую ему строку. Родственная ей функция LookupPrivilegeValue() проводит обратную операцию, т.е. принимая имя, возвращает LUID. Завернём всё вышесказанное в код:
C-подобный:
format pe console
entry start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\advapi32.inc'
;//------------
.data
szEnabled db ' <---// Enabled',0
szDefault db ' <---// Enabled by default',0
hToken dd 0
pReturn dd 0
align 16
privStr rb 128
strSize dd 0
buff db 0
;//------------
.code
start: invoke SetConsoleTitle,<'*** System Privileges List ***',0>
;//----- Открываем первичный токен своего процесса
invoke GetCurrentProcess
invoke OpenProcessToken,eax,TOKEN_QUERY,hToken
;//----- Запрос списка привелегий в токене
;//----- (первый вызов возвращает требуемый размер буфера)
invoke GetTokenInformation,[hToken],TokenPrivileges,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenPrivileges,buff,[pReturn],pReturn
cinvoke printf,<10,10,' Total privileges in system = %d',\
10,10,' Id Name',\
10,' *************************************',0>,dword[buff]
;//----- В буфере лежат все привилегии - перебираем их..
align 16
mov ecx,dword[buff] ;// PrivilegeCount
mov esi,buff+4 ;// начало массива "LUID_AND_ATTRIBUTES"
@@: push esi ecx esi
mov [strSize],128 ;// обновить длину строки
mov eax,[esi] ;// LUID очередной привилегии
push eax
invoke LookupPrivilegeName,0,esi,privStr,strSize ;// в строку его,
pop eax
cinvoke printf,<10,' %02d. %-25s',0>,eax,privStr ;// ..и на консоль
pop eax
mov eax,[eax+8] ;// взять атрибут
or eax,eax ;// если нуль (отключена),
je @next ;// ..пропустить.
mov esi,szEnabled ;// иначе: попалась активная привилегия!
cmp eax,SE_PRIVILEGE_ENABLED ;// программно включена?
je @prn
mov esi,szDefault ;// значит идёт в дефолте.
@prn: cinvoke printf,<'%s',0>,esi
@next: pop ecx esi
add esi,sizeof.LUID_AND_ATTRIBUTES ;// перейти в массиве на сл.привилегию
dec ecx ;// счётчик -1
jnz @b ;// повторить, если не нуль..
@exit: cinvoke _getch
cinvoke exit,0
;//------------
section '.idata' import data readable
library kernel32,'kernel32.dll',msvcrt,'msvcrt.dll',advapi32,'advapi32.dll'
include 'api\kernel32.inc'
include 'api\msvcrt.inc'
include 'api\advapi32.inc'
Если верить функции GetTokenInformation() (а не верить ей у нас нет причин), то семёрка поддерживает всего 24 привилегии, причём по-умолчанию у меня (как входящего в группу админов) включены только три из них. "ChangeNotify" имеют все пользователи, "Impersonate" позволяет олицетворять себя с другими лицами, а привилегию "CreateGlobal" система назначает только админам, что даём им право создавать глобальные объекты в пространстве имён. Список всех привилегий и заложенный в них глубокий смысл можно найти в репозитории MSDN
Ссылка скрыта от гостей
.Обратите внимание на идентификаторы – первые четыре привилегии(0,1,2,3) под семёркой уже отправлены в утиль, причём и дальше порядок не последовательный. Более того, содержимое листа привязано и к версии операционной системы. Так, запустив этот код на своём буке с Win-10 я обнаружил, что в ней пропала привилегия с идентификатором(4), зато появилась новая с LUID=36, под громким названием "SeDelegateSessionUserImpersonatePrivilege". На сайте RSDN есть статья
Ссылка скрыта от гостей
– можете почитать на досуге.3.1. Включение привилегий в токене
Ладно, лист поддержки получили.. теперь попробуем повысить себя в ранге и добавить парочку привилегий в токен функцией AdjustTokenPrivileges(). Она может как включать, так и отключать ненужные привилегии. Данная операция требует, чтобы токен был открыт с доступом
TOKEN_QUERY + TOKEN_ADJUST_PRIVILEGES
. Вот её описание:
C-подобный:
BOOL AdjustTokenPrivileges
TokenHandle dd 0 ;// хендл токена безопасности
DisableAllPrivileges dd 0 ;// 1 = отмена всех привилегий, 0 = добавить в токен
NewState dd 0 ;// нужные привилегии в виде структуры TOKEN_PRIVILEGES
BufferLength dd 0 ;// размер буфера (опционально)
PreviousState dd 0 ;// лист предыдущих (опционально)
ReturnLength dd 0 ;// размер PreviousState (опционально)
Если во-второй аргумент запихать логическую единицу, функция превратит токен в ограниченный "Resticted", удалив из него все привилегии кроме "SeChangeNotify". Особого внимания заслуживает здесь аргумент "NewState", который является указателем на структуру "TOKEN_PRIVILEGES". Как упоминалось выше, в первом поле этой структуры лежит счётчик вложенных структур "LUID_AND_ATTRIBUTES", за которым следует и сам массив. Значит чтобы добавить в токен, например, четыре какие-нибудь дополнительные привилегии, можно воспользоваться такой конструкцией в секции-данных программы:
C-подобный:
align 16
newState: ;//<-----<---- Структура "TOKEN_PRIVILEGES"
PrivilegeCount dd 4
Luid1 dq 0
Attribute1 dd SE_PRIVILEGE_ENABLED
Luid2 dq 0
Attribute2 dd SE_PRIVILEGE_ENABLED
Luid3 dq 0
Attribute3 dd SE_PRIVILEGE_ENABLED
Luid4 dq 0
Attribute4 dd SE_PRIVILEGE_ENABLED
Теперь у нас есть структура, в которую необходимо занести LUID требуемых привилегий – их возвращает функция LookupPrivilegeValue(), которой мы передаём имя привилегии, а она возвращает LUID. Подправим немного предыдущий исходник и посмотрим, что из этого выйдет:
C-подобный:
format pe console
entry start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\advapi32.inc'
;//------------
.data
szEnabled db ' <---// Enabled',0
szDefault db ' <---// Enabled by default',0
hToken dd 0
pReturn dd 0
align 16
;//---- Структура "TOKEN_PRIVILEGES"
newState:
PrivilegeCount dd 4
Luid1 dq 0
Attribute1 dd SE_PRIVILEGE_ENABLED
Luid2 dq 0
Attribute2 dd SE_PRIVILEGE_ENABLED
Luid3 dq 0
Attribute3 dd SE_PRIVILEGE_ENABLED
Luid4 dq 0
Attribute4 dd SE_PRIVILEGE_ENABLED
align 16
privStr rb 128
strSize dd 0
buff db 0
;//------------
.code
start: invoke SetConsoleTitle,<'*** System Privileges List ***',0>
cinvoke printf,<10,' Set Privileges: SeDebugPrivilege',\
10,' SeBackupPrivilege',\
10,' SeRestorePrivilege',\
10,' SeLoadDriverPrivilege',0>
;//----- Открываем первичный токен своего процесса
invoke GetCurrentProcess
invoke OpenProcessToken,eax,\
TOKEN_QUERY + TOKEN_ADJUST_PRIVILEGES,\
hToken
or eax,eax
jnz @f
cinvoke printf,<10,' Error set AdjustPrivileges flag!',0>
jmp @exit
;//----- Получаем LUID'ы привилегий по их именам
@@: invoke LookupPrivilegeValue,0,<'SeDebugPrivilege',0>,Luid1
invoke LookupPrivilegeValue,0,<'SeBackupPrivilege',0>,Luid2
invoke LookupPrivilegeValue,0,<'SeRestorePrivilege',0>,Luid3
invoke LookupPrivilegeValue,0,<'SeLoadDriverPrivilege',0>,Luid4
;//----- Выставляем привилегии в токене!
invoke AdjustTokenPrivileges,[hToken],0,newState,0,0,0
or eax,eax
jnz @f
cinvoke printf,<10,' fn. AdjustTokenPrivileges() error!',0>
jmp @exit
;//----- Запрос списка привелегий
;//----- (первый вызов возвращает требуемый размер буфера)
@@: invoke GetTokenInformation,[hToken],TokenPrivileges,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenPrivileges,buff,[pReturn],pReturn
cinvoke printf,<10,10,' Total privileges in system = %d',\
10,10,' Id Name',\
10,' *************************************',0>,dword[buff]
;//..............
;//<----- здесь всё остаётся без изменений
Привилегии, которые мы назначаем сами должны иметь атрибут(2), а которые идут по-умолчанию(1). На случай, если нам нужно наоборот отключить какую-нибудь привилегию оставив все остальные, предусмотрен атрибут(4) "SE_PRIVILEGE_REMOVED". Так они представлены в сишном хидере Winnt.h:
C-подобный:
;//--- Атрибуты привилегий -------------------
SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001 ;// по умолчанию
SE_PRIVILEGE_ENABLED = 0x00000002 ;// предоставлена по запросу
SE_PRIVILEGE_REMOVED = 0x00000004 ;// удалена
Теперь мой процесс может без проблем копировать системные файлы привилегией "SeBackup" (даже если этого запрещает DACL файла, поскольку привилегии имеют приоритет над правом), производить в них запись посредством "SeRestore", и захватывать чужие процессы, для чего и предназначена "SeDebug". Следуя этикету нужно отметить, после того-как мы выполним задачу, нужно восстановить свои дефолтные привилегии (см.аргумент PreviousState в AdjustTokenPrivileges). В противном случае, даже после закрытия софта установленные привилегии остаются в нашем токене, и будут действительны вплоть до окончания текущего сеанса (т.е. до следующей перезагрузки системы). В некоторых случаях это может создать проблемы, например оставив лазейку для хакеров.
4. Сбор информации о токенах, и списках DACL
В примере ниже я только беру информацию из токена, хотя при родственной Set можно и редактировать его. Вот функции, которые решают следующие задачи:
• GetTokenInformation() – запрашивает 48-типов информации из токена;
• LookupPrivilegeName() – принимая LUID возвращает привилегию в виде строки;
• ConvertSidToStringSid() – конвертирует двоичный SID в строку.
Можно было вывести намного больше данных, но лист не вместился уже в окно консоли, поэтому минимум.:
C-подобный:
format pe console
entry start
;//------------
section '.inc' data readable
include 'win32ax.inc'
include 'equates\advapi32.inc'
;//------------
.data
hToken dd 0
pReturn dd 0
counter dd 0
number dd 0
offset dd 0
align 16
privStr rb 256
strSize dd 0
sidBuff rb 128
align 16
buff db 0
;//------------
.code
start: invoke SetConsoleTitle,<'*** Current Token Info ***',0>
invoke OpenProcessToken,-1,TOKEN_QUERY,hToken
;//----- SID юзера в системе ----------
invoke GetTokenInformation,[hToken],TokenUser,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenUser,buff,[pReturn],pReturn
mov eax,dword[buff]
invoke ConvertSidToStringSid,eax,buff
mov eax,dword[buff]
cinvoke printf,<10,' User System Sid..: %s',0>,eax
;//----- Получим SID из токена ----------
invoke GetTokenInformation,[hToken],TokenGroups,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenGroups,buff,[pReturn],pReturn
mov ecx,dword[buff]
mov esi,buff+4
@@: mov eax,[esi + SID_AND_ATTRIBUTES.Attributes]
and eax,SE_GROUP_LOGON_ID
cmp eax,SE_GROUP_LOGON_ID
je @found
add esi,sizeof.SID_AND_ATTRIBUTES
dec ecx
jnz @b
cinvoke printf,<10,' ERROR! SID not found!',0>
jmp @exit
@found: mov eax,dword[esi]
invoke ConvertSidToStringSid,eax,buff
mov eax,dword[buff]
cinvoke printf,<10,' User Logon Sid..: %s',0>,eax
;//----- Перечислим привелегии в токене ----------
invoke GetTokenInformation,[hToken],TokenPrivileges,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenPrivileges,buff,[pReturn],pReturn
cinvoke printf,<10,10,' Total privileges in system = %d **********',\
10,' ******************************************',\
10,' User privileges: ',0>,dword[buff]
mov ecx,dword[buff]
mov esi,buff+4
@@: cmp [esi + LUID_AND_ATTRIBUTES.Attributes],0
jz @fuck
push esi ecx
mov [strSize],128
invoke LookupPrivilegeName,0,esi,privStr,strSize
cinvoke printf,<10,' %s',0>,privStr
pop ecx esi
@fuck: add esi,sizeof.LUID_AND_ATTRIBUTES
dec ecx
jnz @b
;//----- Общая статистика токена ----------
; invoke GetTokenInformation,[hToken],TokenStatistics,buff,0,pReturn
; invoke GetTokenInformation,[hToken],TokenStatistics,buff,[pReturn],pReturn
; cinvoke printf,<10,' Stat: %x',0>,eax
;//**************************************************************
cinvoke printf,<10,10,' Default Security Descriptor **************',\
10,' ******************************************',0>
;//----- Owner (владелец).
;//----- SID, который будет записан как владелец любых объектов,
;//----- созданных процессом с этим токеном доступа.
invoke GetTokenInformation,[hToken],TokenOwner,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenOwner,buff,[pReturn],pReturn
mov eax,dword[buff]
invoke ConvertSidToStringSid,eax,buff
mov eax,dword[buff]
cinvoke printf,<10,' Owner.......: %s',0>,eax
;//----- PrimaryGroup.
;//----- SID, который будет записан как основная группа любых объектов,
;//----- созданных процессом с этим токеном доступа.
invoke GetTokenInformation,[hToken],TokenPrimaryGroup,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenPrimaryGroup,buff,[pReturn],pReturn
mov eax,dword[buff]
invoke ConvertSidToStringSid,eax,buff
mov eax,dword[buff]
cinvoke printf,<10,' Group.......: %s',0>,eax
;//----- DefaultDacl.
;//----- DACL, который будет назначен любым объектам,
;//----- созданным процессом c этим токеном, если не указан явный ACL.
invoke GetTokenInformation,[hToken],TokenDefaultDacl,buff,0,pReturn
invoke GetTokenInformation,[hToken],TokenDefaultDacl,buff,[pReturn],pReturn
mov esi,dword[buff]
push esi
movzx eax,[esi + DACL_HEADER.AclRevision]
movzx ebx,[esi + DACL_HEADER.AclSize]
movzx ecx,[esi + DACL_HEADER.AceCount]
mov [counter],ecx
cinvoke printf,<10,10,' DACL ver....: %d',\
10,' DACL size...: %d byte',\
10,' ACE count..: %d',0>,eax,ebx,ecx
pop esi
add esi,sizeof.DACL_HEADER
mov ecx,[counter]
@@: push esi ecx
movzx eax,[esi + ACCESS_ALLOWED_ACE.AceFlags]
movzx ebx,[esi + ACCESS_ALLOWED_ACE.AceSize]
mov ecx,[esi + ACCESS_ALLOWED_ACE.Mask]
mov [offset],ebx
push esi
cinvoke printf,<10,10,' ACE.%d Size: %d byte',\
10,' ACE.%d Flags: 0x%x',\
10,' ACE.%d Mask: 0x%08X',0>,[number],ebx,\
[number],eax,\
[number],ecx
pop esi
add esi,ACCESS_ALLOWED_ACE.SidStart
invoke ConvertSidToStringSid,esi,sidBuff
mov eax,dword[sidBuff]
cinvoke printf,< 10,' ACE.%d SID: %s',0>,[number],eax
pop ecx esi
inc [number]
add esi,[offset]
dec ecx
jnz @b
@exit: cinvoke _getch
cinvoke exit,0
;//------------
;//------------
section '.idata' import data readable
library kernel32,'kernel32.dll',msvcrt,'msvcrt.dll',advapi32,'advapi32.dll'
include 'api\kernel32.inc'
include 'api\msvcrt.inc'
include 'api\advapi32.inc'
5. Под занавес..
Информация на уровне токенов позволяет нам приобретать права и привилегии. Здесь играет роль каждый бит, который мы указываем в масках доступа, поэтому знать их назначение наша обязанность. Практика ничто, перед теорией, и можно долго ломиться в открытые двери, даже не подразумевая, что ключ лежит в единственном бите. К сожалению, здесь не было возможности в полной мере освятить эту тему, т.к. говорить о ней можно бесконечно. В результате у меня осталось впечатление, что тема не раскрыта всё-таки до конца, и будем считать ей отправной точкой. В скрепке готовые примеры и инклуд. Всем желаю удачи, и до следующего..
Вложения
Последнее редактирование модератором: