当前位置: 代码迷 >> Android >> Android逆向分析学习与研究(二)————通过“轮盘赌”简要看看smali的基本流程控制
  详细解决方案

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

热度:14   发布时间:2016-04-28 03:12:42.0
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代码会容易些,但是没有关系的,结合我在里面的的注释也是能看懂的。最好还是结合我给你推荐的资料看,这里不再多解释。

^_^

  相关解决方案