Перехват управления
Перехват управления можно осуществить двояко (существует по меньшей мере два пути перехвата управления, рассмотрим их):
Путь первый: проанализируйте уязвимую программу и определите какой из обработчиков будет текущим на момент переполнения и где именно расположен его SEH-фрейм (учитывая, что адрес последнего может быть непостоянным и зависящим от множества трудно прогнозируемых обстоятельств, например, от рода и характера запросов, предшествующих переполнению). Теперь придумайте как переполнить буфер так, чтобы затереть handler, подменив содержащийся в нем указатель на адрес shell-кода. Значение поля prev не играет никакой роли (shell-код ведь не собирается на халяву возвращать таким трудом захваченное управление!).
Путь второй: зарегистрируйте свой собственный SEH-фрейм. Как же мы сможем что-то зарегистрировать в системе, если еще не перехватили управления? – воскликнете вы. Это, что, шутка?! А вот и нет! Указатель на текущего обработчика всегда содержится в одном и том же месте – в первом двойном слове TIB'а, лежащего по адресу fs:[00000000h] и псевдо-функцией poke его вполне реально перезаписать. Пусть вас не смущает наличие сегментного регистра FS – вся память, принадлежащая процессу, отображается на единое адресное пространство и до TIB'а можно дотянуться и через другие сегментные регистры, например, через тот же DS, используемый процессором по умолчанию. Естественно, при адресации через DS, TIB будет располагаться совсем по другому смещению и, чтобы его узнать, придется прибегнуть к услугам отладчика. Вы можете использовать soft-ice, Microsoft Kernel Debugger или любой другой отладчик по своему вкусу.
Сначала необходимо определить значение селектора, загруженного в регистр FS. В soft-ice за это отвечает команда "CPU" (если soft-ice настроен правильно, то все основные регистры автоматически отображаются в верхней части окна). Затем, просматривая таблицу глобальных дескрипторов, содержимое которой выводит команда "GDI", находим соответствующий ему базовый адрес.
Для первого потока процесса на всех NT-подобных системах он равен FFDFF00h, а все последующие потоки уменьшают его на 1000h, т. е. мы получаем ряд указателей вида: 7FFDE000h, 7FFDD000h, 7FFDC000h…
В любом случае, протестировать вашу машину не помешает (вдруг какая-то из NT поведет себя иначе?). Протокол работы с отладчиком приводится ниже.
:cpu
Processor 00 Registers
----------------------
CS:EIP=0008:8046455B SS:ESP=0010:8047381C
EAX=00000000 EBX=FFDFF000 ECX=FFDFF890 EDX=00000023
ESI=8046F870 EDI=8046F5E0 EBP=FFDFF800 EFL=00000246
DS=0023 ES=0023 FS=0030 GS=0000
:gdt
Sel. Type Base Limit DPL Attributes
GDTbase=80036000 Limit=03FF
0008 Code32 00000000 FFFFFFFF 0 P RE
0010 Data32 00000000 FFFFFFFF 0 P RW
001B Code32 00000000 FFFFFFFF 3 P RE
0023 Data32 00000000 FFFFFFFF 3 P RW
0028 TSS32 80295000 000020AB 0 P B
0030 Data32 FFDFF000 00001FFF 0 P RW
003B Data32 00000000 00000FFF 3 P RW
Листинг 6 определение адреса указателя на текущий SEH-фрейм
Обратите внимание, FFDFF000h – это не адрес текущего SEH-фрейма. Это – указатель на фрейм. Сам же фрейм должен быть сформирован непосредственно в shell-коде, а в FFDFx000h занесен указатель на него (см. рис. 1).
Затем остается лишь совершить что-нибудь недозволенное или же пустить все на самотек, дождавшись пока исковерканная переполнением программа не вызовет исключения естественным путем и тогда наш SEH-обработчик немедленно получит управление. Остальное, как говориться, дело техники…