Last active
June 6, 2021 23:36
-
-
Save peremen/71330cbb4a6556358b938e0e353255a6 to your computer and use it in GitHub Desktop.
This file contains 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
/** | |
* 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