smali修改Textview+smali常用语法

来源:互联网 发布:淘宝充值米币靠谱吗 编辑:程序博客网 时间:2024/05/29 02:41

Smali语言其实就是Davlik的寄存器语言;Smali语言就是android的应用程序.apk通过apktool反编译出来的都有一个smali文件夹,里面都是以.smali结尾的文件的展示语言,我们可以通过分析修改Smali程序达到修改源程序的目的。

  下面给出修改一个android工程其中Textview的教程。


   如图,下面是android工程listedittest的目录:

              

 

               通过apktool反编译出来的Smali文件夹里面的目录

                

 

              先打开一个主类MainActivity.smali文件,先来浏览一下里面的语言,再来说说smali的语法规则:


.class public Lcom/myandroid/listedittest/MainActivity;.super Landroid/app/Activity;.source "MainActivity.java"# static fields.field static str:[Ljava/lang/String;# instance fields.field private bt_cancel:Landroid/widget/Button;.field private bt_confirmdelete:Landroid/widget/Button;.field private bt_deselectall:Landroid/widget/Button;.field private bt_selectall:Landroid/widget/Button;.field private checkNum:I.field private list:Ljava/util/ArrayList;    .annotation system Ldalvik/annotation/Signature;        value = {            "Ljava/util/ArrayList",            "<",            "Ljava/util/HashMap",            "<",            "Ljava/lang/String;",            "Ljava/lang/String;",            ">;>;"        }    .end annotation.end field.field private lv:Landroid/widget/ListView;.field private mAdapter:Lcom/myandroid/listedittest/MyAdapter;.field private tv_show:Landroid/widget/TextView;# direct methods.method static constructor <clinit>()V    .locals 3    .prologue    .line 30    const/16 v0, 0xd    new-array v0, v0, [Ljava/lang/String;    const/4 v1, 0x0    const-string v2, "data1"    aput-object v2, v0, v1    const/4 v1, 0x1    const-string v2, "data2"    aput-object v2, v0, v1    const/4 v1, 0x2    const-string v2, "data3"    aput-object v2, v0, v1    const/4 v1, 0x3    const-string v2, "data4"    aput-object v2, v0, v1    const/4 v1, 0x4    const-string v2, "data5"    aput-object v2, v0, v1    const/4 v1, 0x5    const-string v2, "data6"    aput-object v2, v0, v1    const/4 v1, 0x6    const-string v2, "7"    aput-object v2, v0, v1    const/4 v1, 0x7    const-string v2, "data8"    aput-object v2, v0, v1    const/16 v1, 0x8    const-string v2, "data9"    aput-object v2, v0, v1    const/16 v1, 0x9    const-string v2, "data10"    aput-object v2, v0, v1    const/16 v1, 0xa    const-string v2, "data11"    aput-object v2, v0, v1    const/16 v1, 0xb    const-string v2, "data12"    aput-object v2, v0, v1    const/16 v1, 0xc    const-string v2, "data13"    aput-object v2, v0, v1    sput-object v0, Lcom/myandroid/listedittest/MainActivity;->str:[Ljava/lang/String;    return-void.end method.method public constructor <init>()V    .locals 0    .prologue    .line 20    invoke-direct {p0}, Landroid/app/Activity;-><init>()V    return-void.end method.method static synthetic access$000(Lcom/myandroid/listedittest/MainActivity;)Ljava/util/ArrayList;    .locals 1    .parameter "x0"    .prologue    .line 20    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->list:Ljava/util/ArrayList;    return-object v0.end method.method static synthetic access$100(Lcom/myandroid/listedittest/MainActivity;)I    .locals 1    .parameter "x0"    .prologue    .line 20    iget v0, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    return v0.end method.method static synthetic access$102(Lcom/myandroid/listedittest/MainActivity;I)I    .locals 0    .parameter "x0"    .parameter "x1"    .prologue    .line 20    iput p1, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    return p1.end method.method static synthetic access$108(Lcom/myandroid/listedittest/MainActivity;)I    .locals 2    .parameter "x0"    .prologue    .line 20    iget v0, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    add-int/lit8 v1, v0, 0x1    iput v1, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    return v0.end method.method static synthetic access$110(Lcom/myandroid/listedittest/MainActivity;)I    .locals 2    .parameter "x0"    .prologue    .line 20    iget v0, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    add-int/lit8 v1, v0, -0x1    iput v1, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    return v0.end method.method static synthetic access$200(Lcom/myandroid/listedittest/MainActivity;)V    .locals 0    .parameter "x0"    .prologue    .line 20    invoke-direct {p0}, Lcom/myandroid/listedittest/MainActivity;->dataChanged()V    return-void.end method.method static synthetic access$300(Lcom/myandroid/listedittest/MainActivity;)Landroid/widget/TextView;    .locals 1    .parameter "x0"    .prologue    .line 20    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->tv_show:Landroid/widget/TextView;    return-object v0.end method.method private dataChanged()V    .locals 3    .prologue    .line 157    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->mAdapter:Lcom/myandroid/listedittest/MyAdapter;    invoke-virtual {v0}, Lcom/myandroid/listedittest/MyAdapter;->notifyDataSetChanged()V    .line 159    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->tv_show:Landroid/widget/TextView;    new-instance v1, Ljava/lang/StringBuilder;    invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V    const-string v2, "\ufffd\ufffd\u0461\ufffd\ufffd"    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;    move-result-object v1    iget v2, p0, Lcom/myandroid/listedittest/MainActivity;->checkNum:I    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;    move-result-object v1    const-string v2, "\ufffd\ufffd"    invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;    move-result-object v1    invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;    move-result-object v1    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V    .line 160    return-void.end method.method private initDate()V    .locals 4    .prologue    .line 146    const/4 v0, 0x0    .local v0, i:I    :goto_0    sget-object v2, Lcom/myandroid/listedittest/MainActivity;->str:[Ljava/lang/String;    array-length v2, v2    if-ge v0, v2, :cond_0    .line 147    new-instance v1, Ljava/util/HashMap;    invoke-direct {v1}, Ljava/util/HashMap;-><init>()V    .line 148    .local v1, map:Ljava/util/HashMap;,"Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;"    const-string v2, "content"    sget-object v3, Lcom/myandroid/listedittest/MainActivity;->str:[Ljava/lang/String;    aget-object v3, v3, v0    invoke-virtual {v1, v2, v3}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;    .line 149    const-string v2, "flag"    const-string v3, "false"    invoke-virtual {v1, v2, v3}, Ljava/util/HashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;    .line 150    iget-object v2, p0, Lcom/myandroid/listedittest/MainActivity;->list:Ljava/util/ArrayList;    invoke-virtual {v2, v1}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z    .line 146    add-int/lit8 v0, v0, 0x1    goto :goto_0    .line 152    .end local v1           #map:Ljava/util/HashMap;,"Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;"    :cond_0    return-void.end method# virtual methods.method public onCreate(Landroid/os/Bundle;)V    .locals 2    .parameter "savedInstanceState"    .prologue    .line 36    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V    .line 37    const v0, 0x7f030001    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->setContentView(I)V    .line 39    const v0, 0x7f080007    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/ListView;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->lv:Landroid/widget/ListView;    .line 40    const v0, 0x7f080003    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/Button;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_selectall:Landroid/widget/Button;    .line 41    const v0, 0x7f080005    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/Button;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_cancel:Landroid/widget/Button;    .line 42    const v0, 0x7f080004    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/Button;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_deselectall:Landroid/widget/Button;    .line 43    const v0, 0x7f080008    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/Button;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_confirmdelete:Landroid/widget/Button;    .line 44    const v0, 0x7f080006    invoke-virtual {p0, v0}, Lcom/myandroid/listedittest/MainActivity;->findViewById(I)Landroid/view/View;    move-result-object v0    check-cast v0, Landroid/widget/TextView;    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->tv_show:Landroid/widget/TextView;    .line 45    new-instance v0, Ljava/util/ArrayList;    invoke-direct {v0}, Ljava/util/ArrayList;-><init>()V    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->list:Ljava/util/ArrayList;    .line 47    invoke-direct {p0}, Lcom/myandroid/listedittest/MainActivity;->initDate()V    .line 49    new-instance v0, Lcom/myandroid/listedittest/MyAdapter;    iget-object v1, p0, Lcom/myandroid/listedittest/MainActivity;->list:Ljava/util/ArrayList;    invoke-direct {v0, v1, p0}, Lcom/myandroid/listedittest/MyAdapter;-><init>(Ljava/util/ArrayList;Landroid/content/Context;)V    iput-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->mAdapter:Lcom/myandroid/listedittest/MyAdapter;    .line 51    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->lv:Landroid/widget/ListView;    iget-object v1, p0, Lcom/myandroid/listedittest/MainActivity;->mAdapter:Lcom/myandroid/listedittest/MyAdapter;    invoke-virtual {v0, v1}, Landroid/widget/ListView;->setAdapter(Landroid/widget/ListAdapter;)V    .line 53    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_selectall:Landroid/widget/Button;    new-instance v1, Lcom/myandroid/listedittest/MainActivity$1;    invoke-direct {v1, p0}, Lcom/myandroid/listedittest/MainActivity$1;-><init>(Lcom/myandroid/listedittest/MainActivity;)V    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V    .line 67    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_cancel:Landroid/widget/Button;    new-instance v1, Lcom/myandroid/listedittest/MainActivity$2;    invoke-direct {v1, p0}, Lcom/myandroid/listedittest/MainActivity$2;-><init>(Lcom/myandroid/listedittest/MainActivity;)V    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V    .line 82    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_deselectall:Landroid/widget/Button;    new-instance v1, Lcom/myandroid/listedittest/MainActivity$3;    invoke-direct {v1, p0}, Lcom/myandroid/listedittest/MainActivity$3;-><init>(Lcom/myandroid/listedittest/MainActivity;)V    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V    .line 101    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->bt_confirmdelete:Landroid/widget/Button;    new-instance v1, Lcom/myandroid/listedittest/MainActivity$4;    invoke-direct {v1, p0}, Lcom/myandroid/listedittest/MainActivity$4;-><init>(Lcom/myandroid/listedittest/MainActivity;)V    invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V    .line 119    iget-object v0, p0, Lcom/myandroid/listedittest/MainActivity;->lv:Landroid/widget/ListView;    new-instance v1, Lcom/myandroid/listedittest/MainActivity$5;    invoke-direct {v1, p0}, Lcom/myandroid/listedittest/MainActivity$5;-><init>(Lcom/myandroid/listedittest/MainActivity;)V    invoke-virtual {v0, v1}, Landroid/widget/ListView;->setOnItemClickListener(Landroid/widget/AdapterView$OnItemClickListener;)V    .line 142    return-void.end method

上面的smali语言对应的java类就是Mainactivity.java类如下:


package com.myandroid.listedittest;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Button;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import com.myandroid.listedittest.MyAdapter.ViewHolder;public class MainActivity extends Activity {    private ListView lv;    private MyAdapter mAdapter;    private ArrayList<HashMap<String, String>> list;    private Button bt_selectall;    private Button bt_cancel;    private Button bt_deselectall;    private Button bt_confirmdelete;    private int checkNum; // 记录选中的条目数量    private TextView tv_show;// 用于显示选中的条目数量    static String str[] = { "data1", "data2", "data3", "data4", "data5", "data6",         "7", "data8", "data9", "data10", "data11", "data12", "data13" };    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        /* 实例化各个控件 */        lv = (ListView) findViewById(R.id.lv);        bt_selectall = (Button) findViewById(R.id.bt_selectall);        bt_cancel = (Button) findViewById(R.id.bt_cancelselectall);        bt_deselectall = (Button) findViewById(R.id.bt_deselectall);        bt_confirmdelete = (Button) findViewById(R.id.bt_confirmdelete);        tv_show = (TextView) findViewById(R.id.tv);        list = new ArrayList<HashMap<String, String>>();        // 为Adapter准备数据        initDate();        // 实例化自定义的MyAdapter        mAdapter = new MyAdapter(list, this);        // 绑定Adapter        lv.setAdapter(mAdapter);        // 全选按钮的回调接口        bt_selectall.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 遍历list的长度,将MyAdapter中的map值全部设为true                for (int i = 0; i < list.size(); i++) {                    list.get(i).put("flag", "true");                }                // 数量设为list的长度                checkNum = list.size();                // 刷新listview和TextView的显示                dataChanged();            }        });        // 取消按钮的回调接口        bt_cancel.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 遍历list的长度,将已选的按钮设为未选                for (int i = 0; i < list.size(); i++) {                    if (list.get(i).get("flag").equals("true")) {                        list.get(i).put("flag", "false");                        checkNum--;// 数量减1                    }                }                // 刷新listview和TextView的显示                dataChanged();            }        });        // 反选按钮的回调接口        bt_deselectall.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                // 遍历list的长度,将已选的设为未选,未选的设为已选                for (int i = 0; i < list.size(); i++) {                    if (list.get(i).get("flag").equals("true")) {                        list.get(i).put("flag", "false");                        checkNum--;                    } else {                        list.get(i).put("flag", "true");                        checkNum++;                    }                }                // 刷新listview和TextView的显示                dataChanged();            }        });        // 确认删除的回调接口        bt_confirmdelete.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                Iterator<HashMap<String, String>> iterator = list.iterator();                while (iterator.hasNext()) {                    HashMap<String, String> temp = iterator.next();                    if (temp.get("flag").equals("true")) {                        iterator.remove();                    }                }                checkNum = 0;                // 通知列表数据修改                dataChanged();            }        });        // 绑定listView的监听器        lv.setOnItemClickListener(new OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,                    long arg3) {                Toast.makeText(MainActivity.this, "当前位置:" + arg2,                        Toast.LENGTH_SHORT);                // 取得ViewHolder对象,这样就省去了通过层层的findViewById去实例化我们需要的cb实例的步骤                ViewHolder holder = (ViewHolder) arg1.getTag();                // 改变CheckBox的状态                holder.cb.toggle();                // 将CheckBox的选中状况记录下来                // 调整选定条目                if (holder.cb.isChecked() == true) {                    list.get(arg2).put("flag", "true");                    checkNum++;                } else {                    list.get(arg2).put("flag", "false");                    checkNum--;                }                // 用TextView显示                tv_show.setText("已选中" + checkNum + "项");            }        });    }    // 初始化数据    private void initDate() {        for (int i = 0; i < str.length; i++) {            HashMap<String, String> map = new HashMap<String, String>();            map.put("content", str[i]);            map.put("flag", "false");            list.add(map);        }    }    // 刷新listview和TextView的显示    private void dataChanged() {        // 通知listView刷新        mAdapter.notifyDataSetChanged();        // TextView显示最新的选中数目        tv_show.setText("已选中" + checkNum + "项");    }}

 通过对比发现基本的方法名称没有改变,多了一个.method public constructor <init>()V表示该类的不带参数缺省的构造方法,onCreate()方法是以.method public onCreate(Landroid/os/Bundle;)V开始,.end method结束;方法的表示形式就是这个样子的。


       下面介绍一下Smali代码注入,原理:在已有APK或JAR包中插入一些Dalvik虚拟机的指令,从而改变原有程序的路径和行为。


如果想修改以下Textview的值,我们可以在smali文件里搜索“tv_show"。


对应



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



对应



-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


对应



由以上分析可得

第44行执行初始化工作,并且在smali文件的第407行把参数p0的内容打入到寄存器v0。于是我们可以通过修改p0来修改textview的值。

    iput-object v0, p0,    Lcom/myandroid/listedittest/MainActivity;->tv_show:Landroid/widget/TextView;

修改为

    iput-object v0, "我是修改后的内容",    Lcom/myandroid/listedittest/MainActivity;->tv_show:Landroid/widget/TextView;

      再通过apktool  b  需要打包回去的文件

     打包回去就在反编译后的Mainactivity文件里生成一个dist文件夹,里面就有打包的Mainactivity.apk。如图

     

       注意:要直接安装到手机或模拟器是不行的,需要签名,可以通过APKSign_gr这个签名软件来签名,也可以通过其他方式签名,在签名的时候如果一开始在eclipse里运行生成的apk是使用了ADB的debug权限签名,如果使用APKSign_gr这个签名,需要在手机或模拟器中把原来的apk卸载掉,在安装新的apk,就ok了,这个是解决安装apk时“INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES”这个错误的。




Smali语法简单介绍如下

       Davlik字节码中,寄存器都是32位的,能够支持任何类型,64位类型(Long/Double)用2个寄存器表示;

       Dalvik字节码有两种类型:原始类型;引用类型(包括对象和数组)

              原始类型v   void  只能用于返回值类型

                                Z   boolean

                                B   byte

                                S   short

                                C   char

                                 I    int

                                J    long(64位)

                                F   float

                                D   double(64位)

              对象类型Lpackage/name/ObjectName;  相当于java中的package.name.ObjectName;解释如下:

                                 L:表示这是一个对象类型

                                 package/name:该对象所在的包

                                 ;:表示对象名称的结束

              数组的表示形式:

                                  [I  :表示一个整形的一维数组,相当于java的int[];

                                  对于多维数组,只要增加[ 就行了,[[I = int[][];注:每一维最多255个; 

               对象数组的表示形式:

                                   [Ljava/lang/String    表示一个String的对象数组;

  

               方法的表示形式:

                                   Lpackage/name/ObjectName;——>methodName(III)Z  详解如下:

                                   Lpackage/name/ObjectName  表示类型

                                   methodName   表示方法名

                                   III   表示参数(这里表示为3个整型参数)

                                   说明:方法的参数是一个接一个的,中间没有隔开;

                

                 字段的表示形式:

                                    Lpackage/name/ObjectName;——>FieldName:Ljava/lang/String;

                                    即表示: 包名,字段名和各字段类型

 

                  有两种方式指定一个方法中有多少寄存器是可用的:

                                     .registers  指令指定了方法中寄存器的总数

                                     .locals        指令表明了方法中非参寄存器的总数,出现在方法中的第一行

 

        方法的传参:

                当一个方法被调用的时候,方法的参数被置于最后N个寄存器中;

                          例如,一个方法有2个参数,5个寄存器(v0~v4)

                                      那么,参数将置于最后2个寄存器(v3和v4)

                 非静态方法中的第一个参数总是调用该方法的对象;

                 说明:对于静态方法除了没有隐含的this参数外,其他都一样

 

          寄存器的命名方式:

                  V命名

                  P命名  第一个寄存器就是方法中的第一个参数寄存器

                  比较:使用P命名是为了防止以后如果在方法中增加寄存器,需要对参数寄存器重新进行编号的缺点

                  特别说明一下:Long和Double类型是64位的,需要2个寄存器

                           例如:对于非静态方法

                                       LMyObject——>myMethod(IJZ)V;

                                       有4个参数:LMyObject,int,long,bool;   需要5个寄存器来存储参数;

                                                            P0    this

                                                            P1    I (int)

                                                            P2,P3  J (long)

                                                            P4    Z(bool)


0 0