Дослідження можливостей нестандартного використання модулів ядра ОС Linux

Неекспортовані символи ядра. Оптимальний підхід до реалізації пошуку символів у ядрі. Виконання, підміна, додавання та приховання системних викликів. Завантаження модуля ядра із програмного коду та з коду іншого модуля. Робота з UNIX-сигналами.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык украинский
Дата добавления 23.05.2013
Размер файла 84,0 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

$ dmesg | tail -n 18 | grep !

! знайдено 26 входів: 017, 031, 032, 035, 044, 053, 056, 058, 098, 112, 127, 130, 137,

167, 188, 189, 222, 223, 251, 273, 274, 275, 276, 285, 294, 317,

Вивчивши отриману інформацію, можна зробити висновок:

· майже 10% таблиці системних викликів не використаються;

· стабільність цього списку дуже висока (тому що для статичного аналізу була обрана версія 3.0. 9, а длядинамического розбору версія 2.6.32, що відстоять друг від друга більш, ніж на 2 роки випуску).

Тепер можна зробити модуль, що реалізує новий системний виклик, і тестову програму для користувальницького простору (процес), що використає цей виклик. Номер нового виклику визначений у загальному заголовному файлі, використовуваному обома програмами.

Лістинг 2. Файл syscall.h із загальними визначеннями.

// номер нового системного виклику

#define __NR_own 223

// може бути взятий будь-який, отриманий при завантаженні модуля ni-test.ko

// для ядра 2.6.32 був отриманий список вільних номерів:

// 017, 031, 032, 035, 044, 053, 056, 058, 098, 112, 127, 130, 137,

// 167, 188, 189, 222, 223, 251, 273, 274, 275, 276, 285, 294, 317,

У лістингу 3 наведена тестова програма, що використає новий системний виклик.

Лістинг 3. Файл syscall.с із тестовою програмою.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "syscall.h"

static int sys_own_call( char *str, int len ) {

long __res;

__asm__ volatile (

"int $0x80":

"=a" (__res):"a"(__NR_own),"b"((long)(str)),"c"((long)(len)) );

return -((int)__res);

}

static void do_own_call( char *str ) {

int n = sys_own_call( str, strlen( str ) );

printf( "syscall return %d : %s\n", n, strerror( n ) );

}

int main( int argc, char *argv[] ) {

char str[] = "DEFAULT STRING";

if( 1 == argc ) do_own_call( str );

else

while( -іargc > 0) do_own_call( argv[ argc ] );

return EXIT_SUCCESS;

};

Дана програма може виконати один або серію (якщо передати кілька параметрів у командному рядку) системних викликів. При цьому вона передає у виклик символьний параметр, щоб у реалізації системного виклику можна було показати, як цей рядок копіюється із простору користувача в простір ядра (або міг би повертатися назад). Але основний інтерес представляє код повернення: успіх або невдача виконання системного виклику.

У лістингу 4 наведений файл add-sys.c, що містить модуль, відповідальний за реалізацію цього системного виклику в просторі ядра.

Лістинг 4. Модуль, що додає новий системний виклик.

#include <linux/module.h>

#include <linux/kallsyms.h>

#include <linux/uaccess.h>

#include <linux/unistd.h>

#include "../find.c"

#include "../CR0.c"

#include "syscall.h"

// системний виклик із двома параметрами:

asmlinkage long (*old_sys_addr) ( const char __user *buf, size_t count );

asmlinkage long new_sys_call ( const char __user *buf, size_t count ) {

static char buf_msg[ 80 ];

int res = copy_from_user( buf_msg, (void*)buf, count );

buf_msg[ count ] = '\0';

printk( "! передано %d байт: %s\n", count, buf_msg );

return res;

};

EXPORT_SYMBOL( new_sys_call );

static void **taddr; // адреса таблиці sys_call_table

static int __init new_sys_init( void ) {

void *waddr;

if( ( taddr = find_sym( "sys_call_table" ) ) != NULL )

printk( "! адреса sys_call_table = %p\n", taddr );

else {

printk( "! sys_call_table не знайдений\n" );

return -EINVAL;

}

old_sys_addr = (void*)taddr[ __NR_own ];

printk( "! адреса в позиції %d[__NR_own] = %p\n", __NR_own, old_sys_addr );

if( ( waddr = find_sym( "sys_ni_syscall" ) ) != NULL )

printk( "! адреса sys_ni_syscall = %p\n", waddr );

else {

printk( "! sys_ni_syscall не знайдений\n" );

return -EINVAL;

}

if( old_sys_addr != waddr ) {

printk( "! незрозуміло! : адреси не збігаються\n" );

return -EINVAL;

}

printk( "! адреса нового sys_call = %p\n", &new_sys_call );

rw_enable();

taddr[ __NR_own ] = new_sys_call;

rw_disable();

return 0;

}

static void __exit new_sys_exit( void ) {

printk( "! адреса sys_call при вивантаженні = %p\n", (void*)taddr[ __NR_own ] );

rw_enable();

taddr[ __NR_own ] = old_sys_addr;

rw_disable();

return;

}

module_init( new_sys_init );

module_exit( new_sys_exit );

MODULE_LICENSE( "GPL" );

MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

У лістингу 4 також виконується подвійна перевірка відповідності адреси в заданій (__NR_own) позиції таблицыsys_call_table адресі sys_ni_syscall.

Тепер можна перевірити спільну роботу модуля й тестової програми.

$ ./syscall

syscall return 38 : Function not implemented

Очевидно, що модуль, що реалізує виклик, необхідний нашій програмі, ще не завантажений. Завантажимо його й повторно запустимо тестову програму.

$ sudo insmod add-sys.ko

$ lsmod | grep 'sys'

add_sys 1432 0

$ dmesg | tail -n 30 | grep !

! адреса sys_call_table = c07ab3d8

! адреса в позиції 223[__NR_own] = c045b9a8

! адреса sys_ni_syscall = c045b9a8

! адреса нового sys_call = fdd7c024

$ ./syscall нові аргументи

syscall return 0 : Success

syscall return 0 : Success

Як видно, програма зробила 2 системних виклики.

$ dmesg | tail -n 30 | grep !

! адреса sys_call_table = c07ab3d8

! адреса в позиції 223[__NR_own] = c045b9a8

! адреса sys_ni_syscall = c045b9a8

! адреса нового sys_call = fdd7c024

! передано 18 байт: аргументи

! передано 10 байт: нові

Вивантажимо реалізуючий модуль і знову запустимо програму:

$ sudo rmmod add-sys

$ dmesg | tail -n 50 | grep !

! адреса sys_call_table = c07ab3d8

! адреса в позиції 223[__NR_own] = c045b9a8

! адреса sys_ni_syscall = c045b9a8

! адреса нового sys_call = fdd7c024

! передано 18 байт: аргументи

! передано 10 байт: нові

! адреса sys_call при вивантаженні = fdd7c024

$ ./syscall 1 2 3

syscall return 38 : Function not implemented

syscall return 38 : Function not implemented

syscall return 38 : Function not implemented

Очевидно, що ядро більше не в змозі надати програмі необхідний системний виклик. Аналогічним способом можна додати в систему новий оброблювач для власного системного виклику.

3.4 Приховання системного виклику

Питання полягає в тім, чи може модуль установити оброблювач (підмінивши існуючий або додавши новий) таким чином, щоб ядро системи «не знало» про це (тобто щоб такий модуль не можна було виявити засобами диагностикиlsmod або у файловій системі /proc). Цінність цього питання в тім, що відповідь на нього визначає, чи можуть шкідливі програми зробити подібні зміни. І відповідь на нього -- позитивний! Щоб потай установити оброблювач, модуль повинен:

· динамічно виділити блок пам'яті для коду функції оброблювача нового системного виклику (і, можливо, окремий блок для власних даних);

· перемістити код функції оброблювача в цю динамічну область, можливо, установивши ознаку выполнимости цієї нової сторінки пам'яті;

· установити адреса оброблювача в таблиці sys_call_table на цей переміщений код;

· завершити функцію ініціалізації модуля з кодом, відмінним від нуля, тим самим модуль фактично не встановлюється й не фіксується ядром.

Ідея полягає в тому, що блоки пам'яті, динамічно виділювані запитами до kmalloc() і іншого подібним API ядра, продовжують існувати, навіть якщо їхній модуль, що створив, припинив існування (якщо при цьому він «забуде» виконати парний виклик kfree()). У цьому випадку в пам'яті виникають об'єкти, всі шляхи доступу до яких втрачені, і які не можуть бути ніяким образом знищені. Подібні дії (свідомо або помилково) приведуть до поступової деградації операційної системи.

Лістинг 1. Загальні визначення.

#include <linux/module.h>

#include <linux/kallsyms.h>

#include <linux/uaccess.h>

#include <linux/unistd.h>

#include <../arch/x86/include/asm/cacheflush.h>

#include "../find.c"

#include "../CR0.c"

#include "syscall.h"

// опису функцій оброблювачів для різних варіантів:

// asmlinkage long new_sys_call( const char __user *buf, size_t count )

#define VARNUM 5

#ifndef VARIANT

#define VARIANT 0

#endif

#if VARIANT>VARNUM

#undef VARIANT

#define VARIANT 0

#endif

static long shift; // величина зрушення тіла функції системного оброблювача

#if VARIANT == 0

#include "null.c"

#elif VARIANT == 1

#include "local.c"

#elif VARIANT == 2

#include "getpid.c"

#elif VARIANT == 3

#include "strlen_1.c"

#elif VARIANT == 4

#include "strlen_2.c"

#elif VARIANT == 5

#include "write.c"

#else

#include "null.c"

#endif

static int __init new_sys_init( void ) {

void *waddr, *move_sys_call,

**taddr; // адреса таблиці sys_call_table

size_t sys_size;

printk( "!... SYSCALL=%d, VARIANT=%d\n", NR, VARIANT );

if( ( taddr = find_sym( "sys_call_table" ) ) != NULL )

printk( "! адреса sys_call_table = %p\n", taddr );

else

return -EINVAL | printk( "! sys_call_table не знайдений\n" );

if( NULL == ( waddr = find_sym( "sys_ni_syscall" ) ) )

return -EINVAL | printk( "! sys_ni_syscall не знайдений\n" );

if( taddr[ NR ] != waddr )

return -EINVAL | printk( "! системний виклик %d зайнятий\n", NR );

{ unsigned long end;

asm( "movl $L2, %%eax \n"

:"=a"(end)::

);

sys_size = end - (long)new_sys_call;

printk( "! статична функція: початок= %p, кінець=%lx, довжина=%d \n",

new_sys_call, end, sys_size );

}

// виділяємо блок пам'яті під функцію оброблювач

move_sys_call = kmalloc( sys_size, GFP_KERNEL );

if( !move_sys_call ) return -EINVAL | printk( "! memory allocation error!\n" );

printk( "! виділено блок %d байт із адреси %p\n", sys_size, move_sys_call );

// копіюємо резидентний код нового оброблювача у виділений блок пам'яті

memcpy( move_sys_call, new_sys_call, sys_size );

shift = move_sys_call - (void*)new_sys_call;

printk( "! зрушення коду = %lx байт\n", shift );

// зняти біт NX-захисту зі сторінки

//int set_memory_x(unsigned long addr, int numpages);

set_memory_x( (long unsigned)move_sys_call, sys_size / PAGE_SIZE + 1 );

printk( "! адреса нового sys_call = %p\n", move_sys_call );

rw_enable();

taddr[ NR ] = move_sys_call;

rw_disable();

return -EPERM;

}

module_init( new_sys_init );

MODULE_LICENSE( "GPL" );

MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Для розгляду різних варіантів зборки код містить у собі файли null.c, local.c й інші, утримуючі різні реалізації функції оброблювача нового системного виклику з єдиним ім'ям і прототипом:

asmlinkage long new_sys_call( const char __user *buf, size_t count );

Прототип цього нового системного виклику був обраний довільно. Найпростіший оброблювач null.c показує тільки можливість здійснення даної операції, тому встановлюваний їм оброблювач системного виклику з номером NR (визначається параметром зборки SYSCALL і за замовчуванням дорівнює 223) нічого не робить, а тільки повертає ознака нормального завершення, як показано в лістингу 2.

Лістинг 2. Найпростіший варіант приховання системного виклику.

asmlinkage long new_sys_call( const char __user *buf, size_t count ) {

asm( "movl $0, %%eax\n" // еквівалент return 0;

"popl %%ebp \n"

"ret \n"

"L2: nop \n" // нам потрібна влучна L2 після return

::: "%eax"

);

return 0; // тільки для синтаксису

};

Оскільки код такого модуля псує таблицю sys_call_table і не може неї відновити, а при багаторазовому виконанні буде залишати після себе блоки назавжди загубленої пам'яті, то буде потрібно ще один модуль для відновлення первинного стану таблиці.

Лістинг 3. Модуль для відновлення таблиці системних викликів.

#include <linux/module.h>

#include <linux/kallsyms.h>

#include <linux/uaccess.h>

#include <linux/unistd.h>

#include "../find.c"

#include "../CR0.c"

#include "syscall.h"

static int __init new_sys_init( void ) {

void **taddr; // адреса таблиці sys_call_table

void *waddr, *old_sys_addr;

if( ( taddr = find_sym( "sys_call_table" ) ) != NULL )

printk( "! адреса sys_call_table = %p\n", taddr );

else return -EINVAL | printk( "! sys_call_table не знайдений\n" );

if( ( waddr = find_sym( "sys_ni_syscall" ) ) != NULL )

printk( "! адреса sys_ni_syscall = %p\n", waddr );

else return -EINVAL | printk( "! sys_ni_syscall не знайдений\n" );

old_sys_addr = (void*)taddr[ NR ];

printk( "! адреса в позиції %d = %p\n", NR, old_sys_addr );

if( old_sys_addr != waddr ) {

kfree( old_sys_addr );

rw_enable();

taddr[ NR ] = waddr; // відновити sys_ni_syscall

rw_disable();

printk( "! підсумкова адреса оброблювача %p\n", taddr[ NR ] );

}

else

printk( "! підсумкова адреса оброблювача не змінюється\n" );

return -EPERM;

}

module_init( new_sys_init );

MODULE_LICENSE( "GPL" );

MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

Тепер ми можемо перевірити, як це працює.

$ make VARIANT=0

...

$ ./syscall

syscall return -38 [ffffffda], reason: Function not implemented

$ sudo insmod hidden.ko

insmod: error inserting 'hidden.ko': -1 Operation not permitted

$ lsmod | grep hid

$

$ ./syscall 1 23 456

syscall return 0 [00000000], reason: Success

syscall return 0 [00000000], reason: Success

syscall return 0 [00000000], reason: Success

$ dmesg | tail -n60 | grep !

!... SYSCALL=223, VARIANT=0

! адреса sys_call_table = c07ab3d8

! статична функція: початок= f7ecd000, кінець=f7ecd00f, довжина=15

! виділений блок 15 байт із адреси d9322030

! зрушення коду = e1455030 байт

! адреса нового sys_call = d9322030

$ sudo insmod restore.ko

insmod: error inserting 'restore.ko': -1 Operation not permitted

$ ./syscall

syscall return -38 [ffffffda], reason: Function not implemented

Як можна бачити, хоча модуль hidden.ko не встановлений у системі, але новий системний виклик з номером NRотрабатывается. Єдиний новий фрагмент у коді hidden.c, що вимагає додаткових коментарів, це рядок, у якій виробляється виклик:

int set_memory_x( unsigned long addr, int numpages );

У старших моделях x86, що працюють в 64-бітному або розширеному режимі PAE, уведений NX-битий захистів сторінки пам'яті від виконання (старший біт у записі таблиці сторінок). У ядрі Linux цей апаратний механізм захисту застосовується, починаючи з версії 2.6.30. Виклик set_memory_x() знімає обмеження виконання для numpages послідовних сторінок (розміром PAGESIZE кожна), починаючи зі сторінки адреси addr. Аналогічно виклик відновлює захист сторінки від виконання. Для 32-бітного ядра на платформі x86 (не PAE!) цей механізм, як уже було сказано, не використається.

Ассемблерная вставка у функції оброблювача представляє повний еквівалент оператора return 0; (відповідно до угод мови З - з виштовхуванням регістра %ebp зі стека). Цей еквівалент знадобився тільки тому, що нам необхідна влучна L2 (її адреса) після оператора return, а компілятор gcc такі мітки після завершення коду «оптимизирует».

Писать код для таких оброблювачів досить складно, фактично при цьому має бути вручну, без допомоги компілятора gcc (опція -fPIC), реалізувати щось подібне до PIC-кодування (Position Independed Code - позиційно незалежне кодування). Складності при розробці переміщуваної функції оброблювача пов'язані з тим, що:

1. функція не може використати ніякі зовнішні змінні, описані поза її тілом, тому що після завершення функції ініціалізації модуля всі такі області пам'яті будуть звільнені;

2. функція може використати тільки локальні змінні в стеці;

3. теж саме ставиться й до інших (локальним) функціям, описаним у модулі.

Для подолання цих обмежень використається таке розширення gcc, як вкладені описи функцій, які будуть переміщені разом з тілом объемлющей їхньої функції оброблювача.

1. Наступна складність полягає в тому, що в такому коді не можна, як звичайно, по імені викликати функції API ядра, неважливо, експортовані вони чи ні. Адреса такого виклику розв'яжеться в момент статичної линковки (зсув адреси запишеться в поле команди), а при виклику приведе до виклику зі зсувом і краху виконання. Адреса потрібної функції можна взяти статично з /proc/kallsym або знайти й динамічно, як було показано в попередніх статтях. У листинге 5 наведений приклад модуля, у якому показане, як обійти обоє цих обмеження.

Лістинг 5. Приклад оброблювача системного виклику із вкладеною функцією й звертанням до API ядра.

asmlinkage long new_sys_call( const char __user *buf, size_t count ) {

long res = 0;

size_t own_strlen( const char* ps ) { // вкладена функція

long res = 0;

asm(

"movl 8(%%ebp), %%eax \n" // ps -> call parameter

"movl %%eax, (%%esp) \n"

"call *%%ebx \n"

:"=a"(res):"b"((long)(&strlen)): // strlen() з API ядра

);

return res;

}

res = own_strlen( buf );

asm( "leave \n"

"ret \n" // еквівалент return res;

"L2: nop \n" // нам потрібна влучна L2 після return

::"a"(res):

);

return 0; // тільки для синтаксису

};

У цьому ж прикладі демонструється, як функція використає рядкову змінну із простору користувача, адреса якої був переданий їй у системному виклику:

$ make VARIANT=4

...

$ sudo insmod hidden.ko

insmod: error inserting 'hidden.ko': -1 Operation not permitted

$ lsmod | grep hid

$

$ ./syscall 1 23 456 'новий рядок'

syscall return 24 [00000018], reason: Success

syscall return 4 [00000004], reason: Success

syscall return 3 [00000003], reason: Success

syscall return 2 [00000002], reason: Success

$ dmesg | tail -n60 | grep !

!... SYSCALL=223, VARIANT=4

! адреса sys_call_table = c07ab3d8

! статична функція: початок= f7ede000, кінець=f7ede018, довжина=24

! виділений блок 24 байт із адреси d9085940

! зрушення коду = e11a7940 байт

! адреса нового sys_call = d9085940

У результаті запуску цього приклада можна побачити чергову проблему, пов'язану з подібними функціями, де ми змогли успішно скористатися скалярними (int, long) локальними змінними, оголошеними усередині функції. Але це не ставиться до масивів, зокрема, до рядків, розміщеним як локальні змінні в тілі функції.

4. ЗАВАНТАЖЕННЯ МОДУЛІВ ЯДРА

4.1 Завантаження модуля ядра із програмного коду

Установка модулів на етапі розробки відбувається за допомогою команди insmod, а при переході до експлуатації модуля для його установки в систему використається команда modprobe. Симетрично, видалення встановлених модулів виконується командою rmmod. Але у всіх цих випадках операції над модулями були статичними. Питання, розглянутий у даній статті, полягає в тім, чи можна модулі встановлювати (і вивантажувати) динамічно (тобто на вимогу) із власного програмного коду? Подібна функціональність може виявитися затребуваної в різних ситуаціях, деякі з яких перераховані нижче:

· програми утиліти insmod, modprobe й rmmod - це програми з користувальницького простору, тому цікаво довідатися, як вони справляються із цим завданням;

· у деяких випадках від системи потрібна функціональність, відсутня в ній і забезпечувана подгружаемым модулем ядра; у подібній ситуації було б зручно довантажити такий модуль безпосередньо перед його використанням.

Класичний приклад такої ситуації - команда для монтування файлової підсистеми (типу qnx4, minix й ін.), що за замовчуванням не завантажується системою. У цьому випадку модуль (minix.ko) довантажується по запиті із користувацької утиліти mount, як показано нижче:

$ lsmod | grep minix

$ sudo mount -t minix /dev/sda5 /mnt/sda5

$ ls /mnt/sda5

bin boot dev etc home mnt proc root sbin tmp usr var

$ lsmod | grep minix

minix 19212 1

Розроблювачі певного класу встаткування могли б використати родовий (generic) модуль драйвера, що при необхідності довантажував би спеціалізований модуль драйвера для конкретної моделі встаткування даного класу. Класичними прикладами подібного підходу є цілі сімейства драйверів (розмежовані по типамплат) в інтерфейсах до плат класу E1/T1 в IP-телефонії, наприклад, інтерфейс DAHDI компанії Digium або інтерфейс компанії Sangoma. У цьому випадку типові модулі динамічно довантажуються із середовища родового модуля ядра.

Якщо розвивати даний підхід далі, те можна створити техніку побудови плагинов: можливість розширення функціональності системи за допомогою додавання нових специфічних частин, не зачіпаючи існуючих частин системи. В області простору користувача така можливість реалізується за рахунок використання поділюваних бібліотек. В області модулів ядра подібного результату можна домогтися шляхом динамічного завантаження модулів.

Завантаження модуля із процесу користувача

Для завантаження модуля із простору користувальницького процесу (як це робить insmod, modprobeи rmmod) існує системний виклик sys_init_module():

asmlinkage long sys_init_module( void __user *umod,

unsigned long len, const char __user *uargs );

А для вивантаження модуля використається системний виклик sys_delete_module():

asmlinkage long sys_delete_module( const char __user *name, unsigned int flags );

На жаль, процес динамічного завантаження й вивантаження модулів досить слабко документований, тому що сторінки довідника man й існуючі Інтернет-ресурси містять застарілі відомості, що ставляться до реалізацій на границі версій ядра 2.4 й 2.6. Тому для збору інформації нам оведеться виконати ряд практичних експериментів, щоб зібрати воєдино фрагменти інформації, розкидані по вихідним тексту ядра Linux.

Для всіх прикладів, вихідний код яких можна знайти в архіві umaster.tgz у розділі "Матеріали для скачивания", буде використатися той самий тестовий модуль, що буде динамічно завантажуватися в різних оточеннях.

Лістинг 1. Подгружаемый модуль (файл slave.ko).

#include "../common.c"

static char* parm1 = "";

module_param( parm1, charp, 0 );

static char* parm2 = "";

module_param( parm2, charp, 0 );

static char this_mod_file[ 40 ];

static int __init mod_init( void ) {

set_mod_name( this_mod_file, __FILE__ );

printk( "+ module %s loaded: parm1=%s, parm2=%s\n", this_mod_file, parm1, parm2 );

return 0;

}

static void __exit mod_exit( void ) {

printk( "+ module %s unloaded\n", this_mod_file );

}

У даному модулі, як і у всіх наступних прикладах модулів, використається загальний включати файл, що, common.c (у батьківському каталозі проекту):

Лістинг 2. Загальні визначення (файл common.c).

#include <linux/module.h>

MODULE_LICENSE( "GPL" );

MODULE_AUTHOR( "Oleg Tsiliuric <olej@front.ru>" );

static int __init mod_init( void );

static void __exit mod_exit( void );

module_init( mod_init );

module_exit( mod_exit );

inline void __init set_mod_name( char *res, char *path ) {

char *pb = strrchr( path, '/' ) + 1,

*pe = strrchr( path, '.' );

strncpy( res, pb, pe - pb );

sprintf( res + ( pe - pb ), "%s", ".ko" );

};

Перевіримо роботу нашого модуля в автономному режимі шляхом стандартного завантаження за допомогою команди insmod.

$ sudo insmod slave.ko

$ dmesg | tail -n30 | grep +

+ module slave.ko loaded: parm1=, parm2=

$ sudo rmmod slave

$ dmesg | tail -n30 | grep +

+ module slave.ko unloaded

При необхідності модуль може прийняти два символьних параметри з командного рядка.

$ sudo ./inst1 slave.ko parm1='рядок1' parm2='рядок2'

завантаження модуля: slave.ko parm1=рядок1 parm2=рядок2

розмір файлу модуля slave.ko = 94800 байт

модуль slave.ko успішно завантажений!

$ dmesg | tail -n30 | grep +

+ module slave.ko loaded: parm1=, parm2=

$ lsmod | grep slave

slave 1009 0

Примітка: якщо передати в модуль некоректні параметри або параметри в некоректному форматі, то завантаження модуля виконане не буде, а буде виведене повідомлення про помилку.

Для завантаження пробного модуля буде використатися спеціальне додаток-завантажник.

Лістинг 3. Додаток для завантаження модулів (файл inst1.c).

#include <sys/stat.h>

#include <errno.h>

#include "common.h"

// asmlinkage long sys_init_module // системний виклик sys_init_module()

// ( void __user *umod, unsigned long len, const char __user *uargs );

int main( int argc, char *argv[] ) {

char parms[ 80 ] = "", file[ 80 ] = SLAVE_FILE;

void *buff = NULL;

int fd, res;

off_t fsize; /* загальний розмір у байтах */

if( argc > 1 ) {

strcpy( file, argv[ 1 ] );

if( argc > 2 ) {

int i;

for( i = 2; i < argc; i++ ) {

strcat( parms, argv[ i ] );

strcat( parms, " " );

}

}

}

printf( "завантаження модуля: %s %s\n", file, parms );

fd = open( file, O_RDONLY );

if( fd < 0 ) {

printf( "помилка open: %m\n" );

return errno;

}

{ struct stat fst;

if( fstat( fd, &fst ) < 0 ) {

printf( "помилка stat: %m\n" );

close( fd );

return errno;

}

if( !S_ISREG( fst.st_mode ) ) {

printf( "помилка: %s не файл\n", file );

close( fd );

return EXIT_FAILURE;

}

fsize = fst.st_size;

}

printf( "розмір файлу модуля %s = %ld байт\n", file, fsize );

buff = malloc( fsize );

if( NULL == buff ) {

printf( "помилка malloc: %m\n" );

close( fd );

return errno;

}

if( fsize != read( fd, buff, fsize ) ) {

printf( "помилка read: %m\n" );

free( buff );

close( fd );

return errno;

}

close( fd );

res = syscall( __NR_init_module, buff, fsize, parms );

free( buff );

if( res < 0) printf( "помилка завантаження: %m\n" );

else printf( "модуль %s успішно завантажений!\n", file );

return res;

};

Цей приклад (як і всі додатки цієї частини викладу) включає файл ./common.h (усередині каталогу umaster), у якому визначається ім'я модуля, що завантажує - ./slave.ko, щоб не вводити його щораз при запуску додатка з командного рядка:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/syscall.h>

#include <fcntl.h>

#define SLAVE_FILE "./slave.ko";

Хоча тестовий додаток, наведений у листинге 3, виглядає досить складним, але логіка його роботи вкрай проста:

1. виконується пошук файлу з відкомпільованим модулем ядра із зазначеним ім'ям (змінна file);

2. визначається повний розмір цього файлу (змінна fsize);

3. динамічно виділяється буфер читання buff зазначеного розміру;

4. у цей буфер читається образ модуля, що завантажує, (у форматі об'єктного файлу);

5. системному виклику sys_init_module( void* umod, unsigned long len, const char* uargs ) передаються три параметри:

· 1-й параметр - адреса образа модуля в пам'яті (buff);

· 2-ий параметр -- розмір образа модуля (fsize);

· в 3-ем параметрі може передаватися рядок параметрів завантаження модуля, але якщо параметри завантаження не використаються, то передається порожній рядок, але в жодному разі не NULL.

Подготовим симетричний додаток для динамічного вивантаження модулів, по своїй функціональності аналогічне утиліті rmmod.

Лістинг 4. Додаток для динамічного вивантаження модулів (файл rem1.c).

#include "common.h"

// системний виклик sys_delete_module()

// asmlinkage long sys_delete_module ( const char __user *name, unsigned int flags );

// flags: O_TRUNC, O_NONBLOCK

int main( int argc, char *argv[] ) {

char file[ 80 ] = SLAVE_FILE;

int res;

if( argc > 1) strcpy( file, argv[ 1 ] );

char *slave_mod = strrchr( file, '/' ) != NULL ?

strrchr( file, '/' ) + 1 :

file;

if( strrchr( file, '.' ) != NULL )

*strrchr( file, '.' ) = '\0';

printf( "вивантажується модуль %s\n", slave_mod );

res = syscall( __NR_delete_module, slave_mod, O_TRUNC );

if( res < 0) printf( "помилка вивантаження: %m\n" );

else printf( "модуль %s успішно завантажений!\n", slave_mod );

return res;

};

Перевіримо спільну роботу всіх компонентів шляхом динамічного завантаження й наступного вивантаження модуля:

$ sudo ./inst1 slave.ko parm1='рядок1' parm2='рядок2'

завантаження модуля: slave.ko parm1=рядок1 parm2=рядок2

розмір файлу модуля slave.ko = 94800 байт

модуль slave.ko успішно завантажений!

$ dmesg | tail -n30 | grep +

+ module slave.ko loaded: parm1=рядок1, parm2= рядок2

$ lsmod | grep slave

slave 1009 0

$ sudo ./rem1

вивантажується модуль slave

$ dmesg | tail -n30 | grep +

+ module slave.ko unloaded

$ lsmod | head -n3

Module Size Used by

fuse 48375 2

ip6table_filter 2227 0

Якщо вам здається не зовсім коректним використання непрямих системних викликів syscall(), то їх можна замінити викликами стандартної системної бібліотеки для цих syscall(), змінивши в кожному листинге всього по одному рядку:

· у файлі inst2.c:

...

res = init_module( buff, fsize, parms ); // виклик sys_init_module()

...

· у файлі rem2.c:

...

res = delete_module( slave_mod, O_TRUNC ); // flags: O_TRUNC, O_NONBLOCK

...

Чому я відразу не скористався викликами init_module() і delete_module(), а замість них використав непрямі системні виклики syscall()? Мені довелося так надійти, тому що ні в літературі, ні в довідкових руководствах Linux, ні в Інтернет мені не вдалося знайти прикладів коректного використання init_module() і delete_module()або їхніх точних прототипів. Навпроти, всі доступні джерела містять синтаксично некоректні й застарілі приклади. Тому мені довелося відновити алгоритми використання цих викликів шляхом реінжинірингу через виклики syscall().

4.2 Завантаження модуля ядра з коду іншого модуля

Ми уже динамічно завантажували й вивантажували модуль slave.ko з коду додатка, що працює в просторі користувача. Тепер нам має бути зробити теж саме, але вже з коду зухвалого модуля (master.ko). У лістингу 1 наведений вихідний код подібного модуля.

Лістинг 1. Модуль для завантаження іншого модуля (файл master.c)

#include <linux/fs.hlinux_kernel_46

#include <linux/vmalloc.h>

#include "../common.c"

#include "../find.c"

static char* file = "./slave.ko";

module_param( file, charp, 0 );

static char this_mod_file[ 40 ], // ім'я файлу master-модуля

slave_name[ 80 ]; // ім'я файлу slave-модуля

static int __init mod_init( void ) {

void *waddr;

long res = 0;

long len;

struct file *f;

void *buff;

size_t n;

asmlinkage long (*sys_init_module) // системний виклик sys_init_module()

( void __user *umod, unsigned long len, const char __user *uargs );

set_mod_name( this_mod_file, __FILE__ );

if( ( waddr = find_sym( "sys_init_module" ) ) == NULL ) {

printk( "! sys_init_module не знайдений\n" );

res = -EINVAL;

goto end;

}

printk( "+ адреса sys_init_module = %p\n", waddr );

sys_init_module = waddr;

strcpy( slave_name, file );

f = filp_open( slave_name, O_RDONLY, 0 );

if( IS_ERR( f ) ) {

printk( "+ помилка відкриття файлу %s\n", slave_name );

res = -ENOENT;

goto end;

}

len = vfs_llseek( f, 0L, 2 ); // 2 - means SEEK_END

if( len <= 0 ) {

printk( "+ помилка lseek\n" );

res = -EINVAL;

goto close;

}

printk( "+ довжина файлу модуля = %d байт\n", (int)len );

if( NULL == ( buff = vmalloc( len ) ) ) {

res = -ENOMEM;

goto close;

};

printk( "+ адреса буфера читання = %p\n", buff );

vfs_llseek( f, 0L, 0 ); // 0 - means SEEK_SET

n = kernel_read( f, 0, buff, len );

printk( "+ лічено з файлу %s %d байт\n", slave_name, n );

if( n != len ) {

printk( "+ помилка читання\n" );

res = -EIO;

goto free;

}

{ mm_segment_t fs = get_fs();

set_fs( get_ds() );

res = sys_init_module( buff, len, "" );

set_fs( fs );

if( res < 0) goto insmod;

}

printk( "+ модуль %s завантажений: file=%s\n", this_mod_file, file );

insmod:

free:

vfree( buff );

close:

filp_close( f, NULL );

end:

return res;

}

static void __exit mod_exit( void ) {

asmlinkage long (*sys_delete_module) // системний виклик sys_delete_module()

( const char __user *name, unsigned int flags );

// flags: O_TRUNC, O_NONBLOCK

void *waddr;

char *slave_mod = strrchr( slave_name, '/' ) != NULL ?

strrchr( slave_name, '/' ) + 1 :

slave_name;

*strrchr( slave_mod, '.' ) = '\0';

printk( "+ вивантажується модуль %s\n", slave_mod );

if( ( waddr = find_sym( "sys_delete_module" ) ) == NULL ) {

printk( "! sys_delete_module не знайдений\n" );

return;

}

printk( "+ адреса sys_delete_module = %p\n", waddr );

sys_delete_module = waddr;

{ long res = 0;

mm_segment_t fs = get_fs();

set_fs( get_ds() );

res = sys_delete_module( slave_mod, 0 );

set_fs( fs );

if( res < 0)

printk( "+ помилка вивантаження модуля %s\n", slave_mod );

}

printk( "+ модуль %s вивантажений\n", this_mod_file );

}

Функціональність модуля master.ko у точності збігається з функціональністю розглянутого раніше користувальницького додатка, однак у даному прикладі присутні й моменти, специфічні для модулів ядра. Так, для цього використаються інші засоби й інструменти, крім того, всі операції на рівні ядра повинні виконуватися з особливою обережністю. Необхідно врахувати, що адреси системних оброблювачів sys_init_module() иsys_delete_module(), які формально не експортуються ядром, нам невідомі. Тому для виконання цих викликів нам необхідно:

· знайти адреси символів ядра sys_init_module й sys_delete_module за допомогою функції find_sym(), як це вже робилося раніше;

· привласнити ці адреси нашим власним змінним: (*sys_init_module)(...) і (*sys_delete_module)(...) (імена цих змінних можуть бути довільними, але те, що вони збіглися з назвами системних викликів, полегшує розуміння коду);

· виконати непрямий виклик потрібних функцій по покажчиках *sys_init_module й *sys_delete_module;

Перевіримо роботу створеного модуля:

$ sudo insmod master.ko

$ dmesg | tail -n30 | grep +

+ адреса sys_init_module = c0470f50

+ довжина файлу модуля = 94692 байт

+ адреса буфера читання = f9d51000

+ лічено з файлу ./slave.ko 94692 байт

+ модуль slave.ko завантажений: parm1=, parm2=

+ модуль master.ko завантажений: file=./slave.ko

Передостанній рядок системного журналу, виділена курсивом, виведена із зовсім іншого модуля (slave.ko), у відмінності від рядків, що обрамляють її зверху й знизу.

$ lsmod | head -n4

Module Size Used by

slave 1001 0

master 1785 0

fuse 48375 2

Модуль slave.ko був завантажений після модуля master.ko, і між ними немає ніяких залежностей (по експорті, зв'язуванню тощо), як це й повинне бути.

Вивантаження модуля master.ko автоматично веде й до вивантаження модуля slave.ko:

$ sudo rmmod master

$ dmesg | tail -n30 | grep +

+ вивантажується модуль slave

+ адреса sys_delete_module = c046f4e8

+ модуль slave.ko вивантажений

+ модуль master.ko вивантажений

$ lsmod | head -n4

Module Size Used by

fuse 48375 2

ip6table_filter 2227 0

ip6_tables 9409 1 ip6table_filter

Рядок системного журналу, виділена курсивом, знову ж виведена з коду викликуваного модуля, у відмінності від інших рядків, які ставляться до зухвалого модуля.

4.3 Підключаючі плагіни

Хоча ми розглянемо навмисно спрощений приклад роботи з динамічними модулями, наведений в архивеplugin.tgz у розділі "Матеріали для скачивания", але згодом ви зможете використати отримані знання й у рамках практичних завдань.

Додамо новий системний виклик __NR_str_trans, створивши підключає файл, що, syscall.h, уміст якого наведено нижче:

1. // номер нового системного виклику

2. #define __NR_str_trans 223

Користувальницька програма, розташована у файлі syscall.с, передає кореневому модулю (master.ko) рядок, що містить запис числового значення (у різних системах числення: 8, 10, 16), і очікує одержати назад обчислене числове значення.

Лістинг 1. Зухвалий процес.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "syscall.h"

static void do_own_call( char *str ) {

int n = syscall( __NR_str_trans, str, strlen( str ) );

if( n >= 0 )

printf( "syscall return %d\n", n );

else

printf( "syscall error %d : %s\n", n, strerror( -n ) );

}

int main( int argc, char *argv[] ) {

if( 1 == argc ) do_own_call( "9876" );

else {

int i;

for( i = 1; i < argc; i++ )

do_own_call( argv[ i ] );

}

return EXIT_SUCCESS;

};

Корнєвій модуль (master.ko) установлює в таблицю системних викликів новий системний оброблювач__NR_str_trans, але сам не займається безпосередньо перекладом отриманого рядка в числове значення залежно від її формату (восьмеричне, десяткове, або шестнадцатеричное значення), а вибирає й завантажує для обчислення значення відповідний модуль: oct.ko, dec.ko або hex.ko.

Всі модулі-плагіни експортують те саме ім'я крапки входу (str_translate), і тому не можуть бути завантажені одночасно. Корнєвій модуль, завантаживши модуль-плагин, повинен знайти за адресою експортоване плагином имяstr_translate() і передати йому рядок для обробки, а отриманий результат і буде підсумком роботи нового системного виклику. Після обробки отриманого запиту модуль-плагин повинен бути обов'язково вивантажений щоб уникнути конфлікту експортованих імен при наступних запитах.

Всі модулі-оброблювачі є однотипними, тому що виконують ту саму завдання, але для великого скорочення обсягу, у них використається ще загальний включати файл, що, slave.c, наведений нижче:

#include "../common.c"

static char this_mod_file[ 40 ];

long str_translate( const char *buf );

EXPORT_SYMBOL( str_translate );

Файл common.c розглядався в попередній статті, тому зараз обговорюватися не буде. У лістингу 2 наведений модуль для перетворення рядка з восьмеричним значенням у число, вихідний код інших модулів для перетворення десяткових і шестнадцатеричных значень.

Лістинг 2. Модуль для перетворення восьмеричного рядка.

#include "slave.c"

static const char dig[] = "01234567";

long str_translate( const char *buf ) {

long res = 0;

const char *p = buf;

printk( "+ %s : запит : %s\n", this_mod_file, buf );

while( *p != '\0' ) {

char *s = strchr( dig, *p );

if( s == NULL ) return -EINVAL;

res = res * 8 + ( s - dig );

p++;

}

return res;

};

static int __init mod_init( void ) {

set_mod_name( this_mod_file, __FILE__ );

printk( "+ модуль %s завантажений\n", this_mod_file );

return 0;

}

static void __exit mod_exit( void ) {

printk( "+ модуль %s вивантажений\n", this_mod_file );

}

Така однотипність допомагає відстежити суть що відбувається. До даного моменту ми одержали три ідентичних модулі:dec.ko, hex.ko, oct.ko.

$ ls -l *.ko

-rw-rw-r-і 1 olej olej 95223 Фев 12 15:13 dec.ko

-rw-rw-r-і 1 olej olej 95553 Фев 12 15:13 hex.ko

-rw-rw-r-і 1 olej olej 126348 Фев 12 15:13 master.ko

-rw-rw-r-і 1 olej olej 95223 Фев 12 15:13 oct.ko

Кожний із цих трьох модулів експортує те саме ім'я крапки входу str_translate(). Тому будь-які два із цих модулів не можуть бути завантажені одночасно, у чому легко переконатися:

$ sudo insmod hex.ko

$ dmesg | tail -n30 | grep +

+ модуль hex.ko завантажений

$ cat /proc/kallsyms | grep T | grep translate

c0579604 T isofs_name_translate

c063f888 T set_translate

c063f8a4 T inverse_translate

f8ab2000 T str_translate [hex]

$ sudo insmod oct.ko

insmod: error inserting 'oct.ko': -1 Invalid module format

$ dmesg | tail -n30 | grep oct

oct: exports duplicate symbol str_translate (owned by hex)

Це досить цікавий експеримент, що стосується поводження символів, експортованих ядром. Але неможливість одночасного завантаження таких модулів не означає, що ми взагалі не зможемо використати їх. Для цього варто підготувати «зовнішню» програмну оболонку, що буде на вимогу завантажувати необхідний модуль.

Такою оболонкою і є кореневий модуль, що завантажує модули-плагины й замикає всю конфігурацію програмних компонентів, вихідний код якого наведений у лістингу 3.

Лістинг 3. Зухвалий модуль (файл master.c)

#include <linux/fs.h>

#include <linux/vmalloc.h>

#include "syscall.h"

#include "../common.c"

#include "../find.c"

#include "CR0.c"

static char this_mod_file[ 40 ]; // ім'я файлу master-модуля

static void **taddr, // адреса таблиці sys_call_table

*old_sys_addr; // адреса старого оброблювача (sys_ni_syscall)

asmlinkage long (*sys_init_module) // системний виклик sys_init_module()

( void __user *umod, unsigned long len, const char __user *uargs );

asmlinkage long (*sys_delete_module) // системний виклик sys_delete_module()

( const char __user *name, unsigned int flags );

static long load_slave( const char* fname ) {

long res = 0;

struct file *f;

long len;

void *buff;

size_t n;

f = filp_open( fname, O_RDONLY, 0 );

if( IS_ERR( f ) ) {

printk( "+ помилка відкриття файлу %s\n", fname );

return -ENOENT;

}

len = vfs_llseek( f, 0L, 2 ); // 2 - means SEEK_END

if( len <= 0 ) {

printk( "+ помилка lseek\n" );

return -EINVAL;

}

printk( "+ довжина файлу модуля = %d байт\n", (int)len );

if( NULL == ( buff = vmalloc( len ) ) ) {

filp_close( f, NULL );

return -ENOMEM;

};

printk( "+ адреса буфера читання = %p\n", buff );

vfs_llseek( f, 0L, 0 ); // 0 - means SEEK_SET

n = kernel_read( f, 0, buff, len );

printk( "+ лічено з файлу %s %d байт\n", fname, n );

if( n != len ) {

printk( "+ помилка читання\n" );

vfree( buff );

filp_close( f, NULL );

return -EIO;

}

filp_close( f, NULL );

{ mm_segment_t fs = get_fs();

set_fs( get_ds() );

res = sys_init_module( buff, len, "" );

set_fs( fs );

}

vfree( buff );

if( res < 0)

printk( "+ помилка завантаження модуля %s : %ld\n", fname, res );

return res;

}

static long unload_slave( const char* fname ) {

long res = 0;

mm_segment_t fs = get_fs();

set_fs( get_ds() );

if( strrchr( fname, '.' ) != NULL )

*strrchr( fname, '.' ) = '\0';

res = sys_delete_module( fname, 0 );

set_fs( fs );

if( res < 0)

printk( "+ помилка вивантаження модуля %s\n", fname );

return res;

}

// новий системний виклик

asmlinkage long sys_str_translate( const char __user *buf, size_t count ) {

static const char* slave_name[] = // імена файлів slave-модулів

{ "dec.ko", "oct.ko", "hex.ko" };

static char buf_msg[ 80 ], mod_file[ 40 ], *par1;

int res = copy_from_user( buf_msg, (void*)buf, count ), ind, trs;

buf_msg[ count ] = '\0';

long (*loaded_str_translate)( const char *buf );

printk( "+ системний запит %d байт: %s\n", count, buf_msg );

if( buf_msg[ 0 ] == '0' ) {

if( buf_msg[ 1 ] == 'x' ) ind = 2; // шестнадцатеричный випадок

else ind = 1; // восьмеричний випадок

}

else if( strchr( "123456789", buf_msg[ 0 ] ) != 0 )

ind = 0; //десятковий випадок

else return -EINVAL;

strcpy( mod_file, slave_name[ ind ] );

par1 = buf_msg + ind;

if( ( res = load_slave( mod_file ) ) < 0) return res;

if( ( loaded_str_translate = find_sym( "str_translate" ) ) != NULL )

printk( "+ адреса оброблювача = %p\n", loaded_str_translate );

else {

printk( "+ str_translate не знайдений\n" );

return -EINVAL;

}

if( ( trs = loaded_str_translate( par1 ) ) < 0)

return trs;

printk( "+ обчислене значення %d\n", trs );

res = unload_slave( mod_file );

if( res < 0) return res;

else return trs;

};

static int __init mod_init( void ) {

long res = 0;

void *waddr;

set_mod_name( this_mod_file, __FILE__ );

if( ( taddr = find_sym( "sys_call_table" ) ) != NULL )

printk( "+ адреса sys_call_table = %p\n", taddr );

else {

printk( "+ sys_call_table не знайдений\n" );

return -EINVAL;

}

old_sys_addr = (void*)taddr[ __NR_str_trans ];

printk( "+ адреса в позиції %d[__NR_str_trans] = %p\n", __NR_str_trans, old_sys_addr );

if( ( waddr = find_sym( "sys_ni_syscall" ) ) != NULL )

printk( "+ адреса sys_ni_syscall = %p\n", waddr );

else {

printk( "+ sys_ni_syscall не знайдений\n" );

return -EINVAL;

}

if( old_sys_addr != waddr ) {

printk( "+ незрозуміло! : адреси не збігаються\n" );

return -EINVAL;

}

printk( "+ адреса нового sys_call = %p\n", &sys_str_translate );

if( ( waddr = find_sym( "sys_init_module" ) ) == NULL ) {

printk( "+ sys_init_module не знайдений\n" );

return -EINVAL;

}

printk( "+ адреса sys_init_module = %p\n", waddr );

sys_init_module = waddr;

if( ( waddr = find_sym( "sys_delete_module" ) ) == NULL ) {

printk( "+ sys_delete_module не знайдений\n" );

return -EINVAL;

}

printk( "+ адреса sys_delete_module = %p\n", waddr );

sys_delete_module = waddr;

rw_enable();

taddr[ __NR_str_trans ] = sys_str_translate;

rw_disable();

printk( "+ модуль %s завантажений\n", this_mod_file );

return res;

}

static void __exit mod_exit( void ) {

printk( "+ адреса syscall при вивантаженні = %p\n", (void*)taddr[ __NR_str_trans ] );

rw_enable();

taddr[ __NR_str_trans ] = old_sys_addr;

rw_disable();

printk( "+ відновлена адреса syscall = %p\n", old_sys_addr );

printk( "+ модуль %s вивантажений\n", this_mod_file );

return;

}

Перевіримо спільну роботу всіх компонентів: користувальницького процесу, кореневого модуля й трьох модулей-плагинов.

$ ./syscall 0x77

syscall error -1 : Operation not permitted

$ sudo insmod master.ko

$ dmesg | tail -n30 | grep +

+ адреса sys_call_table = c07ab3d8

+ адреса в позиції 223[__NR_str_trans] = c045b9a8

+ адреса sys_ni_syscall = c045b9a8

+ адреса нового sys_call = f99db024

+ адреса sys_init_module = c0470f50

+ адреса sys_delete_module = c046f4e8

+ модуль master.ko завантажений

$ sudo ./syscall 077

syscall return 63

$ dmesg | tail -n30 | grep +

+ системний запит 3 байт: 077

+ довжина файлу модуля = 95223 байт

+ адреса буфера читання = f9c09000

+ лічено з файлу oct.ko 95223 байт

+ модуль oct.ko завантажений

+ адреса оброблювача = f9b83000

+ oct.ko : запит : 77

+ обчислене значення 63

+ модуль oct.ko вивантажений

$ sudo ./syscall 77

syscall return 77

$ dmesg | tail -n30 | grep +

+ системний запит 2 байт: 77

+ довжина файлу модуля = 95223 байт

+ адреса буфера читання = f9c41000

+ лічено з файлу dec.ko 95223 байт

+ модуль dec.ko завантажений

+ адреса оброблювача = f9c75000

+ dec.ko : запит : 77

+ обчислене значення 77

+ модуль dec.ko вивантажений

$ sudo ./syscall 0x77

syscall return 119

$ dmesg | tail -n30 | grep +

+ системний запит 4 байт: 0x77

+ довжина файлу модуля = 95553 байт

+ адреса буфера читання = f9c7b000

+ лічено з файлу hex.ko 95553 байт

+ модуль hex.ko завантажений

+ адреса оброблювача = f9caf000

+ hex.ko : запит : 77

+ обчислене значення 119

+ модуль hex.ko вивантажений

$ sudo ./syscall z77

syscall error -1 : Operation not permitted

$ sudo rmmod master

$ lsmod | head -n4

Module Size Used by

minix 19212 1

fuse 48375 2

ip6table_filter 2227 0

$ dmesg | tail -n37 | grep +

+ адреса syscall при вивантаженні = f99db024

+ відновлена адреса syscall = c045b9a8

+ модуль master.ko вивантажений

$ sudo ./syscall 0x77

syscall error -1 : Operation not permitted

Цей приклад виводить так багато проміжних результатів, що додатково коментувати його роботу немає необхідності. Правда, може виникнути закономірне питання: а де ж обіцяна можливість уводити в проект нові модули-плагины, не торкаючись коду кореневого модуля? У явному виді подібна функціональність у нашому прикладі відсутній. Але, якщо винести визначення відповідності між ім'ям використовуваного плагина (переменнаяind) і ім'ям файлу модуля (масив slave_name[]) у текстовий конфігураційний файл, то в результаті буде створена динамічно розширювана система (питання читання інформації з файлів було розглянуто в одній з перших статей даного циклу).

ВИСНОВКИ

У рамках моєї курсової роботи були розглянуті можливості модулів ядра Linux, які не часто зустрічаються в офіційній документації, і в результаті вдалося створити повноцінну систему динамічно підвантажуваних плагінів, функціонуючу в просторі ядра.

Динамічно завантажувати модулі ядра не тільки з коду користувацьких додатків, але і з коду інших модулів ядра. Даний підхід потенційно дозволяє створювати цілі послідовності або ієрархії завантажуваних модулів, детальна конфігурація яких визначається безпосередньо в момент завантаження.

Розглянуто досить складну, але, в принципі, реалізовану техніку, яка потенційно може бути використана для створення експлойтів. Однак для запуску будь-якого модуля ядра потрібні права root. Це один з найпотужніших захисних інструментів платформи UNIX (і Linux), і про це ніколи не слід забувати, коли мова стосується захищеності системи. Саме завдяки цій обставині, можна побачити, що за понад 20 років своєї історії для Linux не з'явилося практично жодного серйозного шкідливого вірусу.

ПЕРЕЛІК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ

1. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 38. Робота з UNIX-сигналами. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_38/. - Назва з екрану.

2. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 39. Пошук символів. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_39/. - Назва з екрану.

3. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 40. Оптимальний підхід до реалізації пошуку символів в ядрі. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_40/. - Назва з екрану.

4. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 41. Виконання системних викликів. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_41/. - Назва з екрану.

5. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 42. Підміна системного виклику. - Режим доступу : URL http://www.ibm.com/developerworks/ru/library/l-linux_kernel_42/. - Назва з екрану.

6. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 43. Додавання системного виклику. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_43/. - Назва з екрану.

7. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 44. Приховування системного виклику. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_44/. - Назва з екрану.

8. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 45. Завантаження модуля ядра з програмного коду. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_45/. - Назва з екрану.

9. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 46. Завантаження модуля ядра з коду іншого модуля. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_46/. - Назва з екрану.

10. Цилюрик О. І. Нестандартні сценарії використання модулів ядра: Частина 47. Підключаючі плагіни. - Режим доступу : URL : http://www.ibm.com/developerworks/ru/library/l-linux_kernel_47/. - Назва з екрану.

Размещено на Allbest.ru


Подобные документы

  • Структура мережевої підсистеми Linux. Створення мережевого інтерфейсу. Передача пакетів та аналіз поведінки інтерфейсу. Протокол транспортного рівня. Використання модулів ядра. Вплив маршрутизації на процес розробки і налагодження мережевих модулів.

    курсовая работа [56,2 K], добавлен 23.05.2013

  • История создания, архитектура операционной системы и перечень возможностей, реализуемых в Linux. Инструментальные средства и цикл разработки новой версии ядра. Жизненный цикл патча. Структура принятия решений при добавлении новых функций (патчей) в ядро.

    лекция [303,8 K], добавлен 29.07.2012

  • UNIX - одна з найпопулярніших в світі операційних систем. Ключеві риси Linux. Порівняльні характеристики 32-розрядних операційних систем. Поверхневий огляд характеристик ядра Linux. Програмні характеристики: базові команди і утиліти, мови програмування.

    курсовая работа [33,3 K], добавлен 07.12.2010

  • Дослідження внутрішньої структури операційної системи Windows. Архітектура NT і структура ядра. Методи перехоплення функцій у режимі ядра та режимі користувача. Поняття драйверу. Пакети вводу-виводу. Оцінка стабільності та безпеки системи Windows.

    курсовая работа [239,3 K], добавлен 02.01.2014

  • Огляд засобів створення програмного забезпечення сучасних мікроконтролерів. Аналіз методів та налаштувань контролерів. Засоби генерації коду налаштувань. Детальний опис розробки програми генератора налаштувань ядра Cortex M4 та методики її тестування.

    курсовая работа [1,3 M], добавлен 20.05.2015

  • Несколько определений ERP системы. Происхождение, развитие, признаки. Что дает внедрение. Особенности разработки программ на Java. Проектирование и реализация модуля ERP системы. Экономическая схема торговой деятельности. Пример реализации схемы.

    курсовая работа [1,1 M], добавлен 10.09.2008

  • Переваги і проблеми дистанційної освіти на прикладі корпорації Microsoft. Створення власного web-додатку. Розробка технічних умов програмної системи, модуля пошуку та бронювання авіаквитків. Інтеграція модуля з сайтом. Використання javascript фреймворків.

    курсовая работа [1,0 M], добавлен 31.08.2014

  • Мониторинг системных вызовов. Системные вызовы shmget, shmat, shmctl, shmdt. Написание и внедрение модуля ядра. Выбор языка программирования. Структура программного обеспечения. Реализация мониторинга управления и удаления сегментов разделяемой памяти.

    курсовая работа [91,4 K], добавлен 24.06.2009

  • Особенности архитектуры MIPS компании MIPS Technology. Иерархия памяти. Обработка команд перехода. Адресная очередь. Переименование регистров. Обоснование выбора операционной системы. Perl-эмулятор и сборка ядра. Электрическая и пожарная безопасность.

    дипломная работа [180,2 K], добавлен 06.03.2013

  • История развития и отличительные признаки UNIX-системы. Основы информационной безопасности и особенности настройки исследуемой операционной системы, ее достоинства, недостатки и базовые права доступа. Общая характеристика безопасности ядра UNIX.

    реферат [599,5 K], добавлен 18.09.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.