0% found this document useful (0 votes)
52 views11 pages

Código de Desembalaje Del PKG

The document contains C code for handling MTK packages, including functions for identifying, extracting, and processing firmware images and compressed file systems. It defines various structures and enums for package headers, manages decryption using AES keys, and utilizes threading for efficient extraction of segments. The code also includes error handling and logging for debugging purposes.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
52 views11 pages

Código de Desembalaje Del PKG

The document contains C code for handling MTK packages, including functions for identifying, extracting, and processing firmware images and compressed file systems. It defines various structures and enums for package headers, manages decryption using AES keys, and utilizes threading for efficient extraction of segments. The code also includes error handling and logging for debugging purposes.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 11

#include <unistd.

h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include "main.h" //for handle_file
#include "mfile.h"
#include "mediatek_pkg.h"
#include "lzhs/lzhs.h"
#include "util.h"
#include "util_crypto.h"
#include "thpool.h"

enum mtkpkg_variant {
OLD = 1 << 0,
NEW = 1 << 1,
THOMPSON = 1 << 2,
PHILIPS = 1 << 3,
SHARP = 1 << 4
};

#define SIZEOF_THOMPSON_HEADER 0x170


#define SIZEOF_OLD_HEADER 0x98

static int mtkpkg_variant_flags = NEW;

static struct mtkupg_header packageHeader;


static bool was_decrypted = false;

int compare_pkg_header(uint8_t *header, size_t headerSize){


struct mtkupg_header *hdr = (struct mtkupg_header *)header;

if( !strncmp(hdr->vendor_magic, HISENSE_PKG_MAGIC,


strlen(HISENSE_PKG_MAGIC)) ){
printf("[+] Found HISENSE Package\n");
return 1;
}
if( !strncmp(hdr->vendor_magic, SHARP_PKG_MAGIC, strlen(SHARP_PKG_MAGIC)) )
{
printf("[+] Found SHARP Package\n");
mtkpkg_variant_flags |= SHARP;
return 1;
}
if( !strncmp(hdr->vendor_magic, TPV_PKG_MAGIC, strlen(TPV_PKG_MAGIC)) ||
!strncmp(hdr->vendor_magic, TPV_PKG_MAGIC2,strlen(TPV_PKG_MAGIC2))
){
printf("[+] Found PHILIPS(TPV) Package\n");
return 1;
}

if( !strncmp(hdr->vendor_magic, PHILIPS_PKG_MAGIC,


strlen(PHILIPS_PKG_MAGIC))
|| !strncmp(hdr->vendor_magic, PHILIPS_PKG_MAGIC2,
strlen(PHILIPS_PKG_MAGIC2))
){
printf("[+] Found PHILIPS Package\n");
return 1;
}
if( !strncmp(hdr->mtk_magic, MTK_FIRMWARE_MAGIC,
strlen(MTK_FIRMWARE_MAGIC)) ){
printf("[+] Found UNKNOWN Package (Magic: '%.*s')\n",
member_size(struct mtkupg_header, vendor_magic),
hdr->vendor_magic
);
return 1;
}

return 0;
}

int compare_content_header(uint8_t *header, size_t headerSize){


struct mtkpkg_data *data = (struct mtkpkg_data *)header;
if ( !strncmp(data->header.mtk_reserved, MTK_RESERVED_MAGIC,
strlen(MTK_RESERVED_MAGIC)) ){
return 1;
}
return 0;
}

bool is_known_partition(struct mtkpkg *pak){


const char *likelyPartitionNames[] = {
"cfig",
"ixml",
"tzbp",
NULL
};

char **curPartName = likelyPartitionNames;


for(int nameIndex=0; *curPartName != NULL; nameIndex++){
if(!strncmp(pak->header.pakName, *curPartName, sizeof(pak-
>header.pakName))){
return true;
}
curPartName++;
}
return false;
}

MFILE *is_mtk_pkg(const char *pkgfile){


setKeyFile_MTK();

MFILE *mf = mopen(pkgfile, O_RDONLY);


if(!mf){
err_exit("Cannot open file %s\n", pkgfile);
}

uint8_t *data = mdata(mf, uint8_t);


void *decryptedHeader = NULL;
KeyPair *headerKey = NULL;

do {
if((headerKey = find_AES_key(data, UPG_HEADER_SIZE,
compare_pkg_header, KEY_CBC, (void **)&decryptedHeader, 0)) != NULL){
break;
}

/* It failed, but we want to check for Philips.


* Philips has an additional 0x80 header before the normal PKG one
*/
if((headerKey = find_AES_key(data + PHILIPS_HEADER_SIZE,
UPG_HEADER_SIZE, compare_pkg_header, KEY_CBC, (void **)&decryptedHeader, 0)) !=
NULL){
mtkpkg_variant_flags |= PHILIPS;
}
} while(0);

if(headerKey != NULL){
was_decrypted = true;
memcpy(&packageHeader, decryptedHeader, sizeof(packageHeader));
free(headerKey);
return mf;
}

/* No AES key found to decrypt the header. Try to check if it's a MTK PKG
anyways
* This method can return false for valid packages as the order of
partitions isn't fixed
*/

/* First pak doesn't have extended fields */


struct mtkpkg *firstPak = (struct mtkpkg *)(data + sizeof(struct
mtkupg_header));
if(is_known_partition(firstPak))
return mf;

firstPak = (struct mtkpkg *)(data + SIZEOF_OLD_HEADER);


if(is_known_partition(firstPak)){
mtkpkg_variant_flags = OLD;
return mf;
}

firstPak = (struct mtkpkg *)(data + SIZEOF_OLD_HEADER +


PHILIPS_HEADER_SIZE);
if(is_known_partition(firstPak)){
mtkpkg_variant_flags = OLD | PHILIPS;
return mf;
}

firstPak = (struct mtkpkg *)(data + SIZEOF_THOMPSON_HEADER);


if(is_known_partition(firstPak)){
mtkpkg_variant_flags = NEW | THOMPSON;
return mf;
}

mclose(mf);
return NULL;
}

#define SIZEOF_FIRM_HEADERS 0x90

MFILE *is_firm_image(const char *pkg){


MFILE *mf = mopen(pkg, O_RDONLY);
if(!mf){
err_exit("Cannot open file %s\n", pkg);
}
if(msize(mf) < (SIZEOF_FIRM_HEADERS + 16)){
mclose(mf);
return NULL;
}

if(is_lzhs_mem(mf, SIZEOF_FIRM_HEADERS)){
return mf;
}
mclose(mf);
return NULL;
}

int extract_firm_image(MFILE *mf){


return process_segment(mf, SIZEOF_FIRM_HEADERS, "firm");
}

MFILE *is_lzhs_fs(const char *pkg){


MFILE *mf = mopen(pkg, O_RDONLY);
if(!mf){
err_exit("Cannot open file %s\n", pkg);
}

uint8_t *data = mdata(mf, uint8_t);

off_t start = MTK_EXT_LZHS_OFFSET;


if(is_nfsb_mem(mf, SHARP_PKG_HEADER_SIZE)){
start += SHARP_PKG_HEADER_SIZE;
}

if(msize(mf) < (start + sizeof(struct lzhs_header))){


goto fail;
}

if(
is_lzhs_mem(mf, start) &&
is_lzhs_mem(mf, start + sizeof(struct lzhs_header)) &&
// First LZHS header contains number of segment in checksum. Make
sure that it is the first segment
((struct lzhs_header *)&data[start])->checksum == 1
){
return mf;
}

fail:
mclose(mf);
return NULL;
}

/* Arguments passed to the thread function */


struct thread_arg {
MFILE *mf;
off_t offset;
char *filename;
uint blockNo;
};

void process_block(struct thread_arg *arg){


printf("[+] Extracting %u...\n", arg->blockNo);
uint8_t out_checksum = 0x00;
cursor_t *out_cur = lzhs_decode(arg->mf, arg->offset, NULL, &out_checksum);
if(out_cur == NULL || (intptr_t)out_cur < 0){
err_exit("LZHS decode failed\n");
}

MFILE *out = mfopen(arg->filename, "w+");


if(!out){
err_exit("mfopen failed for file '%s'\n", arg->filename);
}
mfile_map(out, out_cur->size);
memcpy(
mdata(out, void),
out_cur->ptr,
out_cur->size
);
mclose(out);
free(out_cur);

free(arg->filename);
free(arg);
}

/*
* Hisense (or Mediatek?) uses an ext4 filesystem splitted in chunks, compressed
with LZHS
* They use 2 LZHS header for each chunk
* The first header contains the chunk number, and the compressed size includes the
outer lzhs header (+16)
* The second header contains the actual data
*/
void extract_lzhs_fs(MFILE *mf, const char *dest_file, config_opts_t *config_opts){
int is_sharp = 0;
uint8_t *data = mdata(mf, uint8_t);
if(is_nfsb_mem(mf, SHARP_PKG_HEADER_SIZE)){
data += SHARP_PKG_HEADER_SIZE;
is_sharp = 1;
}

FILE *out_file = fopen(dest_file, "w+");


if(!out_file){
err_exit("Cannot open %s for writing\n", dest_file);
}

char *dir = my_dirname(dest_file);


char *file = my_basename(dest_file);
char *base = remove_ext(file);

char *tmpdir;
asprintf(&tmpdir, "%s/tmp", dir);
createFolder(tmpdir);

printf("Copying 0x%08X bytes\n", MTK_EXT_LZHS_OFFSET);

/* Copy first MB as-is (uncompressed) */


fwrite (
data,
MTK_EXT_LZHS_OFFSET,
1,
out_file
);

data += MTK_EXT_LZHS_OFFSET;

int nThreads = sysconf(_SC_NPROCESSORS_ONLN);


printf("[+] Max threads: %d\n", nThreads);
threadpool thpool = thpool_init(nThreads);

uint segNo = 0;
while(moff(mf, data) < msize(mf)){
struct lzhs_header *main_hdr = (struct lzhs_header *)data;
struct lzhs_header *seg_hdr = (struct lzhs_header *)(data +
sizeof(*main_hdr));

printf("\n[0x%08X] segment #%u (compressed='%u bytes',


uncompressed='%u bytes')\n",
moff(mf, main_hdr),
main_hdr->checksum,
seg_hdr->compressedSize, seg_hdr->uncompressedSize);

char *outSeg;
asprintf(&outSeg, "%s/%s.%d", tmpdir, base, (segNo++) + 1);
struct thread_arg *arg = calloc(1, sizeof(struct thread_arg));
arg->mf = mf;
arg->offset = moff(mf, seg_hdr);
arg->filename = outSeg;
arg->blockNo = main_hdr->checksum;

thpool_add_work(thpool, (void *)process_block, arg);

uint pad;
pad = (pad = (seg_hdr->compressedSize % 16)) == 0 ? 0 : (16 - pad);

data += (
sizeof(*main_hdr) + sizeof(*seg_hdr) +
seg_hdr->compressedSize +
pad
);
}

thpool_wait(thpool);
thpool_destroy(thpool);

int i;
for(i=1; i<=segNo; i++){
printf("[+] Joining Segment %d\n", i);
char *outSeg;
asprintf(&outSeg, "%s/%s.%d", tmpdir, base, i);

MFILE *seg = mopen(outSeg, O_RDONLY);


fwrite(mdata(seg, void), msize(seg), 1, out_file);
mclose(seg);

unlink(outSeg);
free(outSeg);
}

rmrf(tmpdir);
free(tmpdir);
fclose(out_file);

free(dir);
free(file);
free(base);

if(is_sharp){
handle_file(dest_file, config_opts);
}
}

void print_pkg_header(struct mtkupg_header *hdr){


hexdump(hdr, sizeof(*hdr));

printf("======== Firmware Info ========\n");


printf("| Product Name: %s\n", hdr->product_name);
printf("| Firmware ID : %.*s\n",
member_size(struct mtkupg_header, vendor_magic) +
member_size(struct mtkupg_header, mtk_magic) +
member_size(struct mtkupg_header, vendor_info),
hdr->vendor_magic
);
printf("| File Size: %u bytes\n", hdr->fileSize);
printf("| Platform Type: 0x%02X\n", hdr->platform);
printf("======== Firmware Info ========\n");
}

static off_t get_mtkpkg_offset(){


if((mtkpkg_variant_flags & THOMPSON) == THOMPSON){
return SIZEOF_THOMPSON_HEADER;
}

off_t offset = 0;
if((mtkpkg_variant_flags & NEW) == NEW){
offset += sizeof(struct mtkupg_header);
} else if((mtkpkg_variant_flags & OLD) == OLD){
offset += SIZEOF_OLD_HEADER;
}

if((mtkpkg_variant_flags & PHILIPS) == PHILIPS){


offset += PHILIPS_HEADER_SIZE;
}

return offset;
}

void extract_mtk_pkg(const char *pkgFile, config_opts_t *config_opts){


MFILE *mf = mopen_private(pkgFile, O_RDONLY);
mprotect(mf->pMem, msize(mf), PROT_READ | PROT_WRITE);

off_t offset = get_mtkpkg_offset();


uint8_t *data = mdata(mf, uint8_t) + offset;

char *file_name = my_basename(mf->path);


char *file_base = remove_ext(file_name);

struct mtkupg_header *hdr = (was_decrypted) ? &packageHeader : NULL;


if(hdr != NULL)
print_pkg_header(hdr);

if(hdr != NULL){
// Use product name for now (version would be better)
sprintf(config_opts->dest_dir, "%s/%s", config_opts->dest_dir, hdr-
>product_name);
} else {
sprintf(config_opts->dest_dir, "%s/%s", config_opts->dest_dir,
file_base);
}
createFolder(config_opts->dest_dir);

KeyPair *dataKey = NULL;

int pakNo;
for(pakNo=0; moff(mf, data) < msize(mf); pakNo++){
struct mtkpkg *pak = (struct mtkpkg *)data;
if((mtkpkg_variant_flags & PHILIPS) == PHILIPS && moff(mf, data) +
PHILIPS_SIGNATURE_SIZE == msize(mf)){
printf("-- RSA 2048 Signature --\n");
hexdump(data, PHILIPS_SIGNATURE_SIZE);
//Philips RSA-2048 signature
break;
}

size_t cryptedHeaderSize = ((mtkpkg_variant_flags & NEW) == NEW) ?


sizeof(pak->content.header) : 0;

/* Skip pak header and crypted header */


data += sizeof(pak->header) + cryptedHeaderSize;

uint8_t *pkgData = (uint8_t *)&(pak->content.header);


size_t dataSize = sizeof(pak->content.header);
size_t pkgSize = pak->header.pakSize;
if(pkgSize == 0){
goto save_file;
}

if((pak->header.flags & PAK_FLAG_ENCRYPTED) == PAK_FLAG_ENCRYPTED){


dataSize += pak->header.pakSize;
}

#pragma region FindAesKey


uint8_t *decryptedPkgData = NULL;

if(was_decrypted){
if(dataKey == NULL){
dataKey = find_AES_key(
pkgData,
dataSize,
compare_content_header,
KEY_CBC,
(void **)&decryptedPkgData,
1
);
int success = dataKey != NULL;
if(success){
/* Copy decrypted data */
memcpy(pkgData, decryptedPkgData,
dataSize);
free(decryptedPkgData);
} else if(was_decrypted) {
/* Try to decrypt by using vendorMagic
repeated 4 times, ivec 0 */
do {
AES_KEY aesKey;
uint8_t keybuf[16];

uint i;
for(i=0; i<4; i++){
memcpy(&keybuf[4 * i], hdr-
>vendor_magic, sizeof(uint32_t));
}

AES_set_decrypt_key((uint8_t
*)&keybuf, 128, &aesKey);

dataKey = calloc(1,
sizeof(KeyPair)); //also fills ivec with zeros
memcpy(&(dataKey->key), &aesKey,
sizeof(aesKey));

uint8_t iv_tmp[16];
memcpy(&iv_tmp, &(dataKey->ivec),
sizeof(iv_tmp));
AES_cbc_encrypt(
pkgData, pkgData,
dataSize, &(dataKey->key),
(void *)&iv_tmp,
AES_DECRYPT
);

success =
compare_content_header(pkgData, sizeof(struct mtkpkg_data));
} while(0);
}
if(!success){
if((pak->header.flags & PAK_FLAG_ENCRYPTED)
== PAK_FLAG_ENCRYPTED){
printf("[-] Couldn't decrypt data!\
n");
} else {
printf("[-] Couldn't decrypt
header!\n");
}
}
} else {
#pragma endregion
uint8_t iv_tmp[16];
memcpy(&iv_tmp, &(dataKey->ivec), sizeof(iv_tmp));
AES_cbc_encrypt(
pkgData, pkgData,
dataSize, &(dataKey->key),
(void *)&iv_tmp, AES_DECRYPT
);
int success = compare_content_header(pkgData,
sizeof(struct mtkpkg_data));
if(!success){
fprintf(stderr, "[!] WARNING: MTK Crypted
header not found, continuing anyways...\n");
}
}
}

if((mtkpkg_variant_flags & NEW) == NEW){


// Skip the mtk header (reserved inc)
pkgData += sizeof(struct mtkpkg_crypted_header);
}

printf("\nPAK #%u %s (name='%s', offset='0x%lx', size='%u bytes'",


pakNo + 1,
((pak->header.flags & PAK_FLAG_ENCRYPTED) ==
PAK_FLAG_ENCRYPTED) ? "[ENCRYPTED]" : "",
pak->header.pakName,
moff(mf, data),
pak->header.pakSize
);

struct mtkpkg_plat *ext = (struct mtkpkg_plat *)pkgData;

/* Parse the fields at the start of pkgData, and skip them */


if(!strncmp(ext->platform, MTK_PAK_MAGIC, strlen(MTK_PAK_MAGIC))){
uint8_t *extData = (uint8_t *)&(ext->otaID_len);
/* otaID is optional. if we have it, it's preceded by its
length. If we don't have it, we have the iPAD magic instead */
int has_otaID = strncmp(extData, MTK_PAD_MAGIC,
strlen(MTK_PAD_MAGIC)) != 0;
if(has_otaID){
printf(", platform='%s', otaid='%s'", ext-
>platform, ext->otaID);
if(pakNo == 1 && hdr == NULL){
sprintf(config_opts->dest_dir, "%s/%s",
config_opts->dest_dir, ext->otaID);
createFolder(config_opts->dest_dir);
}
} else if(pakNo == 1 && hdr == NULL){
sprintf(config_opts->dest_dir, "%s/%s",
config_opts->dest_dir, file_base);
createFolder(config_opts->dest_dir);
}

/* Skip the headers to get to the data */


uint skip = sizeof(*ext);
if(has_otaID){
skip += ext->otaID_len;
}
if(skip < MTK_EXTHDR_SIZE){
skip += (MTK_EXTHDR_SIZE - skip);
}

pkgData += skip;
pkgSize -= skip;
}

save_file:
printf(")\n");
char *dest_path = NULL;
asprintf(&dest_path, "%s/%.*s.pak",
config_opts->dest_dir,
member_size(struct mtkpkg_header, pakName), pak-
>header.pakName
);

MFILE *out = mfopen(dest_path, "w+");


if(!out){
err_exit("Cannot open %s for writing\n", dest_path);
}

printf("Saving partition (%s) to file %s\n\n", pak->header.pakName,


dest_path);

if(pkgSize == 0)
goto saved_file;

mfile_map(out, pkgSize);
mwrite(pkgData, pkgSize, 1, out);

saved_file:
mclose(out);

if(dest_path != NULL && pkgSize != 0){


handle_file(dest_path, config_opts);
}
if(dest_path != NULL){
free(dest_path);
}

data += pak->header.pakSize;
}

if(dataKey != NULL){
free(dataKey);
}

free(file_name);
free(file_base);
mclose(mf);
}

You might also like