Пошук і аналіз “троянських коней” під UNIX, Unix, Операційні системи, статті

Mixter, Security papers
Переклад: Василь Кондрашов, www.citforum.ru

Цей документ – спроба дати уявлення про методи аналізу виконуваних файлів ОС UNIX для передбачення дій, які вони можуть зробити в системі. Ці методи застосовні для дослідження виявлених “Троянських коней” та іншого шкідливого програмного забезпечення. Вони також будуть корисні для аналізу прекомпилированном програмного забезпечення з метою переконатися в його надійності.

При використанні програмного забезпечення з відкритим вихідним кодом користувач може отримати вихідний код програми і відкомпілювати свою, надійну версію. Однак, вихідний код може містити можливості троянського коня, які нелегко помітити. Деякі з витончених способів виробництва прихованих дій використовують системні виклики system() або exec для передачі команд, що викликають навмисні переповнення або небезпечні ситуації, інтерпретатору shell. Використовується також безпосереднє виконання інструкцій асемблера шляхом створення покажчика (“void (*fp)()”) на двійкову рядок з наступним його виконанням. Проте, буває, що програми прекомпилированном, наприклад, як частина rpm або подібного двійкового пакета, або частини комерційного програмного забезпечення, або двійкові файли скомпільовані з неперевіреного вихідного коду, до того ж віддаленого після компіляції.

На щастя, більшість UNIX-систем пропонують безліч інструментів для розробки та налагодження, які полегшують аналіз двійкових файлів. Перш за все, все повинно робитися в “чистій”, тобто, надійної середовищі, де виявлений двійковий файл досліджується, але не був ще виконаний. Естетственно, потрібно використовувати непривілейованих обліковий запис (account). Якщо Вам дійсно потрібно шукати і аналізувати можливі “Троянців” в ненадійною системі, то потрібно використовувати автономний інтерпретатор shell (sash), який повинен бути об’єднаний статично (statically linked). В такому випадку єдиною програмою, що грає роль “троянця” може бути модуль ядра, відповідальний за упаковку системних викликів open і read, Але такі “троянці” досить рідкісні. При використанні автономного інтерпретатора shell найбільш значимими є команди ls, more і ed.

Перше, що повинно бути зроблено для пошуку “троянця” – пошук явних кодограмм в двійковому файлі. Це може бути зроблено з використанням strings або переглядом за допомогою less. Автор першоджерела воліє редактор joe, Який дозволяє переглядати і редагувати майже всі не-ascii символи. Кодограми зазвичай включають жорстко запрограмований імена використовуваних файлів, ascii-рядки, які програма записує в інші файли або статичні рядки, які вона може шукати, або імена використовуваних бібліотечних функцій, якщо з файлу не видалені символи. Вони можуть також містити імена незвичайних бібліотек або бібліотек, котрі вони не мають наміру використовувати будучи “троянцем”. Краще за все перевірити це, використовуючи ldd для визначення залежностей від бібліотек і file для визначення чи були видалені символи, чи була програма статично пов’язана і інших спеціальних форматів.

На наступному етапі аналізу програми потрібно простежити виклики функцій, що виконуються програмою і порівняти їх з функціями, які програма, за припущенням, повинна виконувати. Системні виклики можуть бути простежені в більшості систем за допомогою strace, ktrace в BSD, або truss в Solaris. Слід звернути увагу на всі спроби доступу до файлів (open/stat/access/read/write), Виклику гнізд (socket calls), особливо, виклики listen() і fork(). Породжені процеси можуть бути простежені за допомогою опції -f у всіх цих програмах.

Заслуговує інтересу подібний інструмент для Linux – ltrace, Який розпізнає всі бібліотечні виклики, вироблені програмою в обхід системних програм і дозволяє створити дуже докладний список параметрів програми.

Нарешті, важливо те, що програма може і повинна бути дизасемблювати, переважно, з використанням gdb. Дизасемблювання, в основному, означає виявлення функцій в двійковому файлі і переклад двійкового коду назад у команди асемблера. Спочатку повинна бути визначена точка входу в програму. Це – адреса початку функції, яка буде виконана при запуску програми і яка, якщо з програми не була модифікована або з неї не були видалені символи, завжди називається main або _start. Слідуючи за цією функцією можна простежити процес виконання програми і побачити, що може, а чого не може зробити програма. Особливо цікаві виклики інструкцій (function
calls
) Та інструкції jmp/int, Якщо вони використовують небібліотечние виклики ядра або скомпільовані статично. Типові точки входу для двійкових файлів архітектури x86 виглядають приблизно так:

0x8048f97 <_start+7>:   call   0x8048eac <atexit>
0x8048f9d <_start+13>:  call   0x8048dcc <__libc_init_first>
0x69662 <__open+18>:    int    $0x80

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

void do_something (char *y) {};
char *h = "hello world";
int main() {
char *text = h;
int x = getchar();
do_something(text);
return 0;
}

Це відповідає наступним командам асемблера:

0x804847e <main+6>:     movl   0x804950c,%eax
0x8048483 <main+11>:    movl   %eax,0xfffffffc(%ebp)

Зберегти покажчик за відносною адресою. Покажчик посилається на статичну, жорстко запрограмовану рядок “hello world” в коді.

0x8048486 <main+14>:    call   0x80483cc <getchar>
0x804848b <main+19>:    movl   %eax,%eax
0x804848d <main+21>:    movl   %eax,0xfffffff8(%ebp)

Викликати функцію getchar і помістити результат (мабуть, ціле. зберігається в регістрі EAX) в стек. EBP – базовий покажчик, який використовується для посилання на адреси щодо текукщей функції в стек.

0x8048490 <main+24>:    movl   0xfffffffc(%ebp),%eax
0x8048493 <main+27>:    pushl  %eax
0x8048494 <main+28>:    call   0x8048470 <do_something>

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

Тепер ми можемо вручну разименовать покажчик за допомогою команди x
:

(gdb) x/a 0x804950c
/* x option /a displays the memory content as an address, to
   see which address a pointer actually points to */
0x804950c <h>:  0x80484fc <_fini+28>
(gdb) x/s 0x80484fc
/* x option /s displays the memory content as string up to the
   point where a terminating \0 is found */
0x80484fc <_fini+28>:    "hello world"

У великих і складних програмах (автор припускає, що це може зажадати часу на пошук всіх можливих дій) це досить повний метод визначення, що насправді робить програма, безвідносно до того, чи була вона прекомпилированном, статично пов’язана, позбавлена ​​символів або що-небудь ще.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*