管理员权限进程以非管理员模式运行程序

5bug 2018-01-08 402人围观 ,发现0个评论 PTokenMandatoryLabelCreateProcessAsUserSetTokenInformation

在Win7/Win8/Win10等系统里,加入了管理员模式,而且对于一些写操作的时候都需要管理员权限才行,例如桌面开发环境下最常见的升级程序就需要管理员权限才能下载文件,但如果程序一直都是使用管理员权限来运行的话也没必要,因为管理员权限下某些功能可能与非管理员模式下有些区别的,我之前遇到的一个情况就是拖放功能受影响。当时是在具有管理员权限的升级程序升级后直接启动主程序,导致主程序的某些功能无法使用,后来测试下来发现就是管理员权限引起的,所以就想有个方法能够让有管理员权限的升级程序以普通用户权限来启动主程序,后来在网上搜集,终于找到了解决方法,好像是武稀松(wr960204)大神分享出来的。代码如下:

unit uLowIntergrityLevelProcess;

interface

uses
  WinApi.Windows;

const
  SECURITY_MANDATORY_UNTRUSTED_RID = $00000000;
  SECURITY_MANDATORY_LOW_RID = $00001000;
  SECURITY_MANDATORY_MEDIUM_RID = $00002000;
  SECURITY_MANDATORY_HIGH_RID = $00003000;
  SECURITY_MANDATORY_SYSTEM_RID = $00004000;
  SECURITY_MANDATORY_PROTECTED_PROCESS_RID = $00005000;

function CreateLowIntegrityProcess(const ExeName: string; const Params: string = ''; TimeOut: DWORD = 0): HResult;
function GetIntegrityLevel(): DWORD;

implementation

type
  PTokenMandatoryLabel = ^TTokenMandatoryLabel;

  TTokenMandatoryLabel = packed record
    Label_: TSidAndAttributes;
  end;

function GetIntegrityLevel(): DWORD;
var
  hProcess, hToken: THandle;
  pTIL: PTokenMandatoryLabel;
  dwReturnLength: DWORD;
  dwTokenUserLength: DWORD;
  psaCount: PUCHAR;
  SubAuthority: DWORD;
begin
  Result := 0;
  dwReturnLength := 0;
  dwTokenUserLength := 0;
  pTIL := nil;

  hProcess := GetCurrentProcess();
  OpenProcessToken(hProcess, TOKEN_QUERY or TOKEN_QUERY_SOURCE, hToken);
  if hToken = 0 then
    Exit;
  if not GetTokenInformation(hToken, WinApi.Windows.TTokenInformationClass(TokenIntegrityLevel), pTIL,
    dwTokenUserLength, dwReturnLength) then
  begin
    if GetLastError = ERROR_INSUFFICIENT_BUFFER then
    Begin
      pTIL := Pointer(LocalAlloc(0, dwReturnLength));
      if pTIL = nil then
        Exit;
      dwTokenUserLength := dwReturnLength;
      dwReturnLength := 0;

      if GetTokenInformation(hToken, WinApi.Windows.TTokenInformationClass(TokenIntegrityLevel), pTIL,
        dwTokenUserLength, dwReturnLength) and IsValidSid((pTIL.Label_).Sid) then
      begin
        psaCount := GetSidSubAuthorityCount((pTIL.Label_).Sid);
        SubAuthority := psaCount^;
        SubAuthority := SubAuthority - 1;
        Result := GetSidSubAuthority((pTIL.Label_).Sid, SubAuthority)^;
      end;
      LocalFree(Cardinal(pTIL));
    End;
  end;

  CloseHandle(hToken);
end;

const
  userenvlib = 'userenv.dll';

function CreateEnvironmentBlock(lpEnvironment: PPointer; hToken: THandle; bInherit: BOOL): BOOL; stdcall;
  external userenvlib;
function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall; external userenvlib;

function CreateLowIntegrityProcess(const ExeName, Params: string; TimeOut: DWORD): HResult;
type
  _TOKEN_MANDATORY_LABEL = Record
    Label_: SID_AND_ATTRIBUTES;
  End;

  TOKEN_MANDATORY_LABEL = _TOKEN_MANDATORY_LABEL;
  PTOKEN_MANDATORY_LABEL = ^TOKEN_MANDATORY_LABEL;

const
  SECURITY_MANDATORY_LABEL_AUTHORITY: TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 16));
  SE_GROUP_INTEGRITY = $00000020;
  SE_GROUP_INTEGRITY_ENABLED = $00000040;
var
  hToken, hNewToken: THandle;
  MLAuthority: SID_IDENTIFIER_AUTHORITY;
  pIntegritySid: PSID;
  tml: TOKEN_MANDATORY_LABEL;
  si: TStartupInfo;
  pi: PROCESS_INFORMATION;
  pszCommandLine: string;
  dwCreationFlag: DWORD;
  pEnvironment: LPVOID;
begin

  Result := ERROR_SUCCESS;
  pszCommandLine := ExeName + Params;
  hToken := 0;
  hNewToken := 0;
  MLAuthority := SECURITY_MANDATORY_LABEL_AUTHORITY;
  pIntegritySid := nil;
  FillChar(tml, sizeof(tml), 0);
  FillChar(si, sizeof(si), 0);
  FillChar(pi, sizeof(pi), 0);

  si.cb := sizeof(si);
  si.lpDesktop := 'Winsta0\Default';
  dwCreationFlag := NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE;
  pEnvironment := nil;

  try
    // 从自己获取一个令牌
    if (not OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE or TOKEN_QUERY or TOKEN_ADJUST_DEFAULT or
      TOKEN_ASSIGN_PRIMARY, hToken)) then
    begin
      Result := GetLastError();
      Exit;
    end;

    // 复制令牌
    if (not DuplicateTokenEx(hToken, 0, nil, SecurityImpersonation, TokenPrimary, hNewToken)) then
    begin
      Result := GetLastError();
      Exit;
    end;

    // 创建一个低权限的SID    SECURITY_MANDATORY_LOW_RID权限太低了  所以使用SECURITY_MANDATORY_MEDIUM_RID
    if (not AllocateAndInitializeSid(MLAuthority, 1, SECURITY_MANDATORY_MEDIUM_RID, 0, 0, 0, 0, 0, 0, 0, pIntegritySid))
    then
    begin
      Result := GetLastError();
      Exit;
    end;

    tml.Label_.Attributes := SE_GROUP_INTEGRITY;
    tml.Label_.Sid := pIntegritySid;

    // 设置这个低权限SID到令牌
    if (not SetTokenInformation(hNewToken, TokenIntegrityLevel, @tml, (sizeof(tml) + GetLengthSid(pIntegritySid)))) then
    begin
      Result := GetLastError();
      Exit;
    end;

    // 创建一个环境变量
    if (CreateEnvironmentBlock(@pEnvironment, hToken, FALSE)) then
      dwCreationFlag := dwCreationFlag or CREATE_UNICODE_ENVIRONMENT
    else
      pEnvironment := nil;

    // 创建一个低权限的进程
    if (not CreateProcessAsUser(hNewToken, nil, PChar(pszCommandLine), nil, nil, FALSE, dwCreationFlag, pEnvironment,
      nil, si, pi)) then
    begin
      Result := GetLastError();
      Exit;
    end;

    WaitForSingleObject(pi.hProcess, TimeOut);
  finally
    // 清理现场
    if pEnvironment <> nil then
    begin
      DestroyEnvironmentBlock(pEnvironment);
      pEnvironment := nil;
    end;

    if (hToken <> 0) then
    begin
      CloseHandle(hToken);
      hToken := 0;
    end;
    if (hNewToken <> 0) then
    begin
      CloseHandle(hNewToken);
      hNewToken := 0;
    end;
    if (pIntegritySid <> nil) then
    begin
      FreeSid(pIntegritySid);
      pIntegritySid := nil;
    end;
    if (pi.hProcess <> 0) then
    begin
      CloseHandle(pi.hProcess);
      pi.hProcess := 0;
    end;
    if (pi.hThread <> 0) then
    begin
      CloseHandle(pi.hThread);
      pi.hThread := 0;
    end;

    if (ERROR_SUCCESS <> Result) then
    begin
      SetLastError(Result);
    end
    else
    begin
      Result := ERROR_SUCCESS;
    end;
  end;
end;

end.

这里还有武稀松(wr960204)大神的另一种解决方法:http://www.raysoftware.cn/?p=49 大家可以参考一下!

不容错过
Powered By Z-BlogPHP