Created
November 7, 2025 20:14
-
-
Save iscle/83044319e7a66df26547024a07732440 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * GFH Header Parser and Payload Extractor | |
| * Based on MediaTek BootROM GFH (Generic File Header) format | |
| * | |
| * This program opens a file with 0x014d4d4d header, displays its information, | |
| * and dumps the payload to a file. | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <fcntl.h> | |
| #include <sys/stat.h> | |
| #include <errno.h> | |
| /* GFH Header definitions (all fields are little-endian) */ | |
| #define GFH_HEADER_MAGIC 0x4D4D4D | |
| #define GFH_HEADER_VERSION_SHIFT 24 | |
| #define GFH_HEADER_MAGIC_VERSION 0x014d4d4d /* Version 1, Magic 0x4D4D4D */ | |
| #define GFH_TYPE_FILE_INFO 0 | |
| #define GFH_TYPE_BL_INFO 1 | |
| #define GFH_TYPE_BROM_CFG 7 | |
| #define GFH_TYPE_BL_SEC_KEY 3 | |
| #define GFH_TYPE_ANTI_CLONE 2 | |
| #define GFH_TYPE_BROM_SEC_CFG 8 | |
| #define GFH_FILE_INFO_NAME "FILE_INFO" | |
| #define GFH_FLASH_TYPE_GEN 5 | |
| #define GFH_FLASH_TYPE_NAND 2 | |
| #define GFH_SIG_TYPE_NONE 0 | |
| #define GFH_SIG_TYPE_SHA256 1 | |
| #define SHA256_SUM_LEN 32 | |
| struct gfh_common_header { | |
| uint32_t magic_version; | |
| uint16_t size; | |
| uint16_t type; | |
| } __attribute__((packed)); | |
| struct gfh_file_info { | |
| struct gfh_common_header gfh; | |
| char name[12]; | |
| uint32_t unused; | |
| uint16_t file_type; | |
| uint8_t flash_type; | |
| uint8_t sig_type; | |
| uint32_t load_addr; | |
| uint32_t total_size; | |
| uint32_t max_size; | |
| uint32_t hdr_size; | |
| uint32_t sig_size; | |
| uint32_t jump_offset; | |
| uint32_t processed; | |
| } __attribute__((packed)); | |
| /* Helper functions for little-endian conversion */ | |
| static inline uint16_t le16_to_cpu(uint16_t val) | |
| { | |
| return val; | |
| } | |
| static inline uint32_t le32_to_cpu(uint32_t val) | |
| { | |
| return val; | |
| } | |
| /* Find GFH header in the file */ | |
| static off_t find_gfh_header(const uint8_t *data, size_t size) | |
| { | |
| size_t i; | |
| /* Search for GFH magic (0x014d4d4d) */ | |
| for (i = 0; i < size - sizeof(struct gfh_common_header); i++) { | |
| uint32_t magic = *(uint32_t *)(data + i); | |
| if (magic == GFH_HEADER_MAGIC_VERSION) { | |
| return (off_t)i; | |
| } | |
| } | |
| return -1; | |
| } | |
| /* Extract and display GFH information */ | |
| static int parse_gfh_header(const uint8_t *data, size_t file_size, off_t gfh_offset) | |
| { | |
| const struct gfh_file_info *file_info; | |
| uint32_t magic_version, version, magic; | |
| uint16_t size, type; | |
| uint32_t load_addr, total_size, hdr_size, sig_size; | |
| uint8_t flash_type, sig_type; | |
| char flash_type_str[32]; | |
| file_info = (const struct gfh_file_info *)(data + gfh_offset); | |
| /* Parse common header */ | |
| magic_version = le32_to_cpu(file_info->gfh.magic_version); | |
| version = (magic_version >> GFH_HEADER_VERSION_SHIFT) & 0xFF; | |
| magic = magic_version & 0xFFFFFF; | |
| size = le16_to_cpu(file_info->gfh.size); | |
| type = le16_to_cpu(file_info->gfh.type); | |
| printf("=== GFH Header Information ===\n"); | |
| printf("Magic Version: 0x%08x\n", magic_version); | |
| printf(" Version: %u\n", version); | |
| printf(" Magic: 0x%06x\n", magic); | |
| printf("Header Size: %u bytes\n", size); | |
| printf("Header Type: %u", type); | |
| if (type == GFH_TYPE_FILE_INFO) | |
| printf(" (FILE_INFO)"); | |
| else if (type == GFH_TYPE_BL_INFO) | |
| printf(" (BL_INFO)"); | |
| else if (type == GFH_TYPE_BROM_CFG) | |
| printf(" (BROM_CFG)"); | |
| printf("\n"); | |
| /* Verify it's a FILE_INFO header */ | |
| if (type != GFH_TYPE_FILE_INFO) { | |
| fprintf(stderr, "Error: Expected FILE_INFO header (type 0), got type %u\n", type); | |
| return -1; | |
| } | |
| /* Verify name */ | |
| if (strncmp(file_info->name, GFH_FILE_INFO_NAME, sizeof(file_info->name)) != 0) { | |
| fprintf(stderr, "Warning: Name mismatch. Expected '%s', got '%.12s'\n", | |
| GFH_FILE_INFO_NAME, file_info->name); | |
| } | |
| /* Parse file info */ | |
| flash_type = file_info->flash_type; | |
| sig_type = file_info->sig_type; | |
| load_addr = le32_to_cpu(file_info->load_addr); | |
| total_size = le32_to_cpu(file_info->total_size); | |
| hdr_size = le32_to_cpu(file_info->hdr_size); | |
| sig_size = le32_to_cpu(file_info->sig_size); | |
| if (flash_type == GFH_FLASH_TYPE_GEN) | |
| strcpy(flash_type_str, "Generic (NOR/SD/eMMC)"); | |
| else if (flash_type == GFH_FLASH_TYPE_NAND) | |
| strcpy(flash_type_str, "NAND"); | |
| else | |
| snprintf(flash_type_str, sizeof(flash_type_str), "Unknown (0x%02x)", flash_type); | |
| printf("\n=== File Information ===\n"); | |
| printf("Name: %.12s\n", file_info->name); | |
| printf("File Type: %u\n", le16_to_cpu(file_info->file_type)); | |
| printf("Flash Type: %s\n", flash_type_str); | |
| printf("Signature Type: "); | |
| if (sig_type == GFH_SIG_TYPE_NONE) | |
| printf("None\n"); | |
| else if (sig_type == GFH_SIG_TYPE_SHA256) | |
| printf("SHA256\n"); | |
| else | |
| printf("Unknown (0x%02x)\n", sig_type); | |
| printf("Load Address: 0x%08x\n", load_addr); | |
| printf("Total Size: %u bytes (0x%x)\n", total_size, total_size); | |
| printf("Header Size: %u bytes (0x%x)\n", hdr_size, hdr_size); | |
| printf("Signature Size: %u bytes\n", sig_size); | |
| printf("Jump Offset: 0x%08x\n", le32_to_cpu(file_info->jump_offset)); | |
| printf("Processed: %u\n", le32_to_cpu(file_info->processed)); | |
| return 0; | |
| } | |
| /* Extract payload to file */ | |
| static int extract_payload(const uint8_t *data, size_t file_size, off_t gfh_offset, | |
| const char *output_file) | |
| { | |
| const struct gfh_file_info *file_info; | |
| FILE *out_fp; | |
| off_t payload_offset; | |
| size_t payload_size; | |
| size_t written; | |
| file_info = (const struct gfh_file_info *)(data + gfh_offset); | |
| /* Calculate payload offset and size */ | |
| payload_offset = gfh_offset + le32_to_cpu(file_info->hdr_size); | |
| payload_size = le32_to_cpu(file_info->total_size); | |
| /* Subtract signature size if present */ | |
| if (file_info->sig_type == GFH_SIG_TYPE_SHA256) { | |
| uint32_t sig_size = le32_to_cpu(file_info->sig_size); | |
| if (payload_size >= sig_size) | |
| payload_size -= sig_size; | |
| } | |
| /* Check bounds */ | |
| if (payload_offset + payload_size > file_size) { | |
| fprintf(stderr, "Error: Payload extends beyond file size\n"); | |
| fprintf(stderr, " Payload offset: 0x%lx\n", (unsigned long)payload_offset); | |
| fprintf(stderr, " Payload size: %zu bytes\n", payload_size); | |
| fprintf(stderr, " File size: %zu bytes\n", file_size); | |
| return -1; | |
| } | |
| /* Open output file */ | |
| out_fp = fopen(output_file, "wb"); | |
| if (!out_fp) { | |
| fprintf(stderr, "Error: Cannot open output file '%s': %s\n", | |
| output_file, strerror(errno)); | |
| return -1; | |
| } | |
| /* Write payload */ | |
| written = fwrite(data + payload_offset, 1, payload_size, out_fp); | |
| if (written != payload_size) { | |
| fprintf(stderr, "Error: Failed to write payload: %s\n", strerror(errno)); | |
| fclose(out_fp); | |
| return -1; | |
| } | |
| fclose(out_fp); | |
| printf("\n=== Payload Extraction ===\n"); | |
| printf("Payload Offset: 0x%lx\n", (unsigned long)payload_offset); | |
| printf("Payload Size: %zu bytes\n", payload_size); | |
| printf("Output File: %s\n", output_file); | |
| printf("Extraction: Success\n"); | |
| return 0; | |
| } | |
| static void usage(const char *progname) | |
| { | |
| fprintf(stderr, "Usage: %s <input_file> [output_file]\n", progname); | |
| fprintf(stderr, "\n"); | |
| fprintf(stderr, " input_file - File containing GFH header (0x014d4d4d)\n"); | |
| fprintf(stderr, " output_file - Output file for payload (default: payload.bin)\n"); | |
| fprintf(stderr, "\n"); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| const char *input_file, *output_file; | |
| FILE *fp; | |
| uint8_t *data; | |
| struct stat st; | |
| size_t file_size; | |
| off_t gfh_offset; | |
| int ret = 0; | |
| if (argc < 2) { | |
| usage(argv[0]); | |
| return 1; | |
| } | |
| input_file = argv[1]; | |
| output_file = (argc >= 3) ? argv[2] : "payload.bin"; | |
| /* Open input file */ | |
| fp = fopen(input_file, "rb"); | |
| if (!fp) { | |
| fprintf(stderr, "Error: Cannot open input file '%s': %s\n", | |
| input_file, strerror(errno)); | |
| return 1; | |
| } | |
| /* Get file size */ | |
| if (fstat(fileno(fp), &st) < 0) { | |
| fprintf(stderr, "Error: Cannot stat file '%s': %s\n", | |
| input_file, strerror(errno)); | |
| fclose(fp); | |
| return 1; | |
| } | |
| file_size = st.st_size; | |
| if (file_size == 0) { | |
| fprintf(stderr, "Error: Input file is empty\n"); | |
| fclose(fp); | |
| return 1; | |
| } | |
| /* Read entire file into memory */ | |
| data = malloc(file_size); | |
| if (!data) { | |
| fprintf(stderr, "Error: Cannot allocate memory: %s\n", strerror(errno)); | |
| fclose(fp); | |
| return 1; | |
| } | |
| if (fread(data, 1, file_size, fp) != file_size) { | |
| fprintf(stderr, "Error: Failed to read file: %s\n", strerror(errno)); | |
| free(data); | |
| fclose(fp); | |
| return 1; | |
| } | |
| fclose(fp); | |
| /* Find GFH header */ | |
| gfh_offset = find_gfh_header(data, file_size); | |
| if (gfh_offset < 0) { | |
| fprintf(stderr, "Error: GFH header (0x014d4d4d) not found in file\n"); | |
| free(data); | |
| return 1; | |
| } | |
| printf("GFH header found at offset: 0x%lx\n\n", (unsigned long)gfh_offset); | |
| /* Parse and display header information */ | |
| if (parse_gfh_header(data, file_size, gfh_offset) < 0) { | |
| free(data); | |
| return 1; | |
| } | |
| /* Extract payload */ | |
| if (extract_payload(data, file_size, gfh_offset, output_file) < 0) { | |
| ret = 1; | |
| } | |
| free(data); | |
| return ret; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment