Перед завершением своей работы функция передает вызвавшей ее программе код возврата в регистре EAX, чтобы та знала об успешном или неуспешном выполнении функции. Чтобы узнать ассемблерную реализацию фрагмента программы, отвечающего за передачу кода завершения, оттранслируем и дизассемблируем следующую программу:
$ cat ret.c
int main()
{
return(0);
}
$ gcc ret.c -o ret
$ gdb ./ret
(gdb) disas main
Dump of assembler code for function main:
0x8048430
: push %EBP
0x8048431
: mov %ESP,%EBP
0x8048433
: mov $0x0,%EAX <���– here it is :)
0x8048438
: pop %EBP
0x8048439
: ret
0x804843a
: mov %ESI,%ESI
0x804843c
: nop
0x804843d
: nop
0x804843e
: nop
0x804843f
: nop
End of assembler dump.
(gdb)
Далее, вместо выполнения оператора возврата return (значение) пропустим его и перепишем значение ESP в регистр EAX. Таким способом значение регистра ESP может быть назначено переменной. Вот пример программы, отображающей содержимое регистра ESP:
–get_ESP.c–
unsigned long get_ESP(void)
{
__asm__(“movl %ESP,%EAX”);
}
int main()
{
printf(“ESP: 0x%x\n”, get_ESP());
return(0);
}
–get_ESP.c–
Можно ли теперь, зная адрес начала стека, точно определить в нем место размещения управляющего кода? Нет, нельзя!
Но для разумной оценки адреса области размещения управляющего кода можно увеличить ее размер способом, аналогичным способу последовательности команд 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 в данном случае параметры не нужны, поэтому фрагмент кода выглядит следующим образом:
Читать дальше
Конец ознакомительного отрывка
Купить книгу