使用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
相关产品推荐
相关产品推荐

