Huawei Bootloader packed image format: C++98/STL parsing library

Format of bootloader-*.img files found in factory images of certain Android devices from Huawei:

All image versions can be found in factory images at https://developers.google.com/android/images for the specific device. To avoid having to download an entire ZIP archive when you only need one file from it, install remotezip and use its command line tool to list members in the archive and then to download only the file you want.

File extension

img

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Huawei Bootloader packed image format 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_bootldr_huawei_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.meta_header() // => get meta header

C++98/STL source code to parse Huawei Bootloader packed image format

android_bootldr_huawei.h

#ifndef ANDROID_BOOTLDR_HUAWEI_H_
#define ANDROID_BOOTLDR_HUAWEI_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>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * Format of `bootloader-*.img` files found in factory images of certain Android devices from Huawei:
 * 
 * * Nexus 6P "angler": [sample][sample-angler] ([other samples][others-angler]),
 *   [releasetools.py](https://android.googlesource.com/device/huawei/angler/+/cf92cd8/releasetools.py#29)
 * 
 * [sample-angler]: https://androidfilehost.com/?fid=11410963190603870158 "bootloader-angler-angler-03.84.img"
 * [others-angler]: https://androidfilehost.com/?w=search&s=bootloader-angler&type=files
 * 
 * All image versions can be found in factory images at
 * <https://developers.google.com/android/images> for the specific device. To
 * avoid having to download an entire ZIP archive when you only need one file
 * from it, install [remotezip](https://github.com/gtsystem/python-remotezip) and
 * use its [command line
 * tool](https://github.com/gtsystem/python-remotezip#command-line-tool) to list
 * members in the archive and then to download only the file you want.
 * \sa https://android.googlesource.com/device/huawei/angler/+/673cfb9/releasetools.py Source
 * \sa https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_format.h?h=LA.UM.6.1.1&id=a68d284aee85 Source
 * \sa https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_image.c?h=LA.UM.6.1.1&id=a68d284aee85 Source
 */

class android_bootldr_huawei_t : public kaitai::kstruct {

public:
    class meta_hdr_t;
    class version_t;
    class image_hdr_t;
    class image_hdr_entry_t;

    android_bootldr_huawei_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, android_bootldr_huawei_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~android_bootldr_huawei_t();

    class meta_hdr_t : public kaitai::kstruct {

    public:

        meta_hdr_t(kaitai::kstream* p__io, android_bootldr_huawei_t* p__parent = 0, android_bootldr_huawei_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~meta_hdr_t();

    private:
        std::string m_magic;
        version_t* m_version;
        std::string m_image_version;
        uint16_t m_len_meta_header;
        uint16_t m_len_image_header;
        android_bootldr_huawei_t* m__root;
        android_bootldr_huawei_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        version_t* version() const { return m_version; }
        std::string image_version() const { return m_image_version; }
        uint16_t len_meta_header() const { return m_len_meta_header; }
        uint16_t len_image_header() const { return m_len_image_header; }
        android_bootldr_huawei_t* _root() const { return m__root; }
        android_bootldr_huawei_t* _parent() const { return m__parent; }
    };

    class version_t : public kaitai::kstruct {

    public:

        version_t(kaitai::kstream* p__io, android_bootldr_huawei_t::meta_hdr_t* p__parent = 0, android_bootldr_huawei_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~version_t();

    private:
        uint16_t m_major;
        uint16_t m_minor;
        android_bootldr_huawei_t* m__root;
        android_bootldr_huawei_t::meta_hdr_t* m__parent;

    public:
        uint16_t major() const { return m_major; }
        uint16_t minor() const { return m_minor; }
        android_bootldr_huawei_t* _root() const { return m__root; }
        android_bootldr_huawei_t::meta_hdr_t* _parent() const { return m__parent; }
    };

    class image_hdr_t : public kaitai::kstruct {

    public:

        image_hdr_t(kaitai::kstream* p__io, android_bootldr_huawei_t* p__parent = 0, android_bootldr_huawei_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~image_hdr_t();

    private:
        std::vector<image_hdr_entry_t*>* m_entries;
        android_bootldr_huawei_t* m__root;
        android_bootldr_huawei_t* m__parent;

    public:

        /**
         * The C generator program defines `img_header` as a [fixed size
         * array](https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_image.c?h=LA.UM.6.1.1&id=a68d284aee85#n42)
         * of `img_header_entry_t` structs with length `MAX_IMAGES` (which is
         * defined as `16`).
         * 
         * This means that technically there will always be 16 `image_hdr`
         * entries, the first *n* entries being used (filled with real values)
         * and the rest left unused with all bytes zero.
         * 
         * To check if an entry is used, use the `is_used` attribute.
         */
        std::vector<image_hdr_entry_t*>* entries() const { return m_entries; }
        android_bootldr_huawei_t* _root() const { return m__root; }
        android_bootldr_huawei_t* _parent() const { return m__parent; }
    };

    class image_hdr_entry_t : public kaitai::kstruct {

    public:

        image_hdr_entry_t(kaitai::kstream* p__io, android_bootldr_huawei_t::image_hdr_t* p__parent = 0, android_bootldr_huawei_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~image_hdr_entry_t();

    private:
        bool f_is_used;
        bool m_is_used;

    public:

        /**
         * \sa https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_image.c?h=LA.UM.6.1.1&id=a68d284aee85#n119 Source
         */
        bool is_used();

    private:
        bool f_body;
        std::string m_body;
        bool n_body;

    public:
        bool _is_null_body() { body(); return n_body; };

    private:

    public:
        std::string body();

    private:
        std::string m_name;
        uint32_t m_ofs_body;
        uint32_t m_len_body;
        android_bootldr_huawei_t* m__root;
        android_bootldr_huawei_t::image_hdr_t* m__parent;

    public:

        /**
         * partition name
         */
        std::string name() const { return m_name; }
        uint32_t ofs_body() const { return m_ofs_body; }
        uint32_t len_body() const { return m_len_body; }
        android_bootldr_huawei_t* _root() const { return m__root; }
        android_bootldr_huawei_t::image_hdr_t* _parent() const { return m__parent; }
    };

private:
    meta_hdr_t* m_meta_header;
    std::string m_header_ext;
    image_hdr_t* m_image_header;
    android_bootldr_huawei_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_image_header;
    kaitai::kstream* m__io__raw_image_header;

public:
    meta_hdr_t* meta_header() const { return m_meta_header; }
    std::string header_ext() const { return m_header_ext; }
    image_hdr_t* image_header() const { return m_image_header; }
    android_bootldr_huawei_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_image_header() const { return m__raw_image_header; }
    kaitai::kstream* _io__raw_image_header() const { return m__io__raw_image_header; }
};

#endif  // ANDROID_BOOTLDR_HUAWEI_H_

android_bootldr_huawei.cpp

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

#include "android_bootldr_huawei.h"
#include "kaitai/exceptions.h"

android_bootldr_huawei_t::android_bootldr_huawei_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, android_bootldr_huawei_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_meta_header = 0;
    m_image_header = 0;
    m__io__raw_image_header = 0;

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

void android_bootldr_huawei_t::_read() {
    m_meta_header = new meta_hdr_t(m__io, this, m__root);
    m_header_ext = m__io->read_bytes((meta_header()->len_meta_header() - 76));
    m__raw_image_header = m__io->read_bytes(meta_header()->len_image_header());
    m__io__raw_image_header = new kaitai::kstream(m__raw_image_header);
    m_image_header = new image_hdr_t(m__io__raw_image_header, this, m__root);
}

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

void android_bootldr_huawei_t::_clean_up() {
    if (m_meta_header) {
        delete m_meta_header; m_meta_header = 0;
    }
    if (m__io__raw_image_header) {
        delete m__io__raw_image_header; m__io__raw_image_header = 0;
    }
    if (m_image_header) {
        delete m_image_header; m_image_header = 0;
    }
}

android_bootldr_huawei_t::meta_hdr_t::meta_hdr_t(kaitai::kstream* p__io, android_bootldr_huawei_t* p__parent, android_bootldr_huawei_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_version = 0;

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

void android_bootldr_huawei_t::meta_hdr_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\x3C\xD6\x1A\xCE", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x3C\xD6\x1A\xCE", 4), magic(), _io(), std::string("/types/meta_hdr/seq/0"));
    }
    m_version = new version_t(m__io, this, m__root);
    m_image_version = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(64), 0, false), std::string("ASCII"));
    m_len_meta_header = m__io->read_u2le();
    m_len_image_header = m__io->read_u2le();
}

android_bootldr_huawei_t::meta_hdr_t::~meta_hdr_t() {
    _clean_up();
}

void android_bootldr_huawei_t::meta_hdr_t::_clean_up() {
    if (m_version) {
        delete m_version; m_version = 0;
    }
}

android_bootldr_huawei_t::version_t::version_t(kaitai::kstream* p__io, android_bootldr_huawei_t::meta_hdr_t* p__parent, android_bootldr_huawei_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

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

void android_bootldr_huawei_t::version_t::_read() {
    m_major = m__io->read_u2le();
    m_minor = m__io->read_u2le();
}

android_bootldr_huawei_t::version_t::~version_t() {
    _clean_up();
}

void android_bootldr_huawei_t::version_t::_clean_up() {
}

android_bootldr_huawei_t::image_hdr_t::image_hdr_t(kaitai::kstream* p__io, android_bootldr_huawei_t* p__parent, android_bootldr_huawei_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = 0;

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

void android_bootldr_huawei_t::image_hdr_t::_read() {
    m_entries = new std::vector<image_hdr_entry_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_entries->push_back(new image_hdr_entry_t(m__io, this, m__root));
            i++;
        }
    }
}

android_bootldr_huawei_t::image_hdr_t::~image_hdr_t() {
    _clean_up();
}

void android_bootldr_huawei_t::image_hdr_t::_clean_up() {
    if (m_entries) {
        for (std::vector<image_hdr_entry_t*>::iterator it = m_entries->begin(); it != m_entries->end(); ++it) {
            delete *it;
        }
        delete m_entries; m_entries = 0;
    }
}

android_bootldr_huawei_t::image_hdr_entry_t::image_hdr_entry_t(kaitai::kstream* p__io, android_bootldr_huawei_t::image_hdr_t* p__parent, android_bootldr_huawei_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_is_used = false;
    f_body = false;

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

void android_bootldr_huawei_t::image_hdr_entry_t::_read() {
    m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(72), 0, false), std::string("ASCII"));
    m_ofs_body = m__io->read_u4le();
    m_len_body = m__io->read_u4le();
}

android_bootldr_huawei_t::image_hdr_entry_t::~image_hdr_entry_t() {
    _clean_up();
}

void android_bootldr_huawei_t::image_hdr_entry_t::_clean_up() {
    if (f_body && !n_body) {
    }
}

bool android_bootldr_huawei_t::image_hdr_entry_t::is_used() {
    if (f_is_used)
        return m_is_used;
    m_is_used =  ((ofs_body() != 0) && (len_body() != 0)) ;
    f_is_used = true;
    return m_is_used;
}

std::string android_bootldr_huawei_t::image_hdr_entry_t::body() {
    if (f_body)
        return m_body;
    n_body = true;
    if (is_used()) {
        n_body = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek(ofs_body());
        m_body = io->read_bytes(len_body());
        io->seek(_pos);
        f_body = true;
    }
    return m_body;
}