Huawei Bootloader packed image format: Nim 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.

Nim source code to parse Huawei Bootloader packed image format

android_bootldr_huawei.nim

import kaitai_struct_nim_runtime
import options

type
  AndroidBootldrHuawei* = ref object of KaitaiStruct
    `metaHeader`*: AndroidBootldrHuawei_MetaHdr
    `headerExt`*: seq[byte]
    `imageHeader`*: AndroidBootldrHuawei_ImageHdr
    `parent`*: KaitaiStruct
    `rawImageHeader`*: seq[byte]
  AndroidBootldrHuawei_MetaHdr* = ref object of KaitaiStruct
    `magic`*: seq[byte]
    `version`*: AndroidBootldrHuawei_Version
    `imageVersion`*: string
    `lenMetaHeader`*: uint16
    `lenImageHeader`*: uint16
    `parent`*: AndroidBootldrHuawei
  AndroidBootldrHuawei_Version* = ref object of KaitaiStruct
    `major`*: uint16
    `minor`*: uint16
    `parent`*: AndroidBootldrHuawei_MetaHdr
  AndroidBootldrHuawei_ImageHdr* = ref object of KaitaiStruct
    `entries`*: seq[AndroidBootldrHuawei_ImageHdrEntry]
    `parent`*: AndroidBootldrHuawei
  AndroidBootldrHuawei_ImageHdrEntry* = ref object of KaitaiStruct
    `name`*: string
    `ofsBody`*: uint32
    `lenBody`*: uint32
    `parent`*: AndroidBootldrHuawei_ImageHdr
    `isUsedInst`: bool
    `isUsedInstFlag`: bool
    `bodyInst`: seq[byte]
    `bodyInstFlag`: bool

proc read*(_: typedesc[AndroidBootldrHuawei], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): AndroidBootldrHuawei
proc read*(_: typedesc[AndroidBootldrHuawei_MetaHdr], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei): AndroidBootldrHuawei_MetaHdr
proc read*(_: typedesc[AndroidBootldrHuawei_Version], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei_MetaHdr): AndroidBootldrHuawei_Version
proc read*(_: typedesc[AndroidBootldrHuawei_ImageHdr], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei): AndroidBootldrHuawei_ImageHdr
proc read*(_: typedesc[AndroidBootldrHuawei_ImageHdrEntry], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei_ImageHdr): AndroidBootldrHuawei_ImageHdrEntry

proc isUsed*(this: AndroidBootldrHuawei_ImageHdrEntry): bool
proc body*(this: AndroidBootldrHuawei_ImageHdrEntry): seq[byte]


##[
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.

@see <a href="https://android.googlesource.com/device/huawei/angler/+/673cfb9/releasetools.py">Source</a>
@see <a href="https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_format.h?h=LA.UM.6.1.1&amp;id=a68d284aee85">Source</a>
@see <a href="https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_image.c?h=LA.UM.6.1.1&amp;id=a68d284aee85">Source</a>
]##
proc read*(_: typedesc[AndroidBootldrHuawei], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): AndroidBootldrHuawei =
  template this: untyped = result
  this = new(AndroidBootldrHuawei)
  let root = if root == nil: cast[AndroidBootldrHuawei](this) else: cast[AndroidBootldrHuawei](root)
  this.io = io
  this.root = root
  this.parent = parent

  let metaHeaderExpr = AndroidBootldrHuawei_MetaHdr.read(this.io, this.root, this)
  this.metaHeader = metaHeaderExpr
  let headerExtExpr = this.io.readBytes(int((this.metaHeader.lenMetaHeader - 76)))
  this.headerExt = headerExtExpr
  let rawImageHeaderExpr = this.io.readBytes(int(this.metaHeader.lenImageHeader))
  this.rawImageHeader = rawImageHeaderExpr
  let rawImageHeaderIo = newKaitaiStream(rawImageHeaderExpr)
  let imageHeaderExpr = AndroidBootldrHuawei_ImageHdr.read(rawImageHeaderIo, this.root, this)
  this.imageHeader = imageHeaderExpr

proc fromFile*(_: typedesc[AndroidBootldrHuawei], filename: string): AndroidBootldrHuawei =
  AndroidBootldrHuawei.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[AndroidBootldrHuawei_MetaHdr], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei): AndroidBootldrHuawei_MetaHdr =
  template this: untyped = result
  this = new(AndroidBootldrHuawei_MetaHdr)
  let root = if root == nil: cast[AndroidBootldrHuawei](this) else: cast[AndroidBootldrHuawei](root)
  this.io = io
  this.root = root
  this.parent = parent

  let magicExpr = this.io.readBytes(int(4))
  this.magic = magicExpr
  let versionExpr = AndroidBootldrHuawei_Version.read(this.io, this.root, this)
  this.version = versionExpr
  let imageVersionExpr = encode(this.io.readBytes(int(64)).bytesTerminate(0, false), "ASCII")
  this.imageVersion = imageVersionExpr
  let lenMetaHeaderExpr = this.io.readU2le()
  this.lenMetaHeader = lenMetaHeaderExpr
  let lenImageHeaderExpr = this.io.readU2le()
  this.lenImageHeader = lenImageHeaderExpr

proc fromFile*(_: typedesc[AndroidBootldrHuawei_MetaHdr], filename: string): AndroidBootldrHuawei_MetaHdr =
  AndroidBootldrHuawei_MetaHdr.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[AndroidBootldrHuawei_Version], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei_MetaHdr): AndroidBootldrHuawei_Version =
  template this: untyped = result
  this = new(AndroidBootldrHuawei_Version)
  let root = if root == nil: cast[AndroidBootldrHuawei](this) else: cast[AndroidBootldrHuawei](root)
  this.io = io
  this.root = root
  this.parent = parent

  let majorExpr = this.io.readU2le()
  this.major = majorExpr
  let minorExpr = this.io.readU2le()
  this.minor = minorExpr

proc fromFile*(_: typedesc[AndroidBootldrHuawei_Version], filename: string): AndroidBootldrHuawei_Version =
  AndroidBootldrHuawei_Version.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[AndroidBootldrHuawei_ImageHdr], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei): AndroidBootldrHuawei_ImageHdr =
  template this: untyped = result
  this = new(AndroidBootldrHuawei_ImageHdr)
  let root = if root == nil: cast[AndroidBootldrHuawei](this) else: cast[AndroidBootldrHuawei](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  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.

  ]##
  block:
    var i: int
    while not this.io.isEof:
      let it = AndroidBootldrHuawei_ImageHdrEntry.read(this.io, this.root, this)
      this.entries.add(it)
      inc i

proc fromFile*(_: typedesc[AndroidBootldrHuawei_ImageHdr], filename: string): AndroidBootldrHuawei_ImageHdr =
  AndroidBootldrHuawei_ImageHdr.read(newKaitaiFileStream(filename), nil, nil)

proc read*(_: typedesc[AndroidBootldrHuawei_ImageHdrEntry], io: KaitaiStream, root: KaitaiStruct, parent: AndroidBootldrHuawei_ImageHdr): AndroidBootldrHuawei_ImageHdrEntry =
  template this: untyped = result
  this = new(AndroidBootldrHuawei_ImageHdrEntry)
  let root = if root == nil: cast[AndroidBootldrHuawei](this) else: cast[AndroidBootldrHuawei](root)
  this.io = io
  this.root = root
  this.parent = parent


  ##[
  partition name
  ]##
  let nameExpr = encode(this.io.readBytes(int(72)).bytesTerminate(0, false), "ASCII")
  this.name = nameExpr
  let ofsBodyExpr = this.io.readU4le()
  this.ofsBody = ofsBodyExpr
  let lenBodyExpr = this.io.readU4le()
  this.lenBody = lenBodyExpr

proc isUsed(this: AndroidBootldrHuawei_ImageHdrEntry): bool = 

  ##[
  @see <a href="https://source.codeaurora.org/quic/la/device/qcom/common/tree/meta_image/meta_image.c?h=LA.UM.6.1.1&amp;id=a68d284aee85#n119">Source</a>
  ]##
  if this.isUsedInstFlag:
    return this.isUsedInst
  let isUsedInstExpr = bool( ((this.ofsBody != 0) and (this.lenBody != 0)) )
  this.isUsedInst = isUsedInstExpr
  this.isUsedInstFlag = true
  return this.isUsedInst

proc body(this: AndroidBootldrHuawei_ImageHdrEntry): seq[byte] = 
  if this.bodyInstFlag:
    return this.bodyInst
  if this.isUsed:
    let io = AndroidBootldrHuawei(this.root).io
    let pos = io.pos()
    io.seek(int(this.ofsBody))
    let bodyInstExpr = io.readBytes(int(this.lenBody))
    this.bodyInst = bodyInstExpr
    io.seek(pos)
  this.bodyInstFlag = true
  return this.bodyInst

proc fromFile*(_: typedesc[AndroidBootldrHuawei_ImageHdrEntry], filename: string): AndroidBootldrHuawei_ImageHdrEntry =
  AndroidBootldrHuawei_ImageHdrEntry.read(newKaitaiFileStream(filename), nil, nil)