安卓基础知识笔记

安卓四大组件

Activity(活动)、Service(服务)、BroadcastReceiver(广播接收者)、ContentProvider(内容提供者)

Binder 工作原理

通过 Binder,Android 可以跨越不同进程和内存空间进行数据交换和调用远程方法

当进程 A 想要获取进程 B 中某个对象的方法时,它先通过 Binder 获取 B 的接口对象,这个接口对象其实就是 B 中服务的代理,A 通过它来发起远程方法调用;Binder 驱动会将 A 的请求数据打包成 Parcel 数据传递给 B,B 收到数据后解包并执行对应的方法,再将结果封装成 Parcel 传递给 A,A 收到结果后解包并继续执行后续逻辑

Parcel 是 Android 中用于在进程间传递数据的容器类,由于不同进程的内存空间是隔离的,不能直接传递内存中的对象,而 Parcel 就负责将对象的序列化(转化为字节流)后传输,接收端再将这些字节流反序列化为对象

跟传统的 socket 通信相比,Binder 是通过内存映射的方式,在进程间共享内存区域来传递数据,而不用进行多次数据拷贝

JNI 静态注册和动态注册

Java 支持调用 C/C++ 代码,JNI(Java Native Interface)的作用是粘合 Java 代码和 C/C++ 代码

静态注册 : 在 AndroidManifest.xml 中注册
遵循一定的命名规则,一般是 Java_packagename_classname_methodname(JNIEnv *env,jclass/jobject,...)

动态注册 : 通过代码在运行时注册
通过 registerReceiver() 方法实现,需要在适当时候调用 unregisterReceiver() 注销

使用结构体 JNINativeMethod 来记录 java 方法和 jni 函数的对应关系

1
2
3
4
5
typedef struct {
const char* name; //Java方法名
const char* signature; //方法的参数和返回值,使用字符串记录,格式形如`()V, (I)I`,括号内表示函数参数,括号右侧表示函数返回值
void* fnPtr; // 指向JNI函数的函数指针
} JNINativeMethod;

java 端的 native 方法
c/c++ 代码实现的 JNI_Onload()方法

JNI 环境指针
JNIEnv* env = self->GetJniEnv();
JNIEnv 是一个结构体,包含了大量的函数指针,这些函数指针指向 JNI 提供的各种功能函数,如查找类、调用方法、访问字段
等,通过 JNIEnv* env,我们可以调用这些函数来实现 Java 和 C/C++ 代码之间的交互

JNIEnv 是线程相关的,一个线程必须 attach 到 JVM 后才有自己的 JNIEnv*

Android 是怎么加载 dex 的

dex 是安卓设计的一种可执行字节码文件格式,开发者写的 Java/Kotlin 代码先会被编译成很多 .class 文件,再被转换并合并成一个或多个 .dex 文件,里面集中保存了应用运行所需的类定义、方法定义、字段信息、字符串常量、类型信息以及方法的字节码指令

dex 的加载链路大致如下:

  • AMS 让应用进程起来,ActivityThread 绑定应用
  • LoadedApk 为这个包创建应用 ClassLoader
  • PathClassLoader/BaseDexClassLoader 把 apk/split/apex/jar 路径整理成 DexPathList
  • DexPathList 为每个代码路径打开 DexFile
  • DexFile 通过 JNI 进入 ART,OatFileManager / DexFileLoader 真正打开 dex、vdex、oat
  • 后续 loadClass() 时,ART 的 ClassLinker 在这些已打开的 dex 里找类,并在第一次命中时 DefineClass

Frida 注入原理

Frida 注入流程通常是:

  • 利用 ptrace 附加到目标进程
  • 强制修改寄存器(如 PC 指针),让目标进程去执行一段临时的 shellcode
  • 这段 shellcode 会调用 dlopen, 将 Frida 的动态库(frida-agent.so) 加载到目标进程的内存空间
  • Agent 加载成功后,它就直接在目标进程内部进行 inline hook
  • 撤销 ptrace

Frida hook 原理

Frida 的 Interceptor 实现 hook 主要通过四级跳板完成

  • 先通过 inline hook 的一级跳板到达 on_enter_trampoline 的位置,此处为二级跳板
  • on_enter_trampoline 处通过 BR X16 跳到第三级跳板 enter_trunk,在这里把 CPU 寄存器全部保存到栈上或者专门的 CpuContext 结构体中
  • 调用 Invocation 进入四级跳板,执行 on_enter 代码
  • 完成 on_enter 处注册代码后根据函数是否被替换分为两种情况:
    • 如果函数被替换,则直接跳转到替换函数
    • 如果函数没有被替换,则跳转到 on_leave_trampoline 处,这里将执行原本的函数代码
  • 执行完成后,根据是否设置 on_leave 又分为两种情况:
    • 如果没有设置在,则直接返回
    • 如果设置了,则跳回到二级跳板执行 leave_trunk,和 enter_trunk 类似,结束后返回

Inline hook 原理:

  • 找到目标函数在内存中的起始地址
  • 读取目标函数的开头几一段指令,因为即将要覆盖这部分指令,所以把它们备份到一块新的内存区域( trampoline 跳板区)
    ps: 如果备份的指令中包含相对跳转(比如 CALL +0x100),直接复制到新位置相对偏移量就错了,需要进行指令修复将其转化为绝对跳转或者重新计算偏移量
  • 将目标函数开头的那段指令覆盖为跳转指令,这条跳转指令指向 trampoline 跳板区

所以 Interceptor 其实是对 inline hook 的一种封装

Frida 检测及绕过

  • 端口检测 : Frida 默认使用 2704227043 端口,可以通过扫描这些端口来检测 Frida 是否在运行

    • 绕过:通过端口转发修改 Frida 使用的端口
  • 检测 /data/local/tmp 目录下的 frida-server 文件

    • 绕过:修改文件名
  • 双进程保护: 一些应用会启动一个守护进程来监控主进程,如果检测到 Frida attach,内核中 TracePid != 0,则杀死主进程,应用闪退

    • 绕过:用 spawn 模式启动
  • 检测 proc/self/maps 文件中是否存在 frida-agent-64.sofrida-agent-32.so 等文件

    • 绕过:
      • 定义一个函数用来阻止字符串的搜索匹配(strstrstrcmp),避免检测到敏感内容如 REJECT Frida
      • 重定向 maps 文件的内容,隐藏特定的库和路径信息
      • eBPF hook 系统调用并修改参数:在内核真正去查找文件之前,挂载在 sys_enter_openat 上的 eBPF 程序被触发,检测到系统调用的第一个参数(路径字符串)是 proc/self/maps, 使用 bpf_probe_write_user 将该内存地址的内容改为事先准备好的抹去了 frida 信息的 maps 文件
        1
        2
        char placeholder[] = "/data/data/.../maps";  
        bpf_probe_write_user((void*)addr, placeholder, sizeof(placeholder));
  • 检测 proc/stack/线程 id/status 文件中是否有 gmain/gdbus/gum-js-loop/pool-frida

    1. Frida 使用 Glib 库,gamain 是主循环事件 GMainLoop 的线程名;
    2. GDBusGlib 提供的用于 D-Bus 通信的库,gdbus 表示 D-Bus 相关线程;
    3. gum-js-loop 表示 Gum(Gum 是 Frida 运行时引擎)执行注入的 js 代码的线程
    4. pool-frida 表示 Frida 中的线程池
    • 绕过:类似上文绕过 maps 文件检测的方式,hook libc.so 中的 strstrstrcmp 函数,一旦检测到敏感字符串就返回未找到
  • 检测 inline hook,比较内存和本地字节,如果不一致则可认为被 inline hook 了

    • 绕过:找到校验逻辑并 hook 掉
  • SVC(Supervisor Call) 指令:软件中断指令,允许用户态程序发起一个系统调用,CPU 从用户态切换到内核态执行特权操作。

    • 绕过:进行内核追踪,找到 SVC 调用出并根据栈回溯往前找到检测逻辑

安卓基础知识笔记
http://example.com/2025/07/02/android_note/
作者
Eleven
发布于
2025年7月2日
许可协议