KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программы » Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК

Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн "Крис Касперский - ТЕХНИКА СЕТЕВЫХ АТАК". Жанр: Программы издательство неизвестно, год неизвестен.
Перейти на страницу:

Речь идет о «большой дыре» в функции “printf”, вернее дыра находится не в одной конкретной функции (тогда бы она могла бы быть безболезненно устранена), а в самом языке Си. Одни из его недостатков заключается в том, что функция не может определить сколько ей было передано параметров. Поэтому, функциям с переменным количеством аргументов, приходится каким-то образом передавать и число этих самых аргументов.

Функция “printf” использует для этой цели строку спецификаторов, и ее вызов может выглядеть, например, так: “printf(“Name: %snAge: %dnIndex: %xn”, amp;s[0],age,index)”. Количество спецификаторов должно быть равно количеству передаваемых функции переменных. Но что произойдет, если равновесие нарушится?

Возможно два варианта - переменных больше, чем спецификаторов и переменных меньше, чем спецификаторов. Пока количество спецификаторов не превышает количества переданных параметров, не происходит ничего интересного, поскольку, из стека аргументы удаляются не самой функцией, а вызывающим ее кодом (который уж наверняка знает, сколько аргументов было передано) разбалансировки стека не происходит и все работает нормально. Но если количество спецификаторов превышает количество требуемых аргументов, функция, пытаясь прочитать очередной аргумент, обратится к «чужим» данным! Конкретное поведение кода зависит от компилятора и содержимого стека на момент вызова функции “printf”.

Сказанное будет рассмотрено ниже на примере следующей программы (на диске, прилагаемом к книге, она находится в файле “/SRC/printf.bug”):

· #include «stdio.h»
·
· main()
· {
· int a=0x666;
· int b=0x777;
· printf("%x %xn",a);
·
·}
·

Если ее откомпилировать с помощью Microsoft Visual Studio 5.0-6.0, результат работы окажется следующий:


· 666 777

Программа выдала два числа, несмотря на то, что ей передавали всего одну переменную ‘a’. Каким же образом она сумела получить значение ‘b’? (а в том, что ‘777’ это действительно значение переменной ‘b’ сомневаться не приходится). Ответить на этот вопрос помогает дизассемблирование:

·.text:00401000 main proc near.text:00401000

·.text:00401000 var_8 = dword ptr -8

·.text:00401000 var_4 = dword ptr -4

·.text:00401000

·.text:00401000 push ebp

·.text:00401001 mov ebp, esp

·.text:00401001; Открывается кадр стека

·.text:00401003 sub esp, 8

·.text:00401003; Относительное значение esp равно 0 (условно)

·.text:00401006 mov [ebp+var_4], 666h

·.text:00401006 ; var_4 - это переменная a, которую компилятор расположил в стеке

·.text:0040100D mov [ebp+var_8], 777h

·.text:0040100D ; var_8 - это переменная b

·.text:00401014 mov eax, [ebp+var_4]

·.text:00401014 ; В регистр eax загружается значение переменной 'a’ для передачи его функции printf

·.text:00401017 push eax

·.text:00401017 ;В стек заносится значение переменной eax

·.text:00401018 push offset aXX; "%x %xn"

·.text:00401018; В стек заносится указатель на строку спецификаторов

·.text:00401018; Содержимое стека на этот момент такого

·.text:00401018; +8 off aXX (‘%x %x’) (строка спецификаторов)

·.text:00401018; +4 var_4 (‘a’) (аргумент функции printf)

·.text:00401018; 0 var_8 (‘b’) (локальная переменная)

·.text:00401018; -4 var_4 (‘a’) (локальная переменная)

·.text:0040101D call printf

·.text:0040101D; Вызов функции printf

·.text:00401022 add esp, 8

·.text:00401022; Выталкивание аргументов функции из стека

·.text:00401025 mov esp, ebp

·.text:00401025; Закрытие кадра стека

·.text:00401027 pop ebp

·.text:00401028 retn

·.text:00401028 main endp

Итак, содержимое стека на момент вызова функции printf такого (смотри комментарии к дизассемблированному листингу) [315]:

· +8 off aXX (‘%x %x’) (строка спецификаторов)

· +4 var_4 (‘a’) (аргумент функции printf)

· 0 var_8 (‘b’) (локальная переменная)

· -4 var_4 (‘a’) (локальная переменная)

Но функция не знает, что ей передали всего один аргумент, - ведь строка спецификаторов требует вывести два (“%x %x). А поскольку аргументы в Си заносятся слева на право, самый левый аргумент расположен в стеке по наибольшему адресу. Спецификатор “%x” предписывает вывести машинное слово [316], переданное в стек по значению. Для сравнения - вот как выглядит стек на момент вызова функции “printf” в следующей программе (на диске, прилагаемом к книге, она расположена в файле “/SRC/printf.demo.c”):

· main()
· {
· int a=0x666;
· int b=0x777;
· printf("%x %xn",a,b);
·
·}

·

· +12 off aXX (‘%x %x’) (строка спецификаторов)

· +08 var_4 (‘a’) (аргумент функции printf)

· +04 var_8 (‘b’) (аргумент функции printf)

· 00 var_8 (‘b’) (локальная переменная)

· -04 var_4 (‘a’) (локальная переменная)

Дизассемблированный листинг в книге не приводится, поскольку он практически ни чем не отличается от предыдущего (на диске, прилагаемом к книге, он расположен в файле “/SRC/printf.demo.lst”). В стеке по относительному смещению [317] +4 расположен второй аргумент функции. Если же его не передать, то функция примет за аргумент любое значение, расположенное в этой ячейке.

Поэтому, несмотря на то, что функции была передана всего лишь одна переменная, она все равно ведет себя так, как будто бы ей передали полный набор аргументов (а что ей еще остается делать?):

· +8 off aXX (‘%x %x’) (строка спецификаторов)

· +4 var_4 (‘a’) (аргумент функции printf)

· 0 var_8 (‘b’) (локальная переменная)

· -4 var_4 (‘a’) (локальная переменная)

Разумеется, в нужном месте стека переменная ‘b’ оказалась по чистой случайности. Но в любом случае - там были бы какие-то данные. Определенным количеством спецификаторов можно просмотреть весь стек - от верхушки до самого низа! Весьма велика вероятность того, что в нем окажется данные, интересные злоумышленнику. Например, пароли на вход в систему.

Теперь становится понятной ошибка, допущенная разработчиком buff.printf.c. Ниже приведен дизассемблированный листинг с подробными пояснениями (на диске, прилагаемом к книге, он находится в файле “/SRC/demo.printf.lst”):

·.text:00401000; --------------- S U B R O U T I N E ---------------------------------------

·.text:00401000

·.text:00401000; Attributes: bp-based frame

·.text:00401000

·.text:00401000 main proc near; CODE XREF: start+AFp

·.text:00401000

·.text:00401000 var_54 = byte ptr -54h

·.text:00401000 var_44 = byte ptr -44h

·.text:00401000 var_34 = byte ptr -34h

·.text:00401000 var_14 = dword ptr -14h

·.text:00401000 var_10 = byte ptr -10h

·.text:00401000

·.text:00401000 push ebp

·.text:00401001 mov ebp, esp

·.text:00401001 ; Открытие кадра стека

·.text:00401003 sub esp, 54h

·.text:00401003 ; Резервируется 0x54 байта для локальных переменных

·.text:00401006 push offset aPrintfBugDemo; "printf bug demon"

·.text:00401006 ; Занесение в стек строки “ printf bug demo"

·.text:0040100B call _printf

·.text:0040100B ; Вызов printf(“printf bug demon")

·.text:00401010 add esp, 4

·.text:00401010 ; Балансировка стека

·.text:00401013 push offset aR; "r"

·.text:00401013 ; Занесение в стек смещения строки “r”

·.text:00401018 push offset aBuff_psw; "buff.psw"

·.text:00401018; Занесение в стек смещения строки “buff.psw”

·.text:0040101D call _fopen

·.text:0040101D; Вызов fopen(“buff.psw”,”r”);

·.text:00401022 add esp, 8

·.text:00401022; Балансировка стека

·.text:00401025 mov [ebp+var_14], eax

·.text:00401025; Переменная var_14 представляет собой указатель файла psw

·.text:00401028 cmp [ebp+var_14], 0

·.text:00401028; Файл открыт успешно?

·.text:0040102C jnz short loc_0_401033

·.text:0040102C ; Файл открыт успешно! Продолжение выполнения программы

·.text:0040102E jmp loc_0_4010CD

·.text:0040102E ; Файл открыт неуспешно, переход к выходу

·.text:00401033; ---------------------------------------------------------------------------

·.text:00401033

·.text:00401033 loc_0_401033:; CODE XREF: main+2Cj

·.text:00401033 mov eax, [ebp+var_14]

·.text:00401033 ; Занесение в регистр EAX указателя на файловый манипулятор psw

·.text:00401036 push eax

·.text:00401036 ; Заталкивание psw в стек

·.text:00401037 push 8

·.text:00401037 ; Заталкивание в стек константы 8

·.text:00401039 lea ecx, [ebp+var_54]

·.text:00401039 ; Занесение в регистр ECX смещения начала буфера var_54

·.text:0040103C push ecx

·.text:0040103C ; Заталкивание его в стек

·.text:0040103D call _fgets

·.text:0040103D ;Вызов fgets( amp;_pass[0],8,psw)

·.text:0040103D ;Буфер var_54 представляет собой _pass

·.text:00401042 add esp, 0Ch

·.text:00401042 ;Балансировка стека

·.text:00401045 push offset aLogin; "Login:"

·.text:00401045 ; Заталкивание в стек смещения строки “Login:”

·.text:0040104A call _printf

·.text:0040104A ;Вызов printf(“Login:”)

·.text:0040104F add esp, 4

·.text:0040104F ; Балансировка стека

·.text:00401052 push offset off_0_407090

·.text:00401052 ; Заталкивание в стек указателя на манипулятор stdin

·.text:00401057 push 0Ch

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*