Обзор упаковщиков исполняемых файлов под UNIX

Автор: (c)Крис Касперски ака мыщъх

Упаковщики исполняемых файлов часто используются для затруднения анализа программы и препятствия взлому, но ни один упаковщик не свободен от ошибок, которые высаживают пользователей на конкретный геморрой, вынуждая распаковывать программу только затем, чтобы ее было можно запустить! Покажем, как "снимаются" наиболее популярные упаковщики: ELFCrypt, UPX, Burneye и Shiva.

Введение

Долгое время единственным упаковщиком исполняемых файлов, существующим в UNIX, был легендарный UPX, содержащий встроенный декомпрессор и распаковывающий файлы без труда. Но сейчас ситуация изменилась и упаковщики прут, как грибы после дождя. Ими охотно пользуются разработчики коммерческих программ с закрытым кодом, не задумываясь о том, какие проблемы они причиняют своим пользователям.

Упакованный файл потребляет намного больше оперативной памяти, а на некоторых UNIX-клонах вообще отказывается запускаться или работает нестабильно. В первую очередь это касается *BSD (основная масса упаковщиков ориентирована на Linux) и экзотических систем с экспериментальными ядрами наподобие Hurd. В результате от упаковщиков/протекторов стремится избавится даже тот, кто вообще не собирался ничего ломать!

В этой статье, ориентированной на продвинутых пользователей, мы покажем как победить наиболее популярные упаковщики, не будучи хакером и не имея навыков в дизассемблировании или отладке.

ELFCrypt

Происхождение: создан индийским студентом по прозвищу JunkCode (junkcode@yahoo.com). Распространяется в исходных текстах на бесплатной основе: www.infogreg.com/source-code/public-domain/elfcrypt-v1.0.html.

Страничка создателя ELFCrypt

Рисунок 1. Страничка создателя ELFCrypt'а на programmer's heaven.

Описание: простейший шифровщик (не упаковщик!) elf-файлов, шифрующий файл по XOR случайно генерируемым ключом. Присваивает кодовой секции атрибут writable и не убирает его после завершения расшифровки (что может приводить к некорректной работе программ, закладывающихся на невозможность модификации кодовой секции). Остальные секции (и секция данных, в том числе!) остаются незашифрованными и все имеющиеся в них текстовые строки видны "как есть". Не содержит никаких антиотладочных приемов, но подкладывает две больших свиньи дизассемблерам: "забывает" скорректировать метку _start и размещает свой код в секции extern, истинное содержимое которой IDA Pro отображает только в режиме ручной загрузке при выбранной опции: "Force using of PHT instead of SHT".

Распаковка: загружаем файл в hiew, двойным нажатием ENTER'а переходит в режим дизассемблера, давим <F8> для отображения заголовка, переходим в точку входа по <F5> и видим следующий код:

.080495DC: EB02       jmps .0080495E0    ; переходим на расшифровщик
.080495DE: 06         push es            ; \ мусор, оставленный...
.080495DF: C6         ???                ; / ...транслятором ассемблера
.080495E0: 60         pushad             ; сохраняем все регистры в стеке
.080495E1: 9C         pushfd             ; сохраняем флаги в стеке
.080495E2: BEC0820408 mov esi, 0080482C0 ; начало расшифровываемого фрагмента
.080495E7: 8BFE       mov edi, esi       ; EDI := EDI (расшифровка на месте)
.080495E9: B978000000 mov ecx, 000000078 ; кол-во двойных слов для расшифровки
.080495EE: BBBD03CC09 mov ebx, 009CC03BD ; ключ расшифровки
.080495F3: AD         lodsd              ; читаем очередное двойное слово <-----+
.080495F4: 33C3       xor eax,ebx        ; расшифровываем через xor             |
.080495F6: AB         stosd              ; записываем результат на место        |
.080495F7: E2FA       loop .0080495F3    ; мотаем цикл -------------------------+
.080495F9: 9D         popfd              ; восстанавливаем флаги из стека
.080495FA: 61         popad              ; восстанавливаем все регистры
.080495FB: BDC0820408 mov ebp, 0080482C0 ; адрес оригинальной точки входа (OEP)
.08049600: FFE5       jmp ebp            ; передаем управление расшифрованному коду

Листинг 1. Дизассемблерный листинг окрестной точки входа программы, зашифрованной ELFCRypt'ом.

Запоминаем (записываем на бумажке): адрес начала расшифровываемого фрагмента (грузится в регистр ESI), кол-во расшифровываемых двойных слов (грузится в регистр ECX), ключ расшифровщика (грузится в регистр EBX) и адрес оригинальной точки входа (грузится в регистр EBP).

Нажимаем <F5> (goto) и вводим адрес начала расшифровываемого фрагмента с точкой впереди (точка указывает hiew'у, что это не смещение внутри файла, а виртуальный адрес), в данном случае - ".80482C0". Переходим в hex-режим двойным нажатием ENTER'a, разрешаем редактирования по <F3> и нажимаем <F8> (xor) - hiew запрашивает маску шифрования, которую необходимо вводить в hex-виде с учетом обратного порядка байт на x86, в результате чего 09CC03BDh превращается в BD 03 CC 09 (а совсем не в DB 30 CC 90, как иногда поступают начинающие), после чего нажимаем <F8> ECX раз. Чтобы не сбиться, можно отталкиваться от адресов начала и конца блока, прекращая давить <F8> только тогда, когда курсор сместиться на ECX двойных слов (не байт!) относительно начальной позиции.

Расшифровка файла

Рисунок 2. Расшифровка файла, обработанного ELFCrypt'ом в hiew'e.

Пара ремарок: при входе в режим редактирования hiew перестает отображать виртуальные адреса, переходя на физические смещения внутри файла, в результате чего 80482C0h превращается в 00002C0h, но пусть нас это не смущает. Конечное смещение расшифровываемого блока вычисляется тривиально 00002C0h +sizeof(DWORD)*78h == 4A0h.

К сожалению, hiew не позволяет расшифровывать более одного экрана за раз и когда курсор подходит к последней строке, hiew пищит, но отказывается прокручивать файл, поэтому необходимо сохранить изменения по <F9>, нажать <Page down>, вновь вернуться в режим редактирования клавишей <F3> и продолжить заниматься расшифровкой.

Остается только скорректировать адрес точки входа. Нажимаем <F5> и переходим по смещению 18h относительно начала файла. Записываем число из EBP, не забывая про обратный порядок байт на x86 (т.е., в данном случае это будет выглядеть так: C0 82 04 08). Нажимаем <F9> для сохранения и выходим. Атрибуты кодовой секции можно, в принципе, и не восстанавливать.

Запускаем расшифрованный файл, чтобы убедиться, что он работает. На этом процедуру распаковки можно считать законченной.

UPX

Происхождение: созданный тройкой магов Markus F.X.J. Oberhumer, László Molnár и John F. Reiser, UPX относится к древнейшим распаковщикам, поддерживающим огромное количество форматов исполняемых файлов, среди которых есть и elf. Собственно говоря, аббревиатура UPX именно так и расшифровывается "Ultimate Packer for executables". Свежую версию вместе с исходными текстами можно бесплатно скачать с "родного" сайта проекта: www.upx.org или с "кузни": upx.sourceforge.net.

Отсюда можно скачать UPX

Рисунок 3. Отсюда можно скачать UPX.

Описание: UPX упаковывает все секции файла (включая и таблицы, содержащие имена функций динамически загружаемых библиотек), вполне корректно обрабатывая elf-формат и успешно работая на всем "зоопарке" UNIX-подобных систем. Не содержит никакого кода, препятствующего его отладке или дизассемблированию.

Распаковка: UPX содержит встроенный распаковщик, возвращающий исполняемые файлы в исходный вид (для этого достаточно указать ключ -d в командной строке), однако этому легко воспрепятствовать. Доступность исходных текстов позволяет модифицировать код упаковщика или изменить "раскладку" служебной информации в генерируемом файле. Еще проще затереть сигнатуру "UPX!", находящуюся в конце упакованного файла. Во всех этих случаях встроенный распаковщик склеивает ласты и распаковкой приходится заниматься самостоятельно.

Нам потребуется утилита для снятия дампа с активных процессов PD, исходный код которой был опубликован в #63 номере электронного журнала PHRAK: www.phrack.org/phrack/63/p63-0x0c_Process_Dump_and_Binary_Reconstruction.txt.

Запускаем упакованную программу (пусть, для определенности, ею будет elinks), открываем новую консоль и, определив идентификатор процесса с помощью штатной утилиты ps, передаем его программе PD.

Сеанс ручной распаковки выглядит следующим образом:

Сеанс ручной распаковки

Рисунок 4. Сеанс ручной распаковки с помощью утилиты PD.

К сожалению, утилита PD еще довольно сыровата и сдампленные программы очень часто оказываются неработоспособными. В некоторых случаях помогает ключ -l, запрещающий трогать секцию .got, но чаще всего над полученным дампом приходится основательно поработать руками. Будем надеяться, что в следующих версиях PD этот недостаток будет преодолен.

Burneye

Происхождение: экспериментальный протектор, созданный хакером по кличке Scut (scut@segfault.net), он же "The Tower", живущим в Западной Германии и входящим в группу TESO, известную своим отладчиком linice - аналогом soft-ice под UNIX. Сначала исходные тексты протектора были недоступны и он распространялся в виде уже откомпилированного файла на бесплатной основе: packetstorm.linuxsecurity.com/groups/teso/burneye-1.0-linux-static.tar.gz, но через некоторое время Scut отдал на растерзание ~30% от общего объема кода проекта: packetstorm.linuxsecurity.com/groups/teso/burneye-stripped.tar.gz, а затем и вовсе открыл все тексты целиком: packetstorm.linuxsecurity.com/groups/teso/burneye-1.0.1-src.tar.bz2.

Описание: никакой это не упаковщик, а самый настоящий протектор, изначально нацеленный на борьбу с хакерами. Умеет шифровать файлы по алгоритмам SHA1 и RC4, требуя от пользователя пароля при запуске и, при необходимости, привязываясь к оборудованию, чтобы пират, купивший одну-единственную лицензионную копию, не выложил свой ключ на всеобщее обозрение. Содержит некоторые приемы против отладчиков и дизассемблеров (прыжки в середину команды и установка собственного обработчика для SIGTRAP), но они реализованы настолько неумело, что протектор без труда отлаживается даже gdb, не говоря уже про ядерные отладчики private-ice и linice. Некоторые защищенные программы падают под BSD, поэтому использовать этот протектор следует с большой долей скептицизма и осторожности.

Страничка пацака ByteRage

Рисунок 5. Страничка пацака ByteRage, поломавшего Burneye.

Снятие пароля: против криптографии, увы, не попрешь и все, что может предложить нам хакерская общественность - это тупой brute-force. Подходящий переборщик можно найти на byterage.hackaholic.org/source/UNFburninhell1.0c.tar.gz, однако следует быть заранее готовым к тому, что вскрыть длинные пароли все равно не удастся.

Типичный сеанс работы с переборщиком выглядит так:

[root@pentagon UNFburninhell]# ./burncrack -f /usr/stuff/burneye-1.0/burneye
UNFburninhell by [ByteRage] / UNF

---[ removing layer 1 (obfuscation layer)
burneye signature found @ 0000100C
obfuscation layer (layer 1) decryption key : B0CE3B8C
length : 00015B7C
 start : 05371090
---[ loading file data
burneye signature found @ 0000100C
burneye stub hash : 66 CC 2F 96 65 3D 4E 36 4D 37 19 20 85 8C AC 91 39 DA FD 88
encrypted length  : 69352
encrypted start   : 0x00005D1B
encrypted dword   : 0x3EBD3C81
checksum          : 0xFDB18CF3
magic XOR block   : B4 AC 25 4A C1 CB 97 39 71 AF E3 54 F0 AD F5 9C FE 42 EC F2
---[ bailing out
[root@pentagon UNFburninhell]# /usr/stuff/john-1.6/run/john -stdout:63 -i | ./burncrack -i
UNFburninhell by [ByteRage] / UNF

---[ loading file data
burneye signature found @ 0000100C
burneye stub hash : 66 CC 2F 96 65 3D 4E 36 4D 37 19 20 85 8C AC 91 39 DA FD 88
encrypted length  : 69352
encrypted start   : 0x00005D1B
encrypted dword   : 0x3EBD3C81
checksum          : 0xFDB18CF3
magic XOR block   : B4 AC 25 4A C1 CB 97 39 71 AF E3 54 F0 AD F5 9C FE 42 EC F2
---[ stdin bruteforce cracker
password "accept" okay! cracked in 134 seconds.
performing sanity check...
found checksum      : 0xFDB18CF3
---[ bailing out

Broken pipe
[root@pentagon UNFburninhell]# ./burncrack -p accept -d unwrapped
[root@pentagon UNFburninhell]# chmod a+x unwrapped
[root@pentagon UNFburninhell]# ./unwrapped

Листинг 3. Подбор пароля методом brute force.

Распаковка: когда борьба с Byrneye всех хакеров окончательно достала, пацак по кличке ByteRage (byterage@yahoo.com) написал утилиту burneye unwrapper для автоматического снятия протектора и представляющую из себя LKM-модуль (загружаемый модуль ядра), бесплатно распространяемый в исходных текстах (впрочем, называть "исходными текстами" крошечную Си-программу можно только с большой натяжкой): byterage.hackaholic.org/source/burndump.c.

Предполагается, что либо программа не защищена паролем, либо он нам известен (или подобран вышеописанной утилитой). Привязка к оборудованию убирается в любом случае.

Компилируем: "gcc -c burndump.c" (на некоторых системах необходимо явно указать включаемые файлы "gcc -c -I/usr/src/linux/include burndump.c"), заходим в систему под root'ом и начинаем взлом, потягивая свежее пиво:

$ insmod burndump   # загружаем LKM-модуль в память
                    # теперь дампер будет висеть резидентно в памяти,
                    # отлеживая запуск всех программ, и ловить те из них,
                    # которые обработаны Burneye

$./file_name        # запускаем программу защищенную Burneye

                    # дампер дожидается, когда Burneye завершит
                    # расшифровку и сохраняет распакованную программу
                    # в файл ./burnout

$./burnout          # запускаем распакованную программу,
                    # чтобы убедиться в ее работоспособности

$rmmod burndump     # выгружаем LKM-модуль из памяти

Листинг 4. Освобождение файла от протектора Burneye.

Shiva

Происхождение: весьма амбициозный протектор, созданный двумя гуру Neel Mehta и Shaun Clowes (E-mail: shiva@securereality.com.au) и неоднократно демонстрируемый ими на конференциях Black Hat. Исходные тексты не разглашаются (как будто там есть, что скрывать!), а сам бинарник можно скачать как с сайта разработчиков www.securereality.com.au/archives/shiva-0.95.tar.gz, так и с сервера Black Hat: blackhat.com/presentations/bh-usa-03/bh-us-03-mehta/bh-us-03-shiva-0.96.tar, причем версия с Black Hat'а посвежее будет, что наводит на определенные размышления.

Сайт разработчиков протектора Shiva

Рисунок 6. Сайт разработчиков протектора Shiva.

Описание: протектор поддерживает парольную защиту (правда, без привязки к оборудованию), реализует мощную антиоталадку, многоуровневую динамическую шифровку с порождением дочернего отладочного процесса, эмуляцию некоторых процессорных инструкций... в общем получился почти что Armadillo, только под Linux. Но, если Armadillo хоть как-то работает, то Shiva на всех доступных мыщъху системах выпадет в Segmentation fault. Конкретно тестировались: KNOPPIX с ядрами 2.6.7/4.2.7 и S.u.S.E c ядром 2.6.8, пускаемых как под VM Ware, так и на "живой" машине с процессором AMD Athol-1700, что делает снятие протектора сверхактуальной задачей.

Shiva

Рисунок 7. Shiva настолько крутой протектор, что ни под одной системой даже не запускается.

Распаковка: морской волк Chris Eagle (cseagle@nps.navy.mil) создал автоматический распаковщик, позволяющий любому желающему поиметь Шиву во все дыры и, как и большинство остальных хакерских инструментов, бесплатно распространяемый в исходных текстах: www.blackhat.com/presentations/bh-federal-03/bh-federal-03-eagle/bh-federal-03-eagle.zip. Распаковав архив, мы найдем мультимедийную презентацию bh-federal-03-eagle.ppt с объяснением принципов работы протектора, пару idc-скриптов для упрощения дизассемблирования защищенных файлов в IDA Pro и еще один архив stripshiva.tar.gz, содержащий исходный код автоматического распаковщика.

Компиляция осуществляется простым запуском утилиты "make", после чего у нас на диске образуется stripshiva - распаковщик не защищенных паролем файлов и shivalkm.o - загружаемый модуль ядра для взлома паролей.

Незапароленнные программы распаковываются так:

# stripshiva x.shiva

Листинг 5. Распаковка незапароленных файлов, обработанных протектором Shiva.

А вот для взлома запароленных файлов приходится совершать гораздо больше телодвижений (при этом предполагается, что запароленный файл уже запущен, то есть пароль должен быть известен - по-другому, увы, ломать не получается):

$insmod shivalkm.o      # загружаем LKM-модуль в память
$rmmod shivalkm         # выгружаем модуль (все что было нужно, он уже сделал)
$tail /var/log/messages # проверяем на наличие любых сообщений
$ls                     # проверяем на наличие сдампленного файла
$stripshiva -p shivaout # превращаем дамп в готовый elf

Листинг 6. Распаковка файлов, обработанных протектором Shiva и защищенных паролем.

Заключение

Shiva - это лучший протектор из всех, существующих под UNIX, но, увы, на проверку это оказывается всего лишь кривая калька с Armadillo и к тому же неработающая. В то время как под Windows протектор Armadillo уже давно не является чудом инженерной мысли, ситуация в мире UNIX напоминает СССР в эпоху "персональных компьютеров коллективного использования". И это хорошо! Потому что UNIX - свободная система и она еще будет ей оставаться какое-то время!