Rust中使用CryptUnprotectData解密数据出现STATUS_HEAP_CORRUPTION错误
问题描述
我正在开发一个Rust函数,接收&[u8]类型数据,调用WinAPI的CryptUnprotectData完成解密,函数定义如下:
fn decrypt(data: &[u8]) -> Result<Vec<u8>, String> {}
解密逻辑已实现,但释放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 }; use std::ptr; fn decrypt(keydpapi: &[u8]) -> Result<Vec<u8>, String> { 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); // 错误触发位置 }; Ok(decrypted_data) }
问题原因与修复方案
核心问题
- 双重释放:调用
Vec::from_raw_parts后,Rust的Vec会接管data_out.pbData指向的内存所有权,当Vec被销毁时会自动释放该内存。但手动调用LocalFree再次释放同一块内存,直接导致堆损坏。 - 分配器不匹配:
CryptUnprotectData返回的内存通过WinAPI的LocalAlloc分配,而Vec::from_raw_parts默认使用Rust全局分配器释放内存,两者分配/释放逻辑不兼容,即使不手动释放也可能引发后续内存错误。
修复方案(推荐)
放弃直接接管WinAPI内存,改为拷贝数据到Rust管理的Vec中,再手动释放原缓冲区:
// 替换原代码中decrypted_data到LocalFree的部分 let decrypted_data = unsafe { // 将WinAPI返回的内存转为不可变切片,再拷贝到Rust Vec中 std::slice::from_raw_parts(data_out.pbData, data_out.cbData as usize).to_vec() }; // 手动释放WinAPI分配的原内存 unsafe { winbase::LocalFree(data_out.pbData as minwindef::HLOCAL); } Ok(decrypted_data)
该方案优势:
- 彻底避免双重释放问题
- 确保内存由对应分配器管理,无兼容性风险
- 实现简单,无需额外依赖
额外优化建议
- 调用
CryptUnprotectData失败时,可调用winapi::um::errhandlingapi::GetLastError()获取具体错误码,替换通用错误信息,便于排查问题。 - 确保导入
std::ptr,否则ptr::null_mut()会编译报错。
内容的提问来源于stack exchange,提问作者anonymous
相关产品推荐
相关产品推荐

