【问题标题】:How to launch an interactive process in Windows on Java?如何在 Java 上的 Windows 中启动交互式进程?
【发布时间】:2019-09-21 13:54:11
【问题描述】:

我需要在另一个用户桌面上以管理员权限在 Windows 中运行应用程序。

我可以使用 PsExec -i https://docs.microsoft.com/en-us/sysinternals/downloads/psexec 执行此操作,但我想在我的 Java 应用程序中执行此操作而无需额外的 exe 文件。

我以具有提升权限的管理员身份运行我的代码。

我找到了这篇文章(它描述了如何在 .net 上进行操作):

https://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite

我将文章中的代码翻译成 Java,但 advapi32.CreateProcessAsUser 返回 false 并且出现 1314 错误。有人看到我在这段代码中遗漏了什么吗?

pom 依赖项

<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>

我的代码

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 之前,在某处执行Native.setPreserveLastError(true),在调用CreateProcessAsUser 之后立即调用Native.getLastError() 以获取错误代码。
  • 我有 5 个代码。这意味着访问被拒绝,但我以管理员身份运行命令。
  • 我想我应该研究一下这个案例stackoverflow.com/questions/13290296/…也许对我有帮助。

标签: java windows winapi jna


【解决方案1】:
  1. 根据CreateProcessAsUser.hToken

代表用户的主令牌句柄。手柄必须 拥有 TOKEN_QUERYTOKEN_DUPLICATETOKEN_ASSIGN_PRIMARY 访问权限 权利。

所以,你应该 OpenProcessTokenTOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY

  1. 复制的令牌也没有足够的权限。你只指定READ_CONTROL的权限。

DuplicateTokenEx.dwDesiredAccess

要请求与现有令牌相同的访问权限,请指定零。

因此,您需要将securityLevel 设置为零。

  1. 或者直接指定TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARYDuplicateTokenEx

根据文档,CreateProcessAsUser需要两个权限:

  1. SE_INCREASE_QUOTA_NAME
  2. SE_ASSIGNPRIMARYTOKEN_NAME

对应Control Panel\All Control Panel Items\Administrative Tools\Local Security Policy\Security Settings\Local Policies\User Rights Assignment

  1. 调整进程的内存配额
  2. 替换进程级令牌

编辑:

终于找到了办法(把错误检查去掉,注意里面的cmets):

#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;
}

【讨论】:

  • 这有帮助吗?
  • 对不起,我还没有检查。一旦我测试它,我会将你的答案标记为正确。
  • 现在我收到 1314 错误。 ibm.com/support/pages/…
  • 最后:我尝试了你的建议,得到了 1314,然后我根据文章 codeproject.com/Articles/35773/… 更改了代码,得到了 1314(我找到了 MAXIMUM_ALLOWED 的等价物)。现在我在代码中有两个地方让我感到困惑。我用“???”标记了它们。
  • 对于dwCreationFlagslpEnvironment,请参见CreateProcessAsUser文档中的参数说明。 Process Creation Flags 详细介绍了所有 dwCreationFlags 及其含义。
猜你喜欢
  • 2017-06-17
  • 1970-01-01
  • 2012-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-14
相关资源
最近更新 更多