2015年11月17日火曜日

smaliの文法まとめ

Androidのapkをapktoolを用いて逆コンパイルするとsmaliファイルが出てきます.
先日,そのsmaliファイルを読む機会があったので,そのときに学んだsmaliの文法をまとめておきます.

注意

  • 説明の簡略化のために,メソッドとフィールドの説明におけるJavaのコードにおいて,文法を守っていない部分があります
  • レジスタの説明などにおいて,一部smaliとdex,Dalvik VMを同一視しています

Javaにおける型は,基本データ型,クラス型,配列型の3種類に分けられます.
それぞれの型について,smaliではどのように型を表記するかを示します.
  • 基本データ型smaliでは,基本データ型は大文字のアルファベット1文字で表します.

    smaliJava
    Vvoid
    Zboolean
    Bbyte
    Sshort
    Cchar
    Iint
    Llong
    Ffloat
    Ddouble

  • クラス型
    smaliでは,Javaと比較してクラス名の表記が以下の点で異なります.
    • クラス名の最初が"L"
    • パッケージ名の区切り文字が"."ではなく"/"
    • クラス名の最後が";"

     例
    smaliJava
    Ljava/lang/Object;java.lang.Object
    Ljava/lang/String;java.lang.String
    Lpackage/name/ObjectName;package.name.ObjectName
  • 配列型
    smaliでは,配列型は"["から始まる文字列で表します.

  • smaliJava
    [Iint[]
    [[Iint[][]
    [[[Iint[][][]
    [Ljava/lang/String;java.lang.String[]

メソッド

smaliでは,メソッドを以下のように表記します.
オブジェクト名->メソッド名(引数の型)戻り値の型

smali
Lpackage/name/ObjectName;->MethodName(III)Z
Java
boolean package.name.ObjectName.MethodName(int, int, int)

smali
Lpackage/name/ObjectName;->MethodName(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
Java
String package.name.ObjectName.MethodName(int, int[][], int, String, Object[])

フィールド(メンバ変数)

smaliでは,フィールドを以下のように表記します.
オブジェクト名->フィールド名:フィールドの型

smali
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
Java
String package.name.ObjectName.FieldName

レジスタ

smaliでは,レジスタとよばれる変数を介して,引数の受け渡しなどを行っています.

ビット数

レジスタの大きさは32ビットで,boolean,intなどのほとんどの型を1つのレジスタに格納することができます.
ただし,long型,double型のみは,格納に64ビット必要なため,レジスタが2つ必要になります.

種類と表記

レジスタは,ローカルレジスタ,引数レジスタの2種類に分けられます.

ローカルレジスタはsmali上でv0,v1,v2…と表記され,レジスタの最大数は.localsディレクティブなどによって変動します.
たとえば".locals 5"であれば,v0~v4のレジスタを使用することができます.

引数レジスタはsmali上でp0,p1,p2…と表記されます.
レジスタの最大数や値は,staticメソッドかどうかによって,少し挙動が変わってきます.
  • staticメソッドの場合
    • 引数レジスタの最大数は(引数の数)個になります.
      たとえば method(int, int, int, int) というメソッドであれば,引数の数は4個なので,p0~p3の4個のレジスタを使用することができます.
    • 引数レジスタは名前の通り,メソッドに渡された引数が格納されます.
      たとえば method(1, 3, 5, 7) のようにメソッドを呼び出した場合,引数レジスタに格納される値はp0 = 1, p1 = 3, p2 = 5, p3 = 7になります.
  • staticメソッドでない場合
    • 引数レジスタの最大数は(引数の数+1)個になります.
      たとえば method(int, int, int, int) というメソッドであれば,引数の数は4個なので,p0~p4の5個のレジスタを使用することができます.
    • 引数レジスタは名前の通り,メソッドに渡された引数が格納されます
      ただし,staticメソッドでない場合は,p0にthisへの参照が格納されます
      たとえば method(1, 3, 5, 7) のようにメソッドを呼び出した場合,引数レジスタに格納される値はp0 = this, p1 = 1, p2 = 3, p3 = 5, p4 = 7になります.
なお,long型,double型はレジスタが2つ必要になるので,引数の数とレジスタの最大数がずれることに注意してください.
たとえばstaticメソッド method(long) の場合,引数は1個ですが,long型の格納にレジスタが2個必要になるため,レジスタの最大数は2個となり,第一引数はp0とp1に格納されます.

smaliの読み方

smaliの各行は大きく「コメント」「ラベル」「ディレクティブ」「命令」に分かれます.

「コメント」は,コンパイル時に無視される部分です.
Javaでは"//"以降がコメントになります.
smaliでは"#"以降がコメントになります.

「ラベル」はジャンプ命令などで飛ばされる先になります.
":"から始まる行がラベル行です.
":label1"のようにしてラベルを定義します.

「ディレクティブ」と「命令」の違いは気にしなくてもいいと思います.
文法的には"."から始まるものが「ディレクティブ」,そうでないものが「命令」となります.
内部的には,定数情報の格納を行うのが「ディレクティブ」,情報の操作などを行うのが「命令」という感じがします(あんまりよくわかってない).

命令一覧に関しては,Dalvik opcodesをご覧ください.

ディレクティブ一覧


私が見たこと無いディレクティブはわからないので省略します….
ディレクティブ説明
.classクラス名の定義
.class public Lpackage/name/ObjectName;
.superスーパークラス
.super Ljava/lang/Object;
.implements実装インターフェース
.implements Ljava/io/Serializable;
.sourceJavaソースファイル名
.source "ObjectName.java"
.fieldフィールド定義
.field private FieldName:Ljava/lang/String;
.annotationアノテーション
.annotation runtime Lorg/junit/Test;
.end annotation
.methodメソッド定義
.method public static main([Ljava/lang/String;)V
    メソッドの中身

.end method
.localsローカルレジスタ数の設定
.locals 5 #ローカル変数の個数は5個
.array-data配列の初期値の定義
.array-data 4 #要素が4バイトの配列
    0x800000
    0x400000
    0x200000
    0x100000
.end array-data
#設定された値はfill-array-data命令で書き込みできる
.packed-switch連続した値におけるswitch文の遷移先設定
.packed-switch 10 #値10から遷移チェックを開始
    :label10 #値が10のときの遷移先ラベル
    :label11 #値が11のときの遷移先ラベル
    :label12 #値が12のときの遷移先ラベル
    :label13 #値が13のときの遷移先ラベル
.end packed-switch
#あくまで遷移先ラベルの設定のみで,実際の遷移は行われない

#実際の遷移はpacked-switch命令で行われる
.sparse-switch飛び飛びの値におけるswitch文の遷移先設定
.sparse-switch
    10 -> :label10 #値が10のときの遷移先ラベル
    20 -> :label20 #値が20のときの遷移先ラベル
    25 -> :label25 #値が25のときの遷移先ラベル
    51 -> :label51 #値が51のときの遷移先ラベル
.end sparse-switch
#あくまで遷移先ラベルの設定のみで,実際の遷移は行われない
#実際の遷移はsparse-switch命令で行われる
.lineJavaソースコードで何行目にあたるか(デバッグ用)
.line 10 #Javaソースコードでは10行目
.localローカル変数の個数
.line 10 #Javaソースコードでは10行目

2 件のコメント:

  1. Treasure Island Las Vegas Gentleman's Club 메리트 카지노 주소 메리트 카지노 주소 10cric login 10cric login 우리카지노 계열사 우리카지노 계열사 770NBA Betting Tips Over Under | Shootercasino

    返信削除
  2. Welcome to the Jungle Casino - Jtmhub
    Welcome to the Jungle Casino. It's 충주 출장샵 the newest casino 창원 출장마사지 in South Africa that has 제주 출장안마 become a cultural hub for 나주 출장안마 the gay 논산 출장안마 community. Since the beginning it has been accepting

    返信削除