经过对海尔0301版BIN长期的分析,发现了一些方法,不敢独享,说出来让愿意进行分析的人参考。
想分析BIN需要有一定的51汇编基础,还要有一定的c语言基础,如果二者都不会,可以不用往下看了。
要是有过逆向工程的经验,分析起来要容易得多。
分析的第一步,是需要将代码正确的反汇编,这个难度比较大,目前我还在继续整理中,最主要的任务是将代码和数据分离,并将代码反汇编出来,分离数据的工作目前已经基本完成,相关的资料在我的另一个帖子中(关于文字显示位置重定位的,昨天发的,找不到的搜索一下即可)。
有了源码后,需要找出关键部分代码,比如说与输入输出相关的部分,然后从点到线,再到面,将重要模块整理出来,并注释到源程序中,再看程序的时候才能更容易些,也更能发现问题。
第二步,将数据整理成易读的形式,并将标号重新修改。比如说原来全是16字节一行的DB数据,经过美化后变成这个样子:
L724397: ;从机已连接
DB 0B4H,0D3H,0BBH,0FAH,0D2H,0D1H,0C1H,0ACH,0BDH,0D3H,0
L7243A2: ;设置波特率
DB 0C9H,0E8H,0D6H,0C3H,0B2H,0A8H,0CCH,0D8H,0C2H,0CAH,0
L7243AD: ;不兼容从机硬件
L7243BC: ;擦除,请稍候
L7243C9: ;写入数据,请稍候
L7243DA: ;等待从机
L7243E3: ;连接错误,等待从机
L7243F6: ;升级成功, 等待从机
字符串部分比较好处理,因为可以看到,更可恨的是有一部分貌似数据,实际是程序入口,例如这些数据:
L72B2AD: DB 0F3H,0B9H,0E9H,00BH,0F3H,0B2H,0CCH,00DH
实际上应该转换为下面这样,0F3代码72程序段,后面的两个字节是地址偏移量,再往后是序号。(感谢经验丰富的quicktime大侠的指正,单字节应该是switch分支,双字节是偏移量)
L72B2AD: ;转移表,共9项
DW L72B9E9
DB 00BH
DW L72B2CC
DB 00CH
DW L72B2CC
DB 00DH
DW L72B542
DB 00EH
DW L72B542
DB 00FH
DW L72B92B
DB 010H
DW L72B9E7
DB 011H
DW L72B6C5
DB 01BH
DW L72B6C5
DB 01CH
DW 0,L72BA19
这样的地址转移表在程序中有十几处,比较大的表也30多项,需要一一整理出来。
在应用程序的最后部分,还有一些数据,凡是3字节一组,以0F3H到0F8H打头的也是转移表,需要一一整理出来,有若干组,每组少的只有3-4项,多的有几组47项的。
如果只是理论上的分析,有上面这两步就可以看明白程序在搞什么了,甚至可以将程序弄到KEIL中跟踪运行,以观察其实际运行情况,可以参考Q大侠的相关帖子,对KEIL进行相关设置。
如果想真正的对程序进行修改,还需要将代码改为可浮动的,因为BIN的代码是已经链接过的代码,转移地址和数据地址全是绝对地址,想增加一段代码只能采用“飞地”的方法,不利于中型和大型的修改。
数据部分可以不浮动,因为海尔的BIN使用了 uC/OS-II 实时操作系统,即使新增的代码要使用内存,也可以通过内存管理函数来申请,即使要使用固定位置的变量,那7D0000和7E0000段有大量的空间可以用来设置far类型的变量,应该不需要担心。否则程序中的MOV DPTR,#xxxxxx指令就有2万1千多条,足够半年忙活了。
代码的浮动需要将所有的绝对转移地址都改为标号,例如下面这样的代码:
L76D4D1: MOV DPTR, #7DB13FH
MOV A, #03H
MOVX @DPTR, A
MOV R3, #0F9H
MOV R2, #00H
MOV R1, #0B4H
改成:
L76D4D1: MOV DPTR, #7DB13FH
MOV A, #03H
MOVX @DPTR, A
MOV R3, #BYTE2 (L7800B4 + 810000H) ;3项转移表
MOV R2, #BYTE1 L7800B4
MOV R1, #BYTE0 L7800B4
这样在重新汇编时才能生成正确的地址, 这样的地方约有几千处,需要手工处理,如果能使用程序脚本来处理,效率会提高很多,但是还有一些不是这样简单的形式,比如在767000附近的代码,地址的几部分是分开的,且每处可能不一样,程序处理有相当的难度,需要手工进行。
除了代码中的直接地址,凡是数据中是函数指针的部分,也需要照此办理。当然难度较大,工作量也不小,关键是分析出哪些数据代表的是地址,且要分析出其构造方式。
[b]最后要说的,也是相当重要的是,海尔的BIN采用了uOS-II实时操作系统[/b],此系统为开放源码的系统,其c语言的各版本源码可以在网上下载,具体功能就不需要我介绍了。据中9STB的开发时间,当时的OS版本应该在2.8左右,要分析的各位大侠可以自行从网上搜索并下载系统的c语言源码。网上关于此系统的资料相当多,有大量的资料可供参考。
目前已经确认,在应用程序区中发现了一个很关键的OS函数:OSTaskCreate(),具体地址位于61000解包后的38BDD至38D4B处,SDRAM中的地址为758BDD至758D4B。
[b](
增补部分:uC/OS-II系统函数在BIN中的入口地址及结束地址。适用于0301版高星原厂BIN。
L73A204 L73A5C5 OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err);
L73BB98 L73BF1C INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt);
L73FA78 L73FD95 INT8U OSQPostOpt(OS_EVENT *pevent, void *msg, INT8U opt);
L741584 L74187F OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err);
L743504 L7437C4 void *OSQPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
L744A7F L744D18 OS_EVENT *OSQCreate(void **start, INT16U size);
L7483D4 L748626 void OS_FlagBlock (OS_FLAG_GRP *pgrp, OS_FLAG_NODE *pnode, OS_FLAGS flags, INT8U wait_type, INT16U timeout)
L74A603 L74A837 INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *msg, INT8U msk)
L74B966 L74BB7F INT8U OSQQuery(OS_EVENT *pevent, OS_Q_DATA *p_q_data);
L74D861 L74DA62 void *OSMboxPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
L74F5C0 L74F7AB INT8U OSQPostFront(OS_EVENT *pevent, void *msg);
L75078C L750969 INT8U OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U milli);
L751BE3 L751DB0 INT8U OSQPost(OS_EVENT *pevent, void *msg);
L7532E9 L7534A5 void *OSQAccept(OS_EVENT *pevent, INT8U *err);
L75381E L7539D4 INT8U OSTaskSuspend(INT8U prio);
L7545BC L754767 void OSTimeTick(void);
L757A4B L757BCC void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
L758BDD L758D4B INT8U OSTaskCreate(void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio);
L759A10 L759B66 static BOOLEAN OS_FlagTaskRdy (OS_FLAG_NODE *pnode, OS_FLAGS flags_rdy)
L759B67 L759CC8 INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *p_sem_data);
L759F8C L75A0EB void OS_EventTaskWait (OS_EVENT *pevent);
L75C691 L75C7C9 INT8U OSTaskResume(INT8U prio);
L75CF13 L75D041 void OS_FlagUnlink (OS_FLAG_NODE *pnode);
L75E097 L75E1BC OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *err);
L75E409 L75E52D static void OS_InitEventList (void);
L75E776 L75E894 OS_STK *OSTaskStkInit(void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt);
L75FD65 L75FE78 void OS_EventTO (OS_EVENT *pevent);
L760110 L76021E void OSTimeDly(INT16U ticks);
L76032C L760437 static void OS_InitTCBList (void);
L760868 L760972 void OS_FlagInit (void);
L7613AE L7614AD OS_EVENT *OSSemCreate(INT16U cnt);
L761C94 L761D8B OS_EVENT *OSMboxCreate(void *msg);
L761D8C L761E83 INT8U OSMboxPost(OS_EVENT *pevent, void *msg);
L762529 L762619 void OS_QInit (void);
L7627FC L7628EB INT8U OSQFlush(OS_EVENT *pevent);
L766D2A L766DE8 void OSIntExit(void);
L767023 L7670DF void OS_Sched (void);
L7670E0 L76719C INT8U OSSemPost(OS_EVENT *pevent);
L7673D3 L76748E OS_FLAGS OSFlagQuery(OS_FLAG_GRP *pgrp, INT8U *err);
L769400 L7694A6 void OSStart(void);
L76954E L7695F3 void *OSMboxAccept(OS_EVENT *pevent);
L76AF07 L76AF9B static void OS_InitRdyList (void);
L76B031 L76B0C4 void OS_EventWaitListInit (OS_EVENT *pevent);
L76B9D7 L76BA64 INT16U OSSemAccept(OS_EVENT *pevent);
L773471 L7734B6 OS_FLAGS OSFlagPendGetFlagsRdy(void);
L774041 L77407B static void OS_InitMisc (void);
L77464F L774684 INT32U OSTimeGet(void);
L775162 L77518F void OSSchedUnlock(void);
L775275 L7752A1 void OSTimeSet(INT32U ticks);
L775E7C L775EA2 static void OS_InitTaskIdle (void);
L77606F L776094 void OS_TaskIdle (void);
L776B5A L776B76 void OSStartHighRdy(void);
L776CE5 L776D00 void OSInit(void);
L7770C5 L7770DB void OSSchedLock(void);
L777247 L77725C void OSIntEnter(void);
)[/b]
其它函数当会被陆续发现,因为应用程序部分的系统代码占据了相当的空间(保守估计得有一半儿吧),如果分析出哪些是系统函数,那些是应用函数,则分析的工作量会少很多。
如果你用过ucOS-II,那分析起来肯定会事半功倍,如果没用过,但是会c语言,也可以进行分析工作,要是顺便学会了51汇编(比c语言容易多了),那就如虎添翼了。
最后附上新编的SDRAM映像图,供分析时使用。
程序的主要流程是:
开机->00段(检测有没有上位机,如果有,进入刷机部分)
->5D-5E段(升级软件部分,汉字库尚不可用,文字信息全是英文)
->41段(对应用程序、字库、开机画面等进行解码)
->72-77段(应用程序部分,主循环)
海尔机SDRAM内存映像图:
[b]000000-0FFFFF FLASH ROM区
260000-283FFF 数据,升级软件用的数据区(包括开机自检)
410000-42FFFF 代码,初始化时将应用程序代码等进行LZSS解码并转移到720000
460000-48C0CC 数据,字库(未解压前) (确认)
4DA800-56556F 数据,解压后的24X24点阵的字模(确认)
560570-5CE3F0 数据,开机画面等(确认)
5D0000-5F23FF 代码,升级软件部分,然后转到410000执行
720000-78FFFF 代码段,主应用程序即在此范围,后面部分包括了相当多的数据
7CF400-7CFFFF 代码段,经过截获,只有一些返回指令,看来是没多大用。
7D0000-7DFFFF 数据段,标准XDATA段,由应用程序使用,包括文字提示信息和图形参数
7E0000-7EFFFF 数据段,扩展数据段。[/b]