首页 > 代码库 > Android逆向分析学习与研究(2)————通过“轮盘赌”简要看看smali的基本流程控制

Android逆向分析学习与研究(2)————通过“轮盘赌”简要看看smali的基本流程控制

1.我写的一个简单的Andriod小Demo,“轮盘赌!”。

我写的这个轮盘赌小游戏(姑且称它为游戏吧),主要玩法是第一次点击屏幕中间的大按钮,会弹出“子弹已上膛”的提示,再次点击会出现“扣动扳机开火吧!”的提示,第三次点击就是“赌命”的时刻了,如果有子弹,会弹出“啪!你被爆头了!”,如果没有,则会弹出“你真幸运”的提示。

技术分享

技术分享


技术分享

技术分享


游戏很简单,用到了Random,代码可能在一些人看来会稍显幼稚,但没关系,游戏不是重点,重点是我们对于这个Android小Demo的逆向,重点是分析smali的流程控制语法。
先看看java代码

package com.example.forreversedemo;

import java.util.Random;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener{

    private Button button;    //按钮
    public int count = 0;    //计数
    public int[] bullet =new int[]{0,0,0,0,0,0};    //用数组来表示弹夹,没子弹时数组元素为0,有子弹时,数组元素之一随机赋值为1
    public int a;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button= (Button) findViewById(R.id.start_count);
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View arg0) {

        switch(arg0.getId()){
            case R.id.start_count:
                Random random = new Random();
                //第一次点击按钮把第一颗子弹上膛
                if(count == 0){
                    MessageBox("子弹已上膛!");
                    bullet[random.nextInt(6)] = 1;
                    count++;
                    break;
                }
                //第2次点击,实现转动轮盘至停止
                if(count == 1){
                    int count1 = -1;
                    for (int i = 0;i<random.nextInt(6)+60000; i++) {
                        count1++;
                        if(count1 == 6)count1 = 0;        //循环遍历弹夹,也可以用while和for来做,这里为分析方便起见
                        a = bullet[count1];
                    }
                    MessageBox("抠动扳机开火吧!");
                    count++;
                    break;
                }
                //第3次点击就是开火!
                if(count == 2){
                    if(a == 1){
                        MessageBox("啪!爆头!");
                        count = 0;
                    }else{
                        MessageBox("你真幸运!");
                        count = 0;
                    }
                }
                break;
            default:
                break;

        }

    }
    //弹出提示,额,这个函数名很熟悉吧,WinAPI啊,开个玩笑
    public void MessageBox(String text){
        Toast.makeText(this, text, Toast.LENGTH_LONG).show();
    }
}

2.逆向这个Demo!

还是使用apktool d apk文件 这个命令
在分析smali时,我参考了一些文章,但重推的是:

  • 唯一的一本书:非虫大大的书《Android软件安全与逆向分析》
  • 文章:我转的文章:apk反汇编之smali语法(转)
    其中非虫的书的第三章和我转的那篇文章对于smali的语法进行了详细的介绍,当然你也可以下个官方文档看。总之,初学者在看下面的smali代码时,一定要对照参考我给你推荐的,下面的注释,在我看来,我写的还算详细但避免不了疏漏和有争议的地方,希望大家分析代码时,能多加思考,更欢迎大家的建议和批评指正。

得到smali代码如下
MainActivity.smali (表示在用MK写博客时,贴smali代码用ruby格式最好^_^,有没有更好的方法呢?)

.class public Lcom/example/forreversedemo/MainActivity;#类名
.super Landroid/app/Activity;#父类名
.source "MainActivity.java"#原文件名

# interfaces
.implements Landroid/view/View$OnClickListener;#接口


# instance fields#成员变量
.field public a:I#int 整形变量   I 代表整形

.field public bullet:[I        #整形一维数组,[  代表数组,

.field private button:Landroid/widget/Button;    #按钮

.field public count:I


# direct methods
.method public constructor <init>()V  #构造函数,使用该方法来初始化数据成员和所需资源。
    .locals 1

    .prologue
    .line 11
    invoke-direct {p0}, Landroid/app/Activity;-><init>()V

    .line 14
    const/4 v0, 0x0

    iput v0, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    .line 15
    const/4 v0, 0x6

    new-array v0, v0, [I

    iput-object v0, p0, Lcom/example/forreversedemo/MainActivity;->bullet:[I

    .line 11
    return-void
.end method


# virtual methods    #public void MessageBox();
.method public MessageBox(Ljava/lang/String;)V    # MessageBox()方法,参数为String类型
    .locals 1
    .parameter "text"    #参数名text

    .prologue
    .line 68
    const/4 v0, 0x1

    invoke-static {p0, p1, v0}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v0    #将上一个invoke类型指令包对象结果存入寄存器v0

    invoke-virtual {v0}, Landroid/widget/Toast;->show()V  #Toast.show();弹出提示框

    .line 69
    return-void
.end method

.method public onClick(Landroid/view/View;)V
    .locals 8
    .parameter "arg0"        # onClick()方法,参数为View对象 对象型

    .prologue            #代码起始指令
    #const为数据定义指令
    const/4 v7, 0x0        #将0x6数值符号扩展为32bit后存入寄存器v7,const为

    const/4 v6, 0x6

    const/4 v5, 0x1

    .line 28
    invoke-virtual {p1}, Landroid/view/View;->getId()I        #调用实例的虚方法  getId() 整形;p0为this指针,p1为View对象。v字开头的为局部变量寄存器,p开头的为参数变量寄存器

    move-result v3            #将上一个invoke类型指令操作的单字(16bit)非对象结果返回给v3寄存器;v3为View对象的id,即v3=p1.getId()

    packed-switch v3, :pswitch_data_0    ##switch(v3),通过pswitch_data_0进行匹配,packed-switch为分支跳转指令,而pswitch_data_0为偏移表,表中的值有规律递增.
    ########switch入口开始
    .line 66
    :cond_0
    :goto_0
    return-void                #表示void类型的函数返回
    ########switch入口结束

    .line 30
    :pswitch_0
    new-instance v2, Ljava/util/Random;        #实例化一个Random对象

    invoke-direct {v2}, Ljava/util/Random;-><init>()V    #调用实例的直接方法

    .line 32
    .local v2, random:Ljava/util/Random;
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I    #获取count变量的值存入

    if-nez v3, :cond_1        #如果不等跳转到cond_1

    .line 33
    const-string v3, "\u5b50\u5f39\u5df2\u4e0a\u819b\uff01"    #unicode解码:“子弹已上膛!”


    invoke-virtual {p0, v3}, Lcom/example/forreversedemo/MainActivity;->MessageBox(Ljava/lang/String;)V        #调用MessageBox()函数

    .line 34
    iget-object v3, p0, Lcom/example/forreversedemo/MainActivity;->bullet:[I  #iget-object 获得(读)整形数组

    invoke-virtual {v2, v6}, Ljava/util/Random;->nextInt(I)I    #通过上面  const/4 v6, 0x6  可知,v6的值为6,也就是nextInt(6)

    move-result v4

    aput v5, v3, v4    #aput  数组操作指令,v3为数组初始地址(数组名),v4为数组索引

    .line 35
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    add-int/lit8 v3, v3, 0x1     #count++

    iput v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I  #向count变量写入值(从v3寄存器取出)

    goto :goto_0   #跳转到goto_0

    .line 39
    :cond_1
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    if-ne v3, v5, :cond_4   #v3存的count值,v5存的值为1,上面有提到,若果v3!=v5则跳转到cond_4

    .line 40
    const/4 v0, -0x1  

    .line 41
    .local v0, count1:I        #count1=-1

############for循环开始#################################################


    const/4 v1, 0x0    #v1=0

    .local v1, i:I    #i=0,用v1寄存器代表i
    :goto_1    #标志位
    ####### i<random.nextInt(6)+1000 #############
    invoke-virtual {v2, v6}, Ljava/util/Random;->nextInt(I)I

    move-result v3

    const v4, 0xea60        #v4=60000

    add-int/2addr v3, v4        #v3+v4放在v3的地址里

    if-lt v1, v3, :cond_2    #如果v1小于v3,ze跳转到cond_2,若大于则顺序向下执行

    .line 46
    const-string v3, "\u62a0\u52a8\u6273\u673a\u5f00\u706b\u5427\uff01"   #扣动扳机开火吧!,此行也是跳出for循环后执行的代码

    invoke-virtual {p0, v3}, Lcom/example/forreversedemo/MainActivity;->MessageBox(Ljava/lang/String;)V

    .line 47
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    add-int/lit8 v3, v3, 0x1       #将第二个寄存器的值加上0x1放入v3寄存器,实现自增长count++

    iput v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I       #写入count值,iput中i代表整形值,put代表写入(赋值),同理get代表读取(获得)

    goto :goto_0

    .line 42
    :cond_2
    add-int/lit8 v0, v0, 0x1        #将第二个寄存器的值加上0x1放入v0寄存器,实现自增长,就是count1++

    .line 43
    if-ne v0, v6, :cond_3    #count1!=6  则  跳转到cond_3

    const/4 v0, 0x0            #if(count1==6)count1=0;

    .line 44
    :cond_3
    iget-object v3, p0, Lcom/example/forreversedemo/MainActivity;->bullet:[I    #获得bullet数组

    aget v3, v3, v0        #v0此时代表count1.所以用count1作为数组索引

    iput v3, p0, Lcom/example/forreversedemo/MainActivity;->a:I        #把当前对象(p0为this指针)的a属性赋值(写入)为v3,v3也就是从上面数组获取的值,就是a = bullet[count1];

    .line 41
    add-int/lit8 v1, v1, 0x1        #实现v1的自增,就是i++

    goto :goto_1         #跳转到goto_1,一次循环结束

    .line 51
    .end local v0           #count1:I
    .end local v1           #i:I
    :cond_4
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    const/4 v4, 0x2

    if-ne v3, v4, :cond_0

    .line 52
    iget v3, p0, Lcom/example/forreversedemo/MainActivity;->a:I

    if-ne v3, v5, :cond_5

    .line 53
    const-string v3, "\u556a!\u7206\u5934\uff01"    #啪!爆头

    invoke-virtual {p0, v3}, Lcom/example/forreversedemo/MainActivity;->MessageBox(Ljava/lang/String;)V

    .line 54
    iput v7, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    goto :goto_0

    .line 56
    :cond_5
    const-string v3, "\u4f60\u771f\u5e78\u8fd0\uff01"    #你真幸运

    invoke-virtual {p0, v3}, Lcom/example/forreversedemo/MainActivity;->MessageBox(Ljava/lang/String;)V

    .line 57
    iput v7, p0, Lcom/example/forreversedemo/MainActivity;->count:I

    goto :goto_0

    .line 28
    nop

    :pswitch_data_0
    .packed-switch 0x7f080000        #default
        :pswitch_0
    .end packed-switch
.end method

.method protected onCreate(Landroid/os/Bundle;)V    #onCreate()方法
    .locals 1
    .parameter "savedInstanceState"        #方法参数名

    .prologue            #开始
    .line 19
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 20
    const/high16 v0, 0x7f03    #表示布局文件的实际值,可在R.java文件中找到,活在R$.smali文件中找到

    invoke-virtual {p0, v0}, Lcom/example/forreversedemo/MainActivity;->setContentView(I)V

    .line 21
    const/high16 v0, 0x7f08    #表示button的实际id值,同理可在R.java里找到

    invoke-virtual {p0, v0}, Lcom/example/forreversedemo/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0

    check-cast v0, Landroid/widget/Button;

    iput-object v0, p0, Lcom/example/forreversedemo/MainActivity;->button:Landroid/widget/Button;

    .line 22
    iget-object v0, p0, Lcom/example/forreversedemo/MainActivity;->button:Landroid/widget/Button;

    invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V

    .line 23
    return-void
.end method

这个小demo里用到了一些基本的流程控制语句,比如if 、else 、for、swtich等,而while循环和嵌套循环等没有提及,在以后的文章里会陆陆续续提到,其实大同小异的。
初学者可能会对其中的一些寄存器啊,跳转啊很是头疼,有过X86汇编和ARM汇编基础的同学,看smali代码会容易些,但是没有关系的,结合我在里面的的注释也是能看懂的。最好还是结合我给你推荐的资料看,这里不再多解释。

^_^

Android逆向分析学习与研究(2)————通过“轮盘赌”简要看看smali的基本流程控制