通过 Google 搜索 android linker namespace 看到官方文档 链接器命名空间 ,在 /linkerconfig/ld.config.txt 文件中配置了相关信息
通过字符串 “Using config section” 定位到 linker_config.cpp 的 parse_config_file 函数,在阅读代码后,我们上面的尝试为什么会失败,就已经有了答案。
虽然有 magisk,替换 system 分区下文件变得方便了起来,但是替换 linker 依然是一件麻烦事,动态 Hook 对于解决这个问题来说,我觉得更加方便。由于目标代码位置的特殊性,现有 inline hook, plt hook 轮子都不能取得比 linker 更早的执行时机。
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
bool
mem_load(const std::string& image, char
*
*
argv, char
*
*
envp){
/
/
创建内存文件,设置这个参数会在
exec
后自动关闭这个文件
int
fd
=
memfd_create(
"/jit-cache"
, MFD_CLOEXEC);
ftruncate(fd, (
long
)image.size());
/
/
设置文件长度
void
*
mem
=
mmap(nullptr, image.size(), PROT_WRITE, MAP_SHARED, fd,
0
);
memcpy(mem, image.data(), image.size());
munmap(mem, image.size());
/
/
此时已经将ELF内容写入到内存文件里面
int
pid
=
fork();
if
(pid <
0
) {
printf(
"mem_load failed\n"
);
/
/
fork 失败
return
false;
}
else
if
(pid
=
=
0
) {
/
/
这里是子进程,使用 PTRACE_TRACEME 主动建立连接
ptrace(PTRACE_TRACEME);
fexecve(fd, argv, envp);
}
/
/
这里是父进程
int
status;
struct user_regs_struct regs{};
struct iovec iov{};
iov.iov_base
=
®s;
iov.iov_len
=
sizeof(user_regs_struct);
while
(true){
wait(&status);
/
/
等待子进程暂停
if
(WIFEXITED(status)){
break
;
/
/
子进程退出
}
/
/
读取通用寄存器,系统调用号
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
if
(regs.regs[
8
]
=
=
__NR_faccessat){
/
/
access函数使用的系统调用号
char path[]
=
"/memfd:"
;
long
word;
/
/
注意,PTRACE_PEEKDATA 固定一次读取 sizeof(
long
) 字节长度的内容
word
=
ptrace(PTRACE_PEEKDATA, pid, regs.regs[
1
], NULL);
if
(strcmp(path, (char
*
)&word)
=
=
0
){
/
/
判断是否是我们添加的目录
ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL);
/
/
单步执行,让系统调用执行完
wait(nullptr);
/
/
等待系统调用执行完
ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
/
/
读取寄存器
regs.regs[
0
]
=
0
;
/
/
修改返回值为寄存器
ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
/
/
修改寄存器
ptrace(PTRACE_DETACH, pid, NULL, NULL);
/
/
detach 进程
return
true;
}
}
ptrace(PTRACE_SYSCALL, pid, NULL, NULL);
/
/
执行到下一个系统调用时暂停
}
return
false;
}