- Introduced md1img.h and md1img.c for parsing MediaTek md1img container format. - Implemented mtk.h and mtk.c for parsing MediaTek GFH firmware images (md1rom). - Added plugin support for md1img and mtk formats in bin_md1img.c and bin_mtk.c. - Updated meson.build to include new source files and plugins. - Enhanced RzBuffer utility with LZMA alone decompression support. --------- Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>
665 lines
20 KiB
C
665 lines
20 KiB
C
// SPDX-FileCopyrightText: 2025 godcodehunter
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/**
|
|
* \file Parser for MediaTek md1img container format.
|
|
*
|
|
* The md1img format is a simple section-based container used by MediaTek
|
|
* to package modem firmware components: md1rom (GFH firmware image),
|
|
* debug info (CATI format), DSP images, certificates, etc.
|
|
*
|
|
* Each section has a header with magic 0x58881688, a name, data size,
|
|
* memory address, and other metadata, followed by the section data
|
|
* and alignment padding.
|
|
*
|
|
* The md1rom section is parsed internally using the mtk GFH parser to
|
|
* provide proper base address, entry points, and code/header sections.
|
|
* Debug symbols are extracted from the CATI-format dbginfo section.
|
|
*
|
|
* References:
|
|
* - https://github.com/nccgroup/mtk_bp/blob/main/mtk_structs/mtk_img.ksy (md1img)
|
|
* - https://github.com/nccgroup/mtk_bp/blob/main/mtk_structs/mtk_dbg_info.ksy (CATI)
|
|
*/
|
|
#include "md1img.h"
|
|
|
|
// --- CATI debug info parsing ---
|
|
|
|
static void mtk_dbg_symbol_free(MtkDbgSymbol *sym) {
|
|
if (sym) {
|
|
free(sym->name);
|
|
free(sym);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Parse symbols from a single CATI debug entry.
|
|
*
|
|
* The debug entry format (after "CATI" magic + type):
|
|
* unk3(u32), unk_str1(strz), unk_str2(strz), unk_str3(strz), date_str(strz),
|
|
* symbols_start(u32), files_start(u32),
|
|
* symbol_entries: [strz name, u32 addr1, u32 addr2] ... until empty name
|
|
*/
|
|
static bool md1img_parse_cati_debug(RzBuffer *b, RzPVector /*<MtkDbgSymbol *>*/ *symbols) {
|
|
// Skip unk3
|
|
rz_buf_seek(b, 4, RZ_BUF_CUR);
|
|
|
|
// Skip 4 strz fields (unk_str1, unk_str2, unk_str3, date_str)
|
|
for (int i = 0; i < 4; i++) {
|
|
ut64 len = rz_buf_read_string(b, NULL);
|
|
if (len == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Skip symbols_start and files_start
|
|
rz_buf_seek(b, 8, RZ_BUF_CUR);
|
|
|
|
// Parse symbol entries (limit guards against corrupted/missing terminator)
|
|
int count = 0;
|
|
while (count < 500000) {
|
|
char *name = NULL;
|
|
ut64 len = rz_buf_read_string(b, &name);
|
|
if (len == 0 || !name) {
|
|
free(name);
|
|
break;
|
|
}
|
|
// Empty string = terminator
|
|
if (name[0] == '\0') {
|
|
free(name);
|
|
break;
|
|
}
|
|
|
|
ut32 addr1 = 0, addr2 = 0;
|
|
if (!rz_buf_read_le32(b, &addr1) || !rz_buf_read_le32(b, &addr2)) {
|
|
free(name);
|
|
break;
|
|
}
|
|
|
|
MtkDbgSymbol *sym = RZ_NEW0(MtkDbgSymbol);
|
|
if (!sym) {
|
|
free(name);
|
|
break;
|
|
}
|
|
sym->name = name;
|
|
sym->addr = addr1;
|
|
// For corrupted entries
|
|
sym->size = (addr2 > addr1) ? (addr2 - addr1) : 0;
|
|
rz_pvector_push(symbols, sym);
|
|
count++;
|
|
}
|
|
|
|
return count > 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Parse a CATI container wrapping one or more debug entries.
|
|
*/
|
|
static bool md1img_parse_cati_container(RzBuffer *b, RzPVector /*<MtkDbgSymbol *>*/ *symbols) {
|
|
// unk1(u32), size(u32)
|
|
ut32 unk1 = 0, container_size = 0;
|
|
if (!rz_buf_read_le32(b, &unk1) || !rz_buf_read_le32(b, &container_size)) {
|
|
return false;
|
|
}
|
|
|
|
// 0x10 = CATI header size (magic[4] + type[4] + unk1[4] + size[4])
|
|
if (container_size < 0x10) {
|
|
return false;
|
|
}
|
|
|
|
// The nested entries are within (container_size - 0x10) bytes
|
|
ut64 entries_end = rz_buf_tell(b) + (container_size - 0x10);
|
|
ut64 buf_size = rz_buf_size(b);
|
|
// Clamp to actual buffer size in case container_size is corrupted
|
|
if (entries_end > buf_size) {
|
|
entries_end = buf_size;
|
|
}
|
|
|
|
// Parse nested CATI headers
|
|
while (rz_buf_tell(b) + 8 <= entries_end) {
|
|
ut8 magic[MTK_CATI_MAGIC_SIZE];
|
|
if (rz_buf_read(b, magic, MTK_CATI_MAGIC_SIZE) != MTK_CATI_MAGIC_SIZE ||
|
|
memcmp(magic, MTK_CATI_MAGIC, MTK_CATI_MAGIC_SIZE) != 0) {
|
|
break;
|
|
}
|
|
|
|
ut32 cati_type = 0;
|
|
if (!rz_buf_read_le32(b, &cati_type)) {
|
|
break;
|
|
}
|
|
|
|
if (cati_type != MTK_CATI_TYPE_DEBUG && cati_type != MTK_CATI_TYPE_DEBUG_DSP) {
|
|
break;
|
|
}
|
|
md1img_parse_cati_debug(b, symbols);
|
|
}
|
|
|
|
return rz_pvector_len(symbols) > 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Parse a CATI debug info buffer and extract symbols.
|
|
*/
|
|
static RzPVector /*<MtkDbgSymbol *>*/ *md1img_parse_dbginfo(RzBuffer *b) {
|
|
rz_return_val_if_fail(b, NULL);
|
|
|
|
ut8 magic[MTK_CATI_MAGIC_SIZE];
|
|
if (rz_buf_read_at(b, 0, magic, MTK_CATI_MAGIC_SIZE) != MTK_CATI_MAGIC_SIZE) {
|
|
return NULL;
|
|
}
|
|
if (memcmp(magic, MTK_CATI_MAGIC, MTK_CATI_MAGIC_SIZE) != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
rz_buf_seek(b, 0 + MTK_CATI_MAGIC_SIZE, RZ_BUF_SET);
|
|
|
|
ut32 cati_type = 0;
|
|
if (!rz_buf_read_le32(b, &cati_type)) {
|
|
return NULL;
|
|
}
|
|
|
|
RzPVector *symbols = rz_pvector_new((RzPVectorFree)mtk_dbg_symbol_free);
|
|
if (!symbols) {
|
|
return NULL;
|
|
}
|
|
|
|
bool ok = false;
|
|
if (cati_type == MTK_CATI_TYPE_CONTAINER) {
|
|
ok = md1img_parse_cati_container(b, symbols);
|
|
} else if (cati_type == MTK_CATI_TYPE_DEBUG || cati_type == MTK_CATI_TYPE_DEBUG_DSP) {
|
|
ok = md1img_parse_cati_debug(b, symbols);
|
|
}
|
|
|
|
if (!ok || rz_pvector_len(symbols) == 0) {
|
|
rz_pvector_free(symbols);
|
|
return NULL;
|
|
}
|
|
|
|
return symbols;
|
|
}
|
|
|
|
// --- md1img container parsing ---
|
|
|
|
static bool md1img_read_section_hdr(RzBuffer *b, ut64 section_start, Md1imgSection *sec) {
|
|
ut64 offset = section_start;
|
|
|
|
// Check magic
|
|
ut8 magic[MD1IMG_MAGIC_SIZE];
|
|
if (!rz_buf_read_offset(b, &offset, magic, MD1IMG_MAGIC_SIZE)) {
|
|
return false;
|
|
}
|
|
if (memcmp(magic, MD1IMG_MAGIC, MD1IMG_MAGIC_SIZE) != 0) {
|
|
return false;
|
|
}
|
|
|
|
// Read dsize
|
|
if (!rz_buf_read_le32_offset(b, &offset, &sec->dsize)) {
|
|
return false;
|
|
}
|
|
|
|
// Read name (32 bytes, null-terminated)
|
|
if (!rz_buf_read_offset(b, &offset, (ut8 *)sec->name, MD1IMG_NAME_SIZE)) {
|
|
return false;
|
|
}
|
|
sec->name[MD1IMG_NAME_SIZE - 1] = '\0';
|
|
|
|
// Read maddr and mode
|
|
if (!rz_buf_read_le32_offset(b, &offset, &sec->maddr) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->mode)) {
|
|
return false;
|
|
}
|
|
|
|
// Verify ext_magic
|
|
ut8 ext_magic[MD1IMG_EXT_MAGIC_SIZE];
|
|
if (!rz_buf_read_offset(b, &offset, ext_magic, MD1IMG_EXT_MAGIC_SIZE)) {
|
|
return false;
|
|
}
|
|
if (memcmp(ext_magic, MD1IMG_EXT_MAGIC, MD1IMG_EXT_MAGIC_SIZE) != 0) {
|
|
return false;
|
|
}
|
|
|
|
// Read remaining header fields
|
|
if (!rz_buf_read_le32_offset(b, &offset, &sec->hdr_size) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->hdr_version) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->img_type) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->img_list_end) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->align_size) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->dsize_extend) ||
|
|
!rz_buf_read_le32_offset(b, &offset, &sec->maddr_extend)) {
|
|
return false;
|
|
}
|
|
|
|
if (sec->hdr_size < MD1IMG_MIN_HDR_SIZE) {
|
|
return false;
|
|
}
|
|
|
|
sec->data_offset = section_start + sec->hdr_size;
|
|
return true;
|
|
}
|
|
|
|
RZ_IPI bool md1img_check_buffer(RZ_BORROW RZ_NONNULL RzBuffer *b) {
|
|
rz_return_val_if_fail(b, false);
|
|
|
|
if (rz_buf_size(b) < MD1IMG_MIN_HDR_SIZE) {
|
|
return false;
|
|
}
|
|
|
|
ut8 magic[MD1IMG_MAGIC_SIZE];
|
|
if (rz_buf_read_at(b, 0, magic, MD1IMG_MAGIC_SIZE) != MD1IMG_MAGIC_SIZE) {
|
|
return false;
|
|
}
|
|
return memcmp(magic, MD1IMG_MAGIC, MD1IMG_MAGIC_SIZE) == 0;
|
|
}
|
|
|
|
/**
|
|
* \brief Register the section's virtual file and run per-section parsers
|
|
* (GFH for md1rom, CATI for dbginfo). Takes ownership of \p vbuf.
|
|
*/
|
|
static void md1img_load_section(Md1imgObj *md1, int sec_idx, RZ_BORROW RZ_NONNULL const Md1imgSection *sec, RZ_OWN RZ_NONNULL RzBuffer *vbuf) {
|
|
RzBinVirtualFile *vfile = RZ_NEW0(RzBinVirtualFile);
|
|
if (!vfile) {
|
|
rz_buf_free(vbuf);
|
|
return;
|
|
}
|
|
vfile->buf = vbuf;
|
|
vfile->buf_owned = true;
|
|
vfile->name = rz_str_dup(sec->name);
|
|
rz_pvector_push(md1->vfiles, vfile);
|
|
|
|
// Parse md1rom section with mtk GFH parser
|
|
if (rz_str_casestr(sec->name, "md1rom") && md1->md1rom_idx < 0) {
|
|
md1->md1rom_idx = sec_idx;
|
|
// Search for GFH magic within md1rom data (may not be at offset 0)
|
|
ut64 gfh_off = 0;
|
|
ut8 magic_buf[4];
|
|
while (gfh_off + MTK_GFH_MIN_FILE_SIZE <= sec->dsize) {
|
|
if (rz_buf_read_at(vbuf, gfh_off, magic_buf, 4) != 4) {
|
|
break;
|
|
}
|
|
ut32 magic_val = rz_read_le32(magic_buf);
|
|
if ((magic_val & MTK_GFH_MAGIC_MASK) == MTK_GFH_MAGIC) {
|
|
break;
|
|
}
|
|
gfh_off += 4;
|
|
}
|
|
if (gfh_off + MTK_GFH_MIN_FILE_SIZE <= sec->dsize) {
|
|
RzBuffer *gfh_buf = rz_buf_new_slice(vbuf, gfh_off, sec->dsize - gfh_off);
|
|
if (gfh_buf) {
|
|
md1->mtk = mtk_obj_new(gfh_buf);
|
|
rz_buf_free(gfh_buf);
|
|
}
|
|
if (md1->mtk) {
|
|
md1->gfh_offset = gfh_off;
|
|
RZ_LOG_INFO("md1img: parsed GFH from '%s' at offset 0x%" PFMT64x " (load_addr=0x%x, entry=0x%x)\n",
|
|
sec->name, gfh_off, md1->mtk->file_info.load_addr, md1->mtk->entry_vaddr);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse CATI debug info from dbginfo sections
|
|
if (rz_str_casestr(sec->name, "dbginfo") && sec->dsize > MTK_CATI_MAGIC_SIZE) {
|
|
RzPVector *syms = md1img_parse_dbginfo(vbuf);
|
|
// Try LZMA alone decompression if raw CATI parsing failed (dbginfo may be LZMA-compressed)
|
|
if (!syms) {
|
|
RzBuffer *decompressed = rz_buf_new_empty(0);
|
|
if (decompressed && rz_lzma_alone_dec_buf(vbuf, decompressed, 4096)) {
|
|
syms = md1img_parse_dbginfo(decompressed);
|
|
}
|
|
rz_buf_free(decompressed);
|
|
}
|
|
if (syms) {
|
|
if (!md1->dbg_symbols) {
|
|
md1->dbg_symbols = syms;
|
|
} else {
|
|
// Merge symbols from additional dbginfo sections
|
|
void **it;
|
|
rz_pvector_foreach (syms, it) {
|
|
rz_pvector_push(md1->dbg_symbols, *it);
|
|
}
|
|
// Disown elements before freeing (ownership transferred)
|
|
syms->v.free = NULL;
|
|
rz_pvector_free(syms);
|
|
}
|
|
RZ_LOG_INFO("md1img: loaded debug symbols from '%s'\n", sec->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_IPI bool md1img_load_buffer(RZ_BORROW RZ_NONNULL RzBinFile *bf, RZ_BORROW RZ_NONNULL RzBinObject *obj, RZ_BORROW RZ_NONNULL RzBuffer *b, RZ_BORROW RZ_NULLABLE Sdb *sdb) {
|
|
rz_return_val_if_fail(bf && obj && b, false);
|
|
|
|
Md1imgObj *md1 = RZ_NEW0(Md1imgObj);
|
|
if (!md1) {
|
|
return false;
|
|
}
|
|
|
|
md1->sections = rz_vector_new(sizeof(Md1imgSection), NULL, NULL);
|
|
md1->vfiles = rz_pvector_new((RzPVectorFree)rz_bin_virtual_file_free);
|
|
md1->md1rom_idx = -1;
|
|
|
|
if (!md1->sections || !md1->vfiles) {
|
|
rz_vector_free(md1->sections);
|
|
rz_pvector_free(md1->vfiles);
|
|
free(md1);
|
|
return false;
|
|
}
|
|
|
|
ut64 buf_size = rz_buf_size(b);
|
|
ut64 offset = 0;
|
|
int count = 0;
|
|
|
|
// Limit iteration count as a safeguard against corrupted headers
|
|
while (offset + MD1IMG_MIN_HDR_SIZE <= buf_size && count < 256) {
|
|
Md1imgSection sec = { 0 };
|
|
if (!md1img_read_section_hdr(b, offset, &sec)) {
|
|
break;
|
|
}
|
|
|
|
// Validate that data fits in the buffer
|
|
if (sec.data_offset + sec.dsize > buf_size) {
|
|
RZ_LOG_WARN("md1img: section '%s' data exceeds file size, truncating\n", sec.name);
|
|
sec.dsize = buf_size - sec.data_offset;
|
|
}
|
|
|
|
int sec_idx = rz_vector_len(md1->sections);
|
|
rz_vector_push(md1->sections, &sec);
|
|
|
|
// Create a virtual file for this section's data and parse it
|
|
RzBuffer *vbuf = rz_buf_new_slice(b, sec.data_offset, sec.dsize);
|
|
if (vbuf) {
|
|
md1img_load_section(md1, sec_idx, &sec, vbuf);
|
|
}
|
|
|
|
// Advance to next section: data_offset + dsize + alignment padding
|
|
ut64 data_end = sec.data_offset + sec.dsize;
|
|
ut64 remainder = sec.align_size > 0 ? sec.dsize % sec.align_size : 0;
|
|
if (remainder > 0) {
|
|
offset = data_end + (sec.align_size - remainder);
|
|
} else {
|
|
offset = data_end;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (offset < buf_size) {
|
|
RZ_LOG_WARN("md1img: %" PFMT64u " trailing bytes after last section header\n", buf_size - offset);
|
|
}
|
|
|
|
if (rz_vector_len(md1->sections) == 0) {
|
|
rz_vector_free(md1->sections);
|
|
rz_pvector_free(md1->vfiles);
|
|
free(md1);
|
|
return false;
|
|
}
|
|
|
|
obj->bin_obj = md1;
|
|
return true;
|
|
}
|
|
|
|
RZ_IPI void md1img_destroy(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
if (!bf || !bf->o || !bf->o->bin_obj) {
|
|
return;
|
|
}
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
rz_vector_free(md1->sections);
|
|
rz_pvector_free(md1->vfiles);
|
|
rz_pvector_free(md1->dbg_symbols);
|
|
mtk_obj_free(md1->mtk);
|
|
free(md1);
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzBinInfo *md1img_info(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
RzBinInfo *info = RZ_NEW0(RzBinInfo);
|
|
if (!info) {
|
|
return NULL;
|
|
}
|
|
|
|
info->file = bf->file ? rz_str_dup(bf->file) : NULL;
|
|
info->type = rz_str_dup("MediaTek md1img container");
|
|
info->machine = rz_str_dup("MediaTek Modem");
|
|
info->arch = rz_str_dup("mips");
|
|
info->cpu = rz_str_dup("nanomips");
|
|
info->rclass = rz_str_dup("firmware");
|
|
info->subsystem = rz_str_dup("modem");
|
|
info->has_va = true;
|
|
info->bits = 32;
|
|
info->big_endian = false;
|
|
|
|
return info;
|
|
}
|
|
|
|
RZ_IPI ut64 md1img_baddr(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
return MTK_MODEM_BADDR;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinAddr *>*/ *md1img_entries(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzPVector *ret = rz_pvector_new(free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
if (md1->mtk) {
|
|
RzBinAddr *entry = RZ_NEW0(RzBinAddr);
|
|
if (entry) {
|
|
entry->vaddr = md1img_baddr(bf) + (md1->mtk->file_info.jump_offset - md1->mtk->code_offset);
|
|
entry->paddr = md1->gfh_offset + md1->mtk->file_info.jump_offset;
|
|
rz_pvector_push(ret, entry);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinVirtualFile *>*/ *md1img_virtual_files(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_virtual_file_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
void **it;
|
|
rz_pvector_foreach (md1->vfiles, it) {
|
|
RzBinVirtualFile *vf = *it;
|
|
RzBinVirtualFile *clone = rz_bin_virtual_file_clone(vf);
|
|
if (clone) {
|
|
rz_pvector_push(ret, clone);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinMap *>*/ *md1img_maps(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_map_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
// Only map the md1rom section into the virtual address space.
|
|
// Other sections (md1dsp, md1drdi, certs, etc.) belong to different
|
|
// processors or address spaces and would conflict with md1rom if mapped.
|
|
// They remain accessible as virtual files for raw data viewing.
|
|
if (md1->md1rom_idx >= 0) {
|
|
Md1imgSection *sec = rz_vector_index_ptr(md1->sections, md1->md1rom_idx);
|
|
if (md1->mtk) {
|
|
ut64 paddr = md1->gfh_offset + md1->mtk->code_offset;
|
|
mtk_append_maps(md1->mtk, paddr, sec->name, ret);
|
|
} else {
|
|
RzBinMap *map = RZ_NEW0(RzBinMap);
|
|
if (map) {
|
|
map->name = rz_str_dup(sec->name);
|
|
map->vfile_name = rz_str_dup(sec->name);
|
|
map->paddr = 0;
|
|
map->psize = sec->dsize;
|
|
map->vaddr = sec->maddr;
|
|
map->vsize = sec->dsize;
|
|
map->perm = RZ_PERM_RX;
|
|
rz_pvector_push(ret, map);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinSection *>*/ *md1img_sections(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_section_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
if (md1->mtk && md1->md1rom_idx >= 0 && md1->mtk->code_size > 0) {
|
|
Md1imgSection *sec = rz_vector_index_ptr(md1->sections, md1->md1rom_idx);
|
|
RzBinSection *code = RZ_NEW0(RzBinSection);
|
|
if (code) {
|
|
code->name = rz_str_newf("%s.code", sec->name);
|
|
code->paddr = md1->gfh_offset + md1->mtk->code_offset;
|
|
code->size = md1->mtk->code_size;
|
|
code->vsize = md1->mtk->code_size;
|
|
code->vaddr = md1img_baddr(bf);
|
|
code->perm = RZ_PERM_RX;
|
|
rz_pvector_push(ret, code);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinSymbol *>*/ *md1img_symbols(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_symbol_free);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!md1->dbg_symbols) {
|
|
return ret;
|
|
}
|
|
|
|
void **it;
|
|
rz_pvector_foreach (md1->dbg_symbols, it) {
|
|
MtkDbgSymbol *dbg = *it;
|
|
RzBinSymbol *sym = RZ_NEW0(RzBinSymbol);
|
|
if (!sym) {
|
|
continue;
|
|
}
|
|
sym->name = rz_str_dup(dbg->name);
|
|
sym->vaddr = dbg->addr;
|
|
sym->size = dbg->size;
|
|
sym->type = RZ_BIN_TYPE_FUNC_STR;
|
|
|
|
// Compute paddr for symbols within mapped regions (kseg0 or kuseg).
|
|
if (md1->mtk) {
|
|
ut64 code_paddr = md1->gfh_offset + md1->mtk->code_offset;
|
|
ut64 kseg0_end = md1img_baddr(bf) + md1->mtk->code_size;
|
|
ut64 kuseg_base = md1->mtk->file_info.load_addr + md1->mtk->code_offset;
|
|
ut64 kuseg_end = kuseg_base + md1->mtk->code_size;
|
|
if (dbg->addr >= md1img_baddr(bf) && dbg->addr < kseg0_end) {
|
|
sym->paddr = code_paddr + (dbg->addr - md1img_baddr(bf));
|
|
} else if (dbg->addr >= kuseg_base && dbg->addr < kuseg_end) {
|
|
sym->paddr = code_paddr + (dbg->addr - kuseg_base);
|
|
}
|
|
}
|
|
|
|
rz_pvector_push(ret, sym);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzPVector /*<RzBinString *>*/ *md1img_strings(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
// Container format: suppress string scanning on the raw container buffer.
|
|
// Strings from the firmware payload are accessible via the mapped vfiles.
|
|
return rz_pvector_new(NULL);
|
|
}
|
|
|
|
RZ_IPI RZ_OWN RzStructuredData *md1img_structure(RZ_BORROW RZ_NONNULL RzBinFile *bf) {
|
|
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
|
|
|
Md1imgObj *md1 = bf->o->bin_obj;
|
|
RzStructuredData *root = rz_structured_data_new_map();
|
|
if (!root) {
|
|
return NULL;
|
|
}
|
|
|
|
rz_structured_data_map_add_unsigned(root, "num_sections", rz_vector_len(md1->sections), false);
|
|
|
|
RzStructuredData *secs = rz_structured_data_map_add_array(root, "sections");
|
|
if (secs) {
|
|
Md1imgSection *sec;
|
|
rz_vector_foreach (md1->sections, sec) {
|
|
RzStructuredData *s = rz_structured_data_array_add_map(secs);
|
|
if (!s) {
|
|
continue;
|
|
}
|
|
rz_structured_data_map_add_string(s, "name", sec->name);
|
|
rz_structured_data_map_add_unsigned(s, "data_size", sec->dsize, true);
|
|
rz_structured_data_map_add_unsigned(s, "data_offset", sec->data_offset, true);
|
|
rz_structured_data_map_add_unsigned(s, "maddr", sec->maddr, true);
|
|
rz_structured_data_map_add_unsigned(s, "mode", sec->mode, true);
|
|
rz_structured_data_map_add_unsigned(s, "hdr_size", sec->hdr_size, true);
|
|
rz_structured_data_map_add_unsigned(s, "hdr_version", sec->hdr_version, true);
|
|
rz_structured_data_map_add_unsigned(s, "img_type", sec->img_type, true);
|
|
rz_structured_data_map_add_unsigned(s, "img_list_end", sec->img_list_end, true);
|
|
rz_structured_data_map_add_unsigned(s, "align_size", sec->align_size, true);
|
|
}
|
|
}
|
|
|
|
// GFH structure from md1rom section
|
|
if (md1->mtk) {
|
|
MtkObj *mtk = md1->mtk;
|
|
RzStructuredData *gfh = rz_structured_data_map_add_map(root, "gfh_file_info");
|
|
if (gfh) {
|
|
rz_structured_data_map_add_unsigned(gfh, "magic_version", mtk->first_common.magic_version, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "header_block_size", mtk->first_common.size, false);
|
|
rz_structured_data_map_add_string(gfh, "header_type", mtk_gfh_type_str(mtk->first_common.type));
|
|
rz_structured_data_map_add_string(gfh, "name", mtk->file_info.name);
|
|
rz_structured_data_map_add_unsigned(gfh, "file_type", mtk->file_info.file_type, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "flash_type", mtk->file_info.flash_type, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "sig_type", mtk->file_info.sig_type, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "load_addr", mtk->file_info.load_addr, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "total_size", mtk->file_info.total_size, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "max_size", mtk->file_info.max_size, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "hdr_size", mtk->file_info.hdr_size, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "sig_size", mtk->file_info.sig_size, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "jump_offset", mtk->file_info.jump_offset, true);
|
|
rz_structured_data_map_add_unsigned(gfh, "entry_point", mtk->entry_vaddr, true);
|
|
}
|
|
|
|
if (rz_vector_len(mtk->extra_headers) > 0) {
|
|
RzStructuredData *hdrs = rz_structured_data_map_add_array(root, "gfh_headers");
|
|
if (hdrs) {
|
|
MtkGfhHeader *extra;
|
|
rz_vector_foreach (mtk->extra_headers, extra) {
|
|
RzStructuredData *h = rz_structured_data_array_add_map(hdrs);
|
|
if (!h) {
|
|
continue;
|
|
}
|
|
rz_structured_data_map_add_unsigned(h, "file_offset", extra->file_offset, true);
|
|
rz_structured_data_map_add_string(h, "type", mtk_gfh_type_str(extra->common.type));
|
|
rz_structured_data_map_add_unsigned(h, "type_id", extra->common.type, true);
|
|
rz_structured_data_map_add_unsigned(h, "size", extra->common.size, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (md1->dbg_symbols) {
|
|
rz_structured_data_map_add_unsigned(root, "debug_symbols_count",
|
|
rz_pvector_len(md1->dbg_symbols), false);
|
|
}
|
|
|
|
return root;
|
|
}
|