Раздел: Документация
0 ... 37 38 39 40 41 42 43 ... 102 Листинг 4.5. Дизассемблерный листинг переполняющейся программы с краткими комментариями .text:00401000 mainproc near: CODE XREF: start+AFvp .text:00401000 .text:00401000 var 14- dword ptr -14h : this .text:00401000 var 10- dword ptr -lOh : *a .text:00401000 var C= byte ptr -OCh .text:00401000 var 4- dword ptr -4 .text:00401000 .text:00401000push ebp .text:00401001mov ebp. esp здесь непригодно и приходится уповать на индексное, все еще остающееся т. ретической экзотикой, робко осваивающей окружающий мир. Модифицировать указатель на объект и/или указатель на виртуальную табли. цу намного проще, поскольку они не только находятся в области памяти, д0. ступной для модификации, но зачастую располагаются в непосредственной близости от переполняющихся буферов. Модификация указателя this приводит к подмене виртуальных функции объек-та. Достаточно лишь найти в памяти указатель на интересующую нас функцию (или вручную сформировать его в переполняющемся буфере) и установить На него this с таким расчетом, чтобы адрес следующей вызываемой виртуальной функции попал на подложный указатель. С инженерной точки зрения это до-статочно сложная операция, поскольку, кроме виртуальных функций, объекты еще содержат и переменные, которые более или менее активно используют Переустановка указателя this искажает их «содержимое», и очень может быть, что уязвимая программа рухнет раньше, чем успеет вызвать подложную виртуальную функцию. Можно, конечно, сымитировать весь объект целиком, но не факт, что это удастся. Сказанное относится и к указателю на объект, поскольку с точки зрения компилятора они скорее похожи, чем различны. Однако наличие двух различных сущностей дает атакующему свободу выбора — в некоторых случаях предпочтительнее затирать указатель this, в некоторых случаях -указатель на объект (листинги 4.4 и 4.5). Листинг 4.4. Фрагмент программы, подверженной последовательному переполнению при записи, с затиранием указателя на таблицу виртуальных функций class А{ public: virtual void f() { printfClegaHn"):}; }: cnainO { char buff[8]: A *a = new A: printf("passwd:"):gets(buff): a->f(): ФИ ,.BlB03subesp. I4h "01003: онрываем кадр сгека и резервируем 14П стековой памяти . 0Q401003: •«ЦОМСбPush4 00401008сoperator new(uint) ??/n!nnn• выделяем память для нозого экземпляра объекта А и получаем указатель text text text text text .text text text text text text .text text text 0040100D 0040100D 0040]C10mov [ebp-wariO] eax С0401010: записываем указатель на обьект в переменную var lD 00401010: 00401013сто [ebp+var 10]. О 00401017J short 1ос 401026 00401017: проверка успешности выделения памяти 00401017 00401019mov есх. Lebp+var 10] 0О4О101Сcall А:: А 00401С1С: вызываем конструктор объекта А 0040101С 00401021nov £oDp-var 14J. eax 00401021: заносим воззоаденный указатель this в переменную var 14 С0401021: lext 004C102Dloc 40102D: : CODE XREF: main+24Aj ;ext.00401023mov eax. [ebp*var 14J text:00401C30mov [ebp+var /.]. eax •cxt:00401030: берем указатель th>s и перепрятываем ею в переменную var 4 • »t:00401030. СС0401033pusn offset aPasswd "passwd:" >::00401038call prir.tf ::0040103Dadd esp. 4 "*-:0040103D. выводим приглашение к вводу на зкран :.004C103D; »t:OC401040lea есх. [ebp+var C] -:00401040; переполняющийся буфер расположен ниже указателя на объект и Л-0040104С; перзи-тного указателя this, но выше порожденного указателя this. t:00401040: что делает последний уязвимым itxt:0040I040; text:004C!043 push есх lext:00401044са11 gt?;s №«00401049add esp. 4 ext:00401049: чтение строки в буфер tn«:00401049; t:0040104Cmov edx. [ebp+var 4] t 00401C4C; за-рукаеч уязвимый указатель this в регистр EDX t:00401C4C• J21Mfmov oax. [edx] °0401()4F 13БЛеК)е1 адреС виРт>альн0Й таблиЧы -t-од401051mov еСХ [ebp"var-4] 401051; передаем функции указатель this * "6Продолжение > ИНДЕКСЫ Индексы являются своеобразной разновидностью указателей. Грубо гоВ0,ц относительные указатели, адресуемые относительно некоторой базы. ~st • те, p[i] можно представить и как *(p+i), практически полностью уравн и i в правах. Модификация индексов имеет свои слабые и сильные стороны, "fqi указатели требуют задания абсолютного адреса целевой ячейки, который неизвестен, в то время как относительный вычисляется «на ура». ИндскС1 до нящисся в переменных типа char, лишены проблемы нулевых символ" , дексы, хранящиеся в неременных типа int, могут беспрепятственно 3 ячейки, расположенные «выше» стартового адреса (то есть лежаШне в Листинг 4.5 (продолжение) .text:00401051 .text:00401054call dwo>TJ Dtr [eax] .text:00401G54 : вызываем виртуальную функцию -первую функцию виртуальной таблиц .text:00401C54 : .text:00401056mov esp. ebp .text:00401058pop ebp .text .-00401059retn .text:00401059 mainendp Рассмотрим ситуацию, когда следом за переполняющимся буфером идет ука затель на скалярную переменную р и сама переменная х, которая в некоторь-, момент выполнения программы по данному указателю и записывается (п0ря док чередования двух последних переменных не существенен, главное, чтобн переполняющийся буфер затирал их все). Допустим также, что с момента переполнения ни указатель, ни переменная не претерпевают никаких измененщ (или изменяются предсказуемым образом). Тогда, в зависимости от состояния ячеек, затирающих оригинальное содержимое переменных х и р, мы сможем записать любое значение х по произвольному адресу р, осуществляя это «рука ми» уязвимой программы. Другими словами, мы получаем аналог функций Р(К и PatchByte/PatchWord языков Бейсик и ГОА-Си соответственно. Вообще-то ш выбор аргументов могут быть наложены некоторые ограничения (например функция gets не допускает символа нуля в середине строки), но это не слип: ком жесткое условие, и имеющихся возможностей вполне достаточно для захвата управления над атакуемой системой (листинг 4.6). Листинг 4.6. Фрагмент программы, подверженной последовательному переполнению при записи, с затиранием скалярной переменной и указателя на данные, поглощающие затертую переменную cata ptr() { char beff[8]: int x; int *p: printf("passws:"): Qets(buff): *p = x; 0 ... 37 38 39 40 41 42 43 ... 102
|