Rust FFI with windows CryptoUnprotectData



我试图通过简单的东西开始学习FFI(并具有实际用途),但这似乎不起作用:

mod bindings {
::windows::include_bindings!();
}
use std::{convert::TryFrom, ptr};

use bindings::{
windows::win32::security::CryptUnprotectData,
windows::win32::security::CRYPTOAPI_BLOB
};

// Powershell code to generate the token
// $pw = read-host "Enter Token" -AsSecureString
// ConvertFrom-SecureString $pw

fn main() -> windows::Result<()> {
// The encrypted string is '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 decrypted = vec![0u8; et_bytes.len()];
let dt_bytes = &mut decrypted;
let mut p_data_in = CRYPTOAPI_BLOB {
cb_data: size,
pb_data: et_bytes.as_mut_ptr(),
};
let mut p_data_out = CRYPTOAPI_BLOB {
cb_data: size,
pb_data: dt_bytes.as_mut_ptr(),
};
let pin = &mut p_data_in;
let pout = &mut p_data_out;

unsafe {
let result = CryptUnprotectData(
pin,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
pout
);
println!("{:?}, {:?}", dt_bytes, result);
}

Ok(())
}

基本上它返回全零数组,但CryptUnprotectData的结果返回1,根据文档意味着成功:https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptunprotectdata

我已经通过尝试篡改十六进制字符串从而破坏加密数据来验证这一点,这导致它返回0。我不确定它是否写入了错误的位置或其他什么,但大概成功条件意味着它写入了某个地方。

CryptUnprotectData API为您分配输出缓冲区。它不会写入您提供的缓冲区。这就是为什么无论API调用的结果如何,您都可以获得原始数据。

相反,您需要传入一个(默认初始化的)CRYPTOAPI_BLOB结构,并观察API传回的值,类似以下操作:
fn main() -> windows::Result<()> {
// The encrypted string is '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(),
};
// Default-initialze; don't allocate any memory
let mut p_data_out = CRYPTOAPI_BLOB::default();
let pin = &mut p_data_in;
let pout = &mut p_data_out;

unsafe {
let result = CryptUnprotectData(
pin,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0,
pout
);
// Probably safe to ignore `result`
if !p_data_out.pb_data.is_null() {
// Construct a slice from the returned data
let output = from_raw_parts(p_data_out.pb_data, p_data_out.cb_data as _);
println!("{:?}", output);
// Cleanup
LocalFree(p_data_out.pb_data as _);
}

Ok(())
}

为我产生以下输出:

[102, 0, 111, 0, 111, 0, 98, 0, 97, 0, 114, 0]

foobar的UTF-16LE编码。


注意,您需要生成并导入windows::win32::system_services::LocalFree来执行清理。

相关内容

  • 没有找到相关文章

最新更新