:: MVP ::
|
|
:: RSS ::
|
|
|
Нашелся в сети один интересный модуль – RunAsSystem,
предназначенный для запуска приложения с правами NT AUTHORITY. А что, какой пользователь не мечтает поднять свои права до уровня
системных!? И как то между делом подумалось – запускать другие приложения с системными правами конечно хорошо, а как бы
приложению самому запуститься от имени системы? Не писать же, в самом деле, загрузчик для своей программы, не серьезно это как то.
Вот первое решение, которое пришло в голову (все изменения производятся в файле проекта):
program Test;
uses
SysUtils,
Forms,
RunAsSystem,
MainFrm in 'MainFrm.pas' {Form1};
{$R *.res}
var
i: Integer;
Run: Boolean;
begin
if ParamCount > 0 then
for i := 1 to ParamCount do
if ParamStr( i ) = '/system' then
begin
Run := True;
Break;
end;
if not Run then
RunProcAsSystem( ParamStr( 0 ),
Copy( CmdLine, Pos( ' ', CmdLine )+1, Length( CmdLine ) ) + ' /system',
SystemIntegrityLevel )
else
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm( TForm1, Form1 );
Application.Run;
end;
end.
|
Все, что делает приложение в момент запуска – создает свою копию с правами NT AUTHORITY. Параметр
‘/system’ (можно назвать его и по-другому) нужен для того, чтобы не попасть в бесконечный цикл.
Это решение, хоть и вполне работоспособное, имеет небольшой недостаток. Думаю, самые внимательные уже
догадались о чём идет речь – если в момент запуска приложения в командной строке будет параметр '/system',
приложение запустится с правами учетной записи, под которой работает пользователь, что совсем не логично
(и для нас недопустимо).
Исправляется это достаточно просто – обнаружив в командной строке параметр ‘/system’ нужно проверить, из-под
какой учетной записи нас запустили. Можно было бы просто проверить имя пользователя, запустившего приложение.
function GetUserName: string;
var
Size: cardinal;
pStr: PChar;
Res: boolean;
begin
pStr := nil;
Size := MAX_COMPUTERNAME_LENGTH + 1;
try
pStr := StrAlloc( Size );
Res := Windows.GetUserName( pStr, Size );
if Res then
Result := StrPas( pStr )
else
Result := 'Имя пользователя неизвестно';
finally
if pStr <> nil then StrDispose( pStr );
end;
end;
|
Полученное имя можно было бы сравнить с именем ‘system’, но загвоздка здесь заключается в том, что функция GetUserName
возвращает имя системного пользователя с учетом локализации операционной системы. То есть в системе с английской
локализацией мы получим ‘system’, а с русской ‘система’. Этот метод не подходит из-за отсутствия в нем универсальности.
Но что же тогда сравнивать? Идентификаторы безопасности (SID) учетных записей! Функция получения SID’а учетной записи
выглядит следующим образом:
function GetUserSIDStr( SystemName, AccountName: string ): string;
var
PSID, PRef: Pointer;
SIDSize, RefSize, peUse: Cardinal;
sSID: PChar;
begin
Result := '';
SIDSize := 0;
RefSize := 0;
// Первый вызов функции позволяет получить необходимые размеры буферов
// для SID и имени домена
LookupAccountName( PChar( SystemName ), PChar( AccountName ), nil, SIDSize,
nil, RefSize, peUse );
GetMem( PSID, SIDSize );
GetMem( PRef, RefSize );
try
// Получаем SID учетной записи
if not LookupAccountName( PChar( SystemName ), PChar( AccountName ), PSID,
SIDSize, PRef,RefSize,peUse ) then
RaiseLastOSError;
// Конвертируем SID в строковое представление
if ConvertSidToStringSid( PSID, sSID ) then
begin
SetLength( Result, StrLen( sSID ) );
StrCopy( PChar( Result ), sSID );
LocalFree( Cardinal( sSID ) );
end;
finally
FreeMem( PRef );
FreeMem( PSID );
end;
end;
|
После перебора параметров командной строки добавляем еще одну проверку:
if Run then
if GetUserSIDStr( '', GetUserName ) <> GetUserSIDStr( '', 'system' ) then
Run := not Run;
|
И все готово! Теперь программа всегда будет запускаться от имени системы! Но… только если учетная запись, из-под которой
запускается приложение, принадлежит к группе администраторов. А что если это не так? Тогда программа не запустится вовсе,
а такой поворот событий нас не устраивает. Нужна дополнительная проверка на наличие необходимых прав, и если их нет, то
приложение должно запуститься в “штатном” режиме. Проверяем права пользователя:
function IsAdmin: Boolean;
function IsWin9x: Boolean;
asm
mov eax, fs:[030h]
test eax, eax
sets al
end;
const
SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority = ( Value: ( 0, 0, 0, 0, 0, 5 ) );
SECURITY_BUILTIN_DOMAIN_RID = $00000020;
DOMAIN_ALIAS_RID_ADMINS = $00000220;
var
IsUserAnAdmin: function: BOOL; stdcall;
hAccessToken: THandle;
ptgGroups: PTokenGroups;
dwInfoBufferSize: DWORD;
psidAdministrators: PSID;
i: Integer;
bSuccess: BOOL;
hMod: Thandle;
begin
Result := True;
if IsWin9x then
Exit;
Result := False;
hAccessToken := 0;
hMod := GetModuleHandle( 'shell32.dll' );
if hMod = 0 then
hMod := LoadLibrary( 'shell32.dll' );
IsUserAnAdmin := GetProcAddress( hMod,'IsUserAnAdmin' );
if not Assigned( IsUserAnAdmin ) then
begin
bSuccess := OpenThreadToken( GetCurrentThread, TOKEN_QUERY, True, hAccessToken );
if not bSuccess then
if GetLastError=ERROR_NO_TOKEN then
bSuccess := OpenProcessToken( GetCurrentProcess, TOKEN_QUERY, hAccessToken );
if bSuccess then
begin
GetMem( ptgGroups, 1024 );
bSuccess := GetTokenInformation( hAccessToken, Windows.TokenGroups, ptgGroups,
1024, dwInfoBufferSize );
CloseHandle( hAccessToken );
if bSuccess then
begin
AllocateAndInitializeSid( SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdministrators );
if ptgGroups.GroupCount > 0 then
for i := 0 to ptgGroups.GroupCount-1 do
if EqualSid( psidAdministrators, ptgGroups.Groups[i].Sid ) then
begin
Result := True;
Break;
end;
FreeSid( psidAdministrators );
end;
FreeMem( ptgGroups );
end;
end
else
Result := IsUserAnAdmin;
end;
|
Окончательный код загрузки приложения выглядит следующим образом:
{...}
procedure RunApplication;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm( TForm1, Form1 );
Application.Run;
end;
var
Param: string;
begin
// Если нет прав администратора, запускаемся как обычно...
if not IsAdmin then
RunApplication
else
// ...иначе запускаемся от имени системы
begin
if ParamCount > 0 then
for i := 1 to ParamCount do
begin
if Trim( ParamStr( i ) ) = '/system' then
begin
Run := True;
Break;
end;
end;
if Run then
if GetUserSIDStr( '', GetUserName ) <> GetUserSIDStr( '', 'system' ) then
Run := not Run;
if not Run then
begin
Param := CmdLine;
Delete( Param, 1, Length( ParamStr( 0 ) ) );
RunProcAsSystem( ParamStr( 0 ),
Copy( Param, Pos( ' ', CmdLine )+1, Length( CmdLine ) ) + ' /system',
SystemIntegrityLevel );
end
else
RunApplication;
end;
end.
|
Конечно, чтобы все это заработало, необходимо чтобы в группу "Администраторы" входил пользователь с системными правами.
Это несколько ограничивает возможности применения данного кода, но все же иногда (хотя бы на домашнем компьютере) это
может быть очень полезно.
Несколько слов напоследок. Автор этой статьи не несет ответственности за использование данного материала.
Вся информация дана исключительно в образовательных целях. Ни при каких условиях ответственность за какие-либо последствия
от использования вами этого материала в практических целях не может возлагаться на автора.
На этом все, успехов в программировании!
.: Пример к данной статье :.
|
При использовании материала - ссылка на сайт обязательна
|
|