In my opinion, the single most effective method of rootkit detection can be summed up by verifying the code integrity of the kernel in the memory—in other words, comparing the code in the kernel memory against the expected code. But what can we compare kernel memory code against? Well, why not vmlinux? This was an approach that I originally explored in 2008. Knowing that an ELF executable's text segment does not change from disk to memory, unless it's some weird self-modifying binary, which the kernel is not… or is it? I quickly ran into trouble and was finding all sorts of code discrepancies between the kernel memory text segment and the vmlinux text segment. This was baffling at first since I had no kernel rootkits installed during these tests. After examining some of the ELF sections in vmlinux, however, I quickly saw some areas that caught my attention:
$ readelf -S vmlinux | grep alt [23] .altinstructions PROGBITS ffffffff81e64528 01264528 [24] .altinstr_replace PROGBITS ffffffff81e6a480 0126a480
There are several sections within the Linux kernel binary that contain alternative instructions. As it turns out, the Linux kernel developers had a bright idea: what if the Linux kernel can intelligently patch its own code segment at runtime, changing certain instructions for "memory barriers" based on the specific CPU that was detected? This would be a nice idea because fewer stock kernels would need to be created for all the different types of CPUs out there. Unfortunately for the security researcher who wants to detect any malicious changes in the kernel's code segment, these alternative instructions would have to be understood and applied first.
There are two sections that contain the majority of information needed to know which instructions in the kernel are getting patched at runtime. There is a great article that explains these sections now, which was not available at the time of my early research into this area of the kernel:
https://lwn.net/Articles/531148/
The general idea, however, is that the .altinstructions section contains an array of struct alt_instr structs. Each one represents an alternative instruction record, giving you the location of the original instruction and the location of the new instruction that should be used to patch the original. The .altinstr_replace section contains the actual alternative instructions that are referenced by the alt_instr->repl_offset member.
struct alt_instr {
s32 instr_offset; /* original instruction */
s32 repl_offset; /* offset to replacement instruction */
u16 cpuid; /* cpuid bit set for replacement */
u8 instrlen; /* length of original instruction */
u8 replacementlen; /* length of new instruction, <= instrlen */
};On older kernels, the first two members gave the absolute addresses of the old and new instructions, but on newer kernels, a relative offset is used.
Over the years, I have designed several tools that detect the integrity of the Linux kernel's code segment. This detection technique will obviously work only on kernel rootkits that modify the text segment, and most of them do in some way or the other. However, there are exceptions such as rootkits that rely only on altering the VFS layer, which resides in the data segment and will not be detected by verifying the integrity of the text segment. Most recently, the tool that I wrote (a part of the kernel Voodoo software suite) is named textify, and it essentially compares the text segment of the kernel memory, taken from /proc/kcore, against the text segment in vmlinux. It parses .altinstructions and various other sections, such as .parainstructions, to learn the locations of code instructions that are legally patched. In this way, there are no false positives showing up. Although textify is currently not available to the public, the general idea has been explained. Therefore, it may be reimplemented by anyone who wishes to attempt the somewhat arduous coding procedures necessary to make it work.
# ./textify vmlinux /proc/kcore -s sys_call_table kernel Detective 2014 - Bitlackeys.org [+] Analyzing kernel code/data for symbol sys_call_table in range [0xffffffff81801460 - 0xffffffff81802570] [+] No code modifications found for object named 'sys_call_table' # ./textify vmlinux /proc/kcore -a kernel Detective 2014 - Bitlackeys.org [+] Analyzing kernel code of entire text segment. [0xffffffff81000000 - 0xffffffff81773da4] [+] No code modifications have been detected within kernel memory
In the preceding example, we first check to make sure that sys_call_table has not been modified. On modern Linux systems, sys_call_table is marked as read-only and is therefore stored in the text segment, which is why we can use textify to validate its integrity. In the next command, we run textify with the -a switch, which scans every single byte in the entire text segment for illegal modifications. We could have simply run -a to begin with since sys_call_table is included in -a, but sometimes, it's nice to scan things by symbol name too.