0%

Android逆向分析初步(2)

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