Самый маленький эльф Крис Касперски ака мыщъх Хакер, номер #090, стр. 090-110-4 Linux использует fastcall-соглашение о передаче параметров. Это значит, что номер системного вызова помещается в регистр EAX, параметры передаются слева направо через регистры EBX, ECX, EDX, ESI, EDI, EBP. Если системный вызов принимает больше шести параметров, то они передаются со структурой, указатель на которую заносится в EBX. Передача управления происходит путем вызова прерывания "INT 80h". Разумеется, это только общая схема, и на практике постоянно приходится сталкиваться с отступлением от правил. Общение с системными вызовами напоминает хождение по минному полю. Допустим, мы хотим вызвать write (системный вызов). Для начала необходимо узнать его номер. Системные вызовы перечислены в файле /usr/include/sys/syscall.h. В BSD-системах номера присутствуют сразу, а вот Linux нас отсылает к файлу /usr/include/bits/syscall.h, в котором номеров нет, зато есть нисходящие определения. Лезем в man ("man 2 write") и смотрим, какие параметры этот вызов принимает. Ага, write(int d, const void *buf, size_t n_bytes). То есть мы должны занести #4 в EAX, файловый дескриптор – в EBX, указатель на выводимую строку – в ECX и количество выводимых байт – в EDX, после чего вызвать прерывание "INT 80h". BSD-системы используют гибридный механизм: прерывание "INT 80h" и "FAR CALL 0007h:00000000h". Номера системных вызовов так же, как и в Linux, помещаются в регистр eax, а вот параметры передаются через стек по Си-подобному соглашению (то есть первым заносится крайний правый параметр, последним в стек ложится фиктивный dword, а стек чистит за собой вызывающий код). Поскольку номера базовых системных вызовов в обеих системах совпадают, можно исхитриться и написать программу, работающую под обеими операционными системами: Linux не обращает внимания на стек, а BSD — на регистры, что позволяет нам продублировать параметры и там, и там. Естественно, это увеличивает размер программы, но, к нашему счастью, FreeBSD позволяет эмулировать Linux-интерфейс. Достаточно дать команду "brandelf -t Linux имя_файла", после чего нам останется только запустить его! А Linux, в свою очередь, умеет эмулировать BSD, SunOS и еще много чего! Но довольно слов, переходим к делу! Перепишем нашу программу, чтобы она выводила приветствие через системный вызов write без использования libc. Стартовый код в этом случае исчезает, и точкой входа в программу становится метка "_start", объявленная как global. Ну, а сама программа выглядит так: Ассемблерная программа elf_80h.S .text .global _start _start: movl $4,%eax ; // системный вызов #4 "write" movl $1,%ebx ; // 1 - STDOUT movl $msg,%ecx ; // смещение выводимой строки movl $len,%edx ; // длина строки int $0x80 ; // write(1, msg, len); movl $1, %eax ; // системный вызов #1 "exit" |