Issue
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;
Solution
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.
Answered By - KamilCuk