简介&&引言
作为寒冰大佬的好基友,经常有人来问我要FART
脱壳机的镜像,今天,它来了!
我制作好了以上版本的全部FART
脱壳机镜像,百度云盘地址和密码在我的github
:https://github.com/r0ysue/AndroidSecurityStudy,大家可以帮我测测,更加新的设备我也会更新上去,如果刷机和脱壳有问题,欢迎联系我一起探讨和研究。
如果还有人不知道FART
是啥,在这里稍微科普下,FART
是ART
环境下基于主动调用的自动化脱壳方案,FART
的创新主要在两个方面:
- 之前所有的内存脱壳机都是基于
Dalvik
虚拟机做的,比如F8LEFT
大佬的FUPK3
,只能在安卓4.4
之下使用,FART
开创了ART
虚拟机内存脱壳的“新纪元”,上至最新的安卓10
甚至还在preview
的安卓11
都可以。 - 在
ART
虚拟机中彻底解决函数抽取型壳的问题;构造主动调用链,主动调用类中的每一个方法,并实现对应CodeItem
的dump
,最终实现完整dex
的修复和重构。
详细的介绍和源码下载地址当然是在大佬的github
:https://github.com/hanbinglengyue/FART
FART
的脱壳是通过修改ART
解释器的源码来实现的,所以我们要重新编译ART
,这就涉及到AOSP
的源码编译。很多人问我是如何编译AOSP
源码的,其实我就是按照官网指南一路操作到底就实现了,但是大家还是不求甚解,甚至一直出错,所以在这里也把最新的AOSP
源码编译方法介绍给大家,大家可以动手试一试,非常简单,不会出错。
最后还兼顾一下大家现在的电脑硬盘都非常小,这样一个现状。本文介绍的流程全部是在SSD移动硬盘
上完成的,型号是海康威视的T200N
,普通的SATA3
,拷贝速度一般在三四百,当然这个速度已经足够了。如果是NVMe
的SSD移动硬盘
,那肯定就更快了。下图就是我的编译环境,挂着SSD
移动硬盘编译的。
2020年的AOSP源码编译指南
话不多说,开始干活。
选择编译目标:安卓10
-
2020
年了,写个安卓4.4
编译估计要被打; - 其实方法都是一样的,毕竟我在这套组合上完成了所有版本的编译;
选择虚拟机编译
- 现在的虚拟机性能跟真机不相上下,
intel
是有专门给虚拟机优化的指令的; - 编译的时候,还能吃着火锅唱着歌,平时该干嘛干嘛,就是性能有些损失
- 最重要的还是因为虚拟机自带“时光机”功能,可以“时光倒流”,比如秀一下寒冰大佬的日常
FART
开发过程:
- 做危险操作的时候,先拍个快照,如果遭遇了重大挫折,赶紧“回到过去”,岂不美哉。
- 其次虚拟机具有良好的跨平台特性,完美支持
Windows
、macOS
和Linux
三大主流桌面平台。可以随时将完整的学习和工作环境整体打包,在各种环境中进行部署和迁移。
- 虚拟机具有良好的隔离特性,做实验不会“污染”真机,是测试全新功能的天然“沙盘”,推荐读者使用
VMware
出品的系列虚拟机。
选择Kali Linux
操作系统
- 为啥选择
Kali
,因为漂亮!
- 主要是因为默认就是
root
,这一点非常爽,没有令人讨厌的权限问题; - 有专门的
VMware
版本,解压就是个虚拟机,省去了安装的过程,下载地址也放在github
的FART
目录里了,是个种子文件,可以用迅雷下载,下载完带checksum
的; - 自带各种抓包软件,自带
openjdk
,自带radara2
; - 跟
Ubuntu
一样的apt
包管理系统,Ubuntu
有的它都有。
下载完之后用certutil -hashfile yourfilename.ext SHA256
命令查看下包是否损坏:
然后解压到SSD移动硬盘
即可。在VMware
里直接选择“文件”→“打开”,选择文件夹里的vmx
文件,即可直接打开(导入)该虚拟机。
因为该虚拟机默认的八十几G的磁盘空间肯定是不够的,我们给他扩展到450G
:
CPU
给2核2线程没问题,内存要给的多,我一般给12G
,也就是12*1024=12288
。少于12G
会报out of memory
错误。
首次启动的时候会问是“移动”的还是“复制”的,貌似没啥区别,我一直按照默认选。默认账号是root
,密码是toor
,即可登录系统,Ctrl + Alt + t
是新建终端的快捷键,这是用的最多的快捷键。
选择清华源初始包
- 若干年前我也挂着科学上网工具从谷歌那里拖源码,整整三天三夜;
- 现在还是觉得清华源真香,直接用迅雷下载这个地址
https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-latest.tar
就行了,下载好了检查下checksum
; - 进入系统后启动
Gparted
软件,在unallocated
部分右击,选择新建,按照默认即可,即可新建这个370G
的ext4
分区,点击选择Apply
,应用到磁盘。然后将这个新建的磁盘给mount
到某个文件夹:
# cd Desktop
# mkdir COMPILE
# mount /dev/sda3/ /root/COMPILE
- 记得到设置里面的
Display
设置里,把所有的息屏、休眠选项都关掉,否则接下来开始拷贝要几十分钟,老是息屏休眠的耽误事儿。Power Manager
里把Display
里的Display power management
关掉,并且把Plugged in
那里的10 min
调成0 never
- 桌面上就出现了
COMPILE
文件夹,双击打开,把刚刚下载的aosp-latest.tar
拖进这个文件夹内部,VMware
是支持直接拖拽的
到这里已经完成了一半儿不止了。
拷贝完成之后记得把VMware
拖动复制的缓存删掉,否则无法进行任何软件包的安装,系统空间被占满了。
# rm -rf /root/.cache/vmware/drag_and_drop/*
- 解压源码
tar
包,耗时大几分钟,根据你的SSD移动硬盘
速度而定
# tar xvf aosp-latest.tar
- 删掉原来的
tar
包节约空间
# rm aosp-latest.tar
准备编译环境
- 准备环境
# apt update
# git config --global user.email "you@example.com"
# git config --global user.name "Your Name"
# apt install bison tree
# dpkg --add-architecture i386
# apt update
# apt install libc6:i386 libncurses5:i386 libstdc++6:i386
# apt install libxml2-utils
- 系统内置的
openjdk 11
太新了,会报错,装个官网要求的openjdk-8
# apt install openjdk-8-jdk
- 下载
aosp
源码版本管理工具,并设置可运行
# curl https://storage.googleapis.com/git-repo-downloads/repo > /usr/bin/repo
# chmod a+x /usr/bin/repo
- 装个看
CPU
、内存、缓存的htop
# apt install htop
- 装个看系统网络流量连接的
jnettop
# wget http://ftp.us.debian.org/debian/pool/main/j/jnettop/jnettop_0.13.0-1+b3_amd64.deb
# dpkg -i jnettop_0.13.0-1+b3_amd64.deb
- 装个窗口分屏软件
tmux
# apt install tmux
- 将源码同步到最新状态
# cd /root/Desktop/COMPILE/aosp/
# repo sync -j8
-j8 是开启八个线程同时下载
这一步会持续非常久,大概要下载十几个G,具体取决于您的网速;
可以用
jnettop
看看网速,如果陷入假死状态,就ctrl-c
终止进程,重新repo sync
环境都准备好了,关机打个快照吧,接下来编译不同版本的AOSP
,都从这里开始。
开始动手编译
- 重新开机后还是先挂载磁盘:
# mount /dev/sda3 /root/Desktop/COMPILE/
- 然后进入该磁盘,进行代码检出和同步操作:
# cd /root/Desktop/COMPILE/aosp/
# mkdir WORKING_DIRECTORY
# cd WORKING_DIRECTORY
# repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-10.0.0_r2
# repo sync --current-branch
init
那一步有个y
要键入下查看安卓版本的网站是AOSP – Codenames, Tags, and Build Numbers,需要科学上网,并且页面语言调成英文噢
- 删除掉源码节约空间
# rm -rf /root/Desktop/COMPILE/aosp/.repo
- 开始编译:导入环境变量
# cd /root/Desktop/COMPILE/aosp/
# source build/envsetup.sh
- 选择设备:
# lunch
Buildtype | Use |
---|---|
user | Limited access; suited for production |
userdebug | Like user but with root access and debug capability; preferred for debugging |
eng | Development configuration with additional debugging tools |
最终刷机的手机是pixel
,并且是用于调试的,要带root
,所以选择16
。
- 开始编译,有多少个核心就是j几。
# make -j8
编译开始后,CPU
会迅速权限飙到百分百,系统开始进行各项准备。
当开始出现百分比读数的时候,基本上进入“体力活儿”阶段,不会再出错了,除非内存给的不够,会报out of memory
错误,给虚拟机加内存就行了,12G
基本可以满足需求。
还有一个容易报错的阶段是编译接近完成时的链接阶段,主要还是因为各种环境和依赖没有满足,当然按照本教程来是不会出现那些问题的,毕竟我们都是同一个环境,所以会是同一个结果。
编译成功
下图为编译完成的画面。
编译完成的系统镜像位于当前目录的out/target/product/sailfish/
下,各类img
就是。
FART自动脱壳机全设备镜像制作
所谓内存型脱壳机,就是直接修改解释器的具体实现源码,在解释器的内存中直接将捕获到的dex
对象给写到存储器中,下述代码是FART
组件一之dump
dex
的核心源码:
const DexFile *dex_file = artmethod->GetDexFile();
const char *methodname =
PrettyMethod(artmethod).c_str();
//确定dex大小
const uint8_t *begin_ = dex_file->Begin();
size_t size_ = dex_file->Size();
memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;
//构造路径
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "%s", "/sdcard/fart");
mkdir(dexfilepath, 0777);
//构造文件名
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "/sdcard/fart/%s",
szProcName);
mkdir(dexfilepath, 0777);
//创建文件
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_dexfile.dex",
szProcName, size_int_);
int dexfilefp = open(dexfilepath, O_RDONLY, 0666);
if (dexfilefp > 0) {
close(dexfilefp);
dexfilefp = 0;
//写入dex内容
} else {
dexfilefp =
open(dexfilepath, O_CREAT | O_RDWR,
0666);
if (dexfilefp > 0) {
write(dexfilefp, (void *) begin_,
size_);
fsync(dexfilefp);
close(dexfilefp);
}
}
下方代码为FART
第二组件构建主动调用链之后dump
方法体并写入bin
文件的核心代码:
const DexFile::CodeItem * code_item =
artmethod->GetCodeItem();
if (LIKELY(code_item != nullptr)) {
int code_item_len = 0;
uint8_t *item = (uint8_t *) code_item;
//获取方法体大小
if (code_item->tries_size_ > 0) {
const uint8_t *handler_data =
(const uint8_t *) (DexFile::
GetTryItems
(*code_item,
code_item->
tries_size_));
uint8_t *tail =
codeitem_end(&handler_data);
code_item_len =
(int) (tail - item);
} else {
code_item_len =
16 +
code_item->
insns_size_in_code_units_ * 2;
}
//根据方法索引写入文件
memset(dexfilepath, 0, 2000);
int size_int = (int) dex_file->Size(); // Length of data
uint32_t method_idx =
artmethod->get_method_idx();
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_%ld.bin",
szProcName, size_int, gettidv1());
int fp2 =
open(dexfilepath,
O_CREAT | O_APPEND | O_RDWR,
0666);
if (fp2 > 0) {
lseek(fp2, 0, SEEK_END);
memset(dexfilepath, 0, 2000);
int offset = (int) (item - begin_);
sprintf(dexfilepath,
"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",
methodname, method_idx,
offset, code_item_len);
int contentlength = 0;
while (dexfilepath[contentlength]
!= 0)
contentlength++;
write(fp2, (void *) dexfilepath,
contentlength);
long outlen = 0;
//对方法体进行编码,防止打开是乱码,使用fart.py进行还原时,会解码
char *base64result =
base64_encode((char *) item,
(long)
code_item_len,
&outlen);
write(fp2, base64result, outlen);
write(fp2, "};", 2);
fsync(fp2);
close(fp2);
if (base64result != nullptr) {
free(base64result);
base64result = nullptr;
}
}
}
加入FART
代码
这一步就非常简单了,相应的将art
、frameworks
、libcore
目录对应路径下的文件替换掉aosp
源码里的文件即可,然后重新make -j8
一遍,即可得到带脱壳功能的aosp
镜像,也就是FART
脱壳机。
寒冰大佬开源的是6.0
的源码,大家手动编译aosp6.0
的FART
脱壳机是没有问题的。
制作镜像刷机
去官网(需科学上网)下载原设备的线刷包,注意下载对应设备并且与aosp
版本相对应的版本,比如我们要下载nexus5x
的aosp6.0
版本,就是下图所示:
手机如何线刷这个不写了吧?百度一下全都是。具体来说就是手机进bootloader
,电脑上直接运行flash-all.sh
就完事儿,当然路径里得有fastboot
命令。
线刷包解压出来,里面会还有个系统zip
包,这个系统zip
再解压出来,才是各种系统的img
。
这时候把我们编译出来的各种img
,替换掉原zip
包的img
,就可以刷进系统了。
总结
总结一下,难点在于编译aosp
系统,只要会编译系统,改源码放进去其实很简单,当然最难的还是如何改源码,这个功力就更深了,再次膜寒冰大佬。
大家如果在编译、刷机和FART
脱壳方面有疑问,欢迎联系我共同探讨。后续我也会进一步分享一些使用FART
脱壳机进行脱壳的实际操作,敬请期待。
发表评论
您还未登录,请先登录。
登录