use std::io::{Cursor, Write};
#[allow(dead_code)]
pub(crate) fn create_ole_object(
attachment_bytes: &[u8],
filename: Option<&str>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut ole_data = vec![0u8; 3072]; let mut cursor = Cursor::new(&mut ole_data);
let filename_str = filename.unwrap_or("file.bin");
write_cfb_header(&mut cursor)?;
cursor.set_position(512);
write_fat_table(&mut cursor)?;
cursor.set_position(1024);
write_directory_entries(&mut cursor, attachment_bytes.len(), filename_str)?;
cursor.set_position(1536);
write_mini_fat(&mut cursor)?;
cursor.set_position(2048);
write_ministream_data(&mut cursor, attachment_bytes, filename_str)?;
Ok(ole_data)
}
fn write_cfb_header(cursor: &mut Cursor<&mut Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
cursor.write_all(&[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1])?;
cursor.write_all(&[0; 16])?;
cursor.write_all(&0x003E_u16.to_le_bytes())?;
cursor.write_all(&0x0003_u16.to_le_bytes())?;
cursor.write_all(&0xFFFE_u16.to_le_bytes())?;
cursor.write_all(&0x0009_u16.to_le_bytes())?;
cursor.write_all(&0x0006_u16.to_le_bytes())?;
cursor.write_all(&[0; 6])?;
cursor.write_all(&0_u32.to_le_bytes())?;
cursor.write_all(&1_u32.to_le_bytes())?;
cursor.write_all(&2_u32.to_le_bytes())?;
cursor.write_all(&0_u32.to_le_bytes())?;
cursor.write_all(&4096_u32.to_le_bytes())?;
cursor.write_all(&3_u32.to_le_bytes())?;
cursor.write_all(&1_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
cursor.write_all(&0_u32.to_le_bytes())?;
cursor.write_all(&1_u32.to_le_bytes())?; for _ in 1..109 {
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?; }
Ok(())
}
fn write_fat_table(cursor: &mut Cursor<&mut Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
cursor.write_all(&0xFFFFFFFD_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
cursor.write_all(&5_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
for _ in 6..128 {
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
}
Ok(())
}
fn write_ministream_data(
cursor: &mut Cursor<&mut Vec<u8>>,
attachment_bytes: &[u8],
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
cursor.write_all(&[0x00, 0x00, 0x03, 0x00, 0x0D, 0x00])?;
cursor.write_all(&[0; 58])?;
write_ole10_native_data(cursor, attachment_bytes, filename)?;
Ok(())
}
fn write_ole10_native_data(
cursor: &mut Cursor<&mut Vec<u8>>,
attachment_bytes: &[u8],
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
cursor.write_all(&(attachment_bytes.len() as u32).to_le_bytes())?;
cursor.write_all(attachment_bytes)?;
let label = format!("{filename} Document");
cursor.write_all(label.as_bytes())?;
cursor.write_all(&[0])?;
cursor.write_all(filename.as_bytes())?;
cursor.write_all(&[0])?;
cursor.write_all(&[0])?;
cursor.write_all(&[0; 4])?;
cursor.write_all(&[0; 4])?;
Ok(())
}
fn write_mini_fat(cursor: &mut Cursor<&mut Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
cursor.write_all(&2_u32.to_le_bytes())?;
cursor.write_all(&3_u32.to_le_bytes())?;
cursor.write_all(&4_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFE_u32.to_le_bytes())?;
for _ in 5..128 {
cursor.write_all(&0xFFFFFFFC_u32.to_le_bytes())?;
}
Ok(())
}
fn write_directory_entries(
cursor: &mut Cursor<&mut Vec<u8>>,
data_len: usize,
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
write_root_entry(cursor)?;
write_objinfo_entry(cursor)?;
write_ole10native_entry(cursor, data_len, filename)?;
cursor.write_all(&[0; 128])?;
Ok(())
}
fn write_root_entry(cursor: &mut Cursor<&mut Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
let name = "Root Entry";
let mut name_utf16 = Vec::new();
for ch in name.chars() {
name_utf16.extend_from_slice(&(ch as u16).to_le_bytes());
}
name_utf16.resize(64, 0); cursor.write_all(&name_utf16)?;
cursor.write_all(&22_u16.to_le_bytes())?;
cursor.write_all(&[5])?;
cursor.write_all(&[0])?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&1_u32.to_le_bytes())?;
cursor.write_all(&[0; 16])?;
cursor.write_all(&[0; 4])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&4_u32.to_le_bytes())?;
let ministream_size = 64 * 8; cursor.write_all(&(ministream_size as u64).to_le_bytes())?;
Ok(())
}
fn write_objinfo_entry(cursor: &mut Cursor<&mut Vec<u8>>) -> Result<(), Box<dyn std::error::Error>> {
let name = "ObjInfo";
let mut name_utf16 = Vec::new();
for ch in name.chars() {
name_utf16.extend_from_slice(&(ch as u16).to_le_bytes());
}
name_utf16.resize(64, 0); cursor.write_all(&name_utf16)?;
cursor.write_all(&18_u16.to_le_bytes())?;
cursor.write_all(&[2])?;
cursor.write_all(&[1])?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&2_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&[0; 16])?;
cursor.write_all(&[0; 4])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&0_u32.to_le_bytes())?;
cursor.write_all(&6_u64.to_le_bytes())?;
Ok(())
}
fn write_ole10native_entry(
cursor: &mut Cursor<&mut Vec<u8>>,
data_len: usize,
filename: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let mut name_utf16 = Vec::new();
name_utf16.extend_from_slice(&[0x01, 0x00]);
let name = "Ole10Native";
for ch in name.chars() {
name_utf16.extend_from_slice(&(ch as u16).to_le_bytes());
}
name_utf16.extend_from_slice(&[0x00, 0x00]);
name_utf16.resize(64, 0); cursor.write_all(&name_utf16)?;
cursor.write_all(&26_u16.to_le_bytes())?;
cursor.write_all(&[2])?;
cursor.write_all(&[0])?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&0xFFFFFFFF_u32.to_le_bytes())?;
cursor.write_all(&[0; 16])?;
cursor.write_all(&[0; 4])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&[0; 8])?;
cursor.write_all(&1_u32.to_le_bytes())?;
let ole10_size = calculate_ole10_size(data_len, filename);
cursor.write_all(&(ole10_size as u64).to_le_bytes())?;
Ok(())
}
fn calculate_ole10_size(data_len: usize, filename: &str) -> usize {
let label = format!("{filename} Document");
4 + data_len + label.len() + 1 + filename.len() + 1 + 1 + 4 + 4
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_ole_object() {
let test_data = b"Test OLE embedding content";
let filename = "test.txt";
let result = create_ole_object(test_data, Some(filename));
assert!(result.is_ok());
let ole_data = result.unwrap();
assert_eq!(ole_data.len(), 3072);
assert_eq!(&ole_data[0..8], &[0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1]);
let major_version = u16::from_le_bytes([ole_data[26], ole_data[27]]);
assert_eq!(major_version, 0x0003);
std::fs::write("oleObject1.bin", &ole_data).unwrap();
println!("生成的OLE文件已保存为: oleObject1.bin");
}
}