• XSS.stack #1 – первый литературный журнал от юзеров форума

Techniques Обход CET и BTI с помощью функционально-ориентированного программирования (FOP)

weaver

31 c0 bb ea 1b e6 77 66 b8 88 13 50 ff d3
Забанен
Регистрация
19.12.2018
Сообщения
3 301
Решения
11
Реакции
4 622
Депозит
0.0001
Пожалуйста, обратите внимание, что пользователь заблокирован
[Phreak 71] Bypassing CET & BTI With Functional Oriented Programming

Статья посвящена очередной реализации повторного использования кода (Code Reuse attacks)

В частности в статье расматриваетя Functional Oriented Programming (FOP) для обхода CET и BTI

https://phrack.org/issues/71/7.html#article


Дополнительная информация о Functional Oriented Programming

Function-Oriented Programming: A New Class of Code Reuse Attack in C Applications
https://ieeexplore.ieee.org/abstract/document/8433189

Bypassing Modern CPU Protections With Function-Oriented Programming
https://scholar.dsu.edu/cgi/viewcontent.cgi?article=1442&context=theses
 
Обход CET и BTI с помощью функционально-ориентированного программирования (FOP)
Переведено для xss.pro.
Оригинальная статья: https://phrack.org/issues/71/7.html#article См. первый пост в теме.
Автор статьи LMS.
Автор перевода handersen.


1. Введение
2. Общее описание FOP
3. Идентификация FOP-гаджетов
3.1 Процедура идентификации
3.2 Глубина поиска гаджетов
3.3 Примеры гаджетов
4. Диспетчер гаджетов в функционально-ориентированном программировании
4.1 Диспетчер гаджетов _dl_call_fini
4.2 Изучение link_map
5. Движок Symbolic Execution
5.1 Этап 1: Статический анализ
5.2 Этап 2: Анализ с помощью Symbolic Execution
6. Примеры
7. Заключительные соображения
7.1 Ограничения
7.2 Терминология
7.3 Различия архитектур
7.4 Полнота по Тьюрингу
7.5 Функционально-ориентированное программирование ядра
7.6 Перспективы
7.7 Возможные мероприятия по снижению или предотвращению последствий
7.8 Выводы, к которым мы пришли
8. Благодарности
9. Ссылки
10. Приложение A: Цепочка для /bin/sh в памяти ARM
11. Приложение B: Цепочка для /bin/sh в памяти Intel
12. Приложение C: Исходный код

1. Введение

Возвратно-ориентированное программирование (ROP - Return Oriented Programming) существует уже около 15 лет [1]. С тех пор и другие техники повторного использования двоичного кода, например Jump Oriented Programming (JOP) [2][3] – показали эффективность в точно таких же задачах. Из-за развития этих двух техник в течение последнего десятилетия, были разработаны новые методы защиты для ограничения их применимости. Некоторые из них включают Control-Flow Enforcement Technology (CET) [4] для процессоров Intel, а также Pointer Authentication (PAC) и Branch Target Identification (BTI) [5][6] для процессоров ARM. Назначение этих методов защиты – ограничить применение атак повторного использования двоичного кода программ, таких как ROP и JOP.

CET включает два основных механизма защиты: Теневой Стек (Shadow Stack) и Indirect Branch Targeting (IBT) [7]. Теневой Стек, это вспомогательный аппаратный стек, записывающий адреса возврата для проверки того, что последовательность управления потоком выполнения программы не была изменена. Несмотря на то, что существуют альтернативные методы манипуляции значениями во вспомогательном стеке, основной защитной мерой является проверка возвращаемых значений, таким образом ограничивая возможности ROP в таких сценариях, как переполнение буфера в стеке или смещение стека (stack pivots).

IBT дает прямое указание на конкретные места размещения для сегментов с косвенной адресацией, являются ли они регистрами или переходами/вызовами по адресам памяти. Несмотря на то, что поддержка инструкций необходимых для IBT, включена в GCC начиная с версии 8 [8], ее полная интеграция в Linux-системы, была завершена совсем недавно [9]. Как правило это инструкции размещенные в начале функций, допускающих косвенную адресацию, выполняющие роль средства защиты. В процессорах Intel, инструкцией контроля адреса возврата, является ‘ENDBR64’. В случае, когда косвенный переход или вызов не попадает на указанную инструкцию, срабатывает ловушка. Эта защитная мера, предназначена для снижения эффективности JOP-атак.

Что касается защит ARM, у них есть PAC/BTI [6]. PAC маскирует применение защиты с помощью простейшего хеширования значений в памяти, обычно указателей, хотя этот метод можно применить почти к любым значениям. Этот процесс управляет сохранением хешированных значений в неиспользуемых старших битах указателя. Такой подход возможен исходя из того, что приложения в режиме пользователя обычно используют не более 48 бит из 64 доступных в адресном пространстве.

Это можно легко реализовать с помощью нескольких инструкций в начале и в конце функции для защиты адреса возврата от несанкционированного изменения. Хотя это также может служить для проверки на другие виды повреждения памяти, полная проверка каждого потенциального указателя на функцию, пока не типична для Linux (тема для обсуждения будущих разработок).

Второй защитный механизм, BTI – по функциональности повторяет IBT, хотя и слегка отличается в терминологии. Подобно IBT, он включает инструкции указывающие на конкретный адрес возврата в начале функции для защиты от JOP-атак при косвенной адресации. В процессорах ARM, инструкцию контроля адреса возврата, не заморачиваясь назвали BTI.

Исходя из того, что оба типа процессоров (Intel, ARM), включают две основных техники снижения эффективности атак повторного использования кода, таких как ROP и JOP, использование этих техник эксплуатации уязвимостей повреждения памяти, становится крайне сложным, а то и вообще невозможным. Вместо того, чтобы пытаться обойти эти защитные механизмы, почему бы не использовать заложенные в них возможности в своих целях? Этот подход можно воплотить в жизнь с помощью функционально ориентированного программирования (FOP).

2. Общее описание FOP

Действительно, давайте проясним: FOP отклоняется от парадигм из области функционального программирования, так что любителям Haskell не стоит беспокоиться, для них здесь ничего нет. FOP или функционально ориентированное программирование, подразумевает использование функции целиком (от ее пролога до эпилога), в качестве некоего гаджета. В отличие от традиционного представления о "гаджетах" в сценариях ROP или JOP, которые как правило, просто содержат несколько инструкций, вроде классических "POP RDI; RET;" – FOP расширяет понятие гаджета на выполнение функции целиком.

Смысл использования в качестве гаджета всей функции, состоит задействовании двух полезных возможностей, которые они предоставляют. Первая возможность, это использование функции по прямому назначению. Например, через управление двумя параметрами вызываемой функции, такой как strcpy, можно манипулировать значениями в памяти. Это может выглядеть, как мелкий трюк, но это возможно распространить на любые функции в Glibc, включающие в начале блок инструкций контроля адреса возврата a. k. a. landing pad , такие как `mprotect` или `syscall`.

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

Пример этому может быть найден во встроенной функции Glibc, `__hash_string`, он используется в примере в приложении B. Штатно, эта функция применяется для получения хеша строки при операциях поиска и принимает строку в качестве первого аргумента через регистр RDI. У этой функции есть сопутствующий эффект, состоящий в перенастройке RDI на первый нулевой байт в памяти, если RDI указывает на действительный адрес.

Это зависит от архитектуры и компилятора, однако явно просматривается в некоторых версиях Libc. Хотя назначение этой функции, возвращать хеш строки, условный злоумышленник может использовать ее для увеличения значения RDI до окончания сегмента памяти. Объединение схожих побочных эффектов в последовательность, может в дальнейшем привести к модификации содержимого регистров или значений в памяти.

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

Хотя FOP не является оригинальной техникой, эта статья пытается пролить свет на возможности ее воздействия на современные системы. Идея брать в качестве гаджетов функции целиком, впервые была реализована в 2011 [10]. Основным открытием стала возможность объединения нескольких функций Return-Into-Libc в стеке, позволившей достичь функциональности, полной по Тьюрингу. Эта концепция была усовершенствована, что привело появлению Loop Oriented Programming (LOP) Циклично-Ориентированного Программирования в 2015 [11]. LOP с наилучшей стороны продемонстрировало, использование функций объединенных в цепочку с гаджетом цикла, в качестве диспетчера. Позднее, в 2018 году оно эволюционировало в FOP [12].

FOP частично схож с Counterfeit Object-Oriented Programming (COOP) [18], с тем основным отличием, что FOP обходит требования C++ и это широко применимо за пределами эффектов виртуальных таблиц. COOP, хотя и большей частью теоретическая атака, все же показывает потенциальную возможность обхода защиты CET [17].

Наглядный пример в [17], демонстрирует эффективное применение простых функций для для эксплуатации уязвимого приложения Windows, добиваясь выполнения произвольного кода путем применения подходящей стартовой функции, позволяющей управление аргументами и загрузку внешних параметров, с помощью минимально необходимой цепочки. В противоположность такому подходу, эта статья рассматривает аналогичную цепочку, целиком расположенную внутри libc, не полагаясь на загрузку строк или параметров извне, ведущих к сокращению длины цепочки.

В то время как предыдущие статьи предлагали беглое знакомство с реализацией FOP, эта статья стремится со всей тщательностью разобраться в применении FOP в среде Linux. Демонстрируя, что FOP-атака способна работать исключительно внутри Glibc, статья покажет, что FOP эффективно для текущего поколения защиты процессоров. В статье также будет уделено внимание таким аспектам, как идентификация гаджетов и сложность атаки, посредством расширения области применения FOP.

3. Идентификация FOP-гаджетов

В любой атаке с повторным использованием кода, эффективность структуры атаки, критически зависима от применяемых гаджетов. FOP следует оригинальному подходу к идентификации гаджетов, в сравнении с традиционными техниками ROP и JOP.

В ROP идентификация гаджетов обычно включает поиск инструкции возврата (0xC3 в x86-64) и обратную трассировку, чтобы выявить подходящие инструкции сформировавшиеся в байтовых комбинациях. Аналогичным образом в JOP, эта процедура включает замену инструкций возврата, инструкциями перехода в указанное место или регистр. Однако FOP представляет уход от этой традиционной методики. Хотя классические методы все еще применимы для идентификации инструкции возврата и возвращения к началу функции, управление потоком внутри функций не всегда линейно. Наоборот, оно может включать условные переходы и циклы, которые изменяют поток выполнения.

Следовательно в FOP, приходится использовать другой подход, в котором идентификация двигается вперед от выбранной стартовой позиции. К счастью защитные средства, которые FOP намеревается перехитрить, ввели блок инструкций контроля адреса возврата в обеих технологиях, и IBT и Branch Target Identification (BTI). Инструкции контроля адреса возврата, служат в качестве индикаторов для определения стартовой позиции, с которой можно продвигаться вперед в ходе идентификации гаджета.

3.1 Процедура идентификации

Несмотря на то, что фиксация стартовой позиции в функции, отмечена как ключевой момент, это не гарантирует, что функция может послужить в качестве работоспособного FOP-гаджета. Здесь, неоценимую помощь окажет технология Symbolic Execution. Symbolic Execution, включает интерпретацию инструкций, как логических операций, предпочитая их штатному выполнению программы [13]. Этот подход позволяет пройтись по всем возможным вариантам выполнения в пределах сегмента кода или в рамках двоичного файла, без установки соответствующей среды выполнения.

Техника Symbolic Execution, предлагает определенные преимущества по части идентификации потенциальных вариантов выполнения кода, обнаруживая их с помощью переменных. Однако тут не обходится без ограничений. Значительным препятствием, оказываются ложные маршруты возможного выполнения кода, которые реальных задачах – могут оказаться бесполезными. Так как Symbolic Execution не выполняет ветки кода напрямую, операторы сравнения могут быть интерпретированы неверно, ведя к таким проблемам, как разбивка пути выполнения на части, особенно при сравнении или внутри циклов.

Когда встречается оператор сравнения, Symbolic Execution разделяет путь выполнения на две возможные ветви, потенциально приводя к экспоненциальному увеличению числа потенциально обнаруженных путей выполнения. Это значительно замедляет обход всего кода и ведет к исчерпанию оперативной памяти, особенно при работе с кодом содержащим множество операций сравнения, циклов или вызовов функций. Как мы рассмотрим далее, разбивка пути выполнения на части, затрудняет идентификацию гаджетов с большим количеством инструкций. Несмотря на то, что это увеличивает время, затрачиваемое на их идентификацию, в большинстве случаев это не должно помешать обнаружению достаточно большого набора потенциальных гаджетов.

Это приводит нас к главной теме настоящего раздела – инструментарию, разработанному для идентификации FOP-гаджетов из дампов оперативной памяти [14]. Этот инструмент задействует фреймворк Symbolic Execution, чтобы зафиксировать потенциальные FOP-гаджеты и представить их пользователю. Совместимая с дампами обеих архитектур ARM и x86-64, эта программа пытается идентифицировать FOP-гаджеты во всех исполняемых областях памяти.

Рассматривая количество гаджетов в таблице ниже, мы наблюдаем значительное число гаджетов, даже при глубине всего лишь 15 инструкций. В этой таблице 'Built', обозначает софт собранный из исходников, а остальные строки представляют ПО, взятое из репозиториев соответствующих дистрибутивов Linux. Кроме того, 'Depth' представляет количество проанализированных инструкций, идущих после блока инструкций контроля адреса возврата. В столбце показано число гаджетов, содержащих инструкцию возврата при соответствующей глубине анализа:

LibraryDepth 15Depth 25Depth 50
Built (x86-64)​
142627655438
Centos (x86-64)126826184912
Ubuntu (x86-64)129426674897
Built (ARM)134821613298
OpenSuse (ARM)132020843212

В таблице выше наглядно показаны несколько протестированных версий Glibc, включая собственноручно собранный вариант с параметрами компиляции по-умолчанию вместе с необходимыми флагами безопасности, чтобы включить разобранные в статье меры противодействия ROP. Важно отметить, что количество гаджетов для этих версий Glibc, подсчитывалось в течение лета 2023 года и может не точно отражать текущую ситуацию. Это пояснение уведомляет об ограниченных возможностях идентификации библиотек Glibc, для архитектуры ARM на сегодняшний день, собранных с поддержкой BTI.

Кроме того, необходимо сказать, что эта таблица не дает исчерпывающего представления обо всех остальных доступных дистрибутивах.

3.2 Глубина поиска гаджетов

В таблице выше, показано три примера глубины поиска гаджетов. Глубина поиска гаджета, соотносится с количеством инструкций, проверенных до того момента, пока не перестают встречаться инструкции возврата. Значения 15, 25, и 50, выбраны произвольно, чтобы проиллюстрировать способ разбивки количества гаджетов в протестированных библиотеках, по категориям.

На практике, большинство гаджетов могут оказаться слишком сложны для использования или содержать массу факторов ограничивающих их функциональность – эти ограничения будут рассмотрены в следующем разделе. Это приводит к нас к точке зрения, что большинство годных гаджетов находится в пределах 15 инструкций или около того. Такие типы гаджетов, обычно содержат простые инструкции записи в регистры и быстро возвращают управление, произведя минимум работы, как демонстрируется в разделе 3.3. Такие инструкции, как ENDBR64, PUSH, POP и RET – насчитывают до 9 из 15 инструкций гаджета. Хотя эти значения не 100% влияют на конечный результат, т. к. все значения восстанавливаются – их невозможно игнорировать в ходе идентификации гаджетов.

Одно можно сказать точно, глубина поиска гаджета – это произвольная величина, по аналогии с количеством байтов, которые нужно отмотать назад от инструкции RET для при идентификации ROP-гаджета.

3.3 Примеры гаджетов

В этом разделе будут приведены несколько примеров гаджетов, для демонстрации того, как софт упоминавшийся в разделе 3.1 отображает гаджеты и как они преобразованы в код. Все примеры будут взяты из собранной нами для тестов Glibc 2.39 для архитектуры Intel. В выводе сначала будет показано, что выдал софт, а потом код ассемблера, который реализует этот вывод.

В первом примере простой гаджет, записывающий 1 в RDI:

Код:
```
-------------------------------
libc.so.6 0x139e40:
   Results:
      RAX: 0x1
      RDI: 0x1
-------------------------------
0000000000139e40 <_nss_files_endetherent>:
  139e40:       f3 0f 1e fa   endbr64
  139e44:       bf 01 00 ...  mov    edi,0x1
  139e49:       e9 f2 e6 ...  jmp    128540 <__nss_files_data_endent>
 
0000000000128540 <__nss_files_data_endent>:
  128540:       f3 0f 1e fa   endbr64
  128544:       41 54         push   r12
  128546:       55            push   rbp
  128547:       53            push   rbx
  128548:       48 8b 2d ...  mov    rbp,QWORD PTR [rip+0xb75f1]
  12854f:       48 85 ed      test   rbp,rbp
  128552:       75 0c         jne    128560 <__nss_files_data_endent+0x20>
  128554:       5b            pop    rbx
  128555:       b8 01 00 ...  mov    eax,0x1
  12855a:       5d            pop    rbp
  12855b:       41 5c         pop    r12
  12855d:       c3            ret
-----------------------------
```

Как можно понять из кода выше, гаджет зависит от значения в [rip+0xb75f1], однако в файле дампа, использовавшемся для создания гаджета это значение установлено в 0, таким образом проверка завершается неудачей и эта ветвь гарантированно выполнится. В результате в регистре RDI, сохранится изначально установленное значение 1.

Следующий гаджет, показывает возможность перемещения значений между регистрами. В данном случае, значение в регистре RDI, переносится в регистр RSI:

Код:
```
-------------------------------
libc.so.6 0x152510:
   Results:
      RAX: 0x7f309b99c000
      RSI: RDI
-------------------------------
0000000000152510 <_dl_mcount_wrapper_check>:
  152510:       f3 0f 1e fa   endbr64
  152514:       48 8b 05 ...  mov   rax,QWORD PTR [rip+0x84a6d]
  15251b:       48 89 fe      mov   rsi,rdi
  15251e:       48 83 b8 ...  cmp   QWORD PTR [rax+0xa90],0x0
  152525:       00
  152526:       74 18         je    152540 <_dl_mcount_wrapper_check+0x30>
    ...
  152540:       c3            ret
-------------------------------

```

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

Код:
```
-------------------------------
libc.so.6 0x26bb0:
   Results:
      RDI: [RDI]
   Read Constraints:
       0: Qword [RDI]
       1: Dword [16 + RDI]
   Write Constraints:
       2: Dword [16 + RDI] = 0xffffffff + [16 + RDI]
   Jump Constraints:
       Qword [RDI] != 0
       Dword [16 + RDI] != 1
-------------------------------
0000000000026bb0 <__gconv_release_step>:
   26bb0:       f3 0f 1e fa   endbr64
   26bb4:       55            push   rbp
   26bb5:       53            push   rbx
   26bb6:       48 89 fb      mov    rbx,rdi
   26bb9:       48 83 ec 08   sub    rsp,0x8
   26bbd:       48 8b 3f      mov    rdi,QWORD PTR [rdi]
   26bc0:       48 85 ff      test   rdi,rdi
   26bc3:       74 43         je     26c08 <__gconv_release_step+0x58>
   26bc5:       83 6b 10 01   sub    DWORD PTR [rbx+0x10],0x1
   26bc9:       75 32         jne    26bfd <__gconv_release_step+0x4d>
    ...
   26bfd:       48 83 c4 08   add    rsp,0x8
   26c01:       5b            pop    rbx
   26c02:       5d            pop    rbp
   26c03:       c3            ret
-------------------------------
```

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

4. Диспетчер гаджетов в функционально-ориентированном программировании

Хотя многообразие гаджетов сложно не заметить, их практическая применимость значительно снижается без диспетчера. В контексте FOP-атаки диспетчер служит этаким "дирижером" наших гаджетов. Так как FOP-атаки ограничены в модификации стека в соответствии со схемой защиты, диспетчер становится необходим для управления загрузкой и выполнением последовательности гаджетов.

По сути, диспетчер в FOP похож на применяемый в JOP, где он загружает область памяти и затем выполняет переходы или вызовы по этому адресу. Однако основное различие заключается в том, что диспетчеру необходимо работать в рамках ограничений, налагаемых существующими средствами защиты. Это подразумевает отказ диспетчером от прямых переходов, пока не будет гарантированно, что переход будет выполнен на адрес корректного блока инструкций контроля адреса возврата (landing pad). Более того, диспетчер должен обеспечивать адекватное управление началом FOP-атаки придерживаясь ограничений налагаемых современными мерами безопасности.

Когда доходит до идентификации диспетчера гаджетов, ключевым аспектом является то, как диспетчер использует доступные ресурсы. В особенности это относится к анализу манипуляций с ключевыми регистрами в процессе диспетчеризации. Эти ключевые регистры, как правило соотносятся с соглашением о вызовах функций соответствующей архитектуры и программной среды компьютера. В случае с архитектурой Intel, это регистры RDI, RSI, RDX и RCX, а у ARM – R0, R1, R2 и R3.

Те же действия, что производятся для идентификации гаджетов, так же можно применить и для идентификации диспетчеров. По ходу дела было выяснено, что все протестированные версии Glibc для всех архитектур содержали по крайней мере один диспетчер доступный в ходе штатного выполнения программ.

Один из таких диспетчеров гаджетов, описанный ниже, располагается внутри функции `_dl_call_fini`. В предыдущих версиях Glibc, этот функционал находился в `_dl_fini`. Однако начиная с версии 2.37, он был вынесен в отдельную функцию. `_dl_call_fini`, как правило вызывается при выполнении процедуры выхода в двоичном файле, либо после вызова `exit(0)`, либо когда происходит нормальный возврат из функции `main`.

4.1 Диспетчер гаджетов _dl_call_fini

Фрагмент кода, приведенный ниже взят из исходников Glibc 2.39:

C:
void
_dl_call_fini (void *closure_map)
{
  struct link_map *map = closure_map;

  /* Make sure nothing happens if we are called twice.  */
  map->l_init_called = 0;

  ElfW(Dyn) *fini_array = map->l_info[DT_FINI_ARRAY];
  if (fini_array != NULL)
    {
      ElfW(Addr) *array = (ElfW(Addr) *) (map->l_addr
                                          + fini_array->d_un.d_ptr);
      size_t sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
                   / sizeof (ElfW(Addr)));

      while (sz-- > 0)
        ((fini_t) array[sz]) ();
    }

  /* Next try the old-style destructor.  */
  ElfW(Dyn) *fini = map->l_info[DT_FINI];
  if (fini != NULL)
    DL_CALL_DT_FINI (map, ((void *) map->l_addr + fini->d_un.d_ptr));
}

Действительно, приведенный фрагмент кода демонстрирует цикл "while", который выполняет итерацию по массиву указателей на функции и вызывает их до тех пор, пока переменная sz остается больше нуля. Чтобы проникнуть в суть их характеристик и области размещения этих значений, нам следует разобраться в структуре `link_map`, задействованной для отображения в память. Ниже приводится урезанная версия этой структуры:

C:
struct link_map
  {
    /* These first few members are part of the protocol with the debugger.
       This is the same format used in SVR4.  */

    ElfW(Addr) l_addr;      /* Difference between the address in the ELF
                                  file and the addresses in memory.  */
    char *l_name;           /* Absolute file name object was found in.  */
    ElfW(Dyn) *l_ld;        /* Dynamic section of the shared object.  */
    struct link_map *l_next, *l_prev; /* Chain of loaded objects.  */

    /* All following members are internal to the dynamic linker.
       They may change without notice.  */

    /* This is an element that is only ever different from a pointer to
       the very same copy of this type for ld.so when it is used in more
       than one namespace.  */
    struct link_map *l_real;

    /* Number of the namespace this link map belongs to.  */
    Lmid_t l_ns;

    struct libname_list *l_libname;
     ElfW(Dyn) *l_info[DT_NUM + DT_THISPROCNUM
        + DT_VERSIONTAGNUM + DT_EXTRANUM + DT_VALNUM + DT_ADDRNUM];

4.2 Изучение структуры link_map

Давайте скрупулезно проведем краткий анализ предполагаемых значений и их расположения в структуре link_map.

  1. l_addr: этот член структуры, как правило указывает на первую страницу памяти выделенной программе. Он выполняет роль ссылки для доступа по различным смещениям или значениям в бинарнике. Отслеживая базовый адрес, программы могут эффективно перемещаться в своем адресном пространстве.
  2. l_info: этот член содержит смещение указателя, определяющего места хранения данных в отображенных в память страницах основной программы. В связке с `l_addr` это облегчает определение предполагаемого адреса структуры на которую указывает `l_info`. В данном контексте `l_info`, обычно указывает на динамическую таблицу, которая играет ключевую роль в процессе динамического выделения и загрузки в область памяти.
В составе динамической таблицы, примечательно наличие двух полей:

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

- DT_FINI_ARRAY_SZ: Как следует из названия, это поле обозначает размер массива `DT_FINI_ARRAY`.

Неважно, что эти значения и указатели, обычно располагаются в областях памяти двоичного файла, доступных только для чтения. Это довольно важная мера, в прошлом призванная предотвратить подделку таких массивов, снижая эффективность таких методов эксплуатации, как dtor и ctor [15].

Чтобы получить очень краткие пояснения о важности структуры `link_map`, я отобрал несколько комментариев из исходников, находящихся как раз перед определением этой структуры:

C:
/*
Структура описывает загруженный разделяемый объект. Ее члены l_next' и `l_prev', формируют цепочку всех объектов, загруженных при запуске.
Эти структуры данных существуют в адресном пространстве, используемом динамическим линковщиком времени выполнения, их модификация может привести к неприятным последствиям.
Эта структура данных, может быть изменена в будущем, если это потребуется. В пользовательских приложениях, следует избегать определения объектов этого типа.
*/

Структура `link_map`, содержит различные указатели и зарегистрированные значения, необходимые при операциях связывания. Как предупреждалось в комментариях, изменение этих значений во время выполнения, может привести к печальным последствиям. Здесь особенно ценен динамический доступ к `l_info` и `l_addr` в функции `_dl_call_fini`. Это наводит на мысль, что модификация любого из этих значений, дает возможность влиять на адрес с которого производится доступ к массиву указателей на функции. Вдобавок подстройка `l_addr`, позволяет воздействовать на переменную размера, добавляя возможностей по манипуляциям.

Изменение этих значений, содержит возможность захвата контроля над выполнением программы. Объединенные с ранее найденными гаджетами, такие модификации могут привести к полному контролю над программами, схожим по возможностям с тем, что мы видим в ROP и JOP атаках. Еще одним важным аспектом, заслуживающим внимания, является структура цикла в памяти, т. к. этот фактор может существенно повлиять на успех или неудачу FOP-атаки:

Код:
```
    10d4:       49 8d 1c d4             lea    (%r12,%rdx,8),%rbx
    10d8:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
    10df:       00
    10e0:       ff 13                   call   *(%rbx)
    10e2:       48 89 d8                mov    %rbx,%rax
    10e5:       48 83 eb 08             sub    $0x8,%rbx
    10e9:       49 39 c4                cmp    %rax,%r12
    10ec:       75 f2                   jne    10e0 <_dl_call_fini+0x50>
```

Как можно понять из приведенного фрагмента кода, `dl_fini_array` загружен в RBX по адресу 0x10d4, расположенному в конце массива и обрабатывается в цикле в обратном направлении. Это необходимо для разъяснения, что тут не фиксированный счетчик цикла, а скорее сравнение между текущим индексом (RBX/RAX) и началом массива (R12), за которым следует безусловный переход без дополнительных проверок. Следовательно, преднамеренный вызов этого цикла не приведет к модификации ключевых регистров, сохраняя таким образом их целостность.

5. Движок Symbolic Execution

В этом разделе мы тщательным образом разберемся в движке технологии Symbolic Execution и подходе применяемом в процессе идентификации гаджетов. Оригинальный инструментарий был построен на более старом и в настоящее время не поддерживаемом фреймворке под названием pysymemu [19]. Несмотря на отсутствие обновлений и поддержки Python 3, pysymemu был выбран из-за того, что он представляет один из простейших фреймворков, для понимания на низком уровне того, как извлекать нужную функциональность. Сюда включены обновления для объединения с более современными возможностями Z3 для символьных выражений.

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

`echo 0x37 > /proc/self/coredump_filter`

Подразумевается, что вы можете самостоятельно выяснить смысл значения 0x37 поподробнее, обратившись к странице справки по команде core - `man core`. По сути, этот фильтр включает функцию сохранения файлов, отображённых в память.

В целом, этот инструментарий использует модифицированную версию движка Symbolic Execution, в виде двух-ступенчатого подхода к идентификации FOP-гаджетов.

5.1 Этап 1: Статический анализ

Первый этап включает идентификацию потенциальных гаджетов, через полностью статический поиск, до тех пор пока не будет найдена инструкция возврата. Началом гаджета, является выявленная инструкция ENDBR64 для архитектуры x64 или BTI для ARM. Если максимальная глубина поиска гаджета, достигается до нахождения инструкции возврата, ветка отбрасывается и проверяется следующая.

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

Метод статического анализа, применяемый на первом этапе, можно также использовать для идентификации потенциальных диспетчеров гаджетов. Хотя наш инструмент может выдавать ложно-положительные результаты, замусоривая выдачу, это гарантирует, что ни один потенциальный гаджет не был упущен.

5.2 Этап 2: Анализ с помощью Symbolic Execution

На втором этапе инструментарий Symbolic Execution, будет использован для проверки каждого потенциального гаджета, найденного на первом этапе. С помощью Symbolic Execution будет создана рабочая среда, максимально приближенная к реальному выполнению программ, но использующая для идентификации гаджетов файл дампа. Это включает в себя размещение сегментов памяти и ее исполняемых областей для анализа потенциальных обращений к памяти или передачи значений. Все операции отслеживаются и между проверками потенциальных гаджетов, их состояние восстанавливается для обеспечения согласованности.

В течение работы Symbolic Execution, есть несколько возможных условий завершения, известных как тупики. Это включает возможные бесконечные циклы, которые могли быть пропущены на 1 этапе. Другим примером являются недопустимые операции, такие как сравнение значения в памяти со значением регистра внутри функции. Из-за того, что статический анализ мог не дать достаточной информации о состоянии памяти или значениях регистров, в результате в Symbolic Execution передастся множество возможных веток выполнения. Если Symbolic Execution определит, что в памяти отсутствуют требуемые значения, ветка выполнения может быть сочтена невозможной и отброшена.

Тупиковые пути выполнения, также могут появиться, если при статическом анализе находится некий вариант выполнения, предполагающий статический набор инструкций, а в ходе динамического анализа с помощью Symbolic Execution, выполнение идет по другому пути. В результате наш инструментарий, определяет путь выполнения, как не подходящий, потому что он расходится с предположением, определенном на 1 этапе.

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

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

6. Примеры

Хотя примеры в тексте кому-то могут показаться обыденными, те кто способен оценить такие иллюстрации, пожалуйста, оставайтесь здесь! В примерах, представленных в этой статье, будет показано использование собственной сборки GLibc 2.37 для ускоренной идентификации гаджетов. Эти версии в комплекте с примерами, которые использовались для идентификации вместе с готовыми цепочками и исходниками разложенными по тестовым задачам – находятся в конце статьи в приложении C.

Тесты охватывают простую уязвимость кучи, которая дает возможность произвольной записи в память. Для демонстрации этого, приведен базовый сценарий утечки памяти, демонстрирующий полу-произвольный характер утечки адреса в приведенном примере возможных операций над кучей. После того, как достигается возможность произвольной записи в память, `ld_addr` переписывается адресом наших данных в куче, таким образом включая перенаправление на описанную нами FOP-цепочку.

Ниже, в приложениях A и B, представлены две FOP-цепочки, наглядно демонстрирующие возможность манипулировать регистрами, памятью и состоянием выполнения, в степени, достаточной для записи в память строки "/bin/sh\x00" и выполнения в ее контексте функции `system`, для архитектур Intel и ARM.

Кроме того, в каталоге с репозиторием инструментария для Symbolic Execution, содержатся дополнительные примеры, такие как запись в память произвольного шеллкода и манипулирование областью памяти с помощью `mprotect` с последующим выполнением шеллкода.

Однако шеллкод для простого „/bin/sh“ не был включён в эту статью, так как его FOP-цепочка состоит из очень большого количества гаджетов, что затрудняет визуальное восприятие.

Цепочка "/bin/sh" для Intel, в общем включает 123 гаджета, 12 из которых уникальны, а для ARM содержит порядка 140 гаджетов, из которых уникальны 15.

7. Заключительные соображения

Как проиллюстрировано в этой статье, появление FOP в качестве многогранной техники обладает потенциалом успешно заменить ROP и JOP в области атак повторного использования кода. Это совмещается с тем фактом, что FOP обладает возможностью обхода таких средств защиты, встроенных в современные процессоры, как CET и PAC/BTI.

7.1 Ограничения

Однако несмотря на возможности FOP, при управлении основанными на нем атаками, должны соблюдаться несколько условий. Во-первых, так же как для ROP или JOP, для начала FOP-атаки нужна уязвимость повреждения памяти. Это как правило требует возможности произвольной записи для получения достаточного уровня доступа к диспетчеру, чтобы далее можно было указать на цепочку, контролируемую атакующим.

Во-вторых, как и для предыдущих атак ROP, в качестве предварительного условия, часто требуется наличие утечки памяти. С повсеместным распространением Рандомизации Размещения Адресного Пространства (ASLR) и Позиционно Независимого Выполнения (PIE), обход этих защит остается для техник FOP сложной задачей.

И наконец, наверное самое значительное препятствие, заключается в размере FOP-цепочек. Т. к. эти атаки требуют больше замысловатых последовательностей гаджетов – цепочки быстро распухают в размерах. К примеру, образец произвольного шеллкода, упомянутый в этой статье в разделе 6, насчитывает очень большое количество гаджетов, как для ARM, так и для Intel, просто для записи в память небольшой последовательности байт. Эта необходимость может усложнить реализацию FOP-атак по сравнению с ROP, в которых для достижения тех же целей, обычно требуется всего несколько гаджетов.

7.2 Терминология

Размышляя о присвоении наименования этой технике, следует ли назвать ее FOP или же использовать предыдущий термин – LOP, показалось разумным учесть соглашение о наименованиях остальных подобных техник. Как правило эти наименования, происходят преимущественно от типа гаджетов, а не от способа их запуска. Например JOP, назвали именно так из-за того, что гаджеты, преимущественно используют инструкции перехода, а не потому, что диспетчер все эти переходы содержит. По аналогии, Call Oriented Programming (COP), названо по типу преобладающих гаджетов, а не характеру из вызова.

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

7.3 Различия архитектур

Заслуживающим внимания аспектом в области FOP, является сравнение функциональности между архитектурами Intel и ARM. Архитектура ARM может "похвастаться" большей функциональностью в области FOP, если сопоставлять с Intel. Хотя это может быть не совсем очевидно в приведенных примерах, поскольку при рассмотрении важных регистров, используемых в FOP, был использован подход, не зависящий от архитектуры, это стоит отметить.

В архитектуре ARM, регистр R0 предназначен для хранения первого параметра функции и возвращаемого значения, позволяя FOP воспользоваться не только сопутствующими эффектами работы функции, но и возвращаемым ей значением. Это придает устройствам на ARM, дополнительную степень свободы в применении техник FOP. Архитектура Intel же, наоборот лишена этой возможности, так как инструкция возврата, использует регистр RAX. Вследствие этого, на архитектуре Intel невозможно идентифицировать гаджеты, использующие значения из регистра RAX. Это расхождение логически следует из того, что регистр RAX не предполагался в качестве регистра параметров в спецификации x86_64, в отличие от ARM.

7.4 Полнота по Тьюрингу

Несмотря на то, что наша статья не углубляется в эту область, автор думает, что FOP может достигнуть полноты по Тьюрингу, собрав в программу достаточный набор функций. FOP выходит за пределы традиционного применения функций из состава библиотеки Glibc, при этом, когда оценивается полнота по Тьюрингу набора гаджетов – учитывается также и применение сопутствующих эффектов этих функций.

7.5 Функционально-ориентированное программирование ядра

Хотя, это не рассматривалось в рамках этой статьи, важно отметить, что FOP прекрасно показал себя в применении к экземплярам ядра. Учитывая обширный набор функций и возможностей внутри ядра, неудивительно, что там обнаруживается изобилие гаджетов и диспетчеров для выбора в области FOP. Следующий фрагмент кода из ядра Linux [16], отлично иллюстрирует одну такую функцию, которая вероятно сможет служить диспетчером гаджетов беря на себя управление RDI или ненулевым RSI. Этот сценарий возможен, поскольку методы повреждения кучи часто могут привести к первичному управлению регистрами с самого начала:

C:
void destroy_params(const struct kernel_param *params, unsigned num)
{
    unsigned int i;

    for (i = 0; i < num; i++)
        if (params[i].ops->free)
            params[i].ops->free(params[i].arg);
}

7.6 Перспективы

Относительно перспектив развития, в области FOP существует несколько направлений. Хотя существующий инструментарий, вполне в состоянии идентифицировать FOP-гаджеты в представленных примерах, есть еще над чем работать в части скорости или техники анализа. В добавок к этому, несмотря на то, что FOP демонстрирует успешное применение в Linux-окружении, есть возможность расширить его применимость к остальным операционным системам, таким как Windows или Apple.

Учитывая, что Apple внедрила поддержку PAC и BTI в новые модели своих устройств, анализ этих технологий, может дать ценную информацию для эксплуатации в будущем. Исследование возможностей применения FOP в этих окружениях, могло бы открыть новые пути для атак повторного использования кода и улучшить наше понимание современных систем защиты. Поэтому в будущих исследованиях стоит сосредоточиться на адаптации техник FOP к выполнению в среде различных операционных систем, включая платформы Windows и Apple.

7.7 Возможные мероприятия по снижению или предотвращению последствий

И наконец, разумеется существуют возможности по снижению или предотвращению последствий применения техники FOP. Хотя разработчики Glibc могут решить, что относительно просто встроить PAC-аутентификацию в структуру `link_map` в инсталляциях Linux на ARM, это не решает фундаментальной проблемы. Такой патч можно обойти, задействовав подходящие диспетчеры найденные в основной программе или в дополнительных библиотеках для достижения аналогичного эффекта.

В качестве альтернативы, более эффективным подходом к уменьшению последствий FOP-атак, является включение инструкций очистки в конец каждой функции. Это обнуляло бы регистры параметров в обеих архитектурах, и Intel и ARM. В архитектуре Intel это не представляет никакой проблемы, т. к. значения регистров параметров являются временными и не используются после вызова функции повторно. Однако в архитектуре ARM, возможны осложнения с регистром R0, т. к. он не может быть обнулен, из-за того, что содержит возвращаемое значение.

Это позволяет потенциальным гаджетам, модифицировать значение в R0 и впоследствии использовать его в следующих вызовах при построении цепочки, основанной на единственном регистре параметров. Хотя в библиотеке Glibc, не выявлено гаджетов, которыми можно это провернуть, не стоит рассчитывать, что они не появятся в будущем или не обнаружатся при исследовании большего объема кода. Данный подход к смягчению последствий FOP-атак, не защищает против использования в FOP функций по прямому назначению, в особенности когда парамерами диспетчера, можно управлять через уязвимость повреждения памяти. Такой диспетчер показан в разделе 7.5, в качестве одного из примеров этого.

7.8 Выводы, к которым мы пришли

Итак, в этой статье были тщательно рассмотрены тонкости Функционально-Ориентированного-Программирования (FOP) - техники, показавшей себя перспективным продолжением традиционных атак повторного использования кода, таких как Возвратно-Ориентированное-Программирование (ROP) и Jump-Oriented Programming (JOP). Проведя всестороннее изучение и анализ, мы раскрыли гибкость и потенциал FOP применительно к различным архитектурам, включая ARM и Intel. Глядя наперед, будущее FOP содержит потенциал для дальнейших исследований и развития с возможностями применения за пределами Linux-окружений. Так, как сфера безопасности эволюционирует, а противник к этому подстраивается – понимание и умение разобраться в тонкостях FOP, окажет неоценимую помощь в совершенствовании защитных стратегий и предохранении от возникающих угроз.

8. Благодарности

Огромное спасибо Rewzilla, за те знания, что они мне дали и время, которое они уделили. Не только, потому что они проверили мою статью, но и за то, что они привели меня в мир бинарной эксплуатации и низкоуровневого программирования на ассемблере. Без их волшебного пинка в этот мир информационных технологий, я бы не был тем, кем стал сейчас.

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

[1] H. Shacham, "The geometry of innocent flesh on the bone:

return-into-libc without function calls (on the x86)," October, 2007.


[2] S. Checkoway, L. Davi, A. Dmitrienko, A.-R. Sadeghi, H. Shacham, and

M. Winandy, "Return-oriented programming without returns," October, 2010.


[3] T. Bletsch, X. Jiang, V. W. Freeh, and Z. Liang, "Jump-oriented

programming: a new class of code-reuse attack," March, 2011.


[4] T. Garrison, "Intel CET Answers Call to Protect Against Common

Malware Threats," May, 2020. https://newsroom.intel.com/editorials/intel-cet-answers-call-protect-common-malware-threats/

[5] A. Mujumdar, "Armv8.1-M architecture: PACBTI extensions -

Architectures and Processors blog - Arm Community blogs - Arm Community."

April, 2021.


[6] Qualcomm, "Pointer Authentication on ARMv8.3: Design and Analysis of

the New Software Security Instructions." January, 2017.


pointer-auth-v7.pdf

[7] Intel, "Intel vPro® PCs Feature Silicon-Enabled Threat Detection."

November, 2022. https://www.intel.com/content/www/u...-silicon-enabled-threat-protection-paper.html

[8] M. Larabel, "Control-Flow Enforcement Technology Begins To Land In

GCC 8." August, 2017.


[9] P. Zijlstra, "[PATCH 00/29] x86: Kernel IBT." February, 2022.


[10] M. Tran, M. Etheridge, T. Bletsch, X. Jiang, V. Freeh, and P. Ning,

"On the Expressiveness of Return-into-libc Attacks," 2011.


[11] B. Lan, Y. Li, H. Sun, C. Su, Y. Liu, and Q. Zeng, "Loop-Oriented

Programming: A New Code Reuse Attack to Bypass Modern Defenses," August,

2015. http://ieeexplore.ieee.org/document/7345282/

[12] Y. Guo, L. Chen, and G. Shi, "Function-Oriented Programming: A New

Class of Code Reuse Attack in C Applications," May, 2018.


[13] T. Avgerinos, A. Rebert, S. K. Cha, and D. Brumley, "Enhancing

Symbolic Execution with Veritesting," May, 2014.


[13a] Enhancing Symbolic Execution with Veritesting

[13b] TECHNIQUES TO FACILITATE SYMBOLIC EXECUTION OF REAL-WORLD PROGRAMS

[14] LMS57, "LMS57/FOP_Mythoclast." January, 2024.


[15] Juan M. Bello Rivas, "Overwriting the .dtors section." December,

2000. https://lwn.net/2000/1214/a/sec-dtors.php3

[16] Linus Torvalds, "linux/kernel/params.c at master · torvalds/linux."


[17] Offsec, "Bypassing Intel CET with Counterfeit Objects" August, 2022.

http://xssforum7mmh3n56inuf2h73hvhnzobi7h2ytb3gvklrfqm7ut3xdnyd.onion/threads/76588/

[18] F. Schuster, T. Tendyck, C. Liebchen, L. Davi, A.-R. Sadeghi, and T.

Holz, “Counterfeit Object-oriented Programming: On the Difficulty of

Preventing Code Reuse Attacks in C++ Applications,” May, 2015.


[19] feliam, “PySymEmu.” https://github.com/feliam/pysymemu

Код:
+-------------------------+----------------------+
|      Function Name      | Equivalent Operation |
+-------------------------+----------------------+
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| __libc_current_sigrtmin | MOV X0, 0x22         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| xdrmem_create...        | MOV [X0], X3   #'/'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| __libc_current_sigrtmin | MOV X0, 0x22         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __deadline_from_...     | ADD X2, X2, X0       |
| pthread_barrierattr_... | MOV X0, 0x16         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| _svcauth_short          | MOV X0, 0x2          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'b'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| __profile_frequency     | MOV X0, 0x64         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| _svcauth_short          | MOV X0, 0x2          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| _svcauth_short          | MOV X0, 0x2          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'i'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| dysize                  | MOV X0, 0x16D        |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'n'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| __libc_current_sigrtmin | MOV X0, 0x22         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'/'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| dysize                  | MOV X0, 0x16D        |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| _svcauth_short          | MOV X0, 0x2          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'s'  |
| __gconv_compare_...     | MOV X3, 0x0          |
| free_mem                | MOV X2, 0x0          |
| __profile_frequency     | MOV X0, 0x64         |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdr_void@GLIBC_2.17     | MOV X0, 0x1          |
| __deadline_from_...     | ADD X2, X2, X0       |
| inet6_option_init       | MOV X3, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_barrierattr_... | MOV X2, X0           |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| pthread_getcpuclock...  | MOV X0, 0x3          |
| __deadline_from_...     | ADD X2, X2, X0       |
| xdrmem_create...        | MOV [X0], X3   #'h'  |
| __mq_nofity_fork_...    | MOV X0, LIBC         |
| system                  | SYSTEM               |
| _exit                   | EXIT                 |
+-------------------------+----------------------+

Код:
+---------------------------+--------------------------+
|       Function Name       |   Equivalent Operation   |
+---------------------------+--------------------------+
| _nss_files_endpwent       | MOV RDI, 0x6             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'/'  |
| endttyent                 | MOV RDI, 0x0             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __libc_sa_len             | SUB RDI, 0x1             |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'b'  |
| _nss_files_endpwent       | MOV RDI, 0x6             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'i'  |
| endttyent                 | MOV RDI, 0x0             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __libc_sa_len             | SUB RDI, 0x1             |
| __li bc_sa_len            | SUB RDI, 0x1             |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'n'  |
| _nss_files_endpwent       | MOV RDI, 0x6             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'/'  |
| _nss_files_endhostent     | MOV RDI, 0x3             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'s'  |
| _nss_files_endprotoent    | MOV RDI, 0x5             |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| __cache_sysconf           | SUB RDI, 0xB9            |
| _dl_mcount_wrapper...     | MOV RSI, RDI             |
| __default_pthread_attr... | MOV RDI, LIBC            |
| __hash_string             | SET RDI TO END OF STRING |
| _dl_tunable_set_...       | MOV RDX, 0x1             |
| __memset_sse2...          | MOV [RDI], SIL     #'h'  |
| __default_pthread_attr... | MOV RDI, LIBC            |
| system                    | SYSTEM                   |
+---------------------------+--------------------------+

12. Приложение C: Исходный код

FOP_Mythoclast.tar.gz

*прим. переводчика:

Если не удается скачать архив по ссылке, открываете ссылку на оригинальную статью, идете в Appendix C: Source Code, копируете в текстовый редактор начиная с

Код:
begin 644 FOP_Mythoclast.tar.gz
M'XL(`````````^Q;<7/:2++?O_TI

до

Код:
-,_/S_P/JI`YL``@&`0``
`
end


|=[ EOF ]=---------------------------------------------------------------=|

, сохраняете под именем FOP_Mythoclast.tar.gz и затем распаковываете любым современным архиватором.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх