Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #80, АВГУСТ 2005 г.

Штурм ядра Linux

Крис Касперски ака мыщъх

Хакер, номер #080, стр. 080-106-3


Первые 200h байт файла vmlinuz принадлежат boot-сектору, который грузится по адресу 0000:7C00 и выполняется в 16-разряном режиме. Нажимаем <Alt-S> или обращаемся к меню "Edit -> Segment -> Edit Segment" (здесь и далее горячие комбинации указаны для IDA Pro 4.7, в других версиях они могут слегка отличаться). Вводим имя сегмента: boot, начальный адрес оставляем без изменений, а конечный меняем на 200h. На все грозные предупреждения отвечаем однозначным "yes". Затем подводим курсор к первому байту кода и нажимаем <C>, чтобы IDA Pro превратила ячейки памяти в код. После этого дизассемблирование можно продолжать как обычно. Исходный код загрузчика можно найти в файле /arch/i386/boot/bootsect.S, а можно и не искать — нам он не интересен. За долгие годы он вылизан дочиста. Даже если какие-то баги в нем есть, использовать их, скорее всего, не удастся.

Мы видим, что boot-сектор перемещается по адресу 9000h:0000h и считывает с диска вторичный загрузчик, который так же находится внутри vmlinuz, сразу за boot-сектором. Здесь расположены модули setup.S и video.S, загружающиеся по адресу 1000h:0000h и работающие в 16-разрядном режиме. Начало модуля setup.S опознается по сигнатуре "HdrS", следующей после jmp'а. Конец video.S легко определить по строкам: CGA/MDA/HGA/EGA/VGA/VESA/Video adapter, вслед за которыми идет "магическая последовательность" 00 00 B8 00 00. В обоих ядрах он расположен по смещению 14FF от начала файла. Таким образом, вторичный загрузчик начинается со смещения 200h и заканчивается в 14FFh. Он так же исполняется в 16-разрядном режиме и представляет собой смесь кода и данных, поэтому дизассемблировать его приходится с большой осторожностью. Но прежде необходимо создать новый сегмент, ведь предыдущий был усечен! Говорим "Edit -> Segment -> New Segment", вводим имя сегмента (например, "ldr"), адрес начала (200h) и конца (1500h), а так же базовый адрес, равный стартовому адресу, деленному на 10h. Форсируем 16-битный режим и давим ОК.

За вторичным загрузчик идет 100h "ничейных" байт, забитых нулями, а вот затем со смещения 1500h начинается как-то дикий код, который никак не удается дизассемблировать. IDA выводит всего несколько строк, жалобно пищит и отказывается продолжать работу:

IDA Pro дизассемблирует "дикий" код

1600 cld

1601 cli

1602 mov ax, 18h

1605 db 0

1606 db 0

1607 db 8Eh ; О

1608 db 0D8h ; +

HTE и HIEW как будто бы дизассемблируют дикий код, но делают это неправильно!

Неверный результат дизассемблинга

1600 fc cld

1601 fa cli

1602 b81800 mov ax, 0x18

1605 0000 add [bx+si], al

1607 8ed8 mov ds, ax

1609 8ec0 mov es, ax

160b 8ee0 mov fs, ax

160d 8ee8 mov gs, ax

А все потому, что именно с этого места ядро начинает исполняться в 32-разрядном защищенном режиме, и для правильного дизассемблирования разрядность сегмента необходимо изменить. После чего IDA Pro заработает, как ни в чем ни бывало. Сейчас мы находимся в распаковщике, подготавливающем основной ядерный код к работе. Он реализован в файлах /arch/i386/boot/compressed/head.S и misc.c. "Персонального" адреса загрузки он не имеет и грузится вместе с первичным загрузчиком по адресу 1000h:0000h. Таким образом, первый байт распаковщика расположен в памяти по адресу 1000h:0000h + sizeof(ldf) == 1000h:01300h, что соответствует физическому адресу 101300h. Распаковщик настраивает сегментные регистры DS/ES/SS/GS/FS на селектор 18h, а регистр CS на селектор 10h.

Назад на стр. 080-106-2  Содержание  Вперед на стр. 080-106-4