在 Rust 中使用 CryptUnprotectData 解密数据会导致STATUS_HEAP_CORRUPTION错误

Decrypting Data with CryptUnprotectData in Rust Leads to STATUS_HEAP_CORRUPTION Error

提问人:anonymous 提问时间:9/27/2023 最后编辑:cafce25anonymous 更新时间:9/27/2023 访问量:84

问:

我正在开发一个函数,该函数接收并使用 .下面是有问题的函数:Rust&[u8]CryptUnprotectDataWinAPI

fn decrypt(data: &[u8]) -> Result<Vec<u8>, String> {}

我已经成功实现了这个功能,它成功地使用 .DPAPI

但是,我在尝试释放传递给 的缓冲区时遇到了一个问题。我收到以下错误:CryptUnprotectData

error: process didn't exit successfully: `target\debug\main.exe` (exit code: 0xc0000374, STATUS_HEAP_CORRUPTION)

下面是该函数的代码:

use winapi::{
    um::{
        winbase,
        dpapi,
        wincrypt
    },
    shared::minwindef
};

fn decrypt(keydpapi: &[u8]) -> Result<Vec<u8>, String> {
    // https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata
    // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree
    // https://docs.rs/winapi/latest/winapi/um/dpapi/index.html
    // https://docs.rs/winapi/latest/winapi/um/winbase/fn.LocalFree.html

    let mut data_in = wincrypt::DATA_BLOB {
        cbData: keydpapi.len() as minwindef::DWORD,
        pbData: keydpapi.as_ptr() as *mut minwindef::BYTE,
    };
    let mut data_out = wincrypt::DATA_BLOB {
        cbData: 0,
        pbData: ptr::null_mut()
    };
    let result = unsafe {
        dpapi::CryptUnprotectData(
            &mut data_in,
            ptr::null_mut(),
            ptr::null_mut(),
            ptr::null_mut(),
            ptr::null_mut(),
            0,
            &mut data_out
        )
    };
    if result == 0 {
        return Err("CryptUnprotectData failed".to_string())
    };
    if data_out.pbData.is_null() {
        return Err("CryptUnprotectData returned a null pointer".to_string());
    }
    
    let decrypted_data = unsafe {
        Vec::from_raw_parts(data_out.pbData, data_out.cbData as usize, data_out.cbData as usize)
    };
    unsafe {
        winbase::LocalFree(data_out.pbData as minwindef::HLOCAL); // error occured here
    };
    Ok(decrypted_data)
}

我将不胜感激任何见解或解决方案来解决此错误。STATUS_HEAP_CORRUPTION

winapi rust 低级别 dpapi

评论

0赞 IInspectable 9/27/2023
与问题无关,请考虑使用 windows-syswindows crate。任何一个都比积极维护要完整得多。winapi

答:

4赞 Kevin Reid 9/27/2023 #1
let decrypted_data = unsafe {
    Vec::from_raw_parts(data_out.pbData, data_out.cbData as usize, data_out.cbData as usize)
};
unsafe {
    winbase::LocalFree(data_out.pbData as minwindef::HLOCAL); // error occured here
};

此代码在两个方面不正确。

  1. Vec::from_raw_parts() 获取所提供的分配指针的所有权;也就是说,它负责释放分配。如果在创建 后释放分配,则将是双重释放。Vec

    的所有权被有效地转移给,然后可以随意解除分配、重新分配或更改指针所指向的内存内容。确保调用此函数后没有其他指针使用指针。ptrVec<T>

  2. 分配必须由 Rust 全局分配器分配:

    • ptr必须已使用全局分配器进行分配,例如 alloc::alloc 函数。

每当您使用某项功能时,您都必须阅读安全要求并遵守所有这些要求。unsafe

您应该使用 std::slice::from_raw_parts 构造一个非拥有切片引用,而不是 ,然后调用该切片将数据复制到新的 .然后,您可以进行系统创建的分配。Vec::from_raw_partsto_vec()VecLocalFree

如果您不想复制数据,则必须返回拥有 Windows 分配的类型,而不是 Rust 。我不熟悉 Windows 绑定,所以也许这样的类型已经存在,但如果没有,那就编写你自己的 - 一个包含指针的结构,并实现 trait 以提供数据访问和 trait 以在不再需要它时解除分配。VecDerefDrop

评论

0赞 anonymous 9/27/2023
更改为 std::slice::from_raw_parts(...)。to_vec()解决了它。这是一个很好的机会,可以更好地理解 Rust 的所有权原则。我喜欢为从 winapi 返回的内存视图创建可用变量的想法,Rust 允许您自定义 Deref 和 Drop 的行为真是太神奇了。谢谢!