Android逆向,理解反汇编得到的smali文件格式
AndroidMenifest.xml AndroidMenifest.xml极其重要,记录着软件的包名、运行的系统版本、用到的组件等基本信息
intent-filter指定Activity启动意图;
android.intent.action.MAIN表示这个activity为程序的主Activity(没有指定则LAUNCHER无法匹配主Activity,程序没有图标)
android.intent.category.LAUNCHER表示这个Activity可以通过LAUNCHER启动(如果所有的activity都没添加,程序将在程序列表不可见)
重点在Application类 ,主要用于在组件间传递全局变量或Activity启动前做初始化工作.由于Applicaion类比程序中其他类都启动的早,一些商业软件将授权验证代码转移到此类中。
smali文件格式 内部类 baksmali反编译dex文件时,会为每个类单独生成一个smali文件。反编译的smali代码中产生了诸如MainActivity$SNChecker.smali的文件,这是内部类文件名的格式”[外部类]$[内部类].smali”。
比如MainActivity$SNChecker.smali便是MainActivty的一个内部类SNChecker,而MainActivity$1.smali被称作MainActivity的一个示例,在下文将会看到
注解类 Android注解包有两个,抛开不对外开放的dalvik.annotation不谈,另一个是android.annotation.
在反汇编的smali中很多地方都使用了注解类,这里做一下分类说明.
MemberClasses MemberClasses注解是编译时自动加上的,是一个系统注解,为父类提供一个MemberClasses列表,子类成员集合,或者说是内部类列表。 在MainActivity.smali中的片段:
1 2 3 4 5 6 # annotations .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/droider/crackme0502/MainActivity$SNChecker; } .end annotation
可以看到的是SNChecker内部类
Enclossing Enclossing注解用来说明作用范围 EnclossingMethord表明作用于一个方法,value表明位置 如MainActivity$1.smali中的一段:
1 2 3 4 # annotations .annotation system Ldalvik/annotation/EnclosingMethod; value = Lcom/droider/crackme0502/MainActivity;->onCreate(Landroid/os/Bundle;)V .end annotation
作用于MainActivity的onCreate方法
相似的,EnclossingClass表明作用于一个类,value表明作用类。
MainActivity$SNChecker.smali中的一段:
1 2 3 4 5 6 7 8 9 # annotations .annotation system Ldalvik/annotation/EnclosingClass; value = Lcom/droider/crackme0502/MainActivity; .end annotation .annotation system Ldalvik/annotation/InnerClass; accessFlags = 0x1 name = "SNChecker" .end annotation
可以看到,作用于MainAcitivity类,后面还有一个InnerClass注解;
InnerClass注解,表示是内部类,accessFlags访问标志,枚举值:
1 2 3 4 5 enum{ kDexVisibilityBuild = 0x00, kDexVisibilityRuntime = 0x01, kDexVisibilitySytem = 0x2, }
name是名字
监听器 监听器设置的流程:
新建MainActivity$1实例具体可以在MainActivity$1.smali看到;
初始化MainActivity$1示例;
invoke设置按钮点击事件监听器
如下例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .line 32 iget-object v0, p0, Lcom/droider/crackme0502/MainActivity;->btnAnno:Landroid/widget/Button; #获取button对象 new-instance v1, Lcom/droider/crackme0502/MainActivity$1;#新建MainActivity$1实例 invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity$1;-><init>(Lcom/droider/crackme0502/MainActivity;)V #初始化MainActivity$1实例 invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V #设置按钮点击事件监听器 .line 40 iget-object v0, p0, Lcom/droider/crackme0502/MainActivity;->btnCheckSN:Landroid/widget/Button; new-instance v1, Lcom/droider/crackme0502/MainActivity$2; invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity$2;-><init>(Lcom/droider/crackme0502/MainActivity;)V invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
自动生成类 使用Android SDK默认生成的公工程会自动添加一些类.
R类 R类包含大量的资源类,一段R.smali示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 .class public final Lcom/droider/crackme0502/R; .super Ljava/lang/Object; .source "R.java" # annotations .annotation system Ldalvik/annotation/MemberClasses; value = { Lcom/droider/crackme0502/R$attr;, Lcom/droider/crackme0502/R$dimen;, Lcom/droider/crackme0502/R$drawable;, Lcom/droider/crackme0502/R$id;, Lcom/droider/crackme0502/R$layout;, Lcom/droider/crackme0502/R$menu;, Lcom/droider/crackme0502/R$string;, Lcom/droider/crackme0502/R$style; } .end annotation # direct methods .method public constructor <init>()V .locals 0 .prologue .line 10 invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method
可以看到注解里列出了大量的子类,这些子类每个都有单独的smali文件可以查看。
BuildConfig类 只包含一个boolean类型的DEBUG字段标识程序发布的版本类型,默认为true。
注解类 没啥说的,影响不大。
java代码在smali文件中的表现 几个重要的语句结构:
循环语句
switch语句
try catch语句
循环语句 循环主要有三种:
迭代器循环,hasNext()判断
传统for循环
while循环与for循环基本相同,只是判定位置不同
switch语句 switch语句的结构非常清楚: xyyy-switch分支,xswitch_data_0指定case区域
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 packed-switch p1, :pswitch_data_0 .line 36 const-string v0, "she is a person" .line 39 :goto_0 return-object v0 .line 24 :pswitch_0 const-string v0, "she is a baby" .line 25 goto :goto_0 .line 27 :pswitch_1 const-string v0, "she is a girl" .line 28 goto :goto_0 .line 30 :pswitch_2 const-string v0, "she is a woman" .line 31 goto :goto_0 .line 33 :pswitch_3 const-string v0, "she is an obasan" .line 34 goto :goto_0 .line 22 nop :pswitch_data_0 .packed-switch 0x0 #case区域 从0开始递增 :pswitch_0 #case 0 :pswitch_1 #case 1 :pswitch_2 #case 2 :pswitch_3 #case 3 .end packed-switch
pswitch_0pswitch_3对应case 0case 3,default分支放在第一个。
对于有规律递增的switch,结构体如下:
1 2 3 4 5 6 struct packed-switch-palyload{ ushort ident;/*值固定0x100*/ ushort size; /*case数目*/ int first_key;/*初始case的值*/ int[] targets;/*每个case相对switch指令处的偏移*/ }
无规律switch,结构体如下:
1 2 3 4 5 6 struct sparse-switch-payload{ ushort ident;/*值固定为0x200*/ ushort size;/*case数目*/ int[] keys;/*每个case的值,顺序从低到高*/ int[] targets;/*每个case相对switch的偏移*/ }
try/catch语句 有非常明显的标号:try_start_0
以及try_end_0
和.catch
。