Липосакция для fat binary. Ломаем программу для macOS с поддержкой нескольких архитектур

Содержание статьи

  • Немного теории
  • Intel
  • ARM
  • Патчим плагин

Мы мно­го раз писали о взло­ме прог­рамм для Windows. Для нее соз­дано мно­жес­тво отладчи­ков, дизас­сем­бле­ров и дру­гих полез­ных инс­тру­мен­тов. Сегод­ня же мы обра­тим взор на муль­тип­роцес­сорную прог­рамму для macOS, вер­нее, на пла­гин для маков­ско­го Adobe Illustrator CC 2021, который (в целях обу­чения!) будет прев­ращен из проб­ной вер­сии в пол­ноцен­ную. При­чем понадо­бят­ся нам исклю­читель­но инс­тру­мен­ты для Windows: IDA вер­сии 7.2 и Hiew.

warning

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

 

Немного теории

Для начала корот­ко попыта­емся получить пред­став­ление, что имен­но нам пред­сто­ит ломать. Мы уже при­вык­ли, что все исполня­емые фай­лы и биб­лиоте­ки под акту­аль­ные вер­сии Windows име­нуют­ся EXE/DLL и име­ют струк­туру MZ-PE. Под macOS исполь­зует­ся фор­мат Mach-O (сок­ращение от Mach object), явля­ющий­ся потом­ком фор­мата a.out, который макось унас­ледова­ла от Unix.

Как извес­тно, Apple любит пери­оди­чес­ки перехо­дить с одно­го семей­ства про­цес­соров на дру­гое, из‑за чего меня­ется и архи­тек­тура при­ложе­ний. Начав с PowerPC, Apple в середи­не нулевых перемет­нулась в стан Intel, пос­ле чего в недав­нем прош­лом кор­порация решила перей­ти на плат­форму ARM. Дабы поль­зовате­ли помень­ше стра­дали от подоб­ных метаний, был взят на воору­жение муль­тип­роцес­сорный фор­мат Fat binary («жир­ный бинар­ник»), который может содер­жать код одновре­мен­но под нес­коль­ко про­цес­соров. Такой модуль может работать как под Intel, так и под ARM.

Что же такое модуль Mach-O? Обыч­но он сос­тоит из трех областей. Заголо­вок содер­жит общую информа­цию о дво­ичном фай­ле: порядок бай­тов (магичес­кое чис­ло), тип про­цес­сора, количес­тво команд заг­рузки и т. д. Затем сле­дуют коман­ды заг­рузки — это сво­его рода оглавле­ние, которое опи­сыва­ет положе­ние сег­ментов, динами­чес­кую таб­лицу сим­волов и про­чие полез­ные вещи. Каж­дая коман­да заг­рузки содер­жит метадан­ные, такие как тип коман­ды, ее имя, позиция в дво­ичном фай­ле. Наконец, третья область — это дан­ные, обыч­но самая боль­шая часть объ­ектно­го фай­ла. Она содер­жит код и раз­личную допол­нитель­ную информа­цию.

Муль­тип­роцес­сорный «жир­ный» модуль может вклю­чать в себя нес­коль­ко обыч­ных модулей Mach-O, заточен­ных под раз­ные про­цес­соры (обыч­но это i386 и x86_64, ARM или ARM64). Струк­тура его пре­дель­но прос­та — сра­зу за Fat header, в котором опи­сыва­ются вхо­дящие в модуль бло­ки Mach-O, сле­дует код этих бло­ков, рас­положен­ный под­ряд. Я не буду под­робно оста­нав­ливать­ся на опи­сании всех сек­ций и полей дан­ного фор­мата, жела­ющие могут лег­ко нагуг­лить спе­цифи­кацию. Оста­новим­ся лишь на фор­мате заголов­ков, пос­коль­ку они понадо­бят­ся нам в даль­нейших дей­стви­ях.

Струк­тура клас­сичес­кого заголов­ка Mach-O выг­лядит так.

struct mach_header { // Сигнатура, обычно CF FA ED FE или CE FA ED FE, но для варианта с обратным порядком байтов, возможна и обратная сигнатура FE ED FA CF uint32_t magic; // Тип процессора, для intel это 7, для ARM — С cpu_type_t cputype; // Подтип процессора, 1 означает 64-разрядность, например, 07000001h — x86_64 cpu_subtype_t cpusubtype; // Тип файла uint32_t filetype; // Количество команд, следующих за хидером uint32_t ncmds; // Размер команд, следующих за хидером uint32_t sizeofcmds; // Набор битовых флагов, которые указывают состояние некоторых дополнительных функций формата файла Mach-O uint32_t flags;};

«Жир­ный заголо­вок» пред­став­ляет собой типич­ный заголо­вок Universal binary и выг­лядит вот так.

struct fat_header { uint32_t magic; // 0BEBAFECAh // Количество последующих блоков fat_arch, соответствующих поддерживаемым процессорам uint32_t nfat_arch;};struct fat_arch { cpu_type_t cputype; cpu_subtype_t cpusubtype; // Смещение до блока кода относительно начала файла uint32_t offset; // Длина соответствующего блока кода uint32_t size; // Выравнивание uint32_t align;};struct fat_arch { ...}

Струк­тура заголов­ка Mach-O

Ну а теперь, ког­да мы в дос­таточ­ной сте­пени воору­жились теорией, рас­смот­рим прак­тичес­кий при­мер. У нас есть некий инстал­лирован­ный иллюс­тра­торов­ский пла­гин, который тре­бует­ся оту­чить от суици­да по про­шес­твии три­аль­ного пери­ода. Пред­положим так­же, что дос­тупа к маку, на котором он уста­нов­лен, у нас нет, как и дру­гого мака под рукой — толь­ко воз­можность перепи­сывать фай­лы. Ищем в пап­ке нуж­ного пла­гина под­папку ContentsMacOS, а в ней — исполня­емый модуль пла­гина. В дан­ном слу­чае это динами­чес­кая биб­лиоте­ка Fat Mach-O file, о чем нам говорит сиг­натура CA FE BA BE.

Источник: xakep.ru

Ответить

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