第6章 ARM 汇编基础与逆向
1、 系统Santoku IP:192.168.153.133
2、 理解ARM汇编
1、 逆向你的原生Hello ARM 介绍
有过java开发经验的读者一定会知道,java程序的安全多依赖于代码混淆技术,这种技术通过更改程序中的方法,字段名来增加静态分析的难度,从而达到防止程序被破解的目的。Andriod系统采用java作为其平台软件的基础开发语言,代码混淆技术也理所当然的成为保护程序的“基础”手段。但代码混淆技术也有很多弊端,不能满足对安全需求较高的行业及商业软件,andriod NDK的问世,使得开发人员可以通过C/C++代码来编写软件的核心功能代码,这些代码通过编译会生成基于特定处理器的可执行文件,对于使用ARM处理器的andriod手机来说,它最终会生成相应的ARM ELF可执行文件,安全分析人员通过ELF文件分析软件的核心功能入手。然而分析ARM ELF文件比想象中要困难的多,因为无论是静态分析还是动态调试ARM ELF代码都需要具备基本的ARM反汇编代码阅读能力。
2、 实例程序Hello,一个ARM原生程序。将其拖入IDA Pro内如下:
3、 分析Hello文件
text:000082E0 EXPORT main
.text:000082E0main ;CODE XREF: j_mainj
.text:000082E0var_C = -0xC
.text:000082E0var_8 = -8
.text:000082E0 STMFD SP!, {R11,LR}
.text:000082E4 ADD R11, SP, #4
.text:000082E8 SUB SP, SP, #8
.text:000082EC STR R0, [R11,#var_8]
.text:000082F0 STR R1, [R11,#var_C]
.text:000082F4 LDR R3, =(aHelloArm - 0x8300)
.text:000082F8 ADD R3, PC, R3 ; "Hello ARM!"
.text:000082FC MOV R0, R3 ; s
.text:00008300 BL puts
.text:00008304 MOV R3, #0
.text:00008308 MOV R0, R3
.text:0000830C SUB SP, R11, #4
.text:00008310 LDMFD SP!, {R11,PC}
.text:00008310 ;End of function main
第1行的“EXPORT main”表明这个main函数时被程序导出的。
第2行的main为函数的名称。IDA Pro能自动识别原生程序中所有的函数及其名称。
第3-4行是IDA Pro识别出栈变量。IDA Pro是通过函数中分配的栈空间来识别栈变量的,第7行的“SUB SP,SP,#8”指令来完成的,其中SUB为指令操作码,表示减法操作,SP为堆栈指针寄存器,这条指令的含义是将SP寄存器的值减去8后重新赋给SP寄存器,作用是在堆栈上分配8个字节的空间,也就是栈变量var_C与var_8的空间。
第5-17行是函数main指令部分。整段代码涉及到8条指令,下面简单地介绍一下它们的功能。
第5行的STMFD与第17行的LDMFD是堆栈寻址指令(也叫多寄存器寻址),STMFD指令用于把寄存器的值压入堆栈,在以上实例中是为了保护原始寄存器的值,LDMFD指令用于从堆栈中恢复寄存器的值,作用与STMFD恰恰相反。
第6行的ADD与第16行的SUB是操作码的算术指令。ADD为加法指令,“ADD R11,SP,#4”就是将SP寄存器的值加上4后赋给R11寄存器。SUB为减法指令,第16行的代码功能与第6行恰恰相反。
第8-9行的STR与第10行的LDR是存储器访问指令。存储器指的就是内存地址,通常也可以称为内存单元或存储单元,存储器的访问包含从存储器中读数据与写入数据到存储器中,ARM指令中将存储器使用一对中括号“[ ]”表示,STR是写存储器指令(也就是所说的存放到哪里),例如第8行的“STR R0,[R11,#var_8]”就是指R0存储器的值保存到栈变量var_8中,第9行的“STR R1,[R11,#var_C]”则将R1寄存器的值保存到栈变量var_C中。
第12行的MOV为数据处理指令,它用于寄存器间的数据传送,如“MOVR0,R3”表示把R3寄存器的值赋给R0寄存器。
第13行的BL为带链接的跳转指令。完成类似其他编程语言中子程序调用的功能,如
“BL puts”就是调用puts函数。Puts为标准输入输出函数中printf的实现,其作用是向标准输出设备输出指定的内容,本例输出内容为“Hello ARM!”字符串。
4、 原生程序的生成过程分为4个步骤
预处理 —> 编译 —> 汇编 —> 链接
Android NDK支持直接使用ARM汇编语言编写的以“.s”结尾的文件作为程序的源文件。
5、 ARM汇编语言的特点
1) ARM汇编语言是一门“低级”语言,它能够与系统底层打交道,可以直接访问底层硬件资源。
2) 其次,ARM汇编语言编写的程序运行速度快,占用内存少,缺点是编写代码难懂,也难以维护。
3) ARM汇编语言编写的程序几乎不需要其他代码转换就能直接执行,源码与反编译出来的代码基本相似。
4) ARM汇编语言基于模块化的面向过程的编程思想,其他语言实现的功能ARM汇编语言都能实现。
5) ARM汇编语言中特有的寄存器,寄存器是处理器特有的高速存贮部件,它们可用来暂存指令、数据和位址。
6) ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。
6、 ARM处理器支持七种运行模式它们分别为:(注意:在ARM番外篇已经提到这个)
1) 用户模式(USR): ARM处理器正常的程序执行状态。
2) 快速中断模式(FIQ):用于高速数据传输或通道处理。
3) 外部中断模式(IRQ):用于通用的中断处理。
4) 管理模式(SVC):操作系统使用的保护模式。
5) 数据访问终止模式(ABT):当数据或指令预取终止时进入该模式,可用于虚拟存储及存储保护。
6) 系统模式(SYS):运行具有特权的操作系统任务。
7) 未定义指令中止模式(UND):当未定义的指令执行时进入该模式。
(注意:这里是中止不是终止)
ARM处理器的运行模式可以通过软件改变,也可以通过外部中断或异常处理改变。在不同模式下,处理器使用的寄存器不尽相同,而且可供访问的资源也不一样。在7个模式中,除了用户模式外,其它六种模式均为“特权”模式,在“特权”模式下,处理器可以任意访问受保护的系统资源。这里只介绍用户模式。如下图:
7、 在用户模式下,处理器可以访问的寄存器为不分组寄存器R0~R7、分组寄存器R8~R14、程序计数器R15(PC)以及当前程序状态寄存器CPSR。
8、 ARM处理器有两种工作状态:ARM状态与Thumb状态。处理器可以在两种状态之间随意切换。当处理器处于ARM状态是,会执行32位字对齐的ARM指令,当处于Thumb状态时,执行的是16位对齐的Thumb指令。Thumb状态下对寄存器的命名与ARM有部分差异,它们的关系如下:
1) Thumb 状态下的R0~R7与ARM状态下的R0~R7相同。
2) Thumb状态下的CPSR与ARM状态下的CPSR相同。
3) Thumb状态下的FP对应于ARM状态下的R11。
4) Thumb状态下的IP对应于ARM状态下的R12。
5) Thumb状态下的SP对应于ARM状态下的R13。
6) Thumb状态下的LR对应于ARM状态下的R14。
7) Thumb状态下的PC对应于ARM状态下R15。
寄存器可以通俗的理解为存放东西的“储物柜”,并不具备其它的功能,代码能实现什么功能完全是由处理器的指令来决定的。
例子:如果要算一个加法运算,就要让处理器执行ADD(操作码)加法指令即可。
9、 ARM汇编语言程序结构
Android平台的ARM汇编是GNU ARM汇编格式,使用的汇编器为GAS,它有着一套自己的语法结构。官方在线手册地址:http://sourceware.org/binutils/docs/as/index.html