Как запустить процесс без прав с помощью Delphi2007 - PullRequest
6 голосов
/ 05 февраля 2009

У меня есть приложение, похожее на установщик, которое я должен запускать с повышенными правами в Vista. Но оттуда я должен начать новый процесс как не повышенный. Любые советы, как это сделать с Delphi2007?

Ответы [ 7 ]

10 голосов
/ 07 ноября 2011

Я нашел отличный пример для C ++ и адаптировал его для Delphi:

unit MediumIL;

interface

uses
  Winapi.Windows;

function CreateProcessMediumIL(lpApplicationName: PWChar; lpCommandLine: PWChar; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandle: BOOL; dwCreationFlags: DWORD; lpEnvironment: LPVOID; lpCurrentDirectory: PWChar; const lpStartupInfo: TStartupInfoW; var lpProcessInformation: TProcessInformation): DWORD;

implementation

type
  TOKEN_MANDATORY_LABEL = record
    Label_: SID_AND_ATTRIBUTES;
  end;

  PTOKEN_MANDATORY_LABEL = ^TOKEN_MANDATORY_LABEL;

  TTokenMandatoryLabel = TOKEN_MANDATORY_LABEL;
  PTokenMandatoryLabel = ^TTokenMandatoryLabel;

  TCreateProcessWithTokenW = function (hToken: THandle; dwLogonFlags: DWORD; lpApplicationName: LPCWSTR; lpCommandLine: LPWSTR; dwCreationFlags: DWORD; lpEnvironment: LPVOID; lpCurrentDirectory: LPCWSTR; const lpStartupInfo: TStartupInfoW; out lpProcessInfo: TProcessInformation): BOOL; stdcall;

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 GetShellWindow: HWND; stdcall; external 'user32.dll' name 'GetShellWindow';

// writes Integration Level of the process with the given ID into dwProcessIL
// returns Win32 API error or 0 if succeeded
function GetProcessIL(dwProcessID: DWORD; var dwProcessIL: DWORD): DWORD;
label
  _CleanUp;
var
  hProcess: THandle;
  hToken: THandle;
  dwSize: DWORD;
  pbCount: PByte;
  pdwProcIL: PDWORD;
  pTIL: PTokenMandatoryLabel;
  dwError: DWORD;
begin
  dwProcessIL := 0;

  pTIL := nil;

  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION, False, dwProcessID);
  if (hProcess = 0) then
    goto _CleanUp;

  if (not OpenProcessToken(hProcess, TOKEN_QUERY, hToken)) then
    goto _CleanUp;

  if (not GetTokenInformation(hToken, TokenIntegrityLevel, nil, 0, dwSize) and (GetLastError() <> ERROR_INSUFFICIENT_BUFFER)) then
    goto _CleanUp;

  pTIL := HeapAlloc(GetProcessHeap(), 0, dwSize);
  if (pTIL = nil) then
    goto _CleanUp;

  if (not GetTokenInformation(hToken, TokenIntegrityLevel, pTIL, dwSize, dwSize)) then
    goto _CleanUp;

  pbCount := PByte(GetSidSubAuthorityCount(pTIL^.Label_.Sid));
  if (pbCount = nil) then
    goto _CleanUp;

  pdwProcIL := GetSidSubAuthority(pTIL^.Label_.Sid, pbCount^ - 1);
  if (pdwProcIL = nil) then
    goto _CleanUp;

  dwProcessIL := pdwProcIL^;
  SetLastError(ERROR_SUCCESS);

  _CleanUp:
  dwError := GetLastError();
  if (pTIL <> nil) then
    HeapFree(GetProcessHeap(), 0, pTIL);
  if (hToken <> 0) then
    CloseHandle(hToken);
  if (hProcess <> 0) then
    CloseHandle(hProcess);
  Result := dwError;
end;

// Creates a new process lpApplicationName with the integration level of the Explorer process (MEDIUM IL)
// If you need this function in a service you must replace FindWindow() with another API to find Explorer process
// The parent process of the new process will be svchost.exe if this EXE was run "As Administrator"
// returns Win32 API error or 0 if succeeded
function CreateProcessMediumIL(lpApplicationName: PWChar; lpCommandLine: PWChar; lpProcessAttributes: PSecurityAttributes; lpThreadAttributes: PSecurityAttributes; bInheritHandle: BOOL; dwCreationFlags: DWORD; lpEnvironment: LPVOID; lpCurrentDirectory: PWChar; const lpStartupInfo: TStartupInfoW; var lpProcessInformation: TProcessInformation): DWORD;
label
  _CleanUp;
var
  hProcess: THandle;
  hToken: THandle;
  hToken2: THandle;
  bUseToken: BOOL;
  dwCurIL: DWORD;
  dwErr: DWORD;
  f_CreateProcessWithTokenW: TCreateProcessWithTokenW;
  hProgman: HWND;
  dwExplorerPID: DWORD;
  dwError: DWORD;
begin
  bUseToken := False;

  // Detect Windows Vista, 2008, Windows 7 and higher
  if (GetProcAddress(GetModuleHandleA('Kernel32'), 'GetProductInfo') <> nil) then
  begin
    dwErr := GetProcessIL(GetCurrentProcessId(), dwCurIL);
    if (dwErr <> 0) then
    begin
      Result := dwErr;
      Exit;
    end;
      if (dwCurIL > SECURITY_MANDATORY_MEDIUM_RID) then
        bUseToken := True;
  end;

  // Create the process normally (before Windows Vista or if current process runs with a medium IL)
  if (not bUseToken) then
  begin
    if (not CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandle, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)) then
    begin
      Result := GetLastError();
      Exit;
    end;

    CloseHandle(lpProcessInformation.hThread);
    CloseHandle(lpProcessInformation.hProcess);
    Result := ERROR_SUCCESS;
    Exit;
  end;

  f_CreateProcessWithTokenW := GetProcAddress(GetModuleHandleA('Advapi32'), 'CreateProcessWithTokenW');

  if (not Assigned(f_CreateProcessWithTokenW)) then // This will never happen on Vista!
  begin
    Result := ERROR_INVALID_FUNCTION;
    Exit;
  end;

  hProgman := GetShellWindow();

  dwExplorerPID := 0;
  GetWindowThreadProcessId(hProgman, dwExplorerPID);

  // ATTENTION:
  // If UAC is turned OFF all processes run with SECURITY_MANDATORY_HIGH_RID, also Explorer!
  // But this does not matter because to start the new process without UAC no elevation is required.
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION, False, dwExplorerPID);
  if (hProcess = 0) then
    goto _CleanUp;

  if (not OpenProcessToken(hProcess, TOKEN_DUPLICATE, hToken)) then
    goto _CleanUp;

  if (not DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, nil, SecurityImpersonation, TokenPrimary, hToken2)) then
    goto _CleanUp;

  if (not f_CreateProcessWithTokenW(hToken2, 0, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)) then
    goto _CleanUp;

  SetLastError(ERROR_SUCCESS);

  _CleanUp:
  dwError := GetLastError();
  if (hToken <> 0) then
    CloseHandle(hToken);
  if (hToken2 <> 0) then
    CloseHandle(hToken2);
  if (hProcess <> 0) then
    CloseHandle(hProcess);
  CloseHandle(lpProcessInformation.hThread);
  CloseHandle(lpProcessInformation.hProcess);
  Result := dwError;
end;

end.

Чтобы использовать это в своем проекте, просто используйте модуль MediumIL:

uses MediumIL;

…

procedure TForm1.FormCreate(Sender: TObject);
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  ZeroMemory(@ProcessInfo, SizeOf(ProcessInfo));
  CreateProcessMediumIL('C:\Windows\notepad.exe', nil, nil, nil, False, 0, nil, nil, StartupInfo, ProcessInfo);
end;
2 голосов
/ 06 февраля 2009

Один из способов сделать это - использовать планировщик заданий Windows.

Это обсуждается с примером кода в http://delphi.newswhat.com/geoxml/forumhistorythread?groupname=borland.public.delphi.rtl.win32&messageid=473447a5$1@newsgroups.borland.com

2 голосов
/ 05 февраля 2009

Этот блог является подробным и полезным

http://developersoven.blogspot.com/2007/02/leveraging-vistas-uac-with-delphi-part.html

Идея состоит в том, чтобы использовать ваше приложение с низкими привилегиями и COM Dll с повышенными привилегиями. Затем, когда вам нужна высота, вы просто запускаете COM. Полная ссылка на источник в формате MPL включена в сообщение.

1 голос
/ 17 июня 2015

Я бы хотел добавить к Elçins ответ выше:

Если строка кода:

  if (not DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, nil, SecurityImpersonation, TokenPrimary, hToken2)) then
goto _CleanUp;

возвращается с ошибкой 5 (Access Denied), тогда TOKEN_ALL_ACCESS должно быть OR ed с TOKEN_ADJUST_SESSIONID (0x100).

В Delphi 2010 измените LPVOID на POINTER.

1 голос
/ 28 октября 2012

Следующая статья Аарона Маргозиса охватывает именно эту тему: FAQ: Как запустить программу в качестве пользователя рабочего стола из приложения с повышенными привилегиями?

Основная идея состоит в том, чтобы получить пользовательский токен процесса оболочки, то есть explorer.exe, сделать из него первичный токен и, наконец, запустить новый процесс с этим токеном.

Статья включает в себя некоторый код C ++, который должен быть достаточно простым для перевода на Delphi. Он также включает в себя следующий подробный список с изложением подхода:

  1. Включить SeIncreaseQuotaPrivilege в вашем текущем токене
  2. Получение HWND, представляющего оболочку рабочего стола (GetShellWindow)
  3. Получить идентификатор процесса (PID) процесса, связанного с этим окном (GetWindowThreadProcessId)
  4. Открыть этот процесс (OpenProcess)
  5. Получить токен доступа из этого процесса (OpenProcessToken)
  6. Создание основного токена с этим токеном (DuplicateTokenEx)
  7. Запустить новый процесс с этим основным токеном (CreateProcessWithTokenW)
1 голос
/ 06 февраля 2009

Вы можете использовать CreateProcessWithLogonW () Вызов API:

function CreateProcessWithLogonW(lpUsername: PWideChar; lpDomain: PWideChar;
  lpPassword: PWideChar; dwLogonFlags: DWORD; lpApplicationName: PWideChar;
  lpCommandLine: PWideChar; dwCreationFlags: DWORD; lpEnvironment: Pointer;
  lpCurrentDirectory: PWideChar; const lpStartupInfo: TStartupInfo;
  var lpProcessInformation: TProcessInformation): BOOL; stdcall;
  external 'advapi32.dll' name 'CreateProcessWithLogonW';



procedure RunAs(AUsername, APassword, ADomain, AApplication: string);
const
  LOGON_WITH_PROFILE = $00000001;
var
  si: TStartupInfo;
  pi: TProcessInformation;
begin
  ZeroMemory(@si, SizeOf(si));
  si.cb := SizeOf(si);
  si.dwFlags := STARTF_USESHOWWINDOW;
  si.wShowWindow := SW_NORMAL;
  ZeroMemory(@pi, SizeOf(pi));

  if not CreateProcessWithLogonW(PWideChar(WideString(AUsername)),
    PWideChar(WideString(ADomain)), PWideChar(WideString(APassword)),
    LOGON_WITH_PROFILE, nil, PWideChar(WideString(AApplication)),
    0, nil, nil, si, pi)
  then
    RaiseLastOSError;

  CloseHandle(pi.hThread);
  CloseHandle(pi.hProcess);
end;
1 голос
/ 05 февраля 2009

Обратите внимание, если это поможет, но есть похожие вопросы здесь в c # .net, но это может дать вам некоторые подсказки, где искать, или вы можете попробовать порт в Delphi.

и просто совет постарайтесь не обновлять / устанавливать / настраивать в имени файла приложения, так как Vista автоматически добавит значок безопасности к имеющимся файлам.

...