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,学习了非常感谢。