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

如何用Rust构建可注册的Windows Credential Provider DLL?

实现Rust版Windows Credential Provider DLL的可行方案

一、核心前提:确保COM接口实现正确

你已完成ICredentialProvider的POC代码,需确认以下几点:

  • 使用windows crate的#[implement(ICredentialProvider)]宏绑定接口,所有接口方法(如GetCredentialCount、GetCredentialAt等)均已正确实现
  • 类继承自IUnknown,且通过#[derive(Default)]或手动初始化实现默认构造逻辑
  • 为你的Credential Provider生成唯一CLSID(可通过uuidgen工具生成)

二、导出标准COM DLL函数

Windows Credential Provider依赖DLL导出4个核心COM函数:DllGetClassObject、DllCanUnloadNow、DllRegisterServer、DllUnregisterServer。以下是具体实现代码:

1. 实现类工厂(IClassFactory)

类工厂负责创建Credential Provider实例,需实现IClassFactory接口:

use windows::{
    core::{HRESULT, GUID, IUnknown, PCWSTR},
    Win32::{
        System::Com::{IClassFactory, CLASS_E_CLASSNOTAVAILABLE, E_NOINTERFACE, S_OK},
        System::Registry::{RegCloseKey, RegCreateKeyExW, RegDeleteKeyW, RegSetValueExW, HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE, KEY_WRITE, REG_SZ},
    },
};
use once_cell::sync::OnceCell;

// 定义你的Credential Provider类
#[implement(ICredentialProvider)]
struct MyCredentialProvider;

// 全局类工厂实例(线程安全)
static CLASS_FACTORY: OnceCell<IClassFactory> = OnceCell::new();
// 活跃对象计数(用于DllCanUnloadNow)
static mut OBJECT_COUNT: u32 = 0;

#[implement(IClassFactory)]
struct MyClassFactory;

impl IClassFactory_Impl for MyClassFactory {
    fn CreateInstance(&self, outer: Option<&IUnknown>, iid: &GUID, object: *mut *mut std::ffi::c_void) -> HRESULT {
        if outer.is_some() {
            return CLASS_E_CLASSNOTAVAILABLE;
        }

        let provider = MyCredentialProvider::default();
        let unknown: IUnknown = provider.into();
        unknown.QueryInterface(iid, object)
    }

    fn LockServer(&self, lock: bool) -> HRESULT {
        unsafe {
            if lock {
                OBJECT_COUNT += 1;
            } else {
                if OBJECT_COUNT > 0 {
                    OBJECT_COUNT -= 1;
                }
            }
        }
        S_OK
    }
}

2. 导出COM函数

添加#[no_mangle]和extern "system"标记导出函数:

#[no_mangle]
extern "system" fn DllGetClassObject(rclsid: &GUID, riid: &GUID, ppv: *mut *mut std::ffi::c_void) -> HRESULT {
    // 替换成你的Credential Provider的CLSID
    const MY_CRED_PROV_CLSID: GUID = GUID::from_u128(0x12345678_1234_5678_1234_567812345678);

    if *rclsid != MY_CRED_PROV_CLSID {
        return CLASS_E_CLASSNOTAVAILABLE;
    }

    let factory = CLASS_FACTORY.get_or_init(|| MyClassFactory.into());
    factory.QueryInterface(riid, ppv)
}

#[no_mangle]
extern "system" fn DllCanUnloadNow() -> HRESULT {
    unsafe {
        if OBJECT_COUNT == 0 && CLASS_FACTORY.get().is_none() {
            S_OK
        } else {
            windows::Win32::System::Com::S_FALSE
        }
    }
}

#[no_mangle]
extern "system" fn DllRegisterServer() -> HRESULT {
    const MY_CRED_PROV_CLSID: GUID = GUID::from_u128(0x12345678_1234_5678_1234_567812345678);
    let clsid_str = format!("{{{}}}", MY_CRED_PROV_CLSID);
    let clsid_wstr = PCWSTR::from_raw(clsid_str.encode_utf16().chain(Some(0)).collect::<Vec<_>>().as_ptr());

    // 注册CLSID到HKEY_CLASSES_ROOT
    let mut hkey = std::ptr::null_mut();
    let result = unsafe {
        RegCreateKeyExW(
            HKEY_CLASSES_ROOT,
            PCWSTR::from_raw(format!("CLSID\\{}", clsid_str).encode_utf16().chain(Some(0)).collect::<Vec<_>>().as_ptr()),
            0,
            None,
            0,
            KEY_WRITE,
            None,
            &mut hkey,
            None,
        )
    };
    if result != S_OK {
        return result;
    }

    // 设置InprocServer32指向当前DLL路径
    let mut module_path = [0u16; 256];
    unsafe {
        windows::Win32::System::LibraryLoader::GetModuleFileNameW(windows::Win32::Foundation::HMODULE(std::ptr::null_mut()), &mut module_path);
        RegSetValueExW(
            hkey,
            PCWSTR::from_raw("InprocServer32\0".as_ptr() as *const u16),
            0,
            REG_SZ,
            module_path.as_ptr() as *const u8,
            (module_path.iter().position(|&c| c == 0).unwrap_or(0) * 2) as u32,
        );
        RegCloseKey(hkey);
    }

    // 注册到Credential Providers目录
    let mut cp_hkey = std::ptr::null_mut();
    let cp_path = format!("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\Credential Providers\\{}", clsid_str);
    let result = unsafe {
        RegCreateKeyExW(
            HKEY_LOCAL_MACHINE,
            PCWSTR::from_raw(cp_path.encode_utf16().chain(Some(0)).collect::<Vec<_>>().as_ptr()),
            0,
            None,
            0,
            KEY_WRITE,
            None,
            &mut cp_hkey,
            None,
        )
    };
    if result == S_OK {
        unsafe { RegCloseKey(cp_hkey) };
    }

    S_OK
}

#[no_mangle]
extern "system" fn DllUnregisterServer() -> HRESULT {
    const MY_CRED_PROV_CLSID: GUID = GUID::from_u128(0x12345678_1234_5678_1234_567812345678);
    let clsid_str = format!("{{{}}}", MY_CRED_PROV_CLSID);

    // 删除CLSID项
    unsafe {
        RegDeleteKeyW(
            HKEY_CLASSES_ROOT,
            PCWSTR::from_raw(format!("CLSID\\{}", clsid_str).encode_utf16().chain(Some(0)).collect::<Vec<_>>().as_ptr()),
        );
        // 删除Credential Providers项
        RegDeleteKeyW(
            HKEY_LOCAL_MACHINE,
            PCWSTR::from_raw(format!("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\Credential Providers\\{}", clsid_str).encode_utf16().chain(Some(0)).collect::<Vec<_>>().as_ptr()),
        );
    }

    S_OK
}

三、配置Cargo.toml

确保编译为DLL,并添加必要依赖:

[package]
name = "my_cred_prov"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
windows = { version = "0.52", features = ["Win32_System_Com", "Win32_System_Registry", "Win32_Foundation", "Win32_System_LibraryLoader"] }
once_cell = "1.18"

四、编译与注册

  1. 编译DLL:执行cargo build --release,生成的DLL在target/release目录下
  2. 注册DLL:以管理员身份运行regsvr32.exe path/to/your/dll.dll
  3. 测试:使用Windows SDK中的CredProvTest.exe工具验证,或重启系统后查看登录界面是否加载你的Credential Provider

五、关键注意事项

  • CLSID唯一性:必须为你的Credential Provider生成唯一GUID,避免与系统或其他第三方Provider冲突
  • 线程模型:Credential Provider默认要求Apartment Threaded,类工厂的LockServer需正确维护对象计数
  • 接口方法实现:ICredentialProvider的所有方法需严格遵循Windows SDK规范,尤其是返回值和参数处理
  • 权限问题:注册DLL需要管理员权限,调试时可附加到winlogon.exe进程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:20:41