- 积分
- 319
- 实力分
- 点
- 金钱数
- 两
- 技术分
- 分
- 贡献分
- 分
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
主要以"替换运营商标志为自定义字串V2"为例说一下怎么用Keil ARM编译器来做Patch或者移植。
最近一段时间,因为Patch的原因,我试用了所有的主流ARM编译器,包括ADS1.2,RealView2.0,GCC for ARM,Keil ARM。最后发现,最合适的工具就是Keil ARM。虽然还有一些小问题,不过起来最解决。首先,Keil在代码定位方面是最容易的,ADS和GCC都需要用独立的链接文件才能做到Patch这样的松散定位,但仍然很费劲,而且比如ADS需要你的段开始于4字节的边界。这是很麻烦的,很多时候我们要Hook的BL指令开始于2字节边界,此外,这些编译器对于调用地址有很多限制,比如简单的BL指令,在ADS中就很难完成,而BLX指令在ADS中甚至会出错。
痛苦中,我甚至打算自己写一个汇编器,实际我也完成了词法和语法,可以处理简单的BL和BLX,不过从avkiev那里得到解决BLX的办法后,我还是放弃了这个计划。虽然Keil有一些缺陷,但毕竟可以忍受。为了配合Keil,我又重新写了一下Hex2VKP工具,目的只有一个,使Patch工作变简单,目标就是像Riza的sfe那样。(我的编译器计划是apm,ARM Patch Maker)。
下面我就结合"替换运营商标志为自定义字串V2"说明一下基本步骤,为了这个,我已经把它完全移植到Keil下了。下面是这个Patch的所有工程文件。
这个工程里面主要有三个源文件,startup.s、RepSP.c、x65.h。还有Keil的工程文件RepSP.Uv2和Hex2vkp这个工具。startup.s是汇编文件,主要是Hook部分及一些Thunk的函数。RepSP.c是主文件,里面包括了所有的程序部分。而x65.h是头文件,里面定义了一些函数原形和结构体。hex2vkp是我专门为hex文件转换为vkp而写的工具,最新重写了一下,增强一下功能,它的格式如下:
- hex2vkp [-h? -bbaseaddr -fflashfile -ccommentsfile -ovkpfile] [hexfile ...]
- -c specify a commentsfile to describe vkp, all line should be output
- until a line begin with ';;' is found.
- -b specify base address of fullflash, default A0000000
- -f specify original fullflash file
- -h produce this help message
- -o specify output vkp file, default to console
- -v report hex2vkp version info
复制代码
可以指定基地址,默认是A0000000,fullflash文件,注释文件和输出文件。同时输入可以指定一系列的hex文件。
我们主要看一下这个工程中和地址定位相关的部分。
首先是整个工程的地址,在这里面我使用了0x800000开始的Flash部分,指定是在工程的目标选项的的LA Location的里面,作如下指定。
User Classes:DATA (0xA8000000-0xA87FFFFF), CODE (0xA0800040-0xA081FFFF), CONST (0xA0800040-0xA081FFFF)
这里面主要指定了程序的数据部分、代码部分、常量区。
然后是Hook的地址指定,这个我们看一下startup.s。
- ;***替换运营商标志为自定义字串V2.1***
- ;(C) Bennie
- ;for S6CV50
- ;v2.1->重写了代码
- ;支持的格式:
- ;%Y - 年(四位数字)2005
- ;%y - 年(两位数字)05
- ;%M - 月 06
- ;%D - 日 23
- ;%W - 星期 四
- ;%H - 时 00
- ;%F - 分 56
- ;%R - 信号强度 -46
- ;%A - 电池电压 3807
- ;%a - 电池电压,百分数 56
- ;%T - 环境温度 30,74
- ;对齐格式字符:0xE01C=中,0xE01D=右,0xE01E=左
- ;屏幕保护自定义字串,注意必须以\0结尾,最大15个字符。
- ;0x800000: '%M-%D %W %H:%F\0'
- ;待机画面自定义字串
- ;0x800020: '%R %A %T\0'
- ;;==============================================================
- EXTERN CODE16(entryMainscreen?T)
- EXTERN CODE16(entryScreensaver?T)
- ;待机时调用获得运营商名字的函数Hook
- AREA STARTUPCODE1, CODE, AT 0xA0AFCB82 // READONLY, ALIGN=4
- CODE16
- PUSH {LR}
- BL entryScreensaver?T
- POP {PC}
-
- ;屏保时调用获得运营商名字的函数Hook
- AREA STARTUPCODE2, CODE, AT 0xA08DAF2E // READONLY, ALIGN=4
- CODE16
- BL entryMainscreen?T
- ;函数Thunk,处理一下dwmoddw函数
- AREA THUNKFUNCTION, CODE, AT 0xA0800040 // READONLY, ALIGN=4
- PUBLIC dwmoddw?T
- dwmoddw?T PROC CODE16
- LDR R2, =0xA160948C
- BX R2
- ENDP
- END
复制代码
前面的部分是最终vkp的注释部分,这个由hex2vkp输出到vkp的开始。然后前面两个Extern知名了这两个函数是外部函数,然后第一个AREA开始的部分指定了一个区段,后面是这个区段的属性,比如属于那个段(代码段)、对齐边界(默认是4),以及开始地址。可以指定地址是我选择Keil的主要原因,在别的工具上,做这个就困难多了。然后下面的CODE16是说明下面是THUMB指令,在下面就是代码部分了。下面的部分类似,除了最后一个是函数THUNK有点不同,那个主要原因是KEIL在处理多个函数返回值时,用函数指针有点问题,所以我用了C和ASM的混合链接。前面那个PUBLIC是导出一个符号,这里面体现了KEIL对混合编译的符号处理。导出的符号要加上?T或?A。才可以被别的模块使用。后面声名了一个Thumb例程。
下面我们再看一下RepSP.c里面和地址定位有关的部分。
- #include "x65.h"
- #define S6CV50
- /*
- 0x800000: '%M-%D %W %H:%F\0'
- 0x800020: '%R %A %T\0'
- */
- const word format[2][16] __at 0xA0800000;
- const unsigned short w1_7[] = {
- 0x4E00, //一
- 0x4E8C, //二
- 0x4E09, //三
- 0x56DB, //四
- 0x4E94, //五
- 0x516D, //六
- 0x65E5 //日
- };
- #ifdef S6CV50
- //for system function
- const GETDATETIME getdate = (GETDATETIME)0xA0AA6A53;
- const GETWEEK getweek = (GETWEEK)0xA082AD87;
- const GetAkku getakku = (GetAkku)0xA1254830;
- const f_uint2str uint2str = (f_uint2str)0xA0825687;
- //const DWMODDW dwmoddw = (DWMODDW)0xA160948C; //because Keil's problem
- //for system data with Absolute Location.
- word Akkpct __at 0xA863FF08;
- word net __at 0xA86F0BB8;
- #endif
- extern int_int dwmoddw(int a, int b);
- 。。。。。。。
复制代码
无关的部分我已经省略,前面是包含头文件,之后定一个宏,在下面那个格式字串就涉及到了变量的决定定址,在Keil里面试用__at关键词。然后后面是一个常量数组,需要注意的是这些一定要声明为常量,否则会被定位到数据区去。在之后就是本工程使用的函数部分。这个函数原形的声明在x65.h里面,再后面是两个全局变量的绝对定址。分别时电池容量百分比和网络信号强度。这些是和地址有关的部分,在下面的一个声明是为了和startup.s里面联合编译使用的声明部分,Keil对这种用R0-R3返回变量的功能支持也有问题,所以我用了一个Thunk。
此外,我们要设置工程的输出文件为Intel的Hex格式,然后还要设置附加的命令,让他编译完成后自动转换Hex为VKP,那么在Outpu选项卡里面不仅要设定输出格式,还要在User Program那里写下如下的命令:
- .\hex2vkp -fE:\siemens\tools\S6CV50.bin -cstartup.s -oRepSP.vkp output\RepSP.hex
复制代码
意思是原始文件是E:\siemens\tools\S6CV50.bin,这个需要改成你自己的,注释来自于startup.s,输出为RepSP.vkp。输入是output\RepSP.hex。
这样在编译完成后就会自动生成vkp了。
那么,了解了这个工程的各部分以后,我们看一下为了移植这个工程,你需要处理那些部分。可能这才是大家需要的!
移植这个需要处理的函数有:
- const GETDATETIME getdate = (GETDATETIME)0xA0AA6A53;
- const GETWEEK getweek = (GETWEEK)0xA082AD87;
- const GetAkku getakku = (GetAkku)0xA1254830;
- const f_uint2str uint2str = (f_uint2str)0xA0825687;
- //const DWMODDW dwmoddw = (DWMODDW)0xA160948C; //because Keil's problem
- AREA THUNKFUNCTION, CODE, AT 0xA0800040 // READONLY, ALIGN=4
- PUBLIC dwmoddw?T
- dwmoddw?T PROC CODE16
- LDR R2, =0xA160948C
- BX R2
- ENDP
复制代码
需要注意的是,如果是Thumb模式的函数,因为是函数指针,最后一个bit为1。即在函数地址上加1。特别的是dwmoddw是在startup.s里面用thumb处理的。涉及到的全局变量有:
- word Akkpct __at 0xA863FF08;
- word net __at 0xA86F0BB8;
复制代码 涉及到的函数Hook有:
- AREA STARTUPCODE1, CODE, AT 0xA0AFCB82 // READONLY, ALIGN=4
- AREA STARTUPCODE2, CODE, AT 0xA08DAF2E // READONLY, ALIGN=4
复制代码
还有就是Patch的占用地址了,这个在工程选项里设定。
对于这些,我们从几个方面入手:
从待机的显示和处理可以找到getdate getweek dwmoddw 以及信号强度的变量地址
从工模的ACCU菜单可以找到getakku uint2str 和电池的百分比容量。
那么我分别给出S65的类似部分。
- A1253530 ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
- A1253530
- A1253530
- A1253530 wndACCUMonitor ; CODE XREF: j_wndACCUMonitorj
- A1253530 ; DATA XREF: ROM:off_A0A2EBD8o
- A1253530
- A1253530 var_34 = -0x34
- A1253530 var_30 = -0x30
- A1253530 var_2C = -0x2C
- A1253530
- A1253530 F0 4D 2D E9 STMFD SP!, {R4-R8,R10,R11,LR}
- A1253534 01 40 A0 E1 MOV R4, R1
- A1253538 00 70 A0 E1 MOV R7, R0
- A125353C 04 00 A0 E1 MOV R0, R4
- A1253540 4C 13 9F E5 LDR R1, =asc_A1410094
- A1253544 18 D0 4D E2 SUB SP, SP, #0x18
- A1253548 9A 20 A0 E3 MOV R2, #0x9A
- A125354C 71 D7 0E EB BL memcpy
- A1253550 0C 20 A0 E3 MOV R2, #0xC
- A1253554 CF 1F 8F E2 ADR R1, asc_A1253898 ; "AkkuMon (1)"
- A1253558 07 00 A0 E1 MOV R0, R7
- A125355C 6D D7 0E EB BL memcpy
- A1253560 09 10 A0 E3 MOV R1, #9
- A1253564 00 00 A0 E3 MOV R0, #0
- A1253568 B0 04 00 EB BL GetAkku
- A125356C 08 00 8D E5 STR R0, [SP,#0x38+var_30]
- A1253570 00 00 A0 E3 MOV R0, #0
- A1253574 07 10 A0 E3 MOV R1, #7
- A1253578 AC 04 00 EB BL GetAkku
- A125357C 04 00 8D E5 STR R0, [SP,#0x38+var_34]
- A1253580 00 00 A0 E3 MOV R0, #0
- A1253584 08 10 A0 E3 MOV R1, #8
- A1253588 A8 04 00 EB BL GetAkku
- A125358C 00 50 A0 E1 MOV R5, R0
- A1253590 00 00 A0 E3 MOV R0, #0
- A1253594 06 10 A0 E3 MOV R1, #6
- A1253598 A4 04 00 EB BL GetAkku
复制代码
这个地方那个可以找到GetAKKU和uint2str 和百分比容量
- A12536F0 7A 00 84 E2 ADD R0, R4, #0x7A
- A12536F4 07 D7 0E EB BL memcpy
- A12536F8 9C 04 1F E5 LDR R0, =0xA863FF08 ;这个地址就是百分比容量
- A12536FC 04 20 A0 E3 MOV R2, #4
- A1253700 0C 10 8D E2 ADD R1, SP, #0x38+var_2C
- A1253704 B0 00 D0 E1 LDRH R0, [R0]
- A1253708 DD 47 D7 FB BLX uint2str
- A125370C 04 20 A0 E3 MOV R2, #4
- A1253710 0C 10 8D E2 ADD R1, SP, #0x38+var_2C
- A1253714 82 00 84 E2 ADD R0, R4, #0x82
- A1253718 FE D6 0E EB BL memcpy
- A125371C CC 54 1F E5 LDR R5, =0xA863FF00
- A1253720 18 00 95 E5 LDR R0, [R5,#0x18]
- A1253724 00 00 50 E3 CMP R0, #0
- A1253728 38 00 00 0A BEQ loc_A1253810
- A125372C B2 00 D0 E1 LDRH R0, [R0,#2]
- A1253730 04 20 A0 E3 MOV R2, #4
- A1253734 0C 10 8D E2 ADD R1, SP, #0x38+var_2C
- A1253738 D1 47 D7 FB BLX uint2str
复制代码
从GetSPNameWrap出发,可以找到待机和屏保时的Hook地址,如下:
- A08DAF28 ; 圹圹圹圹圹圹圹?S U B R O U T I N E 圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹圹?
- A08DAF28
- A08DAF28
- A08DAF28 GetSPNameWrap ; CODE XREF: ROM:A08DB0C4p
- A08DAF28 ; ROM:A08DB176p ...
- A08DAF28 70 B5 PUSH {R4-R6,LR}
- A08DAF2A 05 1C ADD R5, R0, #0
- A08DAF2C 00 21 MOV R1, #0
- A08DAF2E 21 F2 28 FE BL GetSPName ;待机时Hook的就是这个地址。屏保时Hook的是GetSPName这个函数的开始。
- A08DAF32 01 28 CMP R0, #1
- A08DAF34 17 D1 BNE loc_A08DAF66
- A08DAF36 28 1C ADD R0, R5, #0
- A08DAF38 52 F1 0A E9 BLX sub_A0A2D150
- A08DAF3C 06 1C ADD R6, R0, #0
- A08DAF3E 01 24 MOV R4, #1
- A08DAF40 0D E0 B loc_A08DAF5E
- A08DAF42 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
- A08DAF42
- A08DAF42 loc_A08DAF42 ; CODE XREF: GetSPNameWrap+38j
- A08DAF42 21 1C ADD R1, R4, #0
- A08DAF44 28 1C ADD R0, R5, #0
- A08DAF46 52 F1 74 EE BLX sub_A0A2DC30
- A08DAF4A 0A 28 CMP R0, #0xA
- A08DAF4C 04 D1 BNE loc_A08DAF58
- A08DAF4E 22 1C ADD R2, R4, #0
- A08DAF50 20 21 MOV R1, #0x20
- A08DAF52 28 1C ADD R0, R5, #0
- A08DAF54 52 F1 3E EE BLX sub_A0A2DBD4
- A08DAF58
- A08DAF58 loc_A08DAF58 ; CODE XREF: GetSPNameWrap+24j
- A08DAF58 01 34 ADD R4, #1
- A08DAF5A 24 04 LSL R4, R4, #0x10
- A08DAF5C 24 0C LSR R4, R4, #0x10
- A08DAF5E
- A08DAF5E loc_A08DAF5E ; CODE XREF: GetSPNameWrap+18j
- A08DAF5E B4 42 CMP R4, R6
- A08DAF60 EF D9 BLS loc_A08DAF42
- A08DAF62 01 20 MOV R0, #1
- A08DAF64 70 BD POP {R4-R6,PC}
- A08DAF66 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
- A08DAF66
- A08DAF66 loc_A08DAF66 ; CODE XREF: GetSPNameWrap+Cj
- A08DAF66 00 20 MOV R0, #0
- A08DAF68 70 BD POP {R4-R6,PC}
复制代码
GetDate可以查找类似的特征码,而GetWeek也可以,dwMODdw函数就在GetWeek里面,不过可能是一个Thunk跳转。
- A082AD86
- A082AD86 GetWeek ; DATA XREF: sub_A0C5E764:off_A0C5E76Co
- A082AD86 80 B5 PUSH {R7,LR}
- A082AD88 FF F7 C3 FF BL sub_A082AD12
- A082AD8C 01 1D ADD R1, R0, #4
- A082AD8E 07 20 MOV R0, #7
- A082AD90 0D F0 CC EE BLX j_dwMODdw_0
- A082AD94 08 06 LSL R0, R1, #0x18
- A082AD96 00 0E LSR R0, R0, #0x18
- A082AD98 80 BD POP {R7,PC}
复制代码
如下,前面的其实是一个thunk。
- A0838B2C
- A0838B2C ; Attributes: thunk
- A0838B2C
- A0838B2C j_dwMODdw_0 ; CODE XREF: sub_A082AC58+Cp
- A0838B2C ; sub_A082ACEC+Ap ...
- A0838B2C 04 F0 1F E5 LDR PC, =dwMODdw
- A0838B2C ; End of function j_dwMODdw_0
- A0838B2C
- A0838B2C ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
- A0838B30 8C 94 60 A1 off_A0838B30 DCD dwMODdw ; DATA XREF: j_dwMODdw_0r
复制代码
剩下的就是网络信号了,可以从下面找到:
- A08C0430
- A08C0430 GetSignIconID ; CODE XREF: sub_A08BFE54+3Ap
- A08C0430 70 B5 PUSH {R4-R6,LR}
- A08C0432 2A 48 LDR R0, =0xA860E594
- A08C0434 2A 4E LDR R6, =0xFFFF
- A08C0436 00 68 LDR R0, [R0]
- A08C0438 00 28 CMP R0, #0
- A08C043A 05 D1 BNE loc_A08C0448
- A08C043C 25 48 LDR R0, byte_A08C04D4
- A08C043E 00 88 LDRH R0, [R0]
- A08C0440 C0 07 LSL R0, R0, #0x1F
- A08C0442 01 D4 BMI loc_A08C0448
- A08C0444 30 1C ADD R0, R6, #0
- A08C0446 70 BD POP {R4-R6,PC}
- A08C0448 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
- A08C0448
- A08C0448 loc_A08C0448 ; CODE XREF: GetSignIconID+Aj
- A08C0448 ; GetSignIconID+12j
- A08C0448 26 4C LDR R4, =word_A0A318B2
- A08C044A 6D F1 D8 E8 BLX GetNetSignValue
- A08C044E 05 04 LSL R5, R0, #0x10
- A08C0450 2D 0C LSR R5, R5, #0x10
- A08C0452 33 F2 CF FF BL sub_A0AF43F4
- A08C0456 00 28 CMP R0, #0
- A08C0458 07 D0 BEQ loc_A08C046A
- A08C045A 33 F2 DF FF BL sub_A0AF441C
- A08C045E 02 28 CMP R0, #2
- A08C0460 01 D1 BNE loc_A08C0466
- A08C0462 18 34 ADD R4, #0x18
- A08C0464 01 E0 B loc_A08C046A
- A08C0466 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
- A08C0466
- A08C0466 loc_A08C0466 ; CODE XREF: GetSignIconID+30j
- A08C0466 1F 4C LDR R4, =word_A0A318B2
- A08C0468 0C 34 ADD R4, #0xC
- A08C046A
- A08C046A loc_A08C046A ; CODE XREF: GetSignIconID+28j
- A08C046A ; GetSignIconID+34j
复制代码
从上面那个GetNetSignValue那个函数入手,经过一个Thunk后,到达:
- A00EEADC
- A00EEADC sub_A00EEADC ; CODE XREF: GetNetSignValue+4j
- A00EEADC ; DATA XREF: GetNetSignValue:off_A0A2D604o
- A00EEADC 4F 48 LDR R0, =0xA86F0BB0 ;这个地址加8就是。
- A00EEADE 80 68 LDR R0, [R0,#8]
- A00EEAE0 70 47 BX LR
- A00EEAE0 ; End of function sub_A00EEADC
- A00EEAE0
复制代码
这里面的0xA86F0BB0,系统从它加8那里得到信号值。
完成了,天气太热了,不想抓图了! |
-
-
RepSP.rar
32.63 KB, 下载次数: 100
替换运营商标志为自定义字串V2.1源文件
|