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

Дата публикации 3 июн 2018 | Редактировалось 23 июн 2018
Мы будем стараться продолжать обсуждать распределение, чтобы увидеть сможем ли мы понять логику распределения и увидеть, что говорит программа, если распределение происходит в LFH или в СТАНДАРТНОЙ КУЧЕ.

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

Мы загружаем исполняемый файл вне IDA из консоли, не используя скрипт PYTHON.

Мы вводим номер, который нас просит программа (1073741828), присоединяем WINDBG к IDA как отладчик, помещаем BP на функцию MALLOC и нажимаем клавишу ENTER.

1.png

2.png

Если мы трассируем вход в функцию MALLOC с помощью клавиши F7 мы увидим, что размер помещается в регистр ESI. Программа сравнивает это значение. Если размер больше чем значение 0xFFFFFFE0, то, так как в нашем случае это значение равно 0x10, то у нас нет проблем. Программа помещает в стек размер как аргумент и прямо там мы видим, что программа помещает в стек, в моём случае, значение 0x240000. Это одна из куч, поэтому мы уже знаем, что программа будет работать с ней.

3.png

4.png

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

Это структура кучи. Я оставляю её для копирования и вставки.

# +0x000 Entry : _HEAP_ENTRY
# +0x008 SegmentSignature : Uint4B
# +0x00c SegmentFlags : Uint4B
# +0x010 SegmentListEntry : _LIST_ENTRY
# +0x018 Heap : Ptr32 _HEAP
# +0x01c BaseAddress : Ptr32 Void
# +0x020 NumberOfPages : Uint4B
# +0x024 FirstEntry : Ptr32 _HEAP_ENTRY
# +0x028 LastValidEntry : Ptr32 _HEAP_ENTRY
# +0x02c NumberOfUnCommittedPages : Uint4B
# +0x030 NumberOfUnCommittedRanges : Uint4B
# +0x034 SegmentAllocatorBackTraceIndex : Uint2B
# +0x036 Reserved : Uint2B
# +0x038 UCRSegmentList : _LIST_ENTRY
# +0x040 Flags : Uint4B
# +0x044 ForceFlags : Uint4B
# +0x048 CompatibilityFlags : Uint4B
# +0x04c EncodeFlagMask : Uint4B
# +0x050 Encoding : _HEAP_ENTRY
# +0x058 PointerKey : Uint4B
# +0x05c Interceptor : Uint4B
# +0x060 VirtualMemoryThreshold : Uint4B
# +0x064 Signature : Uint4B
# +0x068 SegmentReserve : Uint4B
# +0x06c SegmentCommit : Uint4B
# +0x070 DeCommitFreeBlockThreshold : Uint4B
# +0x074 DeCommitTotalFreeThreshold : Uint4B
# +0x078 TotalFreeSize : Uint4B
# +0x07c MaximumAllocationSize : Uint4B
# +0x080 ProcessHeapsListIndex : Uint2B
# +0x082 HeaderValidateLength : Uint2B
# +0x084 HeaderValidateCopy : Ptr32 Void
# +0x088 NextAvailableTagIndex : Uint2B
# +0x08a MaximumTagIndex : Uint2B
# +0x08c TagEntries : Ptr32 _HEAP_TAG_ENTRY
# +0x090 UCRList : _LIST_ENTRY
# +0x098 AlignRound : Uint4B
# +0x09c AlignMask : Uint4B
# +0x0a0 VirtualAllocdBlocks : _LIST_ENTRY
# +0x0a8 SegmentList : _LIST_ENTRY
# +0x0b0 AllocatorBackTraceIndex : Uint2B
# +0x0b4 NonDedicatedListLength : Uint4B
# +0x0b8 BlocksIndex : Ptr32 Void
# +0x0bc UCRIndex : Ptr32 Void
# +0x0c0 PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY
# +0x0c4 FreeLists : _LIST_ENTRY
# +0x0cc LockVariable : Ptr32 _HEAP_LOCK
# +0x0d0 CommitRoutine : Ptr32 long
# +0x0d4 FrontEndHeap : Ptr32 Void
# +0x0d8 FrontHeapLockCount : Uint2B
# +0x0da FrontEndHeapType : UChar
# +0x0dc Counters : _HEAP_COUNTERS
# +0x130 TuningParameters : _HEAP_TUNING_PARAMETERS

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

5.png

Я создаю структуру через вкладку STRUCTURES с помощью клавиши INSERT и затем расширяю её до значения 0x130 байт. Позже я увижу, нужно ли мне расширить её ещё больше.

Мы уже знаем как это сделать. Я добавляю однобайтовое поле, помещая курсор на слово ENDS и нажимаю клавишу D, а затем нажимаю правую кнопку, и выбираю пункт EXPAND, и ввожу значение 0x12F.

6.png

7.png

Я нахожусь в структуре размером 0x130 байт. Структура будет немного больше из-за длины последнего поля, но позже мы увидим всё ли правильно.

8.png

Здесь программа использует смещение 44, а ниже смещение 5C. Я нажимаю T на обоих полях, и выбираю структуру HEAP.

9.png

Теперь я должен определить эти поля в структуре.

# +0x044 FORCEFLAGS : UINT4B

# +0x048 CompatibilityFlags : Uint4B
# +0x04c EncodeFlagMask : Uint4B
# +0x050 Encoding : _HEAP_ENTRY
# +0x058 PointerKey : Uint4B
# +0x05C INTERCEPTOR : UINT4B

Это два поля по 4 байты. Я переименую их. Я иду по смещению 0x44 и нажимаю D до тех пор пока не появятся буквы DD.

10.png

И я переименую их.

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

11.png

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

Как только мы дотрассируем до этого места, регистр EBX принимает значение базы кучи, 240000 в моем случае. Я могу пойти пойти этому адресу и назначить этому адресу структуру кучи, которая на данный момент что-то имеет.

12.png

С помощью сочетания ALT + Q или CONVERT TO STRUCT VARIABLE мы можем конвертировать данные в структуру.

13.png

14.png

Выглядит не очень симпатично, но там видны поля FORCEFLAGS и INTERCEPTOR равные нулю.

Это совпадает c результатом отладчика

15.png

И с помощью такой команды видны значения

16.png

17.png

Я скопировал исполняемый файл в другую папки не закрывая предыдущий файл, чтобы проверить его. Я открываю второй файл непосредственно во второй IDA без присоединения, но прямо внутри неё и это значение изменяться на 0x40000060. Это значение, указывает на то отлаживается ли программа.

18.png

Это вывод для файла, который отлаживается.

19.png

А это вывод для файла, который просто отрыт в отладчике.

20.png

Здесь отладчик говорит, что среди прочих вещей, этот флаг может быть использован как АНТИОТЛАДОЧНАЯ ТЕХНИКА.

21.png

Аргумент, который был равен нулю повторно используется в инструкции OR с полем FORCEFLAGS. Таким образом, поскольку аргумент был равен нулю, это выражение будет также равно FORCEFLAGS.

И регистр ECX, который содержит переменную INTERCEPTOR сохраняет её в переменную VAR_C, поэтому я переименовываю эту переменную, хотя я не нашел никаких объяснений для чего она нужна, но позже мы увидим, то что я увидел, это то что переменная не меняется если программа отлаживается или не равна нулю в обоих случаях.

Поскольку переменная INTERCEPTOR в моем случае равна нулю и регистр ESI равен нулю, программа переходит на красную стрелку так как значения равны.

22.png

Затем программа тестирует переменную FORCEFLAGS, которая равна нулю по отношению к константе 7D810F61. Результат будет равен нулю и программа будет идти по красной стрелкой. (Если программа открыта в отладчике, то программа будет идти по зеленой стрелке)

Программа сравнивает, значения. Если размер равен нулю, а в нашем случае он равен 0x10, поэтому программа пойдет сюда.

23.png

Программа прибавляет к регистру EAX значение 0xF, а затем исполняет инструкцию AND с параметром -8, что будет равно 0x18. Это будет равно полному размеру, чтобы распределить добавленный заголовок и быть умноженным на 8.

24.png

В регистре EAX остается значение полного размера, равное 18.

Затем программа сохраняет в переменную VAR_8 это значение, поэтому я переименую эту переменную.

25.png

Затем программа читает поле 0xB8 которое является BLOCKINDEX, поэтому я переименую его в структуре и здесь я нажимаю T для того чтобы освежить информацию.

# +0x0B8 BLOCKSINDEX : PTR32 VOID

26.png

27.png

Поэтому это значение является указателем, которое равно 0x240150 в моём случае.
Давайте запомним, что

shr eax, 3 ;Signed division by 8

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

Например, скопировав из предыдущего туториала поле USERSIZE 0x10, мы получим общий размер 0x3, умножив который на 8, мы получим общее количество байтов.

28.png

Python>hex(0x3*0x8)
0x18

Это обратная операция для переменной SIZE_FULL для нахождения значения 0x3 при делении на 0x8.

29.png

Мы видим, что теперь программа начинает работать со структурой BLOCKSINDEX, которую мы видели в последнем туториале. (Следующее изображение из предыдущего туториала)

30.png

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

31.png

Поэтому сейчас я нажимаю T на инструкции и выбираю эту новую структуру.

32.png

Теперь я должен только переименовать поле, которое находится по смещению 0x4 в ARRAYSIZE.

Поскольку регистр EAX указывает на начало этой структуры, я могу пойти в память и назначить структуру с помощью ALT + Q.

33.png

ARRAYSIZE равен 0x80 в этом случае, остальные поля по-прежнему не определены, поэтому все это выглядит некрасивым.

Поскольку значение ARRAYSIZE меньше, программа не идет на розовые блоки.

34.png

Мы находимся в этой части

35.png

Здесь FULL SIZE делится на 8. Это значение присваивается переменной BLOCKSIZE. Мы все еще храним это значение в регистре ECX. Мы не сохраняем его, только сравниваем, и мы видим, что программа также делает, сравнивая с ARRAYSIZE так же, как и наши.

Программа вычитает 1 из значения ARRAYSIZE, получая значение 0x7F и снова сравнивает результат со значением 0x3, которое находится в регистре ECX.

36.png

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

Сейчас программа прочитает поле 0x14 структуры BLOCKLIST, которое называется BASEINDEX, поэтому я переименую его.

37.png
38.png

Поскольку BASEINDEX равен 0, регистр ECX по-прежнему продолжает быть равен значению 0x3 т.е. BLOCKSIZE.

39.png

Поскольку поле EXTRAITEM, которое находится по смещению 0x8, равно 1 (я не буду повторять, как переименовать поле в структуре), мы доходим до инструкции ADD ECX, ECX, где программа умножает на 2 значение BLOCKSIZE.

40.png

Затем программа использует смещение 0x20 LISTHINTS.

41.png

При умножении 0x6 на 4 и сложение с указателем LISTHINTS в регистре ESI остаётся значение 0x24019C.

42.png

LISTHINTS указывает на FREELIST, который является другим типом более простого распределения. Теперь мы посмотрим, что делает программа.

43.png

Таким образом, мы можем создать новую структуру из 8 байт. Но в моей IDA у меня уже это есть эта структура (если у вас её нет, то создайте её).

44.png

45.png

Т.е. если LFH выключен, у BLINK есть счетчик, а если он включен, то имеет указатель. (HEAP_BUCKET + 1)

Хорошо. Вопрос здесь состоит в том, что программа тестирует регистр AL. Если он равен 1, чтобы увидеть, является ли он счетчиком.

46.png

Если BLINK равен 1, программа переходит к этому зеленому блоку с вызовом функции RTLPALLOCATEHEAP, а если нет, как в моем случае, программа переходит на голубой блок, который идет в вызов RTLPLOWFRAGHEAPALLOCFRONCONTEXT.

Другими словами, есть некоторые вещи, которые мы видим в нашем случае, программа сравнивает размер 0x3 с 0x80, и поскольку это значение меньше, а регистр AL из BLINK отличался от байта 0x1, мы прибыли сюда к чему-то, что, по-видимому, обрабатывается LFH.

47.png
48.png

Регистр ECX имеет указатель, который был в BLINK - 1 и в регистре EDX находится USERSIZE 0x10.

49.png

Поскольку в функции не отображаются переменные и функция основана на EBP (были переменными EBP - X), я изменил тип функции, поставив здесь галочку.

Напомним, что у BLINK было значение HEAP_BUCKET + 1, другими словами, вычитая 1, это будет равно HEAP_BUCKET, поэтому я переименую переменную в HEAP_BUCKET.

50.png

51.png

Другими словами, регистр EDI является базой структуры HEAP_BUCKET которую я создал.

Кажется она имеет 0x3 байта.

52.png

Хорошо. SIZEINDEX равен 2, программа помещает это значение в регистр EAX

=======================================================
Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на русский с испанского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
Исправление ошибок и неточностей - репетитор и носитель испанского языка.

Перевод специально для форума системного и низкоуровневого программирования — WASM.IN
23.06.2018
Версия 1.0


2 5.034
yashechka

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

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