Android与Apk的文件结构

Android与Apk的文件结构在学习逆向很重要的一步。随便记录一下smali语法

1、Android

Android 是google公司针对手机开发的一个平台,并公布了其中大部分代码,其大部分应用程序都是用JAVA开发的。

系统结构

android的系统架构和其操作系统一样,采用了分层的架构。从架构图看,android分为四个层,从高层到低层分别是应用程序层、应用程序框架层、系统运行库层和linux核心层。

从而看出liunx是很重要的,蓝色的代表java程序,黄色的代码为运行JAVA程序而实现的虚拟机,绿色部分为C/C++语言编写的程序库,红色的代码内核(linux内核+drvier)。

1、应用程序

Android会同一系列核心应用程序包一起发布,应用程序基本都是使用JAVA语言编写的。

2、应用程序框架

开发人员也可以完全访问核心应用程序所使用的API 框架,该应用程序的架构设计简化了组件的重用。

3、系统运行库

(1)、程序库
Android 包含一些C/C++库,这些库能被Android系统中不同的组件使用。它们通过 Android 应用程序框架为开发者提供服务。其实Android并非讲所有的设备驱动都放在linux内核里面,而是实现在userspace空间,这么做的主要原因是GPL协议,Linux是遵循该 协议来发布的,也就意味着对 linux内核的任何修改,都必须发布其源代码。而现在这么做就可以避开而无需发布其源代码,毕竟它是用来赚钱的。
(2)Android 运行库
Android 包括了一个核心库,该核心库提供了JAVA编程语言核心库的大多数功能。每一个Android应用程序都在它自己的进程中运 行,都拥有一个独立的Dalvik虚拟 机实例。Dalvik被设计成一个设备可以同时高效地运行多个虚拟系统。 Dalvik虚拟机执行(.dex)的Dalvik可执行文件,该格式文件针对小内存使用做了优化。同时Dalvik虚拟机依赖于linux内核的一些功能,比如线程机制和底层内存管理机制。

4、Linux内核

Android 的核心系统服务依赖于 Linux 2.6 内核,如安全性,内存管理,进程管理, 网络协议栈和驱动模型。 Linux 内核也同时作为硬件和软件栈之间的抽象层。

2、Apk的文件结构

APK就是一个zip压缩包,解开这个APK包我们可以看到以下的结构:

1
2
3
4
5
6
META-INF:签名文件
res:资源文件
AndroidManifest.xml:android 配置文件
classes.dex:java代码编译后产生的类似字节码的文件(dalvik 字节码)
resources.arsc:配置文件
其他文件:诸如assets等,或者lib(native so 代码)等目录

apk的核心逻辑主要在 classes.dex(代码主要存在的文件),有些代码会在.so(动态链接库)文件 [最头痛的文件].

3、Smali语法

1、函数

函数的定义一般为:

Func-Name (Para-Type1Para-Type2Para-Type3…)Return-Type
注意参数与参数之间没有任何分隔符
1、check ()V —-> void check()
2、check (III)Z —-> boolean check(int,int,int)
3、check (Z[I[Ijava/lang/String)java/lang/String; —-> String check(boolean,int[],int[],String)

而函数又分为direct method和virtual method。direct method就是private函数,其余的public和protected函数都属于virtual method。

函数调用

格式:.method + 访问修饰符 + 方法名(参数表) + 返回值类型
.method // 方法的开始
.end method // 方法结束

在调用函数时,有invoke-direct,invoke-virtual,另外还有invoke-static、invoke-super以及invoke-interface等几种不同的指令。
对象 -> 方法名(参数类型)返回值
invoke-virtual 非静态方法调用
invoke-static 静态方法调用
invoke-direct 构造方法调用?
invoke-super 调用父类方法
invoke-interface 调用接口
(1)、invoke-static:顾名思义就是调用static函数的,因为是static函数,所以比起其他调用少一个参数,例如:

1
2
const-string v0, "fmodex"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

这个是调用static void System.loadLibrary(String)来加载NDK编译的so库用的方法,同样也是这里v0就是参数”fmodex”了。
(2)、invoke-virtual:用于调用protected或public函数

1
2
sget-object v0, Lcom/disney/WMW/WMWActivity;->shareHandler:Landroid/os/Handler;
invoke-virtual {v0, v3}, Landroid/os/Handler;->removeCallbacksAndMessages(Ljava/lang/Object;)V

v0是shareHandler:Landroid/os/Handler,v3是传递给removeCallbackAndMessage方法的Ljava/lang/Object参数

调用返回结果:用到move-result(返回基本数据类型)和move-result-object(返回对象)指令

smali中函数实体分析

1
2
3
4
5
6
7
8
9
10
.method protected onDestroy()V
.locals 0
.prologue
.line 277
invoke-super {p0}, Lcom/disney/common/BaseActivity;->onDestroy()V
.line 279
return-void
.end method

.local 0,这句话很重要,标明了你在这个函数中最少要用到的本地寄存器的个数。在这里,由于只需要调用一个父类的onDestroy()处理,所以只需要用到p0,所以使用到的本地寄存器数为0。如果不清楚这个规则,很容易在植入代码后忘记修改.local 的值,那么回编译后运行时将会得到一个VerifyError错误,而且极难发现问题所在。

2、语法

if与switch判断语句

if判断一共有12条指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
if-eq vA, VB, cond_** 如果vA等于vB则跳转到cond_**。相当于if (vA==vB)
if-ne vA, VB, cond_** 如果vA不等于vB则跳转到cond_**。相当于if (vA!=vB)
if-lt vA, VB, cond_** 如果vA小于vB则跳转到cond_**。相当于if (vA<vB)
if-le vA, VB, cond_** 如果vA小于等于vB则跳转到cond_**。相当于if (vA<=vB)
if-gt vA, VB, cond_** 如果vA大于vB则跳转到cond_**。相当于if (vA>vB)
if-ge vA, VB, cond_** 如果vA大于等于vB则跳转到cond_**。相当于if (vA>=vB)
if-eqz vA, :cond_** 如果vA等于0则跳转到:cond_** 相当于if (VA==0)
if-nez vA, :cond_** 如果vA不等于0则跳转到:cond_**相当于if (VA!=0)
if-ltz vA, :cond_** 如果vA小于0则跳转到:cond_**相当于if (VA<0)
if-lez vA, :cond_** 如果vA小于等于0则跳转到:cond_**相当于if (VA<=0)
if-gtz vA, :cond_** 如果vA大于0则跳转到:cond_**相当于if (VA>0)
if-gez vA, :cond_** 如果vA大于等于0则跳转到:cond_**相当于if (VA>=0)

Switch语句

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
整形顺序结构switch(int)
:pswitch_data_0
.packed-switch 0x0
:pswitch_0
:pswitch_1
.end packed-switch
整形非顺序结构switch(int)
:sswitch_data_0
.sparse-switch
0x0 -> :sswitch_0
0x1 -> :sswitch_1
0xa -> :sswitch_2
.end sparse-switch
switch(String)
:pswitch_data_0
.packed-switch 0x41
:pswitch_0
:pswitch_1
.end packed-switch
:pswitch_data_1
.packed-switch 0x0
:pswitch_2
:pswitch_3
.end packed-switch

循环语句

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
28
29
30
.method public statementFor()Z
.locals 4
.prologue
.line 17
const/4 v0, 0x0
.local v0, "a":I
:goto_0
const/16 v1, 0x64
if-ge v0, v1, :cond_0 // 结束循环条件
.line 18
const-string v1, "StateLoop"
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
const-string v3, ""
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v
invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
.line 17
add-int/lit8 v0, v0, 0x1
goto :goto_0 // 通过goto实现循环
.line 20
:cond_0
const/4 v1, 0x0
return v1
.end method
for/foreach循环,while/do-while 几种循环结构smali语法均通过goto实现,内部嵌入if-xxx 实现跳出循环。

try/catch语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# virtual methods
.method public statementTry()Z
.locals 4
.prologue
.line 14
const-wide/16 v2, 0x3e8
:try_start_0
invoke-static {v2, v3}, Ljava/lang/Thread;->sleep(J)V
:try_end_0
.catch Ljava/lang/InterruptedException; {:try_start_0 .. :try_end_0} :catch_0
.line 18
:goto_0
const/4 v1, 0x1
return v1
.line 15
:catch_0
move-exception v0
.line 16
.local v0, "e":Ljava/lang/InterruptedException;
invoke-virtual {v0}, Ljava/lang/InterruptedException;->printStackTrace()V
goto :goto_0
.end method

Android参考:
http://blog.csdn.net/spy19881201/article/details/5775484
samli参考:
http://blog.csdn.net/chenrunhua/article/details/41250613
https://www.cnblogs.com/zhen-android/p/7259434.html

Donate
-------------本文结束感谢您的阅读-------------