summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGravatar Visual Ehrmanntraut2025-09-26 18:42:11 +0200
committerGravatar Nikias Bassen2025-09-26 18:42:11 +0200
commit4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a (patch)
tree9771777090ef06e42ec61c0248c95dc51dbb9adc /src
parent1aea4df8c83053fcd4ef8826c19c0ed58aec323a (diff)
downloadidevicerestore-4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a.tar.gz
idevicerestore-4cf940b9680b94f8d2f1dbd573f1eb2d56574b0a.tar.bz2
Add support for Mav25 baseband firmware (iPhone 17 family)
Co-authored-by: Nikias Bassen <nikias@gmx.li>
Diffstat (limited to 'src')
-rw-r--r--src/mbn.c490
-rw-r--r--src/mbn.h85
-rw-r--r--src/restore.c73
3 files changed, 484 insertions, 164 deletions
diff --git a/src/mbn.c b/src/mbn.c
index 409d9eb..2ba5e65 100644
--- a/src/mbn.c
+++ b/src/mbn.c
@@ -1,9 +1,10 @@
/*
* mbn.c
- * support for .mbn file format (found in .bbfw files)
+ * support for Qualcomm MBN (Modem Binary) formats
*
* Copyright (c) 2012 Martin Szulecki. All Rights Reserved.
* Copyright (c) 2012 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2025 Visual Ehrmanntraut <visual@chefkiss.dev>. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -25,67 +26,456 @@
#include "mbn.h"
#include "common.h"
-mbn_file* mbn_parse(const void* data, size_t size)
+#define MBN_V1_MAGIC "\x0A\x00\x00\x00"
+#define MBN_V1_MAGIC_SIZE 4
+
+#pragma pack(push, 1)
+typedef struct {
+ uint32_t type; // the signed .mbn files have 0xA as value.
+ uint32_t unk_0x04;
+ uint32_t unk_0x08;
+ uint32_t unk_0x0c;
+ uint32_t data_size; // data_size = total_size - sizeof(mbn_header)
+ uint32_t sig_offset; // real offset = enc_sig_offset & 0xFFFFFF00
+ uint32_t unk_0x18;
+ uint32_t unk_0x1c;
+ uint32_t unk_0x20;
+ uint32_t unk_0x24;
+} mbn_header_v1;
+
+#define MBN_V2_MAGIC "\xD1\xDC\x4B\x84\x34\x10\xD7\x73"
+#define MBN_V2_MAGIC_SIZE 8
+
+typedef struct {
+ unsigned char magic1[8];
+ uint32_t unk_0x08;
+ uint32_t unk_0x0c; // 0xFFFFFFFF
+ uint32_t unk_0x10; // 0xFFFFFFFF
+ uint32_t header_size;
+ uint32_t unk_0x18;
+ uint32_t data_size; // data_size = total_size - sizeof(mbn_header_v2)
+ uint32_t sig_offset;
+ uint32_t unk_0x24;
+ uint32_t unk_0x28;
+ uint32_t unk_0x2c;
+ uint32_t unk_0x30;
+ uint32_t unk_0x34; // 0x1
+ uint32_t unk_0x38; // 0x1
+ uint32_t unk_0x3c; // 0xFFFFFFFF
+ uint32_t unk_0x40; // 0xFFFFFFFF
+ uint32_t unk_0x44; // 0xFFFFFFFF
+ uint32_t unk_0x48; // 0xFFFFFFFF
+ uint32_t unk_0x4c; // 0xFFFFFFFF
+} mbn_header_v2;
+
+#define MBN_BIN_MAGIC "\x04\x00\xEA\x6C\x69\x48\x55"
+#define MBN_BIN_MAGIC_SIZE 7
+#define MBN_BIN_MAGIC_OFFSET 1 // we ignore the first byte
+
+typedef struct {
+ unsigned char magic[8];
+ uint32_t unk_0x08;
+ uint32_t version;
+ uint32_t total_size; // size including header
+ uint32_t unk_0x14; // some offset
+} mbn_bin_header;
+
+typedef struct
+{
+ uint32_t reserved;
+ uint32_t version;
+ uint32_t common_metadata_size;
+ uint32_t qti_metadata_size;
+ uint32_t oem_metadata_size;
+ uint32_t hash_table_size;
+ uint32_t qti_signature_size;
+ uint32_t qti_certificate_chain_size;
+ uint32_t oem_signature_size;
+ uint32_t oem_certificate_chain_size;
+} mbn_v7_header;
+
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_ABIVERSION 8
+#define EI_PAD 9
+#define EI_NIDENT 16
+
+#define ELFMAG0 0x7F
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+typedef struct
+{
+ uint8_t e_ident[EI_NIDENT];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint32_t e_entry;
+ uint32_t e_phoff;
+ uint32_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+} elf32_header;
+
+typedef struct
+{
+ uint8_t e_ident[EI_NIDENT];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint64_t e_entry;
+ uint64_t e_phoff;
+ uint64_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+} elf64_header;
+
+typedef struct
+{
+ uint32_t p_type;
+ uint32_t p_offset;
+ uint32_t p_vaddr;
+ uint32_t p_paddr;
+ uint32_t p_filesz;
+ uint32_t p_memsz;
+ uint32_t p_flags;
+ uint32_t p_align;
+} elf32_pheader;
+
+typedef struct
+{
+ uint32_t p_type;
+ uint32_t p_flags;
+ uint64_t p_offset;
+ uint64_t p_vaddr;
+ uint64_t p_paddr;
+ uint64_t p_filesz;
+ uint64_t p_memsz;
+ uint64_t p_align;
+} elf64_pheader;
+#pragma pack(pop)
+
+static int mbn_is_valid_elf(const uint8_t* e_ident, size_t size)
+{
+ return size >= EI_NIDENT && e_ident[EI_MAG0] == ELFMAG0 &&
+ e_ident[EI_MAG1] == ELFMAG1 && e_ident[EI_MAG2] == ELFMAG2 &&
+ e_ident[EI_MAG3] == ELFMAG3 && e_ident[EI_CLASS] != ELFCLASSNONE;
+}
+
+static int mbn_is_64bit_elf(const uint8_t* e_ident)
{
- mbn_file* mbn = (mbn_file*)malloc(sizeof(mbn_file));
- if (!mbn) {
- return NULL;
- }
- memset(mbn, '\0', sizeof(mbn_file));
- mbn->data = malloc(size);
- mbn->size = size;
- memcpy(mbn->data, data, size);
- /* FIXME: header parsing is not big endian safe */
- if (memcmp(data, MBN_V2_MAGIC, MBN_V2_MAGIC_SIZE) == 0) {
- mbn->version = 2;
- memcpy(&mbn->header.v2, data, sizeof(mbn_header_v2));
- mbn->parsed_size = mbn->header.v2.data_size + sizeof(mbn_header_v2);
- } else if (memcmp(data, MBN_V1_MAGIC, MBN_V1_MAGIC_SIZE) == 0) {
- mbn->version = 1;
- memcpy(&mbn->header.v1, data, sizeof(mbn_header_v1));
- mbn->parsed_size = mbn->header.v1.data_size + sizeof(mbn_header_v1);
- } else if (memcmp(data, BIN_MAGIC, BIN_MAGIC_SIZE) == 0) {
- mbn->version = 3;
- memcpy(&mbn->header.bin, data, sizeof(bin_header));
- mbn->parsed_size = mbn->header.bin.total_size;
- } else if (memcmp(data, ELF_MAGIC, ELF_MAGIC_SIZE) == 0) {
- mbn->version = 4;
- memcpy(&mbn->header.elf, data, sizeof(elf_header));
- // we cheat here since we don't parse the actual ELF file
- mbn->parsed_size = mbn->size;
+ return e_ident[EI_CLASS] == ELFCLASS64;
+}
+
+void* mbn_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size)
+{
+ if (!data) {
+ logger(LL_ERROR, "%s: data is NULL\n", __func__);
+ return NULL;
+ }
+
+ if (!data_size) {
+ logger(LL_ERROR, "%s: data size is 0\n", __func__);
+ return NULL;
+ }
+
+ if (!blob) {
+ logger(LL_ERROR, "%s: blob is NULL\n", __func__);
+ return NULL;
+ }
+
+ if (!blob_size) {
+ logger(LL_ERROR, "%s: blob size is 0\n", __func__);
+ return NULL;
+ }
+
+ size_t parsed_size = 0;
+ if (data_size > MBN_V2_MAGIC_SIZE && memcmp(data, MBN_V2_MAGIC, MBN_V2_MAGIC_SIZE) == 0) {
+ parsed_size = ((mbn_header_v2*)data)->data_size + sizeof(mbn_header_v2);
+ logger(LL_DEBUG, "%s: encountered MBN v2 image, parsed_size = 0x%zx\n", __func__, parsed_size);
+ } else if (data_size > MBN_V1_MAGIC_SIZE && memcmp(data, MBN_V1_MAGIC, MBN_V1_MAGIC_SIZE) == 0) {
+ parsed_size = ((mbn_header_v1*)data)->data_size + sizeof(mbn_header_v1);
+ logger(LL_DEBUG, "%s: encountered MBN v1 image, parsed_size = 0x%zx\n", __func__, parsed_size);
+ } else if (data_size > MBN_BIN_MAGIC_SIZE+MBN_BIN_MAGIC_OFFSET && memcmp((uint8_t*)data+MBN_BIN_MAGIC_OFFSET, (uint8_t*)MBN_BIN_MAGIC, MBN_BIN_MAGIC_SIZE) == 0) {
+ parsed_size = ((mbn_bin_header*)data)->total_size;
+ logger(LL_DEBUG, "%s: encountered MBN BIN image, parsed_size = 0x%zx\n", __func__, parsed_size);
+ } else if (mbn_is_valid_elf(data, data_size)) {
+ if (mbn_is_64bit_elf(data)) {
+ const elf64_header* ehdr = data;
+ const elf64_pheader* phdr = data + ehdr->e_phoff;
+ if (ehdr->e_phnum == 0) {
+ logger(LL_ERROR, "%s: ELF has no program sections\n", __func__);
+ return NULL;
+ }
+ uint64_t last_off = 0;
+ uint16_t last_index = 0;
+ for (uint16_t i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_offset > last_off) {
+ last_off = phdr[i].p_offset;
+ last_index = i;
+ }
+ }
+ parsed_size = last_off + phdr[last_index].p_filesz;
+ } else {
+ const elf32_header* ehdr = data;
+ const elf32_pheader* phdr = data + ehdr->e_phoff;
+ if (ehdr->e_phnum == 0) {
+ logger(LL_ERROR, "%s: ELF has no program sections\n", __func__);
+ return NULL;
+ }
+ uint32_t last_off = 0;
+ uint16_t last_index = 0;
+ for (uint16_t i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_offset > last_off) {
+ last_off = phdr[i].p_offset;
+ last_index = i;
+ }
+ }
+ parsed_size = last_off + phdr[last_index].p_filesz;
+ }
+ logger(LL_DEBUG, "%s: encountered ELF image, parsed_size = 0x%zx\n", __func__, parsed_size);
} else {
- logger(LL_DEBUG, "Unknown file format passed to %s\n", __func__);
+ logger(LL_WARNING, "Unknown file format passed to %s\n", __func__);
}
- if (mbn->parsed_size != mbn->size) {
- logger(LL_WARNING, "Size mismatch when parsing MBN file. Continuing anyway.\n");
+ if (parsed_size != data_size) {
+ logger(LL_WARNING, "%s: size mismatch for MBN data, expected 0x%zx, input size 0x%zx\n", __func__, parsed_size, data_size);
}
- return mbn;
+
+ off_t stitch_offset = data_size - blob_size;
+ if (stitch_offset + blob_size > data_size) {
+ logger(LL_ERROR, "%s: stitch offset (0x%llx) + size (0x%zx) is larger than the destination (0x%zx)\n", __func__, stitch_offset, blob_size, data_size);
+ return NULL;
+ }
+
+ void* buf = malloc(data_size);
+ if (buf == NULL) {
+ logger(LL_ERROR, "out of memory\n");
+ return NULL;
+ }
+
+ memcpy(buf, data, data_size);
+ logger(LL_DEBUG, "%s: stitching mbn at 0x%llx, size 0x%zx\n", __func__, stitch_offset, blob_size);
+ memcpy(buf + stitch_offset, blob, blob_size);
+
+ return buf;
}
-void mbn_free(mbn_file* mbn)
+// the sum of header size and all sizes inside it must fit within the size of
+// the data
+static int mbn_v7_header_sizes_valid(const mbn_v7_header* header, size_t size)
{
- if (mbn) {
- if (mbn->data) {
- free(mbn->data);
- }
- free(mbn);
- }
+ return (sizeof(*header) + header->common_metadata_size +
+ header->qti_metadata_size + header->oem_metadata_size +
+ header->hash_table_size + header->qti_signature_size +
+ header->qti_certificate_chain_size + header->oem_signature_size +
+ header->oem_certificate_chain_size) <= size;
+}
+
+// 0xE0 == sizeof(mav25_authority_meta_field_t), 0x68 ==
+// kExpectedOEMSignatureSize, 0xD20 == kExpectedOEMCertChainSize
+static int mbn_v7_header_sizes_expected(const mbn_v7_header* header)
+{
+ return (header->qti_metadata_size == 0 || header->qti_metadata_size == 0xE0) &&
+ (header->oem_metadata_size == 0 || header->oem_metadata_size == 0xE0) &&
+ (header->oem_signature_size == 0 || header->oem_signature_size == 0x68) &&
+ (header->oem_certificate_chain_size == 0 || header->oem_certificate_chain_size == 0xD20);
+}
+
+static void mbn_v7_log_header(const mbn_v7_header* header, const char* func, const char* prefix)
+{
+ logger(LL_DEBUG,
+ "%s: %s header {version=0x%x, common_metadata_size=0x%x, "
+ "qti_metadata_size=0x%x, oem_metadata_size=0x%x, hash_table_size=0x%x, "
+ "qti_signature_size=0x%x, qti_certificate_chain_size=0x%x, "
+ "oem_signature_size=0x%x, oem_certificate_chain_size=0x%x}",
+ func,
+ prefix,
+ header->version,
+ header->common_metadata_size,
+ header->qti_metadata_size,
+ header->oem_metadata_size,
+ header->hash_table_size,
+ header->qti_signature_size,
+ header->qti_certificate_chain_size,
+ header->oem_signature_size,
+ header->oem_certificate_chain_size
+ );
}
-int mbn_update_sig_blob(mbn_file* mbn, const void* sigdata, size_t siglen)
+void* mbn_mav25_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size)
{
- if (!mbn) {
- logger(LL_ERROR, "%s: no data\n", __func__);
- return -1;
+ if (!data) {
+ logger(LL_ERROR, "%s: data is NULL\n", __func__);
+ return NULL;
}
- mbn->parsed_sig_offset = mbn->size - siglen;
- if ((mbn->parsed_sig_offset + siglen) > mbn->size) {
- logger(LL_ERROR, "%s: signature is larger than mbn file size\n", __func__);
- return -1;
+
+ if (!data_size) {
+ logger(LL_ERROR, "%s: data size is 0\n", __func__);
+ return NULL;
}
- memcpy(mbn->data + mbn->parsed_sig_offset, sigdata, siglen);
+ if (!blob) {
+ logger(LL_ERROR, "%s: blob is NULL\n", __func__);
+ return NULL;
+ }
- return 0;
-}
+ if (!blob_size) {
+ logger(LL_ERROR, "%s: blob size is 0\n", __func__);
+ return NULL;
+ }
+
+ if (!mbn_is_valid_elf(data, data_size)) {
+ logger(LL_ERROR, "%s: data is not a valid ELF\n", __func__);
+ return NULL;
+ }
+
+ if (sizeof(mbn_v7_header) > blob_size) {
+ logger(LL_ERROR, "%s: header is bigger than blob\n", __func__);
+ return NULL;
+ }
+
+ const mbn_v7_header* src_header = blob;
+ mbn_v7_log_header(src_header, __func__, "src");
+
+ if (src_header->version != 7) {
+ logger(LL_ERROR, "%s: src header version (0x%x) is incorrect\n", __func__, src_header->version);
+ return NULL;
+ }
+
+ // NOTE: Apple does weird stuff, in this case blob is smaller than
+ // the sizes the header reports, so we can't check their validity.
+ if (!mbn_v7_header_sizes_expected(src_header)) {
+ logger(LL_WARNING, "%s: header sizes in header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, src_header->qti_metadata_size, src_header->oem_metadata_size, src_header->oem_signature_size, src_header->oem_certificate_chain_size);
+ }
+
+ off_t sect_off;
+ size_t sect_size;
+ if (mbn_is_64bit_elf(data)) {
+ const elf64_header* ehdr = data;
+ const elf64_pheader* phdr = data + ehdr->e_phoff;
+ if (ehdr->e_phnum == 0) {
+ logger(LL_ERROR, "%s: ELF has no program sections\n", __func__);
+ return NULL;
+ }
+ if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(elf32_pheader)) > data_size) {
+ logger(LL_ERROR, "%s: Last ELF program section is out of bounds\n", __func__);
+ return NULL;
+ }
+ sect_off = phdr[ehdr->e_phnum-1].p_offset;
+ sect_size = phdr[ehdr->e_phnum-1].p_filesz;
+ } else {
+ const elf32_header* ehdr = data;
+ const elf32_pheader* phdr = data + ehdr->e_phoff;
+ if (ehdr->e_phnum == 0) {
+ logger(LL_ERROR, "%s: ELF has no program sections\n", __func__);
+ return NULL;
+ }
+ if ((ehdr->e_phoff + ehdr->e_phnum * sizeof(elf64_pheader)) > data_size) {
+ logger(LL_ERROR, "%s: Last ELF program section is out of bounds\n", __func__);
+ return NULL;
+ }
+ sect_off = phdr[ehdr->e_phnum-1].p_offset;
+ sect_size = phdr[ehdr->e_phnum-1].p_filesz;
+ }
+
+ if (sect_off == 0) {
+ logger(LL_ERROR, "%s: section has 0 offset\n", __func__);
+ return NULL;
+ }
+
+ if (sect_size == 0) {
+ logger(LL_ERROR, "%s: section has 0 size\n", __func__);
+ return NULL;
+ }
+
+ if (sect_off + sect_size > data_size) {
+ logger(LL_ERROR, "%s: section (0x%llx+0x%zx) is bigger than the data\n", __func__, sect_off, sect_size);
+ return NULL;
+ }
+ if (sizeof(mbn_v7_header) > sect_size) {
+ logger(LL_ERROR, "%s: dest header is bigger than the section (0x%zx)\n", __func__, sect_size);
+ return NULL;
+ }
+
+ const mbn_v7_header* header = data + sect_off;
+ mbn_v7_log_header(header, __func__, "dest");
+ if (header->version != 7) {
+ logger(LL_ERROR, "%s: dest header version (0x%x) is incorrect\n", __func__, header->version);
+ return NULL;
+ }
+
+ if (!mbn_v7_header_sizes_valid(header, sect_size)) {
+ logger(LL_ERROR, "%s: sizes in dest header are invalid (common_metadata_size=0x%x, qti_metadata_size=0x%x, oem_metadata_size=0x%x, hash_table_size=0x%x, qti_signature_size=0x%x, qti_certificate_chain_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, header->common_metadata_size, header->qti_metadata_size, header->oem_metadata_size, header->hash_table_size, header->qti_signature_size, header->qti_certificate_chain_size, header->oem_signature_size, header->oem_certificate_chain_size);
+ return NULL;
+ }
+
+ if (!mbn_v7_header_sizes_expected(header)) {
+ logger(LL_WARNING, "%s: header sizes in dest header are unexpected (qti_metadata_size=0x%x, oem_metadata_size=0x%x, oem_signature_size=0x%x, oem_certificate_chain_size=0x%x)\n", __func__, header->qti_metadata_size, header->oem_metadata_size, header->oem_signature_size, header->oem_certificate_chain_size);
+ }
+
+ size_t new_metadata_size =
+ sizeof(*src_header) + src_header->common_metadata_size +
+ src_header->qti_metadata_size + src_header->oem_metadata_size;
+ size_t new_metadata_and_hash_table_size =
+ new_metadata_size + src_header->hash_table_size;
+ size_t new_oem_sig_and_cert_chain_size =
+ src_header->oem_signature_size + src_header->oem_certificate_chain_size;
+ off_t new_oem_sig_and_cert_chain_off = new_metadata_and_hash_table_size +
+ header->qti_signature_size +
+ header->qti_certificate_chain_size;
+
+ if (new_metadata_and_hash_table_size > blob_size) {
+ logger(LL_ERROR, "%s: new metadata (0x%zx) and hash table (0x%x) are bigger than the source (0x%zx)\n", __func__, new_metadata_size, src_header->hash_table_size, blob_size);
+ return NULL;
+ }
+
+ if (new_metadata_and_hash_table_size > sect_size) {
+ logger(LL_ERROR, "%s: new metadata (0x%zx) and hash table (0x%x) are bigger than the destination (0x%zx)\n", __func__, new_metadata_size, src_header->hash_table_size, sect_size);
+ return NULL;
+ }
+
+ if (new_metadata_and_hash_table_size + new_oem_sig_and_cert_chain_size > blob_size) {
+ logger(LL_ERROR, "%s: new OEM signature and certificate chain are bigger than the source\n", __func__);
+ return NULL;
+ }
+
+ if (new_oem_sig_and_cert_chain_off + new_oem_sig_and_cert_chain_size > sect_size) {
+ logger(LL_ERROR, "%s: new OEM signature and certificate chain are outside the bounds of the destination\n", __func__);
+ return NULL;
+ }
+
+ void* buf = malloc(data_size);
+ if (buf == NULL) {
+ logger(LL_ERROR, "out of memory\n");
+ return NULL;
+ }
+
+ memcpy(buf, data, data_size);
+ logger(LL_DEBUG, "%s: stitching mbn at 0x%llx (0x%zx bytes)\n", __func__, sect_off, new_metadata_and_hash_table_size);
+ memcpy(buf + sect_off, blob, new_metadata_and_hash_table_size);
+ logger(LL_DEBUG, "%s: stitching mbn at 0x%llx (0x%zx bytes)\n", __func__, sect_off + new_oem_sig_and_cert_chain_off, new_oem_sig_and_cert_chain_size);
+ memcpy(buf + sect_off + new_oem_sig_and_cert_chain_off, blob + new_metadata_and_hash_table_size, new_oem_sig_and_cert_chain_size);
+
+ return buf;
+}
diff --git a/src/mbn.h b/src/mbn.h
index a328128..ff44797 100644
--- a/src/mbn.h
+++ b/src/mbn.h
@@ -1,8 +1,9 @@
/*
* mbn.h
- * support for .mbn file format (found in .bbfw files)
+ * support for Qualcomm MBN (Modem Binary) formats
*
* Copyright (c) 2012 Nikias Bassen. All Rights Reserved.
+ * Copyright (c) 2025 Visual Ehrmanntraut <visual@chefkiss.dev>. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -23,85 +24,7 @@
#include <stdint.h>
-#define MBN_V1_MAGIC "\x0A\x00\x00\x00"
-#define MBN_V1_MAGIC_SIZE 4
-
-struct _mbn_header_v1 {
- uint32_t type; // the signed .mbn files have 0xA as value.
- uint32_t unk_0x04;
- uint32_t unk_0x08;
- uint32_t unk_0x0c;
- uint32_t data_size; // data_size = total_size - sizeof(mbn_header)
- uint32_t sig_offset; // real offset = enc_sig_offset & 0xFFFFFF00
- uint32_t unk_0x18;
- uint32_t unk_0x1c;
- uint32_t unk_0x20;
- uint32_t unk_0x24;
-} __attribute__((packed));
-typedef struct _mbn_header_v1 mbn_header_v1;
-
-#define MBN_V2_MAGIC "\xD1\xDC\x4B\x84\x34\x10\xD7\x73"
-#define MBN_V2_MAGIC_SIZE 8
-
-struct _mbn_header_v2 {
- unsigned char magic1[8];
- uint32_t unk_0x08;
- uint32_t unk_0x0c; // 0xFFFFFFFF
- uint32_t unk_0x10; // 0xFFFFFFFF
- uint32_t header_size;
- uint32_t unk_0x18;
- uint32_t data_size; // data_size = total_size - sizeof(mbn_header_v2)
- uint32_t sig_offset;
- uint32_t unk_0x24;
- uint32_t unk_0x28;
- uint32_t unk_0x2c;
- uint32_t unk_0x30;
- uint32_t unk_0x34; // 0x1
- uint32_t unk_0x38; // 0x1
- uint32_t unk_0x3c; // 0xFFFFFFFF
- uint32_t unk_0x40; // 0xFFFFFFFF
- uint32_t unk_0x44; // 0xFFFFFFFF
- uint32_t unk_0x48; // 0xFFFFFFFF
- uint32_t unk_0x4c; // 0xFFFFFFFF
-} __attribute__((packed));
-typedef struct _mbn_header_v2 mbn_header_v2;
-
-#define BIN_MAGIC "\x7D\x04\x00\xEA\x6C\x69\x48\x55"
-#define BIN_MAGIC_SIZE 8
-
-struct _bin_header {
- unsigned char magic[8];
- uint32_t unk_0x08;
- uint32_t version;
- uint32_t total_size; // size including header
- uint32_t unk_0x14; // some offset
-} __attribute__((packed));
-typedef struct _bin_header bin_header;
-
-#define ELF_MAGIC "\x7F\x45\x4C\x46\x01\x01\x01\x00" // ELF magic, 32bit, little endian, SYSV
-#define ELF_MAGIC_SIZE 8
-
-struct _elf_header {
- unsigned char magic[8];
-} __attribute__((packed));
-typedef struct _elf_header elf_header;
-
-typedef struct {
- uint32_t version;
- union {
- mbn_header_v1 v1;
- mbn_header_v2 v2;
- bin_header bin;
- elf_header elf;
- } header;
- uint32_t parsed_size;
- uint32_t parsed_sig_offset;
- void* data;
- uint32_t size;
-} mbn_file;
-
-mbn_file* mbn_parse(const void* data, size_t size);
-void mbn_free(mbn_file* mbn);
-int mbn_update_sig_blob(mbn_file* mbn, const void* data, size_t size);
+void* mbn_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size);
+void* mbn_mav25_stitch(const void* data, size_t data_size, const void* blob, size_t blob_size);
#endif
diff --git a/src/restore.c b/src/restore.c
index 09cbe16..f2fd9be 100644
--- a/src/restore.c
+++ b/src/restore.c
@@ -1857,7 +1857,7 @@ int restore_send_nor(struct idevicerestore_client_t* client, plist_t message)
return 0;
}
-static const char* restore_get_bbfw_fn_for_element(const char* elem)
+static const char* restore_get_bbfw_fn_for_element(const char* elem, uint32_t bb_chip_id)
{
struct bbfw_fn_elem_t {
const char* element;
@@ -1888,16 +1888,30 @@ static const char* restore_get_bbfw_fn_for_element(const char* elem)
{ NULL, NULL }
};
+ struct bbfw_fn_elem_t bbfw_fn_elem_mav25[] = {
+ // Mav25 Firmware files
+ { "Misc", "multi_image.mbn" },
+ { "RestoreSBL1", "restorexbl_sc.elf" },
+ { "SBL1", "xbl_sc.elf" },
+ { "TME", "signed_firmware_soc_view.elf" },
+ { NULL, NULL }
+ };
+
+ struct bbfw_fn_elem_t* bbfw_fn_elems = (struct bbfw_fn_elem_t*)bbfw_fn_elem;
+ if (bb_chip_id == 0x1F30E1) {
+ bbfw_fn_elems = (struct bbfw_fn_elem_t*)bbfw_fn_elem_mav25;
+ }
+
int i;
- for (i = 0; bbfw_fn_elem[i].element != NULL; i++) {
- if (strcmp(bbfw_fn_elem[i].element, elem) == 0) {
- return bbfw_fn_elem[i].fn;
+ for (i = 0; bbfw_fn_elems[i].element != NULL; i++) {
+ if (strcmp(bbfw_fn_elems[i].element, elem) == 0) {
+ return bbfw_fn_elems[i].fn;
}
}
return NULL;
}
-static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned char* bb_nonce)
+static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned char* bb_nonce, uint32_t bb_chip_id)
{
int res = -1;
@@ -1925,7 +1939,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned
struct zip_file* zfile = NULL;
struct zip* za = NULL;
struct zip_source* zs = NULL;
- mbn_file* mbn = NULL;
fls_file* fls = NULL;
za = zip_open(bbfwtmp, 0, &zerr);
@@ -1953,7 +1966,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned
if (node && (strcmp(key + (strlen(key) - 5), "-Blob") == 0) && (plist_get_node_type(node) == PLIST_DATA)) {
char *ptr = strchr(key, '-');
*ptr = '\0';
- const char* signfn = restore_get_bbfw_fn_for_element(key);
+ const char* signfn = restore_get_bbfw_fn_for_element(key, bb_chip_id);
if (!signfn) {
logger(LL_ERROR, "can't match element name '%s' to baseband firmware file name.\n", key);
goto leave;
@@ -2002,12 +2015,6 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned
logger(LL_ERROR, "could not parse fls file\n");
goto leave;
}
- } else {
- mbn = mbn_parse(buffer, zstat.size);
- if (!mbn) {
- logger(LL_ERROR, "could not parse mbn file\n");
- goto leave;
- }
}
free(buffer);
buffer = NULL;
@@ -2019,32 +2026,33 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned
goto leave;
}
+ logger(LL_VERBOSE, "Stitching %s\n", signfn);
if (is_fls) {
- if (fls_update_sig_blob(fls, blob, (unsigned int)blob_size) != 0) {
- logger(LL_ERROR, "could not sign %s\n", signfn);
+ if (fls_update_sig_blob(fls, blob, (size_t)blob_size) != 0) {
+ logger(LL_ERROR, "Could not stitch %s\n", signfn);
goto leave;
}
- } else {
- if (mbn_update_sig_blob(mbn, blob, (unsigned int)blob_size) != 0) {
- logger(LL_ERROR, "could not sign %s\n", signfn);
+ fsize = fls->size;
+ fdata = (unsigned char*)malloc(fsize);
+ if (fdata == NULL) {
+ logger(LL_ERROR, "out of memory\n");
goto leave;
}
- }
-
- fsize = (is_fls ? fls->size : mbn->size);
- fdata = (unsigned char*)malloc(fsize);
- if (fdata == NULL) {
- logger(LL_ERROR, "out of memory\n");
- goto leave;
- }
- if (is_fls) {
memcpy(fdata, fls->data, fsize);
fls_free(fls);
fls = NULL;
+ } else if (bb_chip_id == 0x1F30E1) { // Mav25 - Qualcomm Snapdragon X80 5G Modem
+ fdata = mbn_mav25_stitch(buffer, zstat.size, blob, (size_t)blob_size);
+ if (!fdata) {
+ logger(LL_ERROR, "Could not stitch %s\n", signfn);
+ goto leave;
+ }
} else {
- memcpy(fdata, mbn->data, fsize);
- mbn_free(mbn);
- mbn = NULL;
+ fdata = mbn_stitch(buffer, zstat.size, blob, (size_t)blob_size);
+ if (!fdata) {
+ logger(LL_ERROR, "Could not stitch %s\n", signfn);
+ goto leave;
+ }
}
zs = zip_source_buffer(za, fdata, fsize, 1);
@@ -2055,7 +2063,7 @@ static int restore_sign_bbfw(const char* bbfwtmp, plist_t bbtss, const unsigned
}
if (zip_file_replace(za, zindex, zs, 0) == -1) {
- logger(LL_ERROR, "could not update signed '%s' in archive\n", signfn);
+ logger(LL_ERROR, "Could not update signed '%s' in archive\n", signfn);
goto leave;
}
@@ -2218,7 +2226,6 @@ leave:
zip_unchange_all(za);
zip_close(za);
}
- mbn_free(mbn);
fls_free(fls);
free(buffer);
@@ -2352,7 +2359,7 @@ static int restore_send_baseband_data(struct idevicerestore_client_t* client, pl
response = NULL;
}
- res = restore_sign_bbfw(bbfwtmp, (client->restore->bbtss) ? client->restore->bbtss : response, bb_nonce);
+ res = restore_sign_bbfw(bbfwtmp, (client->restore->bbtss) ? client->restore->bbtss : response, bb_nonce, bb_chip_id);
if (res != 0) {
goto leave;
}