Queden claro tres cosas:
1.- La sesión del otro usuario debe existir y estar abierta.
2.- ExecuteAsUserSession debe ejecutarse como usuario System, en otro caso ejecutará la aplicación pasada como parámetro como nuestro usuario y sesión.
3.- Si se ejecuta como system pero la sesión no está abierta, la aplicación pasada como parámetro se ejecutará como system, dado que es el usuario elegido.
El código:
delphi
program ExecuteAsUserSession; uses Windows, ShellAPI; type WTS_SESSION_INFOA = record SessionId: DWORD; pWinStationName: PCHAR; State: DWORD; end; PWTS_SESSION_INFOA = ^WTS_SESSION_INFOA; PPWTS_SESSION_INFOA = ^PWTS_SESSION_INFOA; WTS_SESSION_INFO_ARRAY = array [0..0] of WTS_SESSION_INFOA; PWTS_SESSION_INFO_ARRAY = ^WTS_SESSION_INFO_ARRAY; type WTS_PROCESS_INFOA = record SessionId: DWORD; // session id ProcessId: DWORD; // process id pProcessName: LPSTR; // name of process pUserSid: PSID; // user's SID end; PWTS_PROCESS_INFOA = ^WTS_PROCESS_INFOA; PPWTS_PROCESS_INFOA = ^PWTS_PROCESS_INFOA; WTS_PROCESS_INFO_ARRAY = array [0..0] of WTS_PROCESS_INFOA; PWTS_PROCESS_INFO_ARRAY = ^WTS_PROCESS_INFO_ARRAY; function WTSEnumerateSessionsA(hServer: THandle; Reserved, Version: DWORD; ppSI: PPWTS_SESSION_INFOA; pCount: PDWORD): BOOL; stdcall external 'Wtsapi32.dll'; function WTSQuerySessionInformationW(hServer: THandle; SessionId: DWORD; WTSInfoClass: DWORD; var Buffer: pointer; var BytesReturned: DWORD): BOOL; stdcall external 'Wtsapi32.dll'; procedure WTSFreeMemory(pMemory: Pointer); stdcall external 'Wtsapi32'; function WTSQueryUserToken(SessionId: ULONG; phToken: PHANDLE ): BOOL; stdcall external 'Wtsapi32'; function CreateEnvironmentBlock(lpEnvironment: PPointer; hToken: THANDLE; bInherit: BOOL): BOOL; stdcall external 'Userenv'; function DestroyEnvironmentBlock(lpEnvironment: Pointer): BOOL; stdcall external 'Userenv'; function AttachConsole(dwProcessId: DWORD): BOOL; stdcall external 'Kernel32.dll'; const WTS_CURRENT_SERVER_HANDLE = 0; function GetUserSessionId(User: PWCHAR): integer; var pSi: PWTS_SESSION_INFOA; pSiA: PWTS_SESSION_INFO_ARRAY; Count, i: DWORD; Buff: PWCHAR; SizeBuff: DWORD; begin Result:= -1; WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, @pSi, @Count); pSiA:= PWTS_SESSION_INFO_ARRAY(pSi); // Buscamos la sesión for i:= 0 to Count-1 do begin if WTSQuerySessionInformationW(0 {WTS_CURRENT_SERVER_HANDLE}, pSiA[i].SessionId, 5 {WTSUserName}, Pointer(Buff), SizeBuff) then begin if lstrcmpiW(Buff, User) = 0 then Result:= pSiA[i].SessionId; WTSFreeMemory(Buff); end; end; WTSFreeMemory(pSi); end; function GetUserToken(User: PWCHAR): THandle; var hToken: THandle; SessionId: integer; begin hToken:= 0; Result:= 0; SessionId:= GetUserSessionId(User); if SessionId <> -1 then // Duplicamos el token del usuario de la sesión if WTSQueryUserToken(SessionId, @hToken) then DuplicateTokenEx(hToken, TOKEN_ASSIGN_PRIMARY or TOKEN_ALL_ACCESS, nil, SecurityImpersonation, TokenPrimary, Result); end; procedure HowToUse; var Help: ShortString; F: THANDLE; begin Help:= #10+#10+'ExecuteAsUserSession User CmdPath' + #10 + #0; AttachConsole(DWORD(-1)); F:= CreateFile('CONOUT$', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); WriteConsole(F, @Help[1], lstrlen(@Help[1]), PDWORD(nil)^, nil); CloseHandle(F); FreeConsole; end; //------------------------------------------------------------------------------ // Punto de entrada al programa //------------------------------------------------------------------------------ type AWCHAR = Array [0..2] of PWCHAR; PAWCHAR = ^AWCHAR; var pi: PROCESS_INFORMATION; si: STARTUPINFO; hToken: THANDLE; lpEnvironment: Pointer; Cmd: PWCHAR; Argc: integer; Argv: PAWCHAR; begin // Buscando los parámetros pasados Argv:= PAWCHAR(CommandLineToArgvW(GetCommandLineW, Argc)); // Si no hay parámetros, muestro ayuda if Argc <= 1 then begin HowToUse; exit; end; // Buscando el hToken del usuario dado hToken:= GetUserToken(Argv[1]); Cmd:= Argv[2]; // Ejecutando el proceso lpEnvironment:= nil; CreateEnvironmentBlock(@lpEnvironment, hToken, FALSE); CreateProcessAsUserW(hToken, nil, Cmd, nil, nil, FALSE, CREATE_NO_WINDOW or NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT, lpEnvironment, nil, si, pi); DestroyEnvironmentBlock(lpEnvironment); CloseHandle(hToken); LocalFree(THANDLE(Argv)); end.
Un ejemplo de uso en una consola ejecutada como administrador:
delphi
MiniSystem ExecuteAsUserSession "pruebas Notepad.exe"
Adjunto el código fuente y binario, así como la aplicación MiniSystem para poder ejecutarlo como System. Recordar que MiniSystem debe ejecutarse específicamente como administrador.
Espero que sea de utilidad.
Saludos.