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

自定义Windows身份验证包触发“远程过程调用失败”登录错误排查

自定义Windows身份验证包“远程过程调用失败”问题排查与解决

问题背景

开发自定义Windows身份验证包时,在LsaApLogonUserEx2函数中调用外部DLL或REST API时触发以下问题:

  • 登录界面弹出“远程过程调用失败”错误,随后机器短时间内自动关机
  • 日志显示执行流程在调用DLL函数前终止,无异常记录
  • 替换为存根代码(如std::this_thread::sleep_for(std::chrono::seconds(10)))时,登录流程可正常完成
  • 核心需求:必须在身份验证包内通过外部DLL从外部资源获取用户密码,无法改用凭据提供方案

相关代码实现

typedef int(__stdcall* ExportHelperFnType)();

int ExportHelperFunc() {

    HMODULE helperDll = LoadLibraryW(L"path to dll.dll");

    if (helperDll) {
        TRACE("function loaded");
        ExportHelperFnType ExportHelperFn = (ExportHelperFnType)GetProcAddress(helperDll, "ExportHelperFn");
        TRACE("function loaded: %p", ExportHelperFn);

        // 函数地址已成功加载,但日志到此终止,无异常捕获
        try
        {
            int rs = ExportHelperFn();
            return rs;
        }
        catch (...)
        {
            TRACE("error");
        }

        TRACE("done ExportHelperFn with error");
    }
    return STATUS_SUCCESS;
}

extern "C" NTSTATUS
NTAPI
LsaApLogonUserEx2_Custom(IN PLSA_CLIENT_REQUEST ClientRequest,
    IN SECURITY_LOGON_TYPE LogonType,
    IN PVOID ProtocolSubmitBuffer,
    IN PVOID ClientBufferBase,
    IN ULONG SubmitBufferSize,
    OUT PVOID* ProfileBuffer,
    OUT PULONG ProfileBufferSize,
    OUT PLUID LogonId,
    OUT PNTSTATUS SubStatus,
    OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
    OUT PVOID* TokenInformation,
    OUT PUNICODE_STRING* AccountName,
    OUT PUNICODE_STRING* AuthenticatingAuthority,
    OUT PUNICODE_STRING* MachineName,
    OUT PSECPKG_PRIMARY_CRED PrimaryCredentials, /* Not supported yet */
    OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY* SupplementalCredentials)
{
    NTSTATUS result = STATUS_SUCCESS;
    try
    {
        // 此处调用出现问题
        result = ExportHelperFunc(); 
    }
    catch (...)
    {
        TRACE("Error ExportHelperFunc: %d", result);
    }

    // 调用MSV1_0原生登录函数
    result = MSV1_0LsaApLogonUserEx2(ClientRequest, LogonType, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferSize, ProfileBuffer, ProfileBufferSize, LogonId, SubStatus, TokenInformationType, TokenInformation, AccountName, AuthenticatingAuthority, MachineName, PrimaryCredentials, SupplementalCredentials);
    return result;
}

错误原因分析

  1. LSA运行上下文的严格限制
    LSA(本地安全授权)服务运行在会话0的系统级上下文,该环境:

    • 无法访问用户会话专属资源(如桌面、用户注册表项)
    • 网络访问受系统级防火墙/代理规则约束,用户态网络组件无法正常初始化
    • 线程属于关键系统线程,长时间阻塞或触发异常会触发系统看门狗机制,直接导致系统重启
  2. 外部调用的依赖兼容性问题

    • 外部DLL可能依赖仅在用户会话中存在的库或资源,加载后执行时触发内核级异常(无法被C++catch(...)捕获)
    • cpr等第三方REST库依赖用户态网络配置,在会话0中无法完成初始化,导致调用挂起或崩溃
  3. 未处理异常触发系统保护机制
    LSA是核心系统服务,其线程出现无响应或未捕获异常时,系统会启动故障恢复流程(自动重启)。C++的异常捕获无法覆盖内核级错误(如栈溢出、资源访问违规),因此日志无法记录异常细节。

可行解决方案

1. 适配会话0的外部DLL改造

  • 确保外部DLL仅依赖系统级库(如kernel32.dll、advapi32.dll),移除用户态组件依赖
  • 所有资源访问使用系统级路径(如C:\Windows\System32),避免用户目录
  • 彻底移除DLL中的GUI相关代码,会话0无桌面环境支持

2. 使用LSA兼容的网络API

  • 替换cpr库,改用WinHTTP的系统级调用,确保在会话0中正常工作:
    HINTERNET hSession = WinHttpOpen(L"CustomAuthPackage/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
    if (hSession) {
        // 后续WinHttpConnect、WinHttpOpenRequest等调用
    }
    

3. 异步调用避免阻塞LSA线程

  • 将外部调用改为异步模式,防止阻塞关键系统线程:
    1. 在LsaApLogonUserEx2_Custom中启动独立系统线程执行密码获取逻辑
    2. 线程完成后通过LsaCallAuthenticationPackage回调通知身份验证流程继续
    3. 需设置合理超时,避免触发系统看门狗

4. 强化调试与日志

  • 改用Windows事件日志替代自定义TRACE,会话0中TRACE输出可能无法被捕获
  • 启用内核调试,通过WinDbg捕获LSA服务崩溃dump,定位具体异常原因
  • 在DLL的DllMain入口添加日志,确认DLL初始化状态

5. 密码预获取缓存方案

  • 若允许,通过独立系统服务在系统启动时预获取用户密码,缓存到LSA秘密等安全存储中,身份验证包直接读取缓存,避免登录流程中发起外部调用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:33:18