Раздел: Документация
0 ... 44 45 46 47 48 49 50 ... 102 LeigbLnC-B-aHM" 5Ьек0да 149 ш1 „ыделенныи регион памяти, доступной для записи. Для перестраховки переполняющийся буфер действительно срывает стек на хрен) регистр рекомендуется инициализировать самостоятельно. Это действительно очень шсто сделать, ведь многие из регистровых переменных уязвимой программы содержат предсказуемые значения, точнее - используются предсказуемым образом. Так, в Си++-нрограммах, откомпилированных MS VC++, ЕСХ наверняка содСржнт указатель this, a this — это не только «ценный мех», но и как мини-мум 4 байта доступной памяти! В порядке дальнейшего развития этой идеи отметим, что не стоит, право же, игнорировать значения регистров, доставшихся shell-коду в момент начала его выполнения. Многие из них указывают на полезные структуры данных и выделенные регионы памяти, которые мы гарантированно можем использовать, не рискуя нарваться на исключение и прочие неожиданные неприятности. Некоторые регистровые неременные чувствительны к версии уязвимого приложения, некоторые — к версии компилятора и ключам компиляции, так что «га-рантированность» эта очень и очень относительна, впрочем, как и все сущее на земле. ТЕХНИКА ВЫЗОВА СИСТЕМНЫХ ФУНКЦИЙ Возможность вызова системных функций, строго говоря, не является обязательным условием успешности атаки, поскольку все необходимое для атаки жертва (уязвимая программа) уже содержит внутри себя, в том числе и вызовы системных функций вместе с высокоуровневой оберткой прикладных библиотек вокруг них. Дезассемблировав исследуемое приложение и определив целевые адреса интересующих нас функций, мы можем сделать CALL целевой адрес или PUSH адрес возврата/JMP относительный целевой адрес или MOV регистр, абсолютный целевой адрес/PUSH адрес возврата/JMP регистр. Замечательно, если уязвимая программа импортирует пару функций LoadLib-ary/GetProcAddress, — тогда shell-код сможет загрузить любую динамическую библиотеку и обратиться к любой из ее функций. Л если функции GetProcAddress "таблице импорта нет? Тогда — атакующий будет вынужден самостоятельно Го]ределять адреса интересующих его функций, отталкиваясь от базового адре-papfP11, возРа,цеш1ЫМ LoadLibrary и действуя либо путем «ручного» разбо--файла, либо отождествляя функции по их сигнатурам. Первое сложно, кШй 6 ~ ненадежно- Закладываться на фиксированные адреса системных фун-011 КатегоРи,,ески недопустимо, поскольку они варьируются от одной версии Рационной системы к другой. и 0д?,Ц1.0а Как быть, когда функция LoadLibrary в таблице импорта отсутствует для р°И "ли нескольких системных функций, жизненно необходимых shell-коду <С"ространеиия, там тоже нет? В UNIX-системах можно (и нужно!) ис- 1.]Ван°Вать "Рямой вызов функций ядра, реализуемый либо посредством пре-TPi,))ИЯ П° вектору 80h (LINUX, Free BSD, параметры передаются через реги- 1 Л1,бо через дальний CALL по адресу 0007h:00000000h (System V, параметры 150 Глава 4. Ошибки переполнения буфера извнр передаются через стек), при этом номера системных вызовов содержатся личных ОС»). Еше можно вспомнить машинные команды syscal 1 /sysente /usr/include/sys/syscall.h (см. далее раздел «Реализация системных вызовов торые, как и следует из их названия, осуществляют прямые системные вц. вместе с передачей параметров. В Windows NT и производных от нее сист дела обстоят намного сложнее. Взаимодействие с ядром реализуется по !!?* ством прерывания INT 2Eh, неофициально называемого native API-interface ной» API-интерфейс). Кое-какая информация па этот счет содержится в лстеи дарном Interrupt Liste Ральфа Брауна и «Недокументированных возможностях Windows NT» Коберниченко, но мало, очень мало. Это чрезвычайно скуди, документированный интерфейс, и единственным источником данных остают ся дизассемблерные листинги KERNEL32.DLL и NTDLL.DLL. Работа с native API требует высокого профессионализма и глубокого знания архитектуры операционной системы, да и как-то громоздко все получается - ядро NT оперируй с небольшим числом довольно примитивных (или, если угодно, — низкоуровневых) функций. К непосредственному употреблению они непригодны и, как и всякий полуфабрикат, должны быть соответствующим образом приготовлены. Например, функция LoadLibrary «распадается», по меньшей мере, на два системных вызова —NtCreateFile (ЕАХ == 17h) — открываетфайл, NtCreateSection (ЕАХ = 2Bh) — проецирует файл в память (то есть работает как CreateFileMapping). после чего NtClose (ЕАХ = OFh) со спокойной совестью закрывает дескриптор. Что же касается функции GetProcAddress, то она целиком реализована в NTDLL.DLL и в ядре даже не ночевала (впрочем, при наличии спецификации РЕ-форма-та — она входит в Platform SDK и MSDN — таблицу экспорта .можно проанализировать и «вручную»). Правда, обращаться к ядру для выбора «эмулятора» LoadLibrary совершенно lit обязательно, поскольку библиотеки NTDLL.DLL и KERNEL32.DLL всегда присутствуют в адресном пространстве любого процесса, и если мы сможем опрел лить адрес их загрузки, мы сорвем банк. Автору известно два способа решения этой задачи — через системный обработчик структурных исключении и ЧСРСЗ РЕВ. Первый — самоочевиден, но громоздок и неэлегантен, а второйэлегаите* но ненадежен. ...РЕВ только на моей памяти менялась тРи1 © Юрий WP" Однако последнее обстоятельство ничуть не помешало червю Love San сать себя по миллионам машин..., Если во время выполнения приложения возникает исключительная t "а11рц-(деление на ноль или обращение к несуществующей странице памяти. t мер) и само приложение никак ее не обрабатывает, то управление лр3г системный обработчик, реализованный внутри KERNEL32.DLL н в W2K положенный по адресу 77EA1856h. В других операционных системах эт< будет иным, поэтому грамотно спроектированный shell-код лол#сН « тически определять адрес обработчика на лету. Вызывать исключе"1 к«Л (как эТО пР,,ходилось лелать во времена старушки MS-DOS) сИРоваТвершенно необязательно. Лучше обратиться к цепочке структурных и;Пе£оТчиков, упакованных в структуру EXCEPTI0NREGISTRATION, первое двой-iP3 в0 которых содержит указатель на следующий обработчик (или "(,С rWh если никаких обработчиков больше нет), а второе — на адрес дан-н)«бработчика(листИнг4.18). Листинг 4.18. Структура EXCEPTION REGISTRATION lKCEPTION REGISTRATION struc prev dd ? hardier dd ? [XCEPTION REGI S7RATI ON ends Первый элемент цепочки обработчиков хранится по адресу FS:[00000000h], а все последующие — непосредственно в адресном пространстве подопытного процесса. Перемещаясь от элемента к элементу, мы будем двигаться до тех пор, пока в поле prev не встретим FFFFFFFFFh, тогда поле handler предыдущего элемента будет содержать адрес системного обработчика. Неофициально этот механизм называется «раскруткой стека структурных исключений», I, подробнее о нем можно прочитать в статье Мэтта Питрека «А Crash Conrse on the Depths of Win32 Structured Exception Handling*, входящей в состав MSDN. В качестве наглядной иллюстрации далее приведен код, возвращающий в регистре ЕАХ адрес системного обработчика (листинг 4.19). Листинг 4.19. Код, определяющий базовый адрес загрузки KERNEL32.DLL по SEH data:00501007 xtr еах еах.ЕАХ .. „ :00501009 Х(Г ebx еЬхЕВХ := о mov есх. fs:[eax+4J:адрес обработчика mov еах. fs:[eaxj:указатель на след. обработчик jmp short 1ос 501019:на проверку условия цикла data •data data data -data -data data -data -data data data data 00501CC3 00501CCF 00501012 00501014 : --------- 00501014 loc 5010l4: 00501014 00501017 00501019 00501019 loc 501019: 00501019 К °050ШСиоинадлежаший библиотеке kfdm СК°Р° 110 крайней мере один адрес нрзаГрУзки уже ие со- NEL32.DLL, нам известен, определить базовый РсС ИЧаЛС neW-exc за-тап- никакого труда (он кратен lOOOh и держит в с Следующий код , «к.здементарноонознаваемый «осип загрузчика и в нем же воз-ет базовый адрес загрузки KERNtt-s-1- mov eDx. Leax+4] : адрес обработчика mov еах. [еах]: указатель на след. обработчик опр еах. OFFFFFFFFh : это последний обработчик? jnz snort loc 501014 : мотаем цикл, пока не конец 0 ... 44 45 46 47 48 49 50 ... 102
|