MENU

Smali 语法学习

December 2, 2016 • Read: 4871 • CTF

smali生成

使用 apktool 反编译 apk 后,会在反编译工程目录下生成一个 smali 文件夹,其中 android 下存放所调用库的 smali 文件,com 才是我们自己写的代码的 smali 文件。

基础语法

首先来看基本格式。

文件基本格式

基本信息

.class <访问权限> <类名>
.super <父类名>
.source <源文件名>
# eg.
.class
 public Lcom/reoky/crackme/challengeone/activities/ChallengeActivity;    

.super
 Landroid/support/v4/app/FragmentActivity;                              

.source "ChallengeActivity.java"  //经过混淆后这项可能为空

类变量声明

.field <访问权限> <变量名>:<变量类型>
# eg.
.field actionBar:Landroid/app/ActionBar; <=对应源码=> ActionBar actionBar;
局部变量声明:
.local <初始值>,<变量名>:<变量类型>
#eg
.local v0, "ans":Ljava/lang/String; <=对应源码=> String ans="";

类方法声明

.method <访问权限> <方法名>(参数原型) <方法原型>
    [.prologue]    // 指定代码开始位置
    [.param]       // 指定方法参数
    [.line]        // 指定代码在源代码中的行数,混淆后可能不存在
    [.locals]  // 使用的局部变量个数
    <代码体>
.end method
# eg
.method public onTabReselected(Landroid/app/ActionBar$Tab;Landroid/app/FragmentTransaction;)V 
    .locals 0
    .param p1, "tab"    # Landroid/app/ActionBar$Tab; 
    .param p2, "fragmentTransaction"    # Landroid/app/FragmentTransaction;   
    .prologue 
    .line 55     //可能经过混淆后不存在
    return-void
.end method 
<=对应源码=>
public void onTabReselected(ActionBar$Tab tab, FragmentTransaction fragmentTransaction){
}

原始类型

B—byte
C—char
D—double
F—float
I—int
J—long
S—short
V—void
Z—boolean
[XXX—array
Lpackage/name/ObjName—object  // 前面表示对象所在包路径

寄存器操作

传入的参数寄存器由p表示,而函数内的本地寄存器则由v表示,多个的话则在后面加上0,1,2...
需要注意的是,在非static函数中,p0表示this,p1才表示第一个参数。
常量赋值

主要是各种const
const                   v0, 0x7F030018  # R.layout.activity_challenge   #从R中取出静态值
const/4                 v3, 0x2   #4也可以换成16或者high16,表示取整数值
const-string            v2, "Challenge"  # 取字符串
const-class             v2, Context    #把类对象取出

变量间赋值

move  vx,vy   # 将vy的值赋值给vx,也可以是move-object等
move-result vx  # 将上个方法调用后的结果赋值给vx,也可以是move-result-object
return-object vx # 将vx的对象作为函数返回值
new-instance            v0, ChallengePagerAdapter  # 实例化一个对象存入v0中

对象赋值

iput-object             a,(this),b   将a的值给b,一般用于b的初始化
iget-object             a,(this),b   将b的值给a,一般用于获取b的地址,接着调用它
# eg.
iput-object             v0, p0, ChallengeActivity->actionBar:ActionBar
iget-object             v0, p0, ChallengeActivity->actionBar:ActionBar

函数操作

最基础的函数操作一般有以下四个:

1.private:invoke-direct
2.public|protected: invoke-virtual
3.static:invoke-static
4.parent:  invoke-super
基本调用形式:invoke-xxx {参数},类;->函数(参数原型)
# eg.
invoke-super {p0, p1}, Landroid/support/v4/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V
<=对应源码=>
super.onCreate(savedInstanceState);  // 其中p0是this,其父类是FragmentActivity,p1,是savedInstanceState,其原型是Bundle;即调用p0->onCreate(p1)

程序语句相关语法

这里列举以下常见程序语句对应的smali语句,并与Android源码相比较分析。

判断语句

if-eq vA, vB, :cond_X   如果vA等于vB则跳转到:cond_X
if-ne vA, vB, :cond_X   如果vA不等于vB则跳转到:cond_X
if-lt vA, vB, :cond_X   如果vA小于vB则跳转到:cond_X
if-ge vA, vB, :cond_X   如果vA大于等于vB则跳转到:cond_X
if-gt vA, vB, :cond_X   如果vA大于vB则跳转到:cond_X
if-le vA, vB, :cond_X   如果vA小于等于vB则跳转到:cond_X
if-eqz vA, :cond_X      如果vA等于0则跳转到:cond_X
if-nez vA, :cond_X      如果vA不等于0则跳转到:cond_X
if-ltz vA, :cond_X      如果vA小于0则跳转到:cond_X
if-gez vA, :cond_X      如果vA大于等于0则跳转到:cond_X
if-gtz vA, :cond_X      如果vA大于0则跳转到:cond_X
if-lez vA, :cond_X      如果vA小于等于0则跳转到:cond_X

循环语句

下面列出一个简单的for循环,其他也差不多

public void encrypt(String str) {
    String ans = "";
    for (int i = 0 ; i < str.length();i++){
        ans += str.charAt(i);
    }
    Log.e("ans:",ans);
}
<=对应smali=>
# public void encrypt(String str) {
.method public encrypt(Ljava/lang/String;)V 
.locals 4 
.param p1, "str"# Ljava/lang/String;
.prologue 
# String ans = "";
const-string v0, "" 
.local v0, "ans":Ljava/lang/String; 
# for (int i  0 ; i < str.length();i++){
# int i=0 =>v1
const/4 v1, 0x0
.local v1, "i":I
:goto_0# for_start_place
# str.length()=>v2
invoke-virtual {p1}, Ljava/lang/String;->length()I
move-result v2 
# i<str.length() 
if-ge v1, v2, :cond_0 
# ans += str.charAt(i); 
# str.charAt(i) => v2
new-instance v2, Ljava/lang/StringBuilder; 
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2 
#str.charAt(i) => v3
invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C 
move-result v3
# ans += v3 =>v0
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder; 
move-result-object v2 
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v0
# i++
add-int/lit8 v1, v1, 0x1
goto :goto_0
# Log.e("ans:",ans);
:cond_0
const-string v2, "ans:" 
invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
return-void 
.end method

switch语句

public void encrypt(int flag) {
        String ans = null;
        switch (flag){
            case 0:
                ans = "ans is 0";
                break;
            default:
                ans = "noans";
                break;
        }
        Log.v("ans:",ans);
    }
<=对应smali=>
#public void encrypt(int flag) {
.method public encrypt(I)V 
    .locals 2
    .param p1, "flag"    # I
    .prologue
#String ans = null;
    const/4 v0, 0x0
    .local v0, "ans":Ljava/lang/String;
#switch (flag){
    packed-switch p1, :pswitch_data_0 # pswitch_data_0指定case区域的开头及结尾
#default: ans="noans"
    const-string v0, "noans"
#Log.v("ans:",ans)
    :goto_0
    const-string v1, "ans:"
    invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
    return-void
#case 0: ans="ans is 0"
    :pswitch_0      #pswitch_<case的值>
    const-string v0, "ans is 0"
    goto :goto_0  # break
    nop
    :pswitch_data_0 #case区域的结束
    .packed-switch 0x0   #定义case的情况
        :pswitch_0   #case 0
    .end packed-switch
.end method

其中case定义情况有两种:

1. 从0开始递增
packed-switch p1, :pswitch_data_0
...
:pswitch_data_0
.packed-switch 0x0
    :pswitch_0
    :pswitch_1

2. 无规则switch
sparse-switch p1,:sswitch_data_0
...
sswitch_data_0
.sparse-switch
    0xa -> : sswitch_0
    0xb -> : sswitch_1 # 字符会转化成数组

try-catch语句

public void encrypt(int flag) {
    String ans = null;
    try {
        ans = "ok!";
    } catch (Exception e){
        ans = e.toString();
    }
    Log.d("error",ans);
}
<=对应smali=>
#public void encrypt(int flag) {
.method public encrypt(I)V
    .locals 3
    .param p1, "flag"    # I
    .prologue
#String ans = null;
    const/4 v0, 0x0
    .line 20
    .local v0, "ans":Ljava/lang/String;
#try { ans="ok!"; }
    :try_start_0  # 第一个try开始,
    const-string v0, "ok!"
    :try_end_0   # 第一个try结束(主要是可能有多个try)
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
#Log.d("error",ans);
    :goto_0
    const-string v2, "error"
    invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
#catch (Exception e){ans = e.toString();}
    :catch_0 #第一个catch
    move-exception v1
    .local v1, "e":Ljava/lang/Exception;
    invoke-virtual {v1}, Ljava/lang/Exception;->toString()Ljava/lang/String;
    move-result-object v0
    goto :goto_0
.end method
Last Modified: February 14, 2017
Archives QR Code
QR Code for this page
Tipping QR Code
Leave a Comment

已有 1 条评论
  1. dr dr

    非常详细,通俗易懂的smali,学习了非常感谢。