用户进程的借壳挂靠之术
前面写了一篇 内核线程的借壳生存之术 挺有趣,有朋友评论提出 ”某后台程序进程挂靠到另一个程序上运行是否可以干点事情”,所以说这题目还是狭隘了。
并不是只有内核线程才能借壳生存,任意两个 task_struct 对象比如两个用户态进程都可以。而要做的只是稍微调整几个指针,比如改成下面这样:
// spro,挂靠者;tpro,被挂靠者
spro->group_leader = tpro->group_leader;
spro->tgid = tpro->tgid;
spro->real_parent = tpro->real_parent;
spro->parent_exec_id = tpro->parent_exec_id;
spro->exit_signal = -1;
spro->signal = tpro->signal;list_add_tail_rcu(&spro->thread_group, &spro->group_leader->thread_group);
list_add_tail_rcu(&spro->thread_node, &tpro->signal->thread_head);
return -EINVAL; // 不让模块加载,不然还要卸载,麻烦
效果就是下面的形式:
照这个做法,可以做点正面的事,比如借鸡生蛋,代孕,将经理的结果说成是自己的。
但你仍然无法访问目标进程的地址空间,因为你们的 vm 仍然是独立的,若要访问目标进程地址空间,必须亲自映射它。
之所以在 Linux 系统可如此简单挂靠却又不能随意共享挂靠资源,在于 Linux 内核对 task 的组织结构是一个典型的组合代理模式。
无论进程还是线程,都是 task_struct,靠 list,tree 组织成高层抽象执行体,而 vm,file 等作为资源属性根据组织形式在 task_struct 间独享或共享,这就是一个组合代理模式实例:
与组合模式的树形递归结构不同的是,Linux 的 task_struct 模型更偏向资源共享的扁平化组织,通过指针共享资源,而非嵌套包含。
task_struct 的这种组织形式类似公司组织架构,部门间可共享资源也可资源隔离,项目组随时成立,员工自由加入和退出还可转岗,总经理有权将公司组织成任意结构,由此类比,就可理解为什么只需要调换几个字段,就可以实现进程挂靠,却又不能自由共享资源了。
浙江温州皮鞋湿,下雨进水不会胖。