Обратный инжиниринг с использованием Radare2 (Reverse Engineering)


Источник: https://medium.com/@jacob16682/reverse-engineering-using-radare2-588775ea38d5

Это разбитая на две части статья, которая показывает самые азы реверс-инжиниринга. Эти статьи и примеры совсем простые, в них содержаться интересные подсказки по анализу исполнимых файлов, которые могут пригодиться даже в повседневной работе с бинарниками, к примеру, будет показано, как можно извлечь все текстовые строки, которые встречаются в файле.

Кстати, как говорит Википедия:

Обратная разработка (обратное проектирование, обратный инжиниринг, реверс-инжиниринг; англ. reverse engineering) — исследование некоторого готового устройства или программы, а также документации на него с целью понять принцип его работы; например, чтобы обнаружить недокументированные возможности (в том числе программные закладки), сделать изменение или воспроизвести устройство, программу или иной объект с аналогичными функциями, но без прямого копирования.

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

Это перевод и автором является Jacob Pimental, но для лаконичности там, где он говорит «я», в тексте также будет говориться от первого лица, но имейте ввиду, что «я» - это Jacob Pimental.

Эта статья подразумевает, что у читателя есть хоть какие-то базовые знания в области программирования и языка Ассемблер. Если нет, то рекомендуется изучить «Введение в Ассемблер», благодаря которому многое при анализе кода и отладке программы станет намного понятнее.

Обратный инжиниринг в программном обеспечении — это возможность разобрать (дизассемблировать) программу, чтобы увидеть, как она функционирует. Реинжиниринг позволяет разбирать на части программу или программное обеспечение и пересоздавать его без даже без доступа к исходному коду. Это используется, к примеру, при анализе вредоносного ПО (Malware Analysis) для понимания, что делают куски вредоносной программы, чтобы создать идентификатор для предупреждения последующих инфицирований вашего компьютера. Это также может использоваться для исправления ошибок в старых играх, или для поиска эксплойтов в некоторых программах.

Radare2 — это инструмент, используемый в обратном инжиниринге. Он отличается от других инструментов тем, что он бесплатный, у него открыт исходный код и он использует интерфейс командной строки, а не графический. В этой статье я собираюсь провести вас по самым основам обратной инженерии: используя radare2, мы дизассемблируем программы, которые я предварительно для этого создал. Здесь вы можете загрузить эти программы, которые мы будем разбирать в этом уроке, или создать свою собственную и следовать уроку по аналогии.

Для установки Radare2 начните с проверки, возможно эта программу уже присутствует в стандартных репозиториях вашего дистрибутива.

Например, в Kali Linux эта программа не только присутствует, но и установлена по умолчанию.

В Debian и производных попробуйте выполнить установку с помощью команды:

sudo apt install radare2

В Arch Linux и BlackArch для установки Radare2 выполните:

sudo pacman -S radare2

Для установки Radare2 в дистрибутивы Linux, для которых она отсутствует в источниках приложений, вам нужно клонировать исходный код с GitHub и запустить файл sys/install.sh:

git clone https://github.com/radare/radare2
cd radare2
sudo sys/install.sh

Теперь давайте взглянем на бинарники, которые вы загрузили ранее (или создали сами). Программа intro при запуске в консоли выводит строку “Hello World”:

./intro
Hello World

Код этой программы простой:

#include <stdio.h>
void main(){
    printf("Hello World\n");
}

Radare2 поставляется с очень удобным инструментом под названием rabin2. Он может использоваться для вытягивания информации из бинарника, в этой информации будут: строки, время компиляции и другая полезная информация. Я обычно использую это перед запуском любого серьёзного анализа, поэтому могу узнать в общих чертах, что программа может быть делает. Я начну с добавления флага -I. Это даст нам важную информацию о бинарнике.

rabin2 -I intro

Пример вывода:

arch     x86
baddr    0x0
binsz    6485
bintype  elf
bits     64
canary   false
class    ELF64
compiler GCC: (GNU) 7.2.0
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      true
relocs   true
relro    partial
rpath    NONE
sanitiz  false
static   false
stripped false
subsys   linux
va       true

Это говорит нам, что программа предназначена для работы на Linux и что она была написана на языке программирования C. Мы также можем увидеть что bintype имеет значение “elf”. Это называется «магическим числом» (“magic number”) и может нам помочь узнать, какого типа этот файл (бинарник Linux). Это всё, что нам нужно сейчас знать. Следующая команда, которую я люблю использовать, это rabin2 -z. Она выведет список всех строк из секции данных (data) этого бинарного файла. Запуском этой команды мы можем увидеть нашу строку “Hello World”:



rabin2 -z intro

Пример вывода:

[Strings]
Num Paddr      Vaddr      Len Size Section  Type  String
000 0x000006e4 0x000006e4  11  12 (.rodata) ascii Hello World

После этого я иногда продолжаю с rabin2 -zz, которая показывает все строки в бинарнике (а не только из секции данных). Она выводит обычно довольно много строк. Иногда там имеются важные спрятанные строки, иногда — нет. В случае с разбираемой программой, там ничего интересного, поэтому мы закончили с rabin2. Другие флаги rabin2 вы можете посмотреть командой:

rabin2 -h

Имеется много других интересных вещей, которые может показать rabin2, такие как импорты, экспорты и секции кода, но для данного двоичного файла это не особо интересно. При анализе вредоносного ПО, этим команды могут пригодиться, но не для такой простой программы вроде этой.

Поэтому теперь мы можем перейти к действительному запуску нашей программы в radare2 для просмотра кода Ассемблера. Запускаем команду:

r2 intro

Она загрузит наш исполнимый файл в radare2. Следующий шаг — это включить анализ двоичного файла в radare2. С помощью него найдуться такие вещи как строки, функции и другая важная информация, которую radare2 может показать нам во время анализа, то есть мы не просто пялимся в код Ассемблера. Для включения анализа, запустите команду “aa”. Это базовая команда анализа для radare2. У нас также есть “aaa” и “aaaa”, каждая анализирует больше информации, чем предыдущая. На данный момент нам достаточно просто “aa”.

r2 intro 
 -- Enhance your graphs by increasing the size of the block and graph.depth eval variable.
[0x00000540]> aa
[x] Analyze all flags starting with sym. and entry0 (aa)
[0x00000540]>

Теперь наш исполнимый файл анализируется, мы можем перейти в раздел бинарника, где на самом деле размещён код. Команда “s” в radare2 используется для “seek” (поиска) места в памяти. Её можно использовать либо с действительным шестнадцатеричным адресом, или мы можем напечатать имя функции, которую надо найти. Поскольку мы знаем, что большинство программ Linux запускаются с функции “main”, мы можем поискать её:

[0x00000540]> s main
[0x0000064a]>

Вы заметите, что адрес у курсора сменился с 0x00000540 на 0x0000064a. Это означает, что теперь текущим адресом является 0x0000064a и что мы в функции main. Мне нравится анализировать код в визуальном режиме radare2 (прямо сейчас мы в командном режиме). Для переключения в визуальный режим наберите “v” и нажмите Enter.

[0x0000064a]> v

То, на что вы смотрите сейчас — это шестнадцатеричный редактор radare2 (да, у него есть всё!). Но это не обязательно код, который мы хотим видеть. Для изменения вида нажимайте “p”. Вид смениться на «вид разборки» (disassembly view) и мы сможем увидеть код ассемблера программы. Обратите внимание, что radare2 отделяет функции на небольшие типы ASCII «блоков». Код главной функции выглядит примерно так:

/ (fcn) sym.main 19                                                                                                                                                                           
|   sym.main ();                                                                                                                                                                              
|              ; DATA XREF from 0x0000055d (entry0)                                                                                                                                           
|           0x0000064a      55             push rbp                                                                                                                                           
|           0x0000064b      4889e5         mov rbp, rsp                                                                                                                                       
|           0x0000064e      488d3d8f0000.  lea rdi, str.Hello_World    ; 0x6e4 ; "Hello World"                                                                                                
|           0x00000655      e8d6feffff     call sym.imp.puts           ;[1] ; int puts(const char *s)                                                                                         
|           0x0000065a      90             nop                                                                                                                                                
|           0x0000065b      5d             pop rbp                                                                                                                                            
\           0x0000065c      c3             ret

Теперь давайте пройдёмся по исходному коду. Поскольку этот исполнимый файл является x64 приложением, мы можем увидеть, что мы помещаем нашу строку “Hello World” в rdi по адресу памяти 0x0000064e. Затем мы вызываем функцию “sym.imp.puts” (или “puts”), которая то же самое, что и “printf”. То есть эта функция напечатает нашу строку. После этого команда “nop” делает абсолютно ничего (как команда pass в python), затем мы очищаем и завершаем функцию, используя “pop rbp” и “ret”. Поскольку наша функция закончила работу, и поскольку это главная функция, наша программа также отработала.

Даже без запуска самой программы мы можем сказать, что эта программа выводит стоку “Hello World”.

Это было очень простое применение radare2. В следующей статье мы будем использовать radare2 для «взлома» простой программы в стиле Capture the Flag (решение хакерских задач, когда доказательством достижения цели является получение специально спрятанной автором задачи информации - «Флага»)


Спасибо за чтение и счастливого ревёрсинга!

Читайте продолжние во второй части.


Рекомендуется Вам:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *