main函数的代码,内容如下:
#include <stdio.h> int main(int argc, char* argv[]){ printf("Hello\n"); return 0; }其原生程序代码如下:
1 EXPORT main 2 main 3 var_C= -0xC 4 var_8= -8 5 STMFD SP!, {r11, LR} 6 ADD R11, SP, #4 7 SUB SP, SP, #8 8 STR R0, [R11,#var_8] 9 STR R1, [R11,#var_c] 10 LDR R3, =(aHelloArm - 0x8300) 11 ADD R3, PC, R3 ; "Hello" 12 MOV R0, R3 ; s 13 BL puts 14 MOV R3, #0 15 MOV R0, R3 16 STB SP, R11, #4 17 LDMFD SP!, {R11, PC}
由一条条ARM汇编指令所组成的汇编代码,我们将其称为原生程序的反汇编代码。
逆向原生程序就是通过阅读这些汇编代码来了解原生程序的功能及流程。
第1行的"EXPORT main"表明这个main函数式被程序导出的。
第2行的main为函数的名称。(JDA Pro能自动识别原生程序中所有的函数及其名称。)
第3行是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为数据处理指令。
它用于寄存器间的数据传送,如"MOV R0,R3"表示把R3寄存器的值赋给R0寄存器。
第13行的BL为带链接的跳转指令。
完成类似其它编程语言中子程序调用的功能,如"BL puts"就是调用puts函数。
puts为标准输入输出函数中printf的实现,其作用是向标准输出设备输出指定的内容,本实例中输出的内容为"Hello"字符串。