Как пользоваться Cff Explorer

Обновлено: 21.11.2024

Приключения в вредоносном ПО с двойным щелчком / Ануй Сони

Точно так же, как хирург должен понимать человеческое тело и его части, чтобы преуспеть в хирургии, специалист по обратному анализу вредоносного ПО должен понимать структуру и компоненты двоичного файла, чтобы иметь опыт анализа вредоносного ПО. В операционной системе Windows мы имеем в виду формат Portable Executable (PE).

В этой статье не будут обсуждаться все мучительные детали исполняемого файла Windows. Если вы хотите избавиться от этого зуда, прочтите Microsoft PE Format and Peering внутри статей по PE или начните читать о структурах, определенных в winnt.h. Не поймите меня неправильно — это отличные справочные статьи, и я буду ссылаться на них в этом посте, но рассмотрение каждого ресурса целиком может быть непосильным.

Для этого обсуждения мы будем перемещаться по PE-файлу, уделяя основное внимание полям, связанным с импортированными библиотеками DLL и функциями двоичного файла. Я надеюсь, что, сосредоточившись только на этом одном аспекте, вы (1) изучите подход к управлению структурой PE и (2) примените этот подход, чтобы лучше понять терминологию, связанную с импортом исполняемого файла.

Для этого обсуждения я буду использовать свободно доступный инструмент CFF Explorer, который является частью NTCore Explorer Suite. Кроме того, мой целевой файл — приготовьтесь — notepad.exe. Зачем использовать законный файл для этого упражнения? Во-первых, чтобы понять структуру PE-файла, вам не нужно вредоносное ПО. Во-вторых, более глубокое понимание законных файлов позволяет вам легче обнаруживать аномалии при анализе подозрительных файлов. Чтобы узнать больше по этой теме, прочитайте мой предыдущий пост об анализе файлов, а не вредоносных программ.

Начнем наше путешествие по файловому формату PE, ну с самого начала. После загрузки notepad.exe в CFF Explorer слева вы увидите заголовки, которые содержат первые байты типичного исполняемого файла Windows. Эти заголовки описывают остальную часть файла, включая исполняемый контент, ресурсы и импорт.


Рисунок 1. Заглушка MS-DOS

Начнем с заголовка MS-DOS (также называемого заглушкой MS-DOS), в котором отображается сообщение «Эта программа не может быть запущена в режиме DOS», когда исполняемый файл запускается в MS-DOS. В начале этого заголовка (см. правый верхний угол рисунка 1) находится поле e_magic, содержащее хорошо известные символы «MZ», представленные шестнадцатеричным значением 0x4D5A (выше показано как 0x5A4D, поскольку значение интерпретируется как малозначительное). порядок байтов). Большинство полей в этом заголовке не относятся к более новым операционным системам, но последнее поле e_lfanew (см. ниже) важно, поскольку оно указывает на заголовок PE, отображаемый в CFF Explorer как Nt заголовков.

Рисунок 2. Указатель (адрес) на заголовок PE

Нажав на Nt Headers (ниже), мы перейдем к смещению файла 0xF0, которое соответствует значению e_lfanew выше. Значение преобразуется в строку «PE», которая обычно появляется в начале заголовка PE.

Рис. 3. Заголовок PE

Следующим на нашем пути является заголовок файла COFF, отображаемый просто как заголовок файла в проводнике CFF. Этот заголовок включает такую ​​информацию, как тип целевой машины (например, x64), отметку времени компиляции и характеристики файла (например, является ли исполняемый файл DLL или EXE?).


Рисунок 4. Заголовок файла

Затем у нас есть необязательный заголовок. Кстати, этот заголовок является «необязательным» для таких файлов, как объектные файлы, которые не являются исполняемыми напрямую. Для файлов изображений, таких как notepad.exe, которые являются непосредственно исполняемыми, этот заголовок обязателен. Он содержит множество информации, которая поддерживает загрузку исполняемого файла в память. Стоит упомянуть одно поле — ImageBase (ниже), которое указывает предпочтительный адрес, по которому исполняемый файл должен быть отображен в памяти. Если ALSR включен, этот адрес рандомизирован.

Рисунок 5. Необязательный заголовок

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

Рисунок 6. Каталоги данных

Как каталог импорта, так и каталог таблицы адресов импорта имеют значения RVA и размера. Размер прост в том смысле, что он указывает размер таблицы в байтах. Относительный виртуальный адрес (RVA) относится к расположению указанной таблицы. RVA — это виртуальный адрес, потому что это адрес после загрузки исполняемого файла в память (т. е. после его «отображения в память»). Это относительно базы изображений, поэтому добавление RVA в базу изображений обеспечивает виртуальный адрес (VA) в памяти указанной таблицы.

Последние заголовки, которые мы видим слева в CFF Explorer, — это заголовки разделов:

Рис. 7. Заголовки разделов

Содержимое исполняемого файла Windows после заголовков упорядочено по разделам. В приведенной выше таблице представлена ​​важная информация об имени, расположении (как на диске, так и в памяти) и характеристиках каждого раздела. Ключевые разделы включают «.text» для исполняемого кода, «.rdata» для данных, доступных только для чтения, и «.rsrc» для ресурсов, таких как значки.

Возможно, вы заметили на рис. 6, что обе выделенные строки RVA имеют «.rdata» в столбце «Разделы», что указывает на то, что обе таблицы находятся в этом разделе. Как это определили? Во-первых, см. значение виртуального адреса .rdata на рисунке 7, которое равно 0x1A000. Я должен уточнить, что в этом столбце перечислены RVA, а не VA, как следует из заголовка столбца. Далее обратите внимание на виртуальный размер .rdata 0x73A8. Выполнение простой математики показывает, что раздел .rdata будет расширяться от RVA 0x1A000 до 0x213A7 (включительно). Возвращаясь к рисунку 6, RVA как для каталога импорта, так и для каталога таблицы адресов импорта (0x1F300 и 0x1A620 соответственно) попадают в этот диапазон.

Импорт каталога RVA — 0x0001F300, а ImageBase notepad.exe — 0x140000000, поэтому VA — 0x14001F300. Что находится по этому адресу? Просмотр этого смещения в файле на диске бесполезен, так как, как упоминалось ранее, VA является адресом в памяти. В результате мы должны использовать инструмент, который будет загружать наш исполняемый файл подобно тому, как загрузчик Windows готовился бы к выполнению. Один из подходов заключается в использовании дизассемблера, такого как IDA Pro, который загружает исполняемый файл в память так же, как загрузчик Windows во время выполнения файла. В этом примере я буду использовать IDA Freeware версии 7.0 для Windows.

При загрузке notepad.exe в IDA вы увидите показанное ниже окно с параметрами загрузки. Я рекомендую снять флажок «Создать сегмент импорта», по крайней мере, на данный момент. Если оставить этот флажок установленным, IDA создаст раздел «.idata» для импорта, и для этого обсуждения я предпочитаю более точно представлять необработанный двоичный файл, не создавая дополнительных разделов.

Рис. 8. Параметры загрузки файла IDA

После нажатия кнопки "ОК" вы также увидите запрос, хотите ли вы воспользоваться отладочной информацией. Пока выберите «Нет».

Далее перейдем к виртуальному устройству 0x14001F300, набрав «g» и вставив адрес:

Рис. 9. Переход к виртуальному каталогу импорта в IDA.

Обратите внимание, что переход по указанному выше адресу предполагает, что загрузчик будет учитывать адрес в поле ImageBase. IDA Pro использует этот подход, но загрузчик Windows и другие дизассемблеры, такие как x64dbg, рандомизируют ImageBase, если для этого исполняемого файла не отключен ASLR (дополнительную информацию по этому вопросу см. в статье Ленни Зельцера здесь).

Рисунок 10. Начало таблицы каталога импорта

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

  1. Импортировать таблицу имен (как показано в IDA Pro) или Импортировать таблицу поиска (как описано в документации Microsoft) RVA: указывает на список имен функций, импортированных из указанной библиотеки DLL. Используя первую запись в качестве примера, двойной щелчок на off_14001F558 приведет нас к расположению ниже:

Рисунок 11. Импорт таблицы имен

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

Рисунок 12. Таблица подсказок/имен

Это начало таблицы подсказок/имен. Мы видим ссылки на функции, включая OpenProcessToken, GetTokenInformation и DuplicateEncryption — все функции, импортированные из advapi32.dll. Это имеет смысл, поскольку мы пришли сюда после двойного щелчка по первой записи в таблице имен импорта advapi32.dll.

Одна таблица подсказок/имен охватывает все импортированные функции для файла. Каждая запись в таблице состоит из 3 компонентов:

  • Подсказка. Это указатель в импортированной библиотеке DLL, который помогает найти нужную функцию. В первой записи таблицы подсказки/имени выше значение равно 0x214.
  • Имя: имя импортированной функции, оканчивающееся нулем. Это используется для поиска импортированной функции в DLL, когда использования подсказки недостаточно. В первой записи выше это значение OpenProcessToken.
  • Заполнение: директива IDA Pro «align» относится к заполнению 0 байтами.
<р>2. Отметка времени: обычно это значение равно нулю, если DLL не привязана. Связывание DLL выходит за рамки этой статьи, но подробнее см. в этой статье.

<р>3. Цепочка пересылки: DLL может ссылаться на функциональность другой DLL, но, как и в поле «Отметка времени» выше, это значение обычно равно нулю. Опять же, детали этого поля выходят за рамки этой статьи, но вы можете выполнить поиск по фразе «ForwarderChain» в этой статье для получения дополнительной информации.

<р>4. Имя DLL RVA: указатель (адрес) на имя импортируемой DLL. В случае advapi32.dll имя DLL RVA указывает на строку «ADVAPI32.DLL».

<р>5. Таблица адресов импорта (IAT) RVA: Во-первых, следует понимать, что таблица адресов импорта заполняется загрузчиком, когда исполняемый файл и его импортированные библиотеки DLL отображаются в память, и это таблица указателей на импортированные функции. Каждая запись в таблице называется «преобразователем», а сама таблица называется «таблицей преобразователей». Имея это в виду, RVA в этом поле указывает на адрес импортированной функции в IAT. Например, двойной щелчок по OpenProcessToken по адресу 0x14001F310 на рис. 10 приведет нас к расположению ниже.

Рисунок 13. Импорт таблицы адресов

Ссылка на OpenProcessToken по адресу 0x14001A620 представляет собой адрес в памяти, где находится код функции. Другими словами, на 0x14001A620 ссылаются, когда OpenProcessToken вызывается в notepad.exe. Чтобы подчеркнуть этот момент, выделите OpenProcessToken и нажмите «x» на клавиатуре. В окне внешних ссылок (ниже) показан вызов API OpenProcessToken.

Рисунок 14. Ссылки OpenProcessToken

Также обратите внимание, что первый адрес 0x14001A620 на рис. 13 соответствует RVA каталога таблицы адресов импорта, указанному на рис. 6, если вы добавите ImageBase. Это имеет смысл, поскольку на рис. 13 показано начало каталога таблицы адресов импорта.

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

  1. Начало с заголовка MS-DOS
  2. Обнаружен заголовок PE.
  3. Обнаружил базу изображений в необязательном заголовке.
  4. Просмотр различных каталогов данных
  5. Переход к таблице импорта каталога VA с помощью IDA.
  6. Проверил компоненты записи таблицы каталогов импорта, включая таблицу поиска импорта.
  7. Найдена ссылка на таблицу подсказок/имен.
  8. Заканчивается таблицей адресов импорта, которая указывает на импортированные функции в памяти.

Если вы хотите узнать больше о заголовке PE и структурах, которые он включает, есть много отличных ресурсов для изучения. Ниже приведены некоторые из моих любимых:

Об авторе:
Анудж Сони — старший исследователь угроз в компании Cylance, где он занимается исследованием вредоносных программ и реверс-инжинирингом. Он также является сертифицированным инструктором SANS и соавтором курса FOR610: Reverse-Engineering Malware. Если вы хотите узнать больше о стратегиях анализа вредоносных программ, присоединяйтесь к нему на предстоящем курсе SANS FOR610.

Цель этой статьи — показать, как обойти различные проверки безопасности путем непосредственного изменения двоичного кода, а не исходного кода, с помощью CFF Explorer. Ранее мы уже рассмотрели различные способы обхода кода IL. Там мы выполнили такие важные задачи, играя с инструкцией байт-кода IL. Эта статья в основном учит вас, как идентифицировать соответствующие инструкции двоичного кода с помощью дизассемблера IL; затем вы узнаете, как изменить такой двоичный код (шестнадцатеричный код) с помощью редактора, такого как CFF Explorer.

Предпосылки

Предполагается, что пользователь хорошо разбирается в манипулировании двоичным кодом и что вы установили новую копию программного обеспечения CFF Explorer для редактирования инструкций двоичного кода. Кроме того, пользователь также должен иметь глубокое понимание инструкций кода MSIL.

[cpp]
преобразование открытого частичного класса: форма
bool isTrialExpired = true;

public Conversion()
InitializeComponent();
>

private void TrialExpiredCheck()
if (isTrialExpired)
MessageBox.Show(@"Продолжительность пробной версии истекла!
Установлена ​​свежая копия",". Предупреждающее сообщение. ");
Приложение.Выход();
>
>
private void Conversion_Load (отправитель объекта, EventArgs e)
TrialExpiredCheck();
>

Здесь, после тщательного изучения кода, мы можем легко понять, что метод TrialExpiredCheck() отвечает за истечение срока действия продукта. Нам не нужно заморачиваться о коде преобразования вычислений и прочем.

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

Но поставщик этого продукта выпускает бета-версию и предоставляет на рынке только бесплатную пробную версию, которая работает в течение определенного периода времени. По истечении этого срока он автоматически истечет, и на экране будет мигать предупреждающее сообщение. После нажатия кнопки OK приложение автоматически выгружается. Предупреждающее сообщение выглядит следующим образом:

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

Разборка кода IL

Дизассемблер IL покажет все операторы для каждого метода в построчном формате. Столбец RVA обычно позволяет среде выполнения вычислять начальный адрес памяти MSIL, определяя метод, содержащий пробную проверку, байты для каждого оператора и их положение относительно RVA. Дизассемблированный или декомпилированный файл, тем не менее, создает большое количество необработанного IL-кода, но наша главная задача — найти код, соответствующий методу TrialExpireCheck(), следующим образом:

[cpp]
.method private hidebysig instance void TrialExpiredCheck() cil управляемый

// Метод начинается с RVA 0x2134
.maxstack 2
.locals init ([0] bool CS$4$0000)

.line 16,16 : 9,10 ”
IL_0000: /* 00 */ nop
.line 17,17 : 13,32 ”
IL_0001: /* 02 */ ldarg.0
IL_0002: /* 7B (04)000004 */ ldfld bool Fahrenheit.Conversion::isTrialExpired
IL_0007: /* 16 */ ldc.i4.0
IL_0008: /* FE01 */ ceq
IL_000a: /* 0A */ stloc.0
IL_000b: /* 06 */ ldloc.0
IL_000c: /* 2D 18 */ brtrue.s IL_0026

IL_000e: /* 00 */ nop
.line 19,20 : 17,81 ”
IL_000f: /* 72 | (70)000041 */ ldstr "Продолжительность пробной версии истекла!"
IL_0014: /* 72 | (70)0000EA*/ ldstr ". Предупреждающее сообщение. "
IL_0019: /* 28 | (0A)000020*/ call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show (строка, строка)

IL_001e: /* 26 */ pop
.line 21,21 : 17,36 ”
IL_001f: /* 28 | (0A)000021*/ call void [System.Windows.Forms]System.Windows.Forms.Application::Exit()
IL_0024: /* 00 */ nop
.line 22,22 : 13 ,14 ”
IL_0025: /* 00 */ nop
.line 23,23 : 9,10 ”
IL_0026: /* 2A */ ret
> // конец Преобразование метода::TrialExpiredCheck
[/cpp]

Обозреватель CFF

Исправление двоичного кода

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

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

Вот почему мы ранее дизассемблировали этот исполняемый файл в IL-код, чтобы найти значение RVA и последовательности двоичного кода. В файле кода IL каждая инструкция имеет свой точный номер строки, который указывает реальный номер строки исходного кода и последовательность байтов. По сути, RVA представляет собой адрес сегмента для метода ( TrialExpiredCheck ), который включает в себя всю логику для ограничений безопасности. Эта инструкция указывает, что тело этого метода начинается с адреса 0x2134 в необработанных шестнадцатеричных байтах.

// Метод начинается с RVA 0x2134

Мы должны выполнить две задачи, чтобы обойти ограничения срока действия пробной версии;

  1. Остановить или перенаправить вызов метода Application.Exit() из-за задержки выполнения.
  2. Удалите окно предупреждения "Срок действия пробной версии истек".

Итак, используя это значение 0x2134, мы можем напрямую перейти к логическому коду ограничения безопасности, как показано ниже;

Перенаправить или удалить вызов метода Application.Exit()

Сначала необходимо определить связанные байты в шестнадцатеричном коде, отвечающие за выполнение метода Application.Exit(). После тщательного изучения кода IL, определенного ранее, мы можем понять, что код операции IL_001f является кодом ключа, как;

IL_001f: /* 28 | (0A)000021 */ call void [System.Windows.Forms]System.Windows.Forms.Application::Exit()
IL_0024: /* 00 */ nop
IL_0025: /* 00 */ nop
IL_0026: /* 2A */ ret

Связанные значения байтов показаны красным; мы должны выровнять их в правильной последовательности. Обычно это делается справа налево следующим образом;

28 21 00 00 0А 00 00 2А

Вы также можете найти такую ​​последовательность байтов в редакторе шестнадцатеричного кода CFF, как показано ниже;

Итак, если мы изменим байты между 26 и 2A на nop (00), то сможем остановить или удалить вызов метода Application.Exit:

Теперь мы успешно удалили вызов метода Exit().

Удалить окно сообщения о вызове для оповещения

Если мы внимательно изучим код IL, мы легко увидим, что есть логическая переменная isTrialExpired, для которой по умолчанию задано значение True, а в TrialExpiredcheck (), его значение проверяется в условии. Поскольку значение логической переменной равно true, выполнение конструкции if condition всегда истинно, и будет мигать окно предупреждающего сообщения.

IL_0002: /* 7B (04)000004 */ ldfld bool Fahrenheit.Conversion::isTrialExpired
IL_0007: /* 16 */ ldc.i4.0
IL_0008: /* FE01 */ ceq
IL_000a: /* 0A */ stloc.0
IL_000b: /* 06 */ ldloc.0
IL_000c: /* 2D 18 */ brtrue.s IL_0026

Настоящая последовательность байтов будет следующей:

Итак, это хак: если мы удалим эту проверку условия if, заменив значение инструкции IL_00c 2D на 2C, тогда конструкция if никогда не будет выполняться, и окно предупреждения не появится.

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

Бинго! Мы успешно сняли все ограничения безопасности.

Резюме

Отказ от ответственности: я, Аджай Кумар, не собираюсь учить какой-либо наступательной тактике и не поддерживаю никакие действия Black Hat. Цель этой статьи — предоставить белую шляпу или защитные знания для изучения или тестирования.

Читайте также: