·汉化新世纪 ·汉化新世纪论坛 ·百家争鸣 ·论坛集萃 ·汉化问答 ·软件介绍
文章首页 >> 汉化教学 >> PPC汉化 >> PPC字体修改基础一 初探ARM汇编语言    Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎

PPC字体修改基础一 初探ARM汇编语言

作者: 雅枫 来源:雅枫的博客 时间:2005-09-07 点击:15658

PPC字体修改基础一  
——初探ARM汇编语言

大家知道平常我们用的windows程序是破解,修改字体等都是通过反汇编,调试等手段来实现的,常用的工具有Ollydbg,SoftICE,IDA Pro等,其中又分静态分析跟动态调试,不过对于PPC的分析,动态就甭指望了,因为调试要求至少程序能运行起来,PPC程序基于ARM,在PC上根本没法运行,所以……(除非有在PPC上运行的调试器,除非调试器自带虚拟机)

只能静态分析了,现在就我所知,IDA Pro是能反汇编ARM程序的,EVC也有这能力(甚至能调试,不过这家伙在我的机器上调试起来就像蜗牛……)所以跟专业的IDA Pro是更好的选择。其他我暂时还没发现有能反汇编ARM程序的软件。IDA pro支持的程序种类非常多。有兴趣可以看看IDA的支持列表,IDA的反汇编速度非常慢,不过这似乎不是问题,因为PPC程序也都非常小:)

具体的ARM指令集,自己去看手册,有中文的,我也不熟,毕竟我以前搞的是x86汇编,跟ARM指令集没什么相似之处的,所以以前的经验就一切索那浮云了,我也是从头开始摸索。其实ARM是精简指令集,指令很少,也很简单。掌握他们并不困难,不过掌握了他们并不代表就是能改程序了,因为如果想改程序,更重要的是知道PPC的编程结构,也就是OS怎么组织程序的,如参数调用约定啦,WINAPI啦之类的,ARM的指令集非常简单,甚至没有CALL指令。不过很可惜,现在这方面的书非常少,并且,也似乎没有深究的必要,因为同Native程序相比,.NET CF程序有着更多的优点。那是未来的趋势。
现在就探索一下PPC的程序,其实探索这东西,最好的办法是分析自己写的程序。因为别人的程序你没有源代码,并且没法按你的要求去组织代码片断分析,所以写点小程序分析之还是很有必要的。好在微软的E VC++是免费下载的并且微软自己都提供了注册码(看来MS是非常急迫的想抢占市场啊),所以每个人都能获得免费的开发环境,至于PPC的SDK,就看需要下了,因为本质上PPC的程序跟wince的程序是没区别的,只不过通过AYGSHELL等库来实现了跟PPC平台在界面上更紧密的集成,这不是我们的重点。其他基本没啥区别,尤其在反汇编代码上:)
我先建了一个mytest的工程,是SDK的Simple工程,这种工程的好处就是反汇编代码异常简洁。没有MFC的干扰代码。然后我只加了一句代码总的代码如下
#include "stdafx.h"

int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     // TODO: Place code here.
    MessageBox(0,_T("helloWorld"),_T("hello"),MB_OK);
    return 0;
}
程序特简单,一次编译通过。然后打开IDA反汇编,主要反汇编代码如下:
WinMain                                 ; CODE XREF: start+34 p
.                                         ; DATA XREF: .pdata:00014000 o
var_4           = -4
                 STR     LR, [SP,#var_4]!
                 LDR     R2, =aHello     ; lpCaption
                 LDR     R1, =aHelloworld ; lpText
                 MOV     R3, #0          ; uType
                 MOV     R0, #0          ; hWnd
                 BL      MessageBoxW
                 MOV     R0, #0
                 LDMFD   SP!, {PC}
.text:0001102C ; End of function WinMain

框架先不管,我们先看看我加的那句MessageBox调用的反汇编代码
LDR     R2, =aHello              ; lpCaption
LDR     R1, =aHelloworld         ; lpText
MOV     R3, #0                  ; uType
MOV     R0, #0                  ; hWnd
BL      MessageBoxW
可以看出,参数还是从右到左传入的,但是却没有使用堆栈,而是用的R3,R2,R1,R0,
AMR没有x86那些eax,ebx等的寄存器,只有R0~R15 16个寄存器,然后我贴出ARM汇编手册的相关介绍
[引用]
ARM 处理器有二十七个寄存器,其中一些是在一定条件下使用的,所以一次只能使用十六个... 
·    寄存器 0 到寄存器 7 是通用寄存器并可以用做任何目的。不象 80x86 处理器那样要求特定寄存器被用做栈访问,或者象 6502 那样把数学计算的结果放置到一个累加器中,ARM 处理器在寄存器使用上是高度灵活的。 
·    寄存器 8 到 12 是通用寄存器,但是在切换到 FIQ 模式的时候,使用它们的影子(shadow)寄存器。 
·    寄存器 13 典型的用做 OS 栈指针,但可被用做一个通用寄存器。这是一个操作系统问题,不是一个处理器问题,所以如果你不使用栈,只要你以后恢复它,你可以在你的代码中自由的占用(corrupt)它。每个处理器模式都有这个寄存器的影子寄存器。 
·    寄存器 14 专职持有返回点的地址以便于写子例程。当你执行带连接的分支的时候,把返回地址存储到 R14 中。同样在程序第一次运行的时候,把退出地址保存在 R14 中。R14 的所有实例必须被保存到其他寄存器中(不是实际上有效)或一个栈中。这个寄存器在各个处理器模式下都有影子寄存器。一旦已经保存了连接地址,这个寄存器就可以用做通用寄存器了。 
·    寄存器 15 是程序计数器。它除了持有指示程序当前使用的地址的二十六位数之外,还持有处理器的状态。 
[引用结束]
可能有些术语会看不懂,没关系,有些地方我也看不懂,慢慢来嘛。像这个例子,四个参数完全用寄存器来传递,不必说是效率非常之高了,不过参数多了必定用栈,因为不就才16个寄存器嘛就用玩了怎么办?虽然在CPU中都是通用寄存器,但在OS里边就不是通用的了,有些是有特殊意义的,上边引用的那段文字就有提及,如上边的R13,R14.
在上边的例子中,MessageBox一共有4个参数,其中两个是字串,两个是常数,所以写入寄存器的方式也不相同。常数直接MOV进去,字串取地址用的是LDR指令
下边是指零手册中的介绍

MOV{条件}{S}  <dest>, <op 1>
                dest = op_1
MOV 从另一个寄存器、被移位的寄存器、或一个立即值装载一个值到目的寄存器。你可以指定相同的寄存器来实现 NOP 指令的效果,你还可以专门移位一个寄存器: 
  MOV     R0, R0                  ; R0 = R0... NOP 指令
  MOV     R0, R0, LSL#3           ; R0 = R0 * 8

LDR{条件}    Rd, <地址>


LDR指令我也不是很太懂,根据指令手册,这个指令是移动相应地址中的内容去寄存器,所以我分析了这两了LDR的指令,IDA反汇编的代码无疑更优秀,不过一句LDR R2,=ahello确实使人不知所以然,是地址?还是其他?下边看看EVC里边的反汇编代码吧:
26011010   mov       r3, #0
26011014   ldr       r2, [pc, #0x30]
26011018   ldr       r1, [pc, #0x28]
2601101C   mov       r0, #0
26011020   bl        |MessageBoxW (2601108c)|
哦,这句的指令是把PC+30地址的内容放到r2里边啊,PC+30时多少?就是现在的26011014+30就是IDA反汇编的00011020里边的内容了,为什么这两个东西反汇编地址不一样呢?因为VC是在调试,所以程序被重定位了。00011020里边是什么呢?经过分析IDA的反汇编代码,就是字串“hello”的首地址了也就是指针。如此看来,r2,r1仍然是放的字串的指针,这跟win32的API实现是完全相同的。就上边看来,似乎EVC反汇编的代码更容易分析,其实不然。因为EVC我用的是DEBUG版,是有调试信息的。如果换REL版则EVC只会反汇编指令,而无任何信息,效果比IDA差得多。
下边我再探索一下API函数参数传递的内部处理方式,以及如何返回值。我在这个工程里边添加了以下代码
#include "stdafx.h"

int WINAPI MyFunc(int,int,int);
int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     // TODO: Place code here.
    MessageBox(0,_T("helloWorld"),_T("hello"),MB_OK);
    MyFunc(1,2,3);
    return 0;
}

int WINAPI MyFunc(int a,int b,int c)

{
    int d =a+b+c;
    return d;
}
可以看出,我在原来的基础上加了一个函数的调用,并且这个函数是声明为WINAPI,即跟API是一种调用方式,最后返回了这三个输入参数的和。其反汇编代码如下:
调用部分:
14:       MyFunc(1,2,3);
26011024   mov       r2, #3
26011028   mov       r1, #2
2601102C   mov       r0, #1
26011030   bl        |MyFunc (26011050)|
这里跟上边分析的一板一眼,把三个参数分别传入r2,r1,r0,然后bl到我们的函数。BL类似于x86中的Call指令,不过动作比Call简单的多。在指令手册里边是这么描述这个函数的:

BL 是另一个分支指令。就在分支之前,在寄存器 14 中装载上 R15 的内容。你可以重新装载 R14 到 R15 中来返回到在这个分支之后的那个指令,它是子例程的一个基本但强力的实现。

R15是什么呢?就是PC!即程序计数器,看到这里,不用说也知道如果程序返回只要把R14的值再装回R15子程序也就返回了。实际是不是这样呢?我们来看子程序的反汇编代码:为了使大家能看懂这段反汇编程序,我加了部分注释。
18:   int WINAPI MyFunc(int a,int b,int c)
19:
20:   {
26011050   mov       r12, sp             ;先把sp的内容装入r12
26011054   stmdb     sp!, {r0 - r2}        ;stm的作用是复制寄存器的内容到栈,为什么要这样呢?
;刚才说过参数是通过Rx寄存器传递的参数,现在复制到了栈上,不
;过这好像抵消了用寄存器传递带来的性能的提升……考虑到这是
;debug版,release版本应该情况会好一些吧,这个一会儿再分析。
26011058   stmdb     sp!, {r12, lr}        ;把r12根lr复制到堆栈,r12存的是刚才保存的sp即栈顶指针
                                    ;lr则是r14的别名,即函数的返回点。我倒,现在的栈不就跟
;用完全用栈传递完全相同了么……
2601105C   sub       sp, sp, #8        ;建立了两个中间变量,但为什么是建2个呢?我可只建了一个
                                    ;d啊555。难道是保留的?
21:       int d =a+b+c;                ;
26011060   ldr       r0, [sp, #0x10]        ;
26011064   ldr       r1, [sp, #0x14]        ;
26011068   add       r2, r0, r1            ;这一段就是做加法了。
2601106C   ldr       r3, [sp, #0x18]    ;
26011070   add       r0, r2, r3            ;加法完成,结果存在r0
26011074   str       r0, [sp]            ;结果存入d
22:       return d;                    ;再看看是怎么返回的。
26011078   ldr       r1, [sp]            ;下三句作用是把结果放r0,代码比较垃圾
2601107C   str       r1, [sp, #4]        ;这里r1,r0里边的值都是结果6,为什么我说返回值在r0里边呢?
26011080   ldr       r0, [sp, #4]        ;这是我调整了主函数的代码,分析出的结果。
23:   }
26011084   add       sp, sp, #8        ;释放建的中间变量
26011088   ldmia     sp, {sp, pc}        ;现在栈顶即是保存的PC跟SP,分别弹出,相当于以前x86的
                                    ;mov esp,ebp
                                    ;ret

我的修改后的测试主函数是:
int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     int a;
    int b=4;
    // TODO: Place code here.
    MessageBox(0,_T("helloWorld"),_T("hello"),MB_OK);
    int i=MyFunc(1,2,3);
    a=b+i;
    return 0;
}
下边再看看rel版的程序,因为一般发行的就是rel版,所以探索之更有意义。下边是反汇编代码。
WinMain                                 ; CODE XREF: start+34 p
.                                         ; DATA XREF: .pdata:00014000 o
var_4           = -4
                 STR     LR, [SP,#var_4]!
                 LDR     R2, =aHello     ; lpCaption
                 LDR     R1, =aHelloworld ; lpText
                 MOV     R3, #0          ; uType
                 MOV     R0, #0          ; hWnd
                 BL      MessageBoxW
                 MOV     R2, #3
                 MOV     R1, #2
                 MOV     R0, #1
                 BL      sub_11038
                 MOV     R0, #0
                 LDMFD   SP!, {PC}
.text:0001102C ; End of function WinMain

                              ; CODE XREF: WinMain+24 p
                ADD     R0, R0, R1
                ADD     R0, R0, R2
                RET
. End of function sub_11038
代码无疑比debug模式更简洁易懂,至少在代码方面简直最小代码了,我就不做过多的解释了,不明白的话看上边那个debug的注释,我感兴趣的是:这儿居然有个ret指令,是新加的指令还是IDA为了易懂而弄得伪指令?因为我的指令手册没有该指令的解释。再看看EVC的反编译结果。
   add       r0, r0, r1
   add       r0, r0, r2
   mov       pc, lr
   ldr       r12, [pc]
   ldr       pc, [r12]
   andeq     r3, r1, r12
   andeq     r1, r1, r4, lsl r2
   andeq     r2, r1, r0
这是什么乱七八糟了^^,看看返回那个地方,好像就一句
mov       pc, lr
就返回了。下边的好像都没有用,我调试了一下,果然如此,在这一句就返回了。看来IDA为了使代码易懂,有点不尊重原著了:)现在似乎只有EVC能在模拟器上或者连上机器能调试,不过代码异常混乱,比ida分析困难的多。不推荐使用了,感兴趣可以自己摸索。


原文引自雅枫的博客,如有版权疑问请来信说明,我们会尽快处理

汉化新世纪 责任编辑: 乾 .:|:. 标签(Tag): PPC 字体 汇编

·上一篇: 汉化新世纪网志启用新地址;汉化新世纪各套RSS正式启用 ·下一篇: PPC字体修改基础二 ARM汇编语言之继续探索函数处理

· 版权申明: 本文引自《雅枫的博客》,如有版权疑问请及时联系本站,以便本站处理。

· 转载申明: 本文引自《雅枫的博客》[ 作者: 雅枫],如需转载请直接联系原始作者,并请注明原始出处。

相关文章                                                                                发表评论 打印此文 关闭窗口

| 设为首页 | 加入收藏 | 联系我们 | 友情链接
Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎
本站内容,除转载或版权特别申明的内容外,皆遵守 创造共用协议中文版之“署名-非商业性使用-禁止演绎 2.5 中国大陆”条款
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 China License.
本网站内容源自汉化新世纪论坛的摘录和汉化新世纪成员的原创文章。
凡汉化新世纪论坛的文字皆默认为汉化新世纪与原作者共同拥有并授权发布。
如对本站发布文章有所异议请来信告知,我们将及时删除。
凡商业摘录本站文字请先与我们联系,本站将保留非授权商业发布的追究权利。
凡非商业摘录本站文字请明显注明出处和原作者,并不得改动,凡改动必先征求原作者同意。
苏ICP备05002283号