首页
社区
课程
招聘
[原创]Windows CE ARM平台汇编开发入门系列(第一篇 ARM处理器的寻址方式)
发表于: 2009-3-17 22:17 18665

[原创]Windows CE ARM平台汇编开发入门系列(第一篇 ARM处理器的寻址方式)

2009-3-17 22:17
18665
【文章标题】: Windows CE ARM平台汇编开发入门系列(第一篇 ARM处理器的寻址方式)
【文章作者】: 加百力
【下载地址】: 自己搜索下载
【编写语言】: ARM汇编
【使用工具】: Xarm v1.0,EditPlus,Pocket PC 2003模拟器。
【操作平台】: Windows XP SP2
【学习基础】: 对ARM CPU体系结构有初步了解。
【内部编号】: MASSADA 0022
【作者声明】: 介绍Windows CE ARM平台汇编开发知识;为使用汇编开发和逆向分析打基础;庆祝
             Xarm公开发布1周年
--------------------------------------------------------------------------------
【详细过程】

Windows CE是微软于1996年推出的嵌入式开发平台,该平台具有支持CPU种类多,支持硬件平台多,功能强大开发简单等优点。目前在智能手机、导航仪、机顶盒、医疗设备等方面有广泛的应用。例如摩托罗拉、三星、惠普、HTC、多普达、联想、夏新等厂商都有WM手机生产线,而国内大多数车载导航仪都是基于Windows CE平台的。

嵌入式平台的CPU一般是基于RISC架构的。常见的有ARM、MIPS、Power PC、SH等。其中尤其以ARM架构的CPU使用比例最大。

本系列主要介绍在Windows CE/ARM平台使用汇编语言开发的基础知识为使用ARM汇编做底层开发以及逆向分析打下基础,同时也庆祝Xarm公开发布1周年(2008年03月17日发布)。

对于所有的汇编语言第一个介绍的知识点都是处理器的寻址方式,本系列也不例外。不过ARM处理器是RISC架构的处理器,她的一个重要特点就是寻址方式比CISC架构的处理器如X86简单。ARM处理器只有8种寻址方式。下面就逐个介绍每种寻址方式。实例代码使用Xarm v1.0构建而成。推荐用户使用EditPlus并进行配置,可以使用快捷键构建并传送程序到C:\PPC目录下。

01、立即数寻址
    数据保存在指令中可以立即使用。所以称为立即数。
   
    例如:
    mov   r0    ,    # 50              ;将立即数50传送到r0寄存器中。

    使用规则:

    01、★立即数前面必须使用"#"做标记,这是和MASN32不同之处,初学ARM汇编很容易忘记。
    02、如果没有明确标识默认的进制为10进制。
    03、16进制数使用"0x"做标记如:0x64。不能使用MASM32中的在末尾加H的写法。64H是错误的。
    04、2进制数据使用"2_"做标记如:2_0101。
    05、立即数寻址可以使用4则运算例如:mov   r0    ,   # 50 + 50
      06、立即数寻址可以使用逻辑运算例如:mov  r0    ,   # 1 | 0
      07、★特别注意:由于立即数保存在指令中所以并不是任意立即数都可以使用。立即数必须满足这样的条件:常数必须由一个8位的常数循环移位偶数位得到的。比如:[0,256]之间的数都可以,1024,2048等常数都正确。如果出错则在编译的时候就会出现提示,Xarm构建时控制台会出现白底红字的显示效果。

02、寄存器寻址
    操作数的值保存在寄存器中,指令执行时直接取寄存器中数据操作。
    例如:

    mov   r1    ,    r0                ;将r0中的数据传递到r1寄存器中。
    add   r0    ,    r0    ,    r1     ;将r0和r1中数据累加后保存到r0中。

03、寄存器偏移寻址
    这种寻址方式是ARM处理器特有的。当第二个操作数是寄存器寻址时,可以首先进行移位操作再和第一个操作数结合。

    例如:

    mov      r0   ,   r1    ,    lsl #1      ;将r1中的数据逻辑左移一位再传送到r0中。相当于r0 = r1 * 2

    这是ARM指令的一个特点:可以将移位操作放在其他操作中在一条指令中完成。这样就提高了指令效率。
    移位指令有5种这里简单介绍一下在后面的文章中详细说明:
    lsl:逻辑左移。
    lsr:逻辑右移。
    asr:算术右移。
    ror:循环右移。
    rrx:带扩展的循环右移。

04、寄存器间接寻址
    所需要的操作数位于在寄存器保存的地址数据指向的内存中。寄存器中保存的实际上是一个指针。

    例如:

    ldr    r0    ,    [r1]   ;将r1中保存的内存地址中的数据传送到r0中。

    显然这类寻址方式很重要,因为通过它们可以实现内存和寄存器之间的数据交换。

05、基址变址寻址
    基址变址寻址是将基址寄存器中的内容和指令中给出的偏移量相加,形成操作数的有效地址。这种寻址方式适合访问基址附近的存储单元,常用于查表和数组操作。举个实战中的例子在编写PE程序加密软件时需要读取大量的PE文件头中的信息。一个简单的方法就是用一个基址寄存器保存文件头在内存中的偏移量,然后加上待取数据的偏移量就可以很方便、整齐的读取到所有需要的数据信息。

    ldr r0,[r1,#4]                        ;r0 ← [r1+4]
      ldr r0,[r1,#4]!                ;r0 ← [r1+4]、r1 ← r1+4
      ldr r0,[r1] ,#4                        ;r0 ← [r1]、r1 ← r1+4
      ldr r0,[r1,r2]                        ;r0 ← [r1+r2]

在第一条指令中,将寄存器r1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器r0中。
在第二条指令中,将寄存器r1的内容加上4形成操作数的有效地址,从而取得操作数存入寄存器r0中,然后,r1的内容自增4个字节。
在第三条指令中,以寄存器r1的内容作为操作数的有效地址,从而取得操作数存入寄存器r0中,然后,r1的内容自增4个字节。
在第四条指令中,将寄存器r1的内容加上寄存器R2的内容形成操作数的有效地址,从而取得操作数存入寄存器r0中。

06、多寄存器寻址
采用多寄存器寻址方式,一条指令可以完成多个寄存器值的传送。这种寻址方式可以用一条指令完成传送最多16个通用寄存器的值。以下指令:
    ldmia r0,{r1,R2,R3,R4}               
     ;r1←[r0]
     ;r2←[r0+4]
     ;r3←[r0+8]
     ;r4←[r0+12]

该指令的后缀ia表示在每次执行完加载/存储操作后,r0按字长度增加,因此,指令可将连续存储单元的值传送到r1~r4。

07、堆栈寻址
    堆栈是一种数据结构,按先进后出(First In Last Out,FILO)的方式工作,使用一个称作堆栈指针的专用寄存器指示当前的操作位置,堆栈指针总是指向栈顶。当堆栈指针指向最后压入堆栈的数据时,称为满堆栈(Full Stack),而当堆栈指针指向下一个将要放入数据的空位置时,称为空堆栈(Empty Stack)。同时,根据堆栈的生成方式,又可以分为递增堆栈(Ascending  Stack)和递减堆栈(Decending Stack),当堆栈由低地址向高地址生成时,称为递增堆栈,当堆栈由高地址向低地址生成时,称为递减堆栈。这样就有四种类型的堆栈工作方式,ARM微处理器支持这四种类型的堆栈工作方式,即:

满递增堆栈:堆栈指针指向最后压入的数据,且由低地址向高地址生成。例如:ldmfa和stmfa等。
满递减堆栈:堆栈指针指向最后压入的数据,且由高地址向低地址生成。例如:ldmfd和stmfd等。
空递增堆栈:堆栈指针指向下一个将要放入数据的空位置,且由低地址向高地址生成。例如:ldmea和stmea等。
空递减堆栈:堆栈指针指向下一个将要放入数据的空位置,且由高地址向低地址生成。例如:ldmed和stmed等。

堆栈寻址经常用在函数的首尾,在函数首部用于保存连接寄存器(函数返回地址)、r4以上寄存器等,在函数尾部恢复寄存器数据并将返回地址写入PC。

08、相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以b开头的跳转指令(包括函数调用指令bl)都是使用这类寻址方式。

    mov     r0    ,    # 100
      cmp     r0    ,    # 100
      beq     LabelTest        ;相等时跳转到标号LabelTest位置处继续执行。

    ShowHex

LabelTest

    ShowDec

    ★注意ARM汇编的标号的特点。无论是标号还是函数名都必须在一行的顶格位置书写,末尾没有冒号。我们使用C的标识符定义不会编译错误。ARM的标号可以使用两条"|"在两边限定比如:|LabelText|是正确的。

当我们写子函数或者分支程序时经常用到相对寻址,频繁的使用甚至使我们忽略了对他的认识。但是这个寻址方式在编写加密程序时非常重要!正因为有了这种寻址方式,我们在编写加密程序时很多重定位工作变得非常轻松。在后面的高级专题再介绍。

8种寻址方式都很重要,不过多寄存器寻址和堆栈寻址可能刚开始难理解一些,可以先放放,把其他寻址方式掌握好。这篇介绍不可能包含所有的语法现象,只介绍最基础最重要的语法。大家可以参考本文和实例程序对ARM寻址方式有一个初步了解,然后对比提供的电子档了解其他语法现象。要想掌握好需要多写程序练习,开始最好写非常简单的程序,只要1-2行就够了。慢慢积累就会逐步提高起来!

    祝大家学习顺利!

--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年03月17日 19:30:00

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (32)
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
2
前面准备了一段时间,趁着Xarm发布一周年的时候,开始发布这个系列。

希望大家多提宝贵意见!
2009-3-17 22:29
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
我顶,但不晓得说什么,就顶一下了
2009-3-17 22:58
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
我坐板凳,支持一下
2009-3-17 23:17
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
5
呵呵,顶哈
2009-3-18 12:59
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
6
stmfd    sp!  ,  { lr }

       ldmfd    sp!   ,  { pc }

                 ltorg

是什么啊?
2009-3-18 13:04
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
7
这个问题问得很好!
语法现象不可能一次全部解释,欢迎大家对不了解的语法现象提出问题,一起讨论!

这几条是一个函数的常用框架:
stmfd    sp!  ,  { lr }     ;把lr寄存器中的函数返回地址保存到堆栈中。以便函数可以正确返回。
ldmfd    sp!   ,  { pc }   ;将堆栈中保存的返回地址,写入PC寄存器中从函数中返回主程序。
                        ;上面这两条是典型的堆栈寻址方式的指令。

ltorg                             ;是一条伪指令,告诉构建系统函数中使用的内存地址构建时后放在程序这个位置

因为我们经常会使用 ldr   r1 , = dwNum这样的指令将内存地址装载到寄存器中。由于ARM指令机器码编码规则的限制,内存地址是保存在程序中的,并且位置不能离上面那条指令的位置太远。所以使用ltorg指令告诉系统把内存地址放在这个位置。因此ltorg伪指令必须放在程序中肯定不能执行的位置上,否则程序运行时会误将内存地址当作机器码执行出现错误。放置位置可以是:函数返回指令之后或者无条件跳转指令之后。

有个初步了解即可,如果想深入分析需要看一下ARM指令机器码的编码规则,就能理解充分了。
2009-3-18 15:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
真不错,学习
2009-3-18 16:41
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
9
AREA     |.drectve| , DRECTVE          ;
                      dcb             "-defaultlib:corelibc.lib "
                      dcb         "-defaultlib:coredll.lib  "
                      dcb         "-defaultlib:BFLib.lib "
                      dcb         "-defaultlib:commdlg.lib "
                      dcb         "-defaultlib:commctrl.lib "
                      dcb         "-defaultlib:crypt32.lib "
                      dcb         "-defaultlib:cxport.lib "
                      dcb         "-defaultlib:iphlpapi.lib "
                      dcb         "-defaultlib:mmtimer.lib "
                      dcb         "-defaultlib:msmqrt.lib "
                      dcb         "-defaultlib:ndis.lib "
                      dcb         "-defaultlib:ole32.lib "
                      dcb         "-defaultlib:oleaut32.lib "
                      dcb         "-defaultlib:secur32.lib "
                      dcb         "-defaultlib:urlmon.lib "
                      dcb         "-defaultlib:wininet.lib "
                      dcb         "-defaultlib:winsock.lib "
                      dcb         "-defaultlib:ws2.lib "

是引入lib库文件吧,如果在.S文件中引入的话,语法是什么啊?
2009-3-18 21:08
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
10
在MASM32中可以使用includelib指令引入lib文件。但是前期研究Xarm语法时尝试使用这样的指令没有成功。

目前设计的这个语法,会将你看到的这些信息保存到OBJ文件中,当使用link程序连接时,会根据信息查找程序中使用的API。这样做OBJ文件会大一些,但是不影响最终的EXE文件大小。并且一个额外的好处是不必在每次写程序时手动使用includelib引入lib文件了。
2009-3-18 21:35
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
11
那如果在.S源代码中引入该用什么语法呢?就是在不用inc的情况下该怎么引入呢?
2009-3-18 21:37
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
12
macro
                  call      $FunName

                  IMPORT   $FunName
                  bl   $FunName

                  mend

还有这些macro   mend是语法转译么?如果不引用这个inc在.S中应该怎么写呢?
2009-3-18 21:38
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
13
使用上述语法和使用includelib语法的效果是一样的。

请注意一个问题:
本质上说:我们并不是把lib引入到s代码中!lib库是在连接的时候由link程序使用的。

按你所说的那种语法我还没有找到,不知道微软的ARM编译连接系统是否支持。如果有兴趣你可以研究一下。
2009-3-18 21:44
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
14
这是ARM汇编宏定义的写法。

如果不写在INC文件写在S文件里面也可以,不过显然应该把程序中常用的宏定义写在独立的文件里面包含进程序更清晰一些。
2009-3-18 21:47
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
15
嘻嘻,明白了。太感谢加百力大哥了。
2009-3-18 21:49
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
16
macro

mend

这样的宏定义语法来自ARM公司标准的ARM汇编语法。
推荐你看看置顶的帖子里面标准的ARM汇编语法虽然和微软的有一些不同,但大体框架相似的。对于加深了解有帮助。你应该不是那么急吧?呵呵。
2009-3-18 21:53
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
17
不必客气,大家相互讨论进步更大!

Xarm的语法是ARM标准语法+反汇编的结果,开始也是希望仿照MASM32语法便于大家使用,有的难关不好过了就自己创造了一点。

如果后面能够找到更好的解决方法更好!

比如这个 call 宏定义。就是为了方便大家引入API函数。

例如在程序中: call MessageBoxW

实际上先是: import MessageBoxW    ;  这句告诉编译系统这个标号是从外部引入的。
   然后是:  bl        MessageBoxW    ;  这句是调用函数了。

因为在编译时又引入了一组你前面列出的lib库。

在连接的时候,连接器就可以找到这些API函数在lib中提供的信息了。
2009-3-18 22:02
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
18
看kuang110学的很急切又特别仔细,不知道是不是公司有这方面的需要?
2009-3-18 22:06
0
雪    币: 2604
活跃值: (64)
能力值: (RANK:510 )
在线值:
发帖
回帖
粉丝
19
补充一点语法研究的路线供大家参考:

我前面搞Xarm的时候是首先学习ARM公司标准的ARM汇编语法。然后一点点在S文件中写入,尝试用微软的编译连接系统构建。如果构建成功则语法可用。否则就得另想办法了。不是所有的标准ARM语法都支持。

比如:标准ARM中用entry标识程序的入口点,微软的编译器就不支持。主函数从WinMain开始而且还必须export。
2009-3-18 22:19
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
20
EXTERN     MessageBoxW
       bl        MessageBoxW
也可以。
2009-3-18 22:42
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
21
GLOBAL           WinMain  

也行。

大哥介绍一点反汇编思路哈。
2009-3-18 22:47
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
22
不是公司需要啦,只是爱好。
呵呵,刚转行到计算机,目前还在学习。经济危机不好找工作啊。
目前在培训COBOL。
2009-3-18 22:50
0
雪    币: 89
活跃值: (185)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
23
ldmia r0!,{r1,R2,R3,R4}
;大哥貌似少写了一个!号
stmia r0!,{r1,R2,R3,R4}
;貌似有人说这是第九中寻址,叫块拷贝寻址。感觉和多寄存器差不多。
2009-3-18 23:37
0
雪    币: 214
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
24
没想到这个子论坛做得这么大了, 先来个本人在本区的第一回帖, 下次有时间把自己最近的心得发上来.
2009-3-18 23:57
0
雪    币: 214
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
!的功能是:
在将r1 - r4寄存器存放的内容按"ia"的方式存放在r0指示的内存空间以后, 修改r0的值, 使其改变4 * 4 大小.

如果没有!, 则只是存放r1 - r4, r0的值不会变.
2009-3-19 00:02
0
游客
登录 | 注册 方可回帖
返回
//