Android 四大组件

Android 四大组件

Android 的应用框架核心由四个基本组件构成。每个组件都是一个独立的实体,系统和应用可以通过它进入你的 App。理解这四个组件的职责和生命周期是进行任何 Android 开发或逆向分析的基础。


1. 四大组件概述

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
┌───────────────────────── Android 应用架构 ─────────────────────────┐
│                                                                    │
│  ┌──────────────────────────────────────────────────────────────┐  │
│  │              Application (全局入口)                           │  │
│  │       attachBaseContext() → onCreate()                       │  │
│  └──────────────────────┬───────────────────────────────────────┘  │
│                         │                                          │
│     ┌───────────────────┼───────────────────┐                     │
│     ▼                   ▼                   ▼                     │
│  ┌────────┐      ┌───────────┐      ┌──────────────┐             │
│  │Activity│      │  Service  │      │BroadcastRecvr│             │
│  │ (前台) │      │  (后台)   │      │   (事件)     │             │
│  └───┬────┘      └─────┬─────┘      └──────┬───────┘             │
│      └─────────────────┼────────────────────┘                     │
│                        ▼                                          │
│              ┌──────────────────┐                                 │
│              │ ContentProvider  │                                 │
│              │   (数据层)       │                                 │
│              └──────────────────┘                                 │
└────────────────────────────────────────────────────────────────────┘
组件逆向价值典型场景
Activity用户可见的界面逻辑,登录/支付等关键流程协议分析、界面逻辑还原
Service后台核心业务,加密/通信/数据处理推送协议、数据同步逻辑
BroadcastReceiver事件触发机制,开机自启、指令下发恶意软件分析、远控触发
ContentProvider数据存储与共享接口数据泄露检测、SQL注入

2. Activity 生命周期与逆向

2.1 生命周期图

 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
              ┌─────────────────┐
              │   onCreate()    │ ◄── 布局加载、数据初始化
              └────────┬────────┘     setContentView(), findViewById()
              ┌─────────────────┐
              │   onStart()     │ ◄── Activity 可见但不可交互
              └────────┬────────┘
              ┌─────────────────┐
              │  onResume()     │ ◄── 到达前台,可以交互
              └────────┬────────┘
              ┌─────────────────┐
              │  [运行状态]      │
              └────────┬────────┘
          ┌────────────┴────────────┐
          ▼                         ▼
  ┌─────────────┐          ┌──────────────┐
  │  onPause()  │          │   onStop()   │ ◄── 完全不可见
  └──────┬──────┘          └──────┬───────┘
         │                  ┌─────┴─────┐
         │                  ▼           ▼
         │          ┌────────────┐  ┌────────────┐
         │          │ onRestart()│  │ onDestroy()│
         │          └──────┬─────┘  └────────────┘
         └─────────────────┘

2.2 生命周期与 Hook 时机

生命周期方法Hook 价值典型用途
onCreate()极高捕获初始化参数、加密密钥生成、布局加载
onResume()捕获页面激活时的刷新逻辑、Token 校验
onPause()分析数据持久化逻辑、会话保存
onActivityResult()捕获跨 Activity 的数据回传(如支付结果)
onNewIntent()singleTop/singleTask 模式下的 Intent 更新

Frida Hook Activity 生命周期:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Java.perform(function() {
    var LoginActivity = Java.use("com.example.app.LoginActivity");

    LoginActivity.onCreate.overload("android.os.Bundle").implementation = function(bundle) {
        console.log("[*] LoginActivity.onCreate() called");
        var intent = this.getIntent();
        var extras = intent.getExtras();
        if (extras != null) {
            var keys = extras.keySet().iterator();
            while (keys.hasNext()) {
                var key = keys.next();
                console.log("[*] Extra: " + key + " = " + extras.get(key));
            }
        }
        this.onCreate(bundle);
    };
});

2.3 快速定位当前 Activity

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 查看当前前台 Activity
adb shell dumpsys activity top | grep ACTIVITY

# 监控 Activity 启动(实时)
adb shell am monitor

# 启动指定 Activity(带参数)
adb shell am start -n com.example.app/.DeepLinkActivity \
    -a android.intent.action.VIEW \
    -d "myapp://payment?orderId=12345"

2.4 混淆代码中的定位技巧

Activity 类名不会被混淆(Manifest 中声明),但内部方法和字段会。定位策略:

  1. 从 Manifest 找到 Activity 类名(明文)
  2. 分析 onCreate() 中的 setContentView(R.layout.xxx) 找到布局 ID
  3. 通过布局 XML 中的控件 ID 反向追踪 findViewById() 调用
  4. 从控件的事件监听器切入,追踪业务逻辑

3. Service 分析

3.1 Service 类型

1
2
3
启动服务 (Started):   startService() → onStartCommand() → [后台运行] → stopSelf()
绑定服务 (Bound):     bindService()  → onBind() → [Binder 交互] → onUnbind()
前台服务 (Foreground): startForegroundService() → startForeground(id, notification)

3.2 Service 中常见的逆向目标

 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
public class CryptoService extends Service {
    private SecretKey aesKey;

    @Override
    public void onCreate() {
        super.onCreate();
        // 逆向关键点 1: 密钥初始化
        aesKey = generateKey("hardcoded_seed_123");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if ("ENCRYPT_AND_SEND".equals(intent.getAction())) {
            // 逆向关键点 2: 数据加密流程
            byte[] plainData = intent.getByteArrayExtra("data");
            byte[] encrypted = encrypt(aesKey, plainData);
            sendToServer(encrypted);
        }
        return START_STICKY; // 被杀后自动重启
    }

    @Override
    public IBinder onBind(Intent intent) {
        // 逆向关键点 3: 暴露给其他组件的接口
        return new CryptoBinder();
    }

    public class CryptoBinder extends Binder {
        public byte[] encryptData(byte[] data) { return encrypt(aesKey, data); }
        public byte[] decryptData(byte[] data) { return decrypt(aesKey, data); }
    }
}

3.3 Hook Service 关键方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Java.perform(function() {
    var CryptoService = Java.use("com.example.app.CryptoService");

    CryptoService.onStartCommand.implementation = function(intent, flags, startId) {
        console.log("[*] CryptoService.onStartCommand()");
        console.log("[*] Action: " + intent.getAction());
        return this.onStartCommand(intent, flags, startId);
    };

    // Hook 加密方法直接获取密钥和明文/密文
    CryptoService.encrypt.implementation = function(key, data) {
        console.log("[*] 密钥: " + key.toString());
        console.log("[*] 明文: " + bytesToHex(data));
        var result = this.encrypt(key, data);
        console.log("[*] 密文: " + bytesToHex(result));
        return result;
    };
});
1
2
# 列出运行中的 Service
adb shell dumpsys activity services com.example.app

4. BroadcastReceiver 分析

4.1 静态注册与动态注册

静态注册AndroidManifest.xml 中声明,App 未运行也能响应:

1
2
3
4
5
6
<receiver android:name=".receiver.BootReceiver"
          android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

动态注册 – 代码中注册,需要搜索 registerReceiver 调用来发现:

1
2
3
receiver = new NetworkChangeReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(receiver, filter);

4.2 发现动态注册的 Receiver

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Java.perform(function() {
    var ContextWrapper = Java.use("android.content.ContextWrapper");
    ContextWrapper.registerReceiver.overload(
        "android.content.BroadcastReceiver", "android.content.IntentFilter"
    ).implementation = function(receiver, filter) {
        console.log("[*] registerReceiver: " + receiver.getClass().getName());
        for (var i = 0; i < filter.countActions(); i++) {
            console.log("[*]   Action: " + filter.getAction(i));
        }
        return this.registerReceiver(receiver, filter);
    };
});

4.3 恶意软件中常见的 Receiver 模式

监听的广播 Action恶意用途
BOOT_COMPLETED开机自启,持久化驻留
CONNECTIVITY_ACTION网络恢复时上传窃取的数据
SMS_RECEIVED拦截短信(验证码窃取)
NEW_OUTGOING_CALL监听/拦截电话拨打
SCREEN_ON / SCREEN_OFF判断用户活跃状态
自定义 ActionC2 指挥控制指令下发
1
2
3
4
# 向导出的 Receiver 发送广播测试
adb shell am broadcast \
    -a com.example.app.ACTION_EXECUTE_COMMAND \
    --es command "get_device_info"

5. ContentProvider 分析

5.1 URI 结构

1
2
3
4
5
6
7
8
content://com.example.app.provider/users/10
\_____/   \________________________/ \___/ \/
 scheme          authority           path   id

常见模式:
  content://authority/table          → 所有记录
  content://authority/table/10       → ID=10 的记录
  content://authority/table/10/sub   → 子资源

5.2 安全风险:SQL 注入

1
2
3
4
5
6
7
8
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    // 如果 selection 未做过滤,可能存在 SQL 注入
    // 攻击: selection = "1=1) UNION SELECT password FROM credentials--"
    return db.query("users", projection, selection, selectionArgs,
                    null, null, sortOrder);
}

5.3 通过 adb 测试 ContentProvider

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查询导出的 ContentProvider
adb shell content query --uri content://com.example.app.provider/users

# SQL 注入测试
adb shell content query \
    --uri content://com.example.app.provider/users \
    --where "name='admin' OR '1'='1'"

# 路径遍历测试 (FileProvider)
adb shell content read \
    --uri content://com.example.app.fileprovider/root/data/data/com.example.app/shared_prefs/config.xml

5.4 FileProvider 路径遍历漏洞

1
2
3
4
5
6
7
8
9
<!-- res/xml/file_paths.xml -->
<paths>
    <!-- 危险: 暴露整个外部存储 -->
    <external-path name="external" path="." />
    <!-- 危险: 暴露应用私有目录根 -->
    <root-path name="root" path="" />
    <!-- 安全: 只暴露特定目录 -->
    <files-path name="images" path="images/" />
</paths>

6. Application 类

6.1 Application 的特殊地位

Application 类是应用中最先被创建的对象,生命周期覆盖整个进程。这使它成为逆向中最关键的切入点。

1
2
3
4
5
6
应用启动顺序:
  1. Zygote fork 新进程
  2. Application.attachBaseContext()  ◄── 最早的代码入口,壳在此脱壳
  3. ContentProvider.onCreate()       ◄── 在 Application.onCreate 之前!
  4. Application.onCreate()           ◄── 全局初始化,SDK/网络/安全检测
  5. 首个 Activity/Service/Receiver 创建

6.2 典型的 Application 子类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        MultiDex.install(this);  // 多 DEX 加载
        // 加固壳入口: 360/腾讯乐固/梆梆 在此解密并加载真正的 DEX
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initNetwork();           // 网络框架配置
        initEncryption();        // 加密模块初始化
        initAntiDebug();         // 反调试检测
        initRootDetection();     // Root 检测
        loadNativeLibrary();     // 加载 SO 库
    }
}

6.3 Hook Application 关键方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Java.perform(function() {
    var MyApp = Java.use("com.example.app.MyApplication");

    MyApp.attachBaseContext.implementation = function(context) {
        console.log("[*] attachBaseContext() -- 最早入口");
        this.attachBaseContext(context);
        // 此时加固壳已解密 DEX
    };

    // 绕过安全检测
    MyApp.initAntiDebug.implementation = function() {
        console.log("[*] 跳过反调试检测");
    };
    MyApp.initRootDetection.implementation = function() {
        console.log("[*] 跳过 Root 检测");
    };
});

在 Manifest 中定位: <application android:name=".MyApplication" ...>。如果没有 android:name 属性,则使用默认的 android.app.Application 类。


7. Intent 机制

7.1 Intent 的组成

1
2
3
4
5
6
Action     → 要执行的操作(如 VIEW, SEND, MAIN)
Data       → 操作的数据 URI(如 content://, http://)
Category   → 对组件的附加限定(如 LAUNCHER, DEFAULT)
Component  → 显式指定目标组件(包名+类名)
Extras     → 附加的键值对数据(Bundle)
Flags      → 控制启动模式和任务栈行为

7.2 显式 vs 隐式 Intent

1
2
3
4
5
6
7
8
9
// 显式 -- 直接指定目标,可直接追踪
Intent explicit = new Intent(this, PaymentActivity.class);
explicit.putExtra("order_id", "ORD_12345");
startActivity(explicit);

// 隐式 -- 通过 Action/Data 匹配,需查 Manifest 中的 intent-filter
Intent implicit = new Intent("com.example.app.ACTION_PAY");
implicit.setData(Uri.parse("pay://checkout?token=abc123"));
startActivity(implicit);

Deep Link 允许外部通过 URI 直接打开应用特定页面,是重要的攻击面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<activity android:name=".DeepLinkActivity" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" android:host="payment" />
        <data android:scheme="https" android:host="www.example.com"
              android:pathPrefix="/pay" />
    </intent-filter>
</activity>
1
2
3
# 测试 Deep Link
adb shell am start -a android.intent.action.VIEW \
    -d "myapp://payment?orderId=12345&token=abc"

7.4 追踪 Intent 数据流

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Java.perform(function() {
    var Activity = Java.use("android.app.Activity");
    Activity.startActivity.overload("android.content.Intent").implementation = function(intent) {
        console.log("[*] 调用者: " + this.getClass().getName());
        console.log("[*] 目标: " + intent.getComponent());
        console.log("[*] Action: " + intent.getAction());
        console.log("[*] Data: " + intent.getDataString());
        var bundle = intent.getExtras();
        if (bundle != null) {
            var keys = bundle.keySet().iterator();
            while (keys.hasNext()) {
                var key = keys.next();
                console.log("[*] Extra: " + key + " = " + bundle.get(key));
            }
        }
        this.startActivity(intent);
    };
});

8. 逆向中的关键切入点

8.1 分析流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Step 1: 静态分析 Manifest
  → 找 Application 子类、主 Activity、exported 组件、权限列表

Step 2: 动态定位目标组件
  → dumpsys activity top、dumpsys activity services、logcat

Step 3: Hook 关键生命周期
  → attachBaseContext (壳)、onCreate (页面)、onStartCommand (后台)、onReceive (事件)

Step 4: 追踪数据流
  → Intent 传递、加密函数、网络请求、ContentProvider 操作

8.2 按场景选择 Hook 目标

逆向目标首选 Hook 组件关键方法
登录协议LoginActivityonCreate() 中的按钮事件
支付流程PaymentActivity / PayServiceonClick(), onActivityResult()
数据加密CryptoService / EncryptUtilencrypt(), decrypt(), sign()
网络请求OkHttp Interceptorintercept(), proceed()
推送消息PushService / PushReceiveronStartCommand(), onReceive()
反调试绕过Application / NativeLibonCreate(), JNI 函数

8.3 混淆应用的突破策略

1
2
3
4
未混淆的通常保留:                    通常被混淆:
  Activity/Service/Provider 类名       内部工具类名 (Utils → a)
  JNI native 方法名                    方法名 (encryptData → b)
  日志字符串(如果未被移除)            字段名 (secretKey → c)

突破方法:

  1. 从组件入手: Activity/Service 类名是明文,从生命周期方法开始追踪
  2. 字符串搜索: 搜索错误提示、URL、常量字符串定位关键逻辑
  3. API 调用追踪: Hook Cipher.doFinal()URL.openConnection() 从底层向上追踪
  4. 交叉引用: 在 jadx/IDA 中从已知方法出发追踪调用链

8.4 实战:从零开始分析

💡 思路一句话: 分析一个陌生 App 的入口 — 先看 AndroidManifest.xml 找到启动 Activity 和 Application → 再追踪 onCreate 中的初始化逻辑 → 理解组件之间的调用关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 1. 解包
apktool d target.apk -o target_decoded

# 2. 快速浏览 Manifest
grep "android:name" target_decoded/AndroidManifest.xml | head -5
grep "exported=\"true\"" target_decoded/AndroidManifest.xml
grep -A5 "MAIN" target_decoded/AndroidManifest.xml

# 3. 动态定位
adb shell dumpsys activity top | grep "ACTIVITY"
adb logcat | grep -i "com.example.app"

# 4. Frida 注入
frida -U -f com.example.app -l hook_script.js

8.5 逆向分析优先级

  1. Application - 全局初始化,识别加固方案,绕过安全检测
  2. Activity - 从界面入手,找到目标功能入口点
  3. Service - 后台核心业务:加密、通信、数据处理
  4. BroadcastReceiver - 系统事件和自定义事件响应
  5. ContentProvider - 数据接口,可能存在数据泄露


相关内容

如果这篇文章对你有帮助,请我喝杯咖啡吧~
+5 支付宝支付宝
+5 微信微信