В разработке приложений на Delphi часто возникает необходимость выполнить определенные действия с правами администратора, даже если пользователь вошел в систему с обычными правами. В этой статье мы рассмотрим различные методы повышения привилeгий приложения в Windows, фокусируясь на решении проблемы, когда программа знает учетные данные администратора, но не должна запрашивать их у пользователя.
Проблема и основные подходы
Как видно из обсуждения, существует несколько способов решения этой задачи:
Использование ShellExecuteEx с флагом runas
Применение CreateProcessWithLogonW
Использование CreateProcessAsUser
Метод с ImpersonateUser
Каждый из этих методов имеет свои особенности и ограничения, которые мы рассмотрим подробнее.
Метод 1: ShellExecuteEx с флагом runas
function RunAsAdmin(const Path, Params: string): Boolean;
var
sei: TShellExecuteInfo;
begin
try
FillChar(sei, SizeOf(sei), 0);
sei.cbSize := SizeOf(sei);
sei.fMask := SEE_MASK_FLAG_DDE_USE_AGENT or SEE_MASK_FLAG_NO_UI;
sei.lpVerb := 'runas';
sei.lpFile := PChar(Path);
sei.lpParameters := PChar(Params);
sei.nShow := SW_SHOWNORMAL;
Result := ShellExecuteEx(@sei);
except
Result := False;
end;
end;
Недостатки: Этот метод все равно запрашивает подтверждение UAC, что не соответствует требованиям задачи.
Метод 2: CreateProcessWithLogonW
function RunAsAdminWithPassword(UserName, Password, Domain, AppPath: string): Integer;
var
SI: TStartupInfoW;
PI: TProcessInformation;
begin
Result := 0;
ZeroMemory(@SI, SizeOf(SI));
SI.cb := SizeOf(SI);
if CreateProcessWithLogonW(
PWideChar(UserName),
PWideChar(Domain),
PWideChar(Password),
LOGON_WITH_PROFILE,
nil,
PWideChar(AppPath),
CREATE_NEW_CONSOLE,
nil,
nil,
SI,
PI) then
begin
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end
else
Result := GetLastError;
end;
Особенности: Этот метод не требует прав администратора для вызова, но приложение должно находиться в общем каталоге (например, C:\Users\Public).
Метод 3: Имперсонализация пользователя
type
TImpersonateUser = class
private
FUserToken: THandle;
FErrorCode: DWORD;
public
constructor Create;
destructor Destroy; override;
function Logon(const UserName, Domain, Password: string): Boolean;
procedure Logoff;
property ErrorCode: DWORD read FErrorCode;
end;
constructor TImpersonateUser.Create;
begin
inherited;
FUserToken := 0;
FErrorCode := 0;
end;
destructor TImpersonateUser.Destroy;
begin
Logoff;
inherited;
end;
function TImpersonateUser.Logon(const UserName, Domain, Password: string): Boolean;
var
LoggedOn: Boolean;
begin
Result := False;
Logoff;
if UserName = '' then
begin
FErrorCode := ERROR_BAD_ARGUMENTS;
Exit;
end;
if Domain <> '' then
LoggedOn := LogonUser(PChar(UserName), PChar(Domain), PChar(Password),
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, FUserToken)
else
LoggedOn := LogonUser(PChar(UserName), PChar(Domain), PChar(Password),
LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, FUserToken);
if not LoggedOn then
begin
FErrorCode := GetLastError;
Exit;
end;
if not ImpersonateLoggedOnUser(FUserToken) then
begin
FErrorCode := GetLastError;
Exit;
end;
FErrorCode := ERROR_SUCCESS;
Result := True;
end;
procedure TImpersonateUser.Logoff;
begin
if FUserToken <> 0 then
begin
RevertToSelf;
CloseHandle(FUserToken);
FUserToken := 0;
end;
end;
Применение:
var
Impersonator: TImpersonateUser;
begin
Impersonator := TImpersonateUser.Create;
try
if Impersonator.Logon('AdminUser', 'Domain', 'Password') then
begin
try
// Выполняем действия с правами администратора
finally
Impersonator.Logoff;
end;
end
else
ShowMessage('Ошибка входа: ' + IntToStr(Impersonator.ErrorCode));
finally
Impersonator.Free;
end;
end;
Метод 4: Использование CreateProcessAsUser
function CreateProcessAsAdmin(const CommandLine: string): Boolean;
var
Token: THandle;
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
Result := False;
if not OpenProcessToken(GetCurrentProcess, TOKEN_ALL_ACCESS, Token) then Exit;
try
ZeroMemory(@StartInfo, SizeOf(StartInfo));
StartInfo.cb := SizeOf(StartInfo);
StartInfo.lpDesktop := 'winsta0\default';
StartInfo.dwFlags := STARTF_USESHOWWINDOW;
StartInfo.wShowWindow := SW_SHOW;
if CreateProcessAsUser(Token, nil, PChar(CommandLine), nil, nil, False,
CREATE_NEW_CONSOLE, nil, nil, StartInfo, ProcInfo) then
begin
CloseHandle(ProcInfo.hThread);
CloseHandle(ProcInfo.hProcess);
Result := True;
end;
finally
CloseHandle(Token);
end;
end;
Решение с учетом всех требований
Наиболее подходящим решением, учитывающим все требования (не запрашивать пароль, не требовать дополнительных файлов, работать без установки), является комбинация методов:
Использование CreateProcessWithLogonW для запуска процесса с правами администратора
Предварительное копирование исполняемого файла в общий каталог
procedure CopyFileToPublic(const SourceFile: string);
var
PublicPath: string;
begin
PublicPath := GetEnvironmentVariable('PUBLIC') + '\' + ExtractFileName(SourceFile);
if not FileExists(PublicPath) or (FileAge(SourceFile) <> FileAge(PublicPath)) then
CopyFile(PChar(SourceFile), PChar(PublicPath), False);
end;
function RunAsAdminSilently(const UserName, Password, Domain, AppPath: string): Boolean;
var
PublicAppPath: string;
begin
PublicAppPath := GetEnvironmentVariable('PUBLIC') + '\' + ExtractFileName(AppPath);
CopyFileToPublic(AppPath);
Result := RunAsAdminWithPassword(UserName, Password, Domain, PublicAppPath) = 0;
end;
Альтернативные решения
Использование планировщика задач: Создание задачи с правами администратора и запуск ее программно.
Сервис Windows: Создание сервиса с правами SYSTEM, который будет выполнять необходимые действия.
Использование API NtCreateToken: Более сложный метод, требующий глубоких знаний Windows API.
Заключение
Выбор метода зависит от конкретных требований к приложению. Для большинства случаев подходит комбинация CreateProcessWithLogonW с копированием файла в общий каталог. Однако важно помнить о безопасности - хранение паролей администратора в приложении может представлять угрозу, если приложение будет скомпрометировано.
При реализации таких механизмов следует также учитывать политики безопасности организации и требования к защите информации. В некоторых случаях запрос учетных данных
В статье рассматриваются методы запуска программы от имени администратора в Delphi без запроса пароля, включая обход UAC и повышение привилегий с использованием различных API-функций Windows.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.