Читаем Защита от хакеров корпоративных сетей полностью

Можно ли теперь, зная адрес начала стека, точно определить в нем место размещения управляющего кода? Нет, нельзя!

Но для разумной оценки адреса области размещения управляющего кода можно увеличить ее размер способом, аналогичным способу последовательности команд nop. В начале работы программы все регистры были очищены командами xor, поэтому в качестве заполнителя буфера можно воспользоваться одной из команд работы с регистром, которая не окажет влияния на работу программы. Например, команда inc 0 %>EAX, машинный код представляется шестнадцатеричным байтом 0x41, увеличивает значение регистра EAX на единицу. В управляющем коде регистр EAX перед использованием обнуляется. Поэтому при размещении перед первой командой jmp команд inc %EAX управляющий код будет прекрасно работать. В действительности в управляющем коде можно разметить столько команд inc %EAX, сколько захотим. В данном случае команда inc %EAX эквивалентна команде nop. Поэтому выберем размер управляющего кода равным 1000 байт и заполним его символами 0x41, другими словами, командой inc%EAX.

Определенная в программе переполнения буфера символическая константа OFFSET – предполагаемое смещение области размещения управляющего кода в стеке. В программе ему присвоено символическое значение ESP+1500.

Вот так в конечном счете выглядят управляющий код и программа переполнения:

#include

#include

/***** Shellcode dev with GCC *****/

int main {

__asm__(”

jmp string # jump down to

Это команды, с которых фактически начинается программный код полезной нагрузки. Сначала обнуляются используемые в программе регистры, чтобы находящиеся в них данные не повлияли на работу управляющего кода:

xor %EBX, %EBX

xor %EDX, %EDX

xor %EAX, %EAX

# Now we are going to set up a call to the

write

#function. What we are doing is basically:

# write(1,EXAMPLE!\n,9);

# Syscall reference: /usr/include/asm/unistd.h

#

# write : syscall 4

#

Почти всем системным вызовам Linux параметры передаются через регистры. Параметры системного вызова передаются через следующие регистры:

• ECX: адрес записываемых данных;

• EBX: дескриптор файла, в рассматриваемом случае используется дескриптор стандартного файла вывода stdout;

• EDX: длина записываемых данных.

Теперь в регистр EBX записывается нужный дескриптор файла. В данном случае дескриптор стандартного файла вывода stdout равен 1:

popl %ECX # %ECX now holds the address of our string mov $0x1, %EBX

Затем длина записываемой строки записывается в младший полубайт регистра %EDX:

movb $0x09, %dl

Перед обращением к системному вызову следует сообщить операционной системе, какой системный вызов должен быть выполнен. Достигается это записью номера системного вызова в младший байт регистра %EAX – %al:

movb $0x04, %al

Теперь операционная система выполняет системный вызов, номер которого записан в регистр %al.

int $0x80

В конце программы нужно выполнить системный вызов завершения работы или #syscall 1. Системному вызову exit в данном случае параметры не нужны, поэтому фрагмент кода выглядит следующим образом:

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже