Android Boot Image: C++98/STL parsing library

File extension

img

KS implementation details

License: CC0-1.0

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.

Usage

Runtime library

All parsing code for C++98/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.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (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);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    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

C++98/STL source code to parse Android Boot Image

android_img.h

#ifndef ANDROID_IMG_H_
#define ANDROID_IMG_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>

#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 = 0, android_img_t* p__root = 0);

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 = 0, android_img_t* p__root = 0);

    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 = 0, android_img_t* p__root = 0);

    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 = 0, android_img_t* p__root = 0);

    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 = 0, android_img_t* p__root = 0);

    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;
    load_t* m_kernel;
    load_t* m_ramdisk;
    load_t* m_second;
    uint32_t m_tags_load;
    uint32_t m_page_size;
    uint32_t m_header_version;
    os_version_t* m_os_version;
    std::string m_name;
    std::string m_cmdline;
    std::string m_sha;
    std::string m_extra_cmdline;
    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:
    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; }
    load_t* ramdisk() const { return m_ramdisk; }
    load_t* second() const { return m_second; }
    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; }
    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; }
    uint32_t boot_header_size() const { return m_boot_header_size; }
    load_long_t* dtb() const { return m_dtb; }
    android_img_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // ANDROID_IMG_H_

android_img.cpp

// 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 = 0;
    m_ramdisk = 0;
    m_second = 0;
    m_os_version = 0;
    m_recovery_dtbo = 0;
    m_dtb = 0;
    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;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

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 = new load_t(m__io, this, m__root);
    m_ramdisk = new load_t(m__io, this, m__root);
    m_second = 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 = 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 = 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 = new load_long_t(m__io, this, m__root);
    }
}

android_img_t::~android_img_t() {
    _clean_up();
}

void android_img_t::_clean_up() {
    if (m_kernel) {
        delete m_kernel; m_kernel = 0;
    }
    if (m_ramdisk) {
        delete m_ramdisk; m_ramdisk = 0;
    }
    if (m_second) {
        delete m_second; m_second = 0;
    }
    if (m_os_version) {
        delete m_os_version; m_os_version = 0;
    }
    if (!n_recovery_dtbo) {
        if (m_recovery_dtbo) {
            delete m_recovery_dtbo; m_recovery_dtbo = 0;
        }
    }
    if (!n_boot_header_size) {
    }
    if (!n_dtb) {
        if (m_dtb) {
            delete m_dtb; m_dtb = 0;
        }
    }
    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;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

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;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

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;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

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;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

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;
}