Rust调用Windows CryptoUnprotectData接口解密异常求助:返回成功但结果数组全为零
解决CryptUnprotectData返回成功但解密结果全零的问题
你的问题出在对CryptUnprotectData输出参数CRYPTOAPI_BLOB的处理方式上。这个API不需要你提前分配输出缓冲区,它会自行分配内存来存储解密后的数据,并通过pout参数返回指向这块内存的指针和实际数据大小。你提前预分配的decrypted向量和API分配的内存完全无关,所以才会看到全零的结果。
问题分析
当你手动初始化p_data_out的cb_data和pb_data时,CryptUnprotectData会忽略这些初始值——它在成功调用后会覆盖p_data_out的pb_data为新分配的内存地址,cb_data为解密后数据的真实长度。你之前的代码只是读取了自己预分配的向量,自然看不到正确结果。
修复后的代码
mod bindings { ::windows::include_bindings!(); } use std::{convert::TryFrom, ptr}; use bindings::{ windows::win32::security::CryptUnprotectData, windows::win32::security::CRYPTOAPI_BLOB, windows::win32::memory_management::LocalFree, // 引入内存释放函数 }; // Powershell代码用于生成令牌 // $pw = read-host "Enter Token" -AsSecureString // ConvertFrom-SecureString $pw fn main() -> windows::Result<()> { // 加密字符串为'foobar' let encrypted_token = "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000c336dca1c99b7d40ae3f797c2b5d2951000000000200000000001066000000010000200000007a87d6ac2fc8037bef45e3dbcb0b652432a22a9b48fc5fa3e4fcfd9aaf922949000000000e8000000002000020000000eeaa76a44b6cd5da837f4b0f7040de8e2795ed846f8abe2c7f2d2365d00cf89c1000000069fcaa7fa475178d623f4adab1b08ac4400000008af807014cba53ed2f1e7b8a54c6ad89ff57f0ee3d8c51ecd8c5b48e99b58d0e738c9fae9fc41b4280938865a047f2724106d34313c88a0f3852d5ba9d75abfd"; let mut et_bytes = hex::decode(encrypted_token).unwrap(); let size = u32::try_from(et_bytes.len()).unwrap(); let mut p_data_in = CRYPTOAPI_BLOB { cb_data: size, pb_data: et_bytes.as_mut_ptr(), }; // 初始化输出BLOB为全零,让API自行分配内存 let mut p_data_out = CRYPTOAPI_BLOB { cb_data: 0, pb_data: ptr::null_mut(), }; unsafe { let result = CryptUnprotectData( &mut p_data_in, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), 0, &mut p_data_out ); if result.as_bool() { // 从API分配的内存中复制数据到Vec let decrypted_bytes = std::slice::from_raw_parts(p_data_out.pb_data, p_data_out.cb_data as usize).to_vec(); // 转换为字符串(假设解密后是UTF-8编码) let decrypted_str = String::from_utf8(decrypted_bytes).unwrap(); println!("解密结果: {}", decrypted_str); // 释放API分配的内存,避免泄漏 LocalFree(p_data_out.pb_data as _); } else { println!("解密失败"); } } Ok(()) }
关键改动说明
- 初始化输出BLOB:将
p_data_out的cb_data设为0,pb_data设为ptr::null_mut(),明确告诉API需要它来分配内存。 - 读取解密结果:调用成功后,通过
p_data_out.pb_data和p_data_out.cb_data获取解密数据的指针和长度,用std::slice::from_raw_parts创建切片后复制到Vec中。 - 释放内存:必须调用
LocalFree释放API分配的内存,否则会造成内存泄漏,需要提前引入对应的绑定。
这样修改后,你就能正确获取到解密后的"foobar"字符串了。
内容的提问来源于stack exchange,提问作者Dragoon
相关产品推荐
相关产品推荐

