Windows injection по версии GigaChat. Исключительно для привлечения внимания
Windows injection по версии GigaChat. Исключительно для привлечения внимания

Приветствую всех!

Сегодня я вам представлю свои наработки по способу инжекта 64-битных DLL библиотек в процессы WOW64, сам который считал невозможным. Любителей потрогать внутренности Windows приглашаю под кат.

Windows обеспечивает совместимость со старыми x86-приложениями с помощью прослойки WOW64. Эта технология создает 32-битное пространство, совместимые структуры PEB/TEB и предоставляет полный набор 32-битных библиотек для работы с Win32 API. Важно отметить, что эти библиотеки не всегда копируют код из нативных 32-битных библиотек Windows x86. Вместо этого, выполняется выход из эмуляции 32-битного режима, а затем передается управление в соответствующую функцию из 64-битной библиотеки. В частности, это касается ntdll.dll, поэтому в процессах WOW64 всегда находятся два экземпляра ntdll: один нативный x64 и второй — WOW64.

Это разные ntdll, но находятся в одном адресном пространстве
Это разные ntdll, но находятся в одном адресном пространстве

Выход из эмуляции можно выполнить и произвольно, для выполнения 64-битного кода прямо посреди 32-битного. Этот метод называется Heaven's Gate и доступен в любом WOW64 процессе, но подобный код не получится выполнить в Windows x86, так как там этот механизм отсутствует. Переключение всегда выполняется для всех потоков в WOW64 процессе, если указатель на начальную функцию находится в памяти корректно загруженного 32-битного модуля (exe или dll). Однако, если адрес указывает на код в 64-битном модуле, переключение не произойдет и режим потока останется прежним, что открывает определенные возможности.

Определение типа контекста для потока выполняет wow64.dll!Run64IfContextIs64
Определение типа контекста для потока выполняет wow64.dll!Run64IfContextIs64

Благодаря тому, что любом WOW64 процессе всегда есть несколько 64-битных библиотек, можно использовать свободное место в конце секции .text для выполнения шеллкода, чтобы загрузить нашу библиотеку. Также важно понимать, что DLL-библиотеки можно загружать стандартным способом (например, с помощью LoadLibrary) или вручную, выполняя все действия PE-загрузчика самостоятельно — этот метод называется Manual Map. В этой части мы рассмотрим первый способ.

Место для шеллкода
Место для шеллкода
Hidden text

Предупреждая вопросы - система позволяет записать данные в RX страницы без каких-либо проблем, но такие изменения не проходят бесследно - выполняется механизм Copy-on-Write, создающий изолированную измененную копию для конкретного процесса, что можно отследить с помощью QueryWorkingSetEx (пример кода)

Загрузка DLL с помощью LoadLibrary (но не совсем)

LoadLibrary — функция Win32 API для загрузки динамических библиотек, определенная в kernel32.dll, и часто используемая как явно, так и неявно. Однако в нашем случае мы не можем её использовать, так как kernel32.dll в процессе принадлежит подсистеме WOW64 и может загружать только 32-битные библиотеки. LoadLibrary сама по себе не выполняет загрузку, а вызывает недокументированную функцию LdrLoadDll из ntdll, которая и выполняет всю работу. К счастью, LdrLoadDll, хоть и не упоминается на MSDN, доступна в отладочных символах и публично экспортируется из ntdll. Прототип вызова этой функции следующий:

NTSTATUS __stdcall LdrLoadDll(PWSTR SearchPath, PULONG LoadFlags, PUNICODE_STRING Name, PVOID *BaseAddress)

Так как 64-битная версия ntdll всегда загружается по одному и тому же адресу в памяти, мы можем найти адрес LdrLoadDll и вызвать её в пространстве процесса-цели следующим образом:

  1. Получаем адрес LdrLoadDll с помощью GetProcAddress.

  2. Находим свободное пространство в сегменте .text в ntdll для шеллкода.

  3. Открываем хендл процесса-цели с помощью OpenProcess.

  4. Выделяем страницу памяти под контекст шеллкода с помощью VirtualAllocEx (или находим свободную RW страницу памяти, убедившись, что это не нарушит работу программы).

  5. С помощью WriteProcessMemory записываем в контекст адрес LdrLoadDll и данные о пути к загружаемой библиотеке

  6. Записываем сам шеллкод в найденное место в ntdll.

  7. Создаем новый поток в ntdll с помощью CreateRemoteThread.

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

Пример кода можно увидеть на github.

Hello World из x64 библиотеки в пространстве WOW64
Hello World из x64 библиотеки в пространстве WOW64

Плюсы такого инжекта заключаются в том, что LdrLoadDll выполнит полную загрузку библиотеки, подгрузив все необходимые зависимости, включая 64-битную kernel32.dll и другие. Во-первых, это удобно, так как не требует самостоятельной подгрузки зависимостей. Во-вторых, инжектируемая библиотека не нуждается в серьезных переработках под спартанские условия.

Минусы заключаются в том, что библиотека будет явным образом видна в списке загруженных библиотек и её можно загрузить только из файла на диске, что может стать ограничением для некоторых задач. Тем не менее, для большинства 32-битных приложений такая библиотека будет незаметна. Например, это можно использовать для перехвата NT-функций в 64-битной ntdll, тогда как все проверки на предмет изменений будут направлены на 32-битную версию.

В следующей части я опишу, как выполнить тот же инжект 64-to-32, но уже загружая библиотеку вручную - иначе говоря manual map. Это освободит от упомянутых минусов LdrLoadDll-инжекта, а также даст гибкость при загрузке.

Всех благодарю за внимание.

Комментарии (6)


  1. Ilya_JOATMON
    08.07.2024 19:19

    Некоторый оффтоп, но может кто знает, есть ли какие открытые разработки по загрузке для исследования кернел драйверов windows в user space?


    1. boris768 Автор
      08.07.2024 19:19
      +1

      я думаю, что вы ищите это
      https://github.com/waryas/KACE/


      1. Ilya_JOATMON
        08.07.2024 19:19

        Да, похоже на то. Жаль, что два года как заброшено. Ну ладно, допиливать все равно по месту придется.


  1. NN1
    08.07.2024 19:19

    Есть библиотека с удобными обёртками для работы из под WOW64: https://github.com/rwfpl/rewolf-wow64ext/


    1. boris768 Автор
      08.07.2024 19:19

      Вот этот вариант будет немного получше, не используется инлайн ассемблер и компилируется не только с помощью msvc


      1. NN1
        08.07.2024 19:19
        +1

        Зато имеем опкоды записанные в бинарном виде.

        Лучше ассемблером , чтобы было легко править.