静态分析Android程序的两种方法:
一、阅读反编译生成的Dalvik字节码。
1、使用文本编辑器阅读baksmali反编译生成的smali文件
(1)解压apk包

unzip xxx.apk

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

java -jar baksmali-2.0.3.jar classes.dex

2、使用IDA Pro分析dex文件

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

java -jar dex2jar classes.dex

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

本篇文章注意介绍第一种方式得到smali文件之后,对应smali文件进行分析。

无论是普通类、抽象类、接口类或者内部类,在反编译的代码中,它们都会以单独的smali文件存放。每个smali文件都由若干语句组成,所有的语句都遵循着一套语法规范。下面来具体介绍。

一、头信息——类的主体信息

在打开smali文件的时候,它的头三行描述了当前类的一些信息。

.class <访问权限> [关键修饰字] <类名>;
.super <父类名>;
.source <源文件名>

例如:

//===================================================================
public class MainActivity extends AppCompatActivity {// ......
}
//===================================================================
.class public Ltestdemo/hpp/cn/test/MainActivity;
.super Landroid/support/v7/app/AppCompatActivity;
.source "MainActivity.java"
//===================================================================

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

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

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

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

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

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

#interfaces
.implements <接口名>

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

例如:

//===================================================================
public class MainActivity extends AppCompatActivity implements View.OnClickListener {// ......
}
//===================================================================
# interfaces
.implements Landroid/view/View$OnClickListener;
//===================================================================

三、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位)

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 29const-string v0, "MainActivity"invoke-static {v0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I.line 30return-void
.end method
//===================================================================

.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 26return-void
.end method
//===================================================================

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)

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指令,方法分为直接方法和虚方法两种。

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 29const-string v0, "MainActivity"invoke-static {v0, p1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I.line 30return-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 24const-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 26return-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 64sput-object p0, Ltestdemo/hpp/cn/annotationtest/MainActivity;->TAG:Ljava/lang/String;.line 65return-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 29const-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 31return-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/

欢迎关注微信公众号:DroidMind
精品内容独家发布平台


呈现与博客不一样的技术干货

静态分析Android程序——smali文件解析相关推荐

  1. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  2. Android init.rc文件解析过程详解(三)

    Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...

  3. Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  4. Android init.rc文件解析过程详解(一)

        Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...

  5. Android so(ELF) 文件解析

    文章目录 前言 生成 so 文件 相关工具 objdump readelf 整体结构图 头部结构 段表结构 字符串表结构 程序表结构 符号表结构 重定位表结构 其他结构 解析代码 打开 ELF 文件 ...

  6. Android APK包文件解析

    原博文题目:Android 篡改apk文件的可行性分析 Android的.apk文件实际上就是一个zip文件 可以直接用winrar打开 如下图所示: 包括了一个META-INF目录 一个res目录 ...

  7. Android系统 —— image文件解析

    Android系统编译之后的打包阶段,会将所有编译出来的有需要的执行文件,库文件以及各种配置文件等打包到各个镜像文件中.有时候我们需要看一下镜像文件中都打包了什么东西,那就可以通过一些方法将其挂载到一 ...

  8. 第五章 静态分析 Android 程序(四)(使用 JEB 进行静态分析)

    文章目录 使用 JEB 进行静态分析 安装 JEB JEB 的静态分析功能 JEB 的脚本化与插件 使用 JEB 进行静态分析 JEB:一款强大的跨平台 Android 静态分析工具 相比 jd-gu ...

  9. Android开发--XML文件解析

    开发者一般会在XML文件中定义控件,其中肯定包含控件的一些属性,下面就对这些属性做一些简单的介绍: 1)android:id---这个属性是为控件提供一个标识或者是对一个控件采用引用 2)androi ...

  10. android程序db文件用什么编辑器,在 Android Studio 上调试数据库 ( SQLite )

    以前 Eclipse 时代,调试 SQLite 都是将数据库文件导出到电脑,然后再用软件打开查看.现在我们用 Android Studio,有没有更方便的方法呢? SQLScout 安装 SQLSco ...

最新文章

  1. Binder相关面试总结(三):Binder机制是如何跨进程的
  2. 第一篇博客,写在颓废之时
  3. 计算机网络按信息传输介质的性能来划分,大学计算机基础练习题网络技术.doc...
  4. 工厂方法模式(Factory Method Pattern)
  5. XML Schema语法规则
  6. idea展示runDashboard的窗口
  7. ifound Android wifi,方正新品记录仪iFound V1号称黑夜变白天,真的假的?
  8. 使用python-nmap 搭建基本端口扫描器
  9. 波卡生态跨链隐私中间件协议Raze Network与社区DeFi平台MANTRA DAO达成合作
  10. 从技术角度看人人网,互联网营销
  11. 字典排序 python3_这10个字典操作你必须知道
  12. markdown与latex:单行式子中连加连乘i放在下面\displaystyle
  13. 《海量数据库解决方案》之位图索引的结构和特征
  14. 新手如何学习FPGA技术
  15. 读计算机网络得学五笔吗,电脑五笔打字难不难学?大约要学多久才可以掌握?
  16. 灵飞经5龙生九子 第二十一章 危机四伏 3
  17. 从阿里云购买、域名购买、SSL免费购买到SSL集成开发(网络编程安全三)
  18. 购买服务器 操作系统选什么区别吗,购买服务器选择什么操作系统
  19. 云更新无盘服务器缓存,云更新无盘服务器缓存设置
  20. win10电脑耳机插上突然没音(已解决)

热门文章

  1. 线程学习记录-锁原理与对象头
  2. Sisyphus----a log data-mining toolkit安装过程
  3. vue+echarts+springboot实现云词图
  4. 光格科技递交科创板上会稿:拟募资6亿 预计年营收3亿
  5. 编程愤怒的小鸟代码python_Python 愤怒的小鸟代码实现(1):物理引擎pymunk使用
  6. BaseProxy:异步http/https中间人
  7. 数据库复杂查询,左联右联 聚合 计数 时间查询等,持续更新
  8. 折半查找的实现 swustoj
  9. windows server 2003 IE升级方法
  10. 软件开发程序员需要掌握的技术