发布于 

Android反编译步骤

Step

  1. 使用apkTool反编译apk文件;

  2. 分析apk,查看smail指令,分析smali指令,修改smali;

  3. 重新签名并且打包,android源码包包含签名工具;

  4. adb安装测试;

Android虚拟机(硬知识)

名称:Android Dalvik

作者:丹·伯恩斯坦(Dan Bornstein)

名称来源:他的祖先曾经居住过的Dalvki的小渔村

特点:

  1. 体积小,占用内存空间小;

  2. 专有的Dex可执行文件格式,体积小,速度快;

  3. 常量池采用32位索引值,寻址类方法名,字段名、常量更快;

  4. 寄存器架构,有一套完整的指令系统;

  5. 提供了对象生命周期功能、堆栈管理、线程管理、安全和异常管理以及垃圾回收等重要功能;

  6. 每个进程对应着一个Dalvik虚拟机实例;

Dalvik虚拟机和Java虚拟机区别

  1. Java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码;

  2. dalvik字节码是由java字节码转换而来,并被打包到一个dex(dalvik executable)可执行文件中。

  3. java基于栈架构,读写频繁,读写过程中 需要更多的指令分派和内存访问,导致CPU时间耗费,对于手机设备资源优先,开销太大;

  4. Dalvik基于寄存器架构。数据访问通过寄存器相互传递,速度更快;

Dalvik汇编指令和Arm汇编指令

ARM常用指令

https://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops

.file 指定了源文件名,实例 hello.s 是从hello.c 编译得来的,手写汇编代码时可以忽略它;

.align:指定代码对齐方式,后面跟的数值是2的次数方。如 ".align 4"表示 2^4 = 16字节对齐;

.ascii:声明字符串;

.global:声明全局符号.全局符号是指在本程序外可以访问的符号。

.type:指定符号的类型。“.type main,%function”表示main符号为函数;

.word:用来存放地址值。“.word .LCO-(.LPICO+8)”存放的是一个与地址无关的偏移量;

.size: 设置指定符号的大小.“.size main,.-main"中的点”."表示当前地址,减去main符号的地址即为整个main函数的大小;

.ident 编译器标识,无实际用途,生成可执行程序后他得值被放置到".comment"段中

Arm处理器寻址方式

ARM采用精简指令集!

ARM九种寻址,比x86处理器多两种

立即寻址

立即数寻址只能用于源操作数字段,不能用于目的操作数字段

寄存器寻址

寄存器寻之中,操作数的值存在寄存器中,指令执行时直接从寄存器中取值进行操作,例如:

mov R0,R1

指令执行后R0 = R1

寄存器移位寻址

寄存器移位寻址和寄存器寻址类似,在操作前需要对源寄存器操作数进行移位操作

LSL:逻辑左移,移位后寄存器空出的低位补0;

LSR:逻辑右移,移位后寄存器空出的高位补0;

ASR:算术右移,移位后符号位保持不变,如果源操作数为正数,则移位后空出的高位补0,否则补1。

ROR:循环右移,移位后移位的低位填入移位空出的高位;

RRX:带扩展的循环右移,操作数右移一位,移位空出的高位用C标志的值填充

例如:

MOV R0,R1,LSL #2

指令的功能是将R1寄存器左移2位,即"R1<<2"后赋值给R0寄存器,指令执行后 R0 = R1*4

寄存器间接寻址

寄存器间接寻址中地址码给出的寄存器是操作数的地址指针,所需的操作数保存在寄存器指定地址的存储单元中,例如

LDR R0,[R1]

指令的功能是将R1寄存器的数值作为地址,取出此地址中的值赋值给R0寄存器;

基址寻址

基址寻址是将地址码给出的基址寄存器与偏移量相加,形成操作数的有效地址,所需的操作数保存在有效地址所指向的存储单元。基址寻址多余与查表、数组访问等操作。例如:

LDR R0,[R1,#-4]

指令的功能是将R1寄存器的数值减4作为地址,去除此地址的值赋给R0寄存器;

多寄存器寻址

多寄存器寻址一条指令最多可以完成16个通用寄存器值的传送,例如:

LDMIA R0,[R1,R2,R3,R4]

LDM 是数据加载指令,指令的后缀IA表示每次执行完加载操作后R0寄存器的值自增1个字,ARM指令集中,字表示的是一个32位的数值。这条指令执行后,R1=[R0],R2=[R0+#4],R3=[R0+#8],R4=[R0+#12]

堆栈寻址

堆栈寻址是ARM处理器特有的一种寻址方式,堆栈寻址需要使用特定的指令来完成。堆栈寻址的指令有LDMFA/STMFA、LDMEA/STMEA、LDMFD/STMFD、LDMED/STMED。

LDM和STM作为指令前缀,表示多寄存器寻址,即一次可以传送多个寄存器值。FA,EA,FD,ED

为指令后缀;堆栈寻址举例:

STMFD SP!,{R1-R7,LR} @将R1~R7,LR 入栈。多余与保存子程序“现场”
LDMFD SP!,{R1-R7.LR} @将数据出栈,放入R1~R7,LR寄存器。多用于恢复子程序"现场"

块拷贝寻址

快拷贝寻址可实现连续地址数据从存储器的某一位置拷贝到另一位置。快拷贝寻址的指令有LDMIA/STMIA、LDMDA/STMDA、LDMIB/STMIB、LDMDB/STMDB

LDM 和 STM 为指令前缀;

LDMTA R0!,{R1-R3} @从R0寄存器指向的存储单元中读取3个字到R1~R3寄存器    
STMIA R0!,{R1~R3} @存储R1~R3寄存器的内容到R0寄存器指向的存储单元

相对寻址

相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加得到操作数的有效地址,例如:

BL NEXT 
     ......
NEXT:
         ......

BL NEXT 是跳到NEXT标号处执行。这里的BL采用的就是相对寻址,标号NEXT就是偏移量;

ARM 与 Thumb 指令集

Thumb可以理解为ARM指令集的一个子集,内核armv7-a的处理器为蓝本进行讲解

指令格式

ARM指令的基本格式如下

<opcode>{<cond>}{S}{.W|.N}<Rd>,{Rn}{,<operand2>}
  • opcode为指令助记符。如ADD,MOV等。

  • cond 为执行条件。

指令条件码列表

程序状态寄存器的条件标志位

N  运算结果的b31位值。对于有符号二进制补码,结果为负数时N=1,结果为正数或零时N=0;

Z  指令结果为0时Z=1,否则Z=0;

C  使用加法运算(包括CMN指令),b31位产生进位时C=1,否则C=0。使用减法运算(包括CMP),b31位产生借位时C=0,否则C=1。对于结合移位操作的非加法/减法指令,C为b31位最后的移出值,其它指令C通常不变;

V  使用加法/减法运算,当发生有符号溢出时V=1,否则V=0,其它指令V通常不变。

跳转指令

跳转指令称为分支指令,改变指令序列的执行流程。Arm有两种方式可以实现程序跳转,一种是使用跳转指令直接跳转,另一种是给PC寄存器直接赋值实现跳转,跳转如下4条:

1,B 跳转指令

B{cond}labe

B指令属于ARM指令集,是最简单的分支指令,当执行B指令时,如果条件cond满足,Arm处理器将立即跳转到label指定的地址处继续执行。例如”BNE LABEL“表示条件码 Z = 0 时跳转到LABEL处执行。

2.BL带链接的跳转指令

BL{cond} label

当执行BL执行时,如果条件cond满足,会首先将当前指令的下一条指令的地址拷贝到R14(LR)寄存器中,然后跳转到label指定的地址处继续执行。这条指令通常用于调用子程序,在子程序的尾部,可以通过”MOV PC,LR“返回到主程序中。

3.BX带状态切换的跳转指令

BX{cond} RM

当执行BX指令时,如果条件cond满足,则处理器会判断Rm的位[0]是否为1,如果为1则跳转时自动将CPSR寄存器的标志T置位,并将目标地址处的代码解释为Thumb代码来执行,则处理器会切换至Thumb状态;反之,若Rm的位[0]为0,则跳转时自动将CPSR寄存器的标志T复位,并将目标地址处的代码解释为ARM代码来执行,则处理器会切换到ARM状态,例如下面的代码

.code 32
 ARN R0,Thumbcde + 1
 BX R0 @ 跳转了thumbcode处执行,则将处理器切换为Thumb模式

thumbcode:
.code 16

4.BLX带链接和状态切换的跳转指令

BLX{cond} Rm

BLX指令集合了BL与BX的功能,当条件满足时,除了设置链接寄存器,还根据Rm位[0]的值来切换处理器状态