我写的这个轮盘赌小游戏(姑且称它为游戏吧),主要玩法是第一次点击屏幕中间的大按钮,会弹出“子弹已上膛”的提示,再次点击会出现“扣动扳机开火吧!”的提示,第三次点击就是“赌命”的时刻了,如果有子弹,会弹出“啪!你被爆头了!”,如果没有,则会弹出“你真幸运”的提示。
游戏很简单,用到了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();
}
}
还是使用apktool d apk文件 这个命令
在分析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的基本流程控制
原文地址:http://blog.csdn.net/c_major/article/details/42143245