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

(2)用baksmali进行对解压出来的dex文件反编译

java -jar baksmali-2.0.3.jar classes.dex
  • 1

2、使用IDA Pro分析dex文件

二、阅读反编译生成的Java源码 
(1) 使用 dex2jar 把classes.dex转换成jar

java -jar dex2jar classes.dex
  • 1

(2)使用jd-gui 打开这个jar

本篇文章注意介绍第一种方式得到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

.class指令表示当前的类名,类的访问权限是public,类名为Ltestdemo/hpp/cn/test/MainActivity,类开头的L是遵循Dalvik字节码的相关约定,表示后面跟随的字符串是一个类。

.super指定了当前类所继承的父类,后面指的就是这个父类的类名,L表示后面跟的字符串是一个类

.source指定了当前类的源文件名

注意:经过混淆的dex文件,反编译出来的smali代码可能没有源文件信息,因此source行的代码可能为空。

这三行就是类的主体部分了,另外一个类是由多个字段或者方法组成。

二、接口 
如果一个类实现了一个接口,那么会在smali文件中使用.implements指令指出。

#interfaces
.implements <接口名>
  • 1
  • 2

同样,#interfaces是注释,.implements是接口关键字。

例如:

//===================================================================
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
// ......
}
//===================================================================
# interfaces
.implements Landroid/view/View$OnClickListener;
//===================================================================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三、smali基本语法 
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

2、对象类型 
Lpackage/name/ObjectName; 相当于java中的package.name.ObjectName; 
L 表示这是一个对象类型 
package/name 该对象所在的包 
ObjectName 对象名称 
; 标识对象名称的结束

3、数组类型 
[I :表示一个整形的一维数组,相当于java的int[]; 
对于多维数组,只要增加[ 就行了,[[I = int[][];注:每一维最多255个;

对象数组的表示形式: 
[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

.registers 3 说明该方法有三个寄存器,其中一个本地寄存器v0,两个参数寄存器p0,p1,细心的人可能会注意到没有看到p0,原因是p0存放的是this。如果是静态方法的话就只有2个寄存器了,不需要存this了。

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

6、if判断语句

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

7、循环语句 
常用的循环结构有:迭代器循环,for循环,do while循环。

8、switch分支语句

9、try/catch语句

四、字段

smali文件中,字段的声明使用.field指令,字段分为静态字段和实例字段。

1、静态字段

#static fields
.field <访问权限> static [修饰关键字] <字段名>:<字段类型>

可以看到,baksmali在生成smali文件时,会在静态字段声明的起始处添加注释”static fields”,注释是以#开头。

访问权限包括:private、protected、public(三者之一) 
修饰关键字为字段的其他属性,例如,final 
字段名和类型就不用解释了

例如:

//===================================================================
private static 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 = 0x64
.end annotation
//===================================================================

2、注解字段

//===================================================================
@BindView(R.id.button)
public Button mButton;
//===================================================================
# instance fields
.field public mButton:Landroid/widget/Button;
.annotation build Lbutterknife/BindView;
value = 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 = {
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/

[Android Security] 静态分析Android程序——smali文件解析的更多相关文章

  1. 八、Android学习第七天——XML文件解析方法(转)

    (转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 八.Android学习第七天——XML文件解析方法 XML文件:exten ...

  2. Android学习笔记之AndroidManifest.xml文件解析(转)

    //自已备注: <?xml version="1.0" encoding="utf-8"?>//说明了版本号,字符集 <manifest xm ...

  3. Android学习笔记之AndroidManifest.xml文件解析

    一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(activiti ...

  4. Android学习笔记之AndroidManifest.xml文件解析(详解)

    一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件.它位于整个项目的根目录,描述了package中暴露的组件(activiti ...

  5. android基础知识13:AndroidManifest.xml文件解析

    注:本文转载于:http://blog.csdn.net/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...

  6. Android之AndroidManifest.xml文件解析

    转自:Android学习笔记之AndroidManifest.xml文件解析 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文 ...

  7. [Android Security] jar文件转smali文件

    cp : https://blog.csdn.net/fengmm521/article/details/78446486 jar转smali文件一共要走两步,先将jar文件转为.dex文件 (dx工 ...

  8. Android开发之 Android应用程序详细解析

    我们继续的沿用上一篇所建立的应用. Android应用程序可以分为:应用程序源代码(.java),应用程序描述文件(.xml),各种资源. 可以这么理解: 安卓应用程序,通过java代码来实现其业务逻 ...

  9. Android开发之 Android应用程序目录结构解析

    建立的HelloWorld的应用项目,其代码是由ADT插件自动生成的,形成Android项目特有的结构框架. 接下来让我带领大家解析一个Android程序的各个组成部分,这次我们拿一个Hello,Wo ...

随机推荐

  1. Elasticsearch常用最全最常用工具清单

    https://blog.csdn.net/ZYC88888/article/details/82872558

  2. 【LOJ】#2493. 「BJOI2018」染色

    题面 题解 推结论大题--然而我推不出什么结论 奇环显然是NO 如果一个联通块里有两个分离的环,也是NO 如果一个联通块里,点数为n,边数为m m <= n的时候,是YES m >= n ...

  3. 洛谷P3576 [POI2014]MRO-Ant colony [二分答案,树形DP]

    题目传送门 MRO-Ant colony 题目描述 The ants are scavenging an abandoned ant hill in search of food. The ant h ...

  4. 初识thinkphp(5)

    这次主要内容是模型的基本操作 0x01:什么是模型 通过手册的阅览,笼统的说就是,把打开数据库等操作在另一个php文件中进行 以及对变量的规则具体细节,查询,取值等操作进行定义,方便在控制器中直接使用 ...

  5. 如何对vue项目进行优化,加快首页加载速度

    上个月上线了一个vue小项目,刚做完项目,打包上线之后,传到服务器上发现首页加载巨慢. 由于开发时间比较紧,我想着怎么快怎么来,因而在开发过程中没考虑过优化性能问题,酿成最后在带宽5M的情况下页面加载 ...

  6. Top 10 Revit Architecture 2014 books

    Revit Architecture, along with ArchiCAD, is most used BIM software in architectural design. Although ...

  7. 面向对象设计原则 里氏替换原则(Liskov Substitution Principle)

    里氏替换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一. 里氏替换原则中说,任何基类可以出现的地方,子类一定可以出现. LSP是继承复用的基石,只 ...

  8. Atcoder Tenka1 Programmer Contest 2019 题解

    link 题面真简洁 qaq C Stones 最终一定是连续一段 . 加上连续一段 # .直接枚举断点记录前缀和统计即可. #include<bits/stdc++.h> #define ...

  9. python基础之return,参数

    函数的返回值 1.什么是返回值: 返回值是一个函数的处理结果 2.为什么要有返回值 如果需要在程序中拿到函数的处理结果,做进一步的处理,则需要函数必须有返回值 3.函数返回值的应用: 函数的返回值用r ...

  10. 【BZOJ-3456】城市规划 CDQ分治 + NTT

    题目链接 http://www.lydsy.com/JudgeOnline/problem.php?id=3456 Solution 这个问题可以考虑dp,利用补集思想 N个点的简单图总数量为$2^{ ...