#if defined __i386__
#define START_ADDRESS 0xc0000000
#define END_ADDRESS 0xd0000000
#elif defined __x86_64__
#define START_ADDRESS 0xffffffff81000000
#define END_ADDRESS 0xffffffffa2000000
#else
#error ARCH_ERROR_MESSAGE
#endif
void **sys_call_table;
/**
* Finds a system call table based on a heruistic.
* Note that the heruistic is not ideal, so it might find a memory region that
* looks like a system call table but is not actually a system call table, but
* it seems to work all the time on my systems.
*
* @return system call table on success, NULL on failure.
*/
void **find_syscall_table(void)
{
void **sctable;
void *i = (void*) START_ADDRESS;
while (i < END_ADDRESS) {
sctable = (void **) i;
// sadly only sys_close seems to be exported -- we can't check against more system calls
if (sctable[__NR_close] == (void *) sys_close) {
size_t j;
// we expect there to be at least 300 system calls
const unsigned int SYS_CALL_NUM = 300;
// sanity check: no function pointer in the system call table should be NULL
for (j = 0; j < SYS_CALL_NUM; j ++) {
if (sctable[j] == NULL) {
// this is not a system call table
goto skip;
}
}
return sctable;
}
skip:
;
i += sizeof(void *);
}
return NULL;
}
Another approach could be to get the syscall table through the IDTR register, like shown in this Phrack article. The cool thing about this way is that it doesn't require Linux Kernel Module support.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed)) idtr;
struct {
unsigned short off1;
unsigned short sel;
unsigned char none,flags;
unsigned short off2;
} __attribute__ ((packed)) idt;
int kmem;
void readkmem (void *m,unsigned off,int sz)
{
if (lseek(kmem,off,SEEK_SET)!=off) {
perror("kmem lseek"); exit(2);
}
if (read(kmem,m,sz)!=sz) {
perror("kmem read"); exit(2);
}
}
#define CALLOFF 100 /* we'll read first 100 bytes of int $0x80*/
main ()
{
unsigned sys_call_off;
unsigned sct;
char sc_asm[CALLOFF],*p;
// "asks the processor" for the interrupt
descriptor table
asm ("sidt %0" : "=m" (idtr));
printf("idtr base at 0x%X\n",(int)idtr.base);
/* now we will open kmem */
kmem = open ("/dev/kmem",O_RDONLY);
if (kmem<0) return 1;
// get a pointer to the interrupt descriptor of
int $0x80
readkmem (&idt,idtr.base+8*0x80,sizeof(idt));
// From the IDT we can compute the address of int $0x80's entrypoint
sys_call_off = (idt.off2 << 16) | idt.off1;
printf("idt80: flags=%X sel=%X off=%X\n",
(unsigned)idt.flags,(unsigned)idt.sel,sys_call_off);
/* we have syscall routine address now, look for syscall table
dispatch (indirect call) */
readkmem (sc_asm,sys_call_off,CALLOFF);
p = (char*)memmem (sc_asm,CALLOFF,"\xff\x14\x85",3);
sct = *(unsigned*)(p+3);
if (p) {
printf ("sys_call_table at 0x%x, call dispatch at 0x%x\n",
sct, p);
}
close(kmem);
}
One approach I saw in nurupo's rootkit was:
Another approach could be to get the syscall table through the IDTR register, like shown in this Phrack article. The cool thing about this way is that it doesn't require Linux Kernel Module support.