Detecting Kernel-level Compromises With gdb
Mariusz Burdach
Трояны в инсталлируемых прогаммах могут изменять функции системных утилит .
Например команда ls должна показывать содержимое директорий . Модифицированная команда ls
может скрывать заданные файлы . Подобные модификации распознаются с помощью средств , называемых
integrity checkers.
Трояны могут модифицировать как отдельные программы , так и фрагменты ядра .
Утилита ls при работе использует системный вызов ядра sys_getdents , который в свою очередь
может быть модифицирован . Системные вызовы представляют интерес с точки зрения контроля над машиной ,
поскольку обладают мощным потенциалом . Системные вызовы предоставляют доступ к файлам , устройствам ,
запускают исполняемые файлы и т.д.
В ядре 2.4.27 230 системных вызовов . В ядре 2.6.9 их уже 290 .
Полный их список доступен в файле /usr/include/asm/unistd.h .
Вот список основных системных вызовов ( 2.4.18-3.) :
sys_read
sys_write
sys_open
sys_getdents/sys_getdents64
sys_socketcall
sys_query_module
sys_setuid/sys_getuid
sys_execve
sys_chdir
sys_fork/sys_clone
sys_ioctl
sys_kill
Адрес системного вызова хранится в таблице , которая находится в памяти в пространстве ядра .
У каждого имеется свой ID .
Рассмотрим sys_write . При его вызове ID-шник , равный 4 , помещается в региистр eax и вызывается
прерывание 0х80 . Номер прерывания при этом помещается в специальную таблицу прерываний .
Затем срабатывает system_call , который в нужной таблице по нужному ID-шнику вызывает нужный адрес .
Основной метод троянского вторжения заключается в том , чтобы подсунуть вместо нужного адреса другой .
Отследить вызываемый адрес можно с помощью gdb . Необходимо также убедиться и в том , что и сама
таблица системных вызовов с адресами не подверглась модификации . Адрес памяти таблицы системных
вызовов статический и формируется на этапе компиляции ядра . Для 7-го редхата и его клонов этот адрес
во время компиляции пишется по крайней мере в 2 местах на диске - в файлах System.map и vmlinux-2.4.x ,
которые обычно лежат в каталоге /boot . Иногда доступен оказывается только компрессионный файл vmlinuz-2.4.x ,
Мы можем сравнить адреса системной таблицы в памяти и на диске .
Можно использовать для этих целей также простой модуль , который может быть откомпилирован так :
gcc -c scprint.c -I/usr/src/linux/include/
После загрузки этого модуля текущие системные адреса из памяти пишутся в сислог .
В линуксе трояны загружаются обычно с помощью модулей или добавлением кода в обьект /dev/kmem .
Память операционной системы представлена обьектом kcore в каталоге /proc .
Итак , для начала найдем "первозданный" адрес талицы системных вызовов :
cat System.map-2.4.18-13 | grep sys_call_table c0302c30 D sys_call_table
Теперь распечатаем саму таблицу
nm vmlinux-2.4.18-13 | grep sys_call_table
Вывод
c0302c30 D sys_call_table
То же самое можно сделать с помощью команды gdb :
gdb /boot/vmlinux-2.4.*
После появления приглашения набираем :
(gdb) x/255 0xc0302c30
Вывод
0xc0302c30 : 0xc01261a0 0xc011e1d0 0xc01078a0 0xc013fb70
...
Можно распечатать адрес конкретно для каждой функции :
(gdb) x/x sys_ni_syscall
0xc01261a0 : 0xffffdab8
Адреса должны соответствовать тому , что прописано в entry.S.
Текущие адреса системных функций ядра можно распечатать с помощью команды
gdb /boot/vmlinux-2.4.* /proc/kcore
Остается сравнить 2 полученных дампа .
Можно отдизассемблировать актуальный системный вызов из памяти следующим образом :
gdb /boot/vmlinux* /proc/kcore
(gdb) disass sys_read
То же самое можно сделать с образом на диске
gdb /boot/vmlinux* /proc/kcore
(gdb) disass sys_read
0xc013fb70 : sub $0x28,%esp
0xc013fb73 : mov 0x2c(%esp,1),%eax
0xc013fb77 : mov %esi,0x1c(%esp,1)
0xc013fb7b : mov %edi,0x20(%esp,1)
и сравнить полученные результаты . По идее результаты должны совпасть .
|
|