MENU

Smali 语法学习

December 2, 2016 • Read: 5216 • 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

  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠( ᐛ 」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • ヾ(´・ ・`。)ノ"
  • ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ °Д °;)っ
  • ( ,,´・ω・)ノ"(´っω・`。)
  • ╮(╯▽╰)╭
  • o(*////▽////*)q
  • >﹏<
  • ( ๑´•ω•) "(ㆆᴗㆆ)
  • (。•ˇ‸ˇ•。)
  • 泡泡
  • 阿鲁
  • 颜文字

已有 1 条评论
  1. dr dr

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