Android-smali语法学习

Posted jzdwajue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android-smali语法学习相关的知识,希望对你有一定的参考价值。

转载请标明出处:http://blog.csdn.net/goldenfish1919/article/details/40821415

下面内容来自:http://bbs.pediy.com/showthread.php?t=151769

dalvik字节码有两种类型,原始类型和引用类型。

对象和数组是引用类型。其他都是原始类型。
V  void,仅仅能用于返回值类型
Z  boolean
B  byte
S  short
C  char
I  int
J  long(64位)
F  float
D  double(64位)
对象以Lpackage/name/ObjectName;的形式表示。
前面的L表示这是一个对象类型。package/name/是该对象所在的包,ObjectName是对象的名字,“;”表示对象名称的结束。
相当于java中的package.name.ObjectName。比如:Ljava/lang/String;相当于java.lang.String

数组的表示形式
?  [I——表示一个整型一维数组。相当于java中的int[]。


?  对于多维数组。仅仅要添加[即可了。[[I相当于int[][],[[[I相当于int[][][]。注意每一维的最多255个。


?  对象数组的表示:[Ljava/lang/String;表示一个String对象数组。
方法
表示形式:Lpackage/name/ObjectName;->MethodName(III)Z
?  Lpackage/name/ObjectName;表示类型,MethodName是方法名。III为參数(在此是3个整型參数),Z是返回类型(bool型)。
?  方法的參数是一个接一个的,中间没有隔开。
一个更复杂的样例:
method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
在java中则为:
String method(int, int[][], int, String, Object[])
字段
表示形式:
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
即包名,字段名和各字段类型。


寄存器
在dalvik字节码中。寄存器都是32位的,可以支持不论什么类型。64位类型(Long和Double型)用2个寄存器表示。


有两种方式指定一个方法中有多少寄存器是可用的。.registers指令指定了方法中寄存器的总数。.locals指令表明了方法中非參寄存器的数量。
方法的传參
?  当一个方法被调用的时候,方法的參数被置于最后N个寄存器中。假设一个方法有2个參数,5个寄存器(v0-v4),那么參数将置于最后2个寄存器——v3和v4。
?  非静态方法中的第一个參数总是调用该方法的对象。
比如。非静态方法LMyObject;->callMe(II)V有2个整型參数。另外另一个隐含的LMyObject;參数,所以总共同拥有3个參数。
假如在该方法中指定了5个寄存器(v0-v4)。以.registers方式指定5个或以.locals方式指定2个(即2个local寄存器+3个參数寄存器)。
当该方法被调用的时候,调用该方法的对象(即this引用)存放在v2中,第一个整型參数存放在v3中,第二个整型參数存放在v4中。
对于静态方法除了没有隐含的this參数外其他都一样。


寄存器的命名方式
有两种方式——V命名方式和P命名方式。P命名方式中的第一个寄存器就是方法中的第一个參数寄存器。
在下表中我们用这两种命名方式来表示上一个样例中有5个寄存器和3个參数的方法。
v0    第一个local register
v1    第二个local register
v2  p0  第一个parameter register
v3  p1  第二个parameter register
v4  p2  第三个parameter register
你能够用不论什么一种方式来引用參数寄存器——他们没有不论什么区别。


注意:baksmali默认对參数寄存器使用P命名方式。假设想使用V命名方式,能够使用-pl—no-parameter-registers选项。
使用P命名方式是为了防止以后假设要在方法中添加寄存器,须要对參数寄存器又一次进行编号的缺点。


Long/Double值
Long和double类型是64位的,须要2个寄存器(切记切记)。
比如,对于非静态方法LMyObject;->MyMethod(IJZ)V,參数各自是LMyObject;,int。long,bool。故该方法须要5个寄存器来存储參数。

p0  this
p1  I
p2,p3  J
p4  Z 

补充:
# static fields             定义静态变量的标记
# instance fields        定义实例变量的标记
# direct methods       定义静态方法的标记
# virtual methods      定义非静态方法的标记
构造函数的返回类型为V,名字为<init>。
 
if-eq p1, v0, :cond_8 表示假设p1和v0相等,则运行cond_8的流程:
    :cond_8
    invoke-direct {p0}, Lcom/paul/test/a;->d()V
调用com.paul.test.a的d()方法
if-ne p1, v0, :cond_b 表示不相等则运行cond_b的流程:
    :cond_b
    const/4 v0, 0x0
    invoke-virtual {p0, v0}, Lcom/paul/test/a;->setPressed(Z)V
    invoke-super {p0, p1, p2}, Landroid/view/View;->onKeyUp(ILandroid/view/KeyEvent;)Z
    move-result v0
 
大概意思就是调用com.paul.test.a的setPressed方法。然后再调用父类View的onKeyUp方法。最后 return v0 

举两个样例:

sget-object v5, Lcom/google/youngandroid/runtime;->Lit227:Lgnu/mapping/SimpleSymbol;
获取com.google.youngandroid.runtime中的Lit227字段存入v5寄存器。相当于
gnu.mapping.SimpleSymbol localVariable = com.google.youngandroid.runtime.Lit227;

sput-object v0, Lcom/google/youngandroid/runtime;->Lit78:Lkawa/lang/SyntaxTemplate;
Likewise, this is setting the value of a static field. i.e.
设置com.google.youngandroid.runtime.Lit78的值为v0寄存器中的kawa.lang.SyntaxTemplate类型变量的值。相当于com.google.youngandroid.runtime.Lit78 = kawa.lang.SyntaxTemplate localVariable;
剩下的比較简单你应该能明确了。 

以下我们来看一个简单的样例:

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		TextView textview1 = (TextView) this.findViewById(R.id.text);
		textview1.setText(R.string.hello_world);
	}
}
生成的smali:

.class public Lcom/example/hello/MainActivity;
.super Landroid/app/Activity;
.source "MainActivity.java"


# direct methods
.method public constructor <init>()V
    .locals 0

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

    return-void
.end method


# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 2
    .parameter "savedInstanceState"

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

    .line 19
    const/high16 v1, 0x7f03

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

    .line 20
    const/high16 v1, 0x7f0a

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

    move-result-object v0

    check-cast v0, Landroid/widget/TextView;

    .line 21
    .local v0, textview1:Landroid/widget/TextView;
    const v1, 0x7f070002

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V

    .line 22
    return-void
.end method

我们重点来看下onCreate()这个的方法。

.method protected onCreate(Landroid/os/Bundle;)V
    .locals 2
    .parameter "savedInstanceState"

    .prologue
    .line 18
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V // 这是调用super也就是Activity.onCreate(Bundle) p0:this,p1:Bundle
  
    .line 19
    const/high16 v1, 0x7f03   //把0x7f03这个常量赋值给v1 ,0x7f03就是R.layout.activity_main

    invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->setContentView(I)V // 调用MainActivity.setContentView(v1),p0:this。v1就是R.layout.activity_main

    .line 20
    const/high16 v1, 0x7f0a // 把0x7f0a 这个常量赋值给v1 。0x7f0a 就是R.id.text

    invoke-virtual {p0, v1}, Lcom/example/hello/MainActivity;->findViewById(I)Landroid/view/View;  // 调用View = MainActivity.findViewById(v1)。p0:this。v1:R.id.text

    move-result-object v0  // 把上一个指令的输出结果move到v0,就是给textview1赋值

    check-cast v0, Landroid/widget/TextView;// 把v0强转成TextView

    .line 21
    .local v0, textview1:Landroid/widget/TextView;
    const v1, 0x7f070002    // 把int常量0x7f070002放到v1中,就是读出来R.string.hello_world

    invoke-virtual {v0, v1}, Landroid/widget/TextView;->setText(I)V  //调用TextView.setText(v1) TextView是v0。參数是v1

    .line 22
    return-void
.end method

还是挺简单的一个样例。跟class字节码很向。我们看一下相同的代码在class中和smali中有啥不一样的。

public void multiply(int a, int b){
		int result = a * b;
		System.out.println(result);
	}
smali表示:

# virtual methods
.method public multiply(II)V
    .locals 2
    .parameter "a"
    .parameter "b"

    .prologue
    .line 26
    mul-int v0, p1, p2  // p1和p2相乘,结果放到v0中

    .line 27
    .local v0, result:I
    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;  // 获取System.out的引用。放到v1中

    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(I)V  // 调用PrintStream.println(v0)

    .line 28
    return-void
.end method

class表示:

public void multiply(int, int);
  Code:
   Stack=2, Locals=4, Args_size=3
   0: iload_1                               // 本地变量1压栈
   1: iload_2                               // 本地变量2压栈
   2: imul                                     // 栈中取上面的2个做乘法
   3: istore_3                              // 结果出栈,放到本地变量3中
   4: getstatic#5; //Field java/lang/System.out:Ljava/io/PrintStream; // System.out压栈
   7: iload_3                              // 本地变量3压栈
   8: invokevirtual#6; //Method java/io/PrintStream.println:(I)V   // 调用PrintStream.println
   11: return
  LineNumberTable: 
   line 6: 0
   line 7: 4
   line 8: 11

上面能够非常清晰的看出来,实现相同的功能,基于寄存器要比基于栈的指令数要少。

int result = a * b;在smali中就一条指令,而在class中要4条指令。

关于smali全部指令的格式和含义能够參考:http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html





























































































































































以上是关于Android-smali语法学习的主要内容,如果未能解决你的问题,请参考以下文章

android-smali记录

python 学习python语法的片段

在下面的代码片段中的剩余 ='passthrough' 处的代码中出现语法错误

JSP学习笔记:JSP语法和指令

如何将按钮功能添加到片段中

Java Web学习总结JSP