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
非常详细,通俗易懂的 smali,学习了非常感谢。