静态分析Android程序——smali文件解析
静态分析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文件解析相关推荐
- 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc文件解析过程详解(三)
Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...
- Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc文件解析过程详解(一)
Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...
- Android so(ELF) 文件解析
文章目录 前言 生成 so 文件 相关工具 objdump readelf 整体结构图 头部结构 段表结构 字符串表结构 程序表结构 符号表结构 重定位表结构 其他结构 解析代码 打开 ELF 文件 ...
- Android APK包文件解析
原博文题目:Android 篡改apk文件的可行性分析 Android的.apk文件实际上就是一个zip文件 可以直接用winrar打开 如下图所示: 包括了一个META-INF目录 一个res目录 ...
- Android系统 —— image文件解析
Android系统编译之后的打包阶段,会将所有编译出来的有需要的执行文件,库文件以及各种配置文件等打包到各个镜像文件中.有时候我们需要看一下镜像文件中都打包了什么东西,那就可以通过一些方法将其挂载到一 ...
- 第五章 静态分析 Android 程序(四)(使用 JEB 进行静态分析)
文章目录 使用 JEB 进行静态分析 安装 JEB JEB 的静态分析功能 JEB 的脚本化与插件 使用 JEB 进行静态分析 JEB:一款强大的跨平台 Android 静态分析工具 相比 jd-gu ...
- Android开发--XML文件解析
开发者一般会在XML文件中定义控件,其中肯定包含控件的一些属性,下面就对这些属性做一些简单的介绍: 1)android:id---这个属性是为控件提供一个标识或者是对一个控件采用引用 2)androi ...
- android程序db文件用什么编辑器,在 Android Studio 上调试数据库 ( SQLite )
以前 Eclipse 时代,调试 SQLite 都是将数据库文件导出到电脑,然后再用软件打开查看.现在我们用 Android Studio,有没有更方便的方法呢? SQLScout 安装 SQLSco ...
最新文章
- Binder相关面试总结(三):Binder机制是如何跨进程的
- 第一篇博客,写在颓废之时
- 计算机网络按信息传输介质的性能来划分,大学计算机基础练习题网络技术.doc...
- 工厂方法模式(Factory Method Pattern)
- XML Schema语法规则
- idea展示runDashboard的窗口
- ifound Android wifi,方正新品记录仪iFound V1号称黑夜变白天,真的假的?
- 使用python-nmap 搭建基本端口扫描器
- 波卡生态跨链隐私中间件协议Raze Network与社区DeFi平台MANTRA DAO达成合作
- 从技术角度看人人网,互联网营销
- 字典排序 python3_这10个字典操作你必须知道
- markdown与latex:单行式子中连加连乘i放在下面\displaystyle
- 《海量数据库解决方案》之位图索引的结构和特征
- 新手如何学习FPGA技术
- 读计算机网络得学五笔吗,电脑五笔打字难不难学?大约要学多久才可以掌握?
- 灵飞经5龙生九子 第二十一章 危机四伏 3
- 从阿里云购买、域名购买、SSL免费购买到SSL集成开发(网络编程安全三)
- 购买服务器 操作系统选什么区别吗,购买服务器选择什么操作系统
- 云更新无盘服务器缓存,云更新无盘服务器缓存设置
- win10电脑耳机插上突然没音(已解决)
热门文章
- 线程学习记录-锁原理与对象头
- Sisyphus----a log data-mining toolkit安装过程
- vue+echarts+springboot实现云词图
- 光格科技递交科创板上会稿:拟募资6亿 预计年营收3亿
- 编程愤怒的小鸟代码python_Python 愤怒的小鸟代码实现(1):物理引擎pymunk使用
- BaseProxy:异步http/https中间人
- 数据库复杂查询,左联右联 聚合 计数 时间查询等,持续更新
- 折半查找的实现 swustoj
- windows server 2003 IE升级方法
- 软件开发程序员需要掌握的技术