import re import os import sys import struct import json from bin.log import myprint from bin.base import bin_path from bin.crc import CRC32 # kunlun1,kunlun2,kunlun3的img类型 # FRW是指未压缩的fw CFRW是指压缩之后的fw _ImgType={ 0x0000:"imgROM", # Maybe,ROM doesn't need a imgType */ 0xFC00:"imgSP", # sp */ 0xFC01:"imgSBL", # sbl */ 0xFC02:"imgFRW", # kl1: 1MB fw, uncompressed for sta, run in flash; compressed for cco, run in psram */ 0xFC03:"imgCFG", # The first pib file. */ 0xFC04:"imgEFUSE", # efuse */ 0xFC05:"imgOEM", # oem */ 0xFC06:"imgFRW2", # The second fw, maybe the backup one. not used. */ 0xFC07:"imgCFG2", # The second pib file. */ 0xFC08:"imgCFRW1", # kl1: 2MB fw, compressed for sta or cco, run in flash; kl2: 4M flash fw */ 0xFC09:"imgCFRW2", # The second fw, maybe the backup one. not used. */ 0xFC0A:"imgCFRW", # kl1: 2MB fw, compressed for sta or cco, run in psram; kl2: 4M psram fw */ 0xFC0B:"imgREF", # ref */ 0xFC0C:"imgCUS", # cus */ 0xFC0D:"imgCFRW4F", # kl1: 4MB fw, compressed for sta or cco, run in flash */ 0xFC0E:"imgCFRW4P", # kl1: 4MB fw, compressed for sta or cco, run in psram */ 0xFFFF:"imgMAX", # invalid */ 0x00:"imgV1ROM", # Maybe,ROM doesn't need a imgType */ 0x01:"imgV1SBL", # sbl */ 0x02:"imgV1FRWPLC", # FW-PLC */ 0x03:"imgV1CFG", # The first pib file. */ 0x05:"imgV1OEM", # oem */ 0x06:"imgV1FRWCUS", # FW-CUS */ 0xFF:"imgV1MAX" # invalid */ } _DevType={ 0xDC01:"devkunlun1", 0xDC02:"devkunlun2", 0xDC03:"devkunlun3", 0xFFFF:"devMax", 0x01:"devV1kunlun1", 0x02:"devV1kunlun2", 0x03:"devV1kunlun3", 0xFF:"devV1Max" } _PatternType={ 0x00:"HTZD", 0x55:"HTZD", 0xaa:"JSMT", 0x99:"FLX", 0x66:"QJ", 0xcc:"SPE", 0x33:"GX", 0xdd:"DT", 0xa9:"YP", 0xc6:"WTZ", 0x93:"TCE" } _layout_cfg=''' g_layout_f1_v0 part_name offset size size(kB) note SP 0x00000000 0x00008000 32 SBL 0x00008000 0x00008000 32 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00006000 24 LOG1 0x00018000 0x00001000 4 PIB2 0x00019000 0x00006000 24 LOG2 0x0001F000 0x00001000 4 FW1 0x00020000 0x0006F000 444 CUS 0x0008F000 0x00001000 4 FW2 0x00090000 0x0006F000 444 DUMP 0x00000000 0x00000000 0 not use CAL 0x000FF000 0x00001000 4 - g_layout_f2_v0 part_name offset size size(kB) note SP 0x00000000 0x00008000 32 SBL 0x00008000 0x00008000 32 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 RUN 0x00020000 0x000E0000 896 log part use the remaining space FW1 0x00100000 0x00070000 448 FW2 0x00170000 0x00070000 448 CUS 0x001E0000 0x0001E000 120 DUMP 0x001FE000 0x00001000 4 CAL 0x001FF000 0x00001000 4 - g_layout_f2_v1 part_name offset size size(kB) note SBL 0x00000000 0x0000C000 48 PIB1_EXT 0X0000C000 0X00002000 8 PIB2_EXT 0X0000E000 0X00002000 8 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 RUN 0x00020000 0x000E0000 896 FW1 0x00100000 0x00070000 448 FW2 0x00170000 0x00070000 448 DUMP 0x001E0000 0x00001000 4 CUS 0x001E1000 0x0001E000 120 CALI 0x001FF000 0x00001000 4 - g_layout_f2_v2 part_name offset size size(kb) note SBL 0x00000000 0x0000C000 48 PIB1_EXT 0X0000C000 0X00002000 8 PIB2_EXT 0X0000E000 0X00002000 8 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 RUN_CUS 0x00020000 0x00028000 160 RUN_FW 0x00048000 0x000B8000 736 CUS1 0x00100000 0x00018000 96 FW1 0x00118000 0x00058000 352 CUS2 0x00170000 0x00018000 96 FW2 0x00188000 0x00058000 352 RESERVED 0x001E0000 0x00001000 4 CUST 0x001E1000 0x0001E000 120 CALI 0x001FF000 0x00001000 4 - g_layout_f2_p_v0 part_name offset size size(kB) note SP 0x00000000 0x00008000 32 SBL 0x00008000 0x00008000 32 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 FW1 0x00020000 0x000A0000 640 LOG1 0x000C0000 0x00020000 128 LOG2 0x000E0000 0x00020000 128 FW2 0x00100000 0x000A0000 640 CUS 0x001A0000 0x0005E000 376 SP_FW1 0x001F0000 0x00000000 0 not use PARAM2 0x001F7000 0x00000000 0 not use DUMP 0x001FE000 0x00001000 4 CAL 0x001FF000 0x00001000 4 - g_layout_f2_p_v1 part_name offset size size(kB) note SBL 0x00000000 0x00010000 64 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 FW1 0x00020000 0x000A0000 640 LOG1 0x000C0000 0x00020000 128 FW2 0x000E0000 0x000A0000 640 LOG2 0x00180000 0x00020000 128 DUMP 0x001A0000 0x00001000 4 CUS 0x001A1000 0x0005E000 376 CALI 0x001FF000 0x00001000 4 - g_layout_f4_v0 part_name offset size size(kb) note SP 0x00000000 0x00008000 32 SBL 0x00008000 0x00008000 32 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 LOG1 0x00000000 0x00000000 0 not use PIB2 0x00019000 0x00007000 28 LOG2 0x00000000 0x00000000 0 not use RUN 0x00020000 0x00160000 1408 log part use the remaining space FW1 0x00180000 0x000C0000 768 FW2 0x00240000 0x000C0000 768 SP_FW1 0x00000000 0x00000000 0 not use PARAM2 0x00000000 0x00000000 0 not use SP_FW2 0x00000000 0x00000000 0 not use DUMP 0x00000000 0x00000000 0 not use CUS 0x00300000 0x000FF000 1020 CAL 0x003FF000 0x00001000 4 - g_layout_f4_v1 part_name offset size size(kb) note SBL 0x00000000 0x00010000 64 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 RUN 0x00020000 0x00160000 1408 FW1 0x00180000 0x000B0000 704 FW2 0x00230000 0x000B0000 704 PIB1_EXT 0x002E0000 0x0000F000 60 PIB2_EXT 0x002EF000 0x0000F000 60 DUMP 0x002FE000 0x00001000 4 RESERVED 0x002FF000 0x00001000 4 CUS 0x00300000 0x000FF000 1020 CALI 0x003FF000 0x00001000 4 - g_layout_f4_p_v0 part_name offset size size(kb) note SP 0x00000000 0x00008000 32 SBL 0x00008000 0x00008000 32 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 RUN 0x00020000 0x000FA000 1000 LOG1 0x0011A000 0x00066000 408 FW1 0x00180000 0x000FA000 1000 LOG2 0x0027A000 0x00066000 408 DUMP 0x002E0000 0x00001000 4 RESERVED 0x002E1000 0x00001000 4 SP_FW1 0x002E2000 0x0000F000 60 SP_FW2 0x002F1000 0x0000F000 60 CUS 0x00300000 0x000FF000 1020 CAL 0x003FF000 0x00001000 4 FW2 0x002E0000 0x00000000 0 not use PARAM2 0x00000000 0x00000000 0 not use - g_layout_f4_p_v1 part_name offset size size(kb) note SBL 0x00000000 0x00010000 64 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 FW1 0x00020000 0x000FA000 1000 LOG1 0x0011A000 0x00030000 192 LOG2 0x0014A000 0x00030000 192 RESERVED1 0x0017A000 0x00006000 24 FW2 0x00180000 0x000FA000 1000 PIB1_EXT 0x0027A000 0x0000F000 60 PIB2_EXT 0x00289000 0x0000F000 60 DUMP 0x00298000 0x00001000 4 RESERVED2 0x00299000 0x00067000 412 CUS 0x00300000 0x000FF000 1020 CALI 0x003FF000 0x00001000 4 - g_layout_f4_p_v2 part_name offset size size(kb) note SBL 0x00000000 0x00010000 64 PARAM 0x00010000 0x00001000 4 OEM 0x00011000 0x00001000 4 PIB1 0x00012000 0x00007000 28 PIB2 0x00019000 0x00007000 28 FW1-CUS 0x00020000 0x00080000 512 FW1-PLC 0x000A0000 0x000E0000 896 LOG1 0x00180000 0x00030000 192 LOG2 0x001B0000 0x00030000 192 FW2-CUS 0x001E0000 0x00080000 512 FW2-PLC 0x00260000 0x000E0000 896 PIB1_EXT 0x00340000 0x0000F000 60 PIB2_EXT 0x0034F000 0x0000F000 60 DUMP 0x0035E000 0x00001000 4 CUST 0x0035F000 0x000A0000 640 CALC 0x003FF000 0x00001000 4 - ''' _layout_list=[ 'g_layout_f1_v0', 'g_layout_f2_v0', 'g_layout_f2_p_v0', 'g_layout_f4_v0', 'g_layout_f4_p_v0', 'g_layout_f2_v1', 'g_layout_f2_p_v1', 'g_layout_f4_v1', 'g_layout_f4_p_v1', 'g_layout_f4_p_v2', 'g_layout_f2_v2', ] # 根据layout index 找到 layout def get_layout(index:int): layout_len=len(_layout_list) if(index>=layout_len): return None name=_layout_list[index] layout=_layout_cfg.split('-') for item in layout: if(item.find(name)>=0): part=item.split('\n') break part_dict_list:list[dict]=[] for item in part: sp=item.split() if(len(sp)>=4) and sp[3].isdigit(): # myprint(f"layout {item}") d={ 'name':sp[0], 'offset':int(sp[1],base=16), 'size':int(sp[2],base=16), } part_dict_list.append(d) return part_dict_list # 校验 当前地址是不是分区地址 def check_part_addr(addr:int,part_list:list[dict]): for item in part_list: if(item.get('offset',None)==addr): return item return None _PKT_HEADER_MAGIC_NO = 0x00005566 _IMG_HEADER_MAGIC_NO = 0xa4e49a17 # 从info文件读取flash信息 def load_flash_info(file_name:str): flash_info_list:list[dict]=[] with open(file_name,mode='r',encoding='utf-8') as f: lines=f.readlines() for line in lines: # 如果开头两个字符是数字 info = re.match(r"^\d{2}",line) if(info): # print(info.group(0)) sp=line.split() flash_info={} flash_info["DevType"]=_DevType[int(sp[1],base=16)] flash_info["ImgType"]=_ImgType[int(sp[2],base=16)] flash_info["Offset"]=int(sp[3],base=16) flash_info["Size"]=int(sp[4]) flash_info_list.append(flash_info) return flash_info_list # 查看是不是数据头,不是返回 "",是返回解析值 def tran_pkg_header(data:bytearray): size=len(data) ret={} if(size>=96): # 大写为无符号 B 单字节 H 双字节 I 4字节 L 4字节 Q 8字节 # f 单精度浮点 d 双精度浮点 s 字符串 a=struct.unpack('>BBHIIIIIIIIIIIIIIIIIIIIIIHBB',data) if(a[5]==_PKT_HEADER_MAGIC_NO): ret["enctype"]=str(a[0]) ret["pattern"]=hex(a[1]) ret["dev_type"]=hex(a[2]) ret["file_crc"]=hex(a[3]) ret["file_len"]=str(a[4]) ret["magic_no"]=hex(a[5]) ret["version"]=hex(a[6]) ret["sp_start"]=hex(a[7]) ret["sp_len"]=str(a[8]) ret["sbl_start"]=hex(a[9]) # 32bytes ret["sbl_len"]=str(a[10]) ret["oem_start"]=hex(a[11]) ret["oem_len"]=str(a[12]) ret["pib_start"]=hex(a[13]) ret["pib_len"]=str(a[14]) ret["fw_start"]=hex(a[15]) ret["fw_len"]=str(a[16]) ret["efuse_start"]=hex(a[17]) # 64bytes ret["efuse_len"]=str(a[18]) ret["fw2_start"]=hex(a[19]) ret["fw2_len"]=str(a[20]) ret["ref_start"]=hex(a[21]) ret["ref_len"]=str(a[22]) ret["cus_start"]=hex(a[23]) ret["cus_len"]=str(a[24]) ret["vendor_id"]=hex(a[25]) ret["img_type"]=hex(a[26]) ret["img_flag"]=hex(a[27]) t=str(ret) t=t.replace(',','\n') # t=json.dumps(ret, sort_keys=True, indent=2, separators=(',', ': ')) return t return "" def tran_img_headerv0(data:bytearray): size=len(data) ret={} if(size>=64): # 大写为无符号 B 单字节 H 双字节 I 4字节 L 4字节 Q 8字节 # f 单精度浮点 d 双精度浮点 s 字符串 a=struct.unpack('>HHIHBBIIIBBBBI',data[0:32]) if(a[8]==_IMG_HEADER_MAGIC_NO): ret["devType"]=hex(a[0])+f"({_DevType.get(a[0],'unknown')})" ret["imgType"]=hex(a[1])+f"({_ImgType.get(a[1],'unknown')})" ret["imgSize"]=str(a[2]) ret["imgVer"]=hex(a[3]) ret["psramSize"]=str(a[4]) ret["hdrVer"]=hex(a[5]) ret["fwSize"]=str(a[6]) ret["imgCRC"]=hex(a[7]) ret["guard"]=hex(a[8]) ret["layoutIdx"]=hex(a[9]) # reserved 3bytes ret["fwCRC"]=hex(a[13]) ret["sha256"]=data[32:].hex() t=str(ret) t=t.replace(',','\n') return t,ret return "",{} def tran_img_headerv1(data:bytearray): size=len(data) ret={} if(size>=64): # 大写为无符号 B 单字节 H 双字节 I 4字节 L 4字节 Q 8字节 # f 单精度浮点 d 双精度浮点 s 字符串 a=struct.unpack('>BBBBIBBBBIIIBBBBI',data[0:32]) if(a[11]==_IMG_HEADER_MAGIC_NO): ret["devType"]=hex(a[0])+f"({_DevType.get(a[0],'unknown')})" ret["imgType"]=hex(a[1])+f"({_ImgType.get(a[1],'unknown')})" ret["encType"]=hex(a[2]) ret["cfg"]=hex(a[3]) ret["imgSize"]=str(a[4]) ret["zipType"]=hex(a[5]) ret["flashSize"]=str(a[6]) ret["psramSize"]=hex(a[7]) ret["hdrVer"]=hex(a[8]) ret["imgVer"]=hex(a[9]) ret["imgCRC"]=hex(a[10]) ret["guard"]=hex(a[11]) ret["layoutIdx"]=hex(a[12]) # reserved 3bytes ret["runAddr"]=hex(a[16]) ret["sha256"]=data[32:].hex() t=str(ret) t=t.replace(',','\n') return t,ret return "",{} # 判断是不是pkt_header def pkt_header_check(data:bytearray): magic=(data[12]<<24)|(data[13]<<16)|(data[14]<<8)|(data[15]) if (magic==_PKT_HEADER_MAGIC_NO): return True myprint(f"magic={hex(magic)}") return False # 判断是不是img_header def img_header_check(data:bytearray): if(len(data)<32): return '' magic=(data[20]<<24)|(data[21]<<16)|(data[22]<<8)|(data[23]) if (magic==_IMG_HEADER_MAGIC_NO): imghdr_v=data[11] myprint(f"magic={hex(magic)}, imghdr_v={imghdr_v}") # 返回使用的结构体类型 if(imghdr_v==0x10): return 'V1' else: return "V0" return "" # 判断是不是加密了 def pkt_encrypt_check(data:bytearray): magic_data=data[12:16] pat=magic_data[0] for i in range(len(magic_data)): magic_data[i]=magic_data[i]^pat magic=(magic_data[0]<<24)|(magic_data[1]<<16)|(magic_data[2]<<8)|(magic_data[3]) if (magic==_PKT_HEADER_MAGIC_NO): if(pat!=0): return True return False # 转化为hex文本 def bin_to_hex(bin:bytearray,f,oem_file=None,layout_index=None): all_size=len(bin) pack_size=32 turned=0 turned_old=0 out_text='' kunlun='' if(layout_index is not None): layout_list=get_layout(layout_index) if(pkt_header_check(bin)): out_text=tran_pkg_header(bin[0:96])+'\n' f.write(out_text) while turned < all_size: if(turned+pack_size<=all_size): data=bin[turned:turned+pack_size] turned_old=turned turned+=pack_size else: data=bin[turned:] turned_old=turned turned=all_size if(all_size-turned_old>=64): hdr_data=bin[turned_old:turned_old+64] else: hdr_data=bytearray() hdr_v=img_header_check(hdr_data) if(hdr_v=='V0'): kunlun='kl1' out_text,ret=tran_img_headerv0(hdr_data) out_text+='\n' f.write(out_text) elif(hdr_v=='V1'): kunlun='kl3' out_text,ret=tran_img_headerv1(hdr_data) out_text+='\n' f.write(out_text) # 截取oem部分 if(oem_file is not None): if(_ImgType[hdr_data[1]]=="imgV1OEM"): size=int(ret["imgSize"]) oem_bin=bin[turned_old+64:turned_old+64+size] oem_file.write(oem_bin) if(layout_index is not None): layout=check_part_addr(turned_old,layout_list) if(layout is not None): layout['crc32']=hex(CRC32(bin[turned_old:turned_old+layout['size']])) f.write(str(layout).replace(',',' ')+'\n') out_text=f"[{hex(turned_old)}] {data.hex(' ')}\n" f.write(out_text) return kunlun def bin_to_hex_file(bin_file_name:str,hex_file_name:str,layout_index:int): with open(bin_file_name,mode='rb') as f: bin=bytearray(f.read()) if(pkt_encrypt_check(bin)): myprint("start decrypt:") pat=(bin[12]^0x00)&0xff myprint(f"decrypt image:{hex(pat)}({_PatternType.get(pat,'unknown')})") for index in range(len(bin)): bin[index]=bin[index]^pat with open(hex_file_name,mode='w+',encoding="utf-8") as f: # oem_bin 去除原始后缀 .bin.txt 添加新后缀 -oem.bin oem_name=hex_file_name[0:-8]+'-oem.bin' oem_name=os.path.normpath(oem_name) with open(oem_name,mode='wb+') as oem_f: kl=bin_to_hex(bin,f,oem_f,layout_index) # 调用oem_tool 打印oem信息 oem_tool=os.path.join(bin_path(),f"{kl}_oem.exe") if(os.path.exists(oem_tool)): cmd_str=f"{oem_tool} --parse={oem_name}" with os.popen(cmd_str) as f: for line in f.readlines(): myprint(line.strip()) # os.system(cmd_str) else: myprint(f"oem_tool not found: {oem_tool}") def bin_file_decrypt(bin_file:str): with open(bin_file,mode='rb') as f: bin=bytearray(f.read()) # 自动判断是否需要解密 if (pkt_encrypt_check(bin)): pat=(bin[12]^0x00)&0xff myprint(f"decrypt image:{hex(pat)}({_PatternType.get(pat,'unknown')})") for index in range(len(bin)): bin[index]=bin[index]^pat else: pat=bin[1] myprint(f"copy image:{hex(pat)}({_PatternType.get(pat,'unknown')})") with open('tmp.bin',mode='wb+') as f: f.write(bin) return "tmp.bin" def clear_tmp(): tmp_list=['tmp.bin'] for item in tmp_list: if os.path.exists(item): os.remove(item) # bin_to_hex.py input_file if __name__ == "__main__": bin_to_hex_file(sys.argv[1],"work/"+sys.argv[1]+".txt",int(sys.argv[2])) # get_layout(0)