![]() |
|
![]() |
#1 | |||
Коварный тип
![]() ![]()
|
![]() Обзор защит программного обеспечения под Linux
Дети в сугробах шумно играют в Афганистан. Я через двор не пойду. Электрики вешают красные гирлянды в саду. Синие флаги на ветру. Поутру новый год - все снова. Снова, снова все танцуют, все играют в снежки. Взвожу курки - Так я стал предателем... Аукцион, "Новогодняя песня" альбом "Как я стал предателем", 1989 В этой статье я попытался сделать обзор Linuxа с точки зрения матёрого крякера, для которого нет ничего святого :-). OpenSource, без сомнения, штука хорошая и полезная. Но - отчего-то постоянно хочется кушать, в том числе и программистам и даже жителям Villabajo (как Вы думаете, почему у них постоянно грязная посуда ?). Представим, что Вы потратили кучу времени и ресурсов, разрабатывая последние несколько месяцев неплохую программу, и даже отчего-то имеете версию для Linuxа. Видимо, Вы хотите окупить затраченные усилия - проще говоря, заработать на своей программе кучу денег. И если Вы не опубликовали свой исходный код, в надежде заработать другими способами (кстати, рассказали бы тогда, какими именно), то необходимо как-то защитить Вашу программу от
"А кто такой автор, чтобы судить о столь высоких материях ?" - вполне справедливо может спросить читатель. А никто, в сущности. Так, сломал пару десятков программ и решил сделать что-нть полезное. См. эпиграф, в общем... Итак, в чём же главное отличие Linux от прочих OS ? В доступности исходного кода всего (ну, кроме разве что Вашей программы, стоящей мегабаксы), что работает (или не работает) на Вашей машине. Это делает написание защиты под Linux просто кошмаром - Вы не можете быть уверены, что функция strcmp из стандартной C run-time library - это действительно strcmp, а не её изменённый (обычно не в Вашу пользу) эмулятор. Вы не можете доверять ничему в такой операционной системе - вследствие доступности исходного кода любая её часть может быть модифицирована крякером для взлома Вашей программы, включая такие важнейшие компоненты, как ядро и run-time library. Ваша программа работает в самой агрессивной среде, какую только можно себе представить. В самом деле, если бы Вы могли изменить в Windows 9x, скажем, kernel32.dll (я имею в виду не ковыряние в машинном коде с помощью дизассемблера, хотя +tsehp использовал и такой метод - нет, просто редактирование исходного кода и последующая перекомпиляция - намного эффективнее, не правда ли ?) - разве было бы возможно существование защит вроде VBox ? Я настоятельно рекомендую хорошо подумать об этом как-нибудь на досуге. А пока у меня для Вас плохие новости - Ваша программа обязательно будет сломана. Это на самом деле "чиста" экономический вопрос. Представим, что Ваш программный продукт стоит 1000 $. Среднемесячная зарплата неплохого программиста из нашей сраны едва ли составляет 200 $. Таким образом, если какой-нибудь парнишка из Сибири затратит на слом Вашего творения меньше 5 месяцев - он будет экономически выгоден. Заметьте, что здесь ни слова не было сказано ни об операционной системе, под которой работает Ваш шедевр, ни о сложности и стоимости использованной системы защиты. Что же делать - идти в монастырь (хотя, в женский иногда наведываться - наверное, неплохая идея :-) ? Вы должны рассматривать защиту своего программного продукта не как 100% средство от Ваших головных болей, а всего лишь как средство, затрудняющее жизнь крякеру. Скажем, если Ваша защита остановит 9 крякеров из 10 - это очень неплохой результат. Конечно, не все 9 остановленных купят Вашу программу, но их будет явно больше, чем для случая, когда Ваша защита сломана 9тью из 10. Впрочем, возможно, что я жёстко не прав - я лично никогда не покупал программных продуктов :-) Итак, довольно пустых разговоров. Для начала я сделаю краткий обзор применяемых крякерами инструментов (хотя я склонен рассматривать Linux как один большой инструмент крякера). Отладчики Странно, но до сих пор никто не переписал SoftIce под Linux - я лично не вижу этому никаких препятствий. Более того, у меня есть веские доказательства, что это не просто возможно сделать, но и что его реализация будет значительно легче и проще, чем под всякие глючные операционные системы от не-будем-показывать-пальцем-кого. Так что пока с отладчиками намного хуже (для крякеров, для авторов защит - наоборот, хотя ведь отлаживаться всё равно как-то нужно), чем под Win32. Итак, что есть/годно к употреблению (см. также раздел Debuggers на LinuxLinks):
Без сомнения, IDA Pro. Также иногда можно на скорую руку использовать Biew, objdump (или даже ndisasm, дизассемблер от Netwide Assembler), но это несерьёзно. Мне больше неизвестны инструменты, которые позволяют дописывать к ним новые процессорные модули, загрузчики для нестандартных форматов файлов, а также plugins, облегчающие автоматический/интерактивный анализ. К тому же Ильфак грозился выпустить как раз версию под Linux (как иронично :-). Хит третьего сезона подряд, в общем. Ладно, на этот раз обойдусь без обычных наездов :-). strace или truss под UnixWare. Аналог regmon/filemon/BoundsChecker в одном флаконе. Ядро Linuxа имеет поддержку перехвата системных вызовов (функция ptrace). Т.е. можно запустить любой процесс как подлежащий трассировке через ptrace, и Вы сможете отследить все системные вызовы с их параметрами. Более того, после небольшой модификации эта функция (которая имеет доступ к виртуальной памяти трассируемого процесса) может быть использована, например, для run-time patching, внедрения кода в адресное пространство любого процесса, и так далее, и тому подобное. Пусть кинет в меня камень после всего описанного считающий, что Linux написали не крякеры :-). Если же серьёзно, то я пока не смог выдумать способа, как на userlevel противодействовать этой кувалде. Более того, я пока даже не могу узнать, что мой процесс в настоящий момент трассируется ядром ! Впрочем, пару решений можно придумать:
Ну, здесь даже и писать ничего нужно. Файловая система /proc может использоваться для таких целей. Файл maps используется как карта выделенной процессу виртуальной памяти, а файл mem является её отображением, можно сделать в нём seek на нужный адрес и легко сохранить необходимый участок в файл или куда-Вы-там-хотите. Простая программа на Perlе размером 30 строчек может быть использована для снятия дампа памяти Вашей драгоценной программы и сохранения её в файл. Намного проще, чем под Win32 :-) Шестнадцатеричные редакторы С этим тоже никогда не было больших проблем. Даже стандартный просмотровщик Midnight (или Mortal его второе имя) Comanderа умеет редактировать в шестнадцатеричном представлении. Для гурманов рекомендую Biew. Стандартные средства разработки Это не шутка. Для модификации ядра/runtime библиотек (а также для создания patchей и keygenов) нужен как минимум компилятор "C". Впрочем, я как-то пару раз писал keygenы на Perlе :-). Способы защиты Итак, я надеюсь, Вы ещё не уснули и впали в депрессию. Что же можно противопоставить всему вышеописанному ? Явно годятся далеко не все методы, используемые в Win32. Тем не менее, я смог придумать несколько, и я уверен, что это далеко не всё... Против BFD (GDB, objdump, strings etc) BFD - это библиотека для манипуляций с бинарными файлами. По счастливому стечению обстоятельств она используется также отладчиком GDB. Но для ELF формата (наиболее распространённый формат исполнимых файлов под всеми современными Unixами) она реализует неправильный алгоритм загрузки (подробности см. в моей статье "Как сделать Linux программы меньше"). Для этого можно использовать sstrip или мой ELF.compact (собственно, они делают одно и то же, просто когда я писал свой tool, я не знал о существовании sstrip). Преимущества:
Компрессия/шифрование кода программы Самый распространённый метод защиты под Win32. Под Linuxом этот метод имеет некоторые особенности. Самое большое отличие в том, как разрешаются ссылки на внешние модули. Статическая линковка Не имеющий аналогов под Win32 метод. При сборке программы все используемые ею библиотеки просто статически линкуются в один большой толстый модуль. Т.е. для запуска такой программы ничего дополнительно делать не нужно - такая программа самодостаточна. Преимущества:
Также возможно, что кто-нть станет бросать в меня кирпичи, утверждая, что статическая линковка де увеличивает необходимые ресурсы, и что якобы разделяемые библиотеки разделяют сегмент кода между всеми процессами, использующими их. Я тоже так думал, более того, то же самое утверждают большинство виденных мною учебников по Unix, но это не так. Самое простое доказательство - посмотрите атрибуты сегментов памяти, занимаемых разделяемыми библиотеками (файл maps в файловой системе /proc). Вы почти никогда не увидите атрибута s(hared). Почему ? Короткий ответ звучит так - из-за ELF. Дело в том, что при загрузке ELF файла происходит настройка его перемещаемых адресов - relocations. При этом сегменту памяти (даже если это сегмент кода) присваиваются атрибуты Read/Write, и если он при этом разделялся несколькими процессами, происходит копирование памяти при записи. Таким образом, разделение сегментов кода между процессами возможно только между родителем и его потомками (как результат функции fork). За подробностями обращайтесь к исходникам kernalа. Кстати, эти же аргументы применимы и к утверждению, что якобы "упаковка кода программы приводит к увеличению ресурсов, необходимых для запуска такой программы". Как видите, господа-слюниксоиды, как минимум в управлении виртуальной памятью Linux ничем не лучше поделок от M$ Декомпрессор/декриптор для статически слинкованных файлов ничем не отличаются от своих DOSовых предков, собственно, тут даже и говорить не о чем. Я в настоящий момент занят написанием такого зверя - просто добавляется код распаковки и изменяется точка входа на него. Динамическая линковка Под Win32 все программы являются динамически слинкованными как минимум с системными .DLL, реализующими API. Более того, такая линковка осуществляется опять же самой системой с помощью всё тех же функций API. Под Linuxом всё по-другому. Во-первых, программе совершенно необязательно быть динамически слинкованной. Во-вторых, программа сама должна заботиться о загрузке всех необходимых модулей и динамическом разрешении ссылок. Это, правда, вовсе не означает, что Вам каждый раз придётся писать код загрузки модуля в память. Среда исполнения (а не kernal - как в Win32) предоставляет реализацию динамического загрузчика по умолчанию - в терминах Linux его называют ELF interpretor, или просто interpretor (я не знаю точного перевода и вообще сомневаюсь в его наличии, так что буду просто использовать оригинальный термин). При линковке в программу помещается информация, что для её запуска необходимо после загрузки самой программы также загрузить interpretor, и передать ему управление. На этом загрузка файла на исполнение с точки зрения кернела заканчивается. Но Ваши головные боли только начинаются ! Итак, чем плох стандартный ELF interpretor ?
Да, что-то вроде старой недоброй Java - свой ассемблер, свой процессор, свой язык. Необязательно писать всю программу на таком чудовище - достаточно написать на нём макросы, и добавить к ним парочку, отвечающую за защиту (если я ещё не выжил из ума окончательно, есть такой неплохой редактор CRiSP, хорошо попивший у меня крови в своё время - в нём использовалась именно такая схема защиты. Ему правда всё равно не помогло :-). Если с достоинствами всё ясно, то недостатки несколько менее очевидны:
Я лично считаю, что если некая привинченная к порту железка (dongle) не несёт в себе часть функциональности самой программы, то она просто бесполезна, и программа, "защищённая" таким образом, легко может, и более того, просто обязана быть взломана. Такая затычка занимает целый порт, коих и так обычно недостаточно (я видел однажды гирлянду из пяти dongles, нанизанных друг на друга. Как говорил ослик Иа, "байтораздирающее зрелище" :-). Обратитесь к парням из UCL, они намного лучше меня разбираются в данном вопросе. Но мы имеем совершенно другую картину, если такая железка используется не просто в декоративных целях, а реально необходима для функционирования программы. Примеры таких аппаратно-программных комплексов:
Клиент-сервер Как развитие идеи из предыдущего пункта можно представить переложение части функциональности не на железо, а на программу. Но это не продаваемая пользователю программа, а установленная у Вас - сервер, умеющий выполнять часть функциональности, запрашиваемой клиентской частью, установленной на компьютере пользователя. Причём клиентская часть может распространяться бесплатно, а деньги брать именно пользование сервером. Пожалуй, тогда несанкционированное копирование такой клиентской программы просто не имеет смысла... Впрочем, это уже сфера компетенции хакеров, а не крякеров :-) Практическая реализация Суха теория, да древо жизни пышно зеленеет... Почти всё вышеописанное имеет один недостаток - это всё плоды моего не в меру воспалённого сознания. Так что пару недель назад я таки реализовал некоторые из вышеизложенных идей - я написал настоящий packer/runtine unpacker для Linuxа (первый, насколько я знаю. Дело в том, что UPX и ExePack не являются настоящими unpackerами, какие мы привыкли видеть под DOS/Win32. Они просто распаковывают исполнимый файл во временную директорию, запускают его как обычный файл, а по окончании работы удаляют. Святая наивность в действии...). Я имею версию как для Linux с ядром 2.2, так и под FreeBSD 3.4 (и есть подозрения, что он будет работать также и с версиями FreeBSD 3.X). Распаковка происходит в моём собственном ELF interpretorе, он также не поддерживает GDB и LD_PRELOAD. Вследствие этого, пока мой packer умеет сжимать только файлы с ELF interpretorом. Справедливости ради нужно заметить, что таких в Red Hat 6.1 около 96 %. Кстати, мой packer/unpacker также продаётся :-) Для демонстрации его возможностей я написал простой crack.me под Linux (опять же первый, насколько я знаю), упакованный моим packerом. Он требует Linux с ядром 2.2 и glibc 2.1 (да да, я намеренно использую динамическую линковку с C RunTime library- как наихудший вариант для применения envelop-защиты наподобие моего packerа). Инсталляция очень проста - поместите файл rp-linux.so.0 (собственно это и есть мой ELF interpretor) в директорию /lib и дайте ему права на исполнение. Эти операции необходимо проделать от пользователя root. А дальше запускайте мой crack.me (под каким хотите пользователем. Вообще для параноиков, в любом shell scriptе видящем трояна, я рекомендовал бы использовать отдельно стоящую машинку без сетевой карты с только что сделанным backupом). Смысл заключается в том, чтобы заставить его сказать что-нть кроме "You are wrong". Для этого Вы должны ввести пароль. Пароль имеет длину больше восьми символов, что делает простой перебор несколько неэффективным (я надеюсь :-). |
|||
![]() |
![]() |
Опции темы | Поиск в этой теме |
|
|
![]() |
||||
Тема | Автор | Раздел | Ответов | Последнее сообщение |
[Статья] Обзор аккаумуляторов для внешней выспышки. [Мой личный обзор] | Serberg | Фотоаппараты | 1 | 31.03.2011 10:24 |
Видео самоучитель по Debian Linux и Ubuntu Linux | Serberg | Linux | 8 | 13.06.2010 00:40 |
Масштабный обзор нетбуков ... [обзор] | Serberg | Ноутбуки | 6 | 19.11.2008 19:47 |
Обновление Linux [статья] | Serberg | Linux | 0 | 04.06.2008 22:04 |
Как подключить Linux к Internet [статья] | Serberg | Linux | 0 | 04.06.2008 21:19 |