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

使用AdvApi跨会话启动进程X时出现0xc06d007e异常问题

问题描述

我们有一个运行在系统会话中的服务,执行以下操作启动进程'X'时出现异常:

  • 获取用户'B'的交互式登录令牌
  • 将该令牌的会话ID修改为用户'A'的会话ID
  • 使用修改后的令牌通过CreateProcessAsUser启动进程

进程启动后立即抛出错误:未知软件异常(0xc06d007e),发生在应用程序位置0x0000000076855232

以下是实现逻辑的代码:

boolean res =
            Advapi32.INSTANCE.LogonUser(
                    user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, userToken);

    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(
                    userToken.getValue(),
                    WinNT.TOKEN_ALL_ACCESS|WinNT.TOKEN_ADJUST_SESSIONID,
                    sa,
                    SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                    TOKEN_TYPE.TokenPrimary,
                    hUserTokenDup);
    if (!duplicateTokenEx) {
        Kernel32.INSTANCE.CloseHandle(userToken.getValue());
        Kernel32.INSTANCE.CloseHandle(hUserTokenDup.getValue());
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }

    boolean setTokenInfo =
            Advapi32.INSTANCE.SetTokenInformation(
                    hUserTokenDup.getValue(),
                    TOKEN_INFORMATION_CLASS.TokenSessionId,
                    new IntByReference(sessionId),
                    DWORD.SIZE);

    if (!setTokenInfo) {
        Kernel32.INSTANCE.CloseHandle(hUserTokenDup.getValue());
        Kernel32.INSTANCE.CloseHandle(userToken.getValue());
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }
    final STARTUPINFO startupInfo = new STARTUPINFO();
    startupInfo.lpDesktop = "Winsta0\Winlogon";
   // final STARTUPINFO startupInfo = new STARTUPINFO();
    final PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
    final PointerByReference ref = new PointerByReference();

    try {
        boolean createProcess =
                Advapi32.INSTANCE.CreateProcessAsUser(
                        hUserTokenDup.getValue(),
                        null,
                        cmd,
                        null,
                        null,
                        false,
                        CREATE_UNICODE_ENVIRONMENT,
                        ref.getValue(),
                        null,
                        startupInfo,
                        processInformation);

        if (!createProcess) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }

    } finally {
        Kernel32.INSTANCE.CloseHandle(hUserTokenDup.getValue());
        Kernel32.INSTANCE.CloseHandle(userToken.getValue());
    }
错误原因分析

错误码0xc06d007e对应E_UNEXPECTED,本质是进程启动时的环境、桌面会话或令牌配置出了问题,结合你的代码,主要有几个关键问题:

  • 桌面路径转义错误:Java字符串中的反斜杠必须用双反斜杠\\转义,你写的"Winsta0\Winlogon"会被解析为无效路径,导致进程无法找到正确的桌面会话上下文。
  • 令牌复制的权限与模拟级别不匹配:
    • 使用SecurityIdentification模拟级别权限过低,无法完成会话ID修改和跨会话进程启动的操作,应替换为SecurityImpersonation级别。
    • TOKEN_ALL_ACCESS权限冗余且存在安全风险,仅保留TOKEN_ADJUST_SESSIONID | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY这些必需权限即可。
  • 环境变量处理无效:你传递了一个空的PointerByReference作为环境变量参数,这会导致进程启动时无法获取目标用户的环境变量,进而引发初始化异常。
  • 令牌关闭顺序不合理:finally块中先关闭复制后的令牌再关闭原令牌,虽不直接触发当前错误,但可能导致后续资源释放异常,建议调整关闭顺序。
修复后的代码
boolean res = Advapi32.INSTANCE.LogonUser(
        user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, userToken);

if (!res) {
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

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(
        userToken.getValue(),
        WinNT.TOKEN_ADJUST_SESSIONID | WinNT.TOKEN_QUERY | WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_ASSIGN_PRIMARY,
        sa,
        SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
        TOKEN_TYPE.TokenPrimary,
        hUserTokenDup);

if (!duplicateTokenEx) {
    Kernel32.INSTANCE.CloseHandle(userToken.getValue());
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

// 修改会话ID
IntByReference sessionIdRef = new IntByReference(sessionId);
boolean setTokenInfo = Advapi32.INSTANCE.SetTokenInformation(
        hUserTokenDup.getValue(),
        TOKEN_INFORMATION_CLASS.TokenSessionId,
        sessionIdRef,
        DWORD.SIZE);

if (!setTokenInfo) {
    Kernel32.INSTANCE.CloseHandle(hUserTokenDup.getValue());
    Kernel32.INSTANCE.CloseHandle(userToken.getValue());
    throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}

final STARTUPINFO startupInfo = new STARTUPINFO();
// 修复桌面路径转义问题
startupInfo.lpDesktop = "Winsta0\\Winlogon";
final PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
PointerByReference envBlock = new PointerByReference();

try {
    // 生成目标用户的环境变量块
    if (!Advapi32.INSTANCE.CreateEnvironmentBlock(envBlock, hUserTokenDup.getValue(), false)) {
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }

    boolean createProcess = Advapi32.INSTANCE.CreateProcessAsUser(
            hUserTokenDup.getValue(),
            null,
            cmd,
            null,
            null,
            false,
            CREATE_UNICODE_ENVIRONMENT,
            envBlock.getValue(),
            null,
            startupInfo,
            processInformation);

    if (!createProcess) {
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }

} finally {
    // 调整令牌关闭顺序,先关原令牌,再关复制后的令牌
    Kernel32.INSTANCE.CloseHandle(userToken.getValue());
    Kernel32.INSTANCE.CloseHandle(hUserTokenDup.getValue());
    // 销毁环境变量块
    if (envBlock != null && envBlock.getValue() != null) {
        Advapi32.INSTANCE.DestroyEnvironmentBlock(envBlock.getValue());
    }
}
额外注意事项
  • 确保服务进程拥有SE_ASSIGNPRIMARYTOKEN_NAME和SE_INCREASE_QUOTA_NAME权限,否则CreateProcessAsUser会直接失败。
  • 验证用户'A'的会话ID是否为有效交互式会话ID,可通过WTSQueryUserToken等API获取正确的会话标识。
  • 系统会话(Session 0)无法直接显示UI,若进程需要桌面交互,必须确保目标会话是活跃的用户交互式会话。

内容的提问来源于stack exchange,提问作者Mukesh Methaniya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:35:29