1use binread::{BinRead, BinReaderExt};
2
3use memflow::connector::fileio::{CloneFile, FileIoMemory};
4use memflow::prelude::v1::*;
5
6use std::fs::File;
7use std::io;
8use std::io::{Cursor, Read, Seek, SeekFrom};
9
10#[derive(Debug, BinRead)]
14#[br(magic = 0x4C69_4D45_u32)] struct LimeHeader {
16 #[br(assert(version == 1, "Unsupported LiME version: {}", version))]
18 #[allow(dead_code)]
19 version: u32,
20 s_addr: u64,
22 #[br(assert(e_addr >= s_addr, "End address can not be lower than start address"))]
24 e_addr: u64,
25 #[br(assert(reserved == [0; 8], "Unsupported LiME reserved fields values"))]
27 #[allow(dead_code)]
28 reserved: [u8; 8],
29}
30
31impl LimeHeader {
32 const HEADER_SIZE_IN_BYTES: usize = 32;
34
35 fn next_header_from_file(lime_dump: &mut File) -> Result<Option<LimeHeader>> {
49 let mut buff = [0u8; LimeHeader::HEADER_SIZE_IN_BYTES];
50
51 match lime_dump.read_exact(&mut buff) {
52 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(None),
53 Err(_) => Err(Error(ErrorOrigin::Connector, ErrorKind::UnableToReadFile)),
54 Ok(()) => {
55 let header = Cursor::new(&buff).read_le().map_err(|_| {
56 Error(ErrorOrigin::Connector, ErrorKind::UnableToReadFile)
57 .log_error("Unable to parse the LiME file.")
58 })?;
59
60 Ok(Some(header))
61 }
62 }
63 }
64
65 const fn mem_section_size(&self) -> u64 {
67 self.e_addr - self.s_addr + 1
68 }
69}
70
71#[connector(name = "lime", help_fn = "help")]
82pub fn create_connector(args: &ConnectorArgs) -> Result<FileIoMemory<CloneFile>> {
83 let mut lime_dump = File::open(
84 args.target
85 .as_ref()
86 .ok_or(
87 Error(ErrorOrigin::Connector, ErrorKind::UnableToReadFile)
88 .log_error("LiME file path not specified"),
89 )?
90 .as_ref(),
91 )
92 .map_err(|_| Error(ErrorOrigin::Connector, ErrorKind::UnableToReadFile))?;
93
94 let mut map = MemoryMap::new();
95 let mut offset = 0;
96
97 while let Some(header) = LimeHeader::next_header_from_file(&mut lime_dump)? {
98 offset += LimeHeader::HEADER_SIZE_IN_BYTES as u64;
99
100 map.push_remap(
101 header.s_addr.into(),
102 header.mem_section_size(),
103 offset.into(),
104 );
105 offset = lime_dump
106 .seek(SeekFrom::Current(header.mem_section_size() as i64))
107 .map_err(|_| {
108 Error(ErrorOrigin::Connector, ErrorKind::UnableToSeekFile)
109 .log_error("Corrupted LiME file")
110 })?;
111 }
112
113 lime_dump.seek(SeekFrom::Start(0)).map_err(|_| {
114 Error(ErrorOrigin::Connector, ErrorKind::UnableToSeekFile)
115 .log_error("Unable to seek back to the beginning of the file")
116 })?;
117 FileIoMemory::with_mem_map(lime_dump.into(), map)
118}
119
120pub fn help() -> String {
122 "\
123The `lime` connector implements the LiME file format parser.
124
125The `target` argument specifies the filename of the file to be opened.
126 "
127 .to_string()
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use std::fs;
134 use std::fs::OpenOptions;
135 use std::io::{Seek, SeekFrom, Write};
136
137 #[test]
138 fn unspecified_file_causes_error() {
139 let connector_args = ConnectorArgs::default();
140 let connector = create_connector(&connector_args);
141 assert_eq!(
142 connector.err().unwrap(),
143 Error(ErrorOrigin::Connector, ErrorKind::UnableToReadFile)
144 );
145 }
146
147 #[test]
148 fn header_parser_works() {
149 let raw_header: [u8; LimeHeader::HEADER_SIZE_IN_BYTES] = [
150 69, 77, 105, 76, 1, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 255, 255, 207, 251, 0, 0, 0, 0,
151 0, 0, 0, 0, 0, 0, 0, 0,
152 ];
153 let tmp_file_path = "./test_header.tmp";
154 let mut tmp_file = OpenOptions::new()
155 .read(true)
156 .write(true)
157 .create(true)
158 .open(tmp_file_path)
159 .unwrap();
160
161 tmp_file.write(&raw_header).unwrap();
162 tmp_file.seek(SeekFrom::Start(0)).unwrap();
163
164 let header = LimeHeader::next_header_from_file(&mut tmp_file)
165 .unwrap()
166 .unwrap();
167
168 fs::remove_file(tmp_file_path).unwrap();
169
170 assert_eq!(header.version, 1);
171 assert_eq!(header.s_addr, 0x40000000);
172 assert_eq!(header.e_addr, 0xFBD00000 - 1);
173 assert_eq!(header.reserved, [0; 8]);
174 }
175}