-
-
Save mrnuke/2dbcc6afd40c8d5a819457dfff8cdbcf to your computer and use it in GitHub Desktop.
Extract TP-Link firmware images
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
#!/usr/bin/env python | |
import argparse | |
import pprint | |
import struct | |
import hashlib | |
TPLINK_RSA_SIG_LEN = 128 | |
def align_leftover(num, pow2): | |
mask = pow2 - 1 | |
return (pow2 - num & mask) & mask | |
def decode_header(raw_header): | |
header = {} | |
fmt = '>16s16s200s4I16s16s16s16s16s16s16s16s16s16s104x' | |
fields = struct.unpack(fmt, raw_header) | |
header['md5'] = fields[0].hex() | |
header['product_id'] = fields[1].hex() | |
header['imagename'] = fields[2].decode("utf-8").rstrip('\0') | |
header['rootfs_size'] = fields[3] | |
header['uimage_size'] = fields[4] | |
header['uboot_size'] = fields[5] | |
header['compression_type'] = fields[6] | |
header['mtd_uboot_size'] = fields[7].decode("utf-8").rstrip('\0') | |
header['mtd_kernel_offset'] = fields[8].decode("utf-8").rstrip('\0') | |
header['mtd_kernel_size'] = fields[9].decode("utf-8").rstrip('\0') | |
header['mtd_img1_offset'] = fields[10].decode("utf-8").rstrip('\0') | |
header['mtd_img1_size'] = fields[11].decode("utf-8").rstrip('\0') | |
header['mtd_img1_offset'] = fields[12].decode("utf-8").rstrip('\0') | |
header['mtd_img1_size'] = fields[13].decode("utf-8").rstrip('\0') | |
header['mtd_usrapp_offset'] = fields[14].decode("utf-8").rstrip('\0') | |
header['mtd_usrapp_size'] = fields[15].decode("utf-8").rstrip('\0') | |
header['flash_version'] = fields[16].decode("utf-8").rstrip('\0') | |
return header | |
def extract(datafile): | |
raw_header = datafile.read(512) | |
header = decode_header(raw_header) | |
pretty = pprint.PrettyPrinter(indent=4, sort_dicts=False) | |
pretty.pprint(header) | |
for section in [ 'rootfs', 'uimage', 'uboot' ]: | |
size = header[f'{section}_size'] | |
num_padding_bytes = align_leftover(size, 4) | |
section_contents = datafile.read(size) | |
datafile.read(num_padding_bytes) | |
with open(f'{section}.bin', 'wb') as f: | |
f.write(section_contents) | |
magic = datafile.read() | |
with open('unknown.bin', 'wb') as f: | |
f.write(magic) | |
def check_md5(datafile): | |
image = datafile.read() | |
header_md5 = image[:16].hex() | |
rawb = bytearray(image[ : -TPLINK_RSA_SIG_LEN]) | |
rawb[0:16] = b'\0' * 16 | |
mdfire = hashlib.md5(rawb) | |
md5_sum = mdfire.digest().hex() | |
if (md5_sum != header_md5): | |
print('MD5 sum mismatch') | |
print(f'\tExpected {header_md5}') | |
print(f'\tCalculated {md5_sum}') | |
return 0 | |
else: | |
print('MD5 sum matches') | |
return 1 | |
def main(args): | |
with open(args.image, 'rb') as fool: | |
check_md5(fool) | |
with open(args.image, 'rb') as fool: | |
extract(fool) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='TP extractor') | |
parser.add_argument('image', type=str, | |
help='File containing serial port dump (binary)') | |
main(parser.parse_args()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment