安卓那档事07

so加载流程

img

这些函数的介绍:

函数名 描述
android_dlopen_ext()dlopen()do_dlopen() 这三个函数主要用于加载库文件。android_dlopen_ext 是系统的一个函数,用于在运行时动态加载共享库。与标准的 dlopen() 函数相比,android_dlopen_ext 提供了更多的参数选项和扩展功能,例如支持命名空间、符号版本等特性。
可能在hook一些so的时候,so加载比较早就通过这个去hook原本要hook的方法
find_library() find_library() 函数用于查找库,基本的用途是给定一个库的名字,然后查找并返回这个库的路径。
call_constructors() call_constructors() 是用于调用动态加载库中的构造函数的函数。
init 库的构造函数,用于初始化库中的静态变量或执行其他需要在库被加载时完成的任务。如果没有定义init函数,系统将不会执行任何动作。需要注意的是,init函数不应该有任何参数,并且也没有返回值。
init_array init_array是ELF(Executable and Linkable Format,可执行和可链接格式)二进制格式中的一个特殊段(section),这个段包含了一些函数的指针,这些函数将在main()函数执行前被调用,用于初始化静态局部变量和全局变量。
jni_onload 这是Android JNI(Java Native Interface)中的一个函数。当一个native库被系统加载时,该函数会被自动调用。JNI_OnLoad可以做一些初始化工作,例如注册你的native方法或者初始化一些数据结构。如果你的native库没有定义这个函数,那么JNI会使用默认的行为。JNI_OnLoad的返回值应该是需要的JNI版本,一般返回JNI_VERSION_1_6

下断点时机:

  • 应用级别的:java_com_XXX;
  • 外壳级别的:JNI_Onload,.init,.init_array(反调试);
  • 系统级别的:fopen,fget,dvmdexfileopen(脱壳)

System.loadLibrary()

在android我们通常使用System.loadLibrary或者System.load来加载so文件

1
2
3
4
//加载的是libnative-lib.so,注意的是这边只需要传入"native-lib"
System.loadLibrary("native-lib");
//传入的是so文件完整的绝对路径
System.load("/data/data/应用包名/lib/libnative-lib.so")
  • loadLibrary传入的是编译脚本指定生成的so文件的名称,而load需要传入完整的so文件所在的绝对路径;

  • load并不是随便路径都可以,只支持应用本地存储路径/data/data/${package-name}/,或者是系统lib路径system/lib等,这2类路径;

  • load如果传入的是sdcard路径(SD卡路径),则会导致加载失败,可以采用将sdcard下的so文件复制到应用本地存储路径下进行加载;

  • loadLibrary加载的都是一开始就已经打包进apk或系统的so文件了,而load可以是一开始就打包进来的so文件,也可以是后续从网络下载,外部导入的so文件。

  • 最终都是调用nativeLoad加载指定路径的so文件;

IDA动态调试

查看另一篇ida调试so学习

常见寄存器

寄存器名称 功能介绍
R0-R12 通用寄存器,用于存储临时数据。在函数调用时,R0-R3用于存储前四个参数,其余的参数通过堆栈传递。返回值也通过R0R1传递。
R13 (SP) 堆栈指针寄存器,指向当前堆栈的顶部。
R14 (LR) 链接寄存器,存储子程序的返回地址。
R15 (PC) 程序计数器,指向下一条要执行的指令。
CPSR 当前程序状态寄存器,保存程序的状态信息,如算术运算的结果标志、中断禁止标志等。
FPSCR 浮点状态和控制寄存器,如果你在调试的代码中涉及到浮点运算,那么这个寄存器会比较重要。

防护手段

反调试

1.调试端口检测 检测常见的23946端口,所以在运行时可以加 -p 指定一个另外的端口来过掉这个检测 2.调试进程名检测 固定的进程名 android_server gdb_server等等,所以要改个名字,例如as64 3.ptrace检测 每个进程同时刻只能被1个调试进程ptrace ,主动ptrace本进程可以使得其他调试器无法调试 实现代码:

1
2
3
4
int ptrace_protect()//ptrace附加自身线程 会导致此进程TracerPid 变为父进程的TracerPid 即zygote
{
return ptrace(PTRACE_TRACEME,0,0,0);;//返回-1即为已经被调试
}

SO层防护

常见防护手段:

主要功能 描述
SO加壳 对C/C++源码编译出来的SO文件进行加壳,使SO文件无法正确反编译和反汇编。
SO源码虚拟化保护 将原始汇编指令翻译为自定义的虚拟机指令,跳转到自定义的虚拟机中执行,每次保护生成的虚拟机指令随机,且对虚拟机解释器再度混淆
SO防调用 对SO文件进行授权绑定,防止SO文件被非授权应用调用运行。
SO Linker 对整个SO文件进行加密压缩,包括代码段、符号表和字符串等,运行时再解密解压缩到内存,从而有效的防止SO数据的泄露。
SO源码混淆 常量字符串加密、分裂基本块、等价指令替换、虚假控制流、控制流平坦化。
SO环境监测 防frida、防动态调试、防模拟器、防多开等