![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Как достоверно определить что программа запущена как сервис?
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]тьфу блин... ну так разделяй понятия сервис и приложение.... запутал совсем... )