mirror of
				https://github.com/hzyitc/openwrt-redmi-ax3000
				synced 2025-10-30 07:50:32 +08:00 
			
		
		
		
	 8a15a75e94
			
		
	
	8a15a75e94
	
	
	
		
			
			Create factory tar for EAP620 HD v1 and EAP660 HD v1 which is accepted by the stock UI.
Fix bug in ipq807x and ipq60xx makefiles so TPLINK_SUPPORT_STRING is handled properly.
Modify tplink-mkimage-2022 script and relevant makefiles to allow spaces in support strings (required for EAP620 HD v1 and EAP660 HD v1).
Installation steps are identical to EAP610-Outdoor:
Web UI method
-------------
Set up the device using the vendor's web UI. After that go to
Management->SSH and enable the "SSH Login" checkbox. Select "Save".
The connect to the machine via SSH:
    ssh -o hostkeyalgorithms=ssh-rsa <ip_of_device>
Disable signature verification:
    cliclientd stopcs
Rename the "-web-ui-factory" image to something less than 63
characters, maintaining the ".bin" suffix.
 * Go to System -> Firmware Update.
 * Under "New Firmware File", click "Browse" and select the image
 * Select "Update" and confirm by clicking "OK".
If the update fails, the web UI should show an error message.
Otherwise, the device should reboot into OpenWRT.
Signed-off-by: Shymon Samsel <ssamsel@umass.edu>
Link: https://github.com/openwrt/openwrt/pull/18340
Signed-off-by: Robert Marko <robimarko@gmail.com>
		
	
		
			
				
	
	
		
			201 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| '''A program for manipulating tplink2022 images.
 | |
| 
 | |
| A tplink2022 is an image format encountered on TP-Link devices around the year
 | |
| 2022. This was seen at least on the EAP610-Outdoor. The format is a container
 | |
| for a rootfs, and has optional fields for the "software" version. It also
 | |
|  requires a "support" string that describes the list of compatible devices.
 | |
| 
 | |
| This module is intended for creating such images with an OpenWRT UBI image, but
 | |
| also supports analysis and extraction of vendor images. Altough tplink2022
 | |
| images can be signed, this program does not support signing image.
 | |
| 
 | |
| To get an explanation of the commandline arguments, run this program with the
 | |
| "--help" argument.
 | |
| '''
 | |
| 
 | |
| import argparse
 | |
| import hashlib
 | |
| import os
 | |
| import pprint
 | |
| import re
 | |
| import struct
 | |
| 
 | |
| 
 | |
| def decode_header(datafile):
 | |
|     '''Read the tplink2022 image header anbd decode it into a dictionary'''
 | |
|     header = {}
 | |
|     fmt = '>2I'
 | |
| 
 | |
|     datafile.seek(0x1014)
 | |
|     raw_header = datafile.read(8)
 | |
|     fields = struct.unpack(fmt, raw_header)
 | |
| 
 | |
|     header['rootfs_size'] = fields[0]
 | |
|     header['num_items'] = fields[1]
 | |
|     header['items'] = []
 | |
| 
 | |
|     rootfs = {}
 | |
|     rootfs['name'] = 'rootfs.ubi'
 | |
|     rootfs['offset'] = 0
 | |
|     rootfs['size'] = header['rootfs_size']
 | |
|     header['items'].append(rootfs)
 | |
| 
 | |
|     for _ in range(header['num_items']):
 | |
|         entry = datafile.read(0x2c)
 | |
|         fmt = '>I32s2I'
 | |
|         fields = struct.unpack(fmt, entry)
 | |
| 
 | |
|         section = {}
 | |
|         section['name'] = fields[1].decode("utf-8").rstrip('\0')
 | |
|         section['type'] = fields[0]
 | |
|         section['offset'] = fields[2]
 | |
|         section['size'] = fields[3]
 | |
|         header['items'].append(section)
 | |
|     return header
 | |
| 
 | |
| def extract(datafile):
 | |
|     '''Extract the sections of the tplink2022 image to separate files'''
 | |
|     header = decode_header(datafile)
 | |
| 
 | |
|     pretty = pprint.PrettyPrinter(indent=4, sort_dicts=False)
 | |
|     pretty.pprint(header)
 | |
| 
 | |
|     for section in header['items']:
 | |
|         datafile.seek(0x1814 + section['offset'])
 | |
|         section_contents = datafile.read(section['size'])
 | |
| 
 | |
|         with open(f"{section['name']}.bin", 'wb') as section_file:
 | |
|             section_file.write(section_contents)
 | |
| 
 | |
|     with open('leftover.bin', 'wb') as extras_file:
 | |
|         extras_file.write(datafile.read())
 | |
| 
 | |
| def get_section_contents(section):
 | |
|     '''I don't remember what this does. It's been a year since I wrote this'''
 | |
|     if section.get('data'):
 | |
|         data = section['data']
 | |
|     elif section.get('file'):
 | |
|         with open(section['file'], 'rb') as section_file:
 | |
|             data = section_file.read()
 | |
|     else:
 | |
|         data = bytes()
 | |
| 
 | |
|     if section['size'] != len(data):
 | |
|         raise ValueError("Wrong section size", len(data))
 | |
| 
 | |
|     return data
 | |
| 
 | |
| def write_image(output_image, header):
 | |
|     '''Write a tplink2022 image with the contents in the "header" dictionary'''
 | |
|     with open(output_image, 'w+b') as out_file:
 | |
|         # header MD5
 | |
|         salt = [ 0x7a, 0x2b, 0x15, 0xed,
 | |
|              0x9b, 0x98, 0x59, 0x6d,
 | |
|              0xe5, 0x04, 0xab, 0x44,
 | |
|              0xac, 0x2a, 0x9f, 0x4e
 | |
|         ]
 | |
| 
 | |
|         out_file.seek(4)
 | |
|         out_file.write(bytes(salt))
 | |
| 
 | |
|         # unknown section
 | |
|         out_file.write(bytes([0xff] * 0x1000))
 | |
| 
 | |
|         # Table of contents
 | |
|         raw_header = struct.pack('>2I', header['rootfs_size'],
 | |
|                         header['num_items'])
 | |
|         out_file.write(raw_header)
 | |
| 
 | |
|         for section in header['items']:
 | |
|             if section['name'] == 'rootfs.ubi':
 | |
|                 continue
 | |
| 
 | |
|             hdr = struct.pack('>I32s2I',
 | |
|                 section.get('type', 0),
 | |
|                 section['name'].encode('utf-8'),
 | |
|                 section['offset'],
 | |
|                 section['size']
 | |
|             )
 | |
| 
 | |
|             out_file.write(hdr)
 | |
| 
 | |
|         for section in header['items']:
 | |
|             out_file.seek(0x1814 + section['offset'])
 | |
|             out_file.write(get_section_contents(section))
 | |
| 
 | |
|         size = out_file.tell()
 | |
| 
 | |
|         out_file.seek(4)
 | |
|         md5_sum = hashlib.md5(out_file.read())
 | |
| 
 | |
|         out_file.seek(0)
 | |
|         out_file.write(struct.pack('>I16s', size, md5_sum.digest()))
 | |
| 
 | |
| def encode_soft_verson():
 | |
|     '''Not sure of the meaning of version. Also doesn't appear to be needed.'''
 | |
|     return struct.pack('>4B1I2I', 0xff, 1, 0 ,0, 0x2020202, 30000, 1)
 | |
| 
 | |
| def create_image(output_image, root, support):
 | |
|     '''Create an image with a ubi "root" and a "support" string.'''
 | |
|     header = {}
 | |
| 
 | |
|     header['rootfs_size'] = os.path.getsize(root)
 | |
|     header['items'] = []
 | |
| 
 | |
|     rootfs = {}
 | |
|     rootfs['name'] = 'rootfs.ubi'
 | |
|     rootfs['file'] = root
 | |
|     rootfs['offset'] = 0
 | |
|     rootfs['size'] = header['rootfs_size']
 | |
|     header['items'].append(rootfs)
 | |
| 
 | |
|     support_list = {}
 | |
|     support_list['name'] = 'support-list'
 | |
|     support_list['data'] = re.sub("\\\\r\\\\n ?", "\r\n", support).encode("utf-8")
 | |
|     support_list['offset'] = header['rootfs_size']
 | |
|     support_list['size'] = len(support_list['data'])
 | |
|     header['items'].append(support_list)
 | |
| 
 | |
|     sw_version = {}
 | |
|     sw_version['name'] = 'soft-version'
 | |
|     sw_version['type'] = 1
 | |
|     sw_version['data'] = encode_soft_verson()
 | |
|     sw_version['offset'] = support_list['offset'] + support_list['size']
 | |
|     sw_version['size'] = len(sw_version['data'])
 | |
|     header['items'].append(sw_version)
 | |
| 
 | |
|     header['num_items'] = len(header['items']) - 1
 | |
|     write_image(output_image, header)
 | |
| 
 | |
| def main(args):
 | |
|     '''We support image analysis,extraction, and creation'''
 | |
|     if args.extract:
 | |
|         with open(args.image, 'rb') as image:
 | |
|             extract(image)
 | |
|     elif args.create:
 | |
|         if not args.rootfs or not args.support:
 | |
|             raise ValueError('To create an image, specify rootfs and support list')
 | |
|         create_image(args.image, args.rootfs, args.support)
 | |
|     else:
 | |
|         with open(args.image, 'rb') as image:
 | |
|             header = decode_header(image)
 | |
| 
 | |
|             pretty = pprint.PrettyPrinter(indent=4, sort_dicts=False)
 | |
|             pretty.pprint(header)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     parser = argparse.ArgumentParser(description='EAP extractor')
 | |
|     parser.add_argument('--info', action='store_true')
 | |
|     parser.add_argument('--extract', action='store_true')
 | |
|     parser.add_argument('--create', action='store_true')
 | |
|     parser.add_argument('image', type=str,
 | |
|                     help='Name of image to create or decode')
 | |
|     parser.add_argument('--rootfs', type=str,
 | |
|                     help='When creating an EAP image, UBI image with rootfs and kernel')
 | |
|     parser.add_argument('--support', type=str,
 | |
|                     help='String for the "support-list" section')
 | |
| 
 | |
|     main(parser.parse_args())
 |