安卓系统编译前置知识
aosp源码、对应的Linux内核、对应的手机驱动 这里的对应指的是要跟aosp系统版本对应,要与手机型号对应 推荐电脑配置 硬盘1t以上 内存 32g以上
android10 源码编译的下载
1. 源码下载
北方用清华镜像源AOSP | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 南方用中科大镜像源AOSP - USTC Mirror Help
3. 下载初始化包并解压
BASH
1
2
3
mkdir ~/bin
cd ~/bin
wget https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-20220222.tar
wget可以使用-c选项,来支持断点下载
PLAINTEXT
1
2
md5sum aosp-latest.tar
tar xvf aosp-latest.tar
4. 配置git
PLAINTEXT
1
2
3
sudo apt-get install git
git config --global user.email 24358757@qq.com
git config --global user.name "tuzi"
5. 下载repo
PLAINTEXT
1
2
3
4
5
6
7
echo "PATH=~/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc
sudo apt-get install curl
curl -sSL 'https://gerrit-googlesource.proxy.ustclug.org/git-repo/+/master/repo?format=TEXT' |base64 -d > ~/bin/repo
chmod a+x ~/bin/repo
export REPO_URL='https://gerrit-googlesource.proxy.ustclug.org/git-repo'
cd aosp
6. 修改默认Python
PLAINTEXT
1
2
sudo unlink /usr/bin/python
sudo ln -s /usr/bin/python3.8 /usr/bin/python
7. 同步指定版本源码
PLAINTEXT
1
2
repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b android-10.0.0_r11
repo sync
(a) 代号和细分版本号可查看以下链接https://source.android.com/setup/start/build-numbers?hl=zh_cn
(b) 选个有驱动的,支持机型多的分支
(c) 谷歌手机设备驱动下载地址https://developers.google.com/android/drivers
(d)同步之前先打个虚拟机快照
repo sync的时候有可能会遇到的问题 info: A new version of repo is available repo: Updating release signing keys to keyset ver 2.3 warning: repo is not tracking a remote branch, so it will not receive updates repo reset: error: Entry ‘.github/workflows/test-ci.yml’ not uptodate. Cannot merge. fatal: 不能重置索引文件至版本 ‘v2.16^0’。
解决方案:
PLAINTEXT
1
2
3
4
cd ~/bin/aosp/.repo/repo
git pull
cd ~/bin/aosp
再次repo init 和 repo sync
android10源码的编译
1、安装JDK8
PLAINTEXT
1
2
3
sudo add-apt-repository ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-8-jdk
2、安装所需依赖 (Ubuntu 20.04)
PLAINTEXT
1
sudo apt-get install git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig libncurses5
参考以下地址https://source.android.com/setup/build/initializing?hl=zh-cn
3、设备驱动的准备
谷歌手机设备驱动下载地址https://developers.google.com/android/drivers
4、查看内核是否存在
源码下载完后最好看下里面有没有你手机对应的设备内核 如果没有,需要另外下载或者编译构建内核 | Android Open Source Project
5、编译源码
编译参考以下链接https://source.android.com/setup/build/building
PLAINTEXT
1
2
3
4
5
6
make clobber
source build/envsetup.sh
lunch # 选择设备内核和编译版本
# 增加编译产品选项 修改aosp/device/google/marlin/AndroidProducts.mk
-j 是cpu 选择适合自己的
make -j24
bonito 为手机代号 编译成功后编译生成的文件在home/tuzi/bin/aosp/out/target/product/bonito 对应管方刷机包里的img文件 都拖进去即可
编译补充
编译报错或者修改系统文件以后,都可以直接make,已经编译的部分会跳过
make clean 会清除已经编译的,全部重来,在编译不同lunch选项时使用
单独编译system.img,在根目录下
PLAINTEXT
1
2
3
source build/envsetup.sh
lunch xxx
make systemimage -j4
单独编译某个模块 mmm packages/apps/tuzi 将单独编译的模块打包到img镜像中 make snod
刷机相关命令
BASH
1
2
3
4
5
6
adb reboot bootloader
fastboot flash boot boot.img
fastboot flash recovery recovery.img
fastboot flash system system.img
fastboot flash bootloader bootloader.img
fastboot boot <recovery_filename>.img
AOSP源码导入到AndroidStudio
先成功编译一次,再使用以下方法导入
在ubuntu系统下,进入源码根目录,运行如下命令, 会在源码目录下的out/host/linux-x86/framework目录下生成了idegen.jar文件
PLAINTEXT
1
2
source build/envsetup.sh
mmm development/tools/idegen/
在源码根目录下继续执行如下命令,会在根目录下生成android.iml和android.ipr两个文件,这两个文件是AndroidStudio的工程配置文件
PLAINTEXT
1
development/tools/idegen/idegen.sh
安装并打开AndroidStudio,选择Open an existing Android Studio project,找到源码根目录,点击Android.ipr
Frida持久化介绍
Hook的前提 需要将代码或者能够完成Hook功能的东西,注入到目标进程中 安卓中注入的方式:zygote注入、ptrace注入、文件感染等
frida-server 利用ptrace注入,需要root权限 为了方便Hook代码修改,还设计成了需要与PC端连接
frida-gadget 当Hook代码修改测试完毕,可以通过它来实现免root、脱离PC 但是它本身没有注入功能,需要将其打包到app中
魔改系统 在app启动过程中,自动加载frida-gadget,就可以不用修改app了,更通用
frida持久化的几种方案
1.Android平台感染ELF文件实现模块注入
2.修改smali,调用System.loadLibrary来加载so
上面两种方法都有成品GitHub - nszdhd1/UtilScript: 一些frida脚本 使用方式见https://bbs.pediy.com/thread-268175.htm 3.修改rom 在app启动过程中加载frida-gadget
修改app启动流程
1. 打开app时判断是否启用持久化
/frameworks/base/core/java/android/app/ActivityThread.java 在以上文件中,添加代码
BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// add
String curPkgName = data.appInfo.packageName;
int curUid = Process.myUid();
if (curUid > 10000) {
Persist.LOGD("curPkgName: " + curPkgName + " curUid: " + curUid);
Boolean isPersist = Persist.isEnablePersist(curPkgName);
Persist.LOGD("isPersist: " + isPersist);
if (isPersist) {
if(Persist.dotuzi(appContext, curPkgName)){
Persist.LOGD("dotuzi is ok");
}else {
Persist.LOGD("dotuzi failed");
};
}
}
// add
2. 增加自定义包和类
BASH
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.tuzi;
import android.content.Context;
import android.util.Log;
import android.os.Process;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import org.json.JSONObject;
public class Persist {
public static final String SO_NAME = "libtuzi.so";
public static final String SO_CONFIG_NAME = "libtuzi.config.so";
public static final String LIB32_DIR = "/system/lib";
public static final String LIB64_DIR = "/system/lib64";
public static final String SETTINGS_DIR = "/data/system/xsettings/tuzi/persist";
public static final String ENABLE_PERSIST_FILE_NAME = "tuzi_persist";
public static final String CONFIG_JS_DIR = "/data/system/xsettings/tuzi/jscfg";
public static final String CONFIG_JS_FILE_NAME = "config.js";
public static final String TAG_NAME = "tuzi_persist";
public static void LOGD(String msg) {
Log.d(TAG_NAME, msg);
}
private static boolean saveFile(String filePath, String textMsg) {
try{
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
fileOutputStream.write(textMsg.getBytes("utf-8"));
fileOutputStream.flush();
fileOutputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private static boolean copyFile(File srcFile, File dstFile) {
try{
FileInputStream fileInputStream = new FileInputStream(srcFile);
FileOutputStream fileOutputStream = new FileOutputStream(dstFile);
byte[] data = new byte[16 * 1024];
int len = -1;
while((len = fileInputStream.read(data)) != -1) {
fileOutputStream.write(data,0, len);
fileOutputStream.flush();
}
fileInputStream.close();
fileOutputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// 判断app是否打开自动注入脚本功能
public static boolean isEnablePersist(String pkgName) {
// 判断文件是否存在 /data/system/xsettings/tuzi/persist/com.tuzi.app/tuzi_persist
File enableFile = new File(SETTINGS_DIR, pkgName + File.separator + ENABLE_PERSIST_FILE_NAME);
return enableFile.exists();
}
// 获取源JS文件路径
private static File getConfigJSPath(String pkgName) {
// /data/system/xsettings/tuzi/jscfg/com.tuzi.app/config.js
return new File(CONFIG_JS_DIR, pkgName + File.separator + CONFIG_JS_FILE_NAME);
}
// 拷贝源JS文件到app私有目录
private static File copyJSFile(Context context, String pkgName) {
// 判断源JS文件是否存在
File srcJSFile = getConfigJSPath(pkgName);
if(!srcJSFile.exists()) {
LOGD("srcJSFile not exists");
return null;
}
// 拷贝源JS文件到app私有目录
// /data/data/com.tuzi.app/files/config.js
File dstJSFile = new File(context.getFilesDir(), CONFIG_JS_FILE_NAME);
boolean isCopyJSOk = copyFile(srcJSFile, dstJSFile);
if(!isCopyJSOk){
LOGD("copyJSFile fail: " + srcJSFile + " -> " + dstJSFile);
return null;
}
return dstJSFile;
}
// 生成Gadget配置文件
private static boolean genGadgetConfig(Context context, File dstJSFile) {
JSONObject jsonObject = new JSONObject();
JSONObject childObj = new JSONObject();
try {
childObj.put("type", "script");
childObj.put("path", dstJSFile.toString());
jsonObject.put("interaction", childObj);
}catch (Exception e){
e.printStackTrace();
return false;
}
String configFilePath = context.getFilesDir() + File.separator + SO_CONFIG_NAME;
boolean isSaveOk = saveFile(configFilePath, jsonObject.toString());
if(!isSaveOk){
LOGD("saveFile fail: " + configFilePath);
return false;
}
return true;
}
// 拷贝源so文件到app私有目录
private static File copySoFile(Context context) {
// 判断源so文件是否存在
// /system/lib/libtuzi.so
// /system/lib64/libtuzi.so
File srcSoFile = new File(LIB32_DIR, SO_NAME);
if(Process.is64Bit()) {
srcSoFile = new File(LIB64_DIR, SO_NAME);
}
if(!srcSoFile.exists()) {
LOGD("srcSoFile not exists");
return null;
}
// 拷贝源so文件到app私有目录
// /data/data/com.tuzi.app/files/libtuzi.so
File dstSoFile = new File(context.getFilesDir(), SO_NAME);
if(srcSoFile.length() != dstSoFile.length()) {
boolean isCopyFileOk = copyFile(srcSoFile, dstSoFile);
if(!isCopyFileOk){
LOGD("copySoFile fail: " + srcSoFile + " -> " + dstSoFile);
return null;
}
}
return dstSoFile;
}
// 进行Frida Gadget持久化
public static boolean dotuziPersist(Context context, String pkgName) {
File dstJSFile = copyJSFile(context, pkgName);
if(null == dstJSFile) return false;
if(!genGadgetConfig(context, dstJSFile)) return false;
File dstSoFile = copySoFile(context);
if(null == dstSoFile) return false;
System.load(dstSoFile.toString());
return true;
}
}
3. 将自定义包加入白名单
/build/make/core/tasks/check_boot_jars/package_whitelist.txt
BASH