- 积分
- 813
- 实力分
- 点
- 金钱数
- 两
- 技术分
- 分
- 贡献分
- 分
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
Flash修改进阶
作者:coollang
很久之前写了那篇《Flash修改入门》,一直打算再写一篇,就在年前完成吧!
因为西门子的手机到目前为止,一直使用的是80C166的处理器,我写的东西主要以6688为主,一些东西在整个系列都是适用的,有时我会指出在不同机型的差别。
[B]一、西门子手机的系统软件介绍[/B]
西门子系列手机使用了英飞凌公司(Infineon)的80C166系列的处理器,支持多级的中断。里面包含了大量的片内资源如RAM,ROM,IO部分,中断控制,CAN总线等等,有兴趣的可以看一下附录的处理器手册,对此有详细的介绍!他的片内RAM,主要是两部分:A 2 KByte 16-bit wide internal RAM provides fast access to General Purpose Registers (GPRs),user data (variables) and system stack. The internal RAM may also be used for code.A 2 KByte 16-bit wide on-chip XRAM provides fast access to user data (variables), user stacks and code.
C166的寻址空间是16M,有相当多的通用寄存器(GPRs)和专用寄存器(SFRs)。其中GPRs是16个WORD寄存器,其中前八个可以作为BYTE寄存器使用,R0L,R0H...R7L,R7H,这些GPRs和SFRs都是维持在片内RAM中。
在我的理解上,西门子手机软件是一个基于消息的多任务系统。使用定时中断来执行任务切换(在非常多的系统中使用)。比如大家经常看到的一个函数0xB4724C(pSendMessage),就是用来发送消息的。而在MP3播放、短消息处理时多处用到了这个函数进行发送消息来执行各种操作。所以一些多任务操作才成为可能。和Windows的窗口函数一样,西门子的系统也使用了类似的技术,他在创建一个菜单时,也是注册一个结构,其中提供了一个回调函数来处理各种消息(主要是键盘消息)。
手机内部有一个BootRom,里面包含BootCore(类似于PC的BIOS),负责启动时加载系统,一般嵌入式的CPU都支持多种启动方式,比如从Ram、Flash启动,或者从串行部件启动。这些都是通过BootCore来完成的。启动时,CPU在加电后会跳转到固定地址去执行,一般会把BootRom映射到这个位置,然后BootCore负责硬件自检,然后根据IO量来判断启动方式(Flash或串行借口)。比如长按或短按开机键就可以提供不同的选择。如果是从Flash启动,那么Bootcore会进行一部分地址映射工作,比如6688,他会把Flash中0xC70000的一部分映射到0x870000去,然后跳转到0xC7:FADA,这是手机软件的入口点,然后手机软件启动。手机软件首先会执行C库的初始化函数,把库函数的一部分搬到Ram中去(为了加快速度),并初始化C库的堆栈。(以下部分很多借鉴了Mamaich的IDA汇编,他对这些研究的很深入,有兴趣的可以看一下他的相关的东西http://mamaich.fuckru.net/)
0xC7:FADA ;☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
0xC7:FADA Entry_StartFromFlash:
0xC7:FADA A5 5A A5 A5 diswdt
0xC7:FADE E6 08 00 FC mov CP, #0FC00h
0xC7:FAE2 CC 00 nop
0xC7:FAE4 E6 89 46 94 mov SYSCON, #9446h
0xC7:FAE8 E6 0B 00 FC mov STKUN, #0FC00h
0xC7:FAEC E6 0A 0C F9 mov STKOV, #0F90Ch
0xC7:FAF0 E6 09 00 FC mov SP, #0FC00h
0xC7:FAF4 CC 00 nop
0xC7:FAF6 DA 87 2E FB calls 87h, sub_87FB2E
0xC7:FAFA DA 87 D4 FB calls 87h, sub_87FBD4
0xC7:FAFE DA 87 F2 FB calls 87h, sub_87FBF2
0xC7:FB02 E6 00 00 00 mov DPP0, #0
0xC7:FB06 ; assume dpp0: 0 (page 0x0)
0xC7:FB06 E6 01 03 00 mov DPP1, #3
0xC7:FB0A ; assume dpp1: 3 (page 0xC000)
0xC7:FB0A E6 02 02 00 mov DPP2, #2
0xC7:FB0E ; assume dpp2: 2 (page 0x8000)
0xC7:FB0E E6 03 03 00 mov DPP3, #3
0xC7:FB12 ; assume dpp3: 3 (page 0xC000)
0xC7:FB12 E6 F0 00 60 mov r0, #6000h
0xC7:FB16 DA B3 CE 3D calls 0B3h, sub_B33DCE
0xC7:FB1A B5 4A B5 B5 einit
0xC7:FB1E DA 87 A2 FC calls 87h, sub_87FCA2
0xC7:FB22 DA 87 E2 FC calls 87h, sub_87FCE2
0xC7:FB26 DA 87 36 FD calls 87h, loc_87FD36
0xC7:FB2A FA B4 72 5D jmps 0B4h, loc_B45D72
0xB4:5D72 26 F0 0C 00 sub r0, #0Ch
0xB4:5D76 DA B4 0A 57 calls 0B4h, GbsInit
0xB4:5D7A DA B4 EE 5C calls 0B4h, MainLoop?
0xB4:5D7E DA B4 6E 5D calls 0B4h, GbsExit
0xB4:5D82 E1 02 movb rl1, #0
0xB4:5D84 E4 20 0B 00 movb [r0+0Bh], rl1
0xB4:5D88 A9 40 movb rl2, [r0]
0xB4:5D8A C0 44 movbz r4, rl2
0xB4:5D8C 06 F0 0C 00 add r0, #0Ch
0xB4:5D90 DB 00 rets
从X55开始,将BootCore的一部分放在Flash上,并将直接从串口启动的能力封锁(TestPoint)。通过Flash中的BootCore部分,简化了官方软件升级时的操作,并增加了一些验证。最初第三方软件是是通过处理TestPoint的办法来达到自由读写Flash的目的,后来Nutzo(KSie,Freia系列的作者)找到了BootCore的溢出bug,不需处理TestPoint即可读写FullFlash。所以针对X55的软件都会有两个选项,一个是正常的(Nutzo的Loader),另一个是WithTestPoint,因为会用到不同的Loader。
[B]二、80C166的常用汇编级规则[/B]
西门子软件是用C写成的(会加以汇编),所以在C编译成汇编汇编语言的时候,依赖于编译器的实现,有一些固定的规则,这些规则有利于读懂反汇编的程序。如果有兴趣大家可以用Tasking写一些C语言的程序,编译后看一下对应的LST文件,也可以看到一些规则。
1、堆栈
软件使用的栈有两种,系统栈和用户栈。系统栈主要存储返回地址,段寄存器等等(主要是函数调用),使用Push,PoP一类的操作可以存取系统栈。下面给出一个察看调用栈的修改:
$Segmented
$Mod167
Patch_Address EQU 1F8000h ;Free Space in Flash (CHANGE THIS)
Patch Section Code Word At Patch_address ; Start Patch at Patch_Address
main proc far ; start main of patch
;TODO,恢复跳转指令覆盖的语句
mov [-r0],r12
mov [-r0],r13
mov [-r0],r14
mov [-r0],r15
mov r4,#8 ;调用栈深度
cmp r12, #4E3h ;以下为判断察看条件,比如调用字符4E3的时候,需自行修改
jmp CC_NZ, ProcEnd
mov r15, r4
mov r14, #1000h ;调用栈的输出开始地址,我一般会用0C:1000
PopProc:
pop r12
pop r13
extp #0Ch, #1
mov [r14],r12 ;存储调用栈的Offset
add r14,#2
extp #0Ch, #1
mov [r14],r13 ;存储调用栈的Seg
add r14,#2
mov [-r0],r12 ;将系统栈的内容压入用户栈保存
mov [-r0],r13
sub r15,#1
jmp CC_NZ,PopProc ;判断深度
mov r15,r4 ;恢复系统栈
PushProc:
mov r13,[r0+]
mov r12,[r0+]
push r13
push r12
sub r15,#1
jmp CC_NZ,PushProc
ProcEnd:
mov r15,[r0+]
mov r14,[r0+]
mov r13,[r0+]
mov r12,[r0+]
rets
main endp
Patch EndS
END
这个例程可以把一定深度的调用栈显示在c:1000的位置,然后用AT指令可以看到函数的调用点的下一个位置,因为调用函数是会把吓一条指令的位置压入系统栈。
一般情况下,函数内部的局部变量会使用用户栈,用户栈的栈顶指针一般存储在R0,因为栈是向上增长,若以在压栈的过程就是mov [-R0],R12这种形式,而弹栈就是mov R12,[R0+]。可以压入WORD或BYTE。相应的用mov或movb。
系统的堆内存操作需要使用malloc和free,在6688上的对应函数为
0xB4853C: void far* huge HeapMalloc( void far *Heap, unsigned int Size);
0xB486DC:void far* huge HeapFree( void far *Heap);
用这两个函数可以在堆中分配或释放内存,不过一般情况下Patch会使用全局变量来存储数据。
2、指针
C166的处理器地址空间为24位(16M),整型是16位。对于数据来说,寻址用Page,而对于指令来说,寻址使用Seg。对于一个16位的数据地址Offset来说,要想获得24位的绝对地址,需要和DPP1-4来配合得到。DPP(Data Page Pointer)是4个数据Page寄存器,根据Offset的最高两位Bit,来决定使用哪一个DPP。使用时用Offset和当前使用的DPP左移14位相加即为24bit的地址。如果临时指定一个绝对地址,可以用EXTP或EXTS,分别对应Page和Seg。所以一个DWORD的绝对地址指针(far*)根据是数据还是地址会有不同的解释。在Keil中可以用pag()和seg()来取得对应的内容。这部分的内容可以详细看一下指令手册的第六章Addressing Modes。
3、函数
对于结构性程序设计来说,对程序的理解就是对一个个函数的理解(当然还有数据结构,下面会说到)。因为是用C语言写的,所以调用规则是cdecl。即参数传入顺序是从右到左,调用函数负责清除堆栈。在西门子用的C编译器处理参数的规则是首先使用寄存器R12,R13,R14,R15。如果参数多于4个WORD,则用用户栈来传入参数。比如void f(int a,int b,int c,int d,int e,int f)则汇编结果会是
mov [-R0],f
mov [-R0],e
mov R12,a
mov R13,b
mov R14,c
mov R15,d
calls _f
add R0,#4
而R6,R7,R8,R9一般是作为函数内的局部变量,所以在函数调用的时候,通常需要保存这个寄存器。函数的返回值回放在R4,R5中,一般情况只用到R4,如果返回一个指针,则需要用R4和R5。R0一般是用户栈了。R1,R2,R3和R10,R11则根据需要使用。
[B]三、一些Patch的技巧性内容[/B]
[B]四、Patch中遇到的一些数据结构[/B]
[B]五、对一些常用菜单函数的介绍[/B]
[B]附录:[/B]
Patch所需要的汇编头文件
C166处理器手册[upload=rar]viewfile.asp?ID=6757[/upload]
C166汇编手册[upload=rar]viewfile.asp?ID=6758[/upload]
待续。。。。。。。。。。。。。。。。。。
[此贴子已经被作者于2004-1-12 15:07:53编辑过]
|
|