chat4n6_sqlite_forensics/
header.rs1pub const SQLITE_MAGIC: &[u8] = b"SQLite format 3\x00";
2
3pub fn is_sqlite_header(data: &[u8]) -> bool {
4 data.len() >= 16 && &data[..16] == SQLITE_MAGIC
5}
6
7#[derive(Debug)]
8pub struct DbHeader {
9 pub page_size: u32,
10 pub page_count: u32,
11 pub freelist_trunk_page: u32,
12 pub freelist_page_count: u32,
13 pub user_version: u32,
14 pub text_encoding: u32,
15}
16
17impl DbHeader {
18 pub fn parse(data: &[u8]) -> Option<Self> {
19 if !is_sqlite_header(data) || data.len() < 100 {
20 return None;
21 }
22 let page_size = {
23 let raw = u16::from_be_bytes([data[16], data[17]]) as u32;
24 if raw == 1 {
25 65536
26 } else {
27 raw
28 }
29 };
30 Some(Self {
31 page_size,
32 page_count: u32::from_be_bytes([data[28], data[29], data[30], data[31]]),
33 freelist_trunk_page: u32::from_be_bytes([data[32], data[33], data[34], data[35]]),
34 freelist_page_count: u32::from_be_bytes([data[36], data[37], data[38], data[39]]),
35 text_encoding: u32::from_be_bytes([data[56], data[57], data[58], data[59]]),
36 user_version: u32::from_be_bytes([data[60], data[61], data[62], data[63]]),
37 })
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn test_sqlite_header_magic() {
47 assert!(is_sqlite_header(b"SQLite format 3\x00some more bytes"));
48 assert!(!is_sqlite_header(b"not sqlite"));
49 }
50
51 #[test]
52 fn test_db_header_parse_valid() {
53 let mut buf = vec![0u8; 100];
55 buf[..16].copy_from_slice(b"SQLite format 3\x00");
56 buf[16] = 0x10;
58 buf[17] = 0x00;
59 let hdr = DbHeader::parse(&buf).unwrap();
60 assert_eq!(hdr.page_size, 4096);
61 }
62
63 #[test]
64 fn test_db_header_parse_invalid() {
65 assert!(DbHeader::parse(b"not sqlite").is_none());
66 assert!(DbHeader::parse(b"SQLite format 3\x00").is_none()); }
68}