中兴机顶盒开机LOGO

来自IPTV爱好者
跳转至: 导航搜索

开机LOGO升级文件

开机LOGO包含了2个JPG格式的LOGO图片,分辨率分别是640x526和1920x1080,另外有28个BMP图片,2种分辨率的数字、百分号、色块等。

早期版本机顶盒的LOGO分区只有128KB,新版本扩展到了512KB。B700V2的LOGO区也只有128KB,随便一张1920x1080的照片都要超过128KB,因此用全家福照片替换原来的LOGO几乎无法实现。

以中兴B600V4的升级文件为例,升级文件的开头0x40个字节为首部,头四个字节为55 66 77 88,这是升级包的Magic Word,然后是LOGO PROCESS,接着是版本号和时间。

0000 55 66 77 88 4C 4F 47 4F 20 50 52 4F 43 45 53 53  Ufw.LOGO PROCESS
0010 00 00 00 00 56 34 30 30 30 30 38 20 32 30 30 39  ....V400008 2009
0020 2D 30 31 2D 32 33 00 00 00 00 00 00 00 00 00 00  -01-23..........
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

0x40开始的0x10个字节是LOGO内容的总的空间。

0040 00 00 00 00 00 01 95 88 00 01 00 01 00 00 00 00
                 -----------    --    --
                     大小       未知含义

0x50开始的0x10个字节是LOGO的校验码。

0x50 00 00 00 00 00 60  D0 63 00 00 00 00 00 00 00 00
                 -----  -----
               首部大小 校验码

0x60开始的内容是各个LOGO图片的信息,每个图片占12个字节,其中的偏移量是相对值,加0x60后变成绝对值。

0060 00 00 00 1E 00 00 00 01 00 00 01 D0 00 00 59 94
     ----------- ----------- ----------- -----------
      图片总数     图片类型    偏移量       字节数
0x70 00 00 00 02 00 00 5B 64 00 00 81 02 00 02 00 01
0x80 00 00 DC 68 00 00 06 38 00 02 00 02 00 00 E2 A0

分离LOGO文件的源代码

 #include <stdio.h>
 
 struct image_t {
 	int flag;
 	int offset;
 	int size;
 };
 
 int convert(unsigned char data[])
 {
 	int i = data[0]<<24;
 
 	i += data[1]<<16;
 	i += data[2]<<8;
 	i += data[3];
 
 	return i;
 }
 
 int main(int argc, char* argv[])
 {
 	int i;
 	int size;
 	int count;
 	int jpg = 0;
 	int bmp = 0;
 	FILE *input;
 	FILE *output;
 	char filename[64];
 	unsigned char data[4];
 	unsigned char buffer[1024];
 	struct image_t images[50];
 
 	if (argc < 2)
 	{
 		printf("Usage: splitlog logofile\n");
 
 		return 1;
 	}
 	if ((input = fopen(argv[1], "rb")) == NULL)
 	{
 		printf("File %s open error.\n", argv[1]);
 
 		return 2;
 	}
 	fseek(input, 0x60, SEEK_SET);
 	fread(data, 1, 4, input);
 	count = convert(data);
 
 	printf("Found %d image blocks\n", count);
 	for (i=0; i<count; i++)
 	{
 		fread(data, 1, 4, input);	images[i].flag   = convert(data);
 		fread(data, 1, 4, input);	images[i].offset = convert(data)+0x60;
 		fread(data, 1, 4, input);	images[i].size   = convert(data);
 	}
 	for (i=0; i<count; i++)
 	{
 		size = images[i].size;
 		fseek(input, images[i].offset, SEEK_SET);
 		fread(buffer, 1, size>sizeof(buffer)?sizeof(buffer):size, input);
 		if (buffer[0] == 'B' && buffer[1] == 'M')
 			sprintf(filename, "logo%02d.bmp", i);
 		else if (buffer[0] == 0xFF && buffer[1] == 0xD8)
 			sprintf(filename, "logo%02d.jpg", i);
 		else
 		{
 			printf("Unknown image format\n");
 			continue;
 		}
 		output = fopen(filename, "wb");
 		if (output == NULL)
 		{
 			printf("File %s create error.\n", filename);
 			continue;
 		}
 		fwrite(buffer, 1, size>sizeof(buffer)?sizeof(buffer):size, output);
 		size -= sizeof(buffer);
 		while (size > 0)
 		{
 			fread(buffer, 1, sizeof(buffer), input);
 			fwrite(buffer, 1, size>sizeof(buffer)?sizeof(buffer):size, output);
 			size -= sizeof(buffer);
 		}
 		fclose(output);
 	}
 	fclose(input);
 
 	return 0;
 }

编译后的程序:[1]

B600V4A以后版本的LOGO升级文件中含有ECC校验码,需要先去除ECC码后分离,去除ECC码可用我写的另一个工具实现:[2],这个工具软件中集成了与开机LOGO有关的功能,如:分离图片、替换LOGO图片。

CRC校验码计算

目前还不清楚采用何种CRC16算法,初步确定是从0x60开始的内容参与CRC计算。

通过反编译机顶盒中的appup程序,得到了计算和判断CRC的部分代码。

通过阅读汇编程序,初步判定根本不是CRC校验码,就是普通的检查和,估计是中兴的某个程序员偷懒,用检查和替代了CRC,设计文档上也许要求用CRC16。

终于弄清楚检查和的计算方法了,下面是C语言源代码。

unsigned short CheckSum(unsigned char *p, int len)
{
	int i;
	unsigned short w;
	unsigned int sum = 0;

	for (i=0; i<len; i+=2)
	{
		w  = (p[i]<<8) + p[i+1];
		sum += w;
		if (sum > 0xFFFF)sum -= 0xFFFF;
	}
	if (i > len)	// len是奇数
	{
		w = p[len-1]<<8;
		sum += w;
		if (sum > 0xFFFF)sum -= 0xFFFF;
	}
	sum = ~sum;

	return sum;
}

下面是反汇编内容,供大家参考。

.text:00412744
.text:00412744 ; =============== S U B R O U T I N E =======================================
.text:00412744 sub_412744:
.text:00412744                 mov     #-2, r7
.text:00412746                 mov.l   r8, @-r15
.text:00412748                 and     r5, r7          ; r7=长度-2
.text:0041274A                 mov     r6, r8          ; r8=FF0000?
.text:0041274C                 bra     loc_412766
.text:0041274E                 mov     #0, r3          ; r3是循环变量i
.text:00412750 ; ---------------------------------------------------------------------------
.text:00412750 loc_412750:
.text:00412750                 mov.w   @(r0,r4), r1    ; r4为内容部分首址
.text:00412752                 extu.w  r1, r1
.text:00412754                 extu.b  r1, r2
.text:00412756                 shll8   r2
.text:00412758                 shlr8   r1
.text:0041275A                 or      r2, r1          ; WORD里两字节对掉
.text:0041275C                 add     r1, r8          ; 累加
.text:0041275E                 cmp/hi  r6, r8          ; 
.text:00412760                 bf/s    loc_412766
.text:00412762                 add     #2, r3          ; 循环变量i+=2
.text:00412764                 sub     r6, r8
.text:00412766 loc_412766:
.text:00412766                 cmp/ge  r7, r3           ; 循环是否结束?
.text:00412768                 mov.l   dword_412788, r6 ; h'FFFF
.text:0041276A                 bf/s    loc_412750       ; 继续循环
.text:0041276C                 mov     r3, r0           ; r0=循环变量
.text:0041276E                 cmp/ge  r5, r3
.text:00412770                 bt      loc_412780       ; 处理内容长度为奇数的情况
.text:00412772                 mov     r4, r0
.text:00412774                 mov.b   @(r0,r3), r1
.text:00412776                 shll8   r1
.text:00412778                 add     r1, r8
.text:0041277A                 cmp/hi  r6, r8
.text:0041277C                 bf      loc_412780
.text:0041277E                 sub     r6, r8
.text:00412780
.text:00412780 loc_412780:
.text:00412780                 mov     r8, r0          ; r0返回计算结果
.text:00412782                 mov.l   @r15+, r8
.text:00412784                 rts
.text:00412786                 nop
.text:00412786 ; End of function sub_412744
.text:00412786

.text:0041278C ; =============== S U B R O U T I N E =======================================
.text:0041278C
.text:0041278C sub_41278C:
.text:0041278C                 mov.l   off_41279C, r0 ; sub_412744
.text:0041278E                 sts.l   pr, @-r15
.text:00412790                 jsr     @r0 ; sub_412744
.text:00412792                 mov     #0, r6
.text:00412794                 lds.l   @r15+, pr
.text:00412796                 not     r0, r0         ; 计算出来的检查和逐位取反,这个开始时没留意,验算总是不对。
.text:00412798                 rts
.text:0041279A                 extu.w  r0, r0
.text:0041279A ; End of function sub_41278C

.text:00412820
.text:00412820 ; =============== S U B R O U T I N E =======================================
.text:00412820
.text:00412820 sub_412820:
.text:00412820                 mov.l   r8, @-r15
.text:00412822                 tst     r4, r4          ; r4为存放升级包内容的首址
.text:00412824                 sts.l   pr, @-r15
.text:00412826                 bt/s    loc_4128CE
.text:00412828                 mov     r4, r8          ; r8为存放升级包内容的首址
.text:0041282A                 mov     #h'5F, r1
.text:0041282C                 cmp/gt  r1, r5          ; r5>0x5F? r5应该是升级包的长度
.text:0041282E                 bf/s    loc_4128D0
.text:00412830                 mov     #-1, r0
.text:00412832                 mov.l   @r4, r1          ; 取出首部4个字节
.text:00412834                 mov     #h'18, r0
.text:00412836                 mov.l   dword_4128D8, r6 ; h'FF0000
.text:00412838                 mov.l   dword_4128DC, r4 ; h'FF00
.text:0041283A                 mov     r1, r7           ; 以下代码是作字节顺序调换
.text:0041283C                 mov     r1, r2
.text:0041283E                 mov     r1, r3
.text:00412840                 shlr16  r7
.text:00412842                 and     r6, r2
.text:00412844                 and     r4, r3
.text:00412846                 shlr8   r7
.text:00412848                 shlr8   r2
.text:0041284A                 shll8   r3
.text:0041284C                 shld    r0, r1
.text:0041284E                 or      r3, r1
.text:00412850                 or      r2, r7
.text:00412852                 or      r1, r7
.text:00412854                 mov.l   dword_4128E0, r1 ; h'55667788
.text:00412856                 cmp/eq  r1, r7           ; 判断首部Magic Word
.text:00412858                 bf      loc_4128CE
.text:0041285A                 add     #h'40, r8        ; 首址r8加0x40
.text:0041285C                 mov.l   @(4,r8), r1      ; 取0x44处的内容,即内容部分长度
.text:0041285E                 add     #-h'60, r5       ; 升级包的总长度减去0x60, r5就是内容部分的长度。
.text:00412860                 and     r1, r6           ; 以下代码是作字节顺序调换
.text:00412862                 mov     r1, r2
.text:00412864                 and     r1, r4
.text:00412866                 shlr16  r2
.text:00412868                 shll8   r4
.text:0041286A                 shlr8   r6
.text:0041286C                 shlr8   r2
.text:0041286E                 shld    r0, r1
.text:00412870                 or      r6, r2
.text:00412872                 or      r4, r1
.text:00412874                 or      r1, r2
.text:00412876                 cmp/hs  r5, r2           ; 0x44处存放的长度r2与升级包实际长度r5进行比较
.text:00412878                 bf/s    loc_41287E       ; 相等则转loca_41287E
.text:0041287A                 add     #-h'40, r8       ; r8指向首址
.text:0041287C                 mov     r5, r2
.text:0041287E
.text:0041287E loc_41287E:
.text:0041287E                 mov.l   off_4128E4, r0   ; sub_41278C
.text:00412880                 mov     r2, r5           ; r5为内容部分长度
.text:00412882                 mov     r8, r4
.text:00412884                 add     #h'60, r4        ; r4指向内容部分首址
.text:00412886                 jsr     @r0 ; sub_41278C ; 计算CRC校验码
.text:00412888                 add     #h'56, r8
.text:0041288A                 mov.w   @r8, r1          ; 取出0x56处存放的CRC校验码
.text:0041288C                 mov.l   off_4128E8, r8   ; loc_402598
.text:0041288E                 extu.w  r1, r3
.text:00412890                 extu.b  r3, r2
.text:00412892                 mov     r3, r1
.text:00412894                 shll8   r2
.text:00412896                 shlr8   r1
.text:00412898                 or      r2, r1
.text:0041289A                 cmp/eq  r0, r1            ; CRC检验码是否相等?
.text:0041289C                 bt/s    loc_4128BA        ; 相等转loc_4128BA
.text:0041289E                 mov     #h'60, r1
.text:004128A0                 add     #-h'C, r15
.text:004128A2                 mov.l   dword_4128EC, r4 ; h'80000000
.text:004128A4                 mov     #h'5D, r1
.text:004128A6                 mov.l   r0, @(4,r15)
.text:004128A8                 mov.l   r1, @r15
.text:004128AA                 mov.l   r3, @(8,r15)
.text:004128AC                 mov.l   off_4128F0, r5 ; aSSDErrorCalcul ; "%s[%s][%d]:Error: calculateCheckSum = %"...
.text:004128AE                 mov.l   off_4128F4, r6 ; aCheckimg_c ; "checkimg.c"
.text:004128B0                 mov.l   off_4128F8, r7 ; aCheckimg ; "Checkimg"
.text:004128B2                 jsr     @r8 ; loc_402598
.text:004128B4                 nop
.text:004128B6                 bra     loc_4128CE
.text:004128B8                 add     #h'C, r15
.text:004128BA ; ---------------------------------------------------------------------------
.text:004128BA loc_4128BA:
.text:004128BA                 add     #-4, r15
.text:004128BC                 mov.l   off_4128FC, r5 ; aSSDCrcPass ; "%s[%s][%d]:CRC pass ! \n"
.text:004128BE                 mov.l   off_4128F4, r6 ; aCheckimg_c ; "checkimg.c"
.text:004128C0                 mov.l   r1, @r15
.text:004128C2                 mov.l   off_4128F8, r7 ; aCheckimg ; "Checkimg"
.text:004128C4                 jsr     @r8
.text:004128C6                 mov     #1, r4
.text:004128C8                 add     #4, r15
.text:004128CA                 bra     loc_4128D0
.text:004128CC                 mov     #0, r0
.text:004128CE ; ---------------------------------------------------------------------------
.text:004128CE loc_4128CE:
.text:004128CE                 mov     #-1, r0         ; 错误,返回-1
.text:004128D0
.text:004128D0 loc_4128D0:
.text:004128D0                 lds.l   @r15+, pr
.text:004128D2                 mov.l   @r15+, r8
.text:004128D4                 rts
.text:004128D6                 nop
.text:004128D6 ; End of function sub_412820
.text:004128D6
.text:004128D6 ; ---------------------------------------------------------------------------
.text:004128D8 dword_4128D8:   .data.l h'FF0000        ; DATA XREF: sub_412820+16�r
.text:004128DC dword_4128DC:   .data.l h'FF00          ; DATA XREF: sub_412820+18�r
.text:004128E0 dword_4128E0:   .data.l h'55667788      ; DATA XREF: sub_412820+34�r
.text:004128E4 off_4128E4:     .data.l sub_41278C      ; DATA XREF: sub_412820:loc_41287E�r
.text:004128E8 off_4128E8:     .data.l loc_402598      ; DATA XREF: sub_412820+6C�r
.text:004128EC dword_4128EC:   .data.l h'80000000      ; DATA XREF: sub_412820+82�r
.text:004128F0 off_4128F0:     .data.l aSSDErrorCalcul ; DATA XREF: sub_412820+8C�r
.text:004128F0                                         ; "%s[%s][%d]:Error: calculateCheckSum = %"...
.text:004128F4 off_4128F4:     .data.l aCheckimg_c     ; DATA XREF: sub_412820+8E�r
.text:004128F4                                         ; sub_412820+9E�r
.text:004128F4                                         ; "checkimg.c"
.text:004128F8 off_4128F8:     .data.l aCheckimg       ; DATA XREF: sub_412820+90�r
.text:004128F8                                         ; sub_412820+A2�r
.text:004128F8                                         ; "Checkimg"
.text:004128FC off_4128FC:     .data.l aSSDCrcPass     ; DATA XREF: sub_412820+9C�r