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代码会容易些,但是没有关系的,结合我在里面的的注释也是能看懂的。最好还是结合我给你推荐的资料看,这里不再多解释。
^_^