业无高卑志当坚,男儿有求安得闲?这篇文章主要讲述[Android Pro]静态分析Android程序——smali文件解析相关的知识,希望能为你提供帮助。
cp :
https://blog.csdn.net/hp910315/article/details/51823236
cp : http://www.jb51.net/softjc/119036.html
静态分析android程序的两种方法:
一、阅读反编译生成的Dalvik字节码。
1、使用文本编辑器阅读baksmali反编译生成的smali文件
(1)解压apk包
unzip xxx.apk
- 1
java -jar baksmali-2.0.3.jar classes.dex
- 1
二、阅读反编译生成的Java源码
(1) 使用 dex2jar 把classes.dex转换成jar
java -jar dex2jar classes.dex
- 1
本篇文章注意介绍第一种方式得到smali文件之后,对应smali文件进行分析。
无论是普通类、抽象类、接口类或者内部类,在反编译的代码中,它们都会以单独的smali文件存放。每个smali文件都由若干语句组成,所有的语句都遵循着一套语法规范。下面来具体介绍。
一、头信息— — 类的主体信息
在打开smali文件的时候,它的头三行描述了当前类的一些信息。
.class <
访问权限>
[关键修饰字] <
类名>
;
.super <
父类名>
;
.source <
源文件名>
- 1
- 2
- 3
//===================================================================
public class MainActivity extends AppCompatActivity {
// ......
}
//===================================================================
.class public Ltestdemo/hpp/cn/test/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"
//===================================================================
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
.super指定了当前类所继承的父类,后面指的就是这个父类的类名,L表示后面跟的字符串是一个类
.source指定了当前类的源文件名
注意:经过混淆的dex文件,反编译出来的smali代码可能没有源文件信息,因此source行的代码可能为空。
这三行就是类的主体部分了,另外一个类是由多个字段或者方法组成。
二、接口
如果一个类实现了一个接口,那么会在smali文件中使用.implements指令指出。
#interfaces
.implements <
接口名>
- 1
- 2
例如:
//===================================================================
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
// ......
}
//===================================================================
# interfaces
.implements Landroid/view/View$OnClickListener;
//===================================================================
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
Davlik字节码中,寄存器都是32位的,能够支持任何类型,64位类型(Long/Double)用2个寄存器表示;
Dalvik字节码有两种类型:原始类型;引用类型(包括对象和数组)
1、原始类型
V void (只能用于返回值类型)
Z boolean
B byte
S short
C char
I int
J long(64位)
F float
D double(64位)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Lpackage/name/ObjectName; 相当于java中的package.name.ObjectName;
L 表示这是一个对象类型
package/name 该对象所在的包
ObjectName 对象名称
; 标识对象名称的结束
3、数组类型
[I :表示一个整形的一维数组,相当于java的int[];
对于多维数组,只要增加[ 就行了,[[I = int[][]; 注:每一维最多255个;
【[Android Pro]静态分析Android程序——smali文件解析】对象数组的表示形式:
[Ljava/lang/String 表示一个String的对象数组;
4、寄存器与变量
android变量都是存放在寄存器中的,寄存器为32位,可以支持任何类型,其中long和double是64为的,需要使用两个寄存器保存。
寄存器采用v和p来命名,v表示本地寄存器,p表示参数寄存器。
例如:
//===================================================================
private void print(String string) {
Log.d(TAG, string);
}
//===================================================================
.method private print(Ljava/lang/String;
)V
.registers 3
.param p1, "string"# Ljava/lang/String;
.prologue
.line 29
const-string v0, "MainActivity"invoke-static {v0, p1}, Landroid/util/Log;
->
d(Ljava/lang/String;
Ljava/lang/String;
)I.line 30
return-void
.end method
//===================================================================
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
5、基本指令
smali字节码是类似于汇编的,如果你有汇编基础,理解起来是非常容易的。
move v0, v3 把v3寄存器的值移动到寄存器v0上
const-string v0, “ MainActivity” 把字符串” MainActivity” 赋值给v0寄存器
invoke-super调用父函数
return-void函数返回void
new-instance创建实例
iput-object对象赋值
iget-object调用对象
invoke-static调用静态函数
invoke-direct调用函数
例如:
//===================================================================
@Override
public void onClick(View view) {
String str = "Hello World!";
print(str);
}
//===================================================================
# virtual methods
# 参数类型为Landroid/view/View,返回类型为V
.method public onClick(Landroid/view/View;
)V
# 表示有三个寄存器
.registers 3
# 参数View类型的view变量对应的是寄存器p1
.param p1, "view"# Landroid/view/View;
.prologue
.line 24
#将"Hello World!"字符串放到寄存器v0中
const-string v0, "Hello World!".line 25
# 定义一个Ljava/lang/String类型的str变量对应本地寄存器v0
.local v0, "str":Ljava/lang/String;
# 调用该类的print方法,该方法的参数类型为Ljava/lang/String,返回值为V
# 调用print方法传入的参数为{p0, v0},及print(p0, v0),p0为this,v0为"Hello World!"字符串
invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity;
->
print(Ljava/lang/String;
)V.line 26
return-void
.end method
//===================================================================
- 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
- 31
if判断一共有12条指令:
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
常用的循环结构有:迭代器循环,for循环,do while循环。
8、switch分支语句
9、try/catch语句
四、字段
smali文件中,字段的声明使用.field指令,字段分为静态字段和实例字段。
1、静态字段
#static fields
.field <
访问权限>
static [修饰关键字] <
字段名>
:<
字段类型>
可以看到,baksmali在生成smali文件时,会在静态字段声明的起始处添加注释” static fields” ,注释是以#开头。
访问权限包括:private、protected、public(三者之一)
修饰关键字为字段的其他属性,例如,final
字段名和类型就不用解释了
例如:
//===================================================================
privatestatic final String TAG = "MainActivity";
//===================================================================
# static fields
.field private static final TAG:Ljava/lang/String;
= "MainActivity"
//===================================================================
2、实例字段
相比于静态自动就少了一个static的静态声明而已,其他都一样。
#instance fields
.field <
访问权限>
[修饰关键字] <
字段名>
:<
字段类型>
例如:
//===================================================================
private Button mButton;
//===================================================================
# instance fields
.field private mButton:Landroid/widget/Button;
//===================================================================
五、方法
smali的方法声明使用的.method指令,方法分为直接方法和虚方法两种。
函数调用:
invoke-direct 调用private函数
invoke-super 调用父类函数
invoke-static 调用静态函数
invoke-virtual 用于调用protected或public函数(相当于C++的虚函数,java的重载函数,只有protect和public能够重载)
还有一种比较特殊的:invoke-xxxxx/range:参数多于5个的时候,要加/rang
例子:
invoke-virtual {v4, v1}, Ljava/lang/String; -> equals(Ljava/lang/Object; )Z
v4是this,代表 Ljava/lang/String的一个实例,v1是函数的第一个参数,在这里是调用放在V4寄存器中类型为Ljava/lang/String的实例的equal ()方法,并传入参数v1,返回的结果是Z类型,也就是boolean类型。
如果是invoke-static{v4, v1}, 不同遇在于invoke-virtual {v4, v1}的是v4不是this,而是第一个参数。v1是第二个参数,所调用的方法需要两个参数。
1、直接方法
直接方法指的是该类中定义的方法。
#direct methods .method < 访问权限> [修饰关键字] < 方法原型> < .registers> [.param] [.prologue] [.line] < .local> < 代码体> .end method
#direct methods是注释,是baksmali添加的,访问权限和修饰关键字跟字段是一样的。
方法原型描述了方法的名称、参数与返回值。
.registers 指令指定了方法中寄存器的总数,这个数量是参数和本地变量总和。
.param表明了方法的参数,每个.param指令表示一个参数,方法使用了几个参数就有几个.parameter指令。
.prologue指定了代码的开始处,混淆过的代码可能去掉了该指令。
.line指明了该处代码在源代码中的行号,同样,混淆后的代码可能去掉了行号。
.local 使用这个指定表明方法中非参寄存器
//=================================================================== private void print(String string) { Log.d(TAG, string); } //=================================================================== .method private print(Ljava/lang/String; )V .registers 3 .param p1, "string"# Ljava/lang/String; .prologue .line 29 const-string v0, "MainActivity"invoke-static {v0, p1}, Landroid/util/Log; -> d(Ljava/lang/String; Ljava/lang/String; )I.line 30 return-void .end method //===================================================================
2、虚方法
虚方法指的是从父类中继承的方法或者实现的接口的方法,它的声明跟直接方法相同,只是起始的初始为virtual methods
//=================================================================== @Override public void onClick(View view) { String str = "Hello World!"; print(str); } //=================================================================== # virtual methods .method public onClick(Landroid/view/View; )V .registers 3 .param p1, "view"# Landroid/view/View; .prologue .line 24 const-string v0, "Hello World!".line 25 .local v0, "str":Ljava/lang/String; invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity; -> print(Ljava/lang/String; )V.line 26 return-void .end method //===================================================================
3、静态方法
//=================================================================== public static void setTag(String str) { TAG = str; } //=================================================================== .method public static setTag(Ljava/lang/String; )V .registers 1 .param p0, "str"# Ljava/lang/String; .prologue .line 64 sput-object p0, Ltestdemo/hpp/cn/annotationtest/MainActivity; -> TAG:Ljava/lang/String; .line 65 return-void .end method //===================================================================
六、注解
如果一个类使用了注解,那么smali中会使用.annotation指令。
#annotations
.annotation [注解属性] <
注解类名>
[注解字段 = 值]
.end annotation
注解的作用范围可以是类、方法或者字段。如果注解的作用范围是类,.annotation指令会直接定义在smali文件中,如果是方法或者字段,.annotation指令则会包含在方法或者字段的定义中。
1、注解类
//=================================================================== @BindInt(100) public class MainActivity extends AppCompatActivity {} //=================================================================== # annotations .annotation build Ltestdemo/hpp/cn/annotationtest/BindInt; value = https://www.songbingjia.com/android/0x64 .end annotation //===================================================================
2、注解字段
//=================================================================== @BindView(R.id.button) public Button mButton; //=================================================================== # instance fields .field public mButton:Landroid/widget/Button; .annotation build Lbutterknife/BindView; value = https://www.songbingjia.com/android/0x7f0c0050 .end annotation .end field //===================================================================
3、注解方法
//=================================================================== @OnClick(R.id.button) public void click() { String str = "Hello World!"; print(str); } //=================================================================== # virtual methods .method public click()V .registers 2 .annotation build Lbutterknife/OnClick; value = https://www.songbingjia.com/android/{ 0x7f0c0050 } .end annotation.prologue .line 29 const-string v0,"Hello World!".line 30 .local v0, "str":Ljava/lang/String; invoke-direct {p0, v0}, Ltestdemo/hpp/cn/annotationtest/MainActivity; -> print(Ljava/lang/String; )V.line 31 return-void .end method //===================================================================
七、应用— — smali插桩
插桩的原理就是静态的修改apk的samli文件,然后重新打包。
1、使用上面的方法得到一个apk的smali文件
2、在关键部位添加自己的代码,需要遵循smili语法,例如在关键地方打log,输出关键信息
3、重新进行打包签名
具体例子参考文章:http://drops.wooyun.org/papers/6045
参考文章:
http://drops.wooyun.org/papers/6045
http://blog.isming.me/2015/01/14/android-decompile-smali/
推荐阅读
- tk Mapper Oracle数据库插入时返回主键
- POJ-3321 Apple Tree---树状数组+DFS序
- Android Studio3.1.2编译时Java Compiler出错(Warning: Failed to parse host proxy3.bj...)
- android之使用百度地图
- 10款最佳薪资管理软件推荐合集(你应该使用的电子薪资软件)
- 11大最佳名片制作软件下载推荐合集(哪款最适合你())
- 10个最佳在线Logo制作工具合集(创建你的专业Logo)
- 10款最佳免费文字处理软件(文字处理器的功能、软件包和应用程序)
- 10款最佳在线演奏软件推荐合集(享受在线乐队的演奏)