栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SMALI学习笔记 by vrix.yan

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SMALI学习笔记 by vrix.yan

SMALI学习笔记 by vrix.yan

文章目录
  • 1. 基本语法
    • 1.1 数据类型
    • 1.2 对象类型
    • 1.4 语法关键词
  • 2. smali中的包信息
  • 3. smali中的声明
  • 4. 成员变量
    • 4.1 定义
    • 4.2 普通字段读写操作
    • 4.3 静态字段读写操作
  • 5. smali寄存器
    • 寄存器的表示
  • 6. 指令
    • 6. 1 赋值指令
    • 6.2 定义指令
    • 6.3 跳转指令
    • 6.4 比较指令
    • 6.5 四则运算
    • 6.6 锁指令
    • 6.7 数据转换指令
    • 6.8 数据运行指令
  • 7. smali与java
    • 7.1 简单语法
    • 7.2 java与smali对比
  • 8 函数
    • 8.1 函数的定义
      • 1、直接方法
      • 2、虚方法
      • 3、静态方法
    • 8.2 smali函数调用
    • 8.3 返回
  • 9 注解
    • 1、注解类
    • 2、注解字段
    • 3、注解方法
  • 10. 逆向分析工具
    • 10.1 Dex到Java反编译器
    • 10.2 资源文件xml破解
    • 10.3 将dex文件转换为jar文件
    • 10.4 查看classes-dex2jar.jar
    • 10.5 反编译修改
    • 10.6 二次打包
  • 11 参考文档

1. 基本语法 1.1 数据类型

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

1.2 对象类型

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

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

1.4 语法关键词
关键词	说明
.class	定义java类名
.super	定义父类名
.source	定义Java源文件名
.filed	定义字段
.method	定义方法开始
.end method	定义方法结束
.annotation	定义注解开始
.end annotation	定义注解结束
.implements	定义接口指令
.local	指定了方法内局部变量的个数
.registers	指定方法内使用寄存器的总数
.prologue	表示方法中代码的开始处
.line	表示java源文件中指定行
.paramter	指定了方法的参数
.param	和.paramter含义一致,但是表达格式不同
2. smali中的包信息
.class public Lcom/a;
.super Lcom/b;
.source "c.java"
这是一个由c.java编译得到的smail文件。他是com.a这个包下的一个类,继承自com.b这个类。
3. smali中的声明
# annotations
.annotations system Ldalvik/annotation/MemberClasses;
value={
  Lcom/a$z;
  Lcom/a$x;
}
.end annotation

这个模块是内部类的声明,a这个类中有两个成员内部类:z和x。

4. 成员变量 4.1 定义

成员变量定义格式为:
.field public/private [static][final] varName:<类型>

获取指令
iget, sget, iget-boolean, sget-boolean, iget-object, sget-object

操作指令
iput, sput, iput-boolean, sput-boolean, iput-object, sput-object
array的操作是aget和aput

指令解析
sget-object v0,Lcom/aaa;->ID:Ljava/lang/String;
获取ID这个String类型的成员变量并放到v0这个寄存器中
iget-object v0,p0,Lcom/aaa;->view:Lcom/aaa/view;
iget-object比sget-object多一个参数p0,这个参数代表变量所在类的实例。这里p0就是this

Smali代码示例1:

const/4 v3, 0x0
sput-object v3, Lcom/aaa;->timer:Lcom/aaa/timer;

相当于java代码:this.timer = null;

Smali代码示例2:

.local v0, args:Landroid/os/Message;
const/4 v1, 0x12
iput v1,v0,Landroid/os/Message;->what:I

相当于java代码:args.what = 18;
其中args为Message的实例

4.2 普通字段读写操作

前缀是i的iput-type和iget-type指令用于字段的读写操作.

指令描述
iget-object vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id对象的引用值给vBB寄存器
iget-boolean vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
iget-wide vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
iget vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
iput-object vAA,vBB,filed_id把vAA寄存器指向的对象的引用赋值给vBB寄存器中的filed_id对象
iput-boolean vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的boolean类型
iput-wide vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的wide类型
iput vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的int类型
4.3 静态字段读写操作

前缀是s的sput-type和sget-type指令用于静态字段的读写操作

指令描述
sget-object vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id对象的引用值给vBB寄存器
sget-boolean vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
sget-wide vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
sget vAA,vBB,filed_id读取vAA寄存器中的对象中的filed_id的值给vBB寄存器
sput-object vAA,vBB,filed_id把vAA寄存器指向的对象的引用赋值给vBB寄存器中的filed_id对象
sput-boolean vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的boolean类型
sput-wide vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的wide类型
sput vAA,vBB,filed_id把vAA寄存器的值给vBB寄存器中的int类型
5. smali寄存器

在虚拟机Dalvik中,方法中有两种方式定义方法中可以使用的寄存器数量。.registers 指令定义了方法中可以使用的寄存器的总数量。可以选择性的使用.locals ,该指令定义了方法中非参数寄存器的数量。寄存器的总数量应当包括方法参数所使用的寄存器的数量

v字命名		p字命名				说明
v0								the first local register
v1								the sencond local register
v2l			p0					the first paramter register
v3			p1					the second parameter register
v4			p2					the third parameter register

在smail中所有的操作都必须经过寄存器来进行。本地寄存器用v来表示,例如v0,v1,v2等。参数寄存器用p来表示,例如p0,p1,p2等。特别的一点是,p0并不一定是函数中的第一个参数,在非静态方法中,它指代“this”,p1表示函数的第一个参数。而在静态方法中,p0就是函数的第一个参数。

寄存器的表示
const/4 v0,0x1
iput-boolean v0,p0,Lcom/a;->isTrue:Z

上面的代码块首先使用了v0本地寄存器,并把值0x1存到v0中,第二句用iput-boolean这个指令把v0中的值存放到com.a.isTrue这个成员变量中,即:this.isTrue=true;。

6. 指令 6. 1 赋值指令

move(move destination)。

move va,vb               #将va=vb
move/from16 va,vb        #  va=vb   va(为8位寄存器) vb(为16位寄存器)

move-result vaa          #将上一个invoke类型指令操作的单字非对象结果返回给vaa
move-result-object vaa          #将上一个invoke类型指令操作的对象结果返回给vaa
move-exception vaa          #保存一个运行时发生的异常到vaa
throw vaa                   #抛出vaa寄存器的异常
6.2 定义指令

数据定义指令用来定义程序中用到的常量、字符串、类等。
基础字节码为const

const/4 va,#+b          #将数值符号扩展为32位后赋值给寄存器va
const/16 vaa,#+bbbb          #将数值符号扩展为32位后赋值给寄存器vaa
const va,#+bbbbbbb          #将数值赋值给寄存器va
const/high16 vaa,#+bbbb          #将数值右边零扩展为32位后赋值给寄存器vaa
const-wide/16 vaa,#+bbbb          #将数值符号扩展为64位后赋值给寄存器vaa

const-string vaa,string@bbb  #将字符串赋值给寄存器vaa
指令描述
const/4 vA,#+B将数值符号扩展为32后赋值给寄存器vA
const/16 vAA, #+BBBB将数据符号扩展为32位后赋给寄存器vAA
const vAA, #+BBBBBBBB将数值赋给寄存器vAA
const/high16 vAA, #+BBBB0000将数值右边零扩展为32位后赋给寄存器vAA
const-wide/16 vAA,#+BBBB将数值符号扩展为64位后赋值个寄存器对vAA
const-wide/32 vAA,#+BBBB将数值符号扩展为64位后赋值个寄存器对vAA
const-wide vAA, #+BBBBBBBBBBBBBBBB将数值赋给寄存器对vAA
const-wide/high16 vAA, #+BBBB000000000000将数值右边零扩展为64位后赋给寄存器对vAA
const-string vAA,string@BBBB通过字符串索引高走字符串赋值给寄存器vAA
const-string/jumbo vAA, string@BBBBBBBB通过字符串索引(较大)构造一个字符串并赋给寄存器vAA
const-class vAA,type@BBBB通过类型索引获取一个类的引用赋值给寄存器vAA
const-class/jumbo vAAAA, type@BBBBBBBB通过给定的类型索引获取一个类引用并赋给寄存器vAAAA。这条指令占用两个字节,值为0xooff(Android4.0中新增的指令)
6.3 跳转指令

跳转指令用于从当前地址跳转到指定的偏移处。Dalvik指令集中有三种跳转指令:无条件跳转(goto),分支跳转(switch)与条件跳转(if)。

goto +AA            #无条件跳转到指定偏移处,偏移量AA不能为0。
packed-switch vAA, +BBBBBBBB  #分支跳转指令。vAA寄存器为switch分支中需要判断的值BBBBBBBB指向一个packed-switch-payload格式的偏移表,表中的值是有规律递增的。
sparse-switch vAA, +BBBBBBBB  #分支跳转指令。vAA寄存器为switch分支中需要判断的值,BBBBBBBB指向一个sparse-switch-payload格式的偏移表,表中的值是无规律的偏移量。

if-eq   #如果vA等于vB则跳转。Java语法表示为“if(vA == vB)”
if-ne  #如果vA不等于vB则跳转。Java语法表示为“if(vA != vB)”
if-lt  #如果vA小于vB则跳转。Java语法表示为“if(vA < vB)”
if-ge  #如果vA大于等于vB则跳转。Java语法表示为“if(vA >= vB)”
if-gt  #如果vA大于vB则跳转。Java语法表示为“if(vA > vB)”
if-le  #如果vA小于等于vB则跳转。Java语法表示为“if(vA <= vB)”

if-eqz  #如果vAA为0则跳转。Java语法表示为“if(vAA == 0)”
if-nez  #如果vAA不为0则跳转。Java语法表示为“if(vAA != 0)”
if-ltz  #如果vAA小于0则跳转。Java语法表示为“if(vAA < 0)”
if-gez  #如果vAA大于等于0则跳转。Java语法表示为“if(vAA >= 0)”
if-gtz  #如果vAA大于0则跳转。Java语法表示为“if(vAA > 0)”
if-lez  #如果vAA小于等于0则跳转。Java语法表示为“if(vAA <= 0)”
6.4 比较指令
cmpl-float   #比较两个单精度浮点数。如果vBB寄存器大于vCC寄存器,结果为-1,相等则结果为0,小于的话结果为1
cmpg-float   #比较两个单精度浮点数。如果vBB寄存器大于vCC寄存器,则结果为1,相等则结果为0,小于的话结果为-1
6.5 四则运算
add-type  #vBB寄存器与vCC寄存器值进行加法运算(vBB + vCC)
sub-type  #vBB寄存器与vCC寄存器值进行减法运算(vBB - vCC)
mul-type  #vBB寄存器与vCC寄存器值进行乘法运算(vBB * vCC)
div-type  #vBB寄存器与vCC寄存器值进行除法运算(vBB / vCC
rem-type  #vBB寄存器与vCC寄存器值进行模运算(vBB % vCC)
and-type  #vBB寄存器与vCC寄存器值进行与运算(vBB & vCC)
or-type  #vBB寄存器与vCC寄存器值进行或运算(vBB | vCC)
xor-type  #vBB寄存器与vCC寄存器值进行异或运算(vBB ^ vCC)
shl-type  #vBB寄存器值(有符号数)左移vCC位(vBB << vCC )
shr-type  #vBB寄存器值(有符号)右移vCC位(vBB >> vCC)
ushr-type  #vBB寄存器值(无符号数)右移vCC位(vBB >>> vCC)
6.6 锁指令

锁指令多用在多线程程序中对同一对象的操作。Dalvik指令集中有两条锁指令:

monitor-enter vAA: 为指定的对象获取锁.
monitor-exit vAA: 释放指定的对象的锁.

如java的com.biyou.test类需要加锁:

java代码:

synchronized (test.class)
{
...
}

smali代码:

const-class v0, Lcom/biyou/test;
monitor-enter v0
...
monitor-exit v0
6.7 数据转换指令

数据转换指令用于将一种类型的数值转换成另一种类型。它的格式为“unop vA, vB”,vB寄存器或vB寄存器对存放需要转换的数据,转换后的结果保存在vA寄存器或vA寄存器对中。

•“neg-int”:对整型数求补。
•“not-int”:对整型数求反。
•“neg-long”:对长整型数求补。
•“not-long”:对长整型数求反。
•“neg-float”:对单精度浮点型数求补。
•“neg-double”:对双精度浮点型数求补。
•“int-to-long”:将整型数转换为长整型。
•“int-to-float”:将整型数转换为单精度浮点型数。
•“int-to-dobule”:将整型数转换为双精度浮点数。
•“long-to-int”:将长整型数转换为整型。
•“long-to-float”:将长整型数转换为单精度浮点型。
•“long-to-double”:将长整型数转换为双精度浮点型。
•“float-to-int”:将单精度浮点数转换为整型。
•“float-to-long”:将单精度浮点数转换为长整型数。
•“float-to-double”:将单精度浮点数转换为双精度浮点型数。
•“double-to-int”:将双精度浮点数转换为整型。
•“double-to-long”:将双精度浮点数转换为长整型。
•“double-to-float”:将双精度浮点数转换为单精度浮点型。
•“int-to-byte”:将整型转换为字节型。
•“int-to-char”:将整型转换为字符型。
•“int-to-short”:将整型转换为短整型。

6.8 数据运行指令

数据运算指令包括算术运算指令与逻辑运算指令。算术运算指令主要进行数值间如加,减,乘,除,模,移位等运算。逻辑运算指令主要进行数值间与,或,非,抑或等运算。数据运算指令有如下四类(数据运算时可能是在寄存器或寄存器对间进行,下面的指令作用讲解时使用寄存器来描述):

•“binop vAA, vBB, vCC”:将vBB寄存器与vCC寄存器进行运算,结果保存到vAA寄存器。
•“binop/2addr vA, vB”:将vA寄存器与vB寄存器进行运算,结果保存到vA寄存器。
•“binop/lit16 vA, vB, #+CCCC”:将vB寄存器与常量 CCCC进行运算,结果保存到vA寄存器。
•“binop/lit8 vAA, vBB, #+CC”:将vBB寄存器与常量CC进行运算,结果保存到vAA寄存器。

后面3类指令比第1类指令分别多出了2addr,lit16,lit8等指令后缀。四类指令中基础字节码相同的指令执行的运算操作是类似的,第1类指令中,根据数据的类型不同会在基础字节码后面加上数据类型后缀,如 -int 或 -long 分别表示操作的数据类型为整型与长整型。第1类指令可归类如下:

•“add-type”:vBB寄存器与vCC寄存器值进行加法运算(vBB + vCC)
•”sub-type”:vBB寄存器与vCC寄存器值进行减法运算(vBB - vCC)
•”mul-type”:vBB寄存器与vCC寄存器值进行乘法运算(vBB * vCC)
•”div-type”:vBB寄存器与vCC寄存器值进行除法运算(vBB / vCC)
•”rem-type”:vBB寄存器与vCC寄存器值进行模运算(vBB % vCC)
•”and-type”:vBB寄存器与vCC寄存器值进行与运算(vBB & vCC)
•”or-type”:vBB寄存器与vCC寄存器值进行或运算(vBB | vCC)
•”xor-type”:vBB寄存器与vCC寄存器值进行异或运算(vBB ^ vCC)
•”shl-type”:vBB寄存器值(有符号数)左移vCC位(vBB << vCC )
•”shr-type”:vBB寄存器值(有符号)右移vCC位(vBB >> vCC)
•”ushr-type”:vBB寄存器值(无符号数)右移vCC位(vBB >>> vCC)
其中基础字节码后面的-type可以是-int,-long, -float,-double。后面3类指令与之类似。

7. smali与java 7.1 简单语法
.field  定义变量
.method  方法
.parameter  方法参数
.prologue  方法开始

.line 12  此方法位于第12行
const/high16  v0, 0x7fo3 把0x7fo3赋值给v0

invoke-direct  调用函数
return-void  函数返回void
invoke-static  调用静态函数
invoke-super  调用父函数
.end method  函数结束

new-instance  创建实例
iput-object  对象赋值
iget-object  调用对象
7.2 java与smali对比
Smali                  JAVA
V                      void
Z                      boolean
B                      byte
S                      short
C                      char
I                      int
J                      long
F                      float
D                      double

引用类型
L                      对象
[                      数组

先看一个smali代码熟悉下

java代码
        private boolean show(){
                boolean tempFlag = ((3-2)==1)? true : false;
                if (tempFlag) {
                    return true;
                }else{
                    return false;
                }
            }

转换smali代码
.method private show()Z
    .locals 2
    .prologue           //方法开始
    .line 22
    const/4 v0, 0x1     // v0赋值为1

    .line 24
    .local v0, tempFlag:Z
    if-eqz v0, :cond_0            // 判断v0是否等于0, 不符合条件向下走, 符合条件执行cond_0分支

    .line 25
    const/4 v1, 0x1            // 符合条件分支

    .line 27
    :goto_0
    return v1

    :cond_0
    const/4 v1, 0x0            // cond_0分支

    goto :goto_0
.end method
8 函数 8.1 函数的定义

smali的方法声明使用的.method指令,方法分为直接方法和虚方法两种。

函数定义格式为:

.method public/private [static][final] methodName()<类型>
.end 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 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
//===================================================================
8.2 smali函数调用

smali中的函数和成员变量一样也分为两种类型,direct method和virtual method。

direct method代表的函数是private的,virtual method则代表的是public和protected,所以在调用函数时有invoke-direct和invoke-virtual,invoke-static,invoke-super及invoke-interface等几种不同的指令。对于参数多于4个的函数,调用的形式为invoke-XXXXX/range

invoke-static表示调用的是静态函数。例如invoke-static {},Landroid/os/Debug;->waitForDebugger()V;。

invoke-static后面的{}表示调用此函数的实例和参数列表,由于这个方法是static的也不需要参数,所以{}为空。

const-string v0,"NDKLIB"
invoke-static {v0},Ljava/lang/System;->lodaLibrary(Ljava/lang/Sting;)V

这两行指令表示调用static void System.loadlibrary(String)方法。

invoke-super调用父类方法的指令,生命周期方法中用的较多。

invoke-direct {p0},Landroid/app/TabActivity;->()V 这个init()方法就是定义在TabActivity中的private方法。

sget-object v0,Lcom/a;->b:Lcom/c;
invoke-virtual {v0,v1},Lcom/c;->Messages(Ljava/lang/Object;)V

v0是b:Lcom/c ,v1是传递给void Messages(Object)方法的参数。

如果函数的参数多于4个(不包括4个)时调用方法的指令后跟/range ,例如:

invoke-direct/range {v0...v5},Lcmb/pb/ui/PBContainerActivity;->h(ILjava/lang/CharSequence;Ljava/lang/String;Landroid/content/Intent;I)Z

需要传递v0到v5共6个参数,{}中采用省略号形式,且连续。

smali中函数结果的返回操作
java中调用函数和返回函数结果可以用一条语句完成,但在smail中则需要分开完成,如果调用的函数返回非void,那么还需要用到move-result(返回基本数据类型)或者move-result-object(返回对象类型)。

const-string v0,"aaa"
invoke-static {v0},Lcmb/pbi;->t(Ljava/lang/String;)Ljava/lang/String;
move-result-object v2

其中v2就是保存调用t方法后返回的String类型的字符串。

数组:

整数
I     int 
[I    int[]
[[I   int[][]
[[[I  int[][]

字符串
[L    String

例1:

LpackageName/name/ObjectName;->MethodName(III)Z

MethodName为具体的方法名
(III)Z是方法的签名部分
括号内的III为方法的参数:在此为(int,int,int)
Z表示方法的返回类型:boolean类型

例2:

method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/string
==
String method(int,int[][],int,String,Object)

字段:

Lpackage/name/ObjectName;->FileName:Ljava/lang/String;

类型(Lpackage/name/ObjectName;),字段名(FileName),字段类型(Ljava/lang/String;)
8.3 返回

返回指令指的是函数结尾时运行的最后一条指令。它的基础字节码为teturn,共有以下四条返回指令:

•return-void:表示函数从一个void方法返回。
•return vAA:表示函数返回一个32位非对象类型的值,返回值寄存器为8位的寄存器vAA。
•return-wide vAA:表示函数返回一个64位非对象类型的值,返回值为8位的寄存器对vAA。
•return-object vAA:表示函数返回一个对象类型的值。返回值为8位的寄存器vAA。

9 注解

如果一个类使用了注解,那么smali中会使用.annotation指令。

#annotations
.annotation [注解属性] <注解类名>
    [注解字段 = 值]
.end annotation1234

注解的作用范围可以是类、方法或者字段。如果注解的作用范围是类,.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
//===================================================================
10. 逆向分析工具 10.1 Dex到Java反编译器

工具:jadx-gui
jadx反编译功能很强大,支持apk直接反编译查看,类,方法,变量之间的引用跳转,代码颜色区别提示,检索功能也很给力。

https://github.com/skylot/jadx/releases

10.2 资源文件xml破解

工具:AXMLPrinter2.jar
上面解压缩后,除了获取到dex文件外,还有res资源文件夹,里面有布局文件啊,drawable啊等等,图片资源是可以直接查看了,但布局文件和AndroidManifest.xml等xml格式的打开后都会出现乱码,我们接下来以AndroidManifest.xml为例进行破解,AXMLPrinter2.jar和AndroidManifest.xml至于相同目录下,在当前目录按住shift键点击鼠标右键,选择“在此处打开命令窗口”(win7系统)或“在此处打开Powershell窗口”(win10系统),输入命令:

java -jar AXMLPrinter2.jar AndroidManifest.xml > AndroidManifest.txt
注意:“AXMLPrinter2.jar”为可执行文件,需要安装jave运行库(JRE)才能运行。
至此,我们得到了AndroidManifest.txt,可正常打开查看。

10.3 将dex文件转换为jar文件

工具:dex2jar-2.1
把上面获取到的dex文件——classes.dex放到 dex2jar-2.0 根目录中,然后在 dex2jar-2.0 根目录下打开命令行窗口,然后输入命令:

d2j-dex2jar.bat classes.dex
执行完毕后,会生成一个classes-dex2jar.jar文件

Releases · pxb1988/dex2jar · GitHub

10.4 查看classes-dex2jar.jar

工具:jd-gui
直接打开jd-gui.exe,然后在工具中打开上面生成的classes-dex2jar.jar文件,图形化界面非常nice,操作也非常简单,至此我们直接可以查看class文件了,如下图所示:

jd-gui工具查看class文件

Java Decompiler (java-decompiler.github.io)

10.5 反编译修改

工具:apktool
将待反编译的apk文件与apktool.bat、apktool.jar置于同一目录下,在当前目录打开命令行窗口,输入命令:

apktool.bat d -r test.apk -o test
说明:apktool.bat d -r [待反编译的apk] -o [反编译之后存放文件夹]
反编译结果
注意到上面红色箭头smali目录,里面所有文件都是以“.smali”为后缀的,这是反编译后的格式,如果你需要对其进行修改的话,你需要学一部分smali语法,这里就不做展开了。

Apktool - A tool for reverse engineering 3rd party, closed, binary Android apps. (ibotpeaches.github.io)

10.6 二次打包

工具:apktool, jarsigner
第一步,通过apktool直接解析apk文件。

apktool d apk_path -o output_path

第二步,修改smali文件。在第一步中,会将dex转成smali格式的代码。对于smali语言不太熟悉的话,可以先反编译,找到对应的类以及方法后,再去smali文件夹中直接定位并修改。

第三步,重新打包。

apktool b old_output_path -o new_apk_path

第四部,签名。因为第三步中生成的apk包是没有签名的,无法安装到手机中,需要手动对它进行签名。JRE自带签名工具,地址%jre_path%/bin下。

jarsigner -keystore keystore_path unsign.apk keystore_alias
11 参考文档
  1. [smali语法]smali基础学习 - wulameng (mengwula.com)
  2. Android逆向基础:Smali语法 - 简书 (jianshu.com)
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/683965.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号