在没有管理员权限的情况下启动应用程序
使用管理员权限启动应用程序后,在该应用程序中使用ShellExecute
执行的程序将继承管理员权限。但那不是我想要的:它只需定期开始,无需额外的权限。 ShellExecute
接受参数OPEN
(普通)和RUNAS
(管理员)。但是,如果在以管理员身份启动应用程序后使用OPEN
,它仍会像RUNAS
那样操作。在没有管理员权限的情况下启动应用程序
以下示例演示了这一点:如果您使用常规权限启动它,它会显示'开始正常'。一旦你为'管理员'按1,它将以管理员身份启动。如果在新创建的提示中按2,则不会启动“常规”提示,但会再次出现“管理员”提示。
我发现了RUNAS中的一些参数(https://superuser.com/a/374866),但它们不能通过ShellExecute
传递。有任何想法吗?
program WindowsPrivilegeTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Winapi.Windows,
Winapi.ShellAPI;
function CheckTokenMembership(TokenHandle: THANDLE; SidToCheck: Pointer; var
IsMember: BOOL): BOOL; stdcall; external advapi32 name 'CheckTokenMembership';
// Source: http://*.com/a/28572886/1870208
function IsAdministrator: Boolean;
var
psidAdmin: Pointer;
B: BOOL;
const
SECURITY_NT_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
SECURITY_BUILTIN_DOMAIN_RID = $00000020;
DOMAIN_ALIAS_RID_ADMINS = $00000220;
SE_GROUP_USE_FOR_DENY_ONLY = $00000010;
begin
psidAdmin := nil;
try
Win32Check(AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2,
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
psidAdmin));
if CheckTokenMembership(0, psidAdmin, B) then
Result := B
else
Result := False;
finally
if psidAdmin <> nil then
FreeSid(psidAdmin);
end;
end;
var
lLine : String;
lOperation : PChar;
begin
try
if IsAdministrator then
begin
Writeln('Started as administrator');
end
else
begin
Writeln('Started regular');
end;
while True do
begin
Writeln('');
Writeln('How to start? 1 = admin, 2 = regular user. Type number and press enter');
ReadLn(lLine);
lOperation := '';
if lLine = '1' then
begin
lOperation := 'RUNAS';
end
else
if lLine = '2' then
begin
lOperation := 'OPEN';
end;
if lOperation <> '' then
begin
ShellExecute(0, lOperation, PChar(ParamStr(0)), nil, nil, SW_SHOWNORMAL);
Break;
end;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
我在创建安装程序时用来解决这个难题,这里有一个可能的解决方案。
这个想法是创建一个以用户权限运行的后台进程,主应用程序与它通信并让它知道要启动的程序 - 启动的进程将拥有用户权限。
它应该像这样工作: 后台进程像往常一样启动,具有用户权限。启动后,它会以管理员身份启动主程序。后台进程保持隐藏状态并等待主进程的命令。
这当然是最简单的方法,如果唯一的要求是启动进程*作为当前登录会话的用户*。否则,在不知道或存储密码的情况下,为登录用户获取令牌的唯一方法是[通过作为本地系统用户的API调用](https://msdn.microsoft.com/en-us/library /aa383840(v=vs.85).aspx)(即:你需要编写一个系统服务来调用这个服务,然后用'ShellExecuteEx'进行跟进,传入用户的令牌)。 –
@J ...不完全,请参阅愚蠢 –
@DavidHeffernan令人惊讶的是,考虑到使用'WTSQueryUserToken'时的注意事项(以及令牌泄露的风险),即使从系统上下文中限制使用它,这也是可能的。 +1,无论如何。 –
由于被引用的问题的答案不是正确的方式,因此正确的方法是获取进程标记,使用TokenLinkedToken参数调用GetTokenInformation来获取非夸张的标记。使用该标记启动新的过程。显然还要检查UAC是否实际启用(http://www.remkoweijnen.nl/blog/2011/08/11/gettokeninformation-with-tokenlinkedtoken-returns-error-1312/)。 – Remko