【文章标题】: 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 探索篇!
上传的附件: