Skip to main content

mbr_forensic/
boot_code.rs

1//! Boot code identification by fingerprinting the first 446 bytes of the MBR.
2
3/// Identity of the boot code in the first 446 bytes of the MBR.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum BootCodeId {
6    /// Windows Vista / Server 2008 MBR boot code.
7    WindowsVista,
8    /// Windows 7 / Server 2008 R2 and later MBR boot code.
9    Windows7Plus,
10    /// GRUB Legacy (stage1).
11    GrubLegacy,
12    /// GRUB 2 boot code.
13    Grub2,
14    /// Syslinux / EXTLINUX MBR.
15    Syslinux,
16    /// All 446 bytes are zero — likely wiped or freshly zeroed.
17    AllZeros,
18    /// All 446 bytes are `0xFF` — factory-erased flash or deliberate wipe.
19    AllOnes,
20    /// Unrecognised boot code.
21    Unknown,
22}
23
24// ── Fingerprint patterns ──────────────────────────────────────────────────────
25//
26// Each entry is (offset_in_boot_code, expected_bytes).  All conditions for an
27// entry must match for it to be selected.
28
29const WINDOWS_VISTA_SIG: &[(usize, &[u8])] = &[
30    (0, &[0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C]), // xor ax,ax; mov ss,ax; mov sp,7C00h
31    (424, b"BOOTMGR"),                                // Vista bootmgr string
32];
33
34const WINDOWS7_SIG: &[(usize, &[u8])] = &[
35    (0, &[0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C]),
36    (418, b"BOOTMGR"),
37];
38
39const GRUB2_SIG: &[(usize, &[u8])] = &[
40    (0, &[0xEB, 0x63, 0x90]), // short jmp + nop (GRUB 2 format: JMP +0x65)
41                              // GRUB 2's boot.img starts with EB 63; the value varies by version so we
42                              // accept the common range.
43];
44
45const GRUB_LEGACY_SIG: &[(usize, &[u8])] = &[
46    (0, &[0xEB, 0x48, 0x90]), // GRUB Legacy stage1 JMP
47];
48
49const SYSLINUX_SIG: &[(usize, &[u8])] = &[(3, b"SYSLINUX")];
50
51/// Identify the boot code occupying `code[0..446]`.
52#[must_use]
53pub fn identify(code: &[u8; 446]) -> BootCodeId {
54    if code.iter().all(|&b| b == 0x00) {
55        return BootCodeId::AllZeros;
56    }
57    if code.iter().all(|&b| b == 0xFF) {
58        return BootCodeId::AllOnes;
59    }
60    if matches_all(code, WINDOWS7_SIG) {
61        return BootCodeId::Windows7Plus;
62    }
63    if matches_all(code, WINDOWS_VISTA_SIG) {
64        return BootCodeId::WindowsVista;
65    }
66    if matches_all(code, SYSLINUX_SIG) {
67        return BootCodeId::Syslinux;
68    }
69    if matches_all(code, GRUB_LEGACY_SIG) {
70        return BootCodeId::GrubLegacy;
71    }
72    if matches_all(code, GRUB2_SIG) {
73        return BootCodeId::Grub2;
74    }
75    BootCodeId::Unknown
76}
77
78fn matches_all(code: &[u8; 446], sigs: &[(usize, &[u8])]) -> bool {
79    sigs.iter().all(|(offset, pattern)| {
80        let end = offset + pattern.len();
81        end <= code.len() && &code[*offset..end] == *pattern
82    })
83}