[DELPHI, API] CreateProcessAsUser
От: Eagle-XK Украина http://esoft.pp.ua
Дата: 25.02.06 20:05
Оценка:
Вобщем, попробовал я заюзать CreateProcessAsUser(), без извратов, как я заметил, это сделать не получится... Поискал по локальным базам, нашёл с Delphi Kingdom некоторую инфу об этом, но там код почему-то не весь дан. Говорят, что это пример C-кода, кторый можно найти на http://msdn.microsoft.com/. Я в сишке не особо шарю, посему решил поискать в Гугле — наёшл код, но он не работает под ХР, и у меня не получилось заставить его работать . В смысле, он компилится, CreateProcessAsUser происходит, но процесс не запускается... Вот код:

const
  DESKTOP_ALL = DESKTOP_READOBJECTS or DESKTOP_CREATEWINDOW or DESKTOP_CREATEMENU or DESKTOP_HOOKCONTROL or
    DESKTOP_JOURNALRECORD or DESKTOP_JOURNALPLAYBACK or DESKTOP_ENUMERATE or DESKTOP_WRITEOBJECTS or
    DESKTOP_SWITCHDESKTOP or STANDARD_RIGHTS_REQUIRED;

  WINSTA_ALL = WINSTA_ENUMDESKTOPS or WINSTA_READATTRIBUTES or WINSTA_ACCESSCLIPBOARD or WINSTA_CREATEDESKTOP or
    WINSTA_WRITEATTRIBUTES or WINSTA_ACCESSGLOBALATOMS or WINSTA_EXITWINDOWS or WINSTA_ENUMERATE or
    WINSTA_READSCREEN or STANDARD_RIGHTS_REQUIRED;

  GENERIC_ACCESS = GENERIC_READ or GENERIC_WRITE or GENERIC_EXECUTE or GENERIC_ALL;

  HEAP_ZERO_MEMORY         = 8;
  ACL_REVISION             = 2;
  ACCESS_ALLOWED_ACE_TYPE  = 0;
  CONTAINER_INHERIT_ACE    = 2;
  INHERIT_ONLY_ACE         = 8;
  OBJECT_INHERIT_ACE       = 1;
  NO_PROPAGATE_INHERIT_ACE = 4;
  SE_GROUP_LOGON_ID        = $C0000000;

type
  ACL_SIZE_INFORMATION = record
    AceCount: DWORD;
    AclBytesInUse: DWORD;
    AclBytesFree: DWORD;
  end;

  ACE_HEADER = record
    AceType: BYTE;
    AceFlags: BYTE;
    AceSize: WORD;
  end;
  PACE_HEADER = ^ACE_HEADER;

  ACCESS_ALLOWED_ACE = record
    Header: ACE_HEADER;
    Mask: ACCESS_MASK;
    SidStart: DWORD;
  end;

function AddAceToWindowStation(hwinsta: HWINSTA; psid: PSID): Boolean;
var
  si: SECURITY_INFORMATION;
  psd, psdNew: PSECURITY_DESCRIPTOR;
  dwSidSize, dwSdSizeNeeded, dwNewAclSize: DWORD;
  bDaclPresent, bDaclExist: LongBool;
  pdacl, pNewAcl: PACL;
  aclSizeInfo: ACL_SIZE_INFORMATION;
  i: integer;
  pTempAce: PACE_HEADER;
  pace: ^ACCESS_ALLOWED_ACE;
begin
  Result := False;
  si := DACL_SECURITY_INFORMATION;
  pace := nil;
  psd := nil;
  dwSidSize := 0;
  pNewAcl := nil;
  psdNew := nil;
  // Obtain the DACL for the window station.

  try
    if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded) then begin
      if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
        psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
        if psd = nil then
          Exit;

        psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
        if psdNew = nil then
          Exit;

        dwSidSize := dwSdSizeNeeded;

        if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded) then
          Exit;
      end
      else begin
        Exit;
      end;
    end;

    // Create a new DACL.

    if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION) then
      Exit;

    // Get the DACL from the security descriptor.

    if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist) then
      Exit;

    // Initialize the ACL.

    ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
    aclSizeInfo.AclBytesInUse := SizeOf(ACL);

    // Call only if the DACL is not NULL.

    if pdacl <> nil then begin
      // get the file ACL size info
      if not GetAclInformation(pdacl^, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation) then
        Exit;
    end;

    // Compute the size of the new ACL.

    dwNewAclSize := aclSizeInfo.AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid(psid)) - (2 * SizeOf(DWORD));

    // Allocate memory for the new ACL.

    pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);

    if pNewAcl = nil then
      Exit;

    // Initialize the new DACL.

    if not InitializeAcl(pNewAcl^, dwNewAclSize, ACL_REVISION) then
      Exit;

    // If DACL is present, copy it to a new DACL.

    if bDaclPresent then begin
       // Copy the ACEs to the new ACL.
      if aclSizeInfo.AceCount > 0 then begin
        for i := 0 to aclSizeInfo.AceCount - 1 do begin
          // Get an ACE.
          if not GetAce(pdacl^, i, Pointer(pTempAce)) then
            Exit;

          // Add the ACE to the new ACL.
          if not AddAce(pNewAcl^, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize) then
            Exit;
        end;
      end;
    end;

    // Add the first ACE to the window station.

    pace := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD));

    if pace = nil then
      Exit;

    pace.Header.AceType := ACCESS_ALLOWED_ACE_TYPE;
    pace.Header.AceFlags := CONTAINER_INHERIT_ACE or INHERIT_ONLY_ACE or OBJECT_INHERIT_ACE;
    pace.Header.AceSize := SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD);
    pace.Mask := GENERIC_ACCESS;

    if not CopySid(GetLengthSid(psid), @pace.SidStart, psid) then
      Exit;

    if not AddAce(pNewAcl^, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize) then
      Exit;

    // Add the second ACE to the window station.

    pace.Header.AceFlags := NO_PROPAGATE_INHERIT_ACE;
    pace.Mask := WINSTA_ALL;

    if not AddAce(pNewAcl^, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize) then
      Exit;

    // Set a new DACL for the security descriptor.

    if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False) then
      Exit;

    // Set the new security descriptor for the window station.

    if not SetUserObjectSecurity(hwinsta, si, psdNew) then
      Exit;

    // Indicate success.

    Result := True;
  finally
    // Free the allocated buffers.

    if pace <> nil then
      HeapFree(GetProcessHeap, 0, pace);

    if pNewAcl <> nil then
      HeapFree(GetProcessHeap, 0, pNewAcl);

    if psd <> nil then
      HeapFree(GetProcessHeap, 0, psd);

    if psdNew <> nil then
      HeapFree(GetProcessHeap, 0, psdNew);
  end;
end;

function GetLogonSID(hToken: THandle; var ppsid: PSID): Boolean;
var
  dwLength: DWORD;
  ptg: ^TOKEN_GROUPS;
  i: integer;
begin
  Result := False;
  dwLength := 0;
  ptg := nil;

  try
    // Verify the parameter passed in is not NULL.
//    if ppsid = nil then
//      Exit;

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.

    if not GetTokenInformation(hToken, TokenGroups, ptg, 0, dwLength) then begin

      if GetLastError <> ERROR_INSUFFICIENT_BUFFER then
        Exit;

      ptg := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);

      if ptg = nil then
        Exit;

      // Get the token group information from the access token.

      if not GetTokenInformation(hToken, TokenGroups, ptg, dwLength, dwLength) then
        Exit;

      // Loop through the groups to find the logon SID.

      for i := 0 to ptg.GroupCount - 1 do begin
        if ptg.Groups[i].Attributes and SE_GROUP_LOGON_ID = SE_GROUP_LOGON_ID then begin
          // Found the logon SID; make a copy of it.

          dwLength := GetLengthSid(ptg.Groups[i].Sid);
          ppsid := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
          if ppsid = nil then
            Exit;
          if not CopySid(dwLength, ppsid, ptg.Groups[i].Sid) then begin
            HeapFree(GetProcessHeap, 0, ppsid);
            Exit;
          end;
          Break;
        end;
      end;
      Result := True;
    end;
  finally
    // Free the buffer for the token groups.
    if ptg <> nil then
      HeapFree(GetProcessHeap, 0, ptg);
  end;
end;

function AddAceToDesktop(hdesktop: HDESK; ps: PSID): Boolean;
var
  aclSizeInfo: ACL_SIZE_INFORMATION;
  bDaclExist, bDaclPresent: LongBool;
  dwNewAclSize, dwSidSize, dwSdSizeNeeded: DWORD;
  pdacl, pNewAcl: PACL;
  psd, psdNew: PSECURITY_DESCRIPTOR;
  pTempAce: PACE_HEADER;
  si: SECURITY_INFORMATION;
  i: integer;
begin
  Result := False;
  psd := nil;
  psdNew := nil;
  pNewAcl := nil;
  si := DACL_SECURITY_INFORMATION;
  dwSidSize := 0;
  try
    // Obtain the security descriptor for the desktop object.

    if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded) then begin
      if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
        psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
        if psd = nil then
          Exit;

        psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
        if psdNew = nil then
          Exit;

        dwSidSize := dwSdSizeNeeded;

        if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded) then
          Exit;
      end
      else begin
        Exit;
      end;
    end;

    // Create a new security descriptor.

    if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION) then
      Exit;

    // Obtain the DACL from the security descriptor.

    if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist) then
      Exit;

    // Initialize.

    ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
    aclSizeInfo.AclBytesInUse := SizeOf(ACL);

    // Call only if NULL DACL.

    if pdacl <> nil then begin
      // Determine the size of the ACL information.

      if not GetAclInformation(pdacl^, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation) then
        Exit;
    end;

    // Compute the size of the new ACL.

    dwNewAclSize := aclSizeInfo.AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(ps) - SizeOf(DWORD);

    // Allocate buffer for the new ACL.

    pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);

    if pNewAcl = nil then
      Exit;

    // Initialize the new ACL.

    if not InitializeAcl(pNewAcl^, dwNewAclSize, ACL_REVISION) then
      Exit;

    // If DACL is present, copy it to a new DACL.

    if bDaclPresent then begin
      // Copy the ACEs to the new ACL.
      if aclSizeInfo.AceCount > 0 then begin
        for i := 0 to aclSizeInfo.AceCount - 1 do begin
          // Get an ACE.
          if not GetAce(pdacl^, i, Pointer(pTempAce)) then
            Exit;

          // Add the ACE to the new ACL.
          if not AddAce(pNewAcl^, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize) then
            Exit;
        end;
      end;
    end;

    // Add ACE to the DACL.

    if not AddAccessAllowedAce(pNewAcl^, ACL_REVISION, DESKTOP_ALL, ps) then
      Exit;

    // Set new DACL to the new security descriptor.

    if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False) then
      Exit;

    // Set the new security descriptor for the desktop object.

    if not SetUserObjectSecurity(hdesktop, si, psdNew) then
      Exit;

    // Indicate success.

    Result := True;
  finally
    // Free buffers.

    if pNewAcl <> nil then
      HeapFree(GetProcessHeap, 0, pNewAcl);

    if psd <> nil then
      HeapFree(GetProcessHeap(), 0, psd);

    if psdNew <> nil then
      HeapFree(GetProcessHeap(), 0, psdNew);
  end;
end;

function StartInteractiveClientProcess(lpszUsername, lpszDomain, lpszPassword, lpCommandLine: PChar): Boolean;
var
  hToken: THandle;
  hdesktop: HDESK;
  hwinst, hwinstSave: HWINSTA;
  pi: PROCESS_INFORMATION;
  pS: PSID;
  si: STARTUPINFO;
begin
  Result := False;
  hdesktop := 0;
  hwinst := 0;
  hwinstSave := 0;
  pS := nil;

  try
    // Log the client on to the local computer.

    if not LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken) then
      Exit;

    // Save a handle to the caller's current window station.

    hwinstSave := GetProcessWindowStation;
    if hwinstSave = 0 then
      Exit;

    // Get a handle to the interactive window station.

    hwinst := OpenWindowStation('winsta0', False, READ_CONTROL or WRITE_DAC);

    if hwinst = 0 then
      Exit;

    // To get the correct default desktop, set the caller's
    // window station to the interactive window station.

    if not SetProcessWindowStation(hwinst) then
      Exit;

    // Get a handle to the interactive desktop.

    hdesktop := OpenDesktop('default', 0, False, READ_CONTROL or WRITE_DAC or DESKTOP_WRITEOBJECTS or DESKTOP_READOBJECTS);

    // Restore the caller's window station.

    if not SetProcessWindowStation(hwinstSave) then
      Exit;

    if hdesktop = 0 then
      Exit;

    // Get the SID for the client's logon session.

    if not GetLogonSID(hToken, pS) then
      Exit;

    // Allow logon SID full access to interactive window station.

    if not AddAceToWindowStation(hwinst, pS) then
      Exit;

    // Allow logon SID full access to interactive desktop.

    if not AddAceToDesktop(hdesktop, pS) then
      Exit;

    // Impersonate client to ensure access to executable file.

    if not ImpersonateLoggedOnUser(hToken) then
      Exit;

    // Initialize the STARTUPINFO structure.
    // Specify that the process runs in the interactive desktop.

    ZeroMemory(@si, SizeOf(STARTUPINFO));
    si.cb := SizeOf(STARTUPINFO);
    si.lpDesktop := PChar('winsta0\\\\\\\\default');

    // Launch the process in the client's logon session.

    Result := CreateProcessAsUser(hToken, nil, lpCommandLine, nil, nil, False, // handles are not inheritable
      NORMAL_PRIORITY_CLASS or CREATE_NEW_CONSOLE, nil, nil, si, pi);

    // End impersonation of client.

    RevertToSelf();

    if Result and (pi.hProcess <> INVALID_HANDLE_VALUE) then begin
      WaitForSingleObject(pi.hProcess, INFINITE);
      CloseHandle(pi.hProcess);
    end;

    if pi.hThread <> INVALID_HANDLE_VALUE then
      CloseHandle(pi.hThread);

    Result := True;
  finally

    if hwinstSave <> 0 then
      SetProcessWindowStation(hwinstSave);

    // Free the buffer for the logon SID.

    if pS <> nil then
      HeapFree(GetProcessHeap, 0, pS);

    // Close the handles to the interactive window station and desktop.

    if hwinst <> 0 then
      CloseWindowStation(hwinst);

    if hdesktop <> 0 then
      CloseDesktop(hdesktop);

    // Close the handle to the client's access token.

    if hToken <> INVALID_HANDLE_VALUE then
      CloseHandle(hToken);
  end;
end;


Вот процедурка, из которой должна запускаться прога /консоль/ (имя домена пробовал поставить не '.', а nil — тот же результат):
var
  ERR: DWORD;
begin
{
StartInteractiveClientProcess(
  lpszUsername,
  lpszDomain,
  lpszPassword,
  lpCommandLine: PChar): Boolean;
}
  if not StartInteractiveClientProcess('tst', '.', '1', 'C:\WINDOWS\system32\cmd.exe') then
    begin
      ERR := GetLastError;
      Application.MessageBox(PChar(IntToStr(ERR) + ': ' + SysErrorMessage(ERR)), 'Error', MB_ICONWARNING or MB_OK);
    end;

end;


P.S. Приношу извинения за столь длинный код, просто не нашёл ссылку на ресурс, откуда я его достал, а аттач из-под Януса не делается...


RSDN@Home v.1.1.4
ORIGIN: Для чиво нужин Арфагрофический Славарь?
http://eagle.drkb.ru/delphideveloper.png
Re: [DELPHI, API] CreateProcessAsUser
От: Eagle-XK Украина http://esoft.pp.ua
Дата: 01.03.06 22:55
Оценка:
Вопрос закрыт.


RSDN@Home v.1.1.4
ORIGIN: Восстановите меня из вчерашнего бакапа!!
http://eagle.drkb.ru/delphideveloper.png
Re[2]: [DELPHI, API] CreateProcessAsUser
От: Danchik Украина  
Дата: 02.03.06 11:22
Оценка:
Здравствуйте, Eagle-XK, Вы писали:

EX>Вопрос закрыт.


Может, все таки, запостите что была за проблема и как решилась.
Если честно, меня тоже интересует как вызвать эту процедуру. По этому, работающий код был бы совсем даже кстати.
Re[3]: [DELPHI, API] CreateProcessAsUser
От: Eagle-XK Украина http://esoft.pp.ua
Дата: 02.03.06 12:25
Оценка:
Здравствуйте, Danchik, Вы писали:

D>Может, все таки, запостите что была за проблема и как решилась.

D>Если честно, меня тоже интересует как вызвать эту процедуру. По этому, работающий код был бы совсем даже кстати.

Да, конечно же... Вот, на Исходниках мне помогли с решением. Всё оказалось намного проще, чем я себе представлял.


RSDN@Home v.1.1.4
ORIGIN: При пожаре звонить ATDP901.
http://eagle.drkb.ru/delphideveloper.png
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.