补丁很神秘吗?补丁移植很难吗?我能自己移植补丁吗?我说不好,不过我相信,看完这篇文章你一定会手痒痒。搞补丁移植没有任何经验,但是我有自己的思路:就象自学日语德语一样,不要教科书,直接去日本德国的网站找感兴趣的东西看,越糊涂越好,坚持下去,大彻大悟之时,再去比照别人的经验。所以,至今也不太了解别人是如何移植补丁的。
写完这篇文章后,我想自己应该去深造一下了。如果你觉得我下面的方法比较苯、比较另类,我希望你会谅解。
在研究、移植补丁之前,需要知晓几个必备的条件:
1、不必具有太多的相关专业知识,但是必须具有清晰、灵活的思路;
2、需要一定的耐心和搞坏爱机的心理承受能力;
3、具备几个软件和文件:UltraEdit(二进制文本编辑软件)、Smelter(Fullflash综合分析工具)、IDA(反汇编调试工具)、ABraGen(ArshOr写的分支跳转计算工具)、补丁手机的Fullflash和你自己手机的Fullflash。
一般情况下,我们认为FUBU(FUllflash BackUp)和Fullflash是有所区别的:FUBU是手机的.fbk格式的整机备份(x65flasher-backup-fullflash),内部含有解释性的地址参数,所以在Restore时不用提供起始地址和长度;Fullflash是.bin格式的整机备份(x65flasher-Read Flash),是A0000000H开始的“原汁原味”的纯二进制文件,可以重新定位到任何地址段,是这里移植补丁所需要的格式。
有关地址的说明:x65有32M内存,使用8位地址,基址为A0000000,反汇编都是基于这个基址的,而在V_Klay格式的补丁中,必须去掉最高位的A,剩下的7位数据可以去掉或者保留最前面的0。
IDA的基本用法:处理器选ARM或者ARM 710a;ROM和Loading地址选A0000000;切换到16位的Thumb状态用Alt+G,Value=1;定位地址按G键;反汇编按C键...
我们下面会通过三个比较典型的例子,介绍一下补丁移植的基本方法,大家细嚼慢咽,就会品出“腊肉”的滋味。
请先准备好干净的、没刷过下面补丁的S65 v25和C6C v25(或者其他机型)的Fullflash,我们来把S65 v25的补丁移植到你的手机上。
一、通话时间50秒提示
1.补丁原型:
;S65
0B7BBB6: 00 32
2.基本思路:
上面的补丁是把S65手机中地址为0B7BBB6处的一个字节的16进制数据00(代表默认的60秒)替换为32(表示50秒)。
在C6C(或者其他类型)手机中,数据00可能不在0B7BBB6这个位置上,我们必须重新找到这个位置。要从32M(32x1024x1024)个字节的数据里面直接找到这个特定的00几乎是不可能的。但是由于这两款手机的软件系统结构具有相当的一致性,我们可以将这个00数据及其周围的一段数据作为特征字串,在C6C的Fullflash里面搜索一下,看看能不能找到一个唯一的位置,然后,把这个位置的00改为32,刷进C6C进行测试。
3. 补丁移植
3.1 用UltraEdit打开S65的Fullflash,找到00B7BBB0这一行(00位于这一行的0B7BBB6这个位置上);
3.2 从00B7BBB6开始,直到这一行结束,用“鼠标右键-Hex复制选定区”进行复制;
3.3 用UltraEdit打开C6C的Fullflash,在搜索功能里面,粘贴上面复制的内容,去掉开头的地址和后面的乱码,只要有用的数据:00 29 06 D1 2A 1C 2B 1C 5B 21
3.4 搜索的结果,在地址00b006b2这里找到了这个和S65一样的特征字串,继续搜索,没有发现其他位置有这个字串,现在,我们认定这就是我们要找的位置了;
3.5 新建一个V_Klay文件,输入0b006b2: 00 32,刷进手机试一下补丁的效果。
3.6 很幸运,一次成功。
4. 最终补丁:
;C6C
0b006b2: 00 32
5.补充说明:
如果特征字串选的太短,可能会搜索到多个结果,难以确定真实有效的位置;如果字串太长,可能一个位置也找不到。所以,应该由短到长逐渐改变字串的长度,缩小地址范围。
如果这个补丁的移植过程没有看明白,建议先不要继续下面的内容。
二、待机画面取消软键显示
1.补丁原型:
;S65
0854992: 05D1 0028
08548B3: D0 E0
0877A4C: 06D1 0028
2.基本思路:
相当多的补丁不能用上面那种简单的、搜索固定字串的方式来移植。由于地址、数据和程序位置的不同,特征字串里面可能会有一些不确定的变数,需要进行模糊搜索,具体的方法就是用?号来代替那些不确定的数据。
在“0854992: 05D1 0028”中,05D1是一个条件跳转指令(BNE loc_A08549A0)的机器码,用来判断是否在屏幕上显示软键提示,我们不想要这个判断,需要取消显示,让程序继续执行下去,所以要屏蔽掉这条指令,方法是用一个无实际效果的多余指令来代替它(相当于NOP),作者选用了“ CMP R0, #0”(机器码就是0028)。
我们知道05D1在S65中的位置是A0854992,现在,我们用程序分析的方法来确定这条程序在C6C中的位置,然后屏蔽它。
3.补丁移植:
3.1
用IDA反汇编S65 Fullflash从A0854992开始的代码:
ROM:A0854992* BNE loc_A08549A0
ROM:A0854994 MOV R0, #0x1A
ROM:A0854996 STR R0, [R5,#0x24]
ROM:A0854998 STR R0, [R5,#0x20]
ROM:A085499A B loc_A08549A0
ROM:A085499C MOV R3, #0
ROM:A085499E STR R3, [R5,#0x20]
ROM:A08549A0* loc_A08549A0
ROM:A08549A0 LDR R0, =0xA847F4B4
ROM:A08549A2 LDR R3, [R5,#0x1C]
由于BNE loc_A08549A0只是一个范围很小的、跨越了几条指令的短跳,不同地址,但是具有相同方向和距离的相对地址的短跳指令具有相同的机器码。尽管在它的指令机器码中包含了相对的地址偏移量,我们还是先假定在C6C中具有相同的跳转,这样,可以先直接搜索它的机器码而不是用?号来代替搜索。接下来的几条指令都是一些常数赋值处理,在C6C中应该具有相同的指令。基于这样的认识,我们假定在C6C里面具有完全相同的代码段,先使用固定的特征字串进行定位。
这一次,我们用Smelter来搜索。
用Smelter打开C6C的Fullflash,按Ctrl+B,搜索前两条指令的机器码05D11A20,得到两个地址,增加搜索长度05D11A206862,得到唯一的地址a084bbe6,也许是巧合,但是我们先假定这次也是幸运的,或者可以再用一个IDA看一下C6C的a084bbe6开始的程序结构与S65是否类似,以此确定地址的准确性。
这样就得到了第一行移植后的补丁数据:
084bbe6:05D1 0028
3.2
用IDA反汇编补丁第二行A08548B3处的程序:
ROM:A08548B3* DCB 0xD0 ; 反汇编不出来
ROM:A08548B4 BL sub_A083FA8A
ROM:A08548B8 CMP R0, #0
ROM:A08548BA BEQ loc_A08548E4
ROM:A08548BC MOV R0, #0x1A
ROM:A08548BE STR R0, [R5,#0x1C]
ROM:A08548C0 B loc_A08548F4
发现这个地址开始的数据D0没有反汇编出来,被当作了一个字节的数据,难以确定搜索特征。我们继续从上一个地址位置重新反汇编,得到新的结果:
ROM:A08548B0 CMP R0, #0
ROM:A08548B2* BEQ loc_A08548BC
ROM:A08548B4 BL sub_A083FA8A
ROM:A08548B8 CMP R0, #0
ROM:A08548BA BEQ loc_A08548E4
ROM:A08548BC loc_A08548BC
ROM:A08548BC MOV R0, #0x1A
ROM:A08548BE STR R0, [R5,#0x1C]
ROM:A08548C0 B loc_A08548F4
这一下明白了,原来A08548B2处的数据03是指令BEQ loc_A08548BC的固定部分,在新的补丁中没有变化,所以补丁中没有写出这个字节来。
用UltraEdit将A08548B3处的D0改为E0,再用IDA反汇编:
ROM:A08548B2* B loc_A08548BC
ROM:A08548B4 BL sub_A083FA8A
ROM:A08548B8 CMP R0, #0
ROM:A08548BA BEQ loc_A08548E4
ROM:A08548BC loc_A08548BC
ROM:A08548BC MOV R0, #0x1A
ROM:A08548BE STR R0, [R5,#0x1C]
ROM:A08548C0 B loc_A08548F4
补丁里将条件跳转改成了无条件跳转。其中BEQ loc_A08548BC类似实例一中的情况,所以,我们也假设C6C中有相同的机器码。但是,接下来的BL sub_A083FA8A是一个4字节的绝对地址跳转,这在不同的机型中的机器码很可能是不一样的,不能做为特征值搜索,使用?号代替(或者代替其中的变数部分),为了缩小搜索范围,把后面的两条比较“老实”的指令的机器码也加入搜索。这样,用Smelter在C6C的Fullflash里面搜索“03D0????????002813D0” 得到唯一的地址A084BB06,需要修改的地址是A084BB07,于是,我们得到了第二行移植后的补丁数据:
084BB07:D0 E0
3.3
使用和1同样的方法得到第三行移植后的补丁数据:
086a214: 06D1 0028
4.最终补丁:
;c65
084bbe6: 05D1 0028
084bb07: D0 E0
086a214: 06D1 0028
5.补充说明:
在“将A08548B3处的D0改为E0,再用IDA反汇编”这一步,我们可以用工具软件ABraGen的反汇编功能来确定补丁的指令格式,但是ABraGen的反汇编功能还存在bug,仅供参考。
特征值的模糊搜索也可以用DOS软件sfe来进行,比较而言,Smelter更直观、功能也更丰富一些。
如果不进行反汇编,是难以确定需要进行模糊搜索的?号的位置的。这是移植跳转类补丁的基本方法。
三、按###进入九宫菜单
1.补丁原型:
;S65
0B6E9E0: 39F021FF F3F4C8FF
2.基本思路:
我们曾经移植过“任意处按###进入极速菜单”的补丁,现在,只需改一下补丁的指针,就可以改为调出正常的九宫菜单,也可以进一步修改,按###进入其他功能。
在这个补丁中,包括一个地址和两个入口指针(绝对地址长跳指令)。按###进入陷阱地址A0B6E9E0再转向九宫菜单。
移植工作需要做的是,在C6C中,找到39F021FF(作用相同但数值不一定是39F021FF)的位置,替换为F3F4C8FF(作用相同但数值不一定是F3F4C8FF)的位置。
3.补丁移植:
用IDA反汇编S65的Fullflash:
ROM:A0B6E9E0* BL sub_A0BA8826
ROM:A0B6E9E4 STR R6, [R5,#4]
ROM:A0B6E9E6 POP {R3-R7,PC}
ROM:A0BA8826* PUSH {R4,LR}
ROM:A0BA8828 SUB SP, SP, #0x1C0
ROM:A0BA882A BL sub_A0BA8200
ROM:A0BA882E CMP R0, #0
ROM:A0BA8830 BEQ loc_A0BA8862
ROM:A0BA8832 ADD R0, SP, #0x1C8+var_1C4
ROM:A0BA8834 BL sub_A0BA8610
可以看到BL sub_A0BA8826是一个绝对地址长跳指令,需要进行模糊搜索,增加一些特征值后,我们用Smelter在C6C的Fullflash中搜索“????????6E60F8BD80B5”,得到唯一的地址A0AF9C98 ,数据为2AF03DFF,从而确定了补丁的一部分:
0AF9C98:2AF03DFF
下面需要看一下,修改后的补丁跳到了哪里。将S65的A0B6E9E0处的数据39F021FF替换为F3F4C8FF之后,反汇编得到:
ROM:A0B6E9E0 BL loc_A0862974
ROM:A0B6E9E4 STR R6, [R5,#4]
ROM:A0B6E9E6 POP {R3-R7,PC}
...
ROM:A0862974 PUSH {R7,LR}
ROM:A0862976 BL sub_A0849094
ROM:A086297A BL sub_A083D85E
ROM:A086297E POP {R7,PC}
需要在C6C里面找到这段功能相同的程序:用Smelter搜索“80B5??????F???????F?80BD08B5????????0028”,得到地址A08796E0,这就是正常的九宫菜单的入口。下面我们就要确定一下使用BL指令由A0AF9C98跳到A08796E0的机器码,还是要用到ABraGen这个软件,经计算得到了数据7FF522FD。
4.最终补丁:
;C6C 按###进入九宫菜单
0AF9C98:2AF03DFF 7FF522FD
5.补充说明:
刷这个补丁之前需要先撤销原来的按###进极速菜单的补丁。当然也可以强刷(因为###的陷阱地址相同),V_Klay产生的Repair文件会包含极速菜单的补丁。
实际使用中,并不是任意处按###都有效,大家可以自己尝试一下。
这篇文章向大家介绍了个人在移植补丁中摸索出来的几种基本方法,有不当之处,请大家批评指正。
移植类似“远端同步->极速菜单”和“黑白名单”这样的补丁还需要更灵活的处理方式,其中的原理都是一样的。
有时间的话,我希望再写一点开发补丁的方法和思路,与大家探讨。
文中所用软件和Fullflash都可以在http://yizhe.net/c65/dev.htm下载。