samli文件_smali文件和语法

smali文件格式

smali文件的头3行描述了当前类的一些信息,格式如下:

.class[修饰关键字]

.super

.source

打开MainAcivity.smali,头三行代码:

.class public Lcom/droider/crackme0502/MainActivity;

.super Landroid/app/Activity;

.source "MainActivity.java"

第一行权限public ,类名为Lcom/droider/crackme0502/MainActivity;,类名开头的L是遵循Dalvik字节码的相关约定,表示后面跟随的字符串为一个类。

第二行,Lcom/droider/crackme0502/MainActivity; 的父类是 Landroid/app/Activity;

第三行,.source指令指定了当前类的源文件名

经过混淆的dex文件,反编译出来的smali代码可能没有源文件信息,因此,.source可能为空。

前三行代码过后是类的主体部分,一个类可以由多个字段或方法组成。smali文件中字段的声明使用.field指令。字段有静态字段与实例字段两种。静态字段的声明格式如下:

# static fields

.field[修饰关键字]:

实例字段的声明:

# instance field

.field private btnAnno:Landroid/widget/Button;

private 表示私有;字段btnAnno,它的类型是Landroid/widget/Button;

直接方法的声明:

# direct methods

.method (参数原型)

[.prologue] // 指定代码开始位置

[.param] // 指定方法参数

[.line] // 指定代码在源代码中的行数,混淆后可能不存在

[.locals] // 使用的局部变量个数

.end method

虚方法的声明和直接方法相同,只是起始处的注释为virtual methods

如果一个类实现了接口,会在.smali文件中使用.implement指令指出。声明入下:

# interface

.implements

注解格式声明:

# annotations

.annotation[注解属性]

[注解字段=值]

.end annotation

注解的作用范围可以是类、方法和字段。如果作用范围是类,指令会直接定义在smali文件中,如果是方法或者字段,指令会包含在方法或字段的定义中。例如:

# instace fields

.field public sayWhat:Ljava/lang/String;

.annotation runtime Lcom/droider/anno/MyAnnoField;

info = "Hello my friend"

.end annotation

.end field

转换成Java代码:

@ com.droid.anno MyAnnoField(info = "Hello my friend")

public String sayWhat;

原始类型

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命名法:

假设一个函数中用到M个寄存器,实际传入的参数是N个。

根据传参规则,参数使用后N个寄存器,局部变量使用0到M-N个寄存器。

假如用到5个寄存器,2个局部参数,3个传入参数。

v命名法:

v0,v1,v2,v3,v4;

p命名法:

v0,v1,p0,p1,p2;

只改变传入参数寄存器名。

常量赋值

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)

程序相关语法

判断语句

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

循环语句

public void encrypt(String str) {

String ans = "";

for (int i = 0 ; i < str.length();i++){

ans += str.charAt(i);

}

Log.e("ans:",ans);

}

<=对应smali=>

.method public encrypt(Ljava/lang/String;)V # 方法:public void encrypt(String str)

.locals 4 # 四个变量

.param p1, "str" # 方法参数:Ljava/lang/String;

.prologue # 代码起始处

const-string v0, "" # 赋值给ans

.local v0, "ans":Ljava/lang/String;

const/4 v1, 0x0 # 赋值给 i

.local v1, "i":I

:goto_0 # 循环的地方

invoke-virtual {p1}, Ljava/lang/String;->length()I # 调用虚函数(参数p1)String类中的length方法,返回int

move-result v2 #把前一步的结果放在v2中

if-ge v1, v2, :cond_0 # 如果v1

new-instance v2, Ljava/lang/StringBuilder; # 创建实例 v2 ,类型是Ljava/lang/StringBuilder;

invoke-direct {v2}, Ljava/lang/StringBuilder;->()V # v2初始化

invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; # v2.append(v0)

move-result-object v2 # v2.append(v0) => v2 这里 v2是v0的值,v2=ans

invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C # str.charAt(i)

move-result v3 # str.charAt(i) => v3

invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder; # ans + v3

move-result-object v2 # ans + v3 =>v2

invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; # v2.toString()

move-result-object v0 # v2=>v0

add-int/lit8 v1, v1, 0x1 # i++

goto :goto_0 # 跳转指令

:cond_0

const-string v2, "ans:" # 常量赋值 v2 = "ans:"

invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I # Log.e(v2,v0)

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=>

.method public encrypt(I)V # 方法 public void encrypt(int flag)

.locals 2 # 两个变量

.param p1, "flag" # 一个参数 flag

.prologue

const/4 v0, 0x0 # v0赋值,

.local v0, "ans":Ljava/lang/String; # String ans = null; v0就是ans

packed-switch p1, :pswitch_data_0 # pswitch_data_0指定case区域的开头及结尾

const-string v0, "noans" # 默认 赋值 ans="noans"

:goto_0

const-string v1, "ans:" # 赋值 v1 = "ans:"

invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I # Log.v

return-void

:pswitch_0 #pswitch_ case 0: ans="ans is 0"

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=>

.method public encrypt(I)V # public void encrypt(int flag) {

.locals 3 # 3个变量

.param p1, "flag" # 参数

.prologue # 代码开始

const/4 v0, 0x0

.line 20

.local v0, "ans":Ljava/lang/String; # String ans = null;

:try_start_0 # 第一个try开始,

const-string v0, "ok!" # ans = "ok"

:try_end_0 # 第一个try结束(主要是可能有多个try)

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

:goto_0

const-string v2, "error"

invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

return-void

: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

baksmali在反编译时,为每个类单独生成一个smali文件,内部类作为一个独立类,也拥有自己独立的smali文件,只是内部类的文件名形式为[外部类]$[内部类].smali

class Outer{

class Inner{}

}

上述代码生成两个文件:Outer.smali和Out$Inner.smali

this$0 是什么? 是内部类自动保留的一个指向所在外部类的引用。左边的this表示为父类的引用,右边的0表示引用层数。

public class Outer{ //this$0

public class FirstInner{ //this$1

public class SecondInner{ //this$2

public class ThirdInner{

//在ThirdInner中访问FirstInner类的引用为this$1

}

}

}

}

this$X型字段都被指定了synthetic属性,表明他们是被编译器合成的虚构的,开发者并没有声明该字段。

Reference

《Android软件安全与逆向分析》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值