Ключевые слова:elf, exec, optimization, assembler, (найти похожие документы) From : Red Plait <redplait@usa.net>
Subj : уменьшение размера ELF программ
-------------------------------------------------------------------------------
Как сделать Linux программы меньше
Red Plait <redplait@usa.net>
I'm ahead, I'm the man
I'm the first mammal to wear pants, yeah
I'm at peace with my lust
I can kill 'cause in God I trust, yeah
It's evolution, baby
Pearl Jam, "Do the Evolution"
Данный опус описывает два способа уменьшения размера ELF программ
под Линух, однако эти способы не специфичны для Линукса, и работают
также под FreeBSD (возможно, они будут работать и под некоторыми
другими Unixes на платформе Intel x86).
Предисловие
В наши дни почти никто не задумывается о размере программ. И это
очень печальное последствие монополии Микрософт - вместо того,
чтобы думать об эффективном использовании имеющегося оборудования,
эти парни пишут вс╦ более прожорливые (но вовсе не вс╦ более
лучшие) монстрообразные программы, вынуждая конечных пользователей
каждый год покупать вс╦ более новые компьютеры (более мощные и
дорогие). Меня уже давно тошнит писать программы под "операционные
системы" этой фирмы - да и глупо заниматься оптимизацией программ
для платформы, которая предназначена исключительно для того, чтобы
заставить пользователя купить более мощную машину. Так что мы будем
рассматривать альтернативные платформы - Linux & FreeBSD. Поэтому
Вы должны иметь машину с установленным на ней одним из этих
дистрибутивов. На этом требования и заканчиваются.
Nasm improvements
В самом деле, один из самых простых способов уменьшить размеры
программы - писать е╦ на ассемблере. Я лично считаю одним из лучших
ассемблеров под Unixes на платформе x86 Nasm, взять можно на
[2]http://www.cryogen.com/Nasm/. Однако при создании ELF-файлов
(самый распростран╦нный формат исполнимых файлов под Unix) он
вставляет в каждый объектный файл секцию с комментариями, что
данный файл был произвед╦н Nasmом версии такой-то. Но ведь я и так
знаю это ! Зачем мне нужна в каждом объектном файле такая секция ?
Кроме того, содержимое всех этих секции аккумулируется при линковке
в секции комментариев исполнимого файла ! В общем, я "доработал"
файл outelf.c из версии 0.98 (последняя на сегодняшний момент),
отвечающий за генерацию объектных файлов в ELF формате, так что
теперь туда не помещается секция с комментариями. Я надеюсь, парни
из команды разработчиков Nasmа на меня не сильно за это обидятся -
мы и так знаем, что их продукт лучший, а теперь он стал даже ещ╦
лучше - он генерирует файлы меньшего размера ! Но если Вам вс╦-таки
нравится иметь строку с copyrights, вы можете закомментировать
строчку
#define RP_NO_COMMENT
и вс╦ будет как раньше.
Кроме того, в выходных файлах есть и ещ╦ кое-какая лишняя
информация, а именно - информация о локальных символах модуля. Вы
вс╦ равно не можете их использовать в процессе линковки - они
помещаются в таблицу символов только для корректного создания
relocations, и в дальнейшем могут быть безболезненно удалены. Более
того, если Вы созда╦те разделяемую библиотеку или готовый
исполнимый файл, Вы можете удалить всю символьную таблицу ! Но если
Вы созда╦те простую библиотеку, нет инструмента для удаления
информации о локальных символах - она будет совершенно бесполезно
занимать место на Ваших дисках (впрочем, мне думается, что создание
такого инструмента вполне возможно с помощью библиотеки bfd из
пакета binutils. Возможно, я даже когда-нть займусь этим). Так что
я совешил ещ╦ некоторое насилие над Nasmом - если Вы определите
макрос (уже определ╦н в мо╦м патче)
#define RP_ONLY_GLOBAL
то перед записью ELF объектника будет перестроена таблица символов
(и строковая таблица) чтобы не включать в себя локальные символы.
Установка патча очень проста - переписываете исходный файл моим и
перекомпилируете - вс╦...
Платформы: любой Unix на платформе Intel x86 процессоров
(естественно, если Вы сможете собрать на н╦м сам Nasm). Проверялось
на Nasm 0.98 на
* FreeBSD 3.1 & 3.4
* Red Hat Linux 5.1 & 6.1
* Caldera OpenLinux 2.2
* UnixWare 7.1
* SCO OpenServer 5.0.5
PS: можете считать это моим началом работы над проектом модификации
Nasmа (см. раздел [3]Projects)
Написание Linux kernel modules на ассемблере
Что-то нигде в Сети я ещ╦ не видел документации, описывающей, как
создавать kernel modules не с помощью ассемблера, а целиком на
ассемблере - вообще без использования компилятора C (правда, нам
потребуется линковщик). Тем не менее я обнаружил, что это вполне
возможно. Для проверки был написан (на Nasmе) абсолютно бесполезный
модуль, который умеет только сообщать о факте своей загрузки и
выгрузки. Чтобы пересобрать его для Вашей версии кернела, Вам
потребуется найти в файле заголовков
/usr/include/linux/version-up.h значение макроса UTS_RELEASE
(версия кернела) и вписать его в определение константы kern_ver
моего файла a.asm. Собственно, в этом нехитром действии и кроется
причина отсутствия модулей кернела, написанных целиком на
ассемблере - все файлы заголовков, определяющих структуры и функции
кернела, расcчитаны на использование языка C (хотя мне попадалась
пара экспериментальных модулей, написанных на C++). Полностью
отсутствует инфраструктура, необходимая для полноценного
использования ассемблера.
Для сравнения результатов тот же самый модуль был написан более
традиционным способом - на C. Результаты таковы: размер Cишного
модуля 1064 байта, размер ассемблерного модуля 748 байт.
Комментарии излишни...
ELF compact
Хм, а как быть с уже скомпилированными исполнимыми файлами ?
Действительно ли ELF файлы не содержат ничего лишнего ? На изучение
этого вопроса я потратил около двух недель (в основном просматривая
исходный код загрузчика ELF файлов на исполнение в Linux кернеле и
ELF interpretorа). И в результате выяснились очень интересные вещи.
ELF формат имеет два типа секций - одни обычные, используемые
линковщиком и иже с ним, а другие - так называемые программные,
т.е. которые используются кернелом при загрузке файла на
исполнение. Более того, если, скажем, для объектных файлов и
разделяемых библиотек необходимы первые (символьные) - по
достаточно очевидным причинам, ведь эти файлы предназначены для их
дальнейшей обработки (линковка или динамическая загрузка), то для
обычных исполнимых файлов необходимы ТОЛЬКО программные секции. Ни
код запуска ELFов, ни ELF interpretor в своей работе не используют
символьных секций ! Тем не менее, линковщик честно помещает их в
каждый генерируемый файл. Это цель для приложения усилий номер раз.
Я написал сначала программку elf_dump, которая просто распечатывает
ELF заголовок и заголовки секций обоих типов (я так и не смог
заставить objdump показывать все секции файла, включая программные
секции). И выяснились ещ╦ более забавные вещи - оказывается уже
упомянутая секция с комментариями, а также секции с символьной
таблицей (та, что удаляется, когда Вы делаете strip) вообще не
грузятся кернелом для исполнения. Т.е. программа может легко
обойтись без них. Это наша цель номер два.
Для проверки концепции за два часа на свет появилась другая
программа - elf_compact. Назначение е╦ очень просто - она открывает
файл, проверяет, что открытый файл - ELF, что он исполнимый и
отрезает у него вс╦ лишнее, переписывая вс╦ нужное в файл с таким
же именем, что и основной, с суффиксом ".cmpt". И она работает !
Более того, она работает не только на Linux, но и с программами от
FreeBSD (на что я, честно говоря, даже не рассчитывал).Я даже
собрал обе программы под Win32 с помощью Visual C++ 6.0 -
получилось что-то вроде cross-optimizatorа. Она может также служить
заменой strip. И при всех е╦ достоинствах исходный код имеет
размеры всего 5 Kb ! По всем законам жанра такая хакерская утилита
просто обязана быть написана в машинных кодах, в крайнем случае на
ассемблере, но, поскольку я очень ленив, прид╦тся Вам
довольствоваться переносимым C
Неочевидные следствия
Оказалось, что, несмотря на то, что выходные файлы отлично
запускаются и работают, и вообще кернел не испытывает каких-либо
неудобств от ампутации мусора, ни отладчик gdb, ни утилиты из
binutils не могут жить без символьных секций и отказываются иметь
с такими файлами дело. Т.е. совершенно непроизвольно на свет
появился также простейший ELF protector (насколько я знаю, первый в
сво╦м роде - по крайней мере ни я, ни те люди, которым я дал эту
программу для тестирования, ничего аналогичного под Linux не
знают). Это тем более иронично, что до этого я в основном ломал
программы под Linux/Unix ! Краткий перечень сломанного прилагается
(IMHO, далеко не полный - что-то с памятью в последнее время стало
совсем плохо - весна, однако...):
* SCO OpenServer 5.0.x (практически все версии - 5.0.2, 5.0.4,
5.0.5) - сломал вс╦, что было на их инсталляционных дисках
* UnixWare 7.0 & 7.1 - отломано ограничение на максимальное
количество сетевых подключений
* Oracle 8.0.5 for UnixWare - на предмет максимального числа
подключений
* StarOffice 5.0 for Linux - ещ╦ когда StarDivision не купила Sun.
Инсталлировался на любой серийный номер
* InterBase 5.6 for Linux - написан генератор лицензий
* YardSQL for FreeBSD - написан генератор файлов лицензий
* SOLID for FreeBSD & for Linux - отломлена проверка на окончание
trial-периода и на максимальный размер базы и число подключений
* Yandex Lite - аж целых три версии for Linix и одну for FreeBSD -
устранение всех ограничений демо-версии
* C-Forge for Linux (кстати, хорошая среда разработки) - снятие
trialа
* VmWare 1.0 for Linux - как давно это было...
* Jaguar v3.4 & v4.0 for Linux - прога для химических расч╦тов
* Chili!Soft ASP for Linux
* Fujitsu C/C++/Fortran Express v1.0
И вот на старости лет угораздило написать защиту. Впрочем, я не
совсем болен на голову, и уже на следующий день наш╦л способ, как
загружать такие "оптимизированные" исполнимые файлы в IDA Pro - я
написал для них свой loader. Он грузит такие файлы правильно - т.е.
не так как описано в спецификации ELF формата, а так, как это
делает Linux кернел. Но ! когда я обратился к Гильфанову на предмет
получить исходные коды его загрузчика ELFов, в ответ мне было
сказано, что "папуасам и хакерам никогда я исходные коды не дам,
даже не проси !" Так что
1. Я обиделся на Гильфанова (не только из-за этого факта - подобных
проявлений истинного дружелюбия с его стороны уже было множество)
2. Loader вряд ли обладает всей функциональностью оригинального ELF
loaderа, в частности, понимает ТОЛЬКО x86 файлы. Впрочем, у меня и
нет другого оборудования
3. Поскольку я затратил слишком много времени, нервов и ресурсов на
выяснение сначала как это вс╦ работает, на бесплодную переписку с
Ильфаком и Eric Youngdale (автор кода загрузки ELFов в Linux
кернеле, и по совместительству автор ELF interpretorа), а затем
ещ╦ и на reverse engeneering творений Ильфака, мой loader не будет
выложен как OpenSource, как все прочие инструменты. Если он Вам
нужен - обратитесь к Ильфаку. Или ко мне - у меня будет дешевле
(ведь мне не нужно поддерживать бельгийский уровень жизни, как мне
Ильфак однажды сказал :-)
Я же планирую написать несколько более усложн╦нный вариант
настоящей защиты под Linux (кто же лучше крякера сможет это сделать
?), возможно с применением модулей в кернеле и есть ещ╦ пара идей
(пока не скажу) - в общем, что-то вроде VBoxа под Linux...
Критикам
Когда Вы будете источать праведные вопли о том, что якобы тоже
самое, что и ELF compact, умеют делать утилиты из пакета binutils,
перечитайте ещ╦ раз предыдущую главу, где написано ч╦рным по белому
русскими буквами - что утилиты из binutils не умеют общаться с
выходными файлами ELF compactа. Даже если быть полным ламером, но
мыслить логически - как утилиты binutils могут так исправить файлы,
что потом сами не смогут с ними работать ? Кроме того, настоятельно
рекомендую почитать документацию на ELF - возможно, Вы наконец
пойм╦те разницу между символьными и программными секциями...
Ссылки
* [4]Netwide Assembler home page
* [5]Linux Assembly Project - проект нашего соотечественника
Константина Болдышева по программированию на ассемблере под Linux.
Содержит множество полезной информации и ссылок
* [6]ELF Kickers. Обратите особенное внимание на утилиту ssrtip -
да, оказывается я не оригинален :-(
* [7]Описание ELF формата
* [8]Как сделать Ваши Linux-программы ещ╦ меньше :-)
* [9]Assembly Programming Journal
* [10]Исходные тексты программ, упоминаемых в статье
Жалобы и предложения
можно отправлять автору по адресу [11]redplait@usa.net. На глупые
вопросы типа "Что такое FreeBSD ?" или "где мне взять что-либо из
описанного в этой статье ?" я не отвечаю. Если же Вы нашли bug,
имеете конструктивные идеи или уверены в моей неправоте - всегда
открыт к общению. Также большая просьба - не присылайте мне
десятимегабайтных (притом несжатых) файлов в качестве
доказательства чего бы то ни было.
Ну и если Вы хотите защитить свои программы под Linux/FreeBSD (или
наоборот, сломать чужие :-) - теперь Вы знаете, к кому
обратиться...
Кроме того, автор не нес╦т ответственности за последствия работы
его программ...
4. http://www.cryogen.com/Nasm/
5. http://www.linuxassembly.org/resources.html
6. http://www.muppetlabs.com/~breadbox/software/elfkickers.html
7. http://www.muppetlabs.com/~breadbox/software/ELF.txt
8. http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
9. http://asmjournal.freeservers.com/