Android逆向8-so层逆向知识

来自吾爱破解-正己

https://www.52pojie.cn/thread-1701353-1-1.html

down.52pojie.cn

so加载流程

作用:反调试、脱壳、注入等

image-20250402163251417

函数的基本介绍:

函数名 描述
android_dlopen_ext()dlopen()do_dlopen() 这三个函数主要用于加载库文件。android_dlopen_ext 是系统的一个函数,用于在运行时动态加载共享库。与标准的 dlopen() 函数相比,android_dlopen_ext 提供了更多的参数选项和扩展功能,例如支持命名空间、符号版本等特性。
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(脱壳);

安卓在线源码查看

AOSPXRef

IDA动态调试

  1. 在IDA目录下的dbgsrv,选择跟手机架构一致的server
  2. adb push as /data/local/tmp/
  3. 进入手机端命令:adb shell
  4. 切换获取手机的root权限:su
  5. 跳到对应路径:cd /data/local/tmp/
  6. 提权:chmod 777 as
  7. XappDebug hook

分为两种模式,一种是以debug模式启动,第二种则以普通模式启动,二者的区别在于使用场景,有时候要动态调试的参数在app一启动的时候就产生了,时机较早,所以需要以debug模式去挂起app

1
2
3
4
adb shell am start -D -n com.zj.wuaipojie/.ui.ChallengeEight (去掉-D 则表示不以debug模式启动app)
adb forward tcp:23946 tcp:23946 (端口转发)
adb forward tcp:8700 jdwp:PID (pid监听)
jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700 (jdb挂起)

PS:若不是以debug启动则不需要输入后两条命令其他的步骤具体看视频

佚名大佬:超级详细实战分析一个Crackme的过程

1
2
3
4
@echo on   %关闭回显命令%
start "" cmd /k call IDAdebug2.bat %新打开一个cmd并运行IDAdebug1.bat%
adb shell "su -c './data/local/tmp/as'"
pause

或者adb shell “su -c ‘./data/local/tmp/as’ -端口”

1
2
3
4
@echo on %关闭回显命令%
adb forward tcp:23946 tcp:23946 %端口转发%
adb shell am start -n com.zj.wuaipojie/.ui.ChallengeEight %debug状态启动手机端APP%
pause

可能遇到的问题

1
1.'jdb' 不是内部或外部命令,也不是可运行的程序或批处理文件。

http://t.csdn.cn/paKAt

1
2
3
4
5
6
7
8
9
10
11
12
java.io.IOException: handshake failed - connection prematurally closed
at com.sun.tools.jdi.SocketTransportService.handshake(SocketTransportService.java:136)
at com.sun.tools.jdi.SocketTransportService.attach(SocketTransportService.java:232)
at com.sun.tools.jdi.GenericAttachingConnector.attach(GenericAttachingConnector.java:116)
at com.sun.tools.jdi.SocketAttachingConnector.attach(SocketAttachingConnector.java:90)
at com.sun.tools.example.debug.tty.VMConnection.attachTarget(VMConnection.java:519)
at com.sun.tools.example.debug.tty.VMConnection.open(VMConnection.java:328)
at com.sun.tools.example.debug.tty.Env.init(Env.java:63)
at com.sun.tools.example.debug.tty.TTY.main(TTY.java:1066)
致命错误:
无法附加到目标 VM。
解决方法:有可能是手机问题,建议低版本真机,不要用模拟器!切命令顺序不要乱!另外也有可能软件有反调试!
1
2
3.动态调试中找不到so文件
解决方法:可以尝试手动复制一份对应的so文件放到data/app/包名/lib目录下
1
2
4.device offline
解决方法:重新插拔usb,再不行就重启机子
1
2
3
4
5.0.0.0.0:23946: bind: Address already in use
解决方案:
adb shell "su -c 'lsof | grep 23946'" //获取pid
adb shell "su -c 'kill -9 PID'" //这里的pid要根据上一步获取的填写

常见反调试

  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即为已经被调试
    }

image-20250402171601228

image-20250402171938861

image-20250402184955300

正己大佬的吾爱破解课程第十二节,我用米11p,进行IDA动调so文件,出现以下问题

image-20250403171851784

image-20250403170357390

image-20250403172927849

image-20250403173712162

image-20250404195344309

查了下原因,Android 11+ 对内存访问权限加严从 Android 11(API 30)开始/proc/self/mem 被限制,调试器不能随意访问目标进程内存,默认情况下,即便 attach 成功,也不能访问所有区域;

所以 B40000... 是个典型的 安卓运行时,匿名 mmap 分配的地址段,在高版本设备里这些段几乎都被保护了。

So层防护手段

常见防护手段:

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

LLVM(Obfuscator-LLVM)是瑞士西北应用科技大学安全实验室于2010年6月份发起的一个项目,该项目旨在提供一套开源的针对LLVM的代码混淆工具,以增加对逆向工程的难度,只不过仅更新到llvm的4.0,2017年开始就没在更新。

项目地址

1
源代码(c/c++)经过clang--> 中间代码(经过一系列的优化,优化用的是Pass)--> 机器码

ollvm的分类

分类 描述
指令替换(Instructions Substitution)(Sub) 将一条运算指令替换为多条等价的运算指令,例如:y=x+1变为y=x+1+1-1
虚假控制流(Bogus Control Flow)(bcf) 通过加入包含不透明谓词的条件跳转和不可达的基本块,来干扰IDA的控制流分析和F5反汇编
控制流平坦化(Control Flow Flattening)(Fla) 主要通过一个主分发器来控制程序基本块的执行流程,将所有基本代码放到控制流最底部,然后删除原理基本块之间跳转关系,添加次分发器来控制分发逻辑,然后过新的复杂分发逻辑还原原来程序块之间的逻辑关系
字符串加密 编写一个pass将其中的字符串信息使用一些加密算法进行加密,然后特定的时间进行还原

ollvm对抗

  1. 简单ollvm可以通过交叉引用分析
  2. angr去除不透明谓词
  3. Unicorn/Unidbg/AndroidNativeEmu模拟执行
  4. IDA Trace
  5. binary ninja
  6. 后端编译优化
  7. frida辅助分析

IDA Trace 实战分析ollvm

  1. 在要trace的函数前后下断,触发断点

  2. 配置trace的log输出路径,并选择trace模式

    Instruction tracing 调试器将为每条指令保存所有修改后的寄存器值。

    Basic block tracing 调试器将保存到达临时基本块断点的所有地址。

    Function tracing 调试器将保存发生函数调用或函数返回的所有地址。

  3. run并触发trace

遇到ollvm平坦化怎么办?没事!chatGpt爸爸帮你解决

记一次基于unidbg模拟执行的去除ollvm混淆

Unicorn反ollvm控制流平坦化之bb

ARM64 OLLVM反混淆
RoarCTF2019] polyre 详细WP

移除ollvm中的控制流平坦化、不透明谓词

【reverse】虚假控制流入门:Ubuntu20.04安装ollvm4.0踩坑记+用IDApython去除BCF

基于IDA Python的OLLVM反混淆(一) 手动去混淆

你所需要的对抗ollvm的知识都在这里

ollvm反混淆学习

记使用Trace还原ollvm混淆的函数

frida辅助分析ollvm