Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Win7 + TrayIcon + системная учетная запись


Автор: Romero 18.2.2011, 11:31
Добрый день, уважаемые знатоки.
Необходимо переписать один из своих старых проектов, который разрабатывался во времена Windows XP. Причина - некорректная работа в Windows 7. А именно...
Первая часть проекта - программа с иконкой в системном трее. Назовем ее "Программа".
Вторая часть - служба, запускаемая с правами системы. Она контролирует наличие запущенного процесса Программы: при необходимости выполняет ее запуск или перезапуск. При этом критично, чтобы Программа запускалась именно с правами системы (передаваемыми ей от службы).
Суть проблемы - в "семерке" иконка в трее запущенной службой Программы не отображается. Я так понял, что в Win7, в отличие от WinXP, у каждого пользователя свой рабочий стол, а соответственно и трей. Поэтому и иконка Программы "появляется" в трее системного аккаунта, а не активного пользователя.
Вопрос: как обойти сие нововведение Windows? Вероятно, создавая иконку, нужно каким-то образом указать "чужой" трей, но как это сделать - не нашел. Возможно, предложите свои решения. Обе части проекта написаны мной, можно механизм взаимодействия как угодно переписать. Вариант запуска службой Программы от имени активного пользователя - не подходит точно.

Автор: kami 18.2.2011, 18:57
Цитата(Romero @  18.2.2011,  11:31 Найти цитируемый пост)
 Я так понял, что в Win7, в отличие от WinXP, у каждого пользователя свой рабочий стол, а соответственно и трей. Поэтому и иконка Программы "появляется" в трее системного аккаунта, а не активного пользователя.

Правильно.
Цитата(Romero @  18.2.2011,  11:31 Найти цитируемый пост)
 создавая иконку, нужно каким-то образом указать "чужой" трей, но как это сделать - не нашел.

И не найдете. "Чужой трей" находится на чужом рабочем столе, в чужой оконной станции и чужой терминальной сессии. Вы туда не влезете.
Цитата(Romero @  18.2.2011,  11:31 Найти цитируемый пост)
Вариант запуска службой Программы от имени активного пользователя - не подходит точно.

Тогда - CreateProcesAsUser или CreateProcessWithLogonW.

Автор: bems 18.2.2011, 18:57
Нет никакого трея системы. В остальном Ками прав.

Автор: kami 18.2.2011, 18:59
А, забыл - есть же еще планировщик заданий smile

Кстати, такая политика появилась не в Win7, а в Win Vista

Добавлено через 2 минуты и 54 секунды
Цитата(bems @  18.2.2011,  18:57 Найти цитируемый пост)
SetThreadDesktop

Не получится, я пробовал. Устанавливаемый десктоп должен принадлежать родной для процесса WinSta, а WinSta сервиса<> WinSta десктопного приложения.
Цитата

This desktop must be associated with the current window station for the process.

http://msdn.microsoft.com/en-us/library/ms686250(v=vs.85).aspx

Автор: bems 18.2.2011, 19:10
Цитата(kami @  18.2.2011,  18:59 Найти цитируемый пост)
Устанавливаемый десктоп должен принадлежать родной для процесса WinSta, а WinSta сервиса<> WinSta десктопного приложения.
Да, в курсе. Для этого нужно сделать SetProcessWindowStation, а для этого нужна общая сессия.
Но из системной учетки это точно возможно, нужно только найти правильный путь.

Автор: CodeMonkey 18.2.2011, 19:30
Цитата(Romero @  18.2.2011,  12:31 Найти цитируемый пост)
При этом критично, чтобы Программа запускалась именно с правами системы (передаваемыми ей от службы).


Почему это? Чую запах ###-кода.

Добавлено через 12 минут и 21 секунду
P.S. И гляньте ещё http://www.delphikingdom.ru/asp/answer.asp?IDAnswer=78600.

Цитата(bems @  18.2.2011,  20:10 Найти цитируемый пост)
Но из системной учетки это точно возможно, нужно только найти правильный путь.


А откуда уверенность?

Автор: kami 18.2.2011, 19:51
В свое время пришлось очень плотно заниматься этой проблемой. Знаний и навыков было, конечно, гораздо меньше...
Результатом был отлов смены сессии (в HandlerEx) и запуск процесса через CreateProcessAsUser, с какими-то извращениями над полученным токеном.

Автор: bems 18.2.2011, 20:03
Цитата(CodeMonkey @  18.2.2011,  19:30 Найти цитируемый пост)
А откуда уверенность?

Потому что еще не было такого, чтобы ей что-то эффективно запретили. Весь вопрос только в количестве извращений на тысячу строк кода.
Это конечно не меняет того факта, что не нужно запускать интерактивные программы в контексте системы

Автор: Romero 20.2.2011, 23:56
Спасибо огромное за ответы!
Насчет целесообразности запуска Программы именно с привилегиями системы я еще подумаю на неделе. Дело в том, что писалось это все несколько лет назад (опыта было меньше, винды еще не так к безопасности подходили...), и теперь, возможно, есть смысл более обдуманно подойти к проектированию. Системные права нужны (в подробности вдаваться не буду, если позволите), но другое дело, что можно перепоручить ту часть функционала, где они требуются, самой службе, а не Программе. А ей уж оставить лишь иконку, да окошки.
На всякий случае научился запускать Программу из службы с правами и в сеансе активного пользователя (надо только еще проверить хорошо, что везде и всегда работает - раньше не сталкивался с такими механизмами). Но с этим вариантом еще возникает ряд вопросов. Обмозгую хорошенько на свежую голову и спрошу здесь, коли не разберусь с чем.

Автор: kami 21.2.2011, 00:03
Цитата(Romero @  20.2.2011,  23:56 Найти цитируемый пост)
проверить хорошо, что везде и всегда работает - раньше не сталкивался с такими механизмами

Большая просьба - если будет работать с быстрым переключением пользователей, т.е. десктопная часть будет запускаться под каждым из залогиненных пользователей - выложите код. Безумно интересно, как Вы справились с этой проблемой.

Автор: Romero 21.2.2011, 12:00
kami, мне мыслится сей механизм так: в моей службе по таймеру проверяется наличие запущенного процесса Программы. В эту проверку можно добавить сравнение SID пользователя, от которого запущен процесс, с SID активного пользователя. Если не совпадают, запускать процесс под активным пользователем. С определением SID найденного процесса я разобрался, а с SID активного пользователя - нет. Не сталкивались с этим?
Для запуска Программы в контексте активного пользователя я использую JediWinAPI. Поэтому можно и SID пользователя определять ее средствами. Но я никак не могу найти толковой документации по данной библиотеке.

Автор: Romero 21.2.2011, 15:17
Кажется, получилось!  smile 
Действовал, как описал выше. Сейчас приберусь в коде, и выложу результат. Надеюсь, покажете, что и где оптимизировать можно - писал сумбурно, дергая отовсюду и адаптируя чужие куски кода.

Автор: Romero 21.2.2011, 17:24
Итак, вот что у меня получилось... Тестирование пока провел лишь на Windows 7 32bit. Остальные варианты позже тоже проверю (меня интересует еще по большому счету лишь Windows XP и 64-битные вариации обеих ОС). Помогите с оптимизацией алгоритма и кодов, плиз.
Код

uses
  ..., JwaWinBase, JwaWtsApi32;//Jedi я использовал для реализации запуска процесса в контексте активного пользователя; надо бы избавиться от этих библиотек, не успел еще разобраться, как...

type
  PWTS_PROCESS_INFO = ^WTS_PROCESS_INFO;
  WTS_PROCESS_INFO = packed record
    SessionId: DWORD;
    ProcessId: DWORD;
    pProcessName: PChar;
    pUserSid: PSID;
  end;

type
  procedure ConvertSidToStringSid(SID: PSID; var StringSid: LPSTR); stdcall;
    external advapi32 name 'ConvertSidToStringSidA';

  function WTSEnumerateProcesses(hServer: THandle; Reserved,Version: DWORD;
    var ppProcessInfo: PWTS_PROCESS_INFO;var Count: DWORD): BOOL; stdcall;
      external 'wtsapi32.dll' name 'WTSEnumerateProcessesA';

  procedure WTSFreeMemory(pMemory: Pointer); stdcall;
    external 'wtsapi32.dll' name 'WTSFreeMemory';

...

//получаем SID активного пользователя (того, кто монитором "владеет" на момент запуска)
function GetCurrentUserSID:string;
var
  hToken:THandle;
  ReturnLength:DWORD;
  UserToken:PSIDAndAttributes;
  res:LPSTR;
begin
  if WTSQueryUserToken(WtsGetActiveConsoleSessionID,hToken) then
  begin
    Windows.GetTokenInformation(hToken,TokenUser,UserToken,0,ReturnLength);//здесь и далее "Windows." указываю во избежание конфликтов с переопределенными в Jedi процедурами
    GetMem(UserToken,ReturnLength);
    try
      if Windows.GetTokenInformation(hToken,TokenUser,UserToken,ReturnLength,ReturnLength) then
        begin
          ConvertSidToStringSid(UserToken.Sid,res);
          Result:=res;
        end;
    finally
      FreeMem(UserToken);
    end;
  end;
end;

//запуск приложения от имени активного пользователя
procedure RunApp(FilePath:string);
var
  hToken:THandle;
  si:STARTUPINFOA;
  pi:PROCESS_INFORMATION;
begin
  if WTSQueryUserToken(WtsGetActiveConsoleSessionID,hToken) then
  begin
    ZeroMemory(@si,SizeOf(si));
    si.cb:=SizeOf(si);
    si.lpDesktop:=nil;
    CreateProcessAsUser(hToken,nil,PANSIChar(FilePath),nil,nil,False,0,nil,nil,si,pi);
  end;
end;

//поиск запущенного процесса по имени файла и SID пользователя; я проверяю наличие процесса по имени файла, но по-хорошему надо бы как-то иначе реализовать...
function CheckTask(ExeFileName,CurUserSID:string):Boolean;
var
  Count,i:DWORD;
  pProcessInfo,pCur:PWTS_PROCESS_INFO;
  UserSID:LPSTR;
begin
  Result:=false;
  if not WTSEnumerateProcesses(0,0,1,pProcessInfo,Count)then
    Exit;
  for i:=0 to Count-1 do
    begin
      pCur:=Ptr(DWORD(pProcessInfo)+(i*SizeOf(WTS_PROCESS_INFO)));
      if pCur.pProcessName=ExeFileName then
      begin
        ConvertSidToStringSid(pCur.pUserSid,UserSID);
        if UserSID=CurUserSID then
          Result:=true;
      end;
    end;
  WTSFreeMemory(pProcessInfo);
end;

В основной рабочей процедуре службы в цикле через заданный интервал проверяю наличие процесса и, если нужно, выполняю запуск:
Код

  if not CheckTask('program.exe',GetCurrentUserSID) then
    RunApp('path/program.exe');


Автор: kami 21.2.2011, 19:41
Цитата(Romero @  21.2.2011,  17:24 Найти цитируемый пост)
WtsGetActiveConsoleSessionID

1. Не котируется в WinXP при использовании быстрого переключения пользователей, будет возвращать SessionID первого из залогиненных :(  (хотя, может и ошибаюсь, но у меня так не получалось) Upd. действительно, возвращается активный пользователь...  smile 
2. Попробуйте изначально войти под "гостем". Емнип, там какие-то траблы были с привилегиями как сервиса, так и полученного токена пользователя.
3. Ну и таймер - это...хотя, Ваше право. Тоже решение.

Автор: Romero 22.2.2011, 09:49
1. В теории должно работать. Я когда этот способ нашел, читал, что для WinXP сработает. Но на практике позже проверю, когда время будет.
2. Что значит "изначально"? Установить службу, а затем переключиться в сеанс Гостя? Вообще, для Гостя мне не очень нужно, чтобы работало, но ради эксперимента попробую.
3. Не совсем таймер - цикл со слипом, пока службе не передается "SERVICE_STOP". Но тут других решений я не вижу. Нужно обеспечить постоянное присутствие одного процесса Программы. Поскольку она все же будет запущена с правами пользователя, на случай убийства пользователем процесса служба будет производить проверку каждые Х секунд.

Автор: Romero 22.2.2011, 13:17
Подправил код слегка. Избавился от Jedi.
Код

type
  PWTS_PROCESS_INFO = ^WTS_PROCESS_INFO;
  WTS_PROCESS_INFO = packed record
    SessionId: DWORD;
    ProcessId: DWORD;
    pProcessName: PChar;
    pUserSid: PSID;
  end;

type
  procedure ConvertSidToStringSid(SID: PSID; var StringSid: LPSTR); stdcall;
    external advapi32 name 'ConvertSidToStringSidA';

  function WTSEnumerateProcesses(hServer: THandle; Reserved,Version: DWORD;
    var ppProcessInfo: PWTS_PROCESS_INFO;var Count: DWORD): BOOL; stdcall;
      external 'wtsapi32.dll' name 'WTSEnumerateProcessesA';

  procedure WTSFreeMemory(pMemory: Pointer); stdcall;
    external 'wtsapi32.dll' name 'WTSFreeMemory';

  function WTSQueryUserToken(SessionId: DWORD; phToken: pHandle):bool;stdcall;external 'wtsapi32.dll';

  function WTSGetActiveConsoleSessionId: DWORD; stdcall; external 'Kernel32.dll';

...

//получаем SID активного пользователя (того, кто монитором "владеет" на момент запуска)
function GetCurrentUserSID:string;
var
  hToken:THandle;
  ReturnLength:DWORD;
  UserToken:PSIDAndAttributes;
  res:LPSTR;
begin
  if WTSQueryUserToken(WtsGetActiveConsoleSessionID,@hToken) then
  begin
    GetTokenInformation(hToken,TokenUser,UserToken,0,ReturnLength);
    GetMem(UserToken,ReturnLength);
    try
      if GetTokenInformation(hToken,TokenUser,UserToken,ReturnLength,ReturnLength) then
        begin
          ConvertSidToStringSid(UserToken.Sid,res);
          Result:=res;
        end;
    finally
      FreeMem(UserToken);
    end;
  end;
end;

//запуск приложения от имени активного пользователя
procedure RunApp(FilePath:string);
var
  hToken:THandle;
  si:STARTUPINFO;
  pi:PROCESS_INFORMATION;
begin
  if WTSQueryUserToken(WtsGetActiveConsoleSessionID,@hToken) then
    begin
      ZeroMemory(@si,SizeOf(si));
      si.cb:=SizeOf(si);
      si.lpDesktop:=nil;
      CreateProcessAsUser(hToken,nil,PANSIChar(FilePath),nil,nil,False,0,nil,nil,si,pi);
      CloseHandle(hToken);
    end;
end;

//поиск запущенного процесса по имени файла и SID пользователя; я проверяю наличие процесса по имени файла, но по-хорошему надо бы как-то иначе реализовать...
function CheckTask(ExeFileName,CurUserSID:string):Boolean;
var
  Count,i:DWORD;
  pProcessInfo,pCur:PWTS_PROCESS_INFO;
  UserSID:LPSTR;
begin
  Result:=false;
  if not WTSEnumerateProcesses(0,0,1,pProcessInfo,Count)then
    Exit;
  for i:=0 to Count-1 do
    begin
      pCur:=Ptr(DWORD(pProcessInfo)+(i*SizeOf(WTS_PROCESS_INFO)));
      if UpperCase(pCur.pProcessName)=UpperCase(ExeFileName) then
      begin
        ConvertSidToStringSid(pCur.pUserSid,UserSID);
        if UserSID=CurUserSID then
          Result:=true;
      end;
    end;
  WTSFreeMemory(pProcessInfo);
end;

В основной рабочей процедуре службы в цикле через заданный интервал проверяю наличие процесса и, если нужно, выполняю запуск:
Код

  if not CheckTask('program.exe',GetCurrentUserSID) then
    RunApp('path/program.exe');


Автор: Romero 22.2.2011, 14:06
Теоретический вопрос. Для выполнения действий процедуры RunApp у службы должны быть права системы. А можно ли получить подобные привилегии (и удачно запустить программу от чужого имени) из обычного приложения (которое не имеет прав системы, но, допустим, запущена от имени Администратора)?

И еще, когда я гляжу на другие найденные в Интернете примеры кода запуска процесса от имени активного пользователя, у меня возникает ощущение, что в моем коде чего-то не хватает. Он слишком лаконичный smile Например, по сравнению с функцией RunApplication http://koochangmin.blogspot.com/2010/01/blog-post.html. С виду то всё работает, но вдруг что упустил важного.

Автор: kami 22.2.2011, 18:40
Цитата(Romero @  22.2.2011,  14:06 Найти цитируемый пост)
С виду то всё работает, но вдруг что упустил важного.

Попробуйте запустить что-нибудь "не свое", тогда узнаете smile
У меня больше всего проблем возникло с запуском ACDSee - нехватало параметров в Environment.
В остальном - читайте в MSDN описания тех функций, которые задействуете.
Например, про первый параметр http://msdn.microsoft.com/en-us/library/ms682429(v=vs.85).aspx пишут
Цитата

A handle to the primary token that represents a user. The handle must have the TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access rights. For more information, see Access Rights for Access-Token Objects. The user represented by the token must have read and execute access to the application specified by the lpApplicationName or the lpCommandLine parameter.


Цитата(Romero @  22.2.2011,  14:06 Найти цитируемый пост)
можно ли получить подобные привилегии (и удачно запустить программу от чужого имени) из обычного приложения

Если смотреть в библию, то упираемся в http://msdn.microsoft.com/en-us/library/aa383840(v=vs.85).aspx:
Цитата

To call this function successfully, the calling application must be running within the context of the LocalSystem account and have the SE_TCB_NAME privilege.


Добавлено через 6 минут и 5 секунд
Цитата(Romero @  22.2.2011,  09:49 Найти цитируемый пост)
2. Что значит "изначально"? Установить службу, а затем переключиться в сеанс Гостя? 

Установить службу> выйти из системы> войти под гостем> сменить пользователя на админа (т.е. чтобы получилось 2 одновременно запущенные сессии - первая "гость" и вторая "админ")

Добавлено через 7 минут и 26 секунд
Цитата(Romero @  22.2.2011,  09:49 Найти цитируемый пост)
1. В теории должно работать. Я когда этот способ нашел, читал, что для WinXP сработает.

Проверил под виртуальной XP SP3 - работает.

Автор: Romero 24.2.2011, 10:05
В выходные проверил на Win7x64 - работает. В сеансе Гостя - тоже. Проверял так: запустил сеанс Админа, Программа стартанула; переключился на Гостя (сеанс Админа был активен) - стартанула (в списке процессов видно, что от имени Гостя, как и положено), переключился на другого Админа - тоже стартанула (в списке процессов уже три копии - каждая от имени своего юзера). Или критично было именно "выйти из системы", после чего первым запустить сеанс Гостя?

На ХР когда проверял, возникла проблема, но сейчас до меня дошло, что это мог я сам накосячить. Перепроверю позже.

Запускать "не своё" нужды нет. Пока проблем в работе моей связки не обнаружил, буду считать, что все удачно "прокатило" smile Тестирование на нескольких сотнях компов покажет, хватает ли этого кода...

Спасибо за ответы, kami!

Автор: Romero 26.4.2011, 14:43
Подскажите, пожалуйста, что изменить в моем коде выше, чтобы Программа продолжалась запускаться на рабочем столе активного пользователя, НО с правами родителя (то есть службы)?

Автор: kami 26.4.2011, 21:02
Цитата(Romero @  26.4.2011,  14:43 Найти цитируемый пост)
запускаться на рабочем столе активного пользователя, НО с правами родителя

а что не так сейчас?
Что говорит по этому поводу ProcessExplorer? (а именно - закладка Security свойств процесса)

Автор: Romero 27.4.2011, 08:18
Цитата(kami @  26.4.2011,  21:02 Найти цитируемый пост)
Что говорит по этому поводу ProcessExplorer? (а именно - закладка Security свойств процесса) 

Я так понимаю, речь о верхней строке в этой вкладке? Там указан активный пользователь.

Автор: kami 27.4.2011, 12:19
Romero, нет, о последней строке в верхнем списке, называется "обязательная метка>чего-то там".
Вот это "чего-то там" может быть "обязательный уровень системы", "средний обязательный уровень" и так далее.
Также достаточно важным является нижний список - установленные процессу привилегии. Ибо даже не будучи запущенным "от системы", но имея нужную привилегию, можно выполнить достаточно многое (но не всё, как пример - WTSQueryUserToken, рассмотренный ранее). Какая привилегия нужна - это, само собой - в MSDN.

Добавлено через 1 минуту и 32 секунды
И вообще - почему возникли сомнения, что программа, запущенная на активном десктопе,  не обладает нужными правами?

Автор: Romero 27.4.2011, 16:07
Цитата(kami @  27.4.2011,  12:19 Найти цитируемый пост)
о последней строке в верхнем списке, называется "обязательная метка>чего-то там"

Там стоит "Integrity".

Цитата(kami @  27.4.2011,  12:19 Найти цитируемый пост)
И вообще - почему возникли сомнения, что программа, запущенная на активном десктопе,  не обладает нужными правами? 

Как минимум по тому, что у запускаемой службой Программы нет прав писать в 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
Почитал про имперсонацию, попробовал после получения токена активного пользователя дать ему права таким образом:
Код

OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,sysToken);
DuplicateToken(sysToken,SecurityImpersonation,@hToken);

Фокус не прошел. Может, я неправильно понял теорию? smile

Автор: kami 12.5.2011, 20:28
Цитата(Romero @  12.5.2011,  15:44 Найти цитируемый пост)
Может, я неправильно понял теорию

Смотря что нужно было этим кодом сделать и откуда он вызывается. Это раз. 
Второе - DuplicateToken создает impersonationToken. CreateProcessAsUser требует для работы primary token, который можно получить с помощью DuplicateTokenEx.
Ну и - само собой, если в "исходном" токене нет нужных правов, то в дубликате они не появятся.

Добавлено @ 20:33
ах да, кроме
Цитата(Romero @  12.5.2011,  15:44 Найти цитируемый пост)
TOKEN_ADJUST_PRIVILEGES or TOKEN_QUER

не помешает добавить TOKEN_EXECUTE

Автор: Romero 13.5.2011, 12:24
Цитата(kami @  12.5.2011,  20:28 Найти цитируемый пост)
Смотря что нужно было этим кодом сделать и откуда он вызывается. Это раз. 
Второе - DuplicateToken создает impersonationToken. CreateProcessAsUser требует для работы primary token, который можно получить с помощью DuplicateTokenEx.
Ну и - само собой, если в "исходном" токене нет нужных правов, то в дубликате они не появятся.

Нужно передать привилегии вызывающего процесса (службы, запущенной под системной учеткой). Вызывается после получения юзерского токена, перед вызовом CreateProcessAsUser.
Вот про различие токенов я и не разобрался, попробую сегодня DuplicateTokenEx, спасибо.
В исходном токене, я надеюсь, все нужные права будут при таком подходе:
Код

OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_EXECUTE,sysToken);

Автор: Romero 13.5.2011, 13:33
Код

WTSQueryUserToken(WtsGetActiveConsoleSessionID,@hToken)
...
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY or TOKEN_EXECUTE,sysToken);
DuplicateTokenEx(sysToken,MAXIMUM_ALLOWED,nil,SecurityImpersonation,TokenPrimary,hToken);
...
CreateProcessAsUser(hToken,nil,PANSIChar(FilePath),nil,nil,False,0,nil,nil,si,pi);

где hToken - токен активного юзера, sysToken - токен службы, запущенной от имени системы.
Результат - приложение запускается на рабочем столе юзера, но и с его правами (проверяю попыткой записи в системную директорию).
Где-то что-то не докрутил еще...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)