-
-
[原创]窥探Proot原理
-
发表于: 2025-3-5 21:07 29441
-
引导视频(大佬的视频):
编译教程:
源码地址:
跟踪进程行为:允许一个进程(跟踪者,tracer)观察和控制另一个进程(被跟踪者,tracee)的执行。
读写内存和寄存器:可以修改被跟踪进程的内存和寄存器状态。
拦截信号和系统调用:在系统调用(syscall)或信号(如 SIGTRAP
)触发时暂停被跟踪进程,供跟踪者处理。
通过 ptrace(request, pid, addr, data)
发送请求,常见 request
参数包括:
PTRACE_TRACEME
被跟踪进程主动声明自己被父进程跟踪(常用于子进程)。
PTRACE_ATTACH
跟踪者附加到正在运行的进程(需权限)。
PTRACE_DETACH
解除跟踪,恢复被跟踪进程执行。
PTRACE_PEEKTEXT
/PTRACE_POKETEXT
读/写被跟踪进程的内存。
PTRACE_GETREGS
/PTRACE_SETREGS
读/写被跟踪进程的寄存器(如 eax
, ebx
等)。
PTRACE_SYSCALL
使被跟踪进程在下一个系统调用入口和退出时暂停(用于拦截系统调用)。
PTRACE_CONT
恢复被跟踪进程的执行。
拦截系统调用:通过 PTRACE_SYSCALL
,跟踪者可以在被跟踪进程执行系统调用前(入口)和系统调用后(退出)获取控制权。
修改参数/返回值:
在入口阶段:可修改系统调用的参数(通过寄存器,如 eax
存储系统调用号,ebx
, ecx
等存储参数)。
在退出阶段:可修改系统调用的返回值(如模拟成功或失败)。
开发涉及到的关键API都是直接参考官方文档:
基础理论 到这了 ! 我们来进行实践:
成功的在安卓实现了 使用ptrace 对 主进程的openat的系统函数进行监控
首先还是从main开始分析:
在这个代码中一来就出现不认识的结构体 Tracee
那么我们先了解这个结构体
观察上面的结构体 ,我用ai做了翻译,很清楚的知道 Tracee 这个结构体就是被观测的进程。
我们不关注它的内存管理,我们只关心 它主要的运行逻辑。
好,那么接下来就是创建的一个 Tracee get_tracee(NULL, 0, true)这个方法
看看:
在tracee.h 存在该方法的定义:
ok,不做深入,进行下一步 parse_config这个方法
这里根据参数进行初始化,也就是对proot 后面输入的参数 进行 对应的初始化
后面启动进程:
launch_process
这里创建了 一个子进程 并且 开启了seccomp
我们看看 :enable_syscall_filtering
这里就是设置 设置seccomp过滤器 的地方
完成后就开始 主进程就开始循环处理了
event_loop()
我们跟进 handle_ptracee_event 看看它是如何处理ptrace的调用的?
然后就是对 tracee的处理handle_tracee_event
接着就是系统调用的处理了!
translate_syscall
这里面就有处理 syscall 的函数
这里就对 syscall的函数进行的修改
到这里 就基本搞清楚 proot的实现流程 与我们实现的小 demo 是基本差不多的(假的)
先进行小小的整理一下:
分主进程 与 子进程, proot 对子进程进行 attach 主进程来对 信号进行处理 以及仿真
设置 seccomp 对 ptrace的速度进行优化
那我们接下来 对 proot进行移植到安卓上!
这里使用的环境: Uabntu 22 android Studio Clion
我首先按照教程 ,在clion上实现 然后照猫画虎 移植到 android
cmakelist:
依赖文件 就按照 官网的把源代码下载下来。
后面我的github 会上传源码 这里就完成了proot的移植了(这里我在linux 上 编译arm64成功)
对于子进程干了那些事?
声明可跟踪性:通过 ptrace(PTRACE_TRACEME) 将当前进程标记为可被跟踪。
同步状态:通过 SIGSTOP 暂停自身,确保 tracer 能够捕获初始状态。
性能优化:启用 seccomp 模式(如果未禁用)。
执行目标程序:调用 execvp 替换当前进程为目标程序。
我们主要的侧重点 应该放在 event_loop()这个循环里面干的事!
这个event_loop() 到底干了什么:
在主事件循环 中,就设计到 处理 进程的syscall调用,以及对ptrace的仿真。
下面对他进行 模块化分析 方便后续满足自己项目的需要
设想一个ptrace函数触发
那么会出现在 event_loop() 的这个部分进行判断
会判断这里是不是有tracee->as_ptracee.ptracer 存在
那么一开始 在前面 我也没看到对这个设置的代码 猜测 在系统调用的处理handle_tracee_event
首先会调用 translate_syscall_enter 这个函数
确实存在对ptrace的处理
后面的处理
处理就是将ptrace的 中断调用号变成 0 ? 大概就是设置成无效吧
当然 这是处理前
还有函数调用后呢
translate_syscall_exit(Tracee *tracee) 呐,这个就是了
跟进translate_ptrace_exit
这段代码就非常长了 ,显然就是在模拟ptrace了
这里确实与之前 猜测的一样
这个代码 就设置 上了as_ptracee.ptracer 就对上了 event_loop
这里就可以 进入 handle_ptracee_event
这个函数也是处理ptrace的事件 只不过 是proot仿真的 仅对我们跟踪的函数有效
到这里 proot 对ptrace 的仿真就到这里了
说白了 自己维护一个假的ptrace 给 子进程用 。
我打算将proot 能用的部分拆分下来 供自己实现项目 奈何功力不足 但是柳暗花明又一村,我发现我之前看不懂的abyss项目的 demo代码 就是 魔改的proot ,天助我也。
发现差不多的 在循环部分 处理好tracee 进行 attach 其他地方差不多的
到这里就是 我对proot理论部分的学习了 接下来 开始 自己 的 svc hook框架搭建
上面的代码分析仅仅是我个人的观点,难免会有疏漏之处。望大佬勿喷,但欢迎指正。
https://bbs.kanxue.com/thread-275511.htm
https://bbs.kanxue.com/thread-273160.htm
#include <string>
#include <unistd.h>
#include <android/log.h>
#include <linux/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/elf.h>
#include "Syscall_arm64.h"
#define LOG_TAG "NDK_LOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
void
test();
void
ptrace_attach_pid(
int
pid);
void
install_seccomp_filter(){
struct
sock_filter filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, nr))),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct
sock_fprog prog = {
.len = (unsigned
short
) (
sizeof
(filter) /
sizeof
(filter[0])),
.filter = filter,
};
if
(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
LOGD(
"prctl(PR_SET_NO_NEW_PRIVS)"
);
}
if
(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
LOGD(
"when setting seccomp filter"
);
}
}
void
test() {
LOGD(
"test begin ..."
);
int
mainPid = getpid();
int
childPid = fork();
switch
(childPid) {
case
-1:
LOGE(
"fork fail"
);
break
;
case
0:
LOGD(
"子进程 逻辑 %d"
, childPid);
LOGD(
"mainPid=%d"
, mainPid);
ptrace_attach_pid(mainPid);
break
;
default
:
LOGD(
"主进程 逻辑 %d"
, mainPid);
install_seccomp_filter();
kill(getpid(), SIGSTOP);
kill(getpid(), SIGCONT);
LOGD(
"waitpid"
);
break
;
}
LOGD(
"test end ..."
);
}
void
ptrace_attach_pid(
int
pid) {
int
status;
if
(ptrace(PTRACE_ATTACH,pid,0,0) == -1){
LOGE(
"ptrace attach fail"
);
}
//设置 ptrace 选项
const
unsigned
long
default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
//当被跟踪的进程产生一个系统调用时,会发送 SIGTRAP 信号,并且 siginfo 结构中的 si_code 会被设置为 SYS_SECCOMP。
PTRACE_O_TRACEFORK |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEVFORK |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEVFORKDONE |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACECLONE |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEEXEC |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEEXIT );
//当被跟踪的进程退出时,会触发跟踪事件
int
state;
state = ptrace(PTRACE_SETOPTIONS, pid, 0, default_ptrace_options | PTRACE_O_TRACESECCOMP);
//这个选项特别重要,它允许跟踪 seccomp 过滤器触发的事件。当被跟踪的进程因为 seccomp 规则而触发一个 SIGSYS 信号时,会发送一个 SIGTRAP 信号,并且 siginfo 结构中的 si_code 会被设置为 SYS_SECCOMP。
if
(state == -1){
LOGE(
"PTRACE_SETOPTIONS failed"
);
}
waitpid(pid, &status, 0);
if
( WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP){
while
(1){
struct
user_regs_struct regs;
struct
iovec io;
io.iov_base = ®s;
io.iov_len =
sizeof
(regs);
ptrace(PTRACE_CONT, pid, 0, 0);
waitpid(pid, &status, 0);
ptrace(PTRACE_GETREGSET, pid, (
void
*)NT_PRSTATUS, &io);
if
(status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)) ){
LOGD(
"seccomp svc 中断调用号 : %lx"
,regs.regs[8]);
LOGD(
"seccomp svc 寄存器0内容: %lx"
,regs.regs[0]);
LOGD(
"seccomp svc 寄存器1内容: %lx"
,regs.regs[1]);
LOGD(
"seccomp svc 寄存器2内容: %lx"
,regs.regs[2]);
LOGD(
"seccomp svc 路径内容: %s"
,regs.regs[1]);
}
if
(WIFEXITED(status)){
break
;
}
}
}
}
#include <string>
#include <unistd.h>
#include <android/log.h>
#include <linux/ptrace.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/elf.h>
#include "Syscall_arm64.h"
#define LOG_TAG "NDK_LOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
void
test();
void
ptrace_attach_pid(
int
pid);
void
install_seccomp_filter(){
struct
sock_filter filter[] = {
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(
struct
seccomp_data, nr))),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_openat, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_TRACE),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
};
struct
sock_fprog prog = {
.len = (unsigned
short
) (
sizeof
(filter) /
sizeof
(filter[0])),
.filter = filter,
};
if
(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
LOGD(
"prctl(PR_SET_NO_NEW_PRIVS)"
);
}
if
(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
LOGD(
"when setting seccomp filter"
);
}
}
void
test() {
LOGD(
"test begin ..."
);
int
mainPid = getpid();
int
childPid = fork();
switch
(childPid) {
case
-1:
LOGE(
"fork fail"
);
break
;
case
0:
LOGD(
"子进程 逻辑 %d"
, childPid);
LOGD(
"mainPid=%d"
, mainPid);
ptrace_attach_pid(mainPid);
break
;
default
:
LOGD(
"主进程 逻辑 %d"
, mainPid);
install_seccomp_filter();
kill(getpid(), SIGSTOP);
kill(getpid(), SIGCONT);
LOGD(
"waitpid"
);
break
;
}
LOGD(
"test end ..."
);
}
void
ptrace_attach_pid(
int
pid) {
int
status;
if
(ptrace(PTRACE_ATTACH,pid,0,0) == -1){
LOGE(
"ptrace attach fail"
);
}
//设置 ptrace 选项
const
unsigned
long
default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
//当被跟踪的进程产生一个系统调用时,会发送 SIGTRAP 信号,并且 siginfo 结构中的 si_code 会被设置为 SYS_SECCOMP。
PTRACE_O_TRACEFORK |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEVFORK |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEVFORKDONE |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACECLONE |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEEXEC |
//允许跟踪进程的创建和克隆事件
PTRACE_O_TRACEEXIT );
//当被跟踪的进程退出时,会触发跟踪事件
int
state;
state = ptrace(PTRACE_SETOPTIONS, pid, 0, default_ptrace_options | PTRACE_O_TRACESECCOMP);
//这个选项特别重要,它允许跟踪 seccomp 过滤器触发的事件。当被跟踪的进程因为 seccomp 规则而触发一个 SIGSYS 信号时,会发送一个 SIGTRAP 信号,并且 siginfo 结构中的 si_code 会被设置为 SYS_SECCOMP。
if
(state == -1){
LOGE(
"PTRACE_SETOPTIONS failed"
);
}
waitpid(pid, &status, 0);
if
( WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP){
while
(1){
struct
user_regs_struct regs;
struct
iovec io;
io.iov_base = ®s;
io.iov_len =
sizeof
(regs);
ptrace(PTRACE_CONT, pid, 0, 0);
waitpid(pid, &status, 0);
ptrace(PTRACE_GETREGSET, pid, (
void
*)NT_PRSTATUS, &io);
if
(status >> 8 == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8)) ){
LOGD(
"seccomp svc 中断调用号 : %lx"
,regs.regs[8]);
LOGD(
"seccomp svc 寄存器0内容: %lx"
,regs.regs[0]);
LOGD(
"seccomp svc 寄存器1内容: %lx"
,regs.regs[1]);
LOGD(
"seccomp svc 寄存器2内容: %lx"
,regs.regs[2]);
LOGD(
"seccomp svc 路径内容: %s"
,regs.regs[1]);
}
if
(WIFEXITED(status)){
break
;
}
}
}
}
/* 主函数:程序入口点
* @argc 参数数量
* @argv 参数值数组
* @return 程序退出状态 */
int
main(
int
argc,
char
*
const
argv[])
{
Tracee *tracee;
// 被跟踪进程的上下文对象
int
status;
// 操作状态返回值
/* 配置内存分配器 - 启用内存泄漏报告 */
talloc_enable_leak_report();
/* TALLOC 2.0+ 版本需要将日志输出到标准错误 */
#if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2
talloc_set_log_stderr();
#endif
/* 预创建第一个被跟踪进程(初始pid设为0) */
tracee = get_tracee(NULL, 0,
true
);
if
(tracee == NULL)
goto
error;
tracee->pid = getpid();
// 设置实际进程ID
/* 解析配置参数 */
status = parse_config(tracee, argc, argv);
if
(status < 0)
goto
error;
/* 启动被跟踪进程 */
status = launch_process(tracee, &argv[status]);
if
(status < 0) {
print_execve_help(tracee, tracee->exe, status);
// 执行失败时显示帮助
goto
error;
}
/* 进入事件循环跟踪进程及其子进程 */
exit
(event_loop());
// 事件循环返回时退出程序
/* 错误处理模块 */
error:
TALLOC_FREE(tracee);
// 释放跟踪对象内存
/* 根据错误状态退出 */
if
(exit_failure) {
fprintf
(stderr,
"致命错误:请查看 `%s --help`.\n"
, basename(argv[0]));
exit
(EXIT_FAILURE);
}
else
exit
(EXIT_SUCCESS);
}
/* 主函数:程序入口点
* @argc 参数数量
* @argv 参数值数组
* @return 程序退出状态 */
int
main(
int
argc,
char
*
const
argv[])
{
Tracee *tracee;
// 被跟踪进程的上下文对象
int
status;
// 操作状态返回值
/* 配置内存分配器 - 启用内存泄漏报告 */
talloc_enable_leak_report();
/* TALLOC 2.0+ 版本需要将日志输出到标准错误 */
#if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2
talloc_set_log_stderr();
#endif
/* 预创建第一个被跟踪进程(初始pid设为0) */
tracee = get_tracee(NULL, 0,
true
);
if
(tracee == NULL)
goto
error;
tracee->pid = getpid();
// 设置实际进程ID
/* 解析配置参数 */
status = parse_config(tracee, argc, argv);
if
(status < 0)
goto
error;
/* 启动被跟踪进程 */
status = launch_process(tracee, &argv[status]);
if
(status < 0) {
print_execve_help(tracee, tracee->exe, status);
// 执行失败时显示帮助
goto
error;
}
/* 进入事件循环跟踪进程及其子进程 */
exit
(event_loop());
// 事件循环返回时退出程序
/* 错误处理模块 */
error:
TALLOC_FREE(tracee);
// 释放跟踪对象内存
/* 根据错误状态退出 */
if
(exit_failure) {
fprintf
(stderr,
"致命错误:请查看 `%s --help`.\n"
, basename(argv[0]));
exit
(EXIT_FAILURE);
}
else
exit
(EXIT_SUCCESS);
}
typedef
struct
tracee {
/**********************************************************************
* 私有资源 (Private resources) *
**********************************************************************/
/* 所有被跟踪进程(tracee)列表的链接。 */
LIST_ENTRY(tracee) link;
/* 进程标识符。 */
pid_t pid;
/* 当前是否正在运行? */
bool
running;
/* 该被跟踪进程的父进程,若不存在则为NULL。 */
struct
tracee *parent;
/* 是否为“克隆”进程(即与其创建者共享同一个父进程)。 */
bool
clone;
/* ptrace模拟支持(跟踪器端)。 */
struct
{
size_t
nb_ptracees;
// 被ptrace跟踪的进程数量
LIST_HEAD(zombies, tracee) zombies;
// 僵尸进程列表
struct
direct_ptracees *direct_ptracees;
// 直接跟踪的进程
pid_t wait_pid;
// 等待的进程ID
word_t wait_options;
// 等待选项
/* 等待状态: */
enum
{
DOESNT_WAIT = 0,
// 不等待
WAITS_IN_KERNEL,
// 在内核中等待
WAITS_IN_PROOT
// 在PRoot中等待
} waits_in;
} as_ptracer;
/* ptrace模拟支持(被跟踪进程端)。 */
struct
{
struct
tracee *ptracer;
// 对应的跟踪器
struct
{
#define STRUCT_EVENT struct { int value; bool pending; }
STRUCT_EVENT proot;
// PRoot相关事件
STRUCT_EVENT ptracer;
// 跟踪器相关事件
} event4;
bool
tracing_started;
// 是否已启动跟踪
bool
ignore_loader_syscalls;
// 是否忽略加载器系统调用
bool
ignore_syscalls;
// 是否忽略所有系统调用
word_t options;
// ptrace选项
bool
is_zombie;
// 是否为僵尸进程
} as_ptracee;
/* 当前状态:
* 0: 进入系统调用(syscall入口)
* 1: 系统调用正常退出(无错误)
* -errno: 系统调用错误退出(错误号为errno) */
int
status;
#define IS_IN_SYSENTER(tracee) ((tracee)->status == 0) // 是否在系统调用入口
#define IS_IN_SYSEXIT(tracee) (!IS_IN_SYSENTER(tracee)) // 是否在系统调用退出
#define IS_IN_SYSEXIT2(tracee, sysnum) (IS_IN_SYSEXIT(tracee) \
&&***INAL) == sysnum)
// 是否在指定系统调用的退出阶段
/* 该被跟踪进程的重启方式。 */
enum
__ptrace_request restart_how;
/* 被跟踪进程的通用寄存器值(多版本存储)。 */
struct
user_regs_struct _regs[NB_REG_VERSION];
bool
_regs_were_changed;
// 寄存器是否被修改
bool
restore_original_regs;
// 是否恢复原始寄存器值
/* SIGSTOP信号的特殊处理状态。 */
enum
{
SIGSTOP_IGNORED = 0,
// 忽略SIGSTOP(当父进程已知时)
SIGSTOP_ALLOWED,
// 允许SIGSTOP(当父进程已知时)
SIGSTOP_PENDING,
// 阻塞SIGSTOP直到父进程未知
} sigstop;
/* 用于临时动态内存分配的上下文。 */
TALLOC_CTX *ctx;
/* 用于存储生命周期内动态内存分配的上下文(进程释放时自动回收)。 */
TALLOC_CTX *life_context;
/* 注:可将"ctx"重命名为"event_span","life_context"重命名为"life_span"。 */
/* 在绑定路径初始化时指定最终组件的类型(由bind_path()定义,build_glue()使用)。 */
mode_t glue_type;
/* 在子重配置期间,新配置相对于该被跟踪进程的文件系统命名空间。@paths保存其$PATH环境变量以模拟execvp(3)行为。 */
struct
{
struct
tracee *tracee;
// 关联的被跟踪进程
const
char
*paths;
// 环境变量PATH值
} reconf;
/* 由PRoot在实际系统调用后插入的未请求的系统调用链。 */
struct
{
struct
chained_syscalls *syscalls;
// 链式系统调用
bool
force_final_result;
// 是否强制最终结果
word_t final_result;
// 强制设置的最终结果
} chain;
/* 在execve系统调用入口生成,并在退出时使用的加载信息。 */
struct
load_info *load_info;
/* 加载进程的当前状态。 */
struct
{
enum
{
LOADING_STEP_NONE = 0,
// 未在加载
LOADING_STEP_OPEN,
// 打开文件阶段
LOADING_STEP_MMAP,
// 内存映射阶段
LOADING_STEP_CLOSE
// 关闭文件阶段
} step;
struct
load_info *info;
// 加载信息指针
size_t
index;
// 当前步骤索引
} loading;
/**********************************************************************
* 私有但可继承的资源 *
**********************************************************************/
/* 详细级别。 */
int
verbose;
/* 该被跟踪进程的seccomp加速状态。 */
enum
{ DISABLED = 0, DISABLING, ENABLED } seccomp;
/* 确保在seccomp下始终触发系统调用退出阶段。 */
bool
sysexit_pending;
/**********************************************************************
* 共享或私有资源(取决于CLONE_FS/VM标志) *
**********************************************************************/
/* 文件系统命名空间相关信息。 */
FileSystemNameSpace *fs;
/* 虚拟堆(通过常规内存映射模拟)。 */
Heap *heap;
/**********************************************************************
* 共享资源(直到该进程调用execve()) *
**********************************************************************/
/* 可执行文件路径(类似/proc/self/exe)。 */
char
*exe;
/**********************************************************************
* 共享或私有资源(取决于配置或重配置) *
**********************************************************************/
/* 模拟器(QEMU)命令行参数。 */
char
**qemu;
/* 宿主机与客户机根文件系统之间的粘合路径。 */
const
char
*glue;
/* 为此跟踪对象启用的扩展列表。*/
struct
extensions *extensions;
/**********************************************************************
* 共享但只读的资源 *
**********************************************************************/
/* 在混合模式下,宿主机LD_LIBRARY_PATH会在"客户机->宿主机"转换期间被保存,
* 以便在"宿主机->客户机"转换时恢复(仅当宿主机的LD_LIBRARY_PATH未发生变化时)。*/
const
char
*host_ldso_paths;
const
char
*guest_ldso_paths;
/* 用于诊断目的 */
const
char
*tool_name;
} Tracee;
typedef
struct
tracee {
/**********************************************************************
* 私有资源 (Private resources) *
**********************************************************************/
/* 所有被跟踪进程(tracee)列表的链接。 */
LIST_ENTRY(tracee) link;
/* 进程标识符。 */
pid_t pid;
/* 当前是否正在运行? */
bool
running;
/* 该被跟踪进程的父进程,若不存在则为NULL。 */
struct
tracee *parent;
/* 是否为“克隆”进程(即与其创建者共享同一个父进程)。 */
bool
clone;
/* ptrace模拟支持(跟踪器端)。 */
struct
{
size_t
nb_ptracees;
// 被ptrace跟踪的进程数量
LIST_HEAD(zombies, tracee) zombies;
// 僵尸进程列表
struct
direct_ptracees *direct_ptracees;
// 直接跟踪的进程
pid_t wait_pid;
// 等待的进程ID
word_t wait_options;
// 等待选项
/* 等待状态: */
enum
{
DOESNT_WAIT = 0,
// 不等待
WAITS_IN_KERNEL,
// 在内核中等待
WAITS_IN_PROOT
// 在PRoot中等待
} waits_in;
} as_ptracer;
/* ptrace模拟支持(被跟踪进程端)。 */
struct
{
struct
tracee *ptracer;
// 对应的跟踪器
struct
{
#define STRUCT_EVENT struct { int value; bool pending; }
STRUCT_EVENT proot;
// PRoot相关事件
STRUCT_EVENT ptracer;
// 跟踪器相关事件
} event4;
bool
tracing_started;
// 是否已启动跟踪
bool
ignore_loader_syscalls;
// 是否忽略加载器系统调用
bool
ignore_syscalls;
// 是否忽略所有系统调用
word_t options;
// ptrace选项
bool
is_zombie;
// 是否为僵尸进程
} as_ptracee;
/* 当前状态:
* 0: 进入系统调用(syscall入口)
* 1: 系统调用正常退出(无错误)
* -errno: 系统调用错误退出(错误号为errno) */
int
status;
#define IS_IN_SYSENTER(tracee) ((tracee)->status == 0) // 是否在系统调用入口
#define IS_IN_SYSEXIT(tracee) (!IS_IN_SYSENTER(tracee)) // 是否在系统调用退出
#define IS_IN_SYSEXIT2(tracee, sysnum) (IS_IN_SYSEXIT(tracee) \
&&***INAL) == sysnum)
// 是否在指定系统调用的退出阶段
/* 该被跟踪进程的重启方式。 */
enum
__ptrace_request restart_how;
/* 被跟踪进程的通用寄存器值(多版本存储)。 */
struct
user_regs_struct _regs[NB_REG_VERSION];
bool
_regs_were_changed;
// 寄存器是否被修改
bool
restore_original_regs;
// 是否恢复原始寄存器值
/* SIGSTOP信号的特殊处理状态。 */
enum
{
SIGSTOP_IGNORED = 0,
// 忽略SIGSTOP(当父进程已知时)
SIGSTOP_ALLOWED,
// 允许SIGSTOP(当父进程已知时)
SIGSTOP_PENDING,
// 阻塞SIGSTOP直到父进程未知
} sigstop;
/* 用于临时动态内存分配的上下文。 */
TALLOC_CTX *ctx;
/* 用于存储生命周期内动态内存分配的上下文(进程释放时自动回收)。 */
TALLOC_CTX *life_context;
/* 注:可将"ctx"重命名为"event_span","life_context"重命名为"life_span"。 */
/* 在绑定路径初始化时指定最终组件的类型(由bind_path()定义,build_glue()使用)。 */
mode_t glue_type;
/* 在子重配置期间,新配置相对于该被跟踪进程的文件系统命名空间。@paths保存其$PATH环境变量以模拟execvp(3)行为。 */
struct
{
struct
tracee *tracee;
// 关联的被跟踪进程
const
char
*paths;
// 环境变量PATH值
} reconf;
/* 由PRoot在实际系统调用后插入的未请求的系统调用链。 */
struct
{
struct
chained_syscalls *syscalls;
// 链式系统调用
bool
force_final_result;
// 是否强制最终结果
word_t final_result;
// 强制设置的最终结果
} chain;
/* 在execve系统调用入口生成,并在退出时使用的加载信息。 */
struct
load_info *load_info;
/* 加载进程的当前状态。 */
struct
{
enum
{
LOADING_STEP_NONE = 0,
// 未在加载
LOADING_STEP_OPEN,
// 打开文件阶段
LOADING_STEP_MMAP,
// 内存映射阶段
LOADING_STEP_CLOSE
// 关闭文件阶段
} step;
struct
load_info *info;
// 加载信息指针
size_t
index;
// 当前步骤索引
} loading;
/**********************************************************************
* 私有但可继承的资源 *
**********************************************************************/
/* 详细级别。 */
int
verbose;
/* 该被跟踪进程的seccomp加速状态。 */
enum
{ DISABLED = 0, DISABLING, ENABLED } seccomp;
/* 确保在seccomp下始终触发系统调用退出阶段。 */
bool
sysexit_pending;
/**********************************************************************
* 共享或私有资源(取决于CLONE_FS/VM标志) *
**********************************************************************/
/* 文件系统命名空间相关信息。 */
FileSystemNameSpace *fs;
/* 虚拟堆(通过常规内存映射模拟)。 */
Heap *heap;
/**********************************************************************
* 共享资源(直到该进程调用execve()) *
**********************************************************************/
/* 可执行文件路径(类似/proc/self/exe)。 */
char
*exe;
/**********************************************************************
* 共享或私有资源(取决于配置或重配置) *
**********************************************************************/
/* 模拟器(QEMU)命令行参数。 */
char
**qemu;
/* 宿主机与客户机根文件系统之间的粘合路径。 */
const
char
*glue;
/* 为此跟踪对象启用的扩展列表。*/
struct
extensions *extensions;
/**********************************************************************
* 共享但只读的资源 *
**********************************************************************/
/* 在混合模式下,宿主机LD_LIBRARY_PATH会在"客户机->宿主机"转换期间被保存,
* 以便在"宿主机->客户机"转换时恢复(仅当宿主机的LD_LIBRARY_PATH未发生变化时)。*/
const
char
*host_ldso_paths;
const
char
*guest_ldso_paths;
/* 用于诊断目的 */
const
char
*tool_name;
} Tracee;
/* 根据给定的进程ID查找或创建对应的Tracee结构体 */
Tracee *get_tracee(
const
Tracee *current_tracee, pid_t pid,
bool
create)
{
Tracee *tracee;
/* 如果当前正在追踪的进程就是要找的进程
* 则直接返回当前Tracee对象,避免重置内存收集器。
* 因为调用者可能持有子分配数据的指针 */
if
(current_tracee != NULL && current_tracee->pid == pid)
return
(Tracee *)current_tracee;
/* 遍历tracees链表寻找匹配PID的Tracee对象 */
LIST_FOREACH(tracee, &tracees, link) {
if
(tracee->pid == pid) {
/* 释放旧的内存上下文并创建新的内存分配器
* 使用talloc内存管理库进行内存管理 */
TALLOC_FREE(tracee->ctx);
tracee->ctx = talloc_new(tracee);
return
tracee;
}
}
/* 如果没有找到且允许创建,则新建Tracee对象
* 否则返回NULL */
return
(create ? new_tracee(pid) : NULL);
}
/* 根据给定的进程ID查找或创建对应的Tracee结构体 */
Tracee *get_tracee(
const
Tracee *current_tracee, pid_t pid,
bool
create)
{
Tracee *tracee;
/* 如果当前正在追踪的进程就是要找的进程
* 则直接返回当前Tracee对象,避免重置内存收集器。
* 因为调用者可能持有子分配数据的指针 */
if
(current_tracee != NULL && current_tracee->pid == pid)
return
(Tracee *)current_tracee;
/* 遍历tracees链表寻找匹配PID的Tracee对象 */
LIST_FOREACH(tracee, &tracees, link) {
if
(tracee->pid == pid) {
/* 释放旧的内存上下文并创建新的内存分配器
* 使用talloc内存管理库进行内存管理 */
TALLOC_FREE(tracee->ctx);
tracee->ctx = talloc_new(tracee);
return
tracee;
}
}
/* 如果没有找到且允许创建,则新建Tracee对象
* 否则返回NULL */
return
(create ? new_tracee(pid) : NULL);
}
/**
根据存储在@argv[]中的命令行参数配置@tracee。该函数返回@argv[]中要启动的命令的索引,若发生错误则返回-1。
*/
static
int
parse_config(Tracee *tracee,
size_t
argc,
char
*
const
argv[])
{
option_handler_t handler = NULL;
// 当前选项的处理函数
const
Option *options;
// CLI选项列表
const
Cli *cli = NULL;
// 当前使用的CLI工具(CARE或PRoot)
size_t
argc_offset;
// 命令在argv中的起始位置
size_t
i, j, k;
// 循环计数器
int
status;
// 操作状态码
/* 检查是否为自解压CARE归档 */
if
(get_care_cli != NULL) {
// 尝试从"/proc/self/exe"提取归档(当前可执行文件是否为CARE自解压包?)
status = extract_archive_from_file(
"/proc/self/exe"
);
if
(status == 0) {
// 成功提取则退出
exit_failure = 0;
// 标记正常退出
return
-1;
}
/* 检查工具名是否为"care"(如"care-3.4") */
if
(strncasecmp(basename(argv[0]),
"care"
,
strlen
(
"care"
)) == 0)
cli = get_care_cli(tracee->ctx);
// 获取CARE的CLI配置
}
/* 默认使用PRoot CLI */
if
(cli == NULL)
cli = get_proot_cli(tracee->ctx);
// 获取PRoot的CLI配置
tracee->tool_name = cli->name;
// 设置工具名称(如"proot")
/* 参数不足时打印用法 */
if
(argc == 1) {
print_usage(tracee, cli,
false
);
// 显示帮助信息
return
-1;
}
/* 遍历所有参数进行解析 */
for
(i = 1; i < argc; i++) {
const
char
*arg = argv[i];
复制
/* 处理短选项的值(如 -o value,此时handler已指向处理函数) */
if
(handler != NULL) {
status = handler(tracee, cli, arg);
// 调用之前设置的处理器
if
(status < 0)
return
-1;
// 错误则退出
handler = NULL;
// 重置处理器
continue
;
}
/* 遇到非选项参数(如命令),停止解析 */
if
(arg[0] !=
'-'
)
break
;
/* 遍历所有支持的选项 */
options = cli->options;
// 获取当前CLI的选项列表
for
(j = 0; options[j].
class
!= NULL; j++) {
// 遍历每个选项类别
const
Option *option = &options[j];
/* 检查所有选项别名(如 -v 和 --verbose) */
for
(k = 0; ; k++) {
// 遍历选项的多个别名
const
Argument *argument = &option->arguments[k];
size_t
length;
/* 无更多别名时跳出循环 */
if
(!argument->name)
break
;
length =
strlen
(argument->name);
if
(
strncmp
(arg, argument->name, length) != 0)
continue
;
// 不匹配则跳过
/* 处理选项与值的分隔符(如 -I/usr 或 -I /usr) */
if
(
strlen
(arg) > length && arg[length] != argument->separator) {
print_error_separator(tracee, argument);
// 分隔符错误(如 -I=usr)
return
-1;
}
/* 无值的选项(如 --help) */
if
(!argument->value) {
status = option->handler(tracee, cli, NULL);
// 调用处理函数
if
(status < 0)
return
-1;
goto
known_option;
// 跳转到后续处理
}
/* 合并值的选项(如 -I/usr,分隔符为'/') */
if
(argument->separator == arg[length]) {
status = option->handler(tracee, cli, &arg[length + 1]);
// 提取值
if
(status < 0)
return
-1;
goto
known_option;
}
/* 分隔符必须为空格的情况(如 -I /usr) */
if
(argument->separator !=
' '
) {
print_error_separator(tracee, argument);
return
-1;
}
/* 短选项需要后续参数作为值(如 -o value) */
handler = option->handler;
// 设置处理器,等待下一个参数
goto
known_option;
}
}
/* 未知选项处理 */
note(tracee, ERROR, USER,
"unknown option '%s'."
, arg);
return
-1;
known_option:
/* 检查是否缺少选项值(如 -o 后无参数) */
if
(handler != NULL && i == argc - 1) {
note(tracee, ERROR, USER,
"missing value for option '%s'."
, arg);
return
-1;
}
}
argc_offset = i;
// 记录命令起始位置(如 argv[3] 是命令名)
/* 通过钩子函数进行初始化阶段配置 /
#define HOOK_CONFIG(callback)
do {
if (cli->callback != NULL) {
status = cli->callback(tracee, cli, argc, argv, i);
if (status < 0) return -1;
i = status; / 可能调整参数索引 */
}
}
while
(0)
HOOK_CONFIG(pre_initialize_bindings);
// 绑定初始化前处理
/* 解析用户绑定的路径(如 -b /host:/guest) */
status = initialize_bindings(tracee);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_bindings);
// 绑定初始化后处理
HOOK_CONFIG(pre_initialize_cwd);
// 工作目录初始化前处理
/* 设置当前工作目录 */
status = initialize_cwd(tracee);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_cwd);
// 工作目录初始化后处理
HOOK_CONFIG(pre_initialize_exe);
// 可执行文件初始化前处理
/* 解析目标可执行文件路径 */
status = initialize_exe(tracee, argv[argc_offset]);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_exe);
// 可执行文件初始化后处理
#undef HOOK_CONFIG
print_config(tracee, &argv[argc_offset]);
// 打印最终配置信息
return
argc_offset;
// 返回命令的argv起始索引(如 argv[3])
}
/**
根据存储在@argv[]中的命令行参数配置@tracee。该函数返回@argv[]中要启动的命令的索引,若发生错误则返回-1。
*/
static
int
parse_config(Tracee *tracee,
size_t
argc,
char
*
const
argv[])
{
option_handler_t handler = NULL;
// 当前选项的处理函数
const
Option *options;
// CLI选项列表
const
Cli *cli = NULL;
// 当前使用的CLI工具(CARE或PRoot)
size_t
argc_offset;
// 命令在argv中的起始位置
size_t
i, j, k;
// 循环计数器
int
status;
// 操作状态码
/* 检查是否为自解压CARE归档 */
if
(get_care_cli != NULL) {
// 尝试从"/proc/self/exe"提取归档(当前可执行文件是否为CARE自解压包?)
status = extract_archive_from_file(
"/proc/self/exe"
);
if
(status == 0) {
// 成功提取则退出
exit_failure = 0;
// 标记正常退出
return
-1;
}
/* 检查工具名是否为"care"(如"care-3.4") */
if
(strncasecmp(basename(argv[0]),
"care"
,
strlen
(
"care"
)) == 0)
cli = get_care_cli(tracee->ctx);
// 获取CARE的CLI配置
}
/* 默认使用PRoot CLI */
if
(cli == NULL)
cli = get_proot_cli(tracee->ctx);
// 获取PRoot的CLI配置
tracee->tool_name = cli->name;
// 设置工具名称(如"proot")
/* 参数不足时打印用法 */
if
(argc == 1) {
print_usage(tracee, cli,
false
);
// 显示帮助信息
return
-1;
}
/* 遍历所有参数进行解析 */
for
(i = 1; i < argc; i++) {
const
char
*arg = argv[i];
复制
/* 处理短选项的值(如 -o value,此时handler已指向处理函数) */
if
(handler != NULL) {
status = handler(tracee, cli, arg);
// 调用之前设置的处理器
if
(status < 0)
return
-1;
// 错误则退出
handler = NULL;
// 重置处理器
continue
;
}
/* 遇到非选项参数(如命令),停止解析 */
if
(arg[0] !=
'-'
)
break
;
/* 遍历所有支持的选项 */
options = cli->options;
// 获取当前CLI的选项列表
for
(j = 0; options[j].
class
!= NULL; j++) {
// 遍历每个选项类别
const
Option *option = &options[j];
/* 检查所有选项别名(如 -v 和 --verbose) */
for
(k = 0; ; k++) {
// 遍历选项的多个别名
const
Argument *argument = &option->arguments[k];
size_t
length;
/* 无更多别名时跳出循环 */
if
(!argument->name)
break
;
length =
strlen
(argument->name);
if
(
strncmp
(arg, argument->name, length) != 0)
continue
;
// 不匹配则跳过
/* 处理选项与值的分隔符(如 -I/usr 或 -I /usr) */
if
(
strlen
(arg) > length && arg[length] != argument->separator) {
print_error_separator(tracee, argument);
// 分隔符错误(如 -I=usr)
return
-1;
}
/* 无值的选项(如 --help) */
if
(!argument->value) {
status = option->handler(tracee, cli, NULL);
// 调用处理函数
if
(status < 0)
return
-1;
goto
known_option;
// 跳转到后续处理
}
/* 合并值的选项(如 -I/usr,分隔符为'/') */
if
(argument->separator == arg[length]) {
status = option->handler(tracee, cli, &arg[length + 1]);
// 提取值
if
(status < 0)
return
-1;
goto
known_option;
}
/* 分隔符必须为空格的情况(如 -I /usr) */
if
(argument->separator !=
' '
) {
print_error_separator(tracee, argument);
return
-1;
}
/* 短选项需要后续参数作为值(如 -o value) */
handler = option->handler;
// 设置处理器,等待下一个参数
goto
known_option;
}
}
/* 未知选项处理 */
note(tracee, ERROR, USER,
"unknown option '%s'."
, arg);
return
-1;
known_option:
/* 检查是否缺少选项值(如 -o 后无参数) */
if
(handler != NULL && i == argc - 1) {
note(tracee, ERROR, USER,
"missing value for option '%s'."
, arg);
return
-1;
}
}
argc_offset = i;
// 记录命令起始位置(如 argv[3] 是命令名)
/* 通过钩子函数进行初始化阶段配置 /
#define HOOK_CONFIG(callback)
do {
if (cli->callback != NULL) {
status = cli->callback(tracee, cli, argc, argv, i);
if (status < 0) return -1;
i = status; / 可能调整参数索引 */
}
}
while
(0)
HOOK_CONFIG(pre_initialize_bindings);
// 绑定初始化前处理
/* 解析用户绑定的路径(如 -b /host:/guest) */
status = initialize_bindings(tracee);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_bindings);
// 绑定初始化后处理
HOOK_CONFIG(pre_initialize_cwd);
// 工作目录初始化前处理
/* 设置当前工作目录 */
status = initialize_cwd(tracee);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_cwd);
// 工作目录初始化后处理
HOOK_CONFIG(pre_initialize_exe);
// 可执行文件初始化前处理
/* 解析目标可执行文件路径 */
status = initialize_exe(tracee, argv[argc_offset]);
if
(status < 0)
return
-1;
HOOK_CONFIG(post_initialize_exe);
// 可执行文件初始化后处理
#undef HOOK_CONFIG
print_config(tracee, &argv[argc_offset]);
// 打印最终配置信息
return
argc_offset;
// 返回命令的argv起始索引(如 argv[3])
}
/**
* 启动 @tracee->exe 并使用给定的 @argv[] 参数。此函数在发生错误时返回 -errno,否则返回 0。
*/
int
launch_process(Tracee *tracee,
char
*
const
argv[])
{
char
*
const
default_argv[] = {
"-"
, NULL };
long
status;
pid_t pid;
/* Warn about open file descriptors. They won't be
* translated until they are closed. */
if
(tracee->verbose > 0)
list_open_fd(tracee);
pid = fork();
switch
(pid) {
case
-1:
note(tracee, ERROR, SYSTEM,
"fork()"
);
return
-
errno
;
case
0:
/* child */
/* Declare myself as ptraceable before executing the
* requested program. */
status = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if
(status < 0) {
note(tracee, ERROR, SYSTEM,
"ptrace(TRACEME)"
);
return
-
errno
;
}
/* Synchronize with the tracer's event loop. Without
* this trick the tracer only sees the "return" from
* the next execve(2) so PRoot wouldn't handle the
* interpreter/runner. I also verified that strace
* does the same thing. */
kill(getpid(), SIGSTOP);
/* Improve performance by using seccomp mode 2, unless
* this support is explicitly disabled. */
if
(
getenv
(
"PROOT_NO_SECCOMP"
) == NULL)
(
void
) enable_syscall_filtering(tracee);
/* Now process is ptraced, so the current rootfs is already the
* guest rootfs. Note: Valgrind can't handle execve(2) on
* "foreign" binaries (ENOEXEC) but can handle execvp(3) on such
* binaries. */
execvp(tracee->exe, argv[0] != NULL ? argv : default_argv);
return
-
errno
;
default
:
/* parent */
/* We know the pid of the first tracee now. */
tracee->pid = pid;
return
0;
}
/* Never reached. */
return
-ENOSYS;
}
/**
* 启动 @tracee->exe 并使用给定的 @argv[] 参数。此函数在发生错误时返回 -errno,否则返回 0。
*/
int
launch_process(Tracee *tracee,
char
*
const
argv[])
{
char
*
const
default_argv[] = {
"-"
, NULL };
long
status;
pid_t pid;
/* Warn about open file descriptors. They won't be
* translated until they are closed. */
if
(tracee->verbose > 0)
list_open_fd(tracee);
pid = fork();
switch
(pid) {
case
-1:
note(tracee, ERROR, SYSTEM,
"fork()"
);
return
-
errno
;
case
0:
/* child */
/* Declare myself as ptraceable before executing the
* requested program. */
status = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if
(status < 0) {
note(tracee, ERROR, SYSTEM,
"ptrace(TRACEME)"
);
return
-
errno
;
}
/* Synchronize with the tracer's event loop. Without
* this trick the tracer only sees the "return" from
* the next execve(2) so PRoot wouldn't handle the
* interpreter/runner. I also verified that strace
* does the same thing. */
kill(getpid(), SIGSTOP);
/* Improve performance by using seccomp mode 2, unless
* this support is explicitly disabled. */
if
(
getenv
(
"PROOT_NO_SECCOMP"
) == NULL)
(
void
) enable_syscall_filtering(tracee);
/* Now process is ptraced, so the current rootfs is already the
* guest rootfs. Note: Valgrind can't handle execve(2) on
* "foreign" binaries (ENOEXEC) but can handle execvp(3) on such
* binaries. */
execvp(tracee->exe, argv[0] != NULL ? argv : default_argv);
return
-
errno
;
default
:
/* parent */
/* We know the pid of the first tracee now. */
tracee->pid = pid;
return
0;
}
/* Never reached. */
return
-ENOSYS;
}
/**
* 通知内核仅跟踪由PRoot及其扩展处理的系统调用。
* 该过滤器将对给定的@tracee及其所有未来子进程生效。
* 如果发生错误,该函数返回-errno,否则返回0。
*/
int
enable_syscall_filtering(
const
Tracee *tracee)
{
FilteredSysnum *filtered_sysnums = NULL;
// 被过滤的系统调用号列表
Extension *extension;
// 扩展模块指针
int
status;
// 操作状态码
// 断言确保tracee及其上下文不为空
assert
(tracee != NULL && tracee->ctx != NULL);
/* 将PRoot需要的系统调用号添加到过滤列表。
* TODO: 仅在需要路径转换时添加 */
status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums);
if
(status < 0)
return
status;
/* 将扩展模块需要的系统调用号合并到过滤列表 */
if
(tracee->extensions != NULL) {
// 遍历所有扩展模块
LIST_FOREACH(extension, tracee->extensions, link) {
if
(extension->filtered_sysnums == NULL)
continue
;
// 合并当前扩展的过滤列表
status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums,
extension->filtered_sysnums);
if
(status < 0)
return
status;
}
}
// 设置seccomp过滤器
status = set_seccomp_filters(filtered_sysnums);
if
(status < 0)
return
status;
return
0;
// 成功返回0
}
/**
* 通知内核仅跟踪由PRoot及其扩展处理的系统调用。
* 该过滤器将对给定的@tracee及其所有未来子进程生效。
* 如果发生错误,该函数返回-errno,否则返回0。
*/
int
enable_syscall_filtering(
const
Tracee *tracee)
{
FilteredSysnum *filtered_sysnums = NULL;
// 被过滤的系统调用号列表
Extension *extension;
// 扩展模块指针
int
status;
// 操作状态码
// 断言确保tracee及其上下文不为空
assert
(tracee != NULL && tracee->ctx != NULL);
/* 将PRoot需要的系统调用号添加到过滤列表。
* TODO: 仅在需要路径转换时添加 */
status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums);
if
(status < 0)
return
status;
/* 将扩展模块需要的系统调用号合并到过滤列表 */
if
(tracee->extensions != NULL) {
// 遍历所有扩展模块
LIST_FOREACH(extension, tracee->extensions, link) {
if
(extension->filtered_sysnums == NULL)
continue
;
// 合并当前扩展的过滤列表
status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums,
extension->filtered_sysnums);
if
(status < 0)
return
status;
}
}
// 设置seccomp过滤器
status = set_seccomp_filters(filtered_sysnums);
if
(status < 0)
return
status;
return
0;
// 成功返回0
}
/**
* 等待并处理来自所有被跟踪进程(tracees)的事件。本函数返回最后一个终止程序的退出状态。
*/
int
event_loop()
{
struct
sigaction signal_action;
long
status;
int
signum;
/* 退出时杀死所有被跟踪进程 */
status =
atexit
(kill_all_tracees);
if
(status != 0)
note(NULL, WARNING, INTERNAL,
"atexit() 失败"
);
/* 信号处理函数被调用时会阻塞所有信号。
* 使用 SIGINFO 标识进程信号来源,RESTART 标识无缝重启 waitpid(2) */
bzero(&signal_action,
sizeof
(signal_action));
signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
status = sigfillset(&signal_action.sa_mask);
if
(status < 0)
note(NULL, WARNING, SYSTEM,
"sigfillset() 错误"
);
/* 遍历所有可能的信号并配置处理方式 */
for
(signum = 0; signum < SIGRTMAX; signum++) {
switch
(signum) {
case
SIGQUIT:
// 终止请求信号
case
SIGILL:
// 非法指令
case
SIGABRT:
// 异常中止
case
SIGFPE:
// 算术错误
case
SIGSEGV:
// 段错误
/* 在异常终止信号时杀死所有被跟踪进程,确保无残留 */
signal_action.sa_sigaction = kill_all_tracees2;
break
;
case
SIGUSR1:
// 用户自定义信号1
case
SIGUSR2:
// 用户自定义信号2
/* 调试用:在 stderr 打印完整 talloc 内存层级 */
signal_action.sa_sigaction = print_talloc_hierarchy;
break
;
case
SIGCHLD:
// 子进程状态变化
case
SIGCONT:
// 继续执行
case
SIGSTOP:
// 停止进程
case
SIGTSTP:
// 终端停止请求
case
SIGTTIN:
// 后台读终端
case
SIGTTOU:
// 后台写终端
/* 保留与终端和作业控制相关信号的默认行为 */
continue
;
default
:
/* 忽略其他信号(如终止信号 SIGINT/^C) */
signal_action.sa_sigaction = (
void
*)SIG_IGN;
break
;
}
/* 注册信号处理函数 */
status = sigaction(signum, &signal_action, NULL);
if
(status < 0 &&
errno
!= EINVAL)
note(NULL, WARNING, SYSTEM,
"sigaction(%d) 错误"
, signum);
}
/* 主事件循环 */
while
(1) {
int
tracee_status;
// 被跟踪进程状态
Tracee *tracee;
// 被跟踪进程对象
int
signal
;
// 接收到的信号
pid_t pid;
// 进程ID
/* 等待任意被跟踪进程的状态变化 */
pid = waitpid(-1, &tracee_status, __WALL);
if
(pid < 0) {
if
(
errno
!= ECHILD) {
// 无子进程时正常退出
note(NULL, ERROR, SYSTEM,
"waitpid() 错误"
);
return
EXIT_FAILURE;
}
break
;
// 所有子进程已退出,结束循环
}
/* 获取对应被跟踪进程的信息 */
tracee = get_tracee(NULL, pid,
true
);
assert
(tracee != NULL);
tracee->running =
false
;
// 标记为停止状态
/* 通知扩展模块处理新状态 */
status = notify_extensions(tracee, NEW_STATUS, tracee_status, 0);
if
(status != 0)
continue
;
/* 处理来自 ptrace 的事件 */
if
(tracee->as_ptracee.ptracer != NULL) {
bool
keep_stopped = handle_ptracee_event(tracee, tracee_status);
if
(keep_stopped)
continue
;
// 需要保持停止状态,不重启进程
}
/* 处理事件并获取需传递的信号 */
signal
= handle_tracee_event(tracee, tracee_status);
/* 重启被跟踪进程(可能附带信号) */
(
void
) restart_tracee(tracee,
signal
);
}
return
last_exit_status;
// 返回最后一个退出状态码
}
/**
* 等待并处理来自所有被跟踪进程(tracees)的事件。本函数返回最后一个终止程序的退出状态。
*/
int
event_loop()
{
struct
sigaction signal_action;
long
status;
int
signum;
/* 退出时杀死所有被跟踪进程 */
status =
atexit
(kill_all_tracees);
if
(status != 0)
note(NULL, WARNING, INTERNAL,
"atexit() 失败"
);
/* 信号处理函数被调用时会阻塞所有信号。
* 使用 SIGINFO 标识进程信号来源,RESTART 标识无缝重启 waitpid(2) */
bzero(&signal_action,
sizeof
(signal_action));
signal_action.sa_flags = SA_SIGINFO | SA_RESTART;
status = sigfillset(&signal_action.sa_mask);
if
(status < 0)
note(NULL, WARNING, SYSTEM,
"sigfillset() 错误"
);
/* 遍历所有可能的信号并配置处理方式 */
for
(signum = 0; signum < SIGRTMAX; signum++) {
switch
(signum) {
case
SIGQUIT:
// 终止请求信号
case
SIGILL:
// 非法指令
case
SIGABRT:
// 异常中止
case
SIGFPE:
// 算术错误
case
SIGSEGV:
// 段错误
/* 在异常终止信号时杀死所有被跟踪进程,确保无残留 */
signal_action.sa_sigaction = kill_all_tracees2;
break
;
case
SIGUSR1:
// 用户自定义信号1
case
SIGUSR2:
// 用户自定义信号2
/* 调试用:在 stderr 打印完整 talloc 内存层级 */
signal_action.sa_sigaction = print_talloc_hierarchy;
break
;
case
SIGCHLD:
// 子进程状态变化
case
SIGCONT:
// 继续执行
case
SIGSTOP:
// 停止进程
case
SIGTSTP:
// 终端停止请求
case
SIGTTIN:
// 后台读终端
case
SIGTTOU:
// 后台写终端
/* 保留与终端和作业控制相关信号的默认行为 */
continue
;
default
:
/* 忽略其他信号(如终止信号 SIGINT/^C) */
signal_action.sa_sigaction = (
void
*)SIG_IGN;
break
;
}
/* 注册信号处理函数 */
status = sigaction(signum, &signal_action, NULL);
if
(status < 0 &&
errno
!= EINVAL)
note(NULL, WARNING, SYSTEM,
"sigaction(%d) 错误"
, signum);
}
/* 主事件循环 */
while
(1) {
int
tracee_status;
// 被跟踪进程状态
Tracee *tracee;
// 被跟踪进程对象
int
signal
;
// 接收到的信号
pid_t pid;
// 进程ID
/* 等待任意被跟踪进程的状态变化 */
pid = waitpid(-1, &tracee_status, __WALL);
if
(pid < 0) {
if
(
errno
!= ECHILD) {
// 无子进程时正常退出
note(NULL, ERROR, SYSTEM,
"waitpid() 错误"
);
return
EXIT_FAILURE;
}
break
;
// 所有子进程已退出,结束循环
}
/* 获取对应被跟踪进程的信息 */
tracee = get_tracee(NULL, pid,
true
);
assert
(tracee != NULL);
tracee->running =
false
;
// 标记为停止状态
/* 通知扩展模块处理新状态 */
status = notify_extensions(tracee, NEW_STATUS, tracee_status, 0);
if
(status != 0)
continue
;
/* 处理来自 ptrace 的事件 */
if
(tracee->as_ptracee.ptracer != NULL) {
bool
keep_stopped = handle_ptracee_event(tracee, tracee_status);
if
(keep_stopped)
continue
;
// 需要保持停止状态,不重启进程
}
/* 处理事件并获取需传递的信号 */
signal
= handle_tracee_event(tracee, tracee_status);
/* 重启被跟踪进程(可能附带信号) */
(
void
) restart_tracee(tracee,
signal
);
}
return
last_exit_status;
// 返回最后一个退出状态码
}
/**
* 对于给定的被跟踪进程 @ptracee,如果其跟踪者(ptracer)正在等待当前 @event,
* 则将事件传递给跟踪者;否则将 @ptracee 置为“等待跟踪者”状态。
* 返回值表示是否应保持 @ptracee 的停止状态。
*/
bool
handle_ptracee_event(Tracee *ptracee,
int
event)
{
bool
handled_by_proot_first =
false
;
// 标记事件是否需要由PRoot优先处理
Tracee *ptracer = PTRACEE.ptracer;
// 获取跟踪者(父进程)
bool
keep_stopped;
// 返回值:是否保持停止状态
assert
(ptracer != NULL);
// 确保跟踪者存在
/* 保存原始事件信息,供PRoot后续处理 */
PTRACEE.event4.proot.value = event;
// 存储事件值
PTRACEE.event4.proot.pending =
true
;
// 标记事件待处理
/* 默认情况下,保持ptracee停止,直到跟踪者恢复它 */
keep_stopped =
true
;
/* 处理因信号停止的事件(WIFSTOPPED) */
if
(WIFSTOPPED(event)) {
switch
((event & 0xfff00) >> 8) {
// 提取高位信号类型
case
SIGTRAP | 0x80:
// 系统调用退出事件(带syscall-good标志)
if
(PTRACEE.ignore_syscalls || PTRACEE.ignore_loader_syscalls)
return
false
;
// 若忽略系统调用,直接返回不保持停止
if
((PTRACEE.options & PTRACE_O_TRACESYSGOOD) == 0)
// 未启用TRACESYSGOOD
event &= ~(0x80 << 8);
// 清除高位标志
handled_by_proot_first = IS_IN_SYSEXIT(ptracee);
// 系统调用退出阶段由PRoot处理
break
;
// 宏定义:处理特定跟踪事件(FORK/VFORK等)
#define PTRACE_EVENT_VFORKDONE PTRACE_EVENT_VFORK_DONE // 兼容性定义
#define CASE_FILTER_EVENT(name) \
case
SIGTRAP | PTRACE_EVENT_ ##name << 8: \
if
((PTRACEE.options & PTRACE_O_TRACE ##name) == 0) \
// 未启用对应跟踪选项
return
false
; \
PTRACEE.tracing_started =
true
; \
// 标记跟踪已开始
handled_by_proot_first =
true
; \
// PRoot优先处理
break
;
CASE_FILTER_EVENT(FORK);
// 处理进程fork事件
CASE_FILTER_EVENT(VFORK);
// 处理vfork事件
CASE_FILTER_EVENT(VFORKDONE);
// 处理vfork完成事件
CASE_FILTER_EVENT(CLONE);
// 处理clone事件
CASE_FILTER_EVENT(EXIT);
// 处理进程退出事件
CASE_FILTER_EVENT(EXEC);
// 处理exec事件
/* 以下代码不可达,触发断言 */
assert
(0);
// 不支持的事件类型(如seccomp)
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8:
return
false
;
// 直接返回不处理
default
:
// 其他信号停止事件
PTRACEE.tracing_started =
true
;
// 标记跟踪开始
break
;
}
}
/* 处理进程退出或信号终止事件 */
else
if
(WIFEXITED(event) || WIFSIGNALED(event)) {
PTRACEE.tracing_started =
true
;
// 标记跟踪已开始
keep_stopped =
false
;
// 进程已终止,无需保持停止
}
/* 若跟踪尚未开始(如TRACEME后首次事件),直接返回 */
if
(!PTRACEE.tracing_started)
return
false
;
/* PRoot优先处理事件(如系统调用退出) */
if
(handled_by_proot_first) {
int
signal
= handle_tracee_event(ptracee, PTRACEE.event4.proot.value);
PTRACEE.event4.proot.value =
signal
;
// 更新处理后的信号值
assert
(
signal
== 0);
// 当前逻辑下信号应为0(如sysexit无需传递信号)
}
/* 保存处理后的事件信息,供跟踪者使用 */
PTRACEE.event4.ptracer.value = event;
// 记录最终事件值
PTRACEE.event4.ptracer.pending =
true
;
// 标记事件待跟踪者处理
/* 异步通知跟踪者(发送SIGCHLD模拟内核行为) */
kill(ptracer->pid, SIGCHLD);
/* 若跟踪者正在等待此ptracee的事件 */
if
((PTRACER.wait_pid == -1 || PTRACER.wait_pid == ptracee->pid) &&
EXPECTED_WAIT_CLONE(PTRACER.wait_options, ptracee)) {
bool
restarted;
int
status = update_wait_status(ptracer, ptracee);
// 更新等待状态
poke_reg(ptracer, SYSARG_RESULT, (word_t) status);
// 修改寄存器返回值
(
void
) push_regs(ptracer);
// 写回寄存器缓存
/* 重启跟踪者进程 */
PTRACER.wait_pid = 0;
restarted = restart_tracee(ptracer, 0);
// 尝试恢复跟踪者执行
if
(!restarted)
// 重启失败则不再保持停止
keep_stopped =
false
;
}
return
keep_stopped;
// 返回是否保持ptracee停止
}
// 关键逻辑说明:
// 1. 事件分层处理:
// - 原始事件(如系统调用退出)先由PRoot处理(如模拟执行后清理)
// - 处理后的事件转发给跟踪者(如GDB),模拟内核的ptrace事件传递
//
// 2. 状态同步机制:
// - 通过SIGCHLD通知跟踪者有事件待处理
// - 若跟踪者正在waitpid,直接更新其寄存器状态并唤醒,减少延迟
//
// 3. 停止状态决策:
// - 进程终止(EXITED/SIGNALED)时必须返回false,防止僵尸进程滞留
// - 默认保持停止,直到跟踪者显式调用PTRACE_CONT
//
// 潜在问题:
// 1. 事件掩码计算:
// - `(event & 0xfff00) >> 8` 假设高位存储事件类型,需确保与内核实现一致
// - 若PTRACE_EVENT_* 的编码方式变化,可能导致错误过滤
//
// 2. 异步竞争条件:
// - kill()发送SIGCHLD后,跟踪者可能尚未进入waitpid,导致信号丢失
// - 需依赖内核的ptrace语义保证事件不会丢失
//
// 3. 宏展开风险:
// - CASE_FILTER_EVENT(EXEC) 展开后依赖PTRACE_O_TRACEEXEC选项的存在
// - 若PTrace实现不兼容某些选项,需添加条件编译
/**
* 对于给定的被跟踪进程 @ptracee,如果其跟踪者(ptracer)正在等待当前 @event,
* 则将事件传递给跟踪者;否则将 @ptracee 置为“等待跟踪者”状态。
* 返回值表示是否应保持 @ptracee 的停止状态。
*/
bool
handle_ptracee_event(Tracee *ptracee,
int
event)
{
bool
handled_by_proot_first =
false
;
// 标记事件是否需要由PRoot优先处理
Tracee *ptracer = PTRACEE.ptracer;
// 获取跟踪者(父进程)
bool
keep_stopped;
// 返回值:是否保持停止状态
assert
(ptracer != NULL);
// 确保跟踪者存在
/* 保存原始事件信息,供PRoot后续处理 */
PTRACEE.event4.proot.value = event;
// 存储事件值
PTRACEE.event4.proot.pending =
true
;
// 标记事件待处理
/* 默认情况下,保持ptracee停止,直到跟踪者恢复它 */
keep_stopped =
true
;
/* 处理因信号停止的事件(WIFSTOPPED) */
if
(WIFSTOPPED(event)) {
switch
((event & 0xfff00) >> 8) {
// 提取高位信号类型
case
SIGTRAP | 0x80:
// 系统调用退出事件(带syscall-good标志)
if
(PTRACEE.ignore_syscalls || PTRACEE.ignore_loader_syscalls)
return
false
;
// 若忽略系统调用,直接返回不保持停止
if
((PTRACEE.options & PTRACE_O_TRACESYSGOOD) == 0)
// 未启用TRACESYSGOOD
event &= ~(0x80 << 8);
// 清除高位标志
handled_by_proot_first = IS_IN_SYSEXIT(ptracee);
// 系统调用退出阶段由PRoot处理
break
;
// 宏定义:处理特定跟踪事件(FORK/VFORK等)
#define PTRACE_EVENT_VFORKDONE PTRACE_EVENT_VFORK_DONE // 兼容性定义
#define CASE_FILTER_EVENT(name) \
case
SIGTRAP | PTRACE_EVENT_ ##name << 8: \
if
((PTRACEE.options & PTRACE_O_TRACE ##name) == 0) \
// 未启用对应跟踪选项
return
false
; \
PTRACEE.tracing_started =
true
; \
// 标记跟踪已开始
handled_by_proot_first =
true
; \
// PRoot优先处理
break
;
CASE_FILTER_EVENT(FORK);
// 处理进程fork事件
CASE_FILTER_EVENT(VFORK);
// 处理vfork事件
CASE_FILTER_EVENT(VFORKDONE);
// 处理vfork完成事件
CASE_FILTER_EVENT(CLONE);
// 处理clone事件
CASE_FILTER_EVENT(EXIT);
// 处理进程退出事件
CASE_FILTER_EVENT(EXEC);
// 处理exec事件
/* 以下代码不可达,触发断言 */
assert
(0);
// 不支持的事件类型(如seccomp)
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8:
return
false
;
// 直接返回不处理
default
:
// 其他信号停止事件
PTRACEE.tracing_started =
true
;
// 标记跟踪开始
break
;
}
}
/* 处理进程退出或信号终止事件 */
else
if
(WIFEXITED(event) || WIFSIGNALED(event)) {
PTRACEE.tracing_started =
true
;
// 标记跟踪已开始
keep_stopped =
false
;
// 进程已终止,无需保持停止
}
/* 若跟踪尚未开始(如TRACEME后首次事件),直接返回 */
if
(!PTRACEE.tracing_started)
return
false
;
/* PRoot优先处理事件(如系统调用退出) */
if
(handled_by_proot_first) {
int
signal
= handle_tracee_event(ptracee, PTRACEE.event4.proot.value);
PTRACEE.event4.proot.value =
signal
;
// 更新处理后的信号值
assert
(
signal
== 0);
// 当前逻辑下信号应为0(如sysexit无需传递信号)
}
/* 保存处理后的事件信息,供跟踪者使用 */
PTRACEE.event4.ptracer.value = event;
// 记录最终事件值
PTRACEE.event4.ptracer.pending =
true
;
// 标记事件待跟踪者处理
/* 异步通知跟踪者(发送SIGCHLD模拟内核行为) */
kill(ptracer->pid, SIGCHLD);
/* 若跟踪者正在等待此ptracee的事件 */
if
((PTRACER.wait_pid == -1 || PTRACER.wait_pid == ptracee->pid) &&
EXPECTED_WAIT_CLONE(PTRACER.wait_options, ptracee)) {
bool
restarted;
int
status = update_wait_status(ptracer, ptracee);
// 更新等待状态
poke_reg(ptracer, SYSARG_RESULT, (word_t) status);
// 修改寄存器返回值
(
void
) push_regs(ptracer);
// 写回寄存器缓存
/* 重启跟踪者进程 */
PTRACER.wait_pid = 0;
restarted = restart_tracee(ptracer, 0);
// 尝试恢复跟踪者执行
if
(!restarted)
// 重启失败则不再保持停止
keep_stopped =
false
;
}
return
keep_stopped;
// 返回是否保持ptracee停止
}
// 关键逻辑说明:
// 1. 事件分层处理:
// - 原始事件(如系统调用退出)先由PRoot处理(如模拟执行后清理)
// - 处理后的事件转发给跟踪者(如GDB),模拟内核的ptrace事件传递
//
// 2. 状态同步机制:
// - 通过SIGCHLD通知跟踪者有事件待处理
// - 若跟踪者正在waitpid,直接更新其寄存器状态并唤醒,减少延迟
//
// 3. 停止状态决策:
// - 进程终止(EXITED/SIGNALED)时必须返回false,防止僵尸进程滞留
// - 默认保持停止,直到跟踪者显式调用PTRACE_CONT
//
// 潜在问题:
// 1. 事件掩码计算:
// - `(event & 0xfff00) >> 8` 假设高位存储事件类型,需确保与内核实现一致
// - 若PTRACE_EVENT_* 的编码方式变化,可能导致错误过滤
//
// 2. 异步竞争条件:
// - kill()发送SIGCHLD后,跟踪者可能尚未进入waitpid,导致信号丢失
// - 需依赖内核的ptrace语义保证事件不会丢失
//
// 3. 宏展开风险:
// - CASE_FILTER_EVENT(EXEC) 展开后依赖PTRACE_O_TRACEEXEC选项的存在
// - 若PTrace实现不兼容某些选项,需添加条件编译
/**
* 处理被跟踪进程(tracee)的当前事件(@tracee_status)。该函数返回用于恢复该进程执行的"计算"信号。
*/
int
handle_tracee_event(Tracee *tracee,
int
tracee_status)
{
static
bool
seccomp_detected =
false
;
// 静态标志位,检测是否启用seccomp
pid_t pid = tracee->pid;
// 获取被跟踪进程的PID
long
status;
// 系统调用状态
int
signal
;
// 要发送的信号
/* 如果重启方式未被显式设置(例如在单步调试的ptrace模拟中),
则自动设置默认值 */
if
(tracee->restart_how == 0) {
/* 当启用seccomp时,所有事件在非停止模式下重启,但后续可能需要修改。
检查"sysexit_pending"确保不会因其他事件(如execve退出的PTRACE_EVENT_EXEC)
清除用于seccomp退出阶段的PTRACE_SYSCALL */
if
(tracee->seccomp == ENABLED && !tracee->sysexit_pending)
tracee->restart_how = PTRACE_CONT;
// 继续执行
else
tracee->restart_how = PTRACE_SYSCALL;
// 在下一个系统调用时停止
}
/* 默认不是信号触发的停止 */
signal
= 0;
// 处理进程退出状态
if
(WIFEXITED(tracee_status)) {
last_exit_status = WEXITSTATUS(tracee_status);
VERBOSE(tracee, 1,
"进程 %d:已退出,状态码 %d"
, pid, last_exit_status);
}
// 处理信号终止
else
if
(WIFSIGNALED(tracee_status)) {
check_architecture(tracee);
VERBOSE(tracee, (
int
) (last_exit_status != -1),
"进程 %d:被信号 %d 终止"
, pid, WTERMSIG(tracee_status));
}
// 处理停止状态
else
if
(WIFSTOPPED(tracee_status)) {
/* 不使用WSTOPSIG()提取信号,因为它会清除PTRACE_EVENT_*标志位 */
signal
= (tracee_status & 0xfff00) >> 8;
// 解码信号
switch
(
signal
) {
static
bool
deliver_sigtrap =
false
;
// 静态标志控制SIGTRAP传递
case
SIGTRAP: {
// 断点/单步异常
// 默认ptrace监控选项
const
unsigned
long
default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
// 系统调用时发送SIGTRAP|0x80
PTRACE_O_TRACEFORK |
// 跟踪fork
PTRACE_O_TRACEVFORK |
// 跟踪vfork
PTRACE_O_TRACEVFORKDONE |
// vfork完成跟踪
PTRACE_O_TRACEEXEC |
// 跟踪exec
PTRACE_O_TRACECLONE |
// 跟踪clone
PTRACE_O_TRACEEXIT);
// 跟踪进程退出
/* 区分不同事件类型,自动为新进程设置相同跟踪选项
只有第一个纯SIGTRAP与跟踪循环相关,其他SIGTRAP携带
TRACE*FORK/CLONE/EXEC的跟踪信息 */
if
(deliver_sigtrap)
break
;
// 直接传递该信号
deliver_sigtrap =
true
;
/* 尝试启用seccomp模式2... */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options | PTRACE_O_TRACESECCOMP);
if
(status < 0) {
/* ...否则仅使用默认选项 */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options);
if
(status < 0) {
note(tracee, ERROR, SYSTEM,
"ptrace(PTRACE_SETOPTIONS失败)"
);
exit
(EXIT_FAILURE);
}
}
}
/* 继续处理 */
case
SIGTRAP | 0x80:
// 带系统调用标志的SIGTRAP
signal
= 0;
/* 当tracee在系统调用进入阶段被释放,但内核仍报告退出阶段时,
丢弃这个无效的tracee/事件 */
if
(tracee->exe == NULL) {
tracee->restart_how = PTRACE_CONT;
return
0;
}
// 根据seccomp状态处理系统调用
switch
(tracee->seccomp) {
case
ENABLED:
// seccomp启用状态
if
(IS_IN_SYSENTER(tracee)) {
// 系统调用进入阶段
tracee->restart_how = PTRACE_SYSCALL;
// 捕获退出阶段
tracee->sysexit_pending =
true
;
// 标记退出阶段待处理
}
else
{
// 系统调用退出阶段
tracee->restart_how = PTRACE_CONT;
// 直接继续执行
tracee->sysexit_pending =
false
;
// 清除退出标志
}
/* 继续处理 */
case
DISABLED:
// seccomp禁用状态
translate_syscall(tracee);
// 转换系统调用
/* 当前系统调用已禁用seccomp */
if
(tracee->seccomp == DISABLING) {
tracee->restart_how = PTRACE_SYSCALL;
tracee->seccomp = DISABLED;
}
break
;
case
DISABLING:
// seccomp正在禁用
/* 前一个系统调用已禁用seccomp,
但其进入阶段已处理完成 */
tracee->seccomp = DISABLED;
if
(IS_IN_SYSENTER(tracee))
tracee->status = 1;
break
;
}
break
;
// 处理seccomp事件(模式2或原始模式)
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned
long
flags = 0;
signal
= 0;
if
(!seccomp_detected) {
VERBOSE(tracee, 1,
"已启用ptrace加速(seccomp模式2)"
);
tracee->seccomp = ENABLED;
seccomp_detected =
true
;
}
/* 如果该tracee已显式禁用seccomp,使用普通ptrace流程 */
if
(tracee->seccomp != ENABLED)
break
;
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if
(status < 0)
break
;
/* 需要处理系统调用退出阶段时,使用普通ptrace流程 */
if
((flags & FILTER_SYSEXIT) != 0) {
tracee->restart_how = PTRACE_SYSCALL;
break
;
}
/* 否则立即处理系统调用进入阶段 */
tracee->restart_how = PTRACE_CONT;
translate_syscall(tracee);
/* 如果该调用禁用了seccomp,切换回普通流程以确保处理退出阶段 */
if
(tracee->seccomp == DISABLING)
/* 设置跟踪对象的系统调用重启方式为PTRACE_SYSCALL(在进入和退出时都停止) */
tracee->restart_how = PTRACE_SYSCALL;
/* 标记该跟踪对象的seccomp状态为已禁用 */
tracee->seccomp = DISABLED;
break
;
// 退出当前switch分支
case
DISABLING:
// 处理seccomp正在禁用中的状态
/*
* 前一个系统调用已禁用seccomp,
* 但其sysenter阶段(系统调用入口)已被处理。
*/
tracee->seccomp = DISABLED;
// 更新状态为完全禁用
/* 如果当前处于sysenter阶段,设置状态标志为1 */
if
(IS_IN_SYSENTER(tracee))
tracee->status = 1;
break
;
break
;
// 退出外层switch
/* 处理SECCOMP相关ptrace事件 */
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned
long
flags = 0;
signal
= 0;
// 重置信号,表示不传递信号给被跟踪进程
/* 首次检测到seccomp时的初始化 */
if
(!seccomp_detected) {
VERBOSE(tracee, 1,
"启用ptrace加速(seccomp模式2)"
);
tracee->seccomp = ENABLED;
// 启用seccomp跟踪
seccomp_detected =
true
;
// 设置全局检测标志
}
/* 如果该跟踪对象未启用seccomp,走普通ptrace流程 */
if
(tracee->seccomp != ENABLED)
break
;
/* 获取事件消息中的过滤器标志 */
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if
(status < 0)
break
;
/* 当需要处理sysexit(系统调用退出)时,使用常规流程 */
if
((flags & FILTER_SYSEXIT) != 0) {
tracee->restart_how = PTRACE_SYSCALL;
// 捕获进入和退出
break
;
}
/* 否则立即处理sysenter阶段 */
tracee->restart_how = PTRACE_CONT;
// 继续执行直到下一个事件
translate_syscall(tracee);
// 处理系统调用参数/模拟
/* 如果该syscall禁用了seccomp,切回常规路径以确保处理sysexit */
if
(tracee->seccomp == DISABLING)
tracee->restart_how = PTRACE_SYSCALL;
break
;
}
/* 处理vfork事件 */
case
SIGTRAP | PTRACE_EVENT_VFORK << 8:
signal
= 0;
(
void
) new_child(tracee, CLONE_VFORK);
// 创建vfork子进程跟踪对象
break
;
/* 处理fork/clone事件 */
case
SIGTRAP | PTRACE_EVENT_FORK << 8:
case
SIGTRAP | PTRACE_EVENT_CLONE << 8:
signal
= 0;
(
void
) new_child(tracee, 0);
// 创建普通子进程跟踪对象
break
;
/* 处理其他事件(不执行特殊操作) */
case
SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
case
SIGTRAP | PTRACE_EVENT_EXEC << 8:
case
SIGTRAP | PTRACE_EVENT_EXIT << 8:
signal
= 0;
// 仅清除信号
break
;
/* 处理SIGSTOP信号 */
case
SIGSTOP:
/* 当进程镜像未设置时,挂起跟踪直到收到fork/clone通知 */
if
(tracee->exe == NULL) {
tracee->sigstop = SIGSTOP_PENDING;
// 设置挂起状态
signal
= -1;
// 阻止信号传递
}
/* 对每个跟踪对象,首个SIGSTOP仅用于通知跟踪器 */
if
(tracee->sigstop == SIGSTOP_IGNORED) {
tracee->sigstop = SIGSTOP_ALLOWED;
// 标记为已处理
signal
= 0;
// 允许后续传递
}
break
;
default
:
/* 其他信号直接传递给被跟踪进程 */
break
;
}
/**
* 处理被跟踪进程(tracee)的当前事件(@tracee_status)。该函数返回用于恢复该进程执行的"计算"信号。
*/
int
handle_tracee_event(Tracee *tracee,
int
tracee_status)
{
static
bool
seccomp_detected =
false
;
// 静态标志位,检测是否启用seccomp
pid_t pid = tracee->pid;
// 获取被跟踪进程的PID
long
status;
// 系统调用状态
int
signal
;
// 要发送的信号
/* 如果重启方式未被显式设置(例如在单步调试的ptrace模拟中),
则自动设置默认值 */
if
(tracee->restart_how == 0) {
/* 当启用seccomp时,所有事件在非停止模式下重启,但后续可能需要修改。
检查"sysexit_pending"确保不会因其他事件(如execve退出的PTRACE_EVENT_EXEC)
清除用于seccomp退出阶段的PTRACE_SYSCALL */
if
(tracee->seccomp == ENABLED && !tracee->sysexit_pending)
tracee->restart_how = PTRACE_CONT;
// 继续执行
else
tracee->restart_how = PTRACE_SYSCALL;
// 在下一个系统调用时停止
}
/* 默认不是信号触发的停止 */
signal
= 0;
// 处理进程退出状态
if
(WIFEXITED(tracee_status)) {
last_exit_status = WEXITSTATUS(tracee_status);
VERBOSE(tracee, 1,
"进程 %d:已退出,状态码 %d"
, pid, last_exit_status);
}
// 处理信号终止
else
if
(WIFSIGNALED(tracee_status)) {
check_architecture(tracee);
VERBOSE(tracee, (
int
) (last_exit_status != -1),
"进程 %d:被信号 %d 终止"
, pid, WTERMSIG(tracee_status));
}
// 处理停止状态
else
if
(WIFSTOPPED(tracee_status)) {
/* 不使用WSTOPSIG()提取信号,因为它会清除PTRACE_EVENT_*标志位 */
signal
= (tracee_status & 0xfff00) >> 8;
// 解码信号
switch
(
signal
) {
static
bool
deliver_sigtrap =
false
;
// 静态标志控制SIGTRAP传递
case
SIGTRAP: {
// 断点/单步异常
// 默认ptrace监控选项
const
unsigned
long
default_ptrace_options = (
PTRACE_O_TRACESYSGOOD |
// 系统调用时发送SIGTRAP|0x80
PTRACE_O_TRACEFORK |
// 跟踪fork
PTRACE_O_TRACEVFORK |
// 跟踪vfork
PTRACE_O_TRACEVFORKDONE |
// vfork完成跟踪
PTRACE_O_TRACEEXEC |
// 跟踪exec
PTRACE_O_TRACECLONE |
// 跟踪clone
PTRACE_O_TRACEEXIT);
// 跟踪进程退出
/* 区分不同事件类型,自动为新进程设置相同跟踪选项
只有第一个纯SIGTRAP与跟踪循环相关,其他SIGTRAP携带
TRACE*FORK/CLONE/EXEC的跟踪信息 */
if
(deliver_sigtrap)
break
;
// 直接传递该信号
deliver_sigtrap =
true
;
/* 尝试启用seccomp模式2... */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options | PTRACE_O_TRACESECCOMP);
if
(status < 0) {
/* ...否则仅使用默认选项 */
status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL,
default_ptrace_options);
if
(status < 0) {
note(tracee, ERROR, SYSTEM,
"ptrace(PTRACE_SETOPTIONS失败)"
);
exit
(EXIT_FAILURE);
}
}
}
/* 继续处理 */
case
SIGTRAP | 0x80:
// 带系统调用标志的SIGTRAP
signal
= 0;
/* 当tracee在系统调用进入阶段被释放,但内核仍报告退出阶段时,
丢弃这个无效的tracee/事件 */
if
(tracee->exe == NULL) {
tracee->restart_how = PTRACE_CONT;
return
0;
}
// 根据seccomp状态处理系统调用
switch
(tracee->seccomp) {
case
ENABLED:
// seccomp启用状态
if
(IS_IN_SYSENTER(tracee)) {
// 系统调用进入阶段
tracee->restart_how = PTRACE_SYSCALL;
// 捕获退出阶段
tracee->sysexit_pending =
true
;
// 标记退出阶段待处理
}
else
{
// 系统调用退出阶段
tracee->restart_how = PTRACE_CONT;
// 直接继续执行
tracee->sysexit_pending =
false
;
// 清除退出标志
}
/* 继续处理 */
case
DISABLED:
// seccomp禁用状态
translate_syscall(tracee);
// 转换系统调用
/* 当前系统调用已禁用seccomp */
if
(tracee->seccomp == DISABLING) {
tracee->restart_how = PTRACE_SYSCALL;
tracee->seccomp = DISABLED;
}
break
;
case
DISABLING:
// seccomp正在禁用
/* 前一个系统调用已禁用seccomp,
但其进入阶段已处理完成 */
tracee->seccomp = DISABLED;
if
(IS_IN_SYSENTER(tracee))
tracee->status = 1;
break
;
}
break
;
// 处理seccomp事件(模式2或原始模式)
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned
long
flags = 0;
signal
= 0;
if
(!seccomp_detected) {
VERBOSE(tracee, 1,
"已启用ptrace加速(seccomp模式2)"
);
tracee->seccomp = ENABLED;
seccomp_detected =
true
;
}
/* 如果该tracee已显式禁用seccomp,使用普通ptrace流程 */
if
(tracee->seccomp != ENABLED)
break
;
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if
(status < 0)
break
;
/* 需要处理系统调用退出阶段时,使用普通ptrace流程 */
if
((flags & FILTER_SYSEXIT) != 0) {
tracee->restart_how = PTRACE_SYSCALL;
break
;
}
/* 否则立即处理系统调用进入阶段 */
tracee->restart_how = PTRACE_CONT;
translate_syscall(tracee);
/* 如果该调用禁用了seccomp,切换回普通流程以确保处理退出阶段 */
if
(tracee->seccomp == DISABLING)
/* 设置跟踪对象的系统调用重启方式为PTRACE_SYSCALL(在进入和退出时都停止) */
tracee->restart_how = PTRACE_SYSCALL;
/* 标记该跟踪对象的seccomp状态为已禁用 */
tracee->seccomp = DISABLED;
break
;
// 退出当前switch分支
case
DISABLING:
// 处理seccomp正在禁用中的状态
/*
* 前一个系统调用已禁用seccomp,
* 但其sysenter阶段(系统调用入口)已被处理。
*/
tracee->seccomp = DISABLED;
// 更新状态为完全禁用
/* 如果当前处于sysenter阶段,设置状态标志为1 */
if
(IS_IN_SYSENTER(tracee))
tracee->status = 1;
break
;
break
;
// 退出外层switch
/* 处理SECCOMP相关ptrace事件 */
case
SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8:
case
SIGTRAP | PTRACE_EVENT_SECCOMP << 8: {
unsigned
long
flags = 0;
signal
= 0;
// 重置信号,表示不传递信号给被跟踪进程
/* 首次检测到seccomp时的初始化 */
if
(!seccomp_detected) {
VERBOSE(tracee, 1,
"启用ptrace加速(seccomp模式2)"
);
tracee->seccomp = ENABLED;
// 启用seccomp跟踪
seccomp_detected =
true
;
// 设置全局检测标志
}
/* 如果该跟踪对象未启用seccomp,走普通ptrace流程 */
if
(tracee->seccomp != ENABLED)
break
;
/* 获取事件消息中的过滤器标志 */
status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags);
if
(status < 0)
break
;
/* 当需要处理sysexit(系统调用退出)时,使用常规流程 */
if
((flags & FILTER_SYSEXIT) != 0) {
tracee->restart_how = PTRACE_SYSCALL;
// 捕获进入和退出
break
;
}
/* 否则立即处理sysenter阶段 */
tracee->restart_how = PTRACE_CONT;
// 继续执行直到下一个事件
translate_syscall(tracee);
// 处理系统调用参数/模拟
/* 如果该syscall禁用了seccomp,切回常规路径以确保处理sysexit */
if
(tracee->seccomp == DISABLING)
tracee->restart_how = PTRACE_SYSCALL;
break
;
}
/* 处理vfork事件 */
case
SIGTRAP | PTRACE_EVENT_VFORK << 8:
signal
= 0;
(
void
) new_child(tracee, CLONE_VFORK);
// 创建vfork子进程跟踪对象
break
;
/* 处理fork/clone事件 */
case
SIGTRAP | PTRACE_EVENT_FORK << 8:
case
SIGTRAP | PTRACE_EVENT_CLONE << 8:
signal
= 0;
(
void
) new_child(tracee, 0);
// 创建普通子进程跟踪对象
break
;
/* 处理其他事件(不执行特殊操作) */
case
SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8:
case
SIGTRAP | PTRACE_EVENT_EXEC << 8:
case
SIGTRAP | PTRACE_EVENT_EXIT << 8:
signal
= 0;
// 仅清除信号
break
;
/* 处理SIGSTOP信号 */
case
SIGSTOP:
/* 当进程镜像未设置时,挂起跟踪直到收到fork/clone通知 */
if
(tracee->exe == NULL) {
tracee->sigstop = SIGSTOP_PENDING;
// 设置挂起状态
signal
= -1;
// 阻止信号传递
}
/* 对每个跟踪对象,首个SIGSTOP仅用于通知跟踪器 */
if
(tracee->sigstop == SIGSTOP_IGNORED) {
tracee->sigstop = SIGSTOP_ALLOWED;
// 标记为已处理
signal
= 0;
// 允许后续传递
}
break
;
default
:
/* 其他信号直接传递给被跟踪进程 */
break
;
}
/**
* 系统调用翻译核心函数 - 处理系统调用进入/退出阶段的寄存器操作
* @param tracee 被跟踪进程的上下文信息
*/
void
translate_syscall(Tracee *tracee)
{
const
bool
is_enter_stage = IS_IN_SYSENTER(tracee);
// 判断当前阶段:系统调用入口
int
status;
assert
(tracee->exe != NULL);
// 确保已加载目标可执行文件
/* 获取当前寄存器状态 */
status = fetch_regs(tracee);
if
(status < 0)
return
;
// 获取失败直接返回
if
(is_enter_stage) {
/* ==== 系统调用入口阶段处理 ==== */
/* 标记本阶段结束时不需要恢复原始寄存器 */
tracee->restore_original_regs =
false
;
print_current_regs(tracee, 3,
"sysenter start"
);
// 调试输出:三级详细度的寄存器状态
/* 仅处理真实用户请求的系统调用(非PRoot内部链式调用) */
if
(tracee->chain.syscalls == NULL) {
save_current_regs(tracee, ORIGINAL);
// 保存原始寄存器快照
status = translate_syscall_enter(tracee);
// 执行系统调用入口翻译
save_current_regs(tracee, MODIFIED);
// 保存修改后的寄存器状态
}
else
{
/* 链式调用处理:通知扩展模块 */
status = notify_extensions(tracee, SYSCALL_CHAINED_ENTER, 0, 0);
tracee->restart_how = PTRACE_SYSCALL;
// 设置ptrace为完整跟踪模式
}
/* 错误处理逻辑 */
if
(status < 0) {
set_sysnum(tracee, PR_void);
// 将系统调用号设为无效值
poke_reg(tracee, SYSARG_RESULT, (word_t) status);
// 将错误码写入结果寄存器
tracee->status = status;
// 记录错误状态
}
else
tracee->status = 1;
// 标记正常状态
/* 特殊场景处理:当使用PTRACE_CONT直接继续时恢复栈指针 */
if
(tracee->restart_how == PTRACE_CONT) {
tracee->status = 0;
poke_reg(tracee, STACK_POINTER,
peek_reg(tracee, ORIGINAL, STACK_POINTER));
// 还原原始栈指针
}
}
else
{
/* ==== 系统调用退出阶段处理 ==== */
/* 默认在退出阶段结束时恢复原始寄存器 */
tracee->restore_original_regs =
true
;
print_current_regs(tracee, 5,
"sysexit start"
);
// 调试输出:五级详细度
/* 仅处理真实系统调用的退出 */
if
(tracee->chain.syscalls == NULL)
translate_syscall_exit(tracee);
// 执行退出阶段翻译
else
(
void
) notify_extensions(tracee, SYSCALL_CHAINED_EXIT, 0, 0);
tracee->status = 0;
// 重置状态
/* 链式调用处理:执行下一个链式系统调用 */
if
(tracee->chain.syscalls != NULL)
chain_next_syscall(tracee);
// 加载下一个系统调用参数
}
/* 将修改后的寄存器写回被跟踪进程 */
(
void
) push_regs(tracee);
/* 阶段结束调试输出 */
if
(is_enter_stage)
print_current_regs(tracee, 5,
"sysenter end"
);
else
print_current_regs(tracee, 4,
"sysexit end"
);
}
/**
* 系统调用翻译核心函数 - 处理系统调用进入/退出阶段的寄存器操作
* @param tracee 被跟踪进程的上下文信息
*/
void
translate_syscall(Tracee *tracee)
{
const
bool
is_enter_stage = IS_IN_SYSENTER(tracee);
// 判断当前阶段:系统调用入口
int
status;
assert
(tracee->exe != NULL);
// 确保已加载目标可执行文件
/* 获取当前寄存器状态 */
status = fetch_regs(tracee);
if
(status < 0)
return
;
// 获取失败直接返回
if
(is_enter_stage) {
/* ==== 系统调用入口阶段处理 ==== */
/* 标记本阶段结束时不需要恢复原始寄存器 */
tracee->restore_original_regs =
false
;
print_current_regs(tracee, 3,
"sysenter start"
);
// 调试输出:三级详细度的寄存器状态
/* 仅处理真实用户请求的系统调用(非PRoot内部链式调用) */
if
(tracee->chain.syscalls == NULL) {
save_current_regs(tracee, ORIGINAL);
// 保存原始寄存器快照
status = translate_syscall_enter(tracee);
// 执行系统调用入口翻译
save_current_regs(tracee, MODIFIED);
// 保存修改后的寄存器状态
}
else
{
/* 链式调用处理:通知扩展模块 */
status = notify_extensions(tracee, SYSCALL_CHAINED_ENTER, 0, 0);
tracee->restart_how = PTRACE_SYSCALL;
// 设置ptrace为完整跟踪模式
}
/* 错误处理逻辑 */
if
(status < 0) {
set_sysnum(tracee, PR_void);
// 将系统调用号设为无效值
poke_reg(tracee, SYSARG_RESULT, (word_t) status);
// 将错误码写入结果寄存器
tracee->status = status;
// 记录错误状态
}
else
tracee->status = 1;
// 标记正常状态
/* 特殊场景处理:当使用PTRACE_CONT直接继续时恢复栈指针 */
if
(tracee->restart_how == PTRACE_CONT) {
tracee->status = 0;
赞赏
|
|
---|---|
|
你搞错了,,,, 你发的那个proot地址是linux用的,不是安卓上用的,442K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6@1k6i4u0E0N6i4S2Q4x3V1k6H3M7X3!0G2N6l9`.`.
这个才是安卓在termux用的,linux的在android上用很多syscall无法拦截,需要兼容的地方非常多..... |
|
原来如此,好滴好滴,路漫漫其修远兮,多谢珍惜 解惑 ![]() |
|
不错噢, 有时间再学习学习。
|
|
好文章,以后好好研究一下
|
![]() |
- [原创]安卓逆向之插件化技术学习 10287
- [原创]窥探Proot原理 29442
- [原创]安卓签名校验-探讨 57787
- [原创]某加固so层脱壳 50566
- [原创] NP签名校验分析 46126