14 минут
На radare как на ладони. Основы работы с фреймворком radare2
Intro
Небольшой экскурс в историю. Проект radare начал разрабатывать хакер с ником pancake в 2006 году и долгое время по сути он был единственным разработчиком. Созданный фреймворк обладал простым консольным интерфейсом для работы как шестнадцатеричный редактор, поддерживающий 64-битную архитектуру. Это позволяло находить и восстанавливать данные с жестких дисков. Поэтому его еще называли инструментом для компьютерной криминалистической экспертизы. Но в 2010 год произошел “редизайн” фреймворка и после чего проект стал разрастаться и пополняться новым функционалом, что позволяет его использовать не только как редактор, но и дизассемблер, анализатор как кода, так и шеллкодов. На данный момент этот фреймворк используют как знаменитые CTF-команды (Dragon Sector), так и вирусные аналитики (MalwareMustDie и AlienVault), причем последние представляют его на своем воркшопе на Black Hat. Достаточно большой список тех кто использует radare2 с примерами представлен в блоге проекта.
В общем, не боюсь этого слова, фреймворк тихими шагами догоняет нашу любимую (и довольно трудно получаемую) IDA.А пока рассмотрим особенности фреймворка, которые разработаны на данный момент.
Начнем с поддержки большого количества архитектур, есть даже для Gameboy, видео по анализу популярной игры Pokemon для этого устройства опубликовал на канале youtube один из исследователей, правда на немецком языке.
Одна из особенностей поддержка многих различных скриптовых языков. Помимо популярных Python с Perl, которые поддерживаются в других дизассмблерах, есть так же Vala, Go, Guile, Ruby, Lua (о его плюсах и минусах я писал ранее), Java, JavaScript (nodejs и ducktape), sh и многие другие.
Так же многим пригодится поддержка типов, особенно при анализе C++ программ. Достаточно создать *.h файл с описанием и подключить его. Ниже я привел пример от автора фреймворка. Содержимое файла с описанием структуры:
[0x00000000]> cat test.h
#define uint32_t unsigned int
typedef struct addr {
char street[127];
char city[40];
uint32_t zip;
} addr_t;
Подключаем файл и помечаем область со структурой:
[0x00000000]> to test.h
[0x00000000]> tl addr 0x4000
[0x00000000]> tf 0x4000
struct addr {
street : 0x00004000 = "Wallaby Way"
city : 0x00004000 = "Sydney"
zip : 0x00004008 = 2000
}
Существует поддержка отладки. Причём ты можешь проводить как прямую отладку, так и работу с протоколами gdb, winedbg.
Благодаря тому, что фреймворком заинтересовались вирусные аналитики, появилась поддержка утилиты yara о которой мы писали ранее. Помимо поддержки самой утилиты было встроено большое количество правил. Некоторые из них, например, позволяют определить большое количество упаковщиков. Ниже я привёл такой пример для одного из сэмплов вредоносной программы:
[0x0040324d]> yara scan
dUP_v2_x_Patcher
Nullsoft_PiMP_Stub
Установка
Так как на данный момент radare2 не является версией 1.0 (на момент написания статьи она была 0.9.8), разработчики советуют использовать свой фреймворк, скачав и собрав его из исходников с github ( заодно вспомним как работать с git =) ):
$ git clone https://github.com/radare/radare2.git
Если же у тебя исходники были уже скачены, то нужно их обновить следующей командой:
$ git pull
Для автоматической компиляции можешь воспользоваться встроенный скрипт:
$ sys/install.sh
Если же он выдал ошибку, то попробуй сделать вручную:
$ ./configure --prefix=/usr
$ gmake
$ sudo gmake install
Или вместо gmake используй утилиту make. Например, для Mac OS X мне так и пришлось сделать. А после перезагрузки я увидел долгожданное окно:
Так же ты можешь поставить radare2 из macports или использовать утилиту homebrew, но там версии периодически отстают. По этой же причины, если используешь Kali Linux, то советую удалить встроенный radare2 через утилиту apt-get и поставить фреймворк из исходников, как я описал выше.
Для Windows бинарный файл можно скомпилировать с помощью какой-либо *nix платформы или воспользоваться mingw-компилятором.
Android-версия доступна в google play, причем права root не требуется. Так как с помощью фреймворка можно анализировать и java-файлы, об этом есть неплохая статья с примерами, то легко добавили поддержку apk-файлов. Ниже я покажу как выглядят интерфейс программы и дизассемблерированый код.
Хотя в некоторых случаях можно обойтись и без компиляции. Благодаря сервису СI есть возможность скачивать уже скомпилированные файлы под различные платформы, в том числе и для Windows. Например, именно поэтому android-версия идет рука об руку с основной.
Теперь немного остановимся на доработке функционала. Чтобы отменить свои модификации исходников, вернемся к нормальной версией:
$ git reset --hard HEAD
Если же твои доработки наоборот исправили какую-то проблему, то помимо обычного commit, можно сделать патч и отправить его разработчику:
$ git diff > radare-foo.patch
Теперь рассмотрим саму работу с фреймворком.
Обзор утилит
Помимо основной утилиты radare2 к которой мы вернемся позже, рассмотрим набор программ, который входит в фреймворк на примере простых операций, которые могут пригодится исследователям.
rasm2 - ассемблер/дизассемблер фреймворка выполнен как отдельное приложение и позволяет дизассемблеровать как бинарные, так и отдельные строки.
root@kali:~/# rasm2 -a x86 nop
90
root@kali:~/# rasm2 -a x86 -d 'eb00'
jmp 0x2
Обычным переводом опкодов туда и обратно правда мало кого удивишь, пусть и с поддержкой большого количества архитектур. Зато описание всех опкодов не всегда есть под рукой:
root@kali:~/# rasm2 -w cmpsb
cmp DS:[SI], ES:[edi] (esi++, edi++)
root@kali:~/# rasm2 -w sqrtpd
compute square roots of packed double-fp values
root@kali:~/# rasm2 -d eb165e31d2525689e189f331c0b00bcd8031db31c040cd80e8e5ffffff2f62696e2f7368
jmp 0x18
pop esi
xor edx, edx
push edx
push esi
mov ecx, esp
mov ebx, esi
xor eax, eax
mov al, 0xb
int 0x80
xor ebx, ebx
xor eax, eax
inc eax
int 0x80
call 0x2
das
bound ebp, [ecx+0x6e]
das
jae 0x8c
rabin2 - утилита для работы с различными исполняемыми файлами (ELF, PE, Java CLASS, MACH-O). Используется для получения различной информации о файле: импортируемые функции, экспортируемые символы, секции, подключаемых библиотеках и т.п. Рассмотрим самые популярные действия:
-
Получаем информацию о формате и включенных системах защиты.
root@kali:~/# rabin2 -I 9f2520a3056543d49bb0f822d85ce5dd file 9f2520a3056543d49bb0f822d85ce5dd type DLL (Dynamic Link Library) pic false canary false nx false crypto false has_va true root pe class PE32 lang unknown arch x86 bits 32 machine i386 os windows subsys Windows GUI endian big strip false static false linenum true lsyms true relocs true rpath NONE
-
Получаем список импортируемых функций и из каких библиотек они вызываются:
root@kali:~/# rabin2 -i 9f2520a3056543d49bb0f822d85ce5dd ... ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=WS2_32.DLL_WSAIoctl ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=SHFolder.dll_SHGetFolderPathA ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=ntdll_NtUnmapViewOfSection ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=user32.dll_EnumDisplayMonitors ordinal=002 plt=0x00000000 bind=NONE type=FUNC name=user32.dll_GetMonitorInfoA ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=SHELL32.DLL_SHEmptyRecycleBinA ordinal=001 plt=0x00000000 bind=NONE type=FUNC name=AVICAP32.DLL_capGetDriverDescriptionA 600 imports
-
Ищем строки и где они находятся. Кто-то скажет, что ему хватает утилиты strings, но данный вариант более умный и показывает дополнительную информацию, которая нужна при анализе:
root@kali:~/# rabin2 -z 9f2520a3056543d49bb0f822d85ce5dd ... addr=0x008c970a off=0x000bcd0a ordinal=441 sz=34 len=16 section=.rsrc type=w string=OriginalFilename addr=0x008c972c off=0x000bcd2c ordinal=442 sz=24 len=11 section=.rsrc type=w string=MSRSAAP.EXE addr=0x008c974a off=0x000bcd4a ordinal=443 sz=24 len=11 section=.rsrc type=w string=ProductName addr=0x008c9764 off=0x000bcd64 ordinal=444 sz=54 len=26 section=.rsrc type=w string=Remote Service Application addr=0x008c97a2 off=0x000bcda2 ordinal=445 sz=30 len=14 section=.rsrc type=w string=ProductVersion
Можно указать эти параметры вместе и получить сразу нужную информацию в один поток. А в блоге проекта представлена статья, как вытаскивать исполняемые файлы из бинарного файла.
rahash2 - утилита для получения хэш-значений во многих форматах как от бинарных файлов, так и определенных частей данных. Пример получения хэшей для одной из RAT-малвари представлен на скриншоте.
radiff2 - утилита для сравнения бинарных файлов. Для более успешной работы советую доустановить программу xdot, если у тебя её еще нет:
root@kali:~/# apt-get install xdot
Работает как через xdot, так и в консольном (ascii) режиме. Пример её работы я покажу в разделе по решению crackme.
rafind2 - утилита для поиска как строк с помощью и без регулярных выражений, так и данных в шестнадцатеричном формате или по бинарному шаблону.
ragg2 - экспериментальная утилита для компиляции небольших программ (шеллкодов ;-)) для x86/x64 и ARM-архитектур.
rax2 - утилита для конвертации данных в различных форматах. Преобразуем hex-данные в строку:
root@kali:~/# rax2 -s 43 4a 50
CJP
rarun2 (rr2) - позволяет запускать программу с различными параметрами среды, аргументами, правами и директориями. Это пригодится не только для решения различных crackme или CTF-задач, но и при фаззинге или тестах.
Теперь перейдем к самому radare2 и рассмотрим основные команды для работы с ним на примере небольшого crackme.
Разбираем crackme
В качестве примера я взял простой crackme от пользователя Lord из архива сайта crackmes.de, а работать будет в 32-битном Kali Linux. Запустим загруженный файл:
root@kali:~/crackmes# ./cm1eng
Password : dukebarman
root@kali:~/crackmes#
Значит нам требуется найти правильный пароль. Причём неверный вариант никак не помечается, программа просто завершается. Ну что же, для начала рассмотрим сам файл:
root@kali:~/crackmes# rabin2 -I cm1eng
...
root elf
class ELF32
lang c
arch x86
bits 32
machine Intel 80386
os linux
subsys linux
endian little
strip true
...
Как видим, опция strip
присутствует. Не смотря на то, что crackme записан для новичков, автор решил уж совсем задачу не облегчать и удалил “лишнюю” информацию из файла. Поэтому теперь загрузим программу в radare2 и увидим одну из встречающих случайных фраз:
root@kali:~/crackmes# r2 ./cm1eng
-- Nothing to see here. Move along.
[0x08048080]>
После запуска нам нужно проанализировать файл с помощью команду начинающийся с a
. Кстати, чтобы увидеть возможные команды, то достаточно добавить знак вопроса к изменяющемуся символу, в нашем случае это будет a?
:
[0x08048080]> a?
|Usage: a
| a8 [hexpairs] analyze bytes
| aa analyze all (fcns + bbs)
...
Вернемся к анализу. В фреймворке возможно проанализировать как весь файл, так и отдельные блоки, строки и т.п. Проанализируем весь файл и раз требуется ввести пароль, то попробуем его найти, возможно его оставили вшитым в программу. Всё таки этот crackme считается начального уровня:
[0x08048080]> aa
[0x08048080]> iz
addr=0x100910f8 off=0x000000f8 ordinal=000 sz=13 len=12 section=.data type=a string=\nPassword :
addr=0x10091105 off=0x00000105 ordinal=001 sz=33 len=32 section=.data type=a string=Great you did it !:)\n\n
addr=0x10091126 off=0x00000126 ordinal=002 sz=8 len=7 section=.data type=a string=QTBXCTU
[0x08048080]>
Ты уже знаком с командой iz, поэтому уточню небольшой нюанс о работе со строками в фреймворке. Все строки автоматически преобразуются в переменную со схожим именем:
Great you did it !:)\n\n -> str.Great_you_did_it_____n_n
К таким переменным можно обращаться @str.Great_you_did_it_____n_n
. Так же работает автодополнение через клавишу TAB
, что очень удобно при их большом количестве. Помимо этого есть возможность поиска как строк, так и различных байтов через команду /
. Пример поиска строки:
[0x08048080]> / Password
Searching 8 bytes from 0x08048000 to 0x0804a0f8: 50 61 73 73 77 6f 72 64
hits: 2
0x080480f9 hit3_0 "Password"
0x080490f9 hit3_1 "Password"
[0x08048080]> px 10 @0x080480f9
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x080480f9 5061 7373 776f 7264 203a Password :
Но вернёмся к нашей программе. Так как она небольшая, то у неё нету импортируемых функций, но есть несколько строчек. Одну мы видим постоянно при запуске crackme, а вот остальные две представляют для нас большой интерес. Первая позволит найти место в коде, которое ведёт к верному решению, а вторая возможно является паролем. Попробуем вторую строку:
root@kali:~/crackmes# ./cm1eng
Password : QTBXCTU
root@kali:~/crackmes#
Увы, но это не является паролем, хотя возможно всё таки нам пригодится. А пока, раз у нас есть строка, которая теоретически выводится при удачном решении, попробуем решить crackme “нечестным” образом - пропатчим файл. Создадим копию файла (заодно она нам пригодится для обещанного примера) и загрузим её в фреймворк, но через дополнительную команду, которая откроет файл в режиме записи:
root@kali:~/crackmes# r2 -w ./cm1eng_crack
-- Nothing to see here. Move along.
[0x08048080]> aa
[0x08048080]> pdf
Помимо команды проанализировать файл добавилась новая - pd?
Она позволяет вывести на экран дизассемблерированные строки. В нашем случае случае всей функции, а так как файл небольшой она и является главной. Вывод осуществляется до конца функции. Так как мы запускаем с десктопа, то и прокрутить вывод в терминале не составит труда, но можно вывести только первые N строчек с текущего адреса. Поэтому найдем строчку с позитивным сообщением в этой функции. Если же так её не получается, то воспользуемся ещё одной особенностью фреймворка.
Помимо различного встроенного функционала в radare2 есть поддержка запуска системных утилит, в частности grep
:
[0x08048080]> pdf | grep str.Great
| | 0x080480e3 b905910408 mov ecx, str.Great_you_did_it_____n_n ; 0x08049105
Вот мы и получили сразу нужный адрес, в который передается наша строка, так как проверка пароля должна быть до обращения к ней, то выведем строки до неё. Для начала возьмём 10:
[0x08048080]> pd -10 @0x080480e3
| 0x080480c2 02e2 add ah, dl
| 0x080480c4 f1 int1
| ; JMP XREF from 0x080480c1 (section..text)
| 0x080480c5 be1b910408 mov esi, 0x804911b ; 0x0804911b
| 0x080480ca bf26910408 mov edi, str.QTBXCTU ; 0x08049126
| 0x080480cf b907000000 mov ecx, 0x7 ; 0x00000007
| 0x080480d4 fc cld
| 0x080480d5 f3a6 repe cmpsb
| ,=< 0x080480d7 7516 jne 0x80480ef ; (section..text)
| | 0x080480d9 b804000000 mov eax, 0x4 ; 0x00000004
| | 0x080480de bb01000000 mov ebx, 0x1 ; 0x00000001
[0x08048080]>
Вот мы и нашли проверку и переход не к нужной нам функции по адресу 0x080480d7
. Более наглядно часть кода представлена на скриншоте, где я проскролил до нужного нам места.
Перейдем к ней и проверим, правильный ли адрес указали:
[0x08048080]> s 0x080480d7
[0x080480d7]> pd 7
| ,=< 0x080480d7 7516 jne 0x80480ef ; (section..text)
| | 0x080480d9 b804000000 mov eax, 0x4 ; 0x00000004
| | 0x080480de bb01000000 mov ebx, 0x1 ; 0x00000001
| | 0x080480e3 b905910408 mov ecx, str.Great_you_did_it_____n_n ; 0x08049105
| | 0x080480e8 ba16000000 mov edx, 0x16 ; 0x00000016
| | 0x080480ed cd80 int 0x80
| | syscall[0x80][0]=? ; section_end..shstrtab+91
| | ; JMP XREF from 0x080480d7 (section..text)
| `-> 0x080480ef b801000000 mov eax, 0x1 ; 0x00000001
[0x080480d7]>
А вот запатчить можно разными способами. Примеры операндов в шестнадцатеричном представлении:
[0x080480d7]> !rasm2 -a x86 -d '7516'
jne 0x18
[0x080480d7]> !rasm2 -a x86 -d '7416'
je 0x18
[0x080480d7]> !rasm2 -a x86 -d 'eb00'
jmp 0x2
[0x080480d7]> !rasm2 -a x86 -d '9090'
nop
nop
- Наша непосредственная команда.
- Старый добрый патчинг - видим n - удаляем, не видим, добавляем.
- Прыжок на “следующий” адрес.
- Ну и просто забить пропускающимися байтами
Возьмём 3 вариант и выйдем для проверки:
[0x080480d7]> wx eb00
[0x080480d7]> q
Результат ты можешь увидеть на скриншоте. Теперь любой введённый код будет считаться правильным ;-) А утилиту rasm2 в этом случае можно использовать для проверки правильности ввода. Используя команду w?
вида wx
ты патчишь в виде шестнадчатеричных чисел, но при желании можно вводить и обычными командами:
wa jmp 0x80480d9
С помощью “грязного” трюка мы всё таки решили этот crackme, но хотелось бы разобраться с настоящим паролем. Вспомним, что неизвестная строка всё таки проверяется перед выводом победного сообщения. При анализе находим небольшой цикл, который берет 7 символов и xor’ит их с ключом 0x21.
Попробуем провести обратную операцию с найденной строкой. Найдем её представление в коде:
[0x08048080]> px 16 @str.QTBXCTU
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
0x08049126 5154 4258 4354 5500 0054 6865 204e 6574 QTBXCTU..The Net
Мне было быстрее найденные hex-значения загрузить в 010-Editor и расшифровать, но radare2 поддерживает различные арифметические операции и при желании можно сделать xor для каждого символа:
[0x08048080]> ? 51h^21h
112 0x70 0160 112.0 0000:0070 112 "p" 01110000 112.0 0.000000
Или написать небольшой плагин ;-) Но в итоге всё равно получаем строку “pucybut”. Она и является нашим паролем.
На будущее можно сразу искать необычные xor-команды.
[0x08048080]> pdf | grep xor
| 0x080480b3 31db xor ebx, ebx
| | 0x080480b7 3421 xor al, 0x21
| |||| 0x08048149 2e3338 xor edi, [cs:eax]
[0x08048080]>
Такую выборку еще используют для нахождения различных call-команд. Но теперь покажу обещанный пример использования утилиты radiff2. У нас имеются два файла с одной отличной функцией. Я предпочитаю смотреть сразу на два файла, то есть на одном отличия первого, на другом второго.
root@kali:~/crackmes# radiff2 -g main cm1eng_crack cm1eng > /tmp/cm1
root@kali:~/crackmes# radiff2 -g main cm1eng cm1eng_crack > /tmp/cm2
root@kali:~/crackmes# xdot /tmp/cm1 & xdot /tmp/cm2
root@kali:~/crackmes# radiff2 -g main cm1eng cm1eng > /tmp/cm & xdot /tmp/cm
Ну раз мы коснулись темы с визуализацией, то рассмотрим существующие возможности.
GUI
Единственная, на мой взгляд, почему radare2 до сих пор пробивается в массы не такими быстрыми шагами, потому что отсутствует нормальный GUI-интерфейс. Во времена обилия как карманных, так и настольных устройств с touch-экранами это уже считается минимумом. На данный момент существует несколько встроенных утилит:
- визуальный интерфейс в консольном окне, который запускается командой VV (согласен, страшновато выглядит)
root@kali:~/crackmes# r2 -c=H cm1eng
Для сравнения с мобильным интерфейсом я открыл этот же crackme на своем android-устройстве.
Outro
Увы, полностью описать работу с каждым модулем я не смогу, так как ограничен размером статьи. Но надеюсь тех небольших знаний, которых ты получил из статьи тебе хватит. Так же советую просмотреть материалы по указанным ссылкам в конце статьи и врезках. Некоторые примеры тебя приятно удивят. В случае, если возникнут проблемы с освоением, найдешь возможную ошибку в фреймворке или может захочешь помочь с программированием, то милости просим на irc-канал #radare в сети irc.freenode.net. Причём в отличии от некоторых каналов, на которых я тоже присутствую, на этом ежедневно ведется обсуждение как проекта, так и других различных вещей.
Разработка фреймворка не стоит на месте и версия 1.0 всё ближе. Одно из планируемых нововведений, которое, как мне кажется, пригодится многим - это поддержка 010-шаблонов для одноименного шестнадцатеричного редактора. А такие шаблоны особенно помогают при фаззинге.
Поэтому не удивляйся, если в момент чтения этого номера ты установишь версию 1.x.
Полезные материалы
Книга по Radare2 от Maijin( находится в процессе написания, поэтому постоянно обновляется)
Сравнение фреймворка с другими популярными инструментами для реверсинга - IDA Pro и Hopper
Использование radare2 для анализа BIOS
P.S. Антон Кочков (@akochkov aka xvilka) является одним из разработчиков этого фреймворка и представил его, как мне кажется впервые на русском языке, в виде небольшого доклада на PHDays 2014. В докладе он показал пример использования radare2 для анализа вредоносных программ. В качество экземпляров были представлены Windows-троян Shylock и 64-битного Linux-вирус Snakso.A. Для которых был проведен как статический анализ, так и использование дебаггера. Видео доклада доступно на официальном сайте PHDays. А с презентацией ты можешь ознакомиться на slideshare-аккаунте мероприятия
Знаменитый хакер @pof, а по совместительству тот, кто написал Android Hacker Handbook и поддерживает android-версию фреймворка, написал статью. В ней он объясняет разницу между взломом RAM-памяти и ROM-образа на примере игры Super Street Fighter II Turbo, используя для редактирования образа radare2.
P.S.S. Для улучшения читабельности дизассемблированного кода (в частности сделать непрерывные стрелки, показывающие переходы) советую добавить опцию:
e scr.utf8=true
Такие небольшие команды можно прописать в файле конфигурации ./radarerc
, чтобы они выполнялись автоматически при запуске фреймворка.