| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: WinAPI и системное программирование > Win7 + TrayIcon + системная учетная запись |
| Автор: Romero 18.2.2011, 11:31 |
| Добрый день, уважаемые знатоки. Необходимо переписать один из своих старых проектов, который разрабатывался во времена Windows XP. Причина - некорректная работа в Windows 7. А именно... Первая часть проекта - программа с иконкой в системном трее. Назовем ее "Программа". Вторая часть - служба, запускаемая с правами системы. Она контролирует наличие запущенного процесса Программы: при необходимости выполняет ее запуск или перезапуск. При этом критично, чтобы Программа запускалась именно с правами системы (передаваемыми ей от службы). Суть проблемы - в "семерке" иконка в трее запущенной службой Программы не отображается. Я так понял, что в Win7, в отличие от WinXP, у каждого пользователя свой рабочий стол, а соответственно и трей. Поэтому и иконка Программы "появляется" в трее системного аккаунта, а не активного пользователя. Вопрос: как обойти сие нововведение Windows? Вероятно, создавая иконку, нужно каким-то образом указать "чужой" трей, но как это сделать - не нашел. Возможно, предложите свои решения. Обе части проекта написаны мной, можно механизм взаимодействия как угодно переписать. Вариант запуска службой Программы от имени активного пользователя - не подходит точно. |
| Автор: bems 18.2.2011, 18:57 |
| Нет никакого трея системы. В остальном Ками прав. |
| Автор: kami 18.2.2011, 18:59 | ||
| А, забыл - есть же еще планировщик заданий Кстати, такая политика появилась не в Win7, а в Win Vista Добавлено через 2 минуты и 54 секунды Не получится, я пробовал. Устанавливаемый десктоп должен принадлежать родной для процесса WinSta, а WinSta сервиса<> WinSta десктопного приложения.
http://msdn.microsoft.com/en-us/library/ms686250(v=vs.85).aspx |
| Автор: bems 18.2.2011, 19:10 | ||
Но из системной учетки это точно возможно, нужно только найти правильный путь. |
| Автор: CodeMonkey 18.2.2011, 19:30 | ||||
Почему это? Чую запах ###-кода. Добавлено через 12 минут и 21 секунду P.S. И гляньте ещё http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=78600.
А откуда уверенность? |
| Автор: kami 18.2.2011, 19:51 |
| В свое время пришлось очень плотно заниматься этой проблемой. Знаний и навыков было, конечно, гораздо меньше... Результатом был отлов смены сессии (в HandlerEx) и запуск процесса через CreateProcessAsUser, с какими-то извращениями над полученным токеном. |
| Автор: bems 18.2.2011, 20:03 |
Потому что еще не было такого, чтобы ей что-то эффективно запретили. Весь вопрос только в количестве извращений на тысячу строк кода. Это конечно не меняет того факта, что не нужно запускать интерактивные программы в контексте системы |
| Автор: Romero 20.2.2011, 23:56 |
| Спасибо огромное за ответы! Насчет целесообразности запуска Программы именно с привилегиями системы я еще подумаю на неделе. Дело в том, что писалось это все несколько лет назад (опыта было меньше, винды еще не так к безопасности подходили...), и теперь, возможно, есть смысл более обдуманно подойти к проектированию. Системные права нужны (в подробности вдаваться не буду, если позволите), но другое дело, что можно перепоручить ту часть функционала, где они требуются, самой службе, а не Программе. А ей уж оставить лишь иконку, да окошки. На всякий случае научился запускать Программу из службы с правами и в сеансе активного пользователя (надо только еще проверить хорошо, что везде и всегда работает - раньше не сталкивался с такими механизмами). Но с этим вариантом еще возникает ряд вопросов. Обмозгую хорошенько на свежую голову и спрошу здесь, коли не разберусь с чем. |
| Автор: kami 21.2.2011, 00:03 | ||
Большая просьба - если будет работать с быстрым переключением пользователей, т.е. десктопная часть будет запускаться под каждым из залогиненных пользователей - выложите код. Безумно интересно, как Вы справились с этой проблемой. |
| Автор: Romero 21.2.2011, 12:00 |
| kami, мне мыслится сей механизм так: в моей службе по таймеру проверяется наличие запущенного процесса Программы. В эту проверку можно добавить сравнение SID пользователя, от которого запущен процесс, с SID активного пользователя. Если не совпадают, запускать процесс под активным пользователем. С определением SID найденного процесса я разобрался, а с SID активного пользователя - нет. Не сталкивались с этим? Для запуска Программы в контексте активного пользователя я использую JediWinAPI. Поэтому можно и SID пользователя определять ее средствами. Но я никак не могу найти толковой документации по данной библиотеке. |
| Автор: Romero 21.2.2011, 15:17 |
| Кажется, получилось! Действовал, как описал выше. Сейчас приберусь в коде, и выложу результат. Надеюсь, покажете, что и где оптимизировать можно - писал сумбурно, дергая отовсюду и адаптируя чужие куски кода. |
| Автор: Romero 21.2.2011, 17:24 | ||||
Итак, вот что у меня получилось... Тестирование пока провел лишь на Windows 7 32bit. Остальные варианты позже тоже проверю (меня интересует еще по большому счету лишь Windows XP и 64-битные вариации обеих ОС). Помогите с оптимизацией алгоритма и кодов, плиз.
В основной рабочей процедуре службы в цикле через заданный интервал проверяю наличие процесса и, если нужно, выполняю запуск:
|
| Автор: kami 21.2.2011, 19:41 |
1. Не котируется в WinXP при использовании быстрого переключения пользователей, будет возвращать SessionID первого из залогиненных :( (хотя, может и ошибаюсь, но у меня так не получалось) Upd. действительно, возвращается активный пользователь... 2. Попробуйте изначально войти под "гостем". Емнип, там какие-то траблы были с привилегиями как сервиса, так и полученного токена пользователя. 3. Ну и таймер - это...хотя, Ваше право. Тоже решение. |
| Автор: Romero 22.2.2011, 09:49 |
| 1. В теории должно работать. Я когда этот способ нашел, читал, что для WinXP сработает. Но на практике позже проверю, когда время будет. 2. Что значит "изначально"? Установить службу, а затем переключиться в сеанс Гостя? Вообще, для Гостя мне не очень нужно, чтобы работало, но ради эксперимента попробую. 3. Не совсем таймер - цикл со слипом, пока службе не передается "SERVICE_STOP". Но тут других решений я не вижу. Нужно обеспечить постоянное присутствие одного процесса Программы. Поскольку она все же будет запущена с правами пользователя, на случай убийства пользователем процесса служба будет производить проверку каждые Х секунд. |
| Автор: Romero 22.2.2011, 13:17 | ||||
Подправил код слегка. Избавился от Jedi.
В основной рабочей процедуре службы в цикле через заданный интервал проверяю наличие процесса и, если нужно, выполняю запуск:
|
| Автор: Romero 22.2.2011, 14:06 |
| Теоретический вопрос. Для выполнения действий процедуры RunApp у службы должны быть права системы. А можно ли получить подобные привилегии (и удачно запустить программу от чужого имени) из обычного приложения (которое не имеет прав системы, но, допустим, запущена от имени Администратора)? И еще, когда я гляжу на другие найденные в Интернете примеры кода запуска процесса от имени активного пользователя, у меня возникает ощущение, что в моем коде чего-то не хватает. Он слишком лаконичный |
| Автор: kami 22.2.2011, 18:40 | ||||||||||
Попробуйте запустить что-нибудь "не свое", тогда узнаете У меня больше всего проблем возникло с запуском ACDSee - нехватало параметров в Environment. В остальном - читайте в MSDN описания тех функций, которые задействуете. Например, про первый параметр http://msdn.microsoft.com/en-us/library/ms682429(v=vs.85).aspx пишут
Если смотреть в библию, то упираемся в http://msdn.microsoft.com/en-us/library/aa383840(v=vs.85).aspx:
Добавлено через 6 минут и 5 секунд
Установить службу> выйти из системы> войти под гостем> сменить пользователя на админа (т.е. чтобы получилось 2 одновременно запущенные сессии - первая "гость" и вторая "админ") Добавлено через 7 минут и 26 секунд
Проверил под виртуальной XP SP3 - работает. |
| Автор: Romero 24.2.2011, 10:05 |
| В выходные проверил на Win7x64 - работает. В сеансе Гостя - тоже. Проверял так: запустил сеанс Админа, Программа стартанула; переключился на Гостя (сеанс Админа был активен) - стартанула (в списке процессов видно, что от имени Гостя, как и положено), переключился на другого Админа - тоже стартанула (в списке процессов уже три копии - каждая от имени своего юзера). Или критично было именно "выйти из системы", после чего первым запустить сеанс Гостя? На ХР когда проверял, возникла проблема, но сейчас до меня дошло, что это мог я сам накосячить. Перепроверю позже. Запускать "не своё" нужды нет. Пока проблем в работе моей связки не обнаружил, буду считать, что все удачно "прокатило" Спасибо за ответы, kami! |
| Автор: Romero 26.4.2011, 14:43 |
| Подскажите, пожалуйста, что изменить в моем коде выше, чтобы Программа продолжалась запускаться на рабочем столе активного пользователя, НО с правами родителя (то есть службы)? |
| Автор: kami 26.4.2011, 21:02 | ||
а что не так сейчас? Что говорит по этому поводу ProcessExplorer? (а именно - закладка Security свойств процесса) |
| Автор: Romero 27.4.2011, 08:18 | ||
Я так понимаю, речь о верхней строке в этой вкладке? Там указан активный пользователь. |
| Автор: kami 27.4.2011, 12:19 |
| Romero, нет, о последней строке в верхнем списке, называется "обязательная метка>чего-то там". Вот это "чего-то там" может быть "обязательный уровень системы", "средний обязательный уровень" и так далее. Также достаточно важным является нижний список - установленные процессу привилегии. Ибо даже не будучи запущенным "от системы", но имея нужную привилегию, можно выполнить достаточно многое (но не всё, как пример - WTSQueryUserToken, рассмотренный ранее). Какая привилегия нужна - это, само собой - в MSDN. Добавлено через 1 минуту и 32 секунды И вообще - почему возникли сомнения, что программа, запущенная на активном десктопе, не обладает нужными правами? |
| Автор: Romero 27.4.2011, 16:07 | ||||
Там стоит "Integrity".
Как минимум по тому, что у запускаемой службой Программы нет прав писать в Program files - это первое, что заметил. Думаю, это явно говорит, что права от службы не передались. |
| Автор: kami 28.4.2011, 18:38 |
| Поднял свой старый код, вспомнил, почему забросил это дело. Всё начинается с http://msdn.microsoft.com/en-us/library/aa383840(v=vs.85).aspx. По какой-то (не выясненной мной) причине, у этого полученного токена в http://msdn.microsoft.com/en-us/library/aa379624(v=vs.85).aspx для группы "администраторы" стоит SE_GROUP_USE_FOR_DENY_ONLY. А для работы программы с правами админа, в идеале должно быть значение SE_GROUP_OWNER. Сие (SE_GROUP_USE_FOR_DENY_ONLY) автоматически означает, что права администратора для всего, что будет запускаться под этим токеном будут заблокированы. Установка http://msdn.microsoft.com/en-us/library/aa379626(v=vs.85).aspx для токена не даст ничего, по причине (опять-таки) блокировки прав доступа. А теперь - самое интересное - никакими манипуляциями заменить SE_GROUP_USE_FOR_DENY_ONLY на любое другое значение нельзя. Так прописано в MSDN и подтверждено опытами. Позже выложу код, как пытался это сделать. Общий вывод: сервису чего-то не хватает для того, чтобы получить primarytoken с правильно установленным значением http://msdn.microsoft.com/en-us/library/aa379595(v=vs.85).aspx для группы "администраторы". Может, Гуру чего посоветуют... |
| Автор: Romero 12.5.2011, 15:44 | ||
Почитал про имперсонацию, попробовал после получения токена активного пользователя дать ему права таким образом:
Фокус не прошел. Может, я неправильно понял теорию? |
| Автор: kami 12.5.2011, 20:28 |
Смотря что нужно было этим кодом сделать и откуда он вызывается. Это раз. Второе - DuplicateToken создает impersonationToken. CreateProcessAsUser требует для работы primary token, который можно получить с помощью DuplicateTokenEx. Ну и - само собой, если в "исходном" токене нет нужных правов, то в дубликате они не появятся. Добавлено @ 20:33 ах да, кроме не помешает добавить TOKEN_EXECUTE |
| Автор: Romero 13.5.2011, 12:24 | ||||
Нужно передать привилегии вызывающего процесса (службы, запущенной под системной учеткой). Вызывается после получения юзерского токена, перед вызовом CreateProcessAsUser. Вот про различие токенов я и не разобрался, попробую сегодня DuplicateTokenEx, спасибо. В исходном токене, я надеюсь, все нужные права будут при таком подходе:
|
| Автор: Romero 13.5.2011, 13:33 | ||
где hToken - токен активного юзера, sysToken - токен службы, запущенной от имени системы. Результат - приложение запускается на рабочем столе юзера, но и с его правами (проверяю попыткой записи в системную директорию). Где-то что-то не докрутил еще... |