Rootkit needs to find out if there is a program catching it in time, and the detection program itself needs to be vigilant about Rootkit injection and left-right interaction.
The instrumentation program found Rootkit to be very versatile, and I've previously described how to static scan through address ranges that are called from each other by kernel text segments:
https://blog.csdn.net/dog250/article/details/105474909
In this article, I will introduce a dynamic trace stack approach to catch call exceptions to kernel functions.
Take a neutral program for example.Never praise or disparage good or evil.
Last month, I implemented a function to count the number of packets dropped by iptables on the INPUT chain by DROP:
https://blog.csdn.net/dog250/article/details/105206753
See that article for more details.
I mean, how do I find out that the middle of ip_local_deliver y is hook ed?
It's hard, but it's not impossible.
Assuming that I add a jmp stub to the header of each function in the kernel without regard to performance degradation, dump the current stack in that stub, as long as there are addresses below the RPB of the stack that are not in the range of the text segment addresses of the kernel, then you need to go into detail and exclude the callback function, and the rest is illegal.
The kernel text segment address interval is roughly:
ffffffff81000000 T _text ... ffffffff81649abb T _etext
Let's take an example from DROP statistics to see how code gets hook ed:
# This is the original call 0xffffffff81561eb5 <ip_local_deliver+165>: movq $0xffffffff81561ad0,-0x18(%rbp) 0xffffffff81561ebd <ip_local_deliver+173>: callq 0xffffffff815586a0 <nf_hook_slow> 0xffffffff81561ec2 <ip_local_deliver+178>: cmp $0x1,%eax # This is a call after being hook ed 0xffffffff81561eb5 <ip_local_deliver+165>: movq $0xffffffff81561ad0,-0x18(%rbp) 0xffffffff81561ebd <ip_local_deliver+173>: callq 0xffffffffa00ef000 <test_stub1> 0xffffffff81561ec2 <ip_local_deliver+178>: cmp $0x1,%eax crash> dis test_stub1 0xffffffffa00ef000 <test_stub1>: callq 0xffffffff815586a0 <nf_hook_slow> 0xffffffffa00ef005 <test_stub1+5>: cmp $0x1,%eax 0xffffffffa00ef008 <test_stub1+8>: je 0xffffffffa00ef011 <test_stub1+17> 0xffffffffa00ef00a <test_stub1+10>: incl 0xffffffffa00f1280 0xffffffffa00ef011 <test_stub1+17>: retq 0xffffffffa00ef012 <test_stub1+18>: callq 0xffffffff8162e40d <printk> 0xffffffffa00ef017 <test_stub1+23>: pop %rbp 0xffffffffa00ef018 <test_stub1+24>: retq
OK, we confirm that if nf_hook_slow prints the stack, test_stub1 should be found, after all, because it calls nf_hook_slow.The stack should have the address 0xffffffa00ef005.
Let's try it.
To code without compiling, I still use a guru-mode stap script:
%{ #include <linux/in.h> #include <linux/ip.h> %} %{ unsigned char *_self; %} function init_self() %{ _self = (void *)kallsyms_lookup_name("nf_hook_slow"); %} function filter:long(iphdr:long, stat:long) { hooknum = 0; protocol = @cast(iphdr, "iphdr")->protocol if (stat) { hooknum = @cast(stat, "nf_hook_state")->hook } return (hooknum == 1 && protocol == %{ IPPROTO_ICMP %}) } function ip_hdr:long(skb:long) %{ struct iphdr *iph = ip_hdr((struct sk_buff *)STAP_ARG_skb); STAP_RETVALUE = (long)iph; %} function _backtrace () %{ unsigned long rbp; unsigned long *prbp; int i, prn = 0; asm ("mov %%rbp, %0;\n":"=m"(rbp)::); prbp = (unsigned long *)rbp; for (i = 0; i < 20; i++) { // Since the stap mechanism itself has a lot of calls piled up, we skip them out, starting just below nf_hook_slow. if (prbp[i] == (unsigned long)_self) prn = 1; if (prn == 1) STAP_PRINTF("0x%lx \n", prbp[i]); } %} probe begin { init_self(); } probe kernel.function("nf_hook_slow") { iph = ip_hdr(pointer_arg(1)); stat = pointer_arg(2); if (filter(iph, stat)) { _backtrace(); println(); println(); } }
The experiment will now begin.
Add an iptables rule and run the stap script:
[root@localhost test]# iptables -A INPUT -p icmp -j DROP [root@localhost test]# stap -g ./sdump.stp
Then ping the machine to see the output:
0xffffffff815586a0 0xffff88003fd83c80 0xffffffffa00ef005 0xffff88003fd83c70 0xffffffff8112945e 0xffff88003c92d000 0xffff8800361af410 0xffff88003d4f0000
Ha-ha, find 0xffffffa00ef005 now!I can see with my eyes that something is wrong, obviously not at the beginning of 81...This address is obviously not in the code snippet interval of the kernel, but in the mapping interval of the module:
0xffffffffa0000000 ~ 0xffffffffff000000
This tells you what you do in the module by allocating memory.
However, I find it embarrassing to be caught like this, so what if we hide the code in the kernel code snippet itself?See the following article:
https://blog.csdn.net/dog250/article/details/105496996
There are plenty of places in the kernel for you to hide dirt. Just look directly for the nop area. Here's one where I found it and copied the code from the past:
crash> dis 0xffffffff810001d0 10 0xffffffff810001d0 <_stext+8>: callq 0xffffffff815586a0 <nf_hook_slow> 0xffffffff810001d5 <_stext+13>: cmp $0x1,%eax 0xffffffff810001d8 <_stext+16>: je 0xffffffff810001e1 <_stext+25> 0xffffffff810001da <_stext+18>: incl 0xffffffffa0239280 0xffffffff810001e1 <_stext+25>: retq 0xffffffff810001e2 <_stext+26>: nop
Now load the module for this dirty and scale-resistant template and detect it again with the stap script mentioned above:
0xffffffff815586a0 0xffff88003fd83c80 0xffffffff810001d5 0xffff88003fd83c70 0xffffffff8112945e 0xffff88003ae96f00 0xffff88003bc71210 0xffff88003d4f0000
Notice this 0xffffffff810001d5 address, which is in _step.If you don't look carefully at the contents of this address, it's easy to leave it as a normal address.
Stop!Something the matter!
What if stap is blocked?Try perf probe:
perf probe --del 'nf_hook_slow*' && perf probe 'nf_hook_slow caller=$stack0'&& ping 127.0.0.1
Then print the result:
[root@localhost ~]# perf record -e probe:nf_hook_slow -aR sleep 3 && perf script [ perf record: Woken up 1 times to write data ] ... ping 3376 [003] 5361.373481: probe:nf_hook_slow: (ffffffff815586a0) caller=ffffffff810001d5
We can see caller, ff ffffffff810001d5, still grabbing!
Of course, you'll question, how do I know to probe nf_hook_slow?Isn't this Zhuge Liang afterwards?
Yes, this question is entirely reasonable. I didn't know which function to probe beforehand. I'm just demonstrating here.
If we have a list of functions that can be called after a hook, that is, a list of suspected functions, it's half as simple.We actually have this list.For example, a system call function.
Many times, we hook the original system call, enter our own logic, and then call the original system call function:
int my_sys_write(...) { do_something(...); return orig_sys_write(...); }
Obviously, my_sys_write is definitely not within the scope of the kernel text segment, and we can successfully capture this hook behavior using the above method.
Okay, we're going to start. We'll be here today.
Zhejiang Wenzhou leather shoes are wet, so you won't get fat when it rains.