Трюки от Крыса 20h: юбилейный выпускКрис Касперски Наверное, все слышали о языке brainfuck, программирование на котором представляет мазохизм в стиле «хрен напишешь» и «хрен прочтешь». Однако Си с его гибким синтаксисом и мощным препроцессором позволяет трахать мозги не только секса ради, но и для дела. Обфускация исходных текстов – это конкретная тема, которой и посвящен юбилейный выпуск трюков. Препроцессор Стандартный препроцессор языка Си, конечно, не такой мощный как, например, у MASM'а, но для большинства повседневных задач его вполне хватает. Макросы — великая вещь, но если шагнуть дальше, то можно полностью переопределить синтаксис языка. И писать программу… стихами. Пускай потом коллеги разбираются, откуда это взялось и что оно здесь делает. Классический пример подобного творчества приведен ниже: Программа на языке Си Естественно, чтобы это чудо откомпилировалось, необходимо заблаговременно создать определения для всех ключевых слов. В принципе, определения могут быть любыми (смотри ниже). Стоит только разбросать их по разным включаемым файлам (а в типичном проекте их сотни), и никакой хакер не сообразит, какая свинья его ожидает. Определения лексем Собрав оба листинга воедино и откомпилировав файл, мы получаем вполне работоспособную программу, запрашивающую число и выводящую его в обратном порядке. Как видно, трансляция сишного кода в стихи осуществлялась тривиальной контекстной заменой, причем, часто употребляемые слова английского языка (and, a, the) определены через пробел. Короче, идея понятна. Проблема в том, что практически все компиляторы позволяют вывести результат работы препроцессора в файл (у MS VC за это отвечает ключ /P), проанализировать который не составит никакого труда. Чтобы трахнуть мозги (мы же хотим трахнуться, правда?), необходимо использовать более продвинутые методики. Например, хитрые математические алгоритмы (типа сходящихся рядов), реализованные в виде развернутых циклов. Заслуга препроцессора в том, что он позволяет представить эти ряды в псевдографической форме, высаживающей хакеров на реальную измену. Это не шутка! Программа для расчета числа «пи». Да-да, именно расчета, а не вывода готовой константы посредством сопроцессора. Чем больше круг — тем выше точность расчетов.
Фундамент языкаСи трактует перенос строки, как пробел, что открывает практически неограниченный простор для «графитти», даже безо всякого препроцессора. Для достижения поставленной цели хватит и фундаментальных конструкций самого языка. Программа, изображенная ниже, похожа на первоапрельскую шутку, которой она по сути дела и является. Код исправно компилируется и работает, выводя на экран число «минус два», а если последнюю строку заменить на «1 -+- + -+- 1», мы получим… ноль. Графитти на чистом Си
И в чем прикол? А в том, что никакой программист не сможет быстро сказать, какой результат мы получим на выходе. К тому же, подобные «художества» автоматически трактуются человеком как комментарии, что позволяет скрыть часть функционала. Не говоря уже о том, что после наведения «порядка» и удаления всех «ненужных» комментариев программа просто развалится со всеми вытекающими отсюда… Реальный хардкорСтрого говоря, приведенные выше примеры обфускацией не являются и при наличии мозгов снимаются в два счета. Хорошо, держите программу из трех строк, над анализом алгоритма работы которой можно трахаться часами, причем математика здесь и не ночевала. Всего лишь тривиальные операции с указателями: Программа-головоломка (это только с виду она простая…)
Вопрос на засыпку — что это такое, как оно работает и что делает? Если кто думает, что программа выводит строку «Hello, world», он глубоко ошибается. Благодаря коварному багу, на экране отображается только «Hello», а «world!» появляется только после удаления пробела между запятой и миром. Кстати говоря, 128 байт исходного текста транслируются оптимизирующим компилятором Microsoft Visual C++ в 128 байт машинного кода. Мистика! Совпадение размеров, разумеется, чистая случайность (в 1985 году, где создавалась эта программа, никаких «визуалов» вообще не существовало), но черной магии здесь и без того хватает. Концентрация языковых «извращений» просто поражает! Начнем с того, что программа рекурсивно вызывает сама себя посредством документированной, но малораспространенной функции execlp(). Это серьезно затрудняет отладку, поскольку отладчики на работу с дочерними процессами как-то не рассчитаны, и пошаговая остановка тихо кончается, не достигая цели. Точки останова, установленные в материнском процессе, на дочернее потомство, естественно, не распространяются. Заниматься оплодотворением приходится самостоятельно, прибегая к рукоблудному методу инъекции бряков непосредственно в тело исполняемого файла, что съедает все свободное и несвободное время, без всякой надежды на скорый успех. Во многих случаях обфускация снимается декомпиляцией двоичного файла в Си-код, повторной компиляцией декомпилированного файла с максимальным уровнем оптимизации и последующей финальной декомпиляцией. В качестве декомпилятора обычно используется Hex-Rays, который люди берут так же, где и саму ИДУ – не потому, что Hex-Rays хорош, а потому, что другие декомпиляторы еще хуже. Однако, в нашем случае Hex-Rays генерирует кривой код, повторная компиляция которого разваливает программу. Причем, понять, где она валится, практически невозможно – алгоритм в целом тривиален, но при переходе на частности выясняется, что любая конкретно взятая операция (или даже группа операций) в изоляции от остальных лишена смысла, и декомпозиция идет лесом. Программа представляет собой монолитное сооружение, своеобразный магический кристалл с множеством граней, многократно отражающихся друг в друге, и совокупность этих отражений реализует алгоритм, расшифровав который мы узнаем, что программа считает первый аргумент командой строки, укорачивает его на один символ и выводит результат на экран. Поскольку пробел трактуется как разделитель аргументов, то оригинальный вариант программы оказывается неработоспособен, и нам необходимо либо удалить пробел после запятой, либо заключить строку в кавычки, либо сделать и то, и другое сразу: Результат работы оптимизирующего компилятора с последующей декомпиляцией Hex-Rays – те же яйца, только в профиль |