![]()
LEA EAX, DS:110H[EAX*4]
Программа умножает значение 0x2 на 4 и складывает полученное значение со значением 0x110 и затем вычитает из найденного значения HEAP_BUCKET и сохраняет результат в переменную VAR_2C.
Этот адрес, который сохраняется в переменную VAR_2C является началом таблицы LFH, так как HEAP_BUCKETS находится по смещению 0x110 и должен быть равен 8, потому что поле находится внутри таблицы BUCKETS, поэтому в переменной VAR_2C программа сохраняет значение 0x24ACF8, которое было началом таблицы LFH.
![]()
![]()
![]()
То, что программа сейчас делает, это то, что говорит нам картинка. Программа пытаясь найти адрес LFH для этой структуры BUCKET, используя переменную SIZEINDEX.
![]()
![]()
Очевидно, мы находимся в этой части, только в моём случае переменная не равна единице и программа продолжает выполнение здесь.
![]()
![]()
Программа достигает инструкции LEA, где на данный момент регистр EAX равен нулю, поскольку он получается из умножения 0x3418 на CONST_CERO и суммируется с регистром ESI, который хранит начало таблицы LFH и добавляет значение 0x310.
![]()
Мы напомним, что смещение 0x310 из LFH это _HEAP_LOCAL_DATA.
![]()
Это значение сохраняется в переменную VAR_44. Я переименую это поле в HEAP_LOCAL_DATA.
![]()
![]()
![]()
Основная идея состоит в том, что мы пытаемся найти эти структуры и увидим, можем ли мы их найти и идентифицировать.
Хорошо. Мы должны создать структуру для HEAP_LOCAL_DATA.
![]()
Я создаю структуру из 0x22 байт, хотя она наверняка будет намного больше, но пока пойдет и это.
По смещению 0x18 есть структуры. Их размер равен 0x128 байт.
[128] _HEAP_LOCAL_SEGMENT_INFO
Программа проходит через все эти 128 структур, используя SIZEINDEX как индекс, который умножается на 0x68, а затем добавляет значение, чтобы найти адрес это структуры SEGMENTINFO.
![]()
Это означает, что адрес DIRECCION3 равен HEAP_LOCAL_SEGMENT_INFO.
![]()
![]()
Поэтому мы будем создавать структуру больше чем 0x64 байта.
![]()
![]()
Хорошо. Внутри отладчика этого выглядит так.
ntdll!_HEAP_LOCAL_SEGMENT_INFO
+0x000 Hint : Ptr32 _HEAP_SUBSEGMENT
Первое поле, которое программа пытается использовать здесь, говорит, что это HINT .
![]()
И программа сохранит это значение в переменную VAR_30. Я переименовываю переменную в HINT.
![]()
Если значение HINT не равно нулю, программа ищет ACTIVESUBSEGMENT, что является следующим полем.
![]()
А если это поле равно нулю, программа переходит сюда
![]()
В любом из трех случае, программа сохраняет результат в переменной, которую мы назвали HINT, но она может быть любой из трех.
![]()
Хорошо. Мы находимся здесь.
![]()
Мы будем переименовывать переменную HINT в RESULTADO, поскольку переменная может принимать три значения.
![]()
Картинка показывает нам три варианта. Сейчас же мы находимся в HINT.
Этот результат должен был бы быть _HEAP_SUBSEGMENT.
![]()
![]()
Здесь программа говорит мне, что _HEAP_LOCAL_SEGMENT_INFO находится в памяти по адресу 0x24B0F0 и что 0x282660 это структура _HEAP_USERDATA_HEADER а по смещению 0x08 это структура INTERLOCK_SEQ.
Мы уже подходим к концу. Фууууууууууууууууууххххххххххххх.
Здесь я добавляю новую структуру.
![]()
![]()
Здесь я вижу, что программа находит с помощью инструкции LEA адрес поля _INTERLOCK_SEQ
![]()
![]()
На данный момент, я думаю что она состоит из 4 байтов.
Программа может прочитать поле как WORD или как DWORD, это сложно для имени. В моем случае, программа читает DWORD т.е. имеет значение 0x56000D (ЭТО ЗНАЧЕНИЕ ОЧЕНЬ ВАЖНО)
![]()
![]()
![]()
Я добавляю поле SEQUENCE.
![]()
Программа тестирует регистр DI который хранит поле OFFSETANDDEPTH. Это поле в моём случае равно 0xD.
![]()
Здесь программа читает поле USERBLOCKS, которое находится по смещению 0x4 структуры HEAP_SUBSEGMENT.
![]()
Программа сравнивает регистр EDX который хранит LOCALINFO высчитанный с помощью указателя HEAP_SUBSEGMENT.LOCALINFO и они должны быть одинаковыми.
Поскольку значения одинаковые, программа идет к этому блоку.
![]()
![]()
Хорошо. Я не собираюсь просчитывать все эти значения, но очевидно, что то, что программа делает, это получает первый свободный блок запрошенного размера из значений структуры _INTERLOCK_SEQ, и когда программа покинет этот блок, она сохранит его здесь.
![]()
Регистр ECX здесь равен значению 282918 и если я посмотрю список блоков с размером 0x10 с помощью команды.
!heap -flt s 0x10
В выводе блоки находятся здесь
![]()
И это первое значение из этих LFH, потому что предыдущие размеры 0x10, которые говорят что они FREE(СВОБОДНЫЕ), имеют другую LFH младшего адреса, возможно, соответствующей другой куче.
![]()
Здесь по адресу чанка 0x282918 без заголовка, вычитается 8, и остается адрес полного блока с заголовком 0x282910.
![]()
Структура, которую мы ещё не добавили называется структурой LFH. Сейчас я сделаю это.
![]()
![]()
Теперь используется поле 0x24, которое должно найти базовый адрес КУЧИ по адресу 0x240000. Полученное значение помещается в регистр ESI.
![]()
BLOCKUNITS был равен 0x3 в инструкции SHL EAX, 3
Это похоже на умножение 8 поэтому
![]()
И это то значение, что осталось в поле USERSIZE
![]()
И это значение программа сравнивает со значением 0x3F . Поскольку оно меньше, программа идёт сюда.
![]()
Здесь программа пишет в заголовок чанка LFH. Мы не сделали структуру.
![]()
![]()
![]()
Программа перезаписывает 7 байт, меняя его со значения 0x80 на 0x88.
![]()
![]()
Сейчас блок обозначен как занятый. Если мы посмотрим следующий свободный блок по адресу 282928
![]()
Свободные блоки - это блоки со значение 0x80 , а занятые со значением 0x88.
![]()
Вопрос состоит в том, что значение, которое находится в структуре INTERLOCK_SEQ и которое определяет следующий блок для доставки находится в по адресу 0x282A78 (по смещению 0x8 от начала структуры HEAP_SUBSEGMENT)
До этого значение было равно 56000D.
![]()
А сейчас значение равно 59000C.
![]()
![]()
Расстояние между началом чанка, в который я могу написать, который решает, кто следующий….
![]()
То, что мы сказали в предыдущем туториале проверено здесь.
![]()
Это значение 0x282660 USERBLOCKS, помещается в регистр ESI.
![]()
![]()
Это означает, что в следующий раз, когда запрашивается размер 0x010, программа поместит в EDI значение 0x59000C если оно не перезаписано.
Программа помещает значение в регистр EAX и затем исполняет инструкцию SHR EAX,0xD
2 в 0xD степени = 8192 десятичных байт, т.е. 0x2000 в шестнадцатеричной системе
Другими словами, это эквивалентно значению 0x59000С делённому на значение 0x2000, что даёт мне результат 0x2C8
![]()
К этому значению программа применяет инструкцию AND c параметром 0x7FFF8
![]()
Затем программа добавляет регистр ESI, который равен 0x282660
![]()
И это даём мне результат 0x282928, что является следующим свободным
![]()
Это означает, что если есть переполнение, мы можем перезаписать эти данные переписав значение внутри структуры INTERLOCK_SEQ и программа предоставит мне предыдущий блок, про который мы расскажем в следующей части.
=======================================================
Автор текста: Рикардо Нарваха - Ricardo Narvaja (@ricnar456)
Перевод на русский с испанского: Яша_Добрый_Хакер(Ростовский фанат Нарвахи).
Исправление ошибок и неточностей - репетитор и носитель испанского языка.
Перевод специально для форума системного и низкоуровневого программирования — WASM.IN
24.06.2018
Версия 1.0
Введение в реверсинг с нуля, используя IDA PRO. Часть 48-2.
Дата публикации 20 июн 2018
| Редактировалось 24 июн 2018