Skip to content

Instantly share code, notes, and snippets.

@peremen
Last active June 6, 2021 23:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save peremen/71330cbb4a6556358b938e0e353255a6 to your computer and use it in GitHub Desktop.
Save peremen/71330cbb4a6556358b938e0e353255a6 to your computer and use it in GitHub Desktop.
/**
* eut_extract.c: extract firmware image from C811 EUT
*
* Copyright 2014 by Shinjo Park <peremen@gmail.com>
*
* eut_extract.c is free software: you can redistribute
* it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* eut_extract.c is distributed in the hope that it will
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#pragma pack(push, 1)
struct partition_info {
char dummy[8];
char name[32];
uint32_t start_addr;
uint32_t actual_size;
uint32_t start_sector;
uint32_t unknown;
uint32_t flags;
uint32_t part_size;
};
#pragma pack(pop)
void decode_mbn(char *in_fname, char *out_dir) {
int extract = 1;
int decrypt = 1;
int in_fd = open(in_fname, O_RDONLY);
int out_fd, i, j;
char *header_buf, *content_buf;
char fname_buf[128];
uint32_t magic, num_part;
off_t pos = 0, remaining_size, zerofill_size;
struct stat sb;
struct partition_info cur_part;
const int cbuf_size = (1 << 20);
uint32_t xor_key = 0, *content_xor_buf, header_byte = 0;
if (in_fd < 0) {
printf("Error while opening %s for reading\n", in_fname);
return;
}
header_buf = malloc(0x800);
if (!header_buf) return;
lseek(in_fd, -0x800, SEEK_END);
read(in_fd, header_buf, 0x800);
if (strncmp(header_buf, "GG3_ENCODED_BIN", 0xE) != 0) {
printf("Metadata invalid\n");
return;
}
pos += 0xF;
magic = *(uint32_t *)(header_buf + pos);
num_part = *(uint32_t *)(header_buf + pos + 4);
printf("Magic = 0x%x, Number of partitions = %d\n", magic, num_part);
pos += 8;
// 01000100 00020000 00000000 0096DA3B
// unknown unknown unknown datasize
pos += 16;
if (stat(out_dir, &sb) == -1){
if (errno == ENOENT) {
mkdir(out_dir, 0777);
} else {
return;
}
} else {
if (sb.st_mode & S_IFDIR != S_IFDIR) {
printf("Output path %s is not directory\n", out_dir);
return;
}
}
content_buf = malloc(cbuf_size);
for(i = 0; i < num_part; i++) {
memcpy(&cur_part, header_buf + (0xF + 8 + 16) +
sizeof(struct partition_info) * i, sizeof(struct partition_info));
printf("PartName:%-20s, Flags:%d, ActualSize: 0x%-10x, StartSector: 0x%-10x, StartAddr: 0x%-10x, PartitionSize: 0x%-10x\n", cur_part.name, cur_part.flags, cur_part.actual_size,
cur_part.start_sector, cur_part.start_addr, cur_part.part_size * 512);
if (cur_part.flags == 1) continue;
lseek(in_fd, cur_part.start_addr, SEEK_SET);
read(in_fd, content_buf, 4);
content_xor_buf = (uint32_t *)content_buf;
header_byte = content_xor_buf[0];
if (strncasecmp(cur_part.name, "sbl1.mbn", 8) == 0) {
// xor_key = 0x4861b79e;
xor_key = (header_byte ^ 0x844bdcd1);
} else if (strncasecmp(cur_part.name, "sbl2.mbn", 8) == 0) {
// xor_key = 0x4510baef;
xor_key = (header_byte ^ 0x16);
} else if (strncasecmp(cur_part.name, "sbl3.mbn", 8) == 0) {
// xor_key = 0x4861b79e;
xor_key = (header_byte ^ 0x18);
} else if (strncasecmp(cur_part.name, "tz.mbn", 6) == 0) {
// xor_key = 0x69f79608;
xor_key = (header_byte ^ 0x19);
} else if (strncasecmp(cur_part.name, "rpm.mbn", 7) == 0) {
// xor_key = 0x430ebcf1;
xor_key = (header_byte ^ 0x17);
} else if (strncasecmp(cur_part.name, "boot.img", 8) == 0) {
// xor_key = 0x4d37b2c8;
xor_key = (header_byte ^ 0x52444e41);
} else if (strncasecmp(cur_part.name, "recovery.img", 12) == 0) {
// xor_key = 0x6ed69129;
xor_key = (header_byte ^ 0x52444e41);
} else if (strncasecmp(cur_part.name, "emmc_appsboot.mbn", 17) == 0) {
// xor_key = 0x34e0cb1f;
xor_key = (header_byte ^ 0x5);
} else if (strncasecmp(cur_part.name, "NON-HLOS.bin", 12) == 0) {
// xor_key = 0x4d37b2c8;
xor_key = (header_byte ^ 0x4d903ceb);
} else if ((strncasecmp(cur_part.name, "cache.img.ext4", 14) == 0) ||
(strncasecmp(cur_part.name, "persist.img.ext4", 16) == 0) ||
(strncasecmp(cur_part.name, "system.img.ext4", 15) == 0) ||
(strncasecmp(cur_part.name, "userdata.img.ext4", 17) == 0)) {
xor_key = (header_byte ^ 0xed26ff3a);
} else if (strncasecmp(cur_part.name, "gpt_main0.bin", 13) == 0) {
xor_key = 0x6ed69129;
} else if (strncasecmp(cur_part.name, "gpt_backup0.bin", 15) == 0) {
xor_key = 0x4d37b2c8;
} else {
printf("Warning: unknown partition %s, not decrypting\n", cur_part.name);
xor_key = 0;
}
if (!decrypt) xor_key = 0;
if (!extract) continue;
snprintf(fname_buf, 128, "%s/%s", out_dir, cur_part.name);
out_fd = open(fname_buf, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (out_fd == -1) {
printf("Write file open error, errno=%d\n", errno);
return;
}
lseek(in_fd, cur_part.start_addr, SEEK_SET);
if (cur_part.actual_size <= cbuf_size) {
read(in_fd, content_buf, cur_part.actual_size);
remaining_size = cur_part.actual_size;
} else {
remaining_size = cur_part.actual_size;
while (remaining_size > cbuf_size) {
read(in_fd, content_buf, cbuf_size);
if (xor_key != 0) {
content_xor_buf = (uint32_t *)content_buf;
for (j = 0; j < cbuf_size / 4; j++) {
if (content_xor_buf[j] != 0) content_xor_buf[j] ^= xor_key;
}
}
if (write(out_fd, content_buf, cbuf_size) == -1) {
printf("Write error, errno=%d\n", errno);
return;
}
remaining_size -= cbuf_size;
}
read(in_fd, content_buf, remaining_size);
}
if (remaining_size > 0) {
if (xor_key != 0) {
content_xor_buf = (uint32_t *)content_buf;
for (j = 0; j < remaining_size / 4; j++) {
if (content_xor_buf[j] != 0) content_xor_buf[j] ^= xor_key;
}
}
if (write(out_fd, content_buf, remaining_size) == -1) {
printf("Write error, errno=%d\n", errno);
return;
}
}
memset(content_buf, 0, cbuf_size);
if (cur_part.flags == 2) {
zerofill_size = cur_part.part_size * 512 - cur_part.actual_size;
if (zerofill_size > 0) {
remaining_size = zerofill_size;
while (remaining_size > cbuf_size) {
write(out_fd, content_buf, cbuf_size);
remaining_size -= cbuf_size;
}
write(out_fd, content_buf, remaining_size);
}
}
close(out_fd);
}
close(in_fd);
free(content_buf);
}
int main(int argc, char** argv) {
char *in_fname = NULL, *out_dir = NULL;
printf("C811 EUT extracor by Shinjo Park <peremen@gmail.com>\n");
if (argc != 3) {
printf("Usage: %s input.mbn output_dir\n", argv[0]);
exit(0);
}
in_fname = argv[1];
out_dir = argv[2];
decode_mbn(in_fname, out_dir);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment