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. Windows 7 x64环境下SDK Manager闪退的解决方法

    1.下载并解压:http://dl.google.com/android/adt/adt-bundle-windows-x86_64-20140702.zip 2.安装JDK,否则SDK Manage ...

  2. Codeforces Round #189 (Div. 1) C - Kalila and Dimna in the Logging Industry 斜率优化dp

    C - Kalila and Dimna in the Logging Industry 很容易能得到状态转移方程 dp[ i ] = min( dp[ j ] + b[ j ] * a[ i ] ) ...

  3. linux学习笔记-6.权限

    1.创建a.txt和b.txt文件,将他们设为其拥有者和所在组可写入,但其他以外的人则不可写入 chmod ug+w,o-w a.txt b.txt 2.创建c.txt文件所有人都可以写和执行 chm ...

  4. ServletContextListener 详解

    1.首先来看一看源码 该类的源码 public interface ServletContextListener extends EventListener { /** * Receives noti ...

  5. Python学习——Python基本数据类型(一)

    python3 基本数据类型 Python3 中的变量不需要声明.每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建. Python3 中有6个标准的数据类型:Number(数字);字符串(St ...

  6. Web大前端面试题-Day3

    1. javascript的typeof返回哪些数据类型? 答案: undefined string boolean number symbol(ES6) Object Function 2. 列举3 ...

  7. Android的GridView控件点击图片变暗效果

    @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setC ...

  8. MSDN WinUSB Example

    The WinUSB user-mode library uses device interface classes to communicate with the kernel-mode USB s ...

  9. E3-1260L (8M Cache, 2.40 GHz) E3-1265L v2 (8M Cache, 2.50 GHz)

    http://ark.intel.com/compare/52275,65728         Product Name Intel® Xeon® Processor E3-1260L (8M Ca ...

  10. 从零开始部署CAS服务器

    从0开始部署CAS服务器的操作过程文档,我已经整理完毕,一共分为8步,这8步都是我自己操作实践过的. Setp1:Ubuntu server安装 在virtual box中安装ubuntu serve ...