This page hosts a formal specification of Android Boot Image using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.img", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
android_img_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.base() // => base loading address
#pragma once
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
/**
* \sa https://source.android.com/docs/core/architecture/bootloader/boot-image-header Source
*/
class android_img_t : public kaitai::kstruct {
public:
class load_t;
class load_long_t;
class size_offset_t;
class os_version_t;
android_img_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, android_img_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~android_img_t();
class load_t : public kaitai::kstruct {
public:
load_t(kaitai::kstream* p__io, android_img_t* p__parent = nullptr, android_img_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~load_t();
private:
uint32_t m_size;
uint32_t m_addr;
android_img_t* m__root;
android_img_t* m__parent;
public:
uint32_t size() const { return m_size; }
uint32_t addr() const { return m_addr; }
android_img_t* _root() const { return m__root; }
android_img_t* _parent() const { return m__parent; }
};
class load_long_t : public kaitai::kstruct {
public:
load_long_t(kaitai::kstream* p__io, android_img_t* p__parent = nullptr, android_img_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~load_long_t();
private:
uint32_t m_size;
uint64_t m_addr;
android_img_t* m__root;
android_img_t* m__parent;
public:
uint32_t size() const { return m_size; }
uint64_t addr() const { return m_addr; }
android_img_t* _root() const { return m__root; }
android_img_t* _parent() const { return m__parent; }
};
class size_offset_t : public kaitai::kstruct {
public:
size_offset_t(kaitai::kstream* p__io, android_img_t* p__parent = nullptr, android_img_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~size_offset_t();
private:
uint32_t m_size;
uint64_t m_offset;
android_img_t* m__root;
android_img_t* m__parent;
public:
uint32_t size() const { return m_size; }
uint64_t offset() const { return m_offset; }
android_img_t* _root() const { return m__root; }
android_img_t* _parent() const { return m__parent; }
};
class os_version_t : public kaitai::kstruct {
public:
os_version_t(kaitai::kstream* p__io, android_img_t* p__parent = nullptr, android_img_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~os_version_t();
private:
bool f_month;
int32_t m_month;
public:
int32_t month();
private:
bool f_patch;
int32_t m_patch;
public:
int32_t patch();
private:
bool f_year;
int32_t m_year;
public:
int32_t year();
private:
bool f_major;
int32_t m_major;
public:
int32_t major();
private:
bool f_minor;
int32_t m_minor;
public:
int32_t minor();
private:
uint32_t m_version;
android_img_t* m__root;
android_img_t* m__parent;
public:
uint32_t version() const { return m_version; }
android_img_t* _root() const { return m__root; }
android_img_t* _parent() const { return m__parent; }
};
private:
bool f_kernel_img;
std::string m_kernel_img;
public:
std::string kernel_img();
private:
bool f_tags_offset;
int32_t m_tags_offset;
public:
/**
* tags offset from base
*/
int32_t tags_offset();
private:
bool f_ramdisk_offset;
int32_t m_ramdisk_offset;
public:
/**
* ramdisk offset from base
*/
int32_t ramdisk_offset();
private:
bool f_second_offset;
int32_t m_second_offset;
public:
/**
* 2nd bootloader offset from base
*/
int32_t second_offset();
private:
bool f_kernel_offset;
int32_t m_kernel_offset;
public:
/**
* kernel offset from base
*/
int32_t kernel_offset();
private:
bool f_dtb_offset;
int32_t m_dtb_offset;
bool n_dtb_offset;
public:
bool _is_null_dtb_offset() { dtb_offset(); return n_dtb_offset; };
private:
public:
/**
* dtb offset from base
*/
int32_t dtb_offset();
private:
bool f_dtb_img;
std::string m_dtb_img;
bool n_dtb_img;
public:
bool _is_null_dtb_img() { dtb_img(); return n_dtb_img; };
private:
public:
std::string dtb_img();
private:
bool f_ramdisk_img;
std::string m_ramdisk_img;
bool n_ramdisk_img;
public:
bool _is_null_ramdisk_img() { ramdisk_img(); return n_ramdisk_img; };
private:
public:
std::string ramdisk_img();
private:
bool f_recovery_dtbo_img;
std::string m_recovery_dtbo_img;
bool n_recovery_dtbo_img;
public:
bool _is_null_recovery_dtbo_img() { recovery_dtbo_img(); return n_recovery_dtbo_img; };
private:
public:
std::string recovery_dtbo_img();
private:
bool f_second_img;
std::string m_second_img;
bool n_second_img;
public:
bool _is_null_second_img() { second_img(); return n_second_img; };
private:
public:
std::string second_img();
private:
bool f_base;
int32_t m_base;
public:
/**
* base loading address
*/
int32_t base();
private:
std::string m_magic;
std::unique_ptr<load_t> m_kernel;
std::unique_ptr<load_t> m_ramdisk;
std::unique_ptr<load_t> m_second;
uint32_t m_tags_load;
uint32_t m_page_size;
uint32_t m_header_version;
std::unique_ptr<os_version_t> m_os_version;
std::string m_name;
std::string m_cmdline;
std::string m_sha;
std::string m_extra_cmdline;
std::unique_ptr<size_offset_t> m_recovery_dtbo;
bool n_recovery_dtbo;
public:
bool _is_null_recovery_dtbo() { recovery_dtbo(); return n_recovery_dtbo; };
private:
uint32_t m_boot_header_size;
bool n_boot_header_size;
public:
bool _is_null_boot_header_size() { boot_header_size(); return n_boot_header_size; };
private:
std::unique_ptr<load_long_t> m_dtb;
bool n_dtb;
public:
bool _is_null_dtb() { dtb(); return n_dtb; };
private:
android_img_t* m__root;
kaitai::kstruct* m__parent;
public:
std::string magic() const { return m_magic; }
load_t* kernel() const { return m_kernel.get(); }
load_t* ramdisk() const { return m_ramdisk.get(); }
load_t* second() const { return m_second.get(); }
uint32_t tags_load() const { return m_tags_load; }
uint32_t page_size() const { return m_page_size; }
uint32_t header_version() const { return m_header_version; }
os_version_t* os_version() const { return m_os_version.get(); }
std::string name() const { return m_name; }
std::string cmdline() const { return m_cmdline; }
std::string sha() const { return m_sha; }
std::string extra_cmdline() const { return m_extra_cmdline; }
size_offset_t* recovery_dtbo() const { return m_recovery_dtbo.get(); }
uint32_t boot_header_size() const { return m_boot_header_size; }
load_long_t* dtb() const { return m_dtb.get(); }
android_img_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "android_img.h"
#include "kaitai/exceptions.h"
android_img_t::android_img_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, android_img_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_kernel = nullptr;
m_ramdisk = nullptr;
m_second = nullptr;
m_os_version = nullptr;
m_recovery_dtbo = nullptr;
m_dtb = nullptr;
f_kernel_img = false;
f_tags_offset = false;
f_ramdisk_offset = false;
f_second_offset = false;
f_kernel_offset = false;
f_dtb_offset = false;
f_dtb_img = false;
f_ramdisk_img = false;
f_recovery_dtbo_img = false;
f_second_img = false;
f_base = false;
_read();
}
void android_img_t::_read() {
m_magic = m__io->read_bytes(8);
if (!(magic() == std::string("\x41\x4E\x44\x52\x4F\x49\x44\x21", 8))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x41\x4E\x44\x52\x4F\x49\x44\x21", 8), magic(), _io(), std::string("/seq/0"));
}
m_kernel = std::unique_ptr<load_t>(new load_t(m__io, this, m__root));
m_ramdisk = std::unique_ptr<load_t>(new load_t(m__io, this, m__root));
m_second = std::unique_ptr<load_t>(new load_t(m__io, this, m__root));
m_tags_load = m__io->read_u4le();
m_page_size = m__io->read_u4le();
m_header_version = m__io->read_u4le();
m_os_version = std::unique_ptr<os_version_t>(new os_version_t(m__io, this, m__root));
m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(16), 0, false), std::string("ASCII"));
m_cmdline = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(512), 0, false), std::string("ASCII"));
m_sha = m__io->read_bytes(32);
m_extra_cmdline = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(1024), 0, false), std::string("ASCII"));
n_recovery_dtbo = true;
if (header_version() > 0) {
n_recovery_dtbo = false;
m_recovery_dtbo = std::unique_ptr<size_offset_t>(new size_offset_t(m__io, this, m__root));
}
n_boot_header_size = true;
if (header_version() > 0) {
n_boot_header_size = false;
m_boot_header_size = m__io->read_u4le();
}
n_dtb = true;
if (header_version() > 1) {
n_dtb = false;
m_dtb = std::unique_ptr<load_long_t>(new load_long_t(m__io, this, m__root));
}
}
android_img_t::~android_img_t() {
_clean_up();
}
void android_img_t::_clean_up() {
if (!n_recovery_dtbo) {
}
if (!n_boot_header_size) {
}
if (!n_dtb) {
}
if (f_kernel_img) {
}
if (f_dtb_img && !n_dtb_img) {
}
if (f_ramdisk_img && !n_ramdisk_img) {
}
if (f_recovery_dtbo_img && !n_recovery_dtbo_img) {
}
if (f_second_img && !n_second_img) {
}
}
android_img_t::load_t::load_t(kaitai::kstream* p__io, android_img_t* p__parent, android_img_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void android_img_t::load_t::_read() {
m_size = m__io->read_u4le();
m_addr = m__io->read_u4le();
}
android_img_t::load_t::~load_t() {
_clean_up();
}
void android_img_t::load_t::_clean_up() {
}
android_img_t::load_long_t::load_long_t(kaitai::kstream* p__io, android_img_t* p__parent, android_img_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void android_img_t::load_long_t::_read() {
m_size = m__io->read_u4le();
m_addr = m__io->read_u8le();
}
android_img_t::load_long_t::~load_long_t() {
_clean_up();
}
void android_img_t::load_long_t::_clean_up() {
}
android_img_t::size_offset_t::size_offset_t(kaitai::kstream* p__io, android_img_t* p__parent, android_img_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
_read();
}
void android_img_t::size_offset_t::_read() {
m_size = m__io->read_u4le();
m_offset = m__io->read_u8le();
}
android_img_t::size_offset_t::~size_offset_t() {
_clean_up();
}
void android_img_t::size_offset_t::_clean_up() {
}
android_img_t::os_version_t::os_version_t(kaitai::kstream* p__io, android_img_t* p__parent, android_img_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_month = false;
f_patch = false;
f_year = false;
f_major = false;
f_minor = false;
_read();
}
void android_img_t::os_version_t::_read() {
m_version = m__io->read_u4le();
}
android_img_t::os_version_t::~os_version_t() {
_clean_up();
}
void android_img_t::os_version_t::_clean_up() {
}
int32_t android_img_t::os_version_t::month() {
if (f_month)
return m_month;
m_month = (version() & 15);
f_month = true;
return m_month;
}
int32_t android_img_t::os_version_t::patch() {
if (f_patch)
return m_patch;
m_patch = ((version() >> 11) & 127);
f_patch = true;
return m_patch;
}
int32_t android_img_t::os_version_t::year() {
if (f_year)
return m_year;
m_year = (((version() >> 4) & 127) + 2000);
f_year = true;
return m_year;
}
int32_t android_img_t::os_version_t::major() {
if (f_major)
return m_major;
m_major = ((version() >> 25) & 127);
f_major = true;
return m_major;
}
int32_t android_img_t::os_version_t::minor() {
if (f_minor)
return m_minor;
m_minor = ((version() >> 18) & 127);
f_minor = true;
return m_minor;
}
std::string android_img_t::kernel_img() {
if (f_kernel_img)
return m_kernel_img;
std::streampos _pos = m__io->pos();
m__io->seek(page_size());
m_kernel_img = m__io->read_bytes(kernel()->size());
m__io->seek(_pos);
f_kernel_img = true;
return m_kernel_img;
}
int32_t android_img_t::tags_offset() {
if (f_tags_offset)
return m_tags_offset;
m_tags_offset = (tags_load() - base());
f_tags_offset = true;
return m_tags_offset;
}
int32_t android_img_t::ramdisk_offset() {
if (f_ramdisk_offset)
return m_ramdisk_offset;
m_ramdisk_offset = ((ramdisk()->addr() > 0) ? ((ramdisk()->addr() - base())) : (0));
f_ramdisk_offset = true;
return m_ramdisk_offset;
}
int32_t android_img_t::second_offset() {
if (f_second_offset)
return m_second_offset;
m_second_offset = ((second()->addr() > 0) ? ((second()->addr() - base())) : (0));
f_second_offset = true;
return m_second_offset;
}
int32_t android_img_t::kernel_offset() {
if (f_kernel_offset)
return m_kernel_offset;
m_kernel_offset = (kernel()->addr() - base());
f_kernel_offset = true;
return m_kernel_offset;
}
int32_t android_img_t::dtb_offset() {
if (f_dtb_offset)
return m_dtb_offset;
n_dtb_offset = true;
if (header_version() > 1) {
n_dtb_offset = false;
m_dtb_offset = ((dtb()->addr() > 0) ? ((dtb()->addr() - base())) : (0));
}
f_dtb_offset = true;
return m_dtb_offset;
}
std::string android_img_t::dtb_img() {
if (f_dtb_img)
return m_dtb_img;
n_dtb_img = true;
if ( ((header_version() > 1) && (dtb()->size() > 0)) ) {
n_dtb_img = false;
std::streampos _pos = m__io->pos();
m__io->seek(((((((((page_size() + kernel()->size()) + ramdisk()->size()) + second()->size()) + recovery_dtbo()->size()) + page_size()) - 1) / page_size()) * page_size()));
m_dtb_img = m__io->read_bytes(dtb()->size());
m__io->seek(_pos);
f_dtb_img = true;
}
return m_dtb_img;
}
std::string android_img_t::ramdisk_img() {
if (f_ramdisk_img)
return m_ramdisk_img;
n_ramdisk_img = true;
if (ramdisk()->size() > 0) {
n_ramdisk_img = false;
std::streampos _pos = m__io->pos();
m__io->seek((((((page_size() + kernel()->size()) + page_size()) - 1) / page_size()) * page_size()));
m_ramdisk_img = m__io->read_bytes(ramdisk()->size());
m__io->seek(_pos);
f_ramdisk_img = true;
}
return m_ramdisk_img;
}
std::string android_img_t::recovery_dtbo_img() {
if (f_recovery_dtbo_img)
return m_recovery_dtbo_img;
n_recovery_dtbo_img = true;
if ( ((header_version() > 0) && (recovery_dtbo()->size() > 0)) ) {
n_recovery_dtbo_img = false;
std::streampos _pos = m__io->pos();
m__io->seek(recovery_dtbo()->offset());
m_recovery_dtbo_img = m__io->read_bytes(recovery_dtbo()->size());
m__io->seek(_pos);
f_recovery_dtbo_img = true;
}
return m_recovery_dtbo_img;
}
std::string android_img_t::second_img() {
if (f_second_img)
return m_second_img;
n_second_img = true;
if (second()->size() > 0) {
n_second_img = false;
std::streampos _pos = m__io->pos();
m__io->seek(((((((page_size() + kernel()->size()) + ramdisk()->size()) + page_size()) - 1) / page_size()) * page_size()));
m_second_img = m__io->read_bytes(second()->size());
m__io->seek(_pos);
f_second_img = true;
}
return m_second_img;
}
int32_t android_img_t::base() {
if (f_base)
return m_base;
m_base = (kernel()->addr() - 32768);
f_base = true;
return m_base;
}