I need to run application in Windows with administrator rights on another user desktop.
I could do it with PsExec -i https://learn.microsoft.com/en-us/sysinternals/downloads/psexec but I want to do it in my Java application without additional exe files.
I run my code as administrator with elevated rights.
I found this article (it describes how to do it on .net):
https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite
I translated code from article to Java but advapi32.CreateProcessAsUser returns false and I get 1314 error. Does anybody see what I missed in this code?
pom dependencies
<dependencies>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.2.0</version>
</dependency>
</dependencies>
my code
import com.sun.jna.Native;
import com.sun.jna.platform.win32.*;
public class TestWinRunSessionId {
public static void main(String[] args) {
System.out.println(System.getProperty("user.name"));
// id of the process which we use as a pointer to the target desktop (not administrator) where we will open new application from current user (administrator)
int procId = 18160;
WinNT.HANDLE hProcess = Kernel32.INSTANCE.OpenProcess(
WinNT.PROCESS_ALL_ACCESS,
false,
procId
);
System.out.println(hProcess);
WinNT.HANDLEByReference hPToken = new WinNT.HANDLEByReference();
boolean openProcessToken = Advapi32.INSTANCE.OpenProcessToken(
hProcess,
WinNT.TOKEN_DUPLICATE,
hPToken
);
if (!openProcessToken) {
Kernel32.INSTANCE.CloseHandle(hProcess);
throw new RuntimeException("1");
}
System.out.println(hPToken);
WinBase.SECURITY_ATTRIBUTES sa = new WinBase.SECURITY_ATTRIBUTES();
sa.dwLength = new WinDef.DWORD(sa.size());
WinNT.HANDLEByReference hUserTokenDup = new WinNT.HANDLEByReference();
boolean duplicateTokenEx = Advapi32.INSTANCE.DuplicateTokenEx(
hPToken.getValue(),
WinNT.TOKEN_ALL_ACCESS,
sa,
WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
WinNT.TOKEN_TYPE.TokenPrimary,
hUserTokenDup
);
if (!duplicateTokenEx) {
Kernel32.INSTANCE.CloseHandle(hProcess);
Kernel32.INSTANCE.CloseHandle(hPToken.getValue());
throw new RuntimeException("2");
}
System.out.println(hUserTokenDup);
WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
si.cb = new WinDef.DWORD(si.size());
si.lpDesktop = "winsta0\\default";
boolean result = Advapi32.INSTANCE.CreateProcessAsUser(
hUserTokenDup.getValue(), // client's access token
null, // file to execute
"C:\\Windows\\System32\\cmd.exe", // command line
sa, // pointer to process SECURITY_ATTRIBUTES
sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
WinBase.CREATE_UNICODE_ENVIRONMENT | WinBase.CREATE_NEW_CONSOLE, // creation flags ???
null, // pointer to new environment block ???
null, // name of current directory
si, // pointer to STARTUPINFO structure
new WinBase.PROCESS_INFORMATION() // receives information about new process
);
System.out.println("result: " + result);
System.out.println("error: " + Native.getLastError());
}
}
CreateProcessAsUser.hToken
:A handle to the primary token that represents a user. The handle must have the TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access rights.
So, you should OpenProcessToken
with TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
.
READ_CONTROL
.According to DuplicateTokenEx.dwDesiredAccess
:
To request the same access rights as the existing token, specify zero.
So, you need to set securityLevel
to zero.
TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
directly at DuplicateTokenEx
According to the document, CreateProcessAsUser requires two privileges:
Corresponding to Control Panel\All Control Panel Items\Administrative Tools\Local Security Policy\Security Settings\Local Policies\User Rights Assignment
:
EDIT:
Finally, I found a way to do(The error checking was removed and pay attention to the comments inside):
#include <windows.h>
#include <iostream>
#include <stdio.h>
#pragma comment(lib, "Advapi32.lib")
int main()
{
DWORD session_id = 0;
//Get a system token from System process id.
//Why? Because the following call: "SetTokenInformation" needs "the Act as part of the operating system" privilege, and local system has.
HANDLE hSys_Process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, 588);
HANDLE Sys_Token = 0;
OpenProcessToken(hSys_Process, TOKEN_QUERY| TOKEN_DUPLICATE, &Sys_Token);
CloseHandle(hSys_Process);
HANDLE Sys_Token_Dup;
if (!DuplicateTokenEx(Sys_Token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &Sys_Token_Dup))
{
printf("DuplicateTokenEx ERROR: %d\n", GetLastError());
return FALSE;
}
//Enabling Privileges: "SE_INCREASE_QUOTA_NAME" and "SE_ASSIGNPRIMARYTOKEN_NAME" for CreateProcessAsUser().
TOKEN_PRIVILEGES *tokenPrivs=(TOKEN_PRIVILEGES*)malloc(sizeof(DWORD)+2* sizeof(LUID_AND_ATTRIBUTES));
tokenPrivs->PrivilegeCount = 2;
LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &tokenPrivs->Privileges[0].Luid);
LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &tokenPrivs->Privileges[1].Luid);
tokenPrivs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tokenPrivs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(Sys_Token_Dup, FALSE, tokenPrivs, 0, (PTOKEN_PRIVILEGES)NULL, 0);
free(tokenPrivs);
//let the calling thread impersonate the local system, so that we can call SetTokenInformation().
ImpersonateLoggedOnUser(Sys_Token_Dup);
//get current process user token.
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());
HANDLE Token = 0, hTokenDup = 0;
OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &Token);
CloseHandle(hProcess);
if (!DuplicateTokenEx(Token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hTokenDup))
{
printf("DuplicateTokenEx ERROR: %d\n", GetLastError());
return FALSE;
}
//set session id to token.
if (!SetTokenInformation(hTokenDup, TokenSessionId, &session_id, sizeof(DWORD)))
{
printf("SetTokenInformation Error === %d\n", GetLastError());
return FALSE;
}
//init struct.
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
char temp[] = "winsta0\\default";
char applicationName[] = "C:\\Windows\\System32\\cmd.exe";
si.lpDesktop = temp;
PROCESS_INFORMATION procInfo;
ZeroMemory(&procInfo, sizeof(PROCESS_INFORMATION));
//will return error 5 without CREATE_BREAKAWAY_FROM_JOB
//see https://blogs.msdn.microsoft.com/alejacma/2012/03/09/createprocessasuser-fails-with-error-5-access-denied-when-using-jobs/
int dwCreationFlags = CREATE_BREAKAWAY_FROM_JOB | CREATE_NEW_CONSOLE;
BOOL result = CreateProcessAsUser(
hTokenDup,
NULL, // file to execute
applicationName, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
NULL, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&procInfo // receives information about new process
);
RevertToSelf();
return 0;
}