The Kernel-Bridge Framework

Тема в разделе "WASM.PROJECTS", создана пользователем HoShiMin, 18 ноя 2018.

  1. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    А я уже встроил :3
    Хуклиба в комплекте, можно хукать прямо в драйвере.

    Да нет, мне это интересно лишь в качестве концептов, без какого-то конкретного применения - тем более в таких узкоспециализированных инструментах, как DBI. Пусть этим аверы и занимаются)
    Изначально была какая идея: сделать драйвер, который позволит в юзермоде делать то же самое, что и в ядре, с минимумом ограничений, и предоставить удобный API и набор полезных функций для разработки уже в ядре. А уж как это будут использовать - дело десятое.
    --- Сообщение объединено, 2 авг 2020 ---
    Интересный отладчик на гипервизоре, в октябре ждём релиз: https://github.com/HyperDbg/HyperDbg
     
    q2e74 нравится это.
  2. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    А, ну хорошо тогда.

    Не, я не настаиваю, просто видимо слишком давно тим лидом работаю, всему ищу реализацию в комерции.
     
  3. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    > Считай, что гипервизор - удобный инструмент для двух вещей

    Я не думаю что удобный. Эти все задачи решались всегда и без хард вирты. И Грейт писал ядерный отладчик даже более того вся ось пересобиралась. Зачем для какого то патча нужно запускать гпв я не понимаю. А про память вообще полный бред. Скрывается она на уровне выборки. Допустим есть у тебя область памяти и выбирающий в неё код, что и как ты скроешь не понятно. Подмена данных - это смещение адресной компоненты(с-анклав), твой визор такое не умеет(тк не мониторит саму выборку).

    Скрытие памяти - никто эту задачу не решил. Это базовая для крипторов, скрытие области от ядерного сканера памяти. Но эта задача решена(с-анклавы) и не только для памяти, но и для авер вирты. Одной общей техникой.
     
    Последнее редактирование: 2 авг 2020
  4. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Значит, я первопроходец, потому что на гитхабе у меня готовый код, который делает именно это, а в первой статье по гипервизорам наглядная гифка с демонстрацией работы. Правда, на гифке не скрытие, а подмена, но не суть.

    Считай, я сделал концепт того же самого, но без костылей и страшных патчей системы. И, вдобавок, высокопроизводительный и thread-safe by design. И немногословный: шутка ли, логика ВСЕГО гипервизора умещается всего в полторы тысячи строчек в одном единственном файлике.
     
  5. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Так а напомни мне, зачем тебе интерпретатор в ядре понадобился, чтобы гипервизору скрипты писать?
     
  6. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    Давай пример, ты подменяешь данные для произвольного кода. Самый примитивный пример - аллокация памяти, выборка(нп запись-чтение строки). Ты возвращаешь иную строку. Сойдёт ?

    Как же ты подменишь данные если архитектурно твой визор не отслеживает саму выборку !?
     
  7. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Да нет, чтобы просто писать ядерные скрипты.
    Но если получится юзать их на высоких IRQL - можно скриптовать и гипервизор.

    Отслеживает. Только отслеживает обращения не к виртуальной памяти, а к физической.
    Я же объяснял: после трансляции в PTE гостевой физический адрес транслируется через EPT в хостовый физический.
    У каждого логического процессора своя EPT.
    В этих таблицах можно разрешить или запретить доступ к странице отдельно на чтение, запись и исполнение.
    Если процессор натыкается на запрет в EPT, управление получает гипервизор и решает, что делать дальше.
    В этом обработчике я могу сгенерировать любой эксепшн или подменить страницу в EPT и снова отдать управление гостю: виртуальный адрес останется тем же, но смотреть будет уже на другую физическую память - на какую мне надо.
    И так, переключая физические страницы в EPT, я могу отдавать гостю разную память. И всё это для каждого процессора отдельно, без необходимости синхронизаций.
     
  8. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    > Я же объяснял: после трансляции в PTE гостевой физический адрес транслируется через EPT в хостовый физический.

    Это всё полная чушь ты не понимаешь суть скрытия памяти.

    Смотри допустим есть выборка mov r,[m]

    m указатель на область которую нужно скрыть. Без самой выборки, в этом примере mov r,[m], нет линейного адреса. Ты можешь эту выборку обнаружить лишь через ловушку, но что это тебе даст.

    Подмена памяти - софтверный анклав, в данном примере выборка изменится mov r,[m + delta].

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

    У тебя же не понятно что вообще. Что за манипуляции с таблицами трансляции, зачем ??
     
    hiddy и q2e74 нравится это.
  9. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Ну Инди)
    Тебя даже лайкнули, а я теряюсь в догадках, как тебе объяснить!

    Давай на примере.
    Допустим, в процессе есть страница с кодом по адресу 0x12345.
    И, например, сделаем так:
    - На чтение там будут одни нули
    - На исполнение - оригинальный код, который был там записан
    - На запись - проигнорируем инструкцию: она выполнится без ошибок, но фактическая запись произведена не будет

    И такое поведение должно быть для любых режимов (юм/км) и для доступа из любых процессов.

    Обозначим оригинальную страницу буквой X (executable).
    Выделим любым способом (VirtualAlloc/MmAllocateContiguousMemory/etc.) страницу, которая будет видна на чтение, и обозначим её буквой R (readable).
    Так как на запись нам подменять ничего не надо, то и отдельную страницу для неё выделять не будем.

    Заполним страницу R данными, которые будут видны при чтении (в данном случае, заколотим туда нули).

    Теперь запускаем гипервизор, получаем физический адрес страницы X и находим соответствующую запись в EPT для этого физического адреса.

    В найденном в EPT элементе делаем эту страницу только читабельной (убираем флаги Writeable и Executable), а хостовый физический адрес заменяем на физический адрес страницы R.
    Теперь у нас любое чтение из R транслируется так: 0x12345 -> guest_phys_addr_of_page_X -> host_phys_addr_of_page_R.
    Проще говоря, при чтении по виртуальному адресу 0x12345 будет читаться страница R.

    Итак, процессор читает, видит R, а потом вдруг кто-то вызвал "call 0x12345" и процессор начал её исполнять.
    На прошлом шаге мы убрали у страницы бит Executable: при попытке "исполнить" страницу срабатывает особый гипервизорный эвент "EPT_VIOLATION", который мы ловим и обрабатываем.

    Итак, управление получил наш гипервизор и процессор нам сказал, что эвент сработал при попытке получить доступ на исполнение к неисполняемой странице по такому-то адресу.
    Мы патчим в EPT хостовый физический адрес на адрес страницы X (был R, стал X), убираем флаг Readable и взводим флаг Executable.
    И отдаём управление гостю. Теперь он может только исполнять страницу, но не может писать и читать.

    Теперь виртуальный 0x12345 транслируется в страницу X.

    Итак, процессор исполняет страницу X и вдруг решает что-то в неё записать.
    Как мы помним, в EPT страница помечена как Executable, но нечитаемая и незаписываемая. Соответственно, при попытке записи снова сработает EPT_VIOLATION.
    Мы опять его ловим и видим, что в страницу хотят записать. А так как мы хотим просто игнорировать все записи, мы сдвигаем гостевой EIP/RIP на одну инструкцию вперёд и отдаём управление гостю обратно.
    С точки зрения гостя инструкция выполнилась, а записи не было. И в EPT у страницы атрибуты по-прежнему Executable-only.

    Таким образом, переключая RWX-атрибуты у страницы в EPT и обрабатывая EPT_VIOLATION'ы, мы можем легко и очень быстро подменять и скрывать память.
    Например, если хочется память именно СКРЫТЬ, в обработчике EPT_VIOLATION'a ты можешь заинжектить в гостя #GP или #PF (или любой другой эксепшн). Тогда при попытке доступа к памяти будет вылетать исключение, будто памяти не существует.

    Вот так. Надеюсь, теперь понятно, как это работает. И никакие анклавы здесь не нужны: всё выполняется аппаратно, очень просто и очень быстро.
     
    Последнее редактирование: 2 авг 2020
    hiddy и q2e74 нравится это.
  10. q2e74

    q2e74 Active Member

    Публикаций:
    0
    Регистрация:
    18 окт 2018
    Сообщения:
    999
    да, лайкнул. Очень интересно следить за вашим обсуждением, иногда вроде не связанные с темой вещи, но приходят в голову. Где-то четче вижу свои белые пятна.
     
  11. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Ты давай не спорь с великим мастером аверской науки, а то превратишься из его любимчика в обычного магла вроде Рела.
     
  12. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    > Итак, процессор читает, видит R, а потом вдруг кто-то вызвал "call 0x12345" и процессор начал её исполнять.

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

    > Итак, процессор исполняет страницу X и вдруг решает что-то в неё записать.

    Так это обычная ловушка #GP. И зачем для неё нужен гипер ?

    > если хочется память именно СКРЫТЬ, в обработчике EPT_VIOLATION'a ты можешь заинжектить в гостя #GP или #PF

    Да как бы нет. Тебе для этого нужно отследить выборку иначе ты что не делай с памятью так она и останется открытой. Слишком много извратов не ясно зачем. Дружище ты походу закопался в системных вещах не понимая зачем это всё нужно :)
     
  13. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Ты что, здесь два визора схлестнулись в схватке не на жизнь, а на бсод!

    Ну открой SDM и найди главу "Ept violation".

    ...и заодно главу про events injection, не помню точное название, но где-то недалеко.

    Чтобы работать без влияния на систему и чтобы избавиться от проблем синхронизации в многопроцессорной среде.
    И потому что гипервизор, как ни странно, намного проще, чем сложный и ненадёжный костыль в виде самописных ловушек на #GP.

    А у меня - ни синхронизаций не надо, ни системных патчей, с PatchGuard дружу, с небольшими доработками заведётся и на линуксе, и на маке, и даже в UEFI.

    Какие ещё нужны причины не писать костыли?
     
    Rel и q2e74 нравится это.
  14. q2e74

    q2e74 Active Member

    Публикаций:
    0
    Регистрация:
    18 окт 2018
    Сообщения:
    999
    дай очередь соображений в эту сторону плз. что, где, когда, как.
     
  15. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    На хабре почитай, там есть хорошее введение в разработку под UEFI на EDK2 (кажется, в трёх частях). Начиная с настройки окружения и отладки и закачивая рабочим модулем, который можно запустить в UEFI-шелле.
    Для новичков обязательно к прочтению.

    Сабж: https://m.habr.com/ru/post/338264/
     
  16. q2e74

    q2e74 Active Member

    Публикаций:
    0
    Регистрация:
    18 окт 2018
    Сообщения:
    999
    HoShiMin, не, я про другое. Раскрой мысль, как в уефи воткнуть вм так, чтобы прозрачно для оси сливать на сторону трассы системных событий. Или на примере с подменой кода.

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

    и еще одно уточнение, это всё та же вм, что и в твоих статьях?
     
  17. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    Ох уж эти визоры.
     
  18. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Так ведь нет разницы, где запускать виртуалку. Код платформонезависимый (с оговорками) и работает хоть на голом железе.
    Виртуалка ничего не знает о том, где и на чём она запущена, равно как не знает ничего о том, какой код крутится внутри неё. Ей просто прилетают абстрактные эвенты, она их как-то обрабатывает.

    Да, всё та же. У меня из платформозависимого кода только функции для выделения памяти и межпроцессорных прерываний. Заменить их на линуксовые аналоги - запустится под линуксом. Взять аналоги из UEFI - запустится и под ним. Написать свои - можно и на голом железе запустить.
     
    Последнее редактирование: 3 авг 2020
  19. Indy_

    Indy_ Well-Known Member

    Публикаций:
    4
    Регистрация:
    29 апр 2011
    Сообщения:
    4.775
    HoShiMin,

    EPT viol, это какие то тех механизмы, которые мне точно не нужно знать:

    Это излишнее усложнение элементарных задач.

    И с выборкой это не свзано, это какие то тех нюансы хард вирты:

    И как через это найти чтение в память, те выборку ?

    Да и в общем, зачем это всё нужно если практического смысла не имеет. Тебе может и интересно, но всем другим нужен практический пример. Зачем все эти сложности. И фаулт обработать задача между прочем примитивная и тривиальная.
     
  20. HoShiMin

    HoShiMin Well-Known Member

    Публикаций:
    5
    Регистрация:
    17 дек 2016
    Сообщения:
    1.455
    Адрес:
    Россия, Нижний Новгород
    Indy_, не та глава. EPTP Switching тебе не нужен.

    Ты делаешь данные нечитабельными и ловишь все EPT Violation'ы при попытке кого угодно к ним обратиться. Вот тебе и выборка.

    Проблемы сразу две:
    1. PatchGuard.
    2. Многопроцессорность.

    На второй остановимся подробнее. Допустим, задача та же: на чтение отдать нули, на исполнение обычную страницу, запись проигнорить.

    В этом случае изолированно менять страницы не получится, т.к. PTE общая для всех процессоров.

    Делаем данные нечитабельными, первый процессор их пытается прочитать и тебе прилетает фолт.
    Ты ловишь фолт и подменяешь в PTE физический адрес страницы.
    В этот момент второй процессор вызывает "call твоя_страница", но т.к. первый процессор поменял в PTE адрес, там сейчас страница с нулями. В итоге краш.
    Нужно как-то это синхронить. В итоге ты просто умножишь профайл на ноль.
    И я уже не говорю, что синхронизация в ядре (и на высоких IRQL) - нетривиальная задача.

    Гипервизор архитектурно лишён всех этих недостатков.