import json import os import shutil import subprocess import sys import struct ### 定义参数 # 当前脚本文件名称 script_name = os.path.basename(__file__) # 输出目录 output_path_name = 'output' # 固件内分区信息保存文件 package_json_file_name = "packages.json" # 合并后的新固件名称 merged_firmware_name = "full.new.bin" # zxic 编程器固件文件魔法头 zxic_firmware_magic_header = '00005a045a583735323156316c1e0000' # squashfs 镜像文件魔法头 squashfs_magic_header = '68737173' # 检查核对文件魔法头 def check_magic_header(file_path,size,target): with open(file_path, "rb") as file: header = file.read(size) if header.hex() == target: return True return False # 通过文件魔法头判断文件是否是 zxic 编程器固件 def is_zxic_fireware_file(file_path): return check_magic_header(file_path=file_path, size=16, target=zxic_firmware_magic_header) # 通过文件魔法头判断文件是否是 squashfs 镜像 def is_squashfs_file(file_path): return check_magic_header(file_path=file_path, size=4, target=squashfs_magic_header) # 通过判断是否 squashfs 以及文件大小是否一致,来判断是否是 mtd4 分区 def is_mtd4_squashfs_file(file_path): if is_squashfs_file(file_path): file_size = os.path.getsize(file_path) with open(file_path, "rb") as file: file.seek(40) raw_data = file.read(8) suqashfs_size = struct.unpack(' suqashfs_size: return True elif file_size == suqashfs_size: print(f"{file_path} is a squashfs file not a mtd4 file!") else: raise ValueError(f"{file_path} is a squashfs file but it's file size error!") else: print(f"{file_path} is not a squashfs/mtd4 file !") return False def get_file_size_in_kb(file_path): try: # 获取文件大小(以字节为单位) size_in_bytes = os.path.getsize(file_path) # 将字节转换为KB size_in_kb = size_in_bytes / 1024 print(f"The file size is {size_in_kb} KB") except FileNotFoundError: print("File not found") # 从编程器固件或者 zloader中读取分区结构 def get_partions_info_from_firmware(firmware_file_path): partitions = [] with open(firmware_file_path, "rb") as file: file.seek(8224) # 0x2020 mtd_offsets = 0 i = 0 while True: data = file.read(40) if data[:2] == b"\x00\x00": break name = data[:16].rstrip(b"\x00").decode("utf-8") ftype = data[16:32].rstrip(b"\x00").decode("utf-8") size = struct.unpack(" 0: if len(squashfs_files_list) > 1: print(f'发现 {len(squashfs_files_list)} 个镜像文件') for file_info in squashfs_files_list: extract_squashfs_file(file_path, file_info.get('offset'),file_info.get('size'),f"{hex(file_info.get('offset')).replace('0x', '')}-{hex(file_info.get('offset') + file_info.get('size')).replace('0x', '')}") # 将 squashfs 镜像文件填充 到 编程器固件或 mtd4 分区 def repack_firmware(input_image, target_file_path, replaced_image_file_index=0): if not os.path.exists(input_image): print(f'待回写 {input_image} 文件不存在') return if not os.path.exists(target_file_path): print(f'回写目标 {target_file_path} 文件不存在') return squashfs_files_list = find_squashfs(target_file_path) if len(squashfs_files_list) > 0: replaced_image_file_info = squashfs_files_list[0] print(f'发现 {len(squashfs_files_list)} 个镜像文件') if len(squashfs_files_list) > 0: if replaced_image_file_index > 0: replaced_image_file_info = squashfs_files_list[replaced_image_file_index] original_image_offset = replaced_image_file_info.get('offset') original_image_size = replaced_image_file_info.get('size') input_image_size = os.path.getsize(input_image) if input_image_size > original_image_size: print(f'导入文件大小超过原始文件大小,可能导致文件结构破坏,建议新镜像文件≤原镜像文件') shutil.copy2(target_file_path, target_file_path + ".bak") with open(target_file_path, 'r+b') as file_a: image_file_content = file_a.read() file_a.seek(original_image_offset) empty_bytes = bytes([0xFF] * original_image_size) with open(input_image, 'rb') as file_b: input_image_file_content = file_b.read() file_a.seek(original_image_offset) file_a.write(input_image_file_content) print(f'{input_image} 已回写到 {target_file_path}') def unsquashfs(target_file_path): if not os.path.exists(target_file_path): print(f'suqashfs 镜像 {target_file_path} 文件不存在') return args = ["unsquashfs"] parent_directory = os.path.dirname(target_file_path) target_squashfs_root_dir = os.path.join(parent_directory,"squashfs-root") args.append("-d") args.append(target_squashfs_root_dir) args.append(target_file_path) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = process.communicate() if process.returncode != 0: response = err else: response = out if response != b'': lines = response.decode('utf-8').split('\r\r\n') lines = [line for line in lines if line] print(lines) return out def mksquashfs(target_squashfs_root_dir): if not os.path.exists(target_squashfs_root_dir): print(f'suqashfs-root 目录 {target_squashfs_root_dir} 文件不存在') return # mksquashfs squashfs-root/ new.squashfs -no-xattrs -b 262144 -comp xz -Xbcj armthumb -Xdict-size 256KiB -no-sparse args = ["mksquashfs"] parent_directory = os.path.dirname(target_squashfs_root_dir) new_squashfs_root_file = os.path.join(parent_directory,"new.squashfs") args.append(target_squashfs_root_dir) args.append(new_squashfs_root_file) # args.append('-no-xattrs -b 262144 -comp xz -Xbcj armthumb -Xdict-size 256KiB -no-sparse') args.append("-no-xattrs") args.append("-b") args.append("262144") args.append("-comp") args.append("xz") args.append("-Xbcj") args.append("armthumb") args.append("-Xdict-size") args.append("256KiB") args.append("-no-sparse") args.append("-noappend") # arg =' '.join(args) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = process.communicate() if process.returncode != 0: response = err else: response = out if response != b'': lines = response.decode('utf-8').split('\r\r\n') lines = [line for line in lines if line] print(lines) return out # 使用帮助 def help(file_name): print(f"Usage: python script.py \n") print(f"operations:\n") print(f" split split mtds from a firmware") print(f" eg: python3 {file_name} split c:\\full.bin\n") print(f" merge merge mtds from a directory to a firmware") print(f" eg: python3 {file_name} merge c:\\\n") print(f" unpack unpack a squashfs from mtd4 or firmware ") print(f" eg: python3 {file_name} unpack c:\\full.bin\n") print(f" eg: python3 {file_name} unpack c:\\mtd4\n") print( f" repack repack squashfs-root to a squashfs filesystem and put it into mtd4 or firmware" ) print(f" eg: python3 {file_name} repack c:\\squashfs-root c:\\full.bin\n") print(f" eg: python3 {file_name} repack c:\\squashfs-root c:\\mtd4\n") return # 主函数 def main(): if len(sys.argv) < 3: help(script_name) return operation = sys.argv[1] file_path = sys.argv[2] if operation == "split": # 实现拆分固件的方法 split_firmware(file_path) elif operation == "merge": # 实现合并固件的方法 merge_firmware(file_path) pass elif operation == "unpack": # 实现解包固件的方法 unpack_firmware(file_path) pass elif operation == "repack": # 实现重新打包固件的方法 if len(sys.argv) != 4: print(f"!!!!!!!! repack need at least 4 parameters\n\n") help(script_name) target_file_path = sys.argv[3] replaced_image_file_index = 0 if len(sys.argv) >= 5: replaced_image_file_index = int(sys.argv[4]) repack_firmware(file_path,target_file_path, replaced_image_file_index) else: help(script_name) if __name__ == "__main__": main()