Skip to main content

abootimg_oxide/
lib.rs

1//! A parser for Android boot image headers (e.g. `boot.img` or `vendor_boot.img`).
2//!
3//! This can be used to extract or patch e.g. the kernel or ramdisk.
4//!
5//! Byte array fields (`[u8; N]`) can be used as null-terminated strings.
6//!
7//! [`Header`] denotes the standard `boot.img` boot image's header with the file signature
8//! `ANDROID!`. [`VendorHeader`] denotes the `vendor_boot.img` vendor boot image's header with the
9//! file signature `VNDRBOOT`.
10//!
11//! # Examples
12//!
13//! ```no_run
14//! use std::fs::File;
15//! use abootimg_oxide::{binrw::io::BufReader, Header};
16//!
17//! let mut r = BufReader::new(File::open("boot_a.img").unwrap());
18//! let hdr = Header::parse(&mut r).unwrap();
19//! println!("{hdr:#?}");
20//!
21//! // Extract the kernel
22//! use std::io::{self, BufWriter, Read, Seek, SeekFrom};
23//!
24//! let mut w = BufWriter::new(File::create("boot_a_kernel").unwrap());
25//! let r = r.get_mut();
26//! r.seek(SeekFrom::Start(hdr.kernel_position() as u64))
27//!     .unwrap();
28//! io::copy(&mut r.take(hdr.kernel_size().into()), w.get_mut()).unwrap();
29//! ```
30//!
31//! # Features
32#![doc = document_features::document_features!()]
33//!
34//! # Note about `Seek` requirement
35//!
36//! [`binrw`](mod@binrw) requires the [`Seek` trait](binrw::io::Seek) to be implemented on readers and writers.
37//!
38//! Only the read portion of [`Header`] seeks. For other functionality, you can use the
39//! [`binrw::io::NoSeek`] adapter to be able to read and write to and from unseekable streams.
40
41#![cfg_attr(not(any(test, feature = "std")), no_std)]
42#![cfg_attr(docsrs, feature(doc_cfg))]
43
44extern crate alloc;
45
46/// Re-export for ease of use
47pub use binrw;
48
49/// **DO NOT USE**: This re-export will be removed at the next major release.
50#[deprecated(
51    since = "0.2.3",
52    note = "Will be removed at the next major release. Use `abootimg_oxide::binrw::io::BufReader` instead."
53)]
54#[doc(no_inline)]
55#[cfg(feature = "std")]
56pub use binrw::io::BufReader;
57
58mod standard;
59mod vendor;
60mod version;
61
62/// Either variants of Android boot image header.
63#[binrw::binrw]
64#[derive(Clone, Debug, PartialEq, Eq, Hash)]
65#[brw(little)]
66pub enum EitherHeader {
67    /// Standard Android boot image header, with file signature `ANDROID!`
68    Standard(
69        // TODO: ignores endian...
70        #[br(parse_with = |r, _, ()| Header::parse(r))]
71        #[bw(write_with = |hdr, w, _, ()| hdr.write(w))]
72        Header,
73    ),
74    /// Android vendor boot image header, with file signature `VNDRBOOT`
75    Vendor(VendorHeader),
76}
77
78pub use standard::{Header, HeaderV0, HeaderV0Versioned, HeaderV3};
79pub use vendor::{VendorHeader, VendorHeaderV4};
80pub use version::{OsPatch, OsVersion, OsVersionPatch};
81
82#[cfg(test)]
83mod tests {
84    use alloc::string::ToString;
85    use alloc::vec::Vec;
86    use binrw::{io::Cursor, BinRead};
87
88    use super::*;
89
90    #[track_caller]
91    fn check<T: core::fmt::Debug, E: core::fmt::Display>(
92        res: Result<T, E>,
93        target_err_msgs: &[&str],
94    ) {
95        let s = res.unwrap_err().to_string();
96        for target in target_err_msgs {
97            assert!(
98                s.contains(target),
99                "---\\\n{s}\n\\--- should contain {target:?}"
100            );
101        }
102    }
103
104    #[test]
105    fn invalid_file_signature_either() {
106        let data = b"aaaaaaaa";
107        check(
108            EitherHeader::read(&mut Cursor::new(data)),
109            &["no variants matched", "bad magic"],
110        );
111    }
112    #[test]
113    fn invalid_version_either() {
114        let mut data = Vec::new();
115        data.extend_from_slice(b"ANDROID!");
116        data.append(&mut b"aaaa".repeat(8));
117        data.extend_from_slice(&u32::MAX.to_le_bytes());
118        data.extend_from_slice(b"aaaa");
119        data.append(&mut b"a".repeat(16 + 512 + 32 + 1024));
120
121        check(
122            HeaderV0::read(&mut Cursor::new(&data)),
123            &["invalid header version"],
124        );
125    }
126    #[test]
127    fn invalid_version_direct_v0() {
128        let mut data = Vec::new();
129        data.extend_from_slice(b"ANDROID!");
130        data.append(&mut b"aaaa".repeat(8));
131        data.extend_from_slice(&3u32.to_le_bytes());
132        data.extend_from_slice(b"aaaa");
133        data.append(&mut b"a".repeat(16 + 512 + 32 + 1024));
134
135        check(
136            HeaderV0::read(&mut Cursor::new(&data)),
137            &["invalid header version"],
138        );
139    }
140    #[test]
141    fn invalid_version_direct_v3() {
142        let mut data = Vec::new();
143        data.extend_from_slice(b"ANDROID!");
144        data.append(&mut b"aaaa".repeat(4));
145        data.append(&mut b"a".repeat(16));
146        data.extend_from_slice(&0u32.to_le_bytes());
147        data.append(&mut b"a".repeat(512 + 1024));
148
149        check(
150            HeaderV3::read(&mut Cursor::new(&data)),
151            &["invalid header version"],
152        );
153    }
154
155    #[test]
156    fn invalid_size_direct_v3() {
157        let mut data = Vec::new();
158        data.extend_from_slice(b"ANDROID!");
159        data.append(&mut b"aaaa".repeat(4));
160        data.append(&mut b"a".repeat(16));
161        data.extend_from_slice(&3u32.to_le_bytes());
162        data.append(&mut b"a".repeat(512 + 1024));
163
164        check(
165            HeaderV3::read(&mut Cursor::new(&data)),
166            &["invalid header size"],
167        );
168    }
169
170    #[test]
171    fn invalid_size_either_v3() {
172        let mut data = Vec::new();
173        data.extend_from_slice(b"ANDROID!");
174        data.append(&mut b"aaaa".repeat(4));
175        data.append(&mut b"a".repeat(16));
176        data.extend_from_slice(&3u32.to_le_bytes());
177        data.append(&mut b"a".repeat(512 + 1024));
178
179        check(
180            EitherHeader::read(&mut Cursor::new(&data)),
181            &["invalid header size"],
182        );
183    }
184}