You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JNA通过主令牌以其他用户启动Windows进程遇权限问题求助

用JNA通过主令牌以其他用户身份启动Windows进程的问题解决

问题概述

已完成以下操作:

  • 使用用户名、域和密码完成登录
  • 复制令牌生成主令牌,dwDesiredAccess设为33554432,不确定该值是否正确,需确认MAXIMUM_ALLOWED对应的Java Integer值
  • 尝试执行Windows命令

执行命令时遇到错误:A required privilege is not held by the client,已尝试OpenThreadToken和AdjustTokenPrivilege方法但未解决。需通过主令牌方式执行多个命令。

原代码

public static void main(String[] args) throws Exception {
    HANDLE loginToken = null;
    HANDLE primaryToken = null;
    try {
        loginToken = performLogin("username", "domain", "password");
        setThreadToken(loginToken);
        primaryToken = createPrimaryToken(loginToken, 33554432);
        invokeCommand(primaryToken);
    } catch (ProcessCreationException e) {
        e.printStackTrace();
    } finally {
        if (loginToken != null)
            Kernel32.INSTANCE.CloseHandle(loginToken);
        if (primaryToken != null)
            Kernel32.INSTANCE.CloseHandle(primaryToken);
    }
}

private static HANDLE performLogin(String username, String domain, String password) throws ProcessCreationException {
   HANDLEByReference pointer = new HANDLEByReference();
   invokeWindowsMethod("login", () -> Advapi32.INSTANCE.LogonUser(username, domain, password, WinBase.LOGON32_LOGON_NETWORK, WinBase.LOGON32_PROVIDER_DEFAULT, pointer));
    return pointer.getValue();
}

private static HANDLE createPrimaryToken(HANDLE loginToken, int desiredAccess) throws ProcessCreationException {
    System.out.println("dwDesiredAccess:" + desiredAccess);
    HANDLEByReference pointer = new HANDLEByReference();
    invokeWindowsMethod("duplicated", () -> Advapi32.INSTANCE.DuplicateTokenEx(
            loginToken,
            desiredAccess,
            null,
            SECURITY_IMPERSONATION_LEVEL.SecurityDelegation,
            TOKEN_TYPE.TokenPrimary,
            pointer
    ));
    return pointer.getValue();
}

private static void invokeCommand(HANDLE primaryToken) throws ProcessCreationException {
    STARTUPINFO si = new STARTUPINFO();
    PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
    pi.clear();

    invokeWindowsMethod("invokeCommand", () -> Advapi32.INSTANCE.CreateProcessAsUser(
            primaryToken,
            null,
            "whoami",
            null,
            null,
            true,
            1024,
            null,
            null,
            si,
            pi));
}

private static void invokeWindowsMethod(String method, Supplier<Boolean> processor) throws ProcessCreationException {
    Boolean res = processor.get();
    System.out.println(method + " : " + res);
    if (!res) {
        throw lastErrorProcessCreationException(method, Kernel32.INSTANCE.GetLastError());
    }
}

private static ProcessCreationException lastErrorProcessCreationException(String context, int errorCode) {
    return new ProcessCreationException(String.format("[%s] %s", context, Kernel32Util.formatMessageFromLastErrorCode(errorCode)));
}

关键修复点

1. MAXIMUM_ALLOWED的正确值

你设置的33554432是正确的,对应Windows定义的MAXIMUM_ALLOWED(0x02000000),这个值无需修改,直接使用WinNT.MAXIMUM_ALLOWED常量更规范。

2. 修正LogonUser的登录类型

原代码使用LOGON32_LOGON_NETWORK(3),该登录类型的令牌权限受限,无法用于创建进程。需替换为以下类型之一:

  • LOGON32_LOGON_INTERACTIVE(2):交互式登录,适合需要桌面交互的场景
  • LOGON32_LOGON_BATCH(4):批处理登录,适合后台执行命令

修改performLogin方法中的登录类型参数即可。

3. 启用当前进程的必要权限

调用CreateProcessAsUser需要当前进程持有SE_ASSIGNPRIMARYTOKEN_NAME和SE_INCREASE_QUOTA_NAME权限,需添加权限启用逻辑,提前开启这两个权限。

4. 修正CreateProcessAsUser的参数

  • 添加CREATE_UNICODE_ENVIRONMENT标志(0x00000400),确保环境变量正确处理
  • 若不需要继承句柄,将bInheritHandles设为false
  • 可指定用户的环境变量,或传入null让系统自动加载用户默认环境

完整修改后代码示例

import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.*;
import java.util.function.Supplier;

public class RunAsUser {
    public static void main(String[] args) throws Exception {
        // 启用必要权限
        enablePrivileges();

        HANDLE loginToken = null;
        HANDLE primaryToken = null;
        try {
            loginToken = performLogin("username", "domain", "password");
            primaryToken = createPrimaryToken(loginToken, WinNT.MAXIMUM_ALLOWED);
            invokeCommand(primaryToken);
        } catch (ProcessCreationException e) {
            e.printStackTrace();
        } finally {
            if (loginToken != null)
                Kernel32.INSTANCE.CloseHandle(loginToken);
            if (primaryToken != null)
                Kernel32.INSTANCE.CloseHandle(primaryToken);
        }
    }

    private static void enablePrivileges() {
        HANDLEByReference tokenRef = new HANDLEByReference();
        try {
            if (!Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
                    WinNT.TOKEN_ADJUST_PRIVILEGES | WinNT.TOKEN_QUERY, tokenRef)) {
                throw new RuntimeException("OpenProcessToken failed: " + Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()));
            }
            HANDLE token = tokenRef.getValue();

            // 启用SE_ASSIGNPRIMARYTOKEN_NAME
            adjustPrivilege(token, WinNT.SE_ASSIGNPRIMARYTOKEN_NAME, true);
            // 启用SE_INCREASE_QUOTA_NAME
            adjustPrivilege(token, WinNT.SE_INCREASE_QUOTA_NAME, true);
        } finally {
            if (tokenRef.getValue() != null) {
                Kernel32.INSTANCE.CloseHandle(tokenRef.getValue());
            }
        }
    }

    private static void adjustPrivilege(HANDLE token, String privilegeName, boolean enable) {
        LUID luid = new LUID();
        if (!Advapi32.INSTANCE.LookupPrivilegeValue(null, privilegeName, luid)) {
            throw new RuntimeException("LookupPrivilegeValue failed for " + privilegeName + ": " + Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()));
        }

        WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES();
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = luid;
        tp.Privileges[0].Attributes = enable ? WinNT.SE_PRIVILEGE_ENABLED : 0;

        if (!Advapi32.INSTANCE.AdjustTokenPrivileges(token, false, tp, tp.size(), null, null)) {
            throw new RuntimeException("AdjustTokenPrivileges failed for " + privilegeName + ": " + Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()));
        }
    }

    private static HANDLE performLogin(String username, String domain, String password) throws ProcessCreationException {
        HANDLEByReference pointer = new HANDLEByReference();
        invokeWindowsMethod("login", () -> Advapi32.INSTANCE.LogonUser(
                username,
                domain,
                password,
                WinBase.LOGON32_LOGON_INTERACTIVE, // 使用交互式登录类型
                WinBase.LOGON32_PROVIDER_DEFAULT,
                pointer));
        return pointer.getValue();
    }

    private static HANDLE createPrimaryToken(HANDLE loginToken, int desiredAccess) throws ProcessCreationException {
        System.out.println("dwDesiredAccess:" + desiredAccess);
        HANDLEByReference pointer = new HANDLEByReference();
        invokeWindowsMethod("duplicated", () -> Advapi32.INSTANCE.DuplicateTokenEx(
                loginToken,
                desiredAccess,
                null,
                SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                TOKEN_TYPE.TokenPrimary,
                pointer));
        return pointer.getValue();
    }

    private static void invokeCommand(HANDLE primaryToken) throws ProcessCreationException {
        STARTUPINFO si = new STARTUPINFO();
        PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
        pi.clear();

        int creationFlags = WinBase.CREATE_UNICODE_ENVIRONMENT;
        invokeWindowsMethod("invokeCommand", () -> Advapi32.INSTANCE.CreateProcessAsUser(
                primaryToken,
                null,
                "whoami",
                null,
                null,
                false,
                creationFlags,
                null,
                null,
                si,
                pi));
    }

    private static void invokeWindowsMethod(String method, Supplier<Boolean> processor) throws ProcessCreationException {
        Boolean res = processor.get();
        System.out.println(method + " : " + res);
        if (!res) {
            throw lastErrorProcessCreationException(method, Kernel32.INSTANCE.GetLastError());
        }
    }

    private static ProcessCreationException lastErrorProcessCreationException(String context, int errorCode) {
        return new ProcessCreationException(String.format("[%s] %s", context, Kernel32Util.formatMessageFromLastErrorCode(errorCode)));
    }

    public static class ProcessCreationException extends Exception {
        public ProcessCreationException(String message) {
            super(message);
        }
    }
}

内容的提问来源于stack exchange,提问作者Ravi Soni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 06:10:08