Шприц для bsd или функции на игле
Перехват функций не во сне, а наяву
Самое сложное в перехвате— это определить границы машинных инструкций, поверх которых записывается команда перехода на перехватчик (он же thunk, расположенный в нашем случае в теле функции gets). По-хорошему, для решения этой задачи требуется написать мини-дизассемблер, но… это же сколько всего писать придется! А можно ли без него обойтись? Можно!В начале большинства библиотечных функций расположен стандартный пролог вида PUSH EBP/MOV EBP,ESP/SUB ESP,XXXh (55h/89h E5h/ 83h ECh XXh), дающий нам пять байт — необходимый минимум для внедрения! Встречаются и другие, слегка видоизмененные прологи, например: PUSH EBP/MOV EBP,ESP/PUSH EDI/PUSH ESI (55h/89h E5h/ 57h/ 56h); PUSH EBP/MOV EAX, 0FFFFFFFFh/MOV EBP, ESP (55h/B8h FFh FFh FFh FFh/89h E5h); PUSH EBP/XOR EAX, EAX/MOV EBP,ESP (55h/31h C0h/89h E5h). Хороший перехватчик должен их учитывать.

Рисунок 4 функция gettimer с прологом далеким от стандартного
Таким образом, наш перехватчик должен проверить первые 5 байтов перехватываемой функции и, если они совпадают со стандартным (или слегка оптимизированным) прологом, скопировать этот пролог в свое тело и выполнить его перед передачей управления оригинальной функции. А куда его можно скопировать? Сегмент данных, как уже говорилось, нам недоступен, стек трогать нельзя (перед передачей управления на функции он должен быть восстановлен), а сегмент кода запрещен от модификации.
Существует по меньше мере три решения: во-первых, мы можем вызывать функцию mprotect, присвоив кодовой странице атрибут writable, (но это некрасиво), во-вторых, трогать стек все-таки можно: забрасываем пролог на верхушку, забрасываем туда же копию всех аргументов (а сколько у функции аргументов? да хрен его знает, вот и приходится копировать с запасом) и передаем ей управление как ни в чем не бывало (но это уже не просто "некрасиво", это вообще уродство).
В-третьих, мы можем поступить так:
// "коллекция" разнообразных прологов для сравнения
unsigned char prolog_1[]={0x55h,0x89,0xE5,0x83,0xEC};
unsigned char prolog_2[]={0x55,0x89,0xE5,0x57,0x56};
// буфер в который будет записан сгенерированный код
unsigned char buf_code[1024];
// определяем адрес перехватываемой функции
p = msym(base, fnc_name);
// если в начале перехватываемой функции расположен prolog_1
// внедряем в ее начало call на prepare_prolog_1
if (!memcmp(p,prolog_1,sizeof(prolog_1))
call_r(base, fnc_name, "gets", 0);
// если в начале перехватываемой функции расположен prolog_2
// внедряем в ее начало call на prepare_prolog_2
if (!memcmp(p,prolog_1,sizeof(prolog_2))
call_r(base,fnc_name,"gets", offset prapare_prolog_2-offset prepare_prolog_1);
Листинг 3 фрагмент программы-инсталлятора, анализирующей пролог перехватываемой функции и устанавливающей обработчик с соответствующим прологом
; // заносим номер "нашего" пролога в регистр EAX,
; // чтобы перехватчик знал какой ему пролог эмулировать
; // ВНИМАНИЕ! этот код засирает EAX
и не работает на fastcall-функциях,
; // для поддержки которых регистры трогать нельзя, а номер пролога класть на стек,
; // восстанавливая его перед передачей управления оригинальной функции
prepare_prolog_1:
MOV EAX, 0x1
JMP short do_begin
prepare_prolog_2:
MOV EAX, 0x2
JMP short do_begin
prepare_prolog_n:
MOV EAX, 0x2
JMP do_begin
do_begin:
// ОСНОВНОЙ КОД ПЕРЕХВАТЧИКА
// ДЕЛАЕМ ЧТО ЗАДУМАНО
// [ESP+4]+5 содержит адрес вызванной функции
// это поможет нам отличить перехваченные функции друг от друга
…
…
…
// ПЕРЕДАЧА УПРАВЛЕНИЯ ПЕРЕХВАЧЕННОЙ ФУНКЦИИ
// С ЭМУЛЯЦИЕЙ ЕЕ "РОДНОГО" ПРОЛОГА
DEC EAX
JZ prolog_1
DEC EAX
JZ prolog_2
…
prolog_1: ; // эмулируем выполнение пролога типа PUSH EBP/MOV EBP,ESP/SUB ESP,XXX
PUSH EBP
MOV EBP,ESP
SUB ESP, byte ptr [EAX] ; берем XXh из памяти
INC EAX ; на след. машинную команду
JMP EAX
prolog_2: ;// эмулируем
выполнение пролога
типа PUSB EBP/MOV EBP,ESP/PUSH EDI/PUSH ESI
PUSH EBP
MOV EBP, ESP
PUSH EDI
PUSH ESI
JMP EAX
Листинг 4 базовый код перехватчика (расположенный в gets), поддерживающий несколько различных прологом
Программа-инсталлятор анализирует пролог перехватываемой функции и, в зависимости от результата, внедряет в ее начало либо call prepare_prolog_1 либо call prepare_prolog_2, где prepare_prolog_x – метка, расположенная внутри thunk-кода, помещенного нами в функцию gets. Команда call занимает 5 байт и потому в аккурат накладывается на команду SUB ESP,XXh так, что XXh оказывается прямо за ее концом. Поэтому, сохранять XXh в теле самого перехватчика не нужно!!! Команда SUB ESP, byte ptr [EAX], вызываемая из thunk-кода эмулирует выполнение SUB ESP,XXh на ура!
Приведенный пример портит регистр EAX и работает только с cdecl и stdcall функциями. Перехват fastcall-функции, передающих аргументы через EAX, по этой схеме невозможен. Однако, оригинальный EAX можно сохранять в стеке и восстанавливать непосредственно перед передачей управления перехваченной функции, но в этом случае JMP EAX придется заменить на RETN, а на верхушку стека предварительно положить адрес для перехода.
Вот, собственно говоря, и все. Скелет перехватчика успешно собран и готов к работе. Остается дописать "боевую начинку". Это может быть и логгер, протоколирующий вызовы, и анти-протектор, блокирующий вызовы некоторых функций (например, удаление файла), и макро-машина, "подсовывающая" функциям клавиатурного ввода готовые данные и… да все что угодно!
Шприц для *bsd или функции на игле
крис касперски ака мыщъх, no fucking e-mailвнедриться в адресное пространство чужого процесса — проще простого! в первой части статьи мы показали как сконструировать универсальный шприц-баян, теперь остается только замутить тот фармацевтический ### магический раствор, который будет введен внутрь чужеродных клеток машинного кода. навстречу нашей вакцине тут же устремится батальон иммунных тел, готовых сожрать ее в один момент (и ведь сожрут же!), но мыщщъх знает один хитрый рецепт…
>>> Врезка проблемы стабильности
Сконструированный нами перехватчик довольно капризен по натуре и периодически падает без всяких видимых причин. Почему это происходит? Рассмотрим функцию func со стандартным прологом вида PUSH EBP/MOV EBP,ESP. Допустим, процесс A выполнил команду PUSH EBP и только собирался приступить к выполнению MOV EBP,ESP как был прерван системным планировщиком и управление получил наш процесс B, осуществляющий перехват функции func путем внедрения в ее начало инструкции call. Когда процесс A возобновит свое выполнение, команды MOV EBP ESP там уже и не окажется, а будет торчать хвостовая часть от call при выполнении которой все пойдет в разнос. Конечно, вероятность такого события исчезающе мала, но в особо ответственных случаях с ней все-таки стоит считаться.
Рисунок 5 стандартный пролог функции getaddrinfo, испорченный несвоевременным переключением планировщика
Чтобы "обезопасить" перехват, необходимо сократить длину внедряемой инструкции до одного байта, но… таких инструкций просто нет! То есть как это нет? А INT 03h (CCh) на что? Традиционно она используется для организации точек останова и на прикладном уровне защищенного режима возбуждает исключение, которое легко перехватить из ядра, а точнее из загружаемого модуля. Об этом уже писалось в статье "Handling Interrupt Descriptor Table for fun and profit", опубликованной в 59 номере phrack, так что не будем повторяться.
Заметим, что CCh конфликтует с некоторыми защитными механизмами и, естественно, с отладчиками, поэтому лучше внедрять не INT 03h, а какую-нибудь "запрещенную" однобайтовую команду типа CLI (FAh), возбуждающую исключение, которое мы будем отлавливать.
>>> Врезка точки останова
Отладчики могут устанавливать программные точки останова на библиотечные функции, внедряя в их начало команду INT03h (CCh). В этом случае наш перехватчик не сможет распознать пролог, что не есть хорошо.
Рисунок 6 стандартный пролог функции lchmod, искаженной программной точной останова (CCh), установленной отладчиком
Выход очевиден — сравнивать только 2й, 3й, 4й и 5й байты пролога, игнорируя 1й байт. А что делать с точкой останова? Если записать call поверх нее, то она будет затерта и отладчик потеряет контроль за функцией, что в некоторых случаях неприемлемо и тогда необходимо внедряться со 2го байта, но в этом случае команда call полностью затрет SUB ESP,XXh и XXh придется сохранять где-то в другом месте.
функций под windows хорошо исследованы
Механизмы перехвата API- функций под windows хорошо исследованы и предложить радикально новый трюк довольно трудно. UNIX-системы исследованы намного хуже и таят множество нераскрытых возможностей, притягивающий хакеров и прочих творческих людей. Описанный мыщъх'ем способ — не единственный и, вероятно, не самый удобный, к тому же до "промышленного применения" ему еще расти и расти. Тем не менее, в мыщъхиных утилитах, написанный на скорый хвост, он вполне нормально работает — как под linux'ом, так и под BSD.
Работа с информацией: Безопасность - Защита - Софт - Криптография
- Информационная безопасность
- Аспекты информационной безопасности
- Системы информационной безопасности
- Софт и информационная безопасность
- IInternet Information Services
- Защита и безопасность
- Защита с Firewall
- Атаки и информационная безопасность
- Информационная безопасность в интернет
- Борьба с вирусами
- Антивирусы против вирусов
- Хакеры и информационная безопасность
- Криптография