Android逆向手册

什么是APK?

APK是安卓手机的一个安装包,实质是一个zip压缩包

解压后可以看到内容目录

组成结构

  • lib #存放so库文件
  • META-INF #存放工程属性文件
  • res #资源目录
  • AndroidManifest.xml #Android工程的基础配置属性文件
  • classes.dex #Dalvik VM 可执行文件
  • resources.arsc #res目录的索引文件

asset资源目录和res的区别

res目录下的资源文件在编译时会自动生成索引文件(R.java)

在java代码中用R.xxx.yyy来引用;

asset目录下的资源文件不需要生成索引

在java代码种使用AssetManager访问;


一般,java开发的Android工程资源文件都会放在res目录下 (音频视频放在raw或asset)

使用C++游戏引擎的资源文件均放在asset目录下

什么是Dalvik字节码

Dalvik是Google为Android操作系统设计的一个虚拟机,经过深度的优化。

它和java虚拟机JVM是两回事。

Dalvik VM JVM
基于寄存器 基于栈
文件执行格式dex(davlvik executable) java字节码
速度更快、占用空间小 与之相反

通过Dalvik不能直接看到原逻辑代码,需要借助Apktool

修改APK需要操作 .smali文件

Smali简介

当对APK文件反编译后,会生成此类文件。

smali,Baksmali分别指,dex格式文件的汇编器、反汇编器

有两种类型:原始类型 和 引用类型

原始类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
B——byte		J——long	S——short

C——char V——void

D——double Z——boolean

F——float [xx——array

I——int Lxx——object

举例:

1.main()V == void main()

2.main(II)Z == boolean main(int,int)

3.main(Z[I[ILjava/lang/String;J)Ljava/lang/String; == String main(boolean,int[],int[],String,long)

Smali基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.field private isFlag:z	#定义变量

.method #方法

.parameter #方法参数

.prologue #方法开始

.line 123 #此方法位于第123行

invoke-direct #调用函数

invoke-static #调用静态函数

invoke-super #调用父函数

return-void #函数返回void

.end method #函数结束

const/high16 v0,0x7fo3 #把0x7fo3赋值给v0

new-instance #创建实例

iput-object #对象赋值

iget-object #调用对象
条件分支跳转:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if-eq A,B,:wintry			#如果A=B则跳转到wintry

if-ne -------------- #不等则条

if-It -------------- #A<B跳

if-ge -------------- #A>=B跳

if-gt --------------- #A>B跳

if-le --------------- #A<=B跳

------分割线----------

if-eqz A,:wintry #A=0跳

if-nez ------------ #不等于0跳

if-Itz #A<0

if-gez #A>=0

if-gtz #A>0

if-lez #A<=0

深入Smali文件

smali中的包信息
  • .class public Lcom/aaa;
  • .super Lcom/bbb;
  • .source “ccc.java”

解释:

  • 这是一个由ccc.java编译得到的smali文件
  • 它是com.aaa这个package下的一个类
  • 继承自com.bbb这个类

寄存器相关知识

在smali里的所有操作都必须经过寄存器来进行。

本地寄存器用v开头数字结尾的符号来表示,如v0、v1

参数寄存器用p开头数字结尾的符号来表示,如p0、p1

p0不一定是函数中的第一个参数

在非static函数中,p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数

在static函数中p0才对应第一个参数(因为Java的static方法中没有this方法)

寄存器简单实例分析

1
2
3
const/4 v0, 0x1

iput-boolean v0, p0, Lcom/aaa;->IsRegistered:Z

分析上面的两句smali代码

首先它使用了v0本地寄存器,并把值0x1存到v0中

然后第二句,用iput-boolean这个指令把v0中的值存放到com.aaa.IsRegistered这个成员变量中。
即相当于:this.IsRegistered= true;(上面说过,在非static函数中p0代表的是“this”,在这里就是com.aaa实例)

smali中的成员变量

成员变量格式是:

1
.field public/private [static] [final] varName:<类型>

对于不同的成员变量也有不同的指令。

一般来说,获取的指令有:

1
iget、sget、iget-boolean、sget-boolean、iget-object、sget-object

操作的指令有:

1
iput、sput、iput-boolean、sput-boolean、iput-object、sput-object

没有“-object”后缀的表示操作的成员变量对象是基本数据类型

带“-object”表示操作的成员变量是对象类型

特别地,boolean类型则使用带“-boolean”的指令操作。

Smali成员变量指令简析

1
sget-object v0, Lcom/aaa;->ID:Ljava/lang/String;

sget-object就是用来获取变量值并保存到紧接着的参数的寄存器中

本例中,它获取ID这个String类型的成员变量并放到v0这个寄存器中。

注意:前面需要该变量所属的类的类型,后面需要加一个冒号和该成员变量的类型,中间是“->”表示所属关系


1
iget-object v0, p0, Lcom/aaa;->view:Lcom/aaa/view;

可以看到iget-object指令比sget-object多了一个参数,就是该变量所在类的实例,在这里就是p0即“this”。

获取array的话我们用aget和aget-object,指令使用和上述一致


put指令的使用和get指令是统一的如下:

1
2
3
const/4 v3, 0x0

sput-object v3, Lcom/aaa;->timer:Lcom/aaa/timer;

相当于:this.timer= null;

注意,这里因为是赋值object 所以是null


1
2
3
4
5
s.local v0, args:Landroid/os/Message;

const/4 v1, 0x12

iput v1, v0, Landroid/os/Message;->what:I

相当于:args.what = 18;(args是Message的实例)

Smali中函数的调用

smali中的函数和成员变量一样也分为两种类型,分别为direct和virtual之分。

那么direct method和virtual method有什么区别呢?

简单来说,direct method就是private函数,其余的public和protected函数都属于virtual method。

所以在调用函数时,有invoke-direct,invoke-virtual,另外还有invoke-static、invoke-super以及invoke-interface等几种不同的指令。

当然其实还有invoke-XXX/range 指令的,这是参数多于4个的时候调用的指令,比较少见,了解下即可。


1.invoke-static:用于调用static函数的

例如:

invoke-static {}, Lcom/aaa;->CheckSignature()Z

这里注意到invoke-static后面有一对大括号“{}”,其实是调用该方法的实例+参数列表

由于这个方法既不需参数也是static的,所以{}内为空,再看一个:

1
2
3
const-string v0, "NDKLIB"  

invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

这个是调用static void System.loadLibrary(String)来加载NDK编译的so库用的方法

同样也是这里v0就是参数”NDKLIB”了。

2.invoke-super:

调用父类方法用的指令,一般用于调用onCreate、onDestroy等方法。

3.invoke-direct:

调用private函数:

1
invoke-direct {p0}, Landroid/app/TabActivity;-><init>()V

这里init()就是定义在TabActivity中的一个private函数

4.invoke-virtual:

用于调用protected或public函数,同样注意修改smali时不要错用invoke-direct或invoke-static:

1
2
3
sget-object v0, Lcom/dddd;->bbb:Lcom/ccc;

invoke-virtual {v0, v1}, Lcom/ccc;->Messages(Ljava/lang/Object;)V

v0是bbb:Lcom/ccc

v1是传递给Messages方法的Ljava/lang/Object参数

5.invoke-xxxxx/range:

当方法的参数多于5个时(含5个),不能直接使用以上的指令

而是在后面加上“/range”,range表示范围,使用方法也有所不同:

1
invoke-direct/range {v0 .. v5}, Lcmb/pb/ui/PBContainerActivity;h(ILjava/lang/CharSequence;Ljava/lang/String;Landroid/content/Intent;I)Z

需要传递v0到v5一共6个参数,这时候大括号内的参数采用省略形式,且需要连续。

Smali中函数返回的结果的操作

在Java代码中调用函数和返回函数结果可以用一条语句完成,而在Smali里则需要分开来完成,

在使用上述指令后,如果调用的函数返回非void,那么还需要用到move-result(返回基本数据类型)

和move-result-object(返回对象)指令:

1
2
3
4
5
const-string v0, "Eric"

invoke-static {v0}, Lcmb/pbi;->t(Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

v2保存的就是调用t方法返回的String字符串

Smali中函数实体分析–if函数分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.method private ifRegistered()Z

.locals 2 //在这个函数中本地寄存器的个数

.prologue

const/4 v0, 0x1 // v0赋值为1

.local v0, tempFlag:Z

if-eqz v0, :cond_0 // 判断v0是否等于0,等于0则跳到cond_0执行

const/4 v1, 0x1 // 符合条件分支

:goto_0 //标签

return v1 //返回v1的值

:cond_0 //标签

const/4 v1, 0x0 // cond_0分支

goto :goto_0 //跳到goto_0执行 即返回v1的值 这里可以改成return v1 也是一样的

.end method

Smali中函数实体分析–for函数分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const/4 v0, 0x0   //vo =0;

.local v0, i:I

:goto_0

if-lt v0, v3, :cond_0 // v0小于v3 则跳到cond_0并执行分支 :cond_0

return-void

:cond_0 // 标签

iget-object v1, p0, Lcom/aaa/MainActivity;->listStrings:Ljava/util/List; // 引用对象

const-string v2, "Eric"

invoke-interface {v1, v2}, Ljava/util/List;->add(Ljava/lang/Object;)Z // List是接口, 执行接口方法add

add-int/lit8 v0, v0, 0x1    // 将第二个v0寄存器中的值,加上0x1的值放入第一个寄存器中, 实现自增长

goto :goto_0 // 回去:goto_0标签

Smali实战分析

https://www.52pojie.cn/thread-397987-1-1.html

吾爱破解教程索引

会飞的丑小鸭-叫我兄弟学逆向教程

https://www.52pojie.cn/thread-742703-1-1.html

BubblePig Android逆向笔记

Android逆向-java代码基础(1)

Android逆向-java代码基础(2)

Android逆向-java代码基础(3)

Android逆向-java代码基础(4)

Android逆向-java代码基础(5)

Android逆向-java代码基础(6)

Android逆向-java代码基础(7)

Android逆向-java代码基础(8)


Android逆向-Android基础逆向(1)

1.Android Helloworld编写

2.APK文件内容了解

3.反编译工具进行尝试使用

Android逆向-Android基础逆向(2)

1.zip文件格式分析

2.zip加密研究

3.apk伪加密研究

Android逆向-Android基础逆向(2-2)

1.Androidmanifest.xml二进制格式分析

2.python 分析二进制文件工具制作

Android逆向-Android基础逆向(2-3)

1.APK打包流程

2.反编译流程

Android逆向-Android逆向基础(3)

1.签名基础知识

2.Android 签名流程

3.Android签名方式

4.Android签名认证

5.Android java层签名认证实现

Android逆向-Android基础逆向(4)

(1)Android 布局创建

(2)Android布局反编译

(3)反编译改变布局

(4)小工具制作

Android逆向-Android基础逆向(4-2)

1.两个按钮时smali 逻辑

2.逆向增加按钮逻辑功能

3.制作一个辅助的python工具

Android逆向-Android基础逆向(5)

1.Activity之间的跳转

2.Androidmanifest.xml 属性说明

3.跳转smali分析

4.实战

Android逆向-Android基础逆向(6)

1.如何写一个登录界面?

2.逆向分析登录逻辑

3.如何暴力绕过一个登录界面?

4.如何巧妙登录

5..如何加广告

6.如何去广告

7.实例分析

Android逆向-Android基础逆向(6-2)

广告通杀

Android逆向-Android基础逆向 7(内购干货集合)

1.内购基础
2.咪咕内购实例分析
3.内购方法总结

Android逆向-Android基础逆向 8

1.简单的NDK编程。

2.demo反编译分析。

3.smali层和Native交互。

4.IDA 简单应用。

5.实例分析。

Android逆向-Android基础逆向 9

1.ARM基础知识

2.so文件基础知识

Android逆向-Android基础逆向10

1.if逻辑NDK编程

2.if逻辑ARM分析

3.switch逻辑NDK编程

4.switch逻辑ARM分析

5.循环逻辑NDK编程

6.循环逻辑ARM分析

Android逆向-Android逆向基础 11(so demo分析)

1.金币修改demo

2.用户等级更改demo


Android逆向-python写一个.class分析辅助脚本

https://www.52pojie.cn/thread-687475-1-1.html

Android逆向进阶-ELF文件分析(2)

https://www.52pojie.cn/thread-705085-1-1.html

Android逆向进阶-ELF文件分析(3)

https://www.52pojie.cn/thread-705159-1-1.html