Ну и завершающая часть нашей эпопеи,
рассказывающей о работе со взломанной Linux системой. В прошлый раз мы остановились на
верификации системных вызовов. В следующей
чати приводится один из примеров
реальной работы.

Первым шагом необходимо найти адрес
таблицы системных вызовов (sys_call_table). В
большинстве Linux эту информацию можно
обнаружить в файле Symbol.map:

# cat Symbol.map | grep sys_call_table
c02c209c D sys_call_table

Теперь мы можем получить список из
sys_call_table. Каждая запись — адрес system call. Для
правильной интерпретации исследования
рекомендую использовать entry.S из исходников
ядра. Там содержится правильный порядок
таблицы.

ENTRY(sys_call_table)
     .long SYMBOL_NAME(sys_ni_syscall) /* 0 — old
«setup()» system call*/
     .long SYMBOL_NAME(sys_exit)
     .long SYMBOL_NAME(sys_fork)
     .long SYMBOL_NAME(sys_read)
     .long SYMBOL_NAME(sys_write)
     .long SYMBOL_NAME(sys_open) /* 5 */
     .long SYMBOL_NAME(sys_close)
     …

Такой же порядок можно обнаружить и в
sys_call_table. Для того, чтобы посмотреть первые
10 вызовов делаем так:

(gdb) x/10x 0xc02c209c
0xc02c209c : 0xc01217c0 0xc011ac50 0xc0107510 0xc0138d50
0xc02c20ac : 0xc0138e50 0xc0138880 0xc01389b0
0xc011b010
0xc02c20bc : 0xc0138930 0xc01445c0

Адрес 0xc01217c0 это sys_ni_call, адрес 0xc011ac50 — sys_exit и
так далее. Если есть такая необходимость, то можно
сравнить каждый адрес из System.map или ksyms.
Конечно, взломщику не нужно менять адрес
каждого вызова, но есть несколько самых
«популярных», такие как например sys_read,
sys_getdents или sys_write.

Переходим к дальнейшему опознанию —
списку всех активных процессов.

Для создания полного списка необходимо
найти адрес init_task_union структуры. Она
указывает на дескриптиор первого процесса
process 0 или swapper.

# cat Symbol.map | grep init_task_union
c02da000 D init_task_union

Дальше требуется понять как выглядит эта
структура. Пример init_task можно легко
обнаружить в sched.h из исходников ядра.
Структура task_struct лежит там же.

#define INIT_TASK(tsk) 

state: 0, 
flags: 0, 
sigpending: 0, 
addr_limit: KERNEL_DS, 
exec_domain: &default_exec_domain, 

run_list: LIST_HEAD_INIT(tsk.run_list), 
time_slice: HZ, 
next_task: &tsk, 
prev_task: &tsk, 
p_opptr: &tsk, 

Для нас самое важное это prev_task и next_task для
каждого процесса. Они помогут нам создать
полный список процессов, как ясно из
названия указывают они на предыдущий и
следующий процесс в списке. Ниже пример
дескриптора, который лежит по адресу 0xc514c000.

(gdb) x/180x 0xc514c000

0xc514c040: 0x00000000 0xffffffff 0x00000004 0xc1be0000
0xc514c050: 0xc4dac000 0xc5ea8e40 0xc5ea8e40 0xc02c56d4

0xc514c070: 0x00000001 0x00001ace 0x00001ace 0x00000000

0xc514c230: 0xffffffff 0xffffffff 0x61620000 0x00006873
0xc514c240: 0x00007974 0x00000000 0x00000000 0x00000000

где 0xc1be0000 — следующая задача, 0xc4dac000 —
предыдущая, 0x00001ace — PID процесса, равный 6268 в
десятичном счислении, 0x61620000 и 0x00006873 — имя
процесса, bash в данном случае. Для перехода к
следующему дескриптору делаем так:

(gdb) x/180x 0xc1be0000

Заключение

Цель этой статьи была в показе некоторых
методов сбора и анализа данных. В частности
я сфокусировал свое внимание на тех данных,
которые были бы потеряны после выключения
системы. еще раз хочу отметить, что анализа
нападения и расследование происшествия
очень трудны, необходимо быть очень
осторожным во всех действиях и
документировать всё. Я показал как выглядит
процедура сбора на живой системе и теперь
вы можете повторить ее на своей собственной
машине и на других Linux. В будущем я надеюсь
написать аналогичную статью по сбору
данных в Windows системах.

Ссылки

Оставить мнение

Check Also

Espruino Pico. Учимся программировать USB-микроконтроллер на JavaScript и делаем из него токен авторизации

Несмотря на огромное количество устройств на базе микроконтроллеров, созданных на волне ус…