Пример реверс-инжиниринга в Eclipse-CDT GDB и IDA Pro

Пример реверса инжиниринга Eclipse-CDT GDB и IDA Pro

Во время реверс-инжиниринга есть два пути. Пер­вый — труш­ный и хар­довый: исполь­зовать GDB и там отсле­живать цепоч­ку вызовов, потом ревер­сить в IDA Pro. Одна­ко, ког­да есть исходный код, мож­но облегчить себе задачу и при­бег­нуть к Eclipse-CDT. Мы начнем реверс с использования Eclipse-CDT, потом перейдем GDB и закончим IDA Pro.

Еще по теме: Пример реверс инжиниринга с использованием Ghidra

Пример реверса-инжиниринга в Eclipse-CDT

Eclipse-CDT — это сре­да раз­работ­ки на C и C++, осно­ван­ная на плат­форме Eclipse. Сре­ди ее воз­можнос­тей — нас­трой­ка управля­емой сбор­ки для раз­ных тул­чей­нов, стан­дар­тная сбор­ка make, навига­ция по исходно­му коду, раз­нооб­разные инс­тру­мен­ты иссле­дова­ния кода, инс­тру­мен­ты рефак­торин­га и генера­ции кода, а так­же инс­тру­мен­ты для визу­аль­ной отладки, вклю­чая прос­мотр памяти, регис­тров и дизас­сем­бле­ра.

Так как прог­рамма написа­на на Java, необ­ходимо ска­чать Java SDK:

Ска­чаем прог­рамму и рас­паку­ем ее:

В ито­ге рас­паков­ки получа­ем набор фай­лов, сре­ди которых будет исполня­емый файл eclipse. Запус­каем его.

Eclipse-CDT установка

Нас встре­чает вот такое, впол­не информа­тив­ное окош­ко.

Eclipse-CDT запуск

В меню выбира­ем File → Import, пос­ле это­го зада­ем язык прог­рамми­рова­ния, в нашем слу­чае это C/C++. Выбира­ем пункт Existing Code as Makefile Project, пос­коль­ку у нас есть makefile.

Makefile — это файл, который исполь­зует­ся для авто­мати­зации сбор­ки и ком­пилиро­вания прог­рамм. Он содер­жит все необ­ходимые про­вер­ки, нас­трой­ки и коман­ды.

Eclipse-CDT использование

Eclipse-CDT использование как использовать

Да­лее называ­ем про­ект, выбира­ем дирек­торию с прог­раммой, в которой при­сутс­тву­ет makefile, и ука­зыва­ем ком­пилятор — Linux GCC.

Eclipse-CDT создание проекта

Сле­дующий шаг — под­готов­ка парамет­ров отладки. Перехо­дим на вклад­ку Run —> Debug Configurations.

Eclipse-CDT Run --> Debug Configurations

Да­лее выбира­ем C/C++ Application, а на вклад­ке Main — нуж­ный про­ект. На вклад­ке Arguments вста­вим путь до нашего краш‑фай­ла.

Пример реверс инжиниринга Eclipse-CDT

Пример реверс-инжиниринга Eclipse-CDT

Те­перь жмем Debug и попада­ем в глав­ное окно.

Eclipse-CDT Debug

В левом спис­ке мож­но уви­деть ошиб­ку Segmentation fault, что озна­чает потен­циаль­ное перепол­нение буфера. Так­же мы видим мес­та в коде, где была обна­руже­на эта ошиб­ка.

Eclipse CDT Segmentation fault

Eclipse CDT ошибка Segmentation fault

Те­перь нуж­но понять, почему воз­ника­ет ошиб­ка и как это мож­но про­экс­плу­ати­ровать.

Идем копать­ся в интерне­те и узна­ём, что эти ошиб­ки потен­циаль­но могут быть свя­заны или с перепол­нени­ем кучи (CVE-2009-3895), или с обыч­ным отка­зом в обслу­жива­нии, которое тол­ком ни к чему не при­водит (CVE-2012-2836). Пока что нель­зя с уве­рен­ностью ска­зать, к какой имен­но CVE отно­сят­ся эти кра­ши, поэто­му про­дол­жим изу­чение.

Прос­мотрев потен­циаль­но уяз­вимые мес­та, которые выяви­ла Eclipse-CDT, я обна­ружил фун­кцию exif_data_load_data_thumbnail().

Eclipse CDT как использовать

Од­нако опре­делить, почему упа­ла прог­рамма и что в это вре­мя про­исхо­дило с памятью, в Eclipse-CDT у меня не получи­лось. Поэто­му перехо­дим к GDB.

Пример реверса-инжиниринга используя GDB

GDB при­годит­ся нам, что­бы опре­делить цепоч­ку вызовов фун­кций (см. также Инструкция по использованию отладчика GDB).

Заг­ружа­ем прог­рамму в GDB и запус­каем с ука­зани­ем краш‑фай­ла:

GDB пример реверс инжиниринга

От­ловить мес­то падения нам поможет коман­да bt.

GDB пример использования

bt — это коман­да в GDB, которая отоб­ража­ет стек вызовов для текуще­го потока. Стек вызовов содер­жит информа­цию о фун­кци­ях, вызыва­емых в текущей точ­ке прог­раммы. GDB выведет спи­сок, начиная с самой высоко­уров­невой фун­кции и закан­чивая фун­кци­ей, в которой про­изош­ла ошиб­ка.

Прог­рамма упа­ла в этой фун­кции, при­чем внут­ри этой фун­кции, видимо, про­исхо­дит копиро­вание чего‑то в кучу. Этот факт под­твержда­ет учас­ток кода, на котором оста­новил­ся отладчик, а так­же регис­тры.

GDB как использовать

На самом деле понят­но, почему про­изо­шел краш. Регистр rsi ука­зыва­ет на пус­тую ячей­ку динами­чес­кой памяти. Так как такого чан­ка не сущес­тву­ет, а прог­рамма пыта­ется перенес­ти его в xmm0, воз­ника­ет ошиб­ка. Вот этот учас­ток кода доказы­вает нашу догад­ку:

Пример реверса-инжиниринга в IDA Pro

Под­клю­чаем к делу IDA Pro, что­бы понять, что про­исхо­дило до момен­та вызова уяз­вимой фун­кции. Мы можем пол­ностью вос­ста­новить цепоч­ку вызовов и опре­делить мес­то падения прог­раммы. Давай прой­дем­ся по всем сра­ботав­шим фун­кци­ям:

  • main() заг­ружа­ет файл, а затем вызыва­ет exif_loader_get_data() для генера­ции ExifData;
  • exif_loader_get_data() зап­рашива­ет мес­то для ExifData и затем вызыва­ет exif_data_load_data() для чте­ния потока бай­тов из адре­са loader->buf дли­ной loader->bytes_read. Про­ще говоря, выделя­ется мес­то для чте­ния фай­ла;
  • exif_data_load_data() про­веря­ет заголо­вок, пос­ле чего вызыва­ет exif_data_load_data_content();
  • exif_data_load_data_content() счи­тыва­ет количес­тво записей, и для каж­дой выпол­няет­ся мар­шру­тиза­ция по тегу. Если тег — EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, то есть 0x0202, то счи­тыва­ется дли­на мини­атю­ры. Если зна­чение thumbnail_offset уже уста­нов­лено, то вызыва­ется exif_data_load_data_thumbnail, что­бы разоб­рать мини­атю­ры;
  • exif_data_load_data_thumbnail() копиру­ет дан­ные в динами­чес­кую память и даль­ше выводит на экран.

Раз­берем под­робнее пос­леднюю фун­кцию.

В ее начале идет про­вер­ка раз­мера и сме­щения, если их сум­ма боль­ше чис­ла ds, то прог­рамма завер­шает­ся.

Как пользоваться DA Pro

Пос­ле это­го про­веря­ется память: если она выделе­на, то очи­щает­ся.

IDA Pro использование

Даль­ше выделя­ется новая память и про­веря­ется, выделе­на ли она.

Пос­ледний блок, который у меня выделен крас­ным, и есть мес­то, в котором прог­рамма пада­ет.

У фун­кции void memcpy(void *dest, const void *src, size_t n) три аргу­мен­та:

  • *dst — ука­затель на область памяти, в которую будут ско­пиро­ваны дан­ные;
  • src — ука­затель на область памяти, из которой будут ско­пиро­ваны дан­ные;
  • n — количес­тво бай­тов, которые нуж­но ско­пиро­вать.

В кон­тек­сте иссле­дуемой прог­раммы для memcpy() пер­вый аргу­мент — дан­ные, вто­рой аргу­мент — сме­щение, тре­тий аргу­мент — раз­мер.

Бла­года­ря GDB я выяс­нил, что регистр rdx хра­нит в себе раз­мер, регистр rsi — ука­затель на область памяти, из которой надо копиро­вать, а сме­щение хра­нит­ся в регис­тре rbp. При­чем адрес rsi получа­ется путем сло­жения сме­щения и начала адре­са кучи, в которую был заг­ружен файл. Это под­твержда­ет инс­трук­ция lea rsi, [r13+rbp+0] и регис­тры.

IDA Pro строки

В регис­тре rbp хра­нит­ся зна­чение 0x00000000FFFFFEC3, которое явля­ется перемен­ной offset. В регис­тре r13 — у ка­затель на кучу 0x000055F902C7C9A6. Пос­ле сло­жения получа­ем зна­чение 0x55fa02c7c869, которое хра­нит­ся в rsi и ука­зыва­ет в никуда. Собс­твен­но, что и тре­бова­лось доказать.

Про­деба­жив прог­рамму еще нес­коль­ко раз, я понял, что все эти зна­чения берут­ся из самого фай­ла, а не вычис­ляют­ся. Я открыл краш‑файл в HEX-редак­торе и по сиг­натуре нашел зна­чение для перемен­ной offset 0x00000000FFFFFEC3 и раз­мер 0x8cb.

IDA Pro HEX-редак­тор

То есть если изме­нить эти зна­чения и заг­рузить в прог­рамму, то мож­но попасть в какой‑то дру­гой чанк и перете­реть его. Одна­ко, иссле­довав память, я выяс­нил, что никуда это не при­водит и про­экс­плу­ати­ровать этот баг не получит­ся. Поэто­му, к сожале­нию, до пол­ного роад­мэпа взло­ма мы в этот раз не дотяну­ли.

Заключение

В этой статье я пос­тарал­ся показать шаги ревер­сера на реаль­ном кей­се. К велико­му сожале­нию, на прак­тике быва­ет такое, что уяз­вимость не получа­ется про­экс­плу­ати­ровать и баг при­водит толь­ко к отка­зу в обслу­жива­нии. Одна­ко в про­цес­се мы научи­лись искать и собирать тар­гет, фаз­зить исполня­емый файл, поль­зовать­ся ста­тичес­ким ана­лиза­тором кода и иссле­довать уяз­вимое мес­то в бинаре.

ПОЛЕЗНЫЕ ССЫЛКИ:

Дима (Kozhuh)

Эксперт в кибербезопасности. Работал в ведущих компаниях занимающихся аналитикой компьютерных угроз. Анонсы новых статей в Телеграме.

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