Home > Software engineering >  getdents() syscall implementation for rootkit
getdents() syscall implementation for rootkit

Time:12-28

I plan to hook my own version of getdents() for my rootkit. Code is here:

asmlinkage int new_getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count)
{
    int nread;
    int bpos;
    struct linux_dirent *d;
    int (*orig_func)(unsigned int fd, struct linux_dirent *dirp, unsigned int count);
    t_syscall_hook *open_hook;

    open_hook = find_syscall_hook(__NR_getdents);
    orig_func = (void*) open_hook->orig_func;

    nread = (*orig_func)(fd, dirp, count);
    d = dirp;

    for (bpos = 0; bpos < nread;) {
      d = (struct linux_dirent *) ((char*)dirp   bpos);
      printk(KERN_INFO "%s\n", d->d_name);
      bpos  = d->d_reclen;
    }

    return nread;
}

I fail to understand the type cast in this line: d = (struct linux_dirent *) ((char*)dirp bpos);

Both d and dirp hold memory address for a linux_dirent struct. d_reclen contains length of the entry. If we receive d_reclen as 3, 5, 7, then entries would be present at dirp, dirp 3 /size(linux_dirent), (dirp 3/size(linux_dirent) 5/size(linux_dirent))...

So the line should be something like this then: d = (struct linux_dirent *) ((dirp bpos)/size(linux_dirent));

Why are we converting into (char *)?

typedef struct {
        unsigned long   d_ino;
        unsigned long   d_off;
        unsigned short  d_reclen;
        char            d_name[1];
} linux_dirent;

CodePudding user response:

So the line should be something like this then: d = (struct linux_dirent *) ((dirp bpos)/size(linux_dirent));

No - dirp / sizeof(linux_dirent) makes little sense, the offset of dirp from 0 has no relation to the size of the structure. Dividing memory address by the size of the structure... it's just some unrelated address.

You meant, like, to divide only the offset from the memory location, and then add the resulting pointer to the pointer. Well, along:

(char*)dirp   ((char*)(dirp   bpos) - (char*)dirp)/sizeof(linux_dirent)
                       ^^^^^^^^^^^                                       = (char*)dirp   bpos * sizeof(linux_dirent)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                        = bpos * sizoef(linux_dirent)

              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  = bpos
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  = (char*)dirp   bpos

But... instead of incrementing the dirp pointer in sizeof(linux_dirent) steps, you can just increment it in 1 bytes steps. That's what the cast to char* is doing. sizeof(char) is always 1. The following is true to the value:

dirp   bpos == (char*)dirp   bpos * sizeof(linux_dirent)

Why are we converting into (char *)?

We are converting to char * to change how many bytes the operator will increment. Short example:

int *a = 20;
       a   5 == 20   5 * sizeof(int)
(char*)a   5 == 20   5 * sizeof(char)   // sizeof(char) = 1, so:
(char*)a   5 == 25

Pointer arithmetic is a good topic to research.

  • Related