模拟管理员重新启动 Windows 服务
Impersonate Administrator to restart Windows service
我有一个 Delphi 应用程序可以重新启动某些 Windows 服务。
如果执行此应用程序的用户具有管理权限(Administrators 组的成员),则服务重启会成功。
对于普通用户,服务重启会失败。
如果这个普通用户使用 "right-click" 运行 作为管理员也会成功。
但是,普通用户必须输入管理员用户名和密码。
我想为普通用户解决这个问题。
我的想法是在代码中做"impersonate user",显然是模拟本地管理员帐户。
但是,还是不行。
这是我的代码:
function GetCurrUserName: string;
var
Size : DWORD;
begin
Size := MAX_COMPUTERNAME_LENGTH + 1;
SetLength(Result, Size);
if GetUserName(PChar(Result), Size) then
SetLength(Result, Size)
else
Result := '';
end;
function ConnectAs(const lpszUsername, lpszPassword: string): Boolean;
var
hToken : THandle;
begin
Result := LogonUser(PChar(lpszUsername), nil, PChar(lpszPassword), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken);
if Result then
Result := ImpersonateLoggedOnUser(hToken)
else
RaiseLastOSError;
end;
我这样称呼它:
try
ConnectAs('Administrator','password');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
// Restart the Windows service
// Done, back to the user
RevertToSelf;
服务重启将失败。如果用户 运行 作为管理员(右键单击 运行 作为),它就可以工作。
模拟本身运行良好。我可以通过获取当前用户名来检查它。但是,为什么它仍然不起作用。看起来用户在没有管理员权限的情况下冒充。
还有其他类型的模仿吗?
这是意料之中的事情。在 UAC 下,管理员组中的用户会收到经过过滤的令牌,除非他们通过 UAC 提升过程。您正在模拟其他用户,但您这样做时没有 UAC 提升,因此具有经过过滤的、减少的权限令牌。
为了在 UAC 下以提升的权限执行代码,您必须通过 UAC 提升过程。如果您可以避免这样做,那么 UAC 将毫无意义。
据我所知,您有以下原则选择:
- 启动另一个进程来完成这项工作,使用
runas
shell 动词来强制提升,或者实际上是一些其他机制来安排提升。这将引导您完成您想避免的 UAC 对话框。
- 或者,安装两个服务。第一项服务是您当前安装的服务,即您希望重新启动的服务。第二个服务是一个小而简单的服务,其唯一任务是执行重新启动。因为第二个服务在没有 UAC 的会话 0 中运行,所以您可以轻松地安排它有足够的权限来重新启动第一个服务。当您需要从桌面应用重启服务 1 时,向服务 2 发送请求,以便服务 2 可以执行服务 1 的重启。
顺便说一句,我确实注意到您在调用 ImpersonateLoggedOnUser
.
时没有检查错误
我有一个 Delphi 应用程序可以重新启动某些 Windows 服务。
如果执行此应用程序的用户具有管理权限(Administrators 组的成员),则服务重启会成功。
对于普通用户,服务重启会失败。
如果这个普通用户使用 "right-click" 运行 作为管理员也会成功。 但是,普通用户必须输入管理员用户名和密码。
我想为普通用户解决这个问题。
我的想法是在代码中做"impersonate user",显然是模拟本地管理员帐户。
但是,还是不行。
这是我的代码:
function GetCurrUserName: string;
var
Size : DWORD;
begin
Size := MAX_COMPUTERNAME_LENGTH + 1;
SetLength(Result, Size);
if GetUserName(PChar(Result), Size) then
SetLength(Result, Size)
else
Result := '';
end;
function ConnectAs(const lpszUsername, lpszPassword: string): Boolean;
var
hToken : THandle;
begin
Result := LogonUser(PChar(lpszUsername), nil, PChar(lpszPassword), LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken);
if Result then
Result := ImpersonateLoggedOnUser(hToken)
else
RaiseLastOSError;
end;
我这样称呼它:
try
ConnectAs('Administrator','password');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
// Restart the Windows service
// Done, back to the user
RevertToSelf;
服务重启将失败。如果用户 运行 作为管理员(右键单击 运行 作为),它就可以工作。
模拟本身运行良好。我可以通过获取当前用户名来检查它。但是,为什么它仍然不起作用。看起来用户在没有管理员权限的情况下冒充。
还有其他类型的模仿吗?
这是意料之中的事情。在 UAC 下,管理员组中的用户会收到经过过滤的令牌,除非他们通过 UAC 提升过程。您正在模拟其他用户,但您这样做时没有 UAC 提升,因此具有经过过滤的、减少的权限令牌。
为了在 UAC 下以提升的权限执行代码,您必须通过 UAC 提升过程。如果您可以避免这样做,那么 UAC 将毫无意义。
据我所知,您有以下原则选择:
- 启动另一个进程来完成这项工作,使用
runas
shell 动词来强制提升,或者实际上是一些其他机制来安排提升。这将引导您完成您想避免的 UAC 对话框。 - 或者,安装两个服务。第一项服务是您当前安装的服务,即您希望重新启动的服务。第二个服务是一个小而简单的服务,其唯一任务是执行重新启动。因为第二个服务在没有 UAC 的会话 0 中运行,所以您可以轻松地安排它有足够的权限来重新启动第一个服务。当您需要从桌面应用重启服务 1 时,向服务 2 发送请求,以便服务 2 可以执行服务 1 的重启。
顺便说一句,我确实注意到您在调用 ImpersonateLoggedOnUser
.