0x01 前言

静态分析是探索 Android 程序内幕的一种最常见方法,与动态调试一起,能帮助分析人员解决分析时遇到的各类问题

0x02 简介

  • 静态分析(Static Analysis):在不运行代码的情况下,用词法分析、语法分析等技术手段对程序文件扫描,生成反汇编代码,通过阅读反汇编代码掌握程序功能的一种技术。

  • 然而在实际分析上,想要完全不运行程序是不太可能,通常是要先运行目标程序,从中寻找程序的突破口

    生成反汇编代码的工具称反汇编工具或反编译工具,工具越强大,反汇编效果越好,能事半功倍

0x03 静态分析 Android 程序的方法

它有两种方法:

  • 方法一:阅读反汇编生成的Dalvik字节码,可以用IDA Pro分析dex文件,或者使用文本编辑器阅读baksmali反编译生成的smali文件;

  • 方法二:阅读反汇编生成的java源码,可以使用dex2jar生成jar文件,然后再使用jd-gui阅读jar文件的代码。

快速定位Android程序的关键代码

六种方法定位关键代码:

  • 信息反馈法:先运行目标程序,然后根据程序运行时给出的反馈信息作为突破口寻找关键代码。

  • 特征函数法:跟信息反馈法类似。

  • 顺序查看发:从软件的启动代码开始,逐行的向下分析,掌握软件的执行流程。

  • 代码注入法:手动修改apk文件的反汇编代码,加入Log输出,配合LogCat查看程序执行到特定点时的状态数据。

  • 栈跟踪法:输出运行时的栈跟踪信息,然后查看栈上的函数调用序列来理解方法的执行流程。

  • 方法剖析:热点分析和性能优化。

0x04 阅读 smali 代码

那么,问题来了,什么是smali?

不要急,让我为大家一一道来:

要想了解smali,就得先理清APK、Dalvik字节码和smali文件之间的关系

APK文件

首先是apk,即安卓程序的安装包。Apk是一种类似于Symbian Sis或Sisx的文件格式。通过将APK文件直接传到Android模拟器或Android手机中执行即可安装。

而apk文件实际上就是一个MIME为ZIP的压缩包,只不过后缀名进行了更改

我们直接修改成(.zip)压缩包格式后缀名,打开并解压后就可以看到内部的文件结构,就像下面这样

而每个文件夹都有各自不同的作用,大家可以了解下

  • assets文件夹:保存一些额外的资源文件,如游戏的声音文件,字体文件等等,在代码中可以用AssetManager获取assets文件夹的资源。

  • lib文件夹:存放用C/C++编写的,用NDK编译生成的so文件,供java端调用。

  • META-INF文件夹:存放apk签名信息,用来保证apk包的完整性和系统的安全。

    在IDE编译生成一个apk包时,会对里面所有的文件做一个校验计算,并把计算结果存放在META-INF文件夹内,apk在安装的时候,系统会按照同样的算法对apk包里面的文件做校验,如果结果与META-INF里面的值不一样,系统就不会安装这个apk,这就保证了apk包里的文件不能被随意替换。比如拿到一个apk包后,如果想要替换里面的一幅图片,一段代码, 或一段版权信息,想直接解压缩、替换再重新打包,基本是不可能的。如此一来就给病毒感染和恶意修改增加了难度,有助于保护系 统的安全。

  • res文件夹:存放资源文件,包括icon,xml文件

  • AndroidManifest.xml文件:应用程序配置文件,每个应用都必须定义和包含的,它描述了应用的名字、版本、权限、引用的库文件等信息。

  • classes.dex文件:传统 Class 文件是由一个 Java 源码文件生成的 .Class 文件,而 Android 是把所有 Class 文件进行合并优化,然后生成一个最终的 class.dex 文件。它包含 APK 的可执行代码,是分析 Android 软件时最常见的目标。由于dex文件很难看懂,可通过apktool反编译得到.smali文件,smali文件是对Dalvik虚拟机字节码的一种解释(也可以说是翻译),并非一种官方标准语言。通过对smali文件的解读可以获取源码的信息。

  • resources.arsc文件:二进制资源文件,包括字符串等。

  • smali:smali是将Android字节码用可阅读的字符串形式表现出来的一种语言,可以称之为Android字节码的反汇编语言。利用apktool或者Android Killer,反编classes.dex文件,就可以得到以smali为后缀的文件,这些smali文件就是Dalvik的寄存器语言。

    简单的说,smali就是Dalvik VM内部执行的核心代码,andorid逆向分析的关键点。

Dalvik字节码

概念:Dalvik是google专门为Android操作系统设计的一个虚拟机,经过深度的优化。虽然Android上的程序是使用java来开发的,但是Dalvik和标准的java虚拟机JVM还是两回事。Dalvik VM是基于寄存器的,而JVM是基于栈的;Dalvik有专属的文件执行格式dex(dalvik executable),而JVM则执行的是java字节码。Dalvik VM比JVM速度更快,占用空间更少。

总结来说

  • Dalvik是andrord虚拟机,即Dalvik VM

  • Dalvik字节码是他执行的语言

  • Dalvik的专属文件格式(.dex),就像上面apk文件中的class.dex

  • Dalvik VM是基于寄存器的,基于寄存器的意思是,所有操作都必须经过寄存器来进行。

如果我们直接通过分析Dalvik的字节码,我们是不能看到原来的代码逻辑的,这时需要借助如Apktool或Android Killer工具来帮助查看。但是,注意的是最终我们修改APK需要操作的文件是.smali文件。

smali文件

概念:smali语言是Davlik的寄存器语言,语法上和汇编语言相似,它拥有特定的格式与语法,smali 语言是对 Dalvik 虚拟机字节码的一种解释。它是逆向分析app的重中之重,一般我们逆向都是在smali文件里进行修改代码,进而实现我们想要的结果。

smali文件,我们直接通过解压apk文件是看不到的,需要通过apptool或Android Killer等具备反编译功能工具(网上很多)反编译apk,就可以得到以smali为后缀的文件。

而它三者的关系就像

你们也许会疑惑,图上这不才俩吗?Dalvik去哪了?

从某种方面上讲,这整个框框就是Dalvik,整个过程需要在框里才能运行,脱离了框框,它就。。。understand?,它无处不在。

好了,我们接下来就详细讲下smali文件

smali文件结构

smali文件和java中的类都是一一对应的,它有自己的一套语法,指令都是以“.”开头,常用的一些指令如下:

指令

说明

.class

包名+类名

.super

父类类名

.source

源文件名称

.implements

接口实现

.field

定义字段

invoke-super

调用父函数

invoke-direct

调用函数

invoke-static

调用静态函数

new-instance

创建实例

iput-object

对象赋值

iget-object

调用对象

return-void

函数返回void

const/high16 v0,0x7fo3

把0x7fo3赋值给v0

.method/.end method

方法的开始与结束

.locals

方法内使用的v开口的寄存器个数

.prologue

表示方法中代码的开始处

.line

对应java中的行数

.param

指定了方法的参数

.paramter

和.paramter含义相同

.annotation/.end annotation

注解的开始和结束

smali数据类型

Dalvik字节码和Java一样,都只有两种数据类型:基本类型和引用类型,8种基本数据类型,对象和数组是引用类型,Dalvik字节码和Jvm中对数据类型的描述是一致的,

  • 基础数据类型:Z,B,S,C,I,L,F,D为基本数据类型,从上表可以看出,Dalvik字节码基本类型的描述符基本上是java基本类型的首字母,除了boolean对应为Z外

  • 对象类型:L加上类或者接口的全称表示对象类型,即Lpackage/objectName,如String类型描述符为Ljava/lang/String,包com.biyou下面的test类的类型描述符为Lcom/biyou/test

  • 数组类型:基本类型的数组为”[“加上基本类型描述符来表示,一维数组前面是一个”[“,多一个维度前面多加一个”[“,比如int类型,一维是:[I,二维是:[[I,依次类推。

    对象类型的数组为”[“加上对象类型表示符来表示,如String类型表示为:[Ljava/lang/String。

java数据类型和Dalvik字节码的数据类型一一对应,对应关系如下表:

java类型

smali描述符

备注

boolean

Z

布尔型

byte

B

字节型

short

S

短整数型(16位)

char

C

字符型

int

I

整数型

long

L

长整数型(64位)

float

F

浮点型

double

D

双精度型(64位)

void

V

空类型,仅用作返回类型

L

对象类型

[

数组类型

.mali文件示例

我们就以  MainActivity.smali 为例

头文件:

  • .class 与  .super 指令会指明 smali 文件所保存的类的完整签名,和类的父类的完整签名。像上述文件类(Lcom/example/junior/MainActivity)

  • 像public,protected,private就是所谓的访问权限修饰符,非权限修饰符则指的是final,abstract,static,两者都可以为空。

  • 另外如果原java代码有混淆,那一般.class里面的类名和.source的源文件名则不同,以下是经过混淆的

字段声明

  • smali文件的字段的声明使用".field"指令,字段有静态字段和实例字段两种:

    1、静态字段格式:

    .field 访问权限 static 修饰关键字  字段名  字段类型

    .field public static HELLO:Ljava/lang/String 2、实例字段格式:.field 访问权限 修饰关键字 字段名  字段类型

    .field prviate button:Landroid/widget/Button

注释:

  • smali 文件中会自动生成一些注释,:“# instance fields”和“# virtual methods”

  • smali 文件只支持单行注释,注释可放在一行开头,也可放在一行的任何地方,使用 # 标识注释的开始,井号后的部分都是注释

注解:

  • 若类中包含 Java 注解,在 smali 文件中会以“# annotations”注释加以说明,接着会以 .annotation 指令开始,以 .end annotation 指令结束(声明一个注解)。.annotation 指令后跟着注解的类型和完整的签名,若类中有多个注解,则会有多个指令对

方法:

  • DEX 中的类包含类自己实现的实例方法“# direct methods”(用privat修饰的)和继承自父类的虚方法“# virtual methods”(用public和protected修饰的)。每个方法以 .method 指令开始,以 .end method 指令结束,.method 指令后跟着方法的访问权限和完整的签名

    图中第199行表示方法的开始处,修饰符是static,访问权限是private,方法名是 assertMainThread, 参数为Ljava/lang/String;,V代表无返回值。

  • 在一个方法中,除了真实执行的机器指令,还有其他指令,列举如下:

    .locals :声明当前方法中使用的寄存器数目。对 DEX 中的每个方法,都可通过静态分析知道其使用了多少个寄存器。这样做的好处是,虚拟机执行 DEX 中类的方法时可提前为方法准备栈空间

    .param :指定方法中的参数名,以便程序的调试。经过 Proguard(混淆工具)处理的 DEX 可能不含该信息

  • .prologue :表示下面的部分是 DEX 指令

    .line :保存 DEX 中的方法在 Java 源文件中的行号信息,以便调试。经过 Proguard 处理的 DEX 可能不含该信息

接口实现

  • 若一个类实现了接口,会在smali 文件中使用“ .implements ”指令指出,上述图中第六行

    表明实现了Executor这个接口

if判断语句

类型

解释

等价

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-ltz 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)

switch 分支语句

  • 实例:SwitchCase

  • 实例代码:GitHub

  • 用 AndroidKiller 反编译实例 SwitchCase,然后打开反编译后的工程目录中的 smali/com/droider/switchcase/MainActivity.smali 文件,找到 packedSwitch() 的代码:

    上述实例每句均带有注释,基本上都做好了分析,大家可对照注释一一学习

0x05 应用——smali插桩

插桩的原理:静态的修改apk的samli文件,然后重新打包。

  1. 使用上面的方法得到一个apk的smali文件

  2. 在关键部位添加自己的代码,需要遵循smili语法,例如在关键地方打log,输出关键信息

  3. 重新进行打包签名

0x06 总结

写得这篇文章,主要是smali的基础知识层面进行阐述,知识点比较多,适合安卓逆向入门级人员,希望对大家有所帮助。

静态分析android程序之阅读smali代码相关推荐

  1. Android逆向之调试smali代码基础

    点击上方↑↑↑蓝字[协议分析与还原]关注我们 " 介绍Android逆向中调试smali代码的方法." 最近在重整Android逆向分析环境,一切都在从零开始,做下记录,给大家分享 ...

  2. Android逆向--如何调试smali代码?

    最近在重整Android逆向分析环境,一切都在从零开始,做下记录,给大家分享. 本文介绍Android逆向中smali代码的调试及环境的准备. 事先准备如下工具: Android killer:反编译 ...

  3. 一名Android程序员如何减少代码中该死的-if-else-嵌套,怎么让代码更简洁?

    减少代码中该死的-if-else-嵌套,让代码更简洁! 写在前面 不知大家有没遇到过像"横放着的金字塔"一样的if else嵌套: if (true) {if (true) {if ...

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

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

  5. Android 程序保活,锁机代码

    前言 保活:如何让我们的app在Android系统不被杀死 保证存活,简单做法就是提升程序的优先级,看完本文一些流氓锁机你也会了哦.但锁机源码我不打算提供 为了防止某些恶心的人直接复制然后在市面上搞破 ...

  6. cocos2dx 应用程序调用已有的android程序,cocos2dx-JniHelper 使用(示例代码)

    1.操作的游戏工程和cocos2d_x游戏引擎是一个目录的 2.跟jni相关的C++代码文件放在proj.android\jni\hellocpp目录下,每加一个cpp文件,都需在proj.andro ...

  7. 【转载】Android逆向开发之smali语言的学习

    Android逆向开发之smali语言的学习 该文转载自乱码三千 – 分享实用IT技术 smali和java基本数据类型对比 smali java B byte S short I int J lon ...

  8. 挨踢部落故事汇(20):Android程序员的十大转型之路

    玖哥是一个来自东北的Android攻城狮,现在定居被誉为"大湖名城,创(chuan)新(xiao)高地"的合肥.知识面极广,不仅广泛涉猎IT相关知识,还热爱文学,性格幽默,被誉为& ...

  9. Android程序员的十大转型之路

    IT行业是一个瞬息万变的行业,程序员是一个不进则退的职业.我作为一个Android程序员,多年来一直保持随时可以转型其他技术领域的状态,保持对新技术敏感的嗅觉. 我先说说Android程序员不可能转型 ...

  10. 静态分析android代码, 循环与trycatch

    一:阅读dalvik字节码,可用ida阅读dex, 或直接文本阅读smali文件 二:阅读java源码,可用dex2jar生成jar文件,再使用jd-gui来阅读jar文件的源码. Android.i ...

最新文章

  1. java表或视图不存在_Error querying database. Cause: java.sql.SQLSyntaxErrorException: ORA-00942: 表或视图不存在...
  2. OpenKruise v0.5.0 版本发布,支持无损的流式分批发布策略
  3. IOS UIAlertController 使用方法
  4. Python3身份运算符(比较对象是否相同)
  5. 那些年,我们一起做过的性能优化
  6. golang func 入参为func_Golang函数常见用法
  7. java基础之java内存模型
  8. 学计算机的心理300字,心理的作文300字7篇
  9. 英语计算机简历范文模板,计算机专业英文简历模板范文六篇(3)
  10. 学习linux—— 磁盘相关指令
  11. 简单区块链Python实现
  12. android-ImageView显示选择本地图片或拍照
  13. java 权限管理框架
  14. Android 开源项目及库汇总
  15. NISP题库(八套模拟题)
  16. BZOJ2565最长双回文串——manacher
  17. dfuse 新版 client-js 库发布
  18. cad渐开线齿轮轮廓绘制_CAD渐开线齿形怎么绘制
  19. 超级电脑可下载人类思想 究竟是福是祸?(
  20. 可达性分析算法与理解误区

热门文章

  1. Java逐行读取文件
  2. iOS开发之GameCenter使用
  3. 计算机科学与技术名言,关于科学的名言(20个最值得一读的科技名言)
  4. Ballot evaluation
  5. B - Distributing Ballot Boxes
  6. 打印机有关术语及解释
  7. 上位机开发(软件测试)
  8. ofbiz UOM Conversion Relationship Not Found [单位转化关系没有找到] 问题解决:
  9. 质量与规范,敬我们那些年欠下的技术债
  10. 大学生家乡网页设计作业模板下载 南京城市网页设计作业成品 静态HTML旅游景点网页制作下载_网页设计代码