Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Как достоверно определить что программа запущена как сервис?


Страницы:   1 2

SPeller ©   (2009-04-15 09:15:00) 

Был откуда-то у меня пример, но он вытаскивает имя юзера, с которым сервис запускается, и сравнивает его с именем, под которым запущен процесс. Совпало - типа мы сервис. Но одинаковый юзер еще не означает того, что наш процесс - сервис. Сервис может быть запущен от имени другой учетки, поэтому данный метод отпадает. Каким образом определить что процесс запущен сервис-менеджером, а не кем-то другим?


SPeller   (2009-04-17 16:37:00) [1]

Удалено модератором


Anatoly Podgoretsky   (2009-04-17 09:59:00) [2]

> SPeller  (17.04.2009 2:39:28)  [28]

Не несколько, а много и исправляются или медленно или никогда.


Игорь Шевченко   (2009-04-17 02:51:00) [3]


> Оказалось что у борланда несколько функций объявлены неправильно.
> BuildExplicitAccessWithName и GetSecurityInfo некорректно
> объявлены. А рядом с BuildExplicitAccessWithName еще видел
> функции, которые с постфиксом W имеют параметры PAnsiChar.
>


И на солнце бывают пятна.

Пользуйся лучше Jedi API Translation ( http://www.delphi-jedi.org) - там вроде меньше ошибок


SPeller   (2009-04-17 02:39:00) [4]

Кстати, работаю тут еще с правами. Оказалось что у борланда несколько функций объявлены неправильно.BuildExplicitAccessWithName и GetSecurityInfo некорректно объявлены. А рядом с BuildExplicitAccessWithName еще видел функции, которые с постфиксом W имеют параметры PAnsiChar.


SPeller   (2009-04-17 02:36:00) [5]


> проверь, запусти свой сервис под юзером с правами гостя
Я это с самого начала и делал :) Правда, не под гостем, а под специальной учеткой, которая состоит в группе пользователей. Права на функции Nt/ZwХХХ не проверял, а вот на GetModuleFileNameEx и GetModuleBaseName споткнулся сразу. Через снапшот всё получилось без вопросов.


SPeller   (2009-04-17 02:31:00) [6]


> не вижу красного
Там оно не красным, а черным. В локальной версии - красным выделена строка may be altered or unavailable in future versions of Windows. Разве этого не достаточно чтобы опасаться воплощения в жизнь обещания "may be altered or unavailable"? Да и поля uInheritedFromUniqueProcessId в структуре PROCESS_BASIC_INFORMATION даже в самой свежей, онлайн версии msdn, нет. Изменит там что-то мелкософт, а пинать-то за ошибки в программе будут меня )


> именно поэтому они здесь и запрашиваются
И именно из-за ограниченности прав юзверя OpenProcess возвращает Access denied при попытке открыть родительский процесс.


> там запрашивается инфа о собственном процессе
Зачем мне собственный? Мне родительский нужен. PID я получу родительского, а вот имя файла родительского процесса - только через ToolHelp, который работает из-под ограниченной учетки.


sniknik   (2009-04-16 10:25:00) [7]

> Если msdn красным не рекомендует использовать - значит нафик.
не вижу красного
http://msdn.microsoft.com/en-us/library/ms684280(VS.85).aspx

> The handle must have the PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights
именно поэтому они здесь и запрашиваются
Hndl:= OpenProcess( PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, Info.uInheritedFromUniqueProcessId)
потому, что handle должен их иметь...

а с NtQueryInformationProcess... там запрашивается инфа о собственном процессе, смешно если на себя нет прав даже на чтение... не может такого быть.

вообще права на чтение инфы, имхо, есть даже у гостя... проверь, запусти свой сервис под юзером с правами гостя... в вариантах без этого кода/с ним.
кстати а на снепшот процессов права на чтения их инфы разве не нужны? он же тоже ее читает.

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


SPeller   (2009-04-16 03:59:00) [8]


> это заменяет две трети твоего кода

Так то оно так, но помимо красных предупреждений по поводу NtQueryInformationProcess, есть еще один момент, который я тоже упоминал:

The handle must have the PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights

Это и к GetModuleFileNameEx  относится, и к GetModuleBaseName. Взгляни в msdn, если не веришь. А сервис, напомню, не обязан иметь указанные права, поскольку может быть запущен от имени любого пользователя.


SPeller   (2009-04-16 03:35:00) [9]


> запустили - оно регистрирует свой call-back да и все. И
> ничем кроме сервиса быть не может по определению

Может :) Сервис - это тот же самый процесс, и ничем не отличается от обычного процесса кроме того, что запускается и управляется системным менеджером.


> тебе за построчно платят? тогда все замечательно, отлично
> просто. но вообще там Rouse_ предлагал более компактный,
>  наглядный вариант, который ничего не стоит переделать..

См пост 14 и 15. Если msdn красным не рекомендует использовать - значит нафик. Сегодня работает, завтра не известно будет или нет. Админы поставят 2008-й сервак, а там кто даст гарантию что будет работать? Если бы это было поделкой для своих личных нужд - без проблем. А когда этот софт делается для работы - то лучше избавить себя от потенциальных ошибок в будущем. Имхо.


sniknik   (2009-04-15 13:12:00) [10]

> Вобщем, получился такой код. Интересует критика
тебе за построчно платят? тогда все замечательно, отлично просто. но вообще там Rouse_ предлагал более компактный, наглядный вариант, который ничего не стоит переделать...
вот в это например

function ParentProcName: string;
const
 ProcessBasicInformation = 0;
var
 Info: PROCESS_BASIC_INFORMATION;
 dwProcessHandle: dword;
 ProcessName: string;
 Hndl: THandle;
begin
 Result:= 'noname';
 dwProcessHandle:= GetCurrentProcess;
 if NtQueryInformationProcess(dwProcessHandle, ProcessBasicInformation, @Info, SizeOf(Info), nil) = NO_ERROR
   then begin
     Hndl:= OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, Info.uInheritedFromUniqueProcessId);
     if Hndl <> 0 then
       try
         SetLength(ProcessName, MAX_PATH);
         if GetModuleBaseNameA(Hndl, 0, PChar(ProcessName), MAX_PATH) > 0
           then Result:= PChar(ProcessName);
       finally
         CloseHandle(Hndl);
       end;
   end;
end;


это заменяет две трети твоего кода.

> Зачем родительсткие процессы шукать - мне не ясно.
если не только для отладки, если прога написана так как приведенный мной ранее пример борландовской программы.


Anatoly Podgoretsky   (2009-04-15 13:11:00) [11]

> SPeller  (15.04.2009 11:34:18)  [18]

Можно попытаться передавать в строке запуска ключ.
Это если не изучать работу с сервисами и менеджером служб


KSergey   (2009-04-15 12:18:00) [12]

> SPeller ©   (15.04.09 10:08) [9]
> Никаких параметров не получает. Процесс регистрирует callback через RegisterServiceCtrlHandler.

Ну так я о чем и толкую: запустили - оно регистрирует свой call-back да и все. И ничем кроме сервиса быть не может по определению.
А если нужна отладка - то телается распознавание ключика /d - по нему вместо регистрации call-back передается управление в основную функцию обработки с выставлением флажка "запустили как консольку", в этом случае в консольку можно смело лог, например, писать. Вот и все.
Зачем родительсткие процессы шукать - мне не ясно.


SPeller   (2009-04-15 12:05:00) [13]

Вобщем, получился такой код. Интересует критика

function GetProcessParentID: Cardinal;
var
 ProcEntry: TProcessEntry32;
 hSnapshot: THandle;
 CurrID: Cardinal;
 Success: Boolean;
begin
 Result := 0;
 hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if (hSnapshot <> INVALID_HANDLE_VALUE) then
   try
     ProcEntry.dwSize := SizeOf(ProcEntry);
     Success := Process32First(hSnapshot, ProcEntry);
     if Success then
     begin
       CurrID := GetCurrentProcessId;
       SetLastError(ERROR_SUCCESS);
       while Success do
         if (ProcEntry.th32ProcessID = CurrID) then
         begin
           Result := ProcEntry.th32ParentProcessID;
           Break;
         end
         else
           Success := Process32Next(hSnapshot, ProcEntry);
     end;
     Win32Check(GetLastError);
   finally
     CloseHandle(hSnapshot);
   end
 else
   Win32Check(GetLastError);
end;

function GetProcessParentName: string;
var
 ProcEntry: TProcessEntry32;
 hSnapshot: THandle;
 Success: Boolean;
 ParentPID: Cardinal;
begin
 Result := '';
 hSnapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
 if (hSnapshot <> INVALID_HANDLE_VALUE) then
   try
     ProcEntry.dwSize := SizeOf(ProcEntry);
     Success := Process32First(hSnapshot, ProcEntry);
     if Success then
     begin
       ParentPID := GetProcessParentID;
       SetLastError(ERROR_SUCCESS);
       while Success do
         if (ProcEntry.th32ProcessID = ParentPID) then
         begin
           Result := ProcEntry.szExeFile;
           Break;
         end
         else
           Success := Process32Next(hSnapshot, ProcEntry);
     end;
     Win32Check(GetLastError);
   finally
     CloseHandle(hSnapshot);
   end
 else
   Win32Check(GetLastError);
end;

function GetStartingAsService(const SrvName: string): Boolean;
var
 Mgr, Svc: Integer;
 SSBuf: PServiceStatus;
 SSBufSz: Cardinal;
 SvcState: Cardinal;
begin
 Result := False;
 // То что мы сервис - еще не значит что мы запущены с правами SYSTEM! Поэтому запрашиваем только право чтения
 Mgr := OpenSCManager(nil, nil, GENERIC_READ);
 if (Mgr <> 0) then
 begin
   // Аналогично. Будем только читать
   Svc := OpenService(Mgr, PChar(SrvName), GENERIC_READ);
   Result := (Svc <> 0);
   if Result then
   begin
     QueryServiceStatusEx2(Svc, SC_STATUS_PROCESS_INFO, nil, 0, SSBufSz);
     GetMem(SSBuf, SSBufSz);
     QueryServiceStatusEx2(Svc, SC_STATUS_PROCESS_INFO, SSBuf, SSBufSz, SSbufSz);
     SvcState := SSBuf.dwCurrentState;
     FreeMem(SSBuf);
     Result :=
       (SvcState = SERVICE_START_PENDING) and
       AnsiSameText(GetProcessParentName, 'services.exe');

     CloseServiceHandle(Svc);
   end;
   CloseServiceHandle(Mgr);
 end;
end;

procedure Win32Check(ErrorCode: Cardinal);
begin
 if (ErrorCode <> ERROR_SUCCESS) then
   raise Win32Exception.Create(SysErrorMessage(ErrorCode));
end;


Кстати, нашел багу в д2009 в объявлении функции QueryServiceStatusEx: последний параметр не должен быть указателем. Поэтому в тексте у меня QueryServiceStatusEx2, определенная как должно быть.


SPeller   (2009-04-15 11:34:00) [14]


> Anatoly Podgoretsky ©   (15.04.09 11:22) [16]

Это не последний мой сервис :) Нужны наработки чтобы в будущем легко с ними обращаться.


SPeller   (2009-04-15 11:30:00) [15]

Вот бы еще из-под ограниченного аккаунта получить полное имя файла родительского процесса с путем. А то GetModuleFileNameEx хочет права, которых нет:

The handle must have the PROCESS_QUERY_INFORMATION and PROCESS_VM_READ access rights


Anatoly Podgoretsky   (2009-04-15 11:22:00) [16]

> SPeller  (15.04.2009 10:06:08)  [8]

Тогда и нечего голову ломать. Просто закончить отладку.


SPeller   (2009-04-15 10:28:00) [17]

А еще тот момент, что по ссылке приведено развернутое описание структуры SYSTEM_PROCESS_INFORMATION, тогда как msdn от 2008-й студии гораздо более скуден в плане информации об этой структуре:

typedef struct _SYSTEM_PROCESS_INFORMATION {
   ULONG NextEntryOffset;
   ULONG NumberOfThreads;
   BYTE Reserved1[48];
   PVOID Reserved2[3];
   HANDLE UniqueProcessId;
   PVOID Reserved3;
   ULONG HandleCount;
   BYTE Reserved4[4];
   PVOID Reserved5[11];
   SIZE_T PeakPagefileUsage;
   SIZE_T PrivatePageCount;
   LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION;


SPeller   (2009-04-15 10:25:00) [18]

В приведенной ссылке смущает только красная надпись из msdn:
[ZwQuerySystemInformation may be altered or unavailable in subsequent versions of Windows. Applications should use the alternate functions listed in this topic.]


SPeller   (2009-04-15 10:19:00) [19]


> а ссылка не помогла?

Разбираюсь в коде. Похоже, что проверки на имя процесса services.exe и на статус SERVICE_START_PENDING должно хватить.


Palladin   (2009-04-15 10:14:00) [20]

тьфу блин... ну так разделяй понятия сервис и приложение.... запутал совсем... )


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо