Приветствую всех!
Сегодня я вам представлю свои наработки по способу инжекта 64-битных DLL библиотек в процессы WOW64, сам который считал невозможным. Любителей потрогать внутренности Windows приглашаю под кат.
Windows обеспечивает совместимость со старыми x86-приложениями с помощью прослойки WOW64. Эта технология создает 32-битное пространство, совместимые структуры PEB/TEB и предоставляет полный набор 32-битных библиотек для работы с Win32 API. Важно отметить, что эти библиотеки не всегда копируют код из нативных 32-битных библиотек Windows x86. Вместо этого, выполняется выход из эмуляции 32-битного режима, а затем передается управление в соответствующую функцию из 64-битной библиотеки. В частности, это касается ntdll.dll, поэтому в процессах WOW64 всегда находятся два экземпляра ntdll: один нативный x64 и второй — WOW64.
Выход из эмуляции можно выполнить и произвольно, для выполнения 64-битного кода прямо посреди 32-битного. Этот метод называется Heaven's Gate и доступен в любом WOW64 процессе, но подобный код не получится выполнить в Windows x86, так как там этот механизм отсутствует. Переключение всегда выполняется для всех потоков в WOW64 процессе, если указатель на начальную функцию находится в памяти корректно загруженного 32-битного модуля (exe или dll). Однако, если адрес указывает на код в 64-битном модуле, переключение не произойдет и режим потока останется прежним, что открывает определенные возможности.
Благодаря тому, что любом 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 и вызвать её в пространстве процесса-цели следующим образом:
Получаем адрес LdrLoadDll с помощью GetProcAddress.
Находим свободное пространство в сегменте .text в ntdll для шеллкода.
Открываем хендл процесса-цели с помощью OpenProcess.
Выделяем страницу памяти под контекст шеллкода с помощью VirtualAllocEx (или находим свободную RW страницу памяти, убедившись, что это не нарушит работу программы).
С помощью WriteProcessMemory записываем в контекст адрес LdrLoadDll и данные о пути к загружаемой библиотеке
Записываем сам шеллкод в найденное место в ntdll.
Создаем новый поток в ntdll с помощью CreateRemoteThread.
В шеллкод вызываем LdrLoadDll, остальная работа будет выполнена без нашего участия.
Пример кода можно увидеть на github.
Плюсы такого инжекта заключаются в том, что LdrLoadDll выполнит полную загрузку библиотеки, подгрузив все необходимые зависимости, включая 64-битную kernel32.dll и другие. Во-первых, это удобно, так как не требует самостоятельной подгрузки зависимостей. Во-вторых, инжектируемая библиотека не нуждается в серьезных переработках под спартанские условия.
Минусы заключаются в том, что библиотека будет явным образом видна в списке загруженных библиотек и её можно загрузить только из файла на диске, что может стать ограничением для некоторых задач. Тем не менее, для большинства 32-битных приложений такая библиотека будет незаметна. Например, это можно использовать для перехвата NT-функций в 64-битной ntdll, тогда как все проверки на предмет изменений будут направлены на 32-битную версию.
В следующей части я опишу, как выполнить тот же инжект 64-to-32, но уже загружая библиотеку вручную - иначе говоря manual map. Это освободит от упомянутых минусов LdrLoadDll-инжекта, а также даст гибкость при загрузке.
Всех благодарю за внимание.
Комментарии (6)
NN1
08.07.2024 19:19Есть библиотека с удобными обёртками для работы из под WOW64: https://github.com/rwfpl/rewolf-wow64ext/
boris768 Автор
08.07.2024 19:19Вот этот вариант будет немного получше, не используется инлайн ассемблер и компилируется не только с помощью msvc
NN1
08.07.2024 19:19+1Зато имеем опкоды записанные в бинарном виде.
Лучше ассемблером , чтобы было легко править.
Ilya_JOATMON
Некоторый оффтоп, но может кто знает, есть ли какие открытые разработки по загрузке для исследования кернел драйверов windows в user space?
boris768 Автор
я думаю, что вы ищите это
https://github.com/waryas/KACE/
Ilya_JOATMON
Да, похоже на то. Жаль, что два года как заброшено. Ну ладно, допиливать все равно по месту придется.