Go...
Go...
AOSP 与 Android 系统裁剪
Android 开源项目(AOSP)是 Android 操作系统的开源基础。能够编译和修改 AOSP 是进行深度系统级定制、安全研究和 ROM 开发的核心技能。本节将介绍 AOSP 的基本概念、编译流程以及常见的系统裁剪技术。
1. AOSP 基础与编译
a) AOSP 源码同步
编译 AOSP 的第一步是获取其庞大的源代码树。
环境准备:
一个强大的 Linux 构建服务器(推荐 Ubuntu LTS),至少需要 16GB RAM 和 300GB 的可用磁盘空间。安装必要的依赖包,如 git, curl, python, Java SDK 等。获取 Repo 工具: Repo 是 Google 开发的、基于 Git 的代码库管理工具,用于管理 AOSP 中数百个不同的 Git 仓库。
1
2
3
4
5
# 下载 Repo 工具
mkdir -p ~/.bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
chmod a+x ~/.bin/repo
export PATH=~/.bin:$PATH1
2
3
4
5
6
7
8
# 创建工作目录
mkdir aosp && cd aosp
# 初始化源码仓库,指定分支(例如 android-12.0.0_r1)
repo init -u https://android.googlesource.com/platform/manifest -b android-12.0.0_r1 --depth=1
# 开始同步源码(这将是一个漫长的过程)
repo sync -c -j8--depth=1: 只同步最新的 commit,大幅减少下载量。-c: 只同步当前分支。-j8: 使用 8 个线程并行同步。b) 编译流程
设置环境:1
source build/envsetup.sh选择目标 (Lunch):1
lunch aosp_arm64-engaosp_arm64: 目标设备架构(64 位 ARM)。eng: 构建变体(Engineering),包含最多的调试工具,权限为 root,适合开发和逆向。其他还有 user(发布版)和 userdebug(带 root 和调试功能的用户版)。开始编译 (Make):1
m编译产物:编译完成后,所有的系统镜像文件都存放在 out/target/product/
system.img: 系统分区镜像。vendor.img: 厂商分区镜像。boot.img: 启动分区镜像,包含内核和 ramdisk。userdata.img: 用户数据分区镜像。2. 系统裁剪与定制技术
拥有了编译 AOSP 的能力后,你就可以对系统进行任意的修改。
a) 预置与删除 App
路径: App 通常定义在 packages/apps/ 目录下。修改 PRODUCT_PACKAGES: 在特定设备的 device.mk 文件中(例如 device/
这是更深度的定制,可以改变 Android 系统的核心行为。
路径: Framework 核心代码位于 frameworks/base/。示例:修改状态栏逻辑:找到负责状态栏管理的 SystemUI App (frameworks/base/packages/SystemUI/)。修改其中的 Java 或 XML 文件,例如,改变时钟的显示格式或电池图标。重新编译 SystemUI 模块:m SystemUI。只编译模块并生成新的 system.img:m snod (make systemimage-nodeps)。c) 定制内核 (Kernel)
AOSP 默认不包含内核源码。你需要从 Google 的内核源码仓库或设备厂商的开源站点单独下载内核源码,并进行编译。
获取内核源码:1
git clone https://android.googlesource.com/kernel/common.git配置与编译:1
2
3
4
5
6
7
8
# 使用与 AOSP 匹配的交叉编译工具链
export CROSS_COMPILE=.../aarch64-linux-android-4.9/bin/aarch64-linux-android-
# 配置内核
make defconfig
# 编译内核镜像
maked) 制作完整的自定义 ROM
一个完整的自定义 ROM(如 LineageOS)的制作过程,就是上述所有技术的综合应用:
同步 AOSP 基础代码。集成特定设备的驱动和配置文件(Device Tree)。修改 Framework,添加自定义功能(如高级重启菜单)。移除或替换系统 App。集成定制的内核。编译并打包成一个可供用户刷写的 zip 文件。3. Android Linker 与 SO 加载原理
Linker 架构与工作原理
Android 系统使用动态链接器 (/system/bin/linker 或 /system/bin/linker64) 来加载和链接共享库 (.so 文件)。
系统架构
1
2
3
4
5
6
7
8
9
10
11
12
13
Runtime.loadLibrary()
↓
DexPathList.loadLibrary()
↓
nativeLoad() [JNI]
↓
android_dlopen_ext()
↓
do_dlopen() [linker]
↓
find_library_internal()
↓
load_library() → link_image()核心函数分析
find_library_internal - 查找库:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static soinfo* find_library_internal(android_namespace_t* ns,
const char* name,
int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by) {
// 1. 检查是否已加载
soinfo* si = find_loaded_library_by_soname(ns, name);
if (si != nullptr) {
return si;
}
// 2. 在命名空间中搜索
std::string realpath;
if (!find_library_in_namespace(ns, name, &realpath)) {
return nullptr;
}
// 3. 加载库文件
return load_library(ns, realpath.c_str(), rtld_flags, extinfo, needed_by);
}load_library - 加载库:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static soinfo* load_library(android_namespace_t* ns,
const char* name,
int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by) {
// 1. 打开 ELF 文件
int fd = open(name, O_RDONLY | O_CLOEXEC);
// 2. 解析 ELF 头
ElfReader elf_reader(name, fd, file_offset, file_size);
if (!elf_reader.Load(extinfo)) {
return nullptr;
}
// 3. 创建 soinfo 结构
soinfo* si = soinfo_alloc(ns, realpath, &file_stat, rtld_flags, extinfo);
// 4. 映射内存段
if (!si->prelink_image()) {
return nullptr;
}
return si;
}link_image - 链接镜像:
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
bool soinfo::link_image(const soinfo_list_t& global_group,
const soinfo_list_t& local_group,
const android_dlextinfo* extinfo) {
// 1. 解析动态段
if (!phdr_table_get_dynamic_section(phdr, phnum, load_bias, &dynamic, &dynamic_flags)) {
return false;
}
// 2. 处理依赖库
for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_NEEDED) {
const char* library_name = get_string(d->d_un.d_val);
soinfo* lsi = find_library(library_name, ...);
}
}
// 3. 重定位处理
if (!relocate(global_group, local_group)) {
return false;
}
// 4. 调用构造函数
call_constructors();
return true;
}SO 内存布局
1
2
3
4
5
6
7
8
9
0x7000000000 ┌─────────────────┐
│ .text (RX) │ 代码段
0x7001000000 ├─────────────────┤
│ .rodata (R) │ 只读数据段
0x7002000000 ├─────────────────┤
│ .data (RW) │ 可读写数据段
0x7003000000 ├─────────────────┤
│ .bss (RW) │ 未初始化数据段
0x7004000000 └─────────────────┘内存保护设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table,
size_t phdr_count,
ElfW(Addr) load_bias) {
for (size_t i = 0; i < phdr_count; ++i) {
const ElfW(Phdr)* phdr = &phdr_table[i];
if (phdr->p_type != PT_LOAD) continue;
int prot = PFLAGS_TO_PROT(phdr->p_flags);
if (mprotect(reinterpret_cast
seg_page_end - seg_page_start, prot) < 0) {
return -1;
}
}
return 0;
}4. 反调试与检测技术
init 函数中的反调试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__attribute__((constructor))
void anti_debug_check() {
// 检测 Frida
if (access("/data/local/tmp/frida-server", F_OK) == 0) {
_exit(1);
}
// 检测调试器
if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) {
_exit(1);
}
// 检测虚拟机环境
check_emulator_files();
}符号表检测
1
2
3
4
5
6
7
8
9
10
11
12
13
void check_loaded_symbols() {
void* handle = dlopen(NULL, RTLD_NOW);
// 检测 Frida 符号
if (dlsym(handle, "frida_agent_main") != NULL) {
_exit(1);
}
// 检测 Xposed 符号
if (dlsym(handle, "xposed_bridge") != NULL) {
_exit(1);
}
}代码完整性校验
1
2
3
4
5
6
7
8
9
10
void check_code_integrity() {
Dl_info info;
dladdr((void*)check_code_integrity, &info);
// 计算代码段哈希
uint32_t current_hash = calculate_hash(info.dli_fbase, TEXT_SIZE);
if (current_hash != EXPECTED_HASH) {
_exit(1);
}
}5. Frida 绕过技术
绕过 ptrace 检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var ptrace = Module.findExportByName("libc.so", "ptrace");
if (ptrace) {
Interceptor.attach(ptrace, {
onEnter: function(args) {
var request = args[0].toInt32();
if (request === 0) { // PTRACE_TRACEME
console.log("[+] ptrace(PTRACE_TRACEME) blocked");
args[0] = ptr(-1); // 修改参数使其失败
}
},
onLeave: function(retval) {
retval.replace(ptr(0)); // 返回成功
}
});
}隐藏 Frida 符号
1
2
3
4
5
6
7
8
9
10
11
12
13
var dlsym = Module.findExportByName("libdl.so", "dlsym");
Interceptor.attach(dlsym, {
onEnter: function(args) {
this.symbol_name = args[1].readCString();
},
onLeave: function(retval) {
var blocked_symbols = ["frida_agent_main", "xposed_bridge"];
if (blocked_symbols.includes(this.symbol_name)) {
console.log("[+] Hiding symbol: " + this.symbol_name);
retval.replace(ptr(0));
}
}
});绕过 mprotect 检测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var mprotect = Module.findExportByName("libc.so", "mprotect");
Interceptor.attach(mprotect, {
onEnter: function(args) {
var addr = args[0];
var size = args[1].toInt32();
var prot = args[2].toInt32();
console.log("[+] mprotect called: " + addr + ", size: " + size + ", prot: " + prot);
// 防止移除执行权限
if ((prot & 0x4) == 0) { // PROT_EXEC
args[2] = ptr(prot | 0x4);
}
}
});隐藏敏感路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var access = Module.findExportByName("libc.so", "access");
Interceptor.attach(access, {
onEnter: function(args) {
var path = args[0].readCString();
var sensitive_paths = [
"/data/local/tmp/frida-server",
"/system/xbin/su",
"/system/app/Superuser.apk"
];
if (sensitive_paths.some(p => path.includes(p))) {
console.log("[+] Blocking access to: " + path);
args[0] = Memory.allocUtf8String("/non/existent/path");
}
}
});6. init_array 注入技术
Python 脚本注入
1
2
3
4
5
6
7
8
9
10
11
12
from elftools.elf.elffile import ELFFile
def inject_init_array(elf_path, hook_function_addr):
with open(elf_path, 'r+b') as f:
elf = ELFFile(f)
# 查找 .init_array 段
init_array = elf.get_section_by_name('.init_array')
if init_array:
# 在现有函数指针后添加新函数地址
f.seek(init_array['sh_offset'] + init_array['sh_size'])
f.write(hook_function_addr.to_bytes(8, 'little'))C 代码运行时注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void inject_init_array_runtime() {
// 1. 找到目标 SO 加载基址
void* base_addr = dlopen("target.so", RTLD_NOLOAD);
// 2. 解析 ELF 头找到 .init_array 段
ElfW(Ehdr)* ehdr = (ElfW(Ehdr)*)base_addr;
ElfW(Shdr)* shdr = (ElfW(Shdr)*)((char*)base_addr + ehdr->e_shoff);
// 3. 修改内存保护
mprotect(init_array_addr, init_array_size, PROT_READ | PROT_WRITE);
// 4. 添加函数指针
*(void**)(init_array_addr + init_array_size) = target_function;
// 5. 恢复保护
mprotect(init_array_addr, init_array_size, PROT_READ);
}7. 综合反调试检测
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
class AntiDebugChecker {
private:
static bool check_debugger_presence() {
return ptrace(PTRACE_TRACEME, 0, 1, 0) == -1;
}
static bool check_frida_artifacts() {
const char* frida_files[] = {
"/data/local/tmp/frida-server",
"/data/local/tmp/frida-agent-64.so"
};
for (auto file : frida_files) {
if (access(file, F_OK) == 0) return true;
}
return false;
}
static bool check_memory_maps() {
FILE* fp = fopen("/proc/self/maps", "r");
char line[512];
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "frida") || strstr(line, "gum-js-loop")) {
fclose(fp);
return true;
}
}
fclose(fp);
return false;
}
public:
static void comprehensive_check() {
if (check_debugger_presence() ||
check_frida_artifacts() ||
check_memory_maps()) {
// 执行对抗措施
execute_countermeasures();
}
}
};定时检测线程
1
2
3
4
5
6
7
8
void start_anti_debug_thread() {
std::thread([]{
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(5));
AntiDebugChecker::comprehensive_check();
}
}).detach();
}总结
掌握 AOSP 编译和系统定制技术的意义:
深入理解 Android Framework 的工作原理,为 Hook 和逆向提供更底层的视角。通过修改系统来绕过应用层的反分析技术,实现"降维打击"。