Введение и отказ от ответственности
Этот пост является дополнением к моему путешествию по обнаружению и проверке внутреннего устройства ALPC, которое я задокументировал в статье «Внутреннее устройство Offensive Windows IPC 3: ALPC» . При подготовке этого блога я подумал, что второй пост, объясняющий шаги по отладке, которые я предпринял для проверки и обнаружения поведения ALPC, может быть полезен всем нам, новичкам в области обратного проектирования и/или отладки.Хотя я, безусловно, использовал приемы и методы, показанные в этом посте ниже, это не единственные мои ресурсы и инструменты для погружения в ALPC. Даже намек на это подорвал бы важную и важную работу других исследователей, которые документировали и реверсировали внутренности ALPC в прошлом, таких как Алекс Ионеску и многие другие . Отсюда и эта оговорка.
Приведенные ниже методы практичны и полезны, но я смог применить их только благодаря работе других.
Еще одно важное предупреждение: я ни в коем случае не являюсь опытным реверс-инженером, и этот пост в блоге не предназначен для того, чтобы быть введением в «как стать реверс-инженером» или показать умный способ попасть в эту область. Это сообщение «Используйте отладку Windows, чтобы наткнуться на тему и осмотреться» .
Подготовка среды
Чтобы выполнить шаги, показанные ниже, вы хотите настроить среду отладки ядра. Если у вас уже настроена среда отладки ядра, смело переходите к разделу Начало работы . Если вы этого не сделаете, у вас есть два основных варианта для этой настройки:- Локальная отладка живого ядра
- Удаленная отладка ядра
Настроить локальную отладку ядра
Необходимо выполнить следующие шаги:
- Запустите тестовую машину или виртуальную машину
- Если у вас еще не установлен WinDbg , загрузите и установите отсюда чтобы установить WinDbg.
В качестве альтернативы вы также можете использовать предварительный просмотр WinDbg из приложения Магазина Windows. - Откройте PowerShell с правами администратора и выполните следующую команду, чтобы включить локальную отладку ядра: PS:> bcdedit /debug on & bcdedit /dbgsettings local
- Перезагрузите машину
- Откройте WinDbg и войдите в режим локальной отладки ядра, выполнив следующую команду: .\windbg.exe -kl
В качестве альтернативы вы также можете открыть графический интерфейс WinDbg, щелкнув «Файл» «Отладка ядра» (Ctrl + K) «Локальный (Tab)» «ОК».
Примечание о пользовательском макете, показанном выше
В моем случае мне нравится располагать и выравнивать окна отладки определенным образом (а также иметь цвета, имитирующие темную тему). Вы можете сделать все это, запустив WinDbg, открыть и расположить все окна так, как вам нравится, изменить цвет (если хотите) в разделе « Вид» » «Параметры» «Цвета» и, наконец, сохранить все настройки рабочей области через Файл» » Сохранить рабочую область в файл . После этого вы можете открыть локальную отладку ядра WinDbg с помощью настроенной рабочей области следующим образом: .\windbg.exe -WF <Path-To-File>.WEW -kl
Все параметры командной строки WinDbg можно найти здесь.
Настройка удаленной отладки ядра
- Запустите свою первую тестовую машину или виртуальную машину, которую вы хотите отлаживать, она будет называться отлаживаемой машиной.
- Если у вас еще не установлен kdnet.exe , загрузите и установите WindowsSDK отсюда, чтобы установить его.
- Откройте PowerShell с правами администратора и выполните следующую команду: cd "C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\\" && .\kdnet.exe <DEBUGER-IP> <RandomHighPortNumber>'
Я обычно использую *51111 в качестве номера порта. Эта команда даст вам инструкции командной строки для использования в отладчике, см. шаг 6.* - Запустите вторую тестовую машину или виртуальную машину, которую вы хотите использовать для отладки первой виртуальной машины. Она будет называться отладочной машиной .
- Если у вас еще не установлен WinDbg , загрузите и установите WindowsSDK отсюда, чтобы установить его.
В качестве альтернативы вы также можете использовать предварительный просмотр WinDbg из приложения Магазина Windows. - Выполните следующую команду, чтобы запустить WinDbg и подключить его к отлаживаемой машине: cd "C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\" && .\windbg.exe -k <PASTE-OUTPUT-FROM-kdnet.exe-FROM-YOUR-DEBUGGEE>.
Команда для вставки из kdnet.exe (шаг 3) будет выглядеть примерно так: net:port=<YOUR-RANDOM-PORT>,key=....
Вы увидите приглашение, указывающее, что отладчик настроен и ожидает подключения. - Перезагрузите отлаживаемую машину. Вернитесь к своей отладчиком , которая будет подключаться во время процесса загрузки вашей отлаживаемой .
Возможно, вы заметили, что я упомянул WinDbg Preview как альтернативу классическому отладчику WinDbg. Эта предварительная версия представляет собой обновленную версию классического отладчика и имеет совершенно другой пользовательский интерфейс (включая встроенную темную тему). Если вы смотрите на одноразовую установку и эмоционально не привязаны к старому/классическому WinDbg, я рекомендую вам попробовать WinDbg Preview. Единственная причина, по которой я еще не использую его, связана с тем, что вы не можете экспортировать настройки рабочей области (макет окна), что является важной функцией для меня в моей лаборатории (которую я часто перестраиваю).
В результате ниже я буду использовать классический WinDbg.
Настройка символов
После того, как вы настроили WinDbg, последний подготовительный шаг, который вам нужно сделать, — это настроить отладчик так, чтобы он извлекал символы отладки с официального сервера символов Microsoft.
Запустите следующий набор команд в WinDbg для настройки символов:
- В WinDbg запустить .sympathчтобы показать текущую конфигурацию пути символа.
Если это похоже на приведенное ниже, в котором указано, что вы хотите, чтобы ваши символы загружались с сервера символов Microsoft и кэшировались в C:\Symbols, все готово…
- Если ваш вывод не выглядит так, и вы просто хотите получить все свои символы с официального сервера символов Microsoft, выполните следующую команду в WinDbg: .sympath srv*https://msdl.microsoft.com/download/symbols
Допустим, мы вообще ничего не знаем об ALPC и хотим начать копаться и понимать, как работает ALPC под капотом. Поскольку ALPC не задокументирован, мы не можем начать наше путешествие, заглядывая в богатые каталоги документации Microsoft, но вместо этого мы должны применить методологию, основанную на цикле реверсирования, принятия предположений, проверки предположений и проверки/фальсификации предположений, чтобы, наконец, построить нашу картину ALPC.
Хорошо, если мы ничего не знаем о технологии, кроме ее названия (ALPC), мы можем запустить наш отладчик ядра WinDbg и начать получать некоторую информацию о ней, разрешая вызовы функций, которые содержат имя «ALPC» — это может быть не так. самая умная отправная точка, но это не имеет значения, мы начинаем с чего-то и идем дальше…
Команда WinDbg, которая нам нужна для этого: kd:> x *!*Alpc*
Эта команда будет разрешать имена функций следующего шаблона [ModuleName]![FunctionName], где мы можем использовать подстановочные знаки ('*') как для имен модулей, так и для имен функций. В данном случае это означает, что мы разрешаем все функции, которые содержат слово «Alpc» в своих именах во всех загруженных модулях.
Если вы впервые работаете с WinDbg (или вы похожи на меня и склонны забывать, что означают определенные команды), вы всегда можете использовать меню справки WinDbg для поиска команды через: kd:> .hh [Command], как показано ниже:
Примечание: несмотря на то, что введенная вами команда предварительно выбрана, на самом деле вам нужно нажать кнопку «Показать». Другой вариант — найти команды отладчика онлайн здесь .
Если вы получаете сообщение об ошибке, говорящее о том, что что-то не может быть разрешено, скорее всего, у вас не настроен путь к символу. Убедитесь, что ваши символы либо хранятся локально, либо загружаются с https://msdl.microsoft.com/download/symbols (или с обоих). Вы можете проверить свою симпатию с помощью: .sympath
Если вы правильно настроили путь к символу, вы получите большое количество результатов, показывающих все виды функций, которые содержат имя «ALPC». Если что-то занимает слишком много времени (из-за опечатки, или что-то не может быть решено, или возникла какая-либо другая проблема), вы всегда можете нажать <CTRL>+<Break> или открыть меню « Отладка и нажать Break» , чтобы остановить текущее действие:
Отсюда вы должны скопировать все разрешенные функции в редактор по вашему выбору (я использую VisualStudio Code ) и отсортировать их по имени, чтобы получить представление о том, какие функции Alpc существуют в каких модулях и могут принадлежать каким компонентам. Здесь вам очень поможет строгое соглашение об именах,
применяемое к кодовой базе Windows, поэтому давайте посмотрим на это:
Чтобы сделать это более читабельным:
Код:
00007ff9`49498c54 >> The function address
ntdll >> The module name ("ntddl" in this case)
! >> The seperator
Tp >> Abbreviation of the component ("Thread Pool" in this case)
p >> Abbreviation of the function type ("private")
AllocAlpcCompletion >> Descriptive name of the functions
Применяя эти знания ко всем перечисленным функциям, мы можем отсортировать и организовать разрешенные функции, чтобы создать приблизительную картину того, где (в кодовой базе) они реализованы:
Ценность этого шага не в том, чтобы быть точным на 100% или получить метку, назначенную каждой функции, а вместо этого создать приблизительное отображение того, какие части ОС связаны с ALPC, и какие из этих модулей и названия функций кажутся знакомыми, а какие нет. 'т.
Отсюда мы можем перейти к модулям, которые кажутся нам знакомыми (или интересными). Например, мы заметили ntdllмодуль, который, как мы знаем, является пограничным шлюзом пользовательской области для вызова собственных системных (ядерных) служб (функций). Таким образом, мы можем предположить, что Windows позволяет пользовательским процессам вызывать определенные функции ALPC, что сводится к предположению, что «ALPC можно использовать из пользовательских приложений».
Глядя только на функции «*Alpc*» внутри ntdll , мы можем обнаружить, что существует 4 типа функций:
- Бескомпонентные функции, например: ntdll!AlpcRegisterCompletionList
- Nt-компонентные функции, например: ntdll!NtAlpcCreateResourceReserve
- Zw-компонентные функции, например: ntdll!ZwAlpcCreateResourceReserve
- Функции Tp-Component, например: ntdll!TppAllocAlpcCompletion
Опять же, цель здесь не в том, чтобы выбрать конкретный набор функций, а просто чтобы сделать выбор на основе чего-то. Всегда полезно выбирать то, что вы знаете или звучит знакомо, и циклически повторять путь обучения оттуда…
Верхний список бескомпонентных функций ALPC содержит много имен функций, содержащих слова «CompletionList», которые могут показаться вам знакомыми или нет. Нижний список функций Nt ALPC, с другой стороны, кажется довольно разнородным, и, основываясь на Nt соглашении об именах компонентов Мы углубились так далеко, поэтому давайте возьмем одну из этих функций и начнем реверсивную работу.
Нет правильного или неправильного выбора, вам может повезти, и вы выберете функцию, предназначенную для использования на ранней стадии настройки ALPC, которая имеет дополнительные подсказки о том, как использовать ALPC, или вы можете неосознанно выбрать функцию. это предназначено только для специальных сценариев ALPC … радость от недокументированных вещей…
На данный момент мы не можем знать, какая функция является хорошей отправной точкой, поэтому давайте выберем ту, которая, по крайней мере, звучит так, как будто она предназначена для использования в начале процесса, например что-то с Create в своем имени:
Я, очевидно, уже знаю, что эта функция будет полезна, так что простите меня за танец «давайте выберем что-нибудь наугад».
От пользователя к ядру
Давайте запустим Ghidra и посмотрим на NtAlpcCreatePortфункционировать внутри ntdll.dll:
Хорошо… это не слишком полезно… и к тому же выглядит странно. Системный вызов выполняется без аргументов, после чего функция возвращает целое число 0x79 …
Двойная проверка этого декомпилированного кода с фактическими инструкциями, отображаемыми рядом с декомпилированным окном, показывает другую картину:
Фактические инструкции кода показывают, что целочисленное значение 0x79 перемещается в EAX , а затем выполняется системный вызов . Быстро дважды проверьте это с помощью IDA Free , чтобы убедиться
Да, хорошо, это имеет больше смысла. Первый вывод: Ghidra — действительно отличный инструмент, функция декомпиляции может быть ненадежной (даже для простых функций), но, с другой стороны: автоматическая декомпиляция — это огромная функция, которая здесь раздается бесплатно, так что никаких обид о некоторых ошибках и ручной двойной проверке.
Мы поняли NtAlpcCreatePortфункционировать внутри ntdll.dllв значительной степени вызывает режим ядра сразу, используя номер системного вызова 0x79(121 в десятичной системе).
Отсюда мы получили три варианта продолжения:
- Сразу же отправляйтесь в ядро и ищите функцию с похожим именем и надеемся, что мы получим правильное (имена функций ntdll и ядра часто очень похожи) — это наименее надежный метод .
- Найдите номер системного вызова ( 0x79 ) в Интернете, чтобы найти соответствующую функцию ядра.
- Вручную выполните процесс получения и разрешения номера системного вызова в вашей хост-системе — это самый надежный метод .
Поиск номера системного вызова онлайн
Один из лучших (и самых известных) ресурсов для поиска номеров системных вызовов — https://j00ru.vexillium.org/syscalls/nt/64/ (системные вызовы x86 можно найти здесь ).
Для моей системы Windows 10 20H2 этот отличный онлайн-ресурс напрямую указывает мне на функцию ядра с именем «NtAlpcCreatePort».
Пошаговое выполнение системного вызова вручную
Я изучил и применил процесс на сайте www.ired.team , все заслуги и похвалы принадлежат ired.team!
Мы можем использовать WinDbg для ручного извлечения соответствующей функции ядра из наших отлаженных хост-систем. Здесь задействовано 6 шагов:
- Установка точки останова в ntdll в ntdll!NtAlpcCreatePortчтобы перейти в функцию. Это можно сделать с помощью следующей команды WinDbg:
kd:> bp ntdll!NtAlpcCreatePort - Убедитесь, что наша точка останова установлена правильно, с помощью: kd:> bl
- Пусть отладчик работает до тех пор, пока не будет достигнута эта точка останова в ntdll: kd:> g
- Убедитесь, что мы находимся в правильном месте и перед вами системный вызов: kd:> u .(разобрать следующие инструкции)
- Найдите смещение в SSDT (таблица дескрипторов системных служб) для номера системного вызова, 0x79 : kd:> dd /c1 kiservicetable+4*0x79 L1
- Проверка адреса функции системного вызова по смещению SSDT: kd:> u kiservicetable + (02b62100>>>4) L1
Все эти шаги можно найти на скриншоте ниже:
Используя любой из этих трех методов, мы пришли бы к такому результату, что ntdll!NtAlpcCreatePortобращается к ядру в nt!NtAlpcCreatePort
Охота на объект ALPC
Теперь мы поняли, что в конечном итоге вызовем ядро в nt!NtAlpcCreatePort, так что давайте посмотрим на это.Мы можем запустить IDA Free ( Ghidra тоже подойдет), открыть ntoskrnl.exe из нашего системного каталога, например, C:\Windows\System32\ntoskrnl.exe , загрузить общедоступные символы Microsoft, и мы сможем найти вызов функции NtAlpcCreatePort. Оттуда мы можем просмотреть вызываемые функции, чтобы получить первое представление о том, что происходит под капотом для этого вызова.
После первых нескольких вызовов функций мы направимся к вызову ObCreateObjectEx, который является вызовом функции ObjectManager (Ob) для создания объекта ядра. Похоже, наш объект ALPC создан здесь, и IDA также сообщает нам, что это за объект, двумя строками над отмеченным вызовом в окне справа, AlpcPortObjectType. На данный момент я хотел бы попытаться заполучить такой объект, чтобы лучше понять и понять, что это на самом деле. Как функция ObCreateObjectExсоздаст объект, план здесь состоит в том, чтобы переключиться обратно на WinDbg и установить точку останова сразу после этого вызова, чтобы найти и проверить созданный объект.
После установки этой точки останова мы нажимаем gчтобы позволить WinDbg работать, и как только он срабатывает, мы проверяем, можем ли мы найти созданный объект, на который где-то ссылаются. Надежный метод для этого — следовать процессу создания объекта в ObCreateObjectExи отследить, где хранится объект после завершения функции (менее надежный вариант — проверить общие регистры и стек после завершения функции).
В этом случае мы можем найти созданный объект ALPC в регистре RCX, как только мы достигнем нашей точки останова.
Сладко, мы нашли недавно созданный объект порта ALPC. В этот момент !objectКоманда может сообщить нам тип объекта, расположение его заголовка и его имя, но не может добавить дополнительную информацию об этом объекте, потому что теперь не знает его внутренней структуры. Мы тоже не знаем, но можем проверить, есть ли внутри ядра подходящая общедоступная структура, которую мы можем разрешить. Мы попробуем это с kd:> dt nt!*Alpc*Port…
Мы снова использовали подстановочные знаки в сочетании с информацией, которую мы получили до сих пор, а именно: мы ищем структуру внутри модуля ядра ( nt ), и мы ищем структуру, которая соответствует объекту, который, как мы знали, имеет тип AlpcPortObjectType. . Соглашение об именах в Windows часто называет структуры с начальным символом подчеркивания и всеми заглавными буквами. Первый хит ntkrnlmp!_ALPC_PORTвыглядит многообещающе, так что давайте поместим наш захваченный объект порта ALPC в эту структуру:
Это действительно похоже на совпадение, однако некоторые атрибуты, которые можно было бы ожидать установить, пусты, например атрибут «OwnerProcess». Прежде чем мы выбросим наше совпадение в мусорное ведро, давайте вспомним, что мы все еще находимся в точке останова сразу после ObCreateObjectEx, значит, объект только что создан. Возвращаясь к функциям, которые мы прошли в IDA, мы можем обнаружить, что есть еще пара функций, которые нужно вызвать внутри AlpcpCreateConnectionPortфункция, такая как AlpcpInitializePort, AlpcpValidateAndSetPortAttributesи другие. Похоже, впереди еще кое-что, что мы хотим поймать.
Прямо сейчас мы находимся в каком-то процессе, который создал порт ALPC (до сих пор мы даже не удосужились проверить, какой это процесс), и мы хотим перейти к месту кода после завершения всех функций инициализации и проверить, что наш Структура порта ALPC выглядит так, поэтому вот краткое изложение того, что мы хотим сделать:
- Мы хотим записать адрес нашего объекта ALPC для дальнейшего использования.
- Мы хотим найти конец AlpcpCreateConnectionPortфункция.
- Мы хотим перейти к этому месту в том же процессе, в котором мы сейчас находимся,
- Мы хотим загрузить отмеченный нами объект ALPC в ntkrnlmp!_ALPC_PORTструктуры, чтобы увидеть, как это выглядит.
А вот как это сделать…
- Записываем адрес объекта ALPC… Готово: ffffac0e27ab96e0
2. Нахождение конца AlpcpCreateConnectionPort… Готово 0xfffff803733823c9
Перейти на этот адрес в рамках того же процесса можно с помощью этой команды kd:> bp /p @$proc fffff803733823c9
Примечание. Я также проверяю, в каком процессе я нахожусь до и после вызова, просто чтобы быть в безопасности.
Еще раз проверьте структуру объекта ALPC…
Это выглядит более полным, и отсюда мы могли бы пройтись по всему объекту настройки ALPC так же просто, как использовать ссылки, предоставленные WinDbg, чтобы проверить, какие другие структуры и ссылки связаны с этим объектом.
Просто в качестве примера и чтобы дважды подтвердить, что этот объект порта ALPC действительно принадлежит svchost.exe , который мы определили выше, мы можем проверить _EPROCESS , показанную на ntkrnlmp!_ALPC_PORT + 0x18:
Мы находим ImageFileName процесса-владельца объекта ALPC, который, как мы поймали, является «svchost.exe», что соответствует процессу, в котором мы сейчас находимся.
На данный момент мы нашли объект порта ALPC со всеми настройками, который мы можем далее проанализировать в WinDbg, чтобы изучить другие атрибуты этого объекта ядра. Я не собираюсь углубляться в этот момент, но если вы увлеклись копанием глубже, не стесняйтесь продолжать исследовательский тур.
Если вы следуете этому пути, возможно, вы захотите изучить атрибуты порта ALPC, назначенные найденному вами объекту порта, которые отслеживаются в nt!_ALPC_PORT_ATTRIBUTESструктура в nt!_ALPC_PORT + 0x100для проверки Quality of Service (QOS), назначенного этому объекту ( nt!_ALPC_PORT + 0x100 + 0x04).
Если вы обнаружили объект порта ALPC с уровнем имперсонациивыше SecurityIdentification , возможно, вы нашли интересную цель для атаки с имперсонацией , подробно описанной в моем предыдущем посте Offensive Windows IPC Internals 3: ALPC .
К настоящему времени вы должны быть готовы исследовать и копаться в ALPC. Первые шаги, очевидно, будут медленными, и вы (и я) сделаете несколько неправильных поворотов, но это часть опыта каждого.
Если бы я мог добавить последнее замечание, чтобы помочь в радостной поездке, это было бы так: мне лично нравится читать старые добрые книги в мягкой обложке, чтобы учиться, копать глубже и улучшать свои навыки работы с внутренними компонентами Windows. Если вы относитесь к тому же типу, вам могут понравиться эти ссылки на книги (если они еще не лежат у вас на столе):
- Внутреннее устройство Windows, часть 1
- Внутреннее устройство Windows, часть 2
- Отладка внутри Windows
- Программирование ядра Windows
Уже опубликовано 1-е издание этого, но если вам нужны последние и лучшие, вы можете дождаться @zodiacon нового выпуска