Введение в крэкинг с нуля, используя OllyDbg - Глава 54

Дата публикации 3 май 2017 | Редактировалось 1 июн 2017
При анализе неизвестного упаковщика лучше всего сначала проверить, существует ли для него известный анпэкми, поскольку нам нужен его распакованный код. Если, как в нашем случае, анпэкми есть, у нас будет и код распакованного анпэкми для сравнения.

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

Здесь будет рассмотрена серия анпэкми ExeCryptor’а, которая отражает хоть и не самую последнюю версию, но всё же почти последнюю, и это позволит нам постепенно его изучить, если мы будем пытаться узнать как можно больше. Это гнусный упаковщик, и для его изучения потребуется приложить немало сил.

У нас есть несколько уровней анпэкми, в которых постепенно повышается трудность и добавляются трюки:

1.png

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

2.png

Здесь почти ничего нет, хе-хе, но для начала этого вполне достаточно. Кроме того, у меня есть распакованный анпэкми UPX [ссылка], основанный на том же коде (я добавил его к туториалу). Этот распакованный крэкми можно будет использовать для сравнения, так как здесь IAT видна из OEP и всё просто. Данный крэкми сам по себе не столь важен, тем не ме- нее он может существенно ускорить дальнейшее изучение.

Вот EP распакованного UPX’а:

3.png

Как видим, это типичный анпэкми Тедди Роджерса, подобный тем, что уже рассматривались во «Введении». И данный анпэкми ExeCryptor’а основан на том же коде, а затем упакован. У него будет та же OEP = 4271B0, та же IAT; первая вызываемая им API-функция — GetVersion и т.д. Данное обстоятельство при изучении таких сложных упаковщиков (протекторов) оказывается большой помощью.

Хорошо, теперь приступим к изучению самого легкого ExeCryptor’а [ссылка], то есть A.

Я подключил плагин Olly Advanced 1.26 Beta 12 [ссылка]:

4.png

и воспользовался Parcheado 4 [ссылка] (я всегда пользуюсь этой версией отладчика). В Olly Advanced отмечено много опций, но большинство из них не столь важны, кроме указанной на скриншоте Break on TSL Callback.

Думаю, все уже знают, что ExeCryptor перед переходом на EP выполняет код посредством трюка, объясненного уже очень много раз, и останавливается в TSL Callback перед EP. Предыстория такова, что один вирусописатель обнаружил, что система позволяет выполнять код перед EP, если была активизирована упомянутая опция, а автор ExeCryptor’а воспользовался этой идеей, поэтому обычно ExeCryptor при отладке в Olly закрывается перед переходом на EP, ибо он это обнаруживает.

5.png

Изменим настройки Olly так, чтобы он останавливался на System breakpoint.

Находясь на системном брейке, нажмем RUN, и следующая остановка произойдет в TLS Callback:

6.png

В строке статуса Olly сообщает:

7.png

Чтобы найти этот адрес вручную, обратимся к заголовку по Go to –> Expression = 400000 и установим режим Special –> PE header. Прокрутив вниз, увидим:

8.png

Начало таблицы TLS равно 93110, а с ImageBase будет 493110. Посмотрим в Dump’е чуть ниже начала этой таблицы:

9.png

Здесь находим адрес, откуда начинает выполняться ExeCryptor, но плагин Olly Advanced уже сделал эту работу за нас и остановился в нужном месте.

Кроме того, если откроем файл в PEditor’е:

10.png

11.png

В directory видно, что Tls Table начинается в 493110 и размер у нее 18. Но будет точнее, если нажмем кнопку TLS [в PE Editor’е LordPE или PE Tools это будет кнопка «...» в соответствующей строке. — Р. С.], и станет ясно, где именно в таблице искать нужный адрес:

12.png

То есть адрес, откуда начинается выполнение, нужно смотреть в 49312C. В Дампе Olly видим:

13.png

Хорошо, теперь мы знаем, как найти этот адрес в Olly и в PE Editor’е. Если у нас есть Olly Advanced, то он поможет остановиться на TLS Callback, и искать это место вручную не придется. А если нет — то, находясь на System breakpoint, следует установить на найденный адрес BP, и будет тот же результат.

Есть одна проблема. Мы остановились в месте, где начинается выполнение.

14.png

Нажмем RUN:

15.png

Программа что-то обнаруживает и закрывается. Я активизировал все опции Olly Advanced, а также попытался запустить анпэкми в другой версии отладчика, пропатченной AntiDetectOlly_v2.2.4.exe [ссылка], и произошло то же самое. Давайте исследовать. Вернемся сюда:

17.png

Если открыть окно Breakpoints, то там окажется один брейк, хотя мы и не устанавливали ни одного:

18.png

Ах! Ведь этот BP ставит OllyDbg для остановки на EP программы. Но ExeCryptor выполняется раньше, и так как это байт CC, записанный в указанную на скриншоте ячейку памяти, протектор может обнаружить его. Удалим его и посмотрим, что изменится. Нажимаем RUN:

19.png

Действительно, устанавливаемый Olly BP обнаруживается: если он присутствует, программа завершается, а если его вручную удалить, то работает. Если же анпэкми не запускается и после удаления брейка, стоит поставить больше галок в закладках Anti-Debug плагина Olly Advanced. Напоминаю, что мы используем версию OllyDbg под названием Parcheado 4; до сих пор ничего другого не потребовалось.

Теперь, зная решение проблемы, перезагрузим анпэкми:

20.png

Установим Break-on-execute на секцию кода, то есть на ту, которая начинается в 401000, поскольку предполагается, что распаковка происходит туда:

21.png

Вновь удалим BP, так как он автоматически устанавливается после каждой перезагрузки:
22.png

Теперь нажмем RUN:

23.png

Остановка произошла в знакомой OEP, то есть на 4271B0. У уровня защиты A нет ни украденных байтов, ни сокрытия OEP — ничего. Поэтому мы находимся на OEP и остаемся без работы. [Следует помнить, что для нормальной работы плагина OllyBonE галка с исключения Single-step break должна быть снята. — Р. С.]

24.png


Этот CALL, вызывающий GetVersion, в распакованном UPX’е указывает прямо на API-функцию, так как он не был перенаправлен. Здесь же он перенаправлен, поэтому придется фиксить IAT.

Посмотрим начало и конец IAT:

25.png

Она начинается в 460818, и ее можно сравнить с IAT распакованного UPX’а, чтобы посмотреть, какие есть различия и применены ли какие-либо трюки.

Если спустимся, увидим окончание таблицы:

26.png

27.png
Последний адрес — 460F28, он следует сразу после последнего элемента. Таким образом, Конец минус Начало дает Размер:

28.png

Это 710h, так что всё вместе:

OEP: 271B0
RVA (Начало IAT): 60818
Size: 710

Данные для Import Reconstructor’а у нас есть.

Теперь нам нужно выяснить способ восстановления IAT’а. В данном случае «ключевого перехода» нет, но далее мы увидим, что ExeCryptor восстанавливает адрес и сохраняет его в элементе IAT’а при выполнении каждой API-функции.

Установим BPM ON WRITE на элемент IAT’а, соответствующий GetVersion, но прежде удалим Break-on-execute, иначе программа выдаст ошибку:

29.png

Находим элемент, соответствующий первой вызываемой API-функции:

30.png

Устанавливаем MEMORY BREAKPOINT ON WRITE и смотрим, где произойдет остановка:

31.png

Здесь сохраняется правильный адрес API-функции в соответствующем элементе.

32.png

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

33.png

Здесь видно, что после сохранения адреса API-функции идет запись байта C3 по адресу 479030, предварительно загруженному в EAX командой LEA. Посмотрим, что здесь происходит.

Листинг в 479030 изначально такой:

34.png

А после записи C3 становится таким:

35.png

То есть установлена команда RET, код автомодифицирован. Кроме восстановления API-функции происходит модификация части кода, находящегося не в первой секции, то есть принадлежащего ExeCryptor’у. Хмм, мы увидели, что происходит с этим адресом. Теперь перезагрузим анпэкми, дойдем до OEP и установим BPM ON ACCESS на инструкцию в 479030:

36.png

Доходим до первого вызова API-функции:

37.png

Ставим BP на ВОЗВРАТ, т.е. на 4271DC:

38.png

И нажимаем RUN:

39.png

Остановились на оригинальном переходе, т.е. это часть процедуры, которая узнает API-функцию. И только после этого записывается RET. Иными словами, здесь происходит автоизменение процедуры, распознающей API-функции.

Мы можем проверить, будут ли еще изменения: после перезагрузки дойдем до OEP и установим BPM ON WRITE на секцию ExeCryptor’а, в которую перенаправлены API-функции и упомянутая процедура. Доходим до OEP:

40.png

Я снова нахожусь на первом вызове API-функции, и здесь видно, что переход будет выполнен на адрес переадресации 492493, принадлежащий той же самой секции, что и условный переход в 479030, подлежащий замене на RET. Таким образом, можно установить BPM ON WRITE на всю эту секцию и посмотреть, что происходит в программе…

41.png

42.png
Ставим BP на возврате, нажимаем RUN и наблюдаем изменения, которые произойдут в секции и в процедуре, распознающей API-функции, во время этого первого вызова:

43.png

Продолжаем:

44.png

45.png

Далее:

46.png

Так меняется последовательность байтов. Я не стал показывать их все, чтобы не перегружать туториал. Посмотрим в DUMP’е изменяемое место:

47.png

Хорошо, сохранилось имя DLL’ки, в которой будет происходить поиск API-функции.

Продолжим:

48.png

Теперь там же сохраняется ноль, чтобы обозначить конец имени.

49.png

Процесс повторяется, но теперь поверх «K» сохраняется байт B4, и так со всем именем — оно скрывается.

50.png

Здесь сохраняется адрес базы kernel32.dll, равный на моем компьютере 7C800000.

51.png

И после того, как в IAT’е сохранен верный адрес, записывается байт C3. Теперь ситуация проясняется: RET нужен, чтобы не выполнять два раза одну и ту же процедуру. Данный путь для рассмотренного элемента закрывается — он пройден и больше не потребуется, поскольку соответствующий адрес восстановлен.

53.png

До остановки на BP изменений больше не произошло. Удалим брейк и с помощью F7 дойдем до этой API-функци:

54.png

Поставим BP на возврате и нажмем RUN:

55.png

56.png

Остановка произошла при записи другого RET’а уже в 47A0BC.

57.png

Далее доходим до BP. На скриншоте видно, что восстановленный элемент IAT’а соответствует kernel32.dll, как и первый. Ух…
Дойдем до вызова третьей API-функции:

58.png

Попав сюда, посмотрим в распакованном UPX’е то же место:

59.png

Это CALL в VirtualAlloc. Установим BP на возврат и нажмем RUN:

60.png

Здесь сохраняется еще один RET. Посмотрим участок соответствующего кода перед его изменением:

61.png

Этот RET записывается чуть ниже JMP’а на восстановленный адрес API-функции.

62.png

То есть происходит то же самое, что и с предыдущей API-функцией — закрывается вход в процедуру. Сперва она выполняется, а уже после восстановления элемента IAT’а происходит запись RET, чтобы она не выполнялась повторно. Это подобно закрытию внутреннего входа в элемент.

Сделанный нами вывод можно проверить ручной трассировкой.

63.png

Это происходит дважды: первый раз программа доходит до POP EDX и процедура выполняется. А после восстановления элемента IAT’а записывается RET и вход закрывается:

64.png

То есть, когда API-функция восстановлена, вся эта область автоматически аннулируется, поскольку программе достаточно прямого адреса API-функции в IAT’е. И даже если есть другой путь, программа им не пойдет, ибо обнаружит RET на входе, и всё закончится JMP’ом на API-функцию.

Таким образом, сначала находится имя DLL’ки, которое используется для получения основания (скорее всего с помощью LoadLibraryA). Затем производится поиск адреса API-функции и его сохранение в IAT’е. И, в конце концов, в секцию вносятся изменения, чтобы закрыть вход в отработанную процедуру.

Можно попытаться задействовать скрипт: дойти до ExitProcess, а далее с помощью скрипта восстановить CALL’ы API-функций. Данное задание довольно трудно из-за того, что код меняется, однако это может оказаться хорошим стимулом для выхода следующей части «Введения». Как бы то ни было, но проблему с IAT нужно решать, поскольку она есть во всех уровнях ExeCryptor’а, которые мы рассмотрим. IAT всех уровней одинакова, и если мы сейчас восстановим ее, то проблема будет полностью преодолена (только эта — другие же только начнутся, хе-хе).

До следующей главы, где будет рассмотрен скрипт для восстановления IAT ExeCryptor’а.

03.10.06
Рикардо Нарваха

[Перевод с испанского: Рома Стремилов, 11.2010]

2 6.426
yashechka

yashechka
Ростовский фанат Нарвахи

Регистрация:
2 янв 2012
Публикаций:
90

Комментарии


      1. yashechka 6 май 2017
        Выложите из песочницы, или как там?
      2. yashechka 4 май 2017
        Подкинул старых дровишек. Принимайте. Готово.