如何在Java中利用JNA调用CreateProcessWithTokenW并加载用户配置
在Java中加载用户配置或调用CreateProcessWithTokenW的解决方案
一、自定义JNA接口调用CreateProcessWithTokenW
CreateProcessWithTokenW 包含在advapi32.dll中,JNA/Waffle默认未提供封装,你可以通过自定义接口直接调用:
1. 定义扩展的Advapi32接口
import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; public interface Advapi32Extended extends StdCallLibrary { Advapi32Extended INSTANCE = Native.load("advapi32", Advapi32Extended.class, W32APIOptions.DEFAULT_OPTIONS); boolean CreateProcessWithTokenW( Pointer hToken, int dwLogonFlags, String lpApplicationName, String lpCommandLine, int dwCreationFlags, Pointer lpEnvironment, String lpCurrentDirectory, Structure lpStartupInfo, Structure lpProcessInformation ); }
2. 调用示例
假设你已经通过Waffle获取到目标用户的主token(比如WindowsIdentity对象的token):
import com.sun.jna.platform.win32.Kernel32; import com.sun.jna.platform.win32.W32API; // 从Waffle获取用户主token Pointer userToken = new Pointer(windowsIdentity.getToken()); W32API.STARTUPINFO startupInfo = new W32API.STARTUPINFO(); W32API.PROCESS_INFORMATION processInfo = new W32API.PROCESS_INFORMATION(); // 调用CreateProcessWithTokenW,指定LOGON_WITH_PROFILE标志加载用户配置 boolean success = Advapi32Extended.INSTANCE.CreateProcessWithTokenW( userToken, 0x00000001, // LOGON_WITH_PROFILE:加载用户配置文件 "C:\\Windows\\notepad.exe", null, 0, Pointer.NULL, null, startupInfo, processInfo ); if (success) { // 进程创建成功后,关闭句柄释放资源 Kernel32.INSTANCE.CloseHandle(processInfo.hProcess); Kernel32.INSTANCE.CloseHandle(processInfo.hThread); } else { // 获取错误码排查问题 int errorCode = Kernel32.INSTANCE.GetLastError(); System.err.println("进程创建失败,错误码:" + errorCode); }
二、手动调用LoadUserProfile加载配置(配合CreateProcessAsUser)
如果仍需使用CreateProcessAsUser,可以先通过LoadUserProfile加载用户配置,该函数在userenv.dll中,同样需要自定义JNA接口:
1. 定义Userenv接口和PROFILEINFO结构体
import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; public interface Userenv extends StdCallLibrary { Userenv INSTANCE = Native.load("userenv", Userenv.class, W32API.DEFAULT_OPTIONS); boolean LoadUserProfile( Pointer hToken, Structure lpProfileInfo ); boolean UnloadUserProfile( Pointer hToken, Pointer hProfile ); @Structure.FieldOrder({"dwSize", "dwFlags", "lpUserName", "lpProfilePath", "lpDefaultPath", "lpServerName", "lpPolicyPath", "hProfile"}) class PROFILEINFO extends Structure { public int dwSize = size(); public int dwFlags; public String lpUserName; public String lpProfilePath; public String lpDefaultPath; public String lpServerName; public String lpPolicyPath; public Pointer hProfile; } }
2. 加载配置并创建进程
Userenv.PROFILEINFO profileInfo = new Userenv.PROFILEINFO(); profileInfo.lpUserName = "目标用户名"; profileInfo.dwFlags = 0x00000002; // PI_NOUI:不显示配置加载界面 // 加载用户配置 boolean loadSuccess = Userenv.INSTANCE.LoadUserProfile(userToken, profileInfo); if (loadSuccess) { try { // 调用CreateProcessAsUser,指定用户配置路径 boolean processSuccess = Advapi32.INSTANCE.CreateProcessAsUser( userToken, "C:\\Windows\\notepad.exe", null, null, null, false, 0, Pointer.NULL, profileInfo.lpProfilePath, // 使用加载的用户配置目录 startupInfo, processInfo ); // 处理进程创建结果 } finally { // 进程结束后必须卸载用户配置,避免资源泄漏 Userenv.INSTANCE.UnloadUserProfile(userToken, profileInfo.hProfile); } } else { int errorCode = Kernel32.INSTANCE.GetLastError(); System.err.println("加载用户配置失败,错误码:" + errorCode); }
关键注意事项
- 权限要求:调用这些API需要进程拥有
SE_INCREASE_QUOTA_NAME和SE_ASSIGNPRIMARYTOKEN_NAME权限,Waffle登录流程通常会处理,但如果遇到权限错误,需手动调整token权限。 - 主token要求:
CreateProcessWithTokenW和CreateProcessAsUser都需要用户的主token(primary token),而非模拟token,确保从Waffle获取的token类型正确。 - 资源清理:进程创建后必须关闭
PROCESS_INFORMATION中的句柄,加载的用户配置也必须调用UnloadUserProfile释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Chandru
相关产品推荐
相关产品推荐

