【Android安全-关于最近脱壳的笔记 还请各位大佬指教】此文章归类为:Android安全。
近期整理的脱壳笔记有点多
笔记地址
https://github.com/madking177/zzj_md/blob/main/%E9%80%86%E5%90%91/%E8%84%B1%E5%A3%B3.md
视频地址
整体壳
https://www.bilibili.com/video/BV1iZ421e7Xs/?spm_id_from=333.999.0.0
类加载器
https://www.bilibili.com/video/BV1yF4m1N7sb/?spm_id_from=333.788&vd_source=d1067370dca5b90f8acffbc140968f18
加壳流程
1.原始dex加密
2.记录dex还原信息
3.壳dex尾部追加加密的dex
4.追加dex还原信息(加密dex偏移量等还原信息)
5.追加还原信息长度(末尾4字节)
6.filesize:dex原来长度+dex加密字节长度+dex还原信息长度+4字节(还原信息长度)
6.计算signature(除了标识头,checksum其余的hash值)
7.计算checksum(除了标识头,checksum外所有)
8.重新打包
实现还原:
被保护apk+脱壳dex 合并生成新的dex 重新签名打包apk
打包后的结构
简述
通用脱壳工具好使,遇到反脱壳的检测手段稍微上点难度就用不了。
dexdump,blackdex,fart
dumpsys window windows | grep mCurrentFocus
adb shell dumpsys window windows | grep mCurrentFocus mCurrentFocus=Window{e415d0f u0 com.chaoxing.mobile/com.chaoxing.mobile.main.ui.MainTabActivity}
1 2 3 4 5 6 7 8 9 10 11 | 核心原理:暴力搜索 git地址:https: / / github.com / hluwa / frida - dexdump?tab = readme - ov - file 公众号地址:https: / / mp.weixin.qq.com / s / n2XHGhshTmvt2FhxyFfoMA frida - dexdump - U - n com.chaoxing.mobile - o . / xuexitong - d - - sleep 5 - o 输出目录 - d 深度搜索 - - sleep 等待?s开始脱壳 结果:扫描出来一堆没用的dex文件 |
1 2 3 4 5 6 7 | 显示 all done说明结束 然后整体编译刚脱壳出来的文件 jadx - d <输出目录路径> * .dex jadx - d output * .dex 如果hook时间不合适 如下图 |
blackdex地址 https://github.com/CodingGay/BlackDex/releases
开始抓包(分析三个变化参数 inf_enc明显是个hash串,token没有变化过)
分析笔记页面 多刷新几次 观察参数变化
这是一个变化参数 因为同接口刷新几次以后发现-time,inf_enc变化
搜索inf_enc发现上文在拼接一个stringbuilder函数
1 2 3 | 深入探讨文章地址 https: / / blog.niunaijun.top / index.php / tag / BlackDex / |
职责:类加载器的职责是寻找和加载类
根据用途划分
系统加载器
扩展类加载器
应用程序加载器
用户自定义加载器
双亲委派
从下面往上找 截止顶部结束
从上向下加载到底部或者加载成功结束,否则classnotfound error
目的
执行一次
避免核心类库被替换
简单说 不同加载其有不同职责,职责若干 加载器若干!
引导类加载器(BootClassLoader C++),扩展加载器,应用程序加载器(统称 原生的系统类加载器)
主要还是因为了解不够的情况下 分不太清
用户类自定义类加载器
注意:类加载器的层次关系不代表是类加载器的继承关系 如下图
第一层
classloader 抽象 定义
secure classloader 继承+安全权限 定义
urlclassloader 通过url路径从jar文件和文件夹加载类和资源
注意:
开发代码上区别不大,但是jdk下的类加载与art下类加载并不同
art,jdk都是虚拟机 并遵守了jvm设计上的一些规范 art完全兼容支持 jvm 这也是java可以在sdk环境运行的主要原因
都是引导类加载器 解释如下
注意:真实情况下的类加载器数不胜数
bootclassloader(核心类库),pathclassloader(扩展),dexclassloader(app程序)
dexclassloader继承basedexclassloader
可加载dex相关文件
pathclassloader继承baseclassloader
加载系统类和应用程序类
加载类介绍
zygote进程
main方法是zygoteinit的入口
调用zygoteinit的preload
zygote的preloadClasses(初始化时候预加载常用类)
罗列部分类名
结论就是预加载越多(通用) 后期app启动就会越快
结论
art下的BootClassLoader用于在zygote的初始化阶段创建 用于预加载类的加载
总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 假设 dex 文件位于应用的 files 目录下,名为 "classes2.dex" File dexFile = new File( "/data/local/tmp/classes.dex" ); try { // 使用 PathClassLoader 加载 dex 文件 // PathClassLoader pathClassLoader = new PathClassLoader(dexFile.getAbsolutePath(), getClassLoader()); // 使用 DexFile 来获取 dex 文件中的类 DexFile dex = new DexFile(dexFile); Enumeration<String> entries = dex.entries(); // 遍历 dex 文件中的类 while (entries.hasMoreElements()) { String className = entries.nextElement(); Log.d( "dex-test" , className); // 你可以在这里进行类的进一步操作,例如实例化或调用方法 } } catch (IOException e) { e.printStackTrace(); } |
1 | PathClassLoader pathClassLoader = new PathClassLoader( "/data/local/tmp/classes.dex" ); |
输出
(逆向分析)art虚拟机中只涉及优化器和后端
编译模式迭代过程(jit just-in-time,aot ahead-of-time)
so文件就是elf(理解elf最好的方式就是写一个解析工具解析一下so文件)
可用readelf工具解析elf
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 | elf头部定义得伪代码 #include <cstdint> / / ELF文件头部结构体 struct ElfHeader { uint8_t magic[ 4 ]; / / Magic Number uint8_t file_type; / / 文件类型 uint8_t machine_type; / / 机器类型 uint8_t version; / / 版本号 uint64_t entry_point_addr; / / 入口地址 uint64_t program_header_offset; / / 程序头表偏移 uint64_t section_header_offset; / / 节头表偏移 uint32_t flags; / / 标志 uint16_t header_size; / / 头部大小 uint16_t program_header_entry_size; / / 程序头表条目大小 uint16_t num_program_headers; / / 程序头表条目数量 uint16_t section_header_entry_size; / / 节头表条目大小 uint16_t num_section_headers; / / 节头表条目数量 uint16_t string_table_index; / / 字符串表索引 }; / / 定义ELF文件头部的魔术数字 const uint8_t ELF_MAGIC[ 4 ] = { 0x7F , 'E' , 'L' , 'F' }; / / 根据实际情况定义其他常量和结构体 |
头部信息解释
安卓打包
string_ids 索引所有字符串
type_ids 储存所有类型(类类型,基本类型)
proto_ids 函数原型
fields_id 类变量
存放类信息,相比dex文件更复杂,因为有数据指向了data区
public
, private
, final
等。registers_size
: 该方法使用的寄存器数量。ins_size
: 方法的输入参数数量。outs_size
: 方法的输出参数数量。tries_size
: 异常处理器列表的大小。debug_info_off
: 调试信息的偏移量,指向方法的调试信息。insns_size
: 方法的指令数量。insns
: 方法的指令数组,包含方法的所有指令。总结:
虚拟化技术让app进程运行,加固壳自解密
前置原理
基于blackbox开发的
简单说在blackdex app中安装一个安卓系统,在这个安卓系统中安装需要脱壳的app,然后基于dexfile结构体和加载原理脱壳进行代码还原
dex加载进内存
application通过loadedapk#makeapplication完成
找到海量c++脱壳点
彩蛋部分提供的脱壳原理基于Android ART(Android RunTime)环境的特定运行机制,特别是其对DEX(Dalvik Executable)文件的处理方式。以下是彩蛋部分脱壳原理的详细解读:
1. ART编译机制与脱壳
dex2oat: 将编译的dex文件(编译代码的中间表示形式)编译成art虚拟机可以直接执行的oat机器码
编译粒度: oat编译是函数粒度的编译,但有一部分函数不会被预编译,而是动态编译执行。
脱壳机会: 壳会干扰dex2oat
的编译过程,即使预编译的代码也不一定会执行,那么结论就是还是倾向于动态编译执行,这就是脱壳机会,拿到dex句柄或者指针,从而获得dex尺寸和偏移量。
上图右下角 art下类加载流程 经历了 loadclass ---> loadclassmember(一个脱壳点) ->linkcode的过程(逻辑推理 就是代码执行过程中需要什么就提前加载什么到自己的作用域,如指令,类,方法等)
insns_size代表smali数组长度
insns代表smali数组 通常两个字节数组
抽取壳子的主要下手思路(占坑型或重构型策略,)
把codeitem中insns的smali数组全部nop掉
code_off链接到代码,破坏虚拟机加载好的codeitem区域,建立新的codeitem 区域,代码执行之前修改code_off指向正确的codeitem的地址区域(用户指定地址下的codeitem区域)
总结就是:
直接,间接有dexfile引用的地方都可以整一个dump点,进行整体dex的脱壳
Android ART环境中的一种通用、简单且高效的内存中DEX脱壳方法。这种方法利用了ART类加载执行流程中的关键类ArtMethod
及其相关函数,通过Hook技术(如Xposed或Frida)实现对加固应用的脱壳。以下是该方法的概述和实现细节:
ActivityThread.main()
是App进程的入口,负责启动主消息循环和初始化关键组件。handleBindApplication
方法在接收到系统bindApplication
请求时被调用,完成应用的真正启动过程。LoadedApk
、ContextImpl
和Instrumentation
对象,然后创建并初始化Application
对象。Application
的attachBaseContext
和onCreate
方法是App代码执行的最早入口,它们分别负责与应用上下文关联和执行全局初始化逻辑。这也是加固工具常选择在此处进行干预(如代码脱壳、安全检查、权限控制等)的原因。
加壳原理与运行流程:
Application
类的attachBaseContext
和onCreate
方法作为切入点,因为它们是App启动时最先获得执行权限的函数。在这些方法中,壳程序执行加密DEX文件的解密操作。ClassLoader
在内存中加载解密后的DEX文件。这种自定义ClassLoader
通常绕过标准的类加载机制,直接加载内存中的DEX数据,避免在磁盘上留下未加密的痕迹。ClassLoader
。如果不修正ClassLoader
,双亲委派机制会导致系统无法找到解密DEX中的类,引发ClassNotFoundException
,导致应用崩溃。ClassLoader
,攻击者可以通过一系列反射操作获取到当前应用所加载的解密后的内存中DEX文件,实现对加密内容的提取。对抗与反制:
将dex文件和bin文件 拼接搞定的抽取壳的smali代码
整体壳dump+主动调用
APP中的Application类中的attachBaseContext和onCreate函数是app中最先执行的方法。壳都是通过替换APP的Application类并自己实现这两个函数,并在这两个函数中实现dex的解密加载,hook系统中Class和method加载执行流程中的关键函数,最后通过反射完成关键变量如最终的Classloader,Application等的替换从而完成执行权的交付。、
dumpMethodCode
activityThread中的performLaunchActivity 函数作为时机,获取最终classloader,还有一个好处该函数与最终的application都在activityThread类中
通过classloader得到mcookie (so层dexfile的句柄) 在framework添加两个函数提供调用 dumpDexFile,dumpMethodCode
调用art的invoke完成方法的主动调用
在invoke中判断 如果是我们自己的调用 使用dump 然后返回从而完成对壳的欺骗
对java层传来的method结构体进行类型转换,转成native的ArtMethod对象,接下来调用artMethod的myfartinvoke实现,并完成方法体的dump,
artMethod的invoke我们在第一个参数Thread传递null标志作为主动调用的标识,
(art原理将的不是很好 因为没有手动去做实验所以并不知道里面的坑,移植fart编译rom镜像之后再给大家汇报成果)
网页上就能搜到 (https://zhuanlan.zhihu.com/p/573577231)
找pixel ,pixel xl2,nexus5三种机型的fart镜像(百度就有)
刷机文章链接
https://blog.csdn.net/blackblackblack223/article/details/137341966?spm=1001.2014.3001.5501
镜像成功以后装需要的app,在/sdcard/xxxx下面找自己需要脱的代码包
脱壳以后的代码
对dex进行反编译
切换到smali视角
可以得到类的信息和函数的smali信息 说明dex的整体是抽取下来了,问题是代码全部是nop 怎么办
这里就需要做第二件事情了,被抽取smali的还原。
这里运行hanbing大佬写的fart python脚本还原
可以还原但是有一个问题,python2版本太老了,并且smali代码看着不习惯
因为找到了github的第二个项目 dexrepair,luoyesiqiu写的还原dex文件的抽取代码的项目
https://github.com/luoyesiqiu/DexRepair
结论:
出发点是好的,但是由于fart脚本的迭代,导致项目的适用性变差,思路一致 ,但是还原,解析格式的字节偏移量也需要做一些相应的改动,虽然只是单纯的解析脚本,但是对于不懂dex抽取壳子,codeitem原理的同学来说会有一些吃力。
三个区
类id,访问标志,父类id,接口偏移量,源文件id,注解偏移量,类数据偏移量,静态值偏移量等
class_data_off链接到数据库的class_data_item中(从索引区链接到数据区了)
encodeed_field
描述静态变量,类变量 (类变量 id,访问权限标志)
encoded_method
注意:
描述类变量,静态变量,类方法,静态方法都用的数组!!!!
描述结构体都加了idx_off后缀,因为除了第一个id真的存id以外,后面的所有id都存的是相对于前一个id的差值,这种匪夷所思的设计据说是为了避免id太大造成内存浪费。
classdef 到class_data_item 到 encode_method的code_off
code_off 就是codeitem的偏移量了
针对二代壳 codeItem里面全部是重点,确定一下有什么
!!!! 注意以上全部是两字节
到此为止 后面不分析了(数据够了)
抽取壳就是把insns给抽取掉了
这块借鉴寒冰大佬总结的
抽取壳 大多情况两种思路
修改class_data_item里面encoded_method的code_off 偏移量指向正确codeItem才能执行正确代码,原有的codeItem被破坏掉了
将code_item中insns全部抹0
以后造成的结果就是 除非执行前还原,否则离线分析,codeitem里面的代码永远都是坏的
dexrepair里面代码没什么 ,就是解析,定义和dex文件一定的数据结构(解析需要的就行),然后计算偏移量
适配 dexrepair 中对应fart的偏移量
解析地址是对的,但是函数没正确还原出来(就是dexRepair和fart版本没对应上)
经过按照上述原理修正偏移量和部分适配逻辑以后执行的代码在010editor中去验证
修复之前 除了大面积的 00 00 00 00
修复以后
jadx反编译验证(二代抽取壳抽取的smali代码)
抽取壳代码还原以后
反调试,反脱壳有时候脱壳前需要解决的问题,特征屏蔽。在脱壳之前加一个证书几乎无解
目前能把二代壳还原可以更大程度解决一些问题
现状
脱壳脱不下来(脱壳闪退 运行了但是壳脱不下来)
加hook有反调试 (反调试 找不到地方)
抓包有证书双向校验 (不知道证书在哪里)
改aosp 自编译系统镜像破局
rpc ,objection,frida-trace,frida-talker ,等基于frida的上层工具去进行辅助
总结到目前全是java层的内容居多
娜迦
libchaosvmp.so , libddog.solibfdog.so
爱加密
libexec.so, libexecmain.so,ijiami.dat
梆梆
libsecexe.so, libsecmain.so,libSecShell.so
梆梆企业版
libDexHelper.so , libDexHelper-x86.so
360
libprotectClass.so, libjiagu.so
libjiagu.so, libjiagu_art.so
libjiagu.so, libjiagu_x86.so
通付盾
libegis.so,libNSaferOnly.so
网秦
libnqshield.so
百度
libbaiduprotect.so
阿里聚安全
aliprotect.dat,libsgmain.so,libsgsecuritybody.so
腾讯
libtup.so, libexec.so,libshell.so
mix.dex
lib/armeabi/mix.dex ,lib/armeabi/mixz.dex
腾讯御安全
libtosprotection.armeabi.so,
libtosprotection.armeabi-v7a.so,
libtosprotection.x86.so
网易易盾
libnesec.so
APKProtect
libAPKProtect.so
几维安全
libkwscmm.so, libkwscr.so, libkwslinker.so
顶像科技
libx3g.so
特征:
确定是爱加密的壳
反调试思路 为了不阻塞app主进程 一般都是异步检测
hook create_pthread 进行绕过解决frida反调试问题
frida可以注入后dump出libexec的so文件(源文件加密 dump运行时明文) 用sofix进行还原 可以在ida中正常打开
刷fart8 到手机脱壳
使用dexrepair进行二代壳还原
加上双向证书绕过的代码
进入正常分析流程
数据可能会加密 通过r0capture 抓包,但是最上层的堆栈都是线程的run方法 因为通过hook Thread 的构造函数关联到r0capture的打印堆栈,即可将加密的数据代码与流量堆栈快速关联起来
objection,frida-trace,frida-stalker辅助定位代码,strace,readelf等辅助分析so文件
更多【Android安全-关于最近脱壳的笔记 还请各位大佬指教】相关视频教程:www.yxfzedu.com