Введение в реверсинг с нуля, используя IDA PRO. Часть 42.

Дата публикации 21 апр 2018 | Редактировалось 23 апр 2018
В 41 части, для практики было оставлено два упражнения. К сожалению, никто не прислал мне решения. Это заставляет меня задаться вопросом, стоит ли оно того, что я делаю. По крайней мере для меня это сейчас вопрос. Ни обратной связи, ни вопросов ко мне. Ничего нет.

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

Нам необходимо обновить до новой версии плагин KEYPATCH, поскольку мы будем использовать его в конце.

1.png
https://github.com/keystone-engine/keypatch

Я заменяю файл .PY новым скриптом.

http://ricardo.crver.net/WEB/INTRODUCCION AL REVERSING CON IDA PRO DESDE CERO/EJERCICIOS/

Давайте откроем пример 41 в IDA.

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

2.png

Если мы посмотрим STRINGS WINDOWS, мы увидим строку MYPEPE.DLL. Поэтому, возможно, что необходимо поместить библиотеку в ту же папку. Это та же самая DLL из предыдущих упражнений.

3.png

Я помещаю библиотеку в нужную папку.

4.png

Я делаю двойной щелчок по этой строке.

5.png

Мы видим ссылку. Мы идем в неё с помощью нажатия X или CTRL + X.

6.png

Мы видим, что программа загружает библиотеку здесь. Поэтому всё хорошо. Это похоже на функцию MAIN. Поскольку это консольная программа, если это так, то она должна иметь функцию в качестве ссылки, которой передаётся такие аргументы как ARGC, ARGV и т.д.

7.png

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

8.png

Поэтому, по адресу 401090 находится функция MAIN. Мы переименовываем её.

9.png

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

Давайте начнем реверсить.

10.png

Здесь, мы видим, что программа читает запись IAT SYSTEM, которая находится в секции IDATA. Адрес указанной API помещается в регистр EAX.

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

11.png

Здесь, Вы можете увидеть запись IAT. IDA логически говорит нам о типе EXTRN, потому что это внешняя API импортированного модуля.

12.png

Конечно, в IMPORTS есть импортированные функции для модуля и адрес записи IAT, который показывает значение 4020A8, поэтому у нас всё совпадает.

13.png

Затем программа запишет в глобальную переменную DWORD_403088, (которая является DWORD) значение регистра EAX.

14.png

Мы помним, что в IDA если существует префикс типа данных перед адресом, это означает, что содержимое этого адреса имеет тот же тип. В этом случае это DWORD. По крайней мере это 4 байта. Также программа записывает адрес API здесь.

Поэтому мы переименовываем глобальную переменную в P_SYSTEM.

Мы знаем, что это переменная длиной 4 байта и так как она сохраняет адрес, то она должна быть типа указатель. Я изменяю её тип.

15.png

Поскольку я знаю, что это указатель на API, то могу легко сделать его указателем на что-то неизвестное.

VOID * P_SYSTEM

В любом случае, нет необходимости иметь слишком точные определения, потому что они лишние. Важно просто то, что это указатель на что-то.

16.png

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

17.png

Существует другая переменная того же типа, которая хранит адрес API SETPROCESSDEPPOLICY. Я делаю то же самое.

Я изменяю тип также в декомпиляторе HEXRAYS с помощью F5. Изменение типа ни на что не влияет.

18.png

19.png

Это не имеет большого значение. Это просто нужно, чтобы показать Вам больше вариантов.

20.png

Мы видим, что глобальная переменная P_SYSTEM используется повторно и сохраняет указатель (используется инструкция LEA для поиска адреса) на переменную SIZE, которая является DWORD. Очевидно, it will cast it to do it C++(??? Опять куча itов. Не понял как тут правильно будет), но здесь это не имеет значения. Они обе являются указателями.

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

Что-то вроде этого:

P_SYSTEM_____P_SIZE

В сложных функциях они много раз повторяется и мы должны следовать этому.

21.png

Затем программа сравнивает переменную ARGC, которая представляет собой число аргументов с числом 2. Таким образом это имя исполняемого файла + аргумент, т.е. всего два аргумента. Если нет двух аргументов, программа пропускает всё и выходит из функции напрямую.

22.png

Мы видим, что ARGV это массив и что в позиции 0 он хранит строку имени исполняемого файла и если мы добавим в нему 4, то программа получит адрес строки первого аргумента.

23.png

Затем программа передает эту строку в функцию ATOI, чтобы попытаться преобразовать строку в целое число. Если у функции это не получается, программа выдаёт ошибку.

24.png

25.png

После возврата из функции ATOI это значение сохраняется в переменную SIZE, которая находится в стеке.

26.png

Не путайте её с глобальной переменной P_SYSTEM_____P_SIZE, которая имеет сохраненный АДРЕС этой же переменной SIZE.

Программа сравнивает это значение размера, которое пришло из ARGV со значением 0x300 и если оно больше, программа переходит и возвращается в функцию MAIN. Конечно, этот SIZE является знаковым, потому что сравнение с использованием инструкции JLE говорит нам об этом.

27.png

Поскольку переменная является знаковой, когда мы передаем ей отрицательное значение, она будет меньше 0x300. Например, если я передам ей значение -1, это значение будет меньше 0x300, потому что рассматривается знак и переменная пройдет сравнение.

Очевидно, если эта переменная SIZE используется как размер в API функции, которая принимает переменную как беззнаковую, то в программе может случиться переполнение, потому что для этой API функции значение не будет отрицательным, но без знака. Например, если значение равно -1, для API, которая принимает это значение как положительное, максимальным положительным будет значение 0xFFFFFFFF. Т.е. -1 равно 0xFFFFFFFF.

28.png

Мы видим функцию, ей аргументом является переменная SIZE. Мы ещё не переименовывали её. Давайте посмотрим что она делать. Пойдем туда и посмотрим.

29.png

Мы видим, что есть здесь есть функция GETS_S для ввода данных. Поэтому я даю функции имя INGRESO.

Эта функция GETS_S имеет переменную SIZE, которая принимается как беззнаковая и может вызвать переполнение буфера.

30.png

Здесь мы видим, что тип переменной SIZEINCHARACTERS является типом SIZE_T.

32.png

И этот SIZE_T является беззнаковым. Поэтому я уверен, что мы можем переполнить буфер. Давайте посмотрим насколько он большой. Хотя мы уже видели, что я сделал его плохим, но программы пытается фильтровать SIZE больше чем 0x300 байт. Поэтому очень вероятно, что размер буфера таков.

33.png

Мы видим, что буфер находится в секции DATA и IDA говорит мне, что его размер равен 0x64 байт. Чуть ниже буфера мы видим глобальные переменные P_SETDEP и P_SYSTEM____P_SIZE. Поэтому здесь нет ошибки. Максимальная проверка позволяет нам переполнить даже этот 0x64 байтный буфер (100 десятичных байт), без требования быть отрицательным. Только с размером больше чем 0x64 байта.

34.png

Как только я запишу больше чем 0x64 байта, я буду продолжать двигаться вниз и смогу перезаписать сохраненные указатели в секции DATA.

Сейчас, после того как Вы перезапишите указатели, будет ли программа использовать эти указатели?

35.png

Я вижу в ссылках на P_SETDEP, что есть вызов с использованием этого указателя на функцию и что, если я перезаписываю его с помощью переполнения, он может отклонить выполнение программы.

36.png

Это происходит сразу после функции GETS_S. Это прекрасно. Давайте сделаем скрипт.

37.png

Я изменяю скрипт, который был достаточно похожий и устанавливаю размер равным -1. Он будет проходить проверку. Я помещаю 0x64 байта символов A чтобы заполнить буфер, и затем я помещаю значение 0x99989796, которое предположительно должно перезаписать указатель, который находится чуть ниже.

Поскольку ROP для библиотеки MYPEPE.DLL уже создан, я оставляю его. Я увижу, как я расположу ROP. Сейчас я запускаю скрипт и присоединяю IDA к процессу, помещаю BP сразу после функции GETS_S, для того, чтобы остановится там.

38.png

39.png

40.png

Мы видим, что буфер выглядит почти полным. Давайте пойдем туда, чтобы увидеть его.

Если я нажму U для UNDEFINE, мы увидим много символов A. Давайте посмотрим перезаписался ли указатель.

41.png

Мы должны увидеть, перезаписали ли мы указатель.

42.png

Я нажимаю D до тех пор пока не получу DWORD и вижу что это всё хорошо перезаписалось. Установлено значение, которое я хотел.

43.png

Я вижу, что у процесса нет DEP, потому что мы перезаписали правильный API SETPROCESSDEPPOLICY, которая собиралась включить его. Поэтому здесь нам не понадобится ROP.

44.png

Регистр EAX указывает на буфер, который заполнен A. Поэтому если я смогу перейти туда и разместить шеллкод в начале буфера и выполнить его.

Я мог бы искать вызов CALL EAX в библиотеке MYPEPE, который не содержит защиту ASLR. Я использую новый KEYPATCH с опцией SEARCH и ввожу инструкцию CALL EAX.

45.png

46.png

Мы видим в результатах, что есть несколько, которые принадлежат библиотеке MYPEPE. Я выбираю некоторые для примера.

47.png

Когда я иду туда, я нажимаю C для преобразования его в код, потому что я его не дизассемблировал.

По адресу 0x7802C16E будет инструкция CALL EAX, которую я выбрал. Я добавляю этот адрес в скрипт.

48.png

Я помещаю шеллкод вперед, потому что он переходит в начало буфера и не меняет длину перед указателем. Оставшаяся часть шеллкода вычитается из общего количества букв A. Мне больше не понадобится ROP. Поэтому я удаляю его.

49.png

Наша курочка готова. Шеллкод запускает калькулятор. Я надеюсь, что кто-то сделает меня счастливым, решив задачку 41B или, по крайней мере, просто попробует её на вкус.

============================================================
#8eqw0uExIPQMo0hlG2SMbg.kWxA60qd204.wQ0f6qnVEDF3sTM4kUnjA82Jgnlue8Wsgi0LIXEGPRLM89DcLVaUVNH29WUFmLizrcwP9OtNDlc4wPiQ6DtpTgbIFIhKqOATEW8MBxHDk2Uvz/8H8BgX5L2XHkS/zfDWlnPEVsMXsg
=======================================================

Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на английский: IvinsonCLS (@IvinsonCLS)
Перевод на русский с испанского+английского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
Перевод специально для форума системного и низкоуровневого программирования — WASM.IN
22.04.2018
Версия 1.0

2 5.630
yashechka

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

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