源码中的内核文件dtb格式结尾的内核文件位于aosp/device目录下

https://source.android.com/source/building-kernels

查看当前内核文件

# tree -NCfhl|grep dtb

下载内核源码:

// 清华  // 谷歌

# git clone https://aosp.tuna.tsinghua.edu.cn/android/kernel/msm.git
# git clone https://android.googlesource.com/kernel/msm.git

# cd msm //进入msm工程目录

查看手机内核版本:

# adb ahell
# su
# cat /proc/version

g开头的后面XXXXXX就是对应的版本

检出内核版本:

# git checkout XXXXXX

设置编译环境,源码目录要对应

export PATH=$PATH:/root/aosp/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin

设置目标架构,Pixel Xl(marlin)是arm64的CPU

export ARCH=arm64
export SUBARCH=arm64
export PATH=$PATH:$ANDROID_AARCH64
export CROSS_COMPILE=aarch64-linux-android-

准备编译参数

make marlin_defconfig

开始编译

make

可能两个报错,安装库

# apt install bc
# apt install liblz4-tool

生成成功的内核位于arch/arm64/boot/Image.lz4-dtb路径中

重新生成刷机镜像

cd /root/aosp/
 . build/envsetup.sh && lunch aosp_marlin-userdebug
(在这个版本中,选择aosp_marlin_userdebug)

按照官网指示,重新定义下TARGET_PREBUILT_KERNEL变量的路径

export TARGET_PREBUILT_KERNEL=/root/aosp/arch/arm64/boot/Image.lz4-dtb

然后重新编译boot镜像,不需要全部重新编译了。

make bootimage

最后生成的boot镜像还是在out/target/product/marlin/目录下,可以看到boot.img镜像是刚刚生成的,其余原先已经生成过的各种img的最后修改日期是Yesterday。

最后使用新的boot.img来制作刷机包,刷机即可。重新开机后可以看到内核已经是新编译的了,甚至都能看到我的编译系统的hostname,编译时间也是2020年的时间。

修改内核源码

在《安卓4.4的内核源码,也就是msm/fs/proc/base.c、msm/fs/proc/array.c、msm/fs/proc/array.c这三个文件

在《安卓7.1.2 内核编译修改 TracerPid》文章中修改的是msm/fs/proc/base.c和msm/fs/proc/array.c这两个文件。

在《安卓8.1.0_r1版本的内核。原因是proc_pid_wchan这个函数,在三个版本中都进行了重构,8.1.0_r1版本的fs/proc/base.c中的proc_pid_wchan函数

当前修改为8.1.0版本如下

fs/proc/base.c中的proc_pid_wchan函数

修改前

/*
 * Provides a wchan file via kallsyms in a proper one-value-per-file format.
 * Returns the resolved symbol.  If that fails, simply return the address.
 */
static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
			  struct pid *pid, struct task_struct *task)
{
	unsigned long wchan;
	char symname[KSYM_NAME_LEN];

	wchan = get_wchan(task);

	if (lookup_symbol_name(wchan, symname) < 0)
		if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
			return 0;
		else
			return seq_printf(m, "%lu", wchan);
	else
		return seq_printf(m, "%s", symname);
}

修改后

/*
 * Provides a wchan file via kallsyms in a proper one-value-per-file format.
 * Returns the resolved symbol.  If that fails, simply return the address.
 */
static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
			  struct pid *pid, struct task_struct *task)
{
	unsigned long wchan;
	char symname[KSYM_NAME_LEN];

	wchan = get_wchan(task);

	if (lookup_symbol_name(wchan, symname) < 0)
		if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
			return 0;
		else
			return seq_printf(m, "%lu", wchan);
    else {
        if (strstr(symname, "trace")) {
            return seq_printf(m, "%s", "sys_epoll_wait");
        }
		return seq_printf(m, "%s", symname);
    }
}

然后就是跟老版本一样的,在fs/proc/array.c文件中,将

修改前

/*
 * The task state array is a strange "bitmap" of
 * reasons to sleep. Thus "running" is zero, and
 * you can test for combinations of others with
 * simple bit tests.
 */
static const char * const task_state_array[] = {
	"R (running)",		/*   0 */
	"S (sleeping)",		/*   1 */
	"D (disk sleep)",	/*   2 */
	"T (stopped)",		/*   4 */
	"t (tracing stop)",	/*   8 */
	"X (dead)",		/*  16 */
	"Z (zombie)",		/*  32 */
};

修改后

/*
 * The task state array is a strange "bitmap" of
 * reasons to sleep. Thus "running" is zero, and
 * you can test for combinations of others with
 * simple bit tests.
 */
static const char * const task_state_array[] = {
	"R (running)",		/*   0 */
	"S (sleeping)",		/*   1 */
	"D (disk sleep)",	/*   2 */
	"S (sleeping)",		/*   4 */
	"S (sleeping)",	/*   8 */
	"X (dead)",		/*  16 */
	"Z (zombie)",		/*  32 */
};

seq_printf(m,
		"State:\t%s\n"
		"Tgid:\t%d\n"
		"Pid:\t%d\n"
		"PPid:\t%d\n"
		"TracerPid:\t%d\n"
		"Uid:\t%d\t%d\t%d\t%d\n"
		"Gid:\t%d\t%d\t%d\t%d\n"
		"Ngid:\t%d\n",
		get_task_state(p),
		leader ? task_pid_nr_ns(leader, ns) : 0,
		pid_nr_ns(pid, ns),
		ppid, tpid,
		from_kuid_munged(user_ns, cred->uid),
		from_kuid_munged(user_ns, cred->euid),
		from_kuid_munged(user_ns, cred->suid),
		from_kuid_munged(user_ns, cred->fsuid),
		from_kgid_munged(user_ns, cred->gid),
		from_kgid_munged(user_ns, cred->egid),
		from_kgid_munged(user_ns, cred->sgid),
		from_kgid_munged(user_ns, cred->fsgid),
		task_numa_group_id(p));

更改为:

seq_printf(m,
		"State:\t%s\n"
		"Tgid:\t%d\n"
		"Pid:\t%d\n"
		"PPid:\t%d\n"
		"TracerPid:\t0\n"
		"Uid:\t%d\t%d\t%d\t%d\n"
		"Gid:\t%d\t%d\t%d\t%d\n"
		"Ngid:\t%d\n",
		get_task_state(p),
		leader ? task_pid_nr_ns(leader, ns) : 0,
		pid_nr_ns(pid, ns),
		ppid, /*tpid,*/
		from_kuid_munged(user_ns, cred->uid),
		from_kuid_munged(user_ns, cred->euid),
		from_kuid_munged(user_ns, cred->suid),
		from_kuid_munged(user_ns, cred->fsuid),
		from_kgid_munged(user_ns, cred->gid),
		from_kgid_munged(user_ns, cred->egid),
		from_kgid_munged(user_ns, cred->sgid),
		from_kgid_munged(user_ns, cred->fsgid),
		task_numa_group_id(p));

确保更改无误后,直接重新编译内核即可,然后按照同样的流程刷入手机。

进一步反反反调试
当然,是不是只要在内核中修改了TracerPid部分的机制的源码,就可以一劳永逸了呢?答案并不是这样,App还可以通过fork出子进程附加父进程的方式,父进程中读自己的TracerPid是否为子进程的PID,如果不是则说明TracerPid机制已经失效,此系统为修改版的系统,同样结束自己的进程。