Се­год­ня в выпус­ке: реверс‑инжи­ниринг Flutter-при­ложе­ния, под­борка полез­ных фун­кций‑рас­ширений на Kotlin, две статьи об ошиб­ках исполь­зования корутин и Flow в Kotlin, замет­ка об авто­мати­чес­ки уста­рева­ющих ком­мента­риях, а так­же под­борка из один­надца­ти must have биб­лиотек и десяток новых биб­лиотек.
 

Почитать

 

Реверс-инжиниринг Flutter-приложения

Reverse Engineering a Flutter app by recompiling Flutter Engine — статья о реверс‑инжи­нирин­ге при­ложе­ний, написан­ных с исполь­зовани­ем фрей­мвор­ка Flutter.

Flutter — это кросс‑плат­формен­ный инс­тру­мент, пред­назна­чен­ный для соз­дания быс­трых при­ложе­ний на язы­ке Dart с исполь­зовани­ем реак­тивно­го UI-фрей­мвор­ка. Написан­ные с помощью Flutter при­ложе­ния могут работать на Android, iOS, дес­кто­пе и вебе. При этом интерфейс будет пол­ностью иден­тичен на всех плат­формах.

Глав­ная осо­бен­ность, отли­чающая Flutter от фрей­мвор­ка, пре­дос­тавля­емо­го Android, в том, что код все­го при­ложе­ния, вмес­то набора из байт‑кода и ресур­сов, ком­пилиру­ется в еди­ную натив­ную биб­лиоте­ку, разоб­рать­ся в струк­туре которой дос­таточ­но слож­но. К тому же фор­мат дан­ных в этой биб­лиоте­ке пос­тоян­но меня­ется, что еще силь­нее запуты­вает ревер­сера.

Биб­лиоте­ка, содер­жащая код при­ложе­ния, называ­ется libapp.so. При­чем это не прос­то код и дан­ные при­ложе­ния, а так называ­емый snapshot, пред­став­ляющий собой сни­мок сос­тояния вир­туаль­ной машины Dart перед переда­чей управле­ния на точ­ку вхо­да при­ложе­ния (фун­кция main), плюс ском­пилиро­ван­ный с помощью AOT-ком­пилято­ра код всех клас­сов при­ложе­ния.

Раз­бирать код биб­лиоте­ки libapp.so клас­сичес­ким спо­собом (запус­каем IDA Pro и начина­ем иссле­довать) бес­полез­но. Да, это натив­ный код, но фор­мат самого фай­ла в кор­не отли­чает­ся от обыч­ных биб­лиотек.

Один из методов ана­лиза сос­тоит в том, что­бы про­пар­сить заголо­вок снап­шота, най­ти в нем ссыл­ки на все объ­екты типа Code (они как раз и хра­нят натив­ный код методов), а затем дизас­сем­бли­ровать находя­щиеся по этим адре­сам инс­трук­ции. В этом поможет инс­тру­мент Doldrums. Он выведет на экран все име­ющиеся в коде клас­сы и ука­жет, по каким адре­сам рас­полага­ется код методов.

Проб­лема это­го под­хода в том, что фор­мат снап­шота меня­ется от вер­сии к вер­сии. Тот же Doldrums отлично работа­ет для при­ложе­ний, соб­ранных с помощью Flutter 2.5, но не работа­ет для более поз­дних вер­сий.

Уни­вер­саль­ный под­ход к ана­лизу зак­люча­ется в том, что­бы модифи­циро­вать сам фрей­мворк Flutter, рас­полага­ющий­ся в биб­лиоте­ке libflutter.so рядом с libapp.so. Для это­го необ­ходимо взять исходни­ки фрей­мвор­ка той же вер­сии, добавить в них код для печати всех нуж­ных нам дан­ных (име­на клас­сов, методов и адре­са их кода), а затем соб­рать его и заменить им ори­гиналь­ный фрей­мворк в пакете при­ложе­ния.

В час­тнос­ти, мож­но внес­ти исправ­ления в метод Deserializer::ReadProgramSnapshot(ObjectStore* object_store) в фай­ле runtime/vm/clustered_snapshot.cc, что­бы зас­тавить его рас­печатать таб­лицу клас­сов. Так­же мож­но изме­нить метод void ClassTable::Print() в фай­ле runtime/vm/class_table.cc для печати более под­робной информа­ции.

В статье при­веде­но еще нес­коль­ко деталей, как это сде­лать пра­виль­но, но нет готовых фай­лов. Так что в дан­ный момент реверс‑инжи­ниринг Flutter-при­ложе­ний — дело неб­лагодар­ное и дос­таточ­но слож­ное. До появ­ления пол­ноцен­ных инс­тру­мен­тов еще год‑дру­гой.

Пример печати классов с помощью Doldrums
При­мер печати клас­сов с помощью Doldrums

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Оставить мнение