Всем привет! Столкнулся с проблемой, что когда через LoadLibrary() загружаю либу lpk.dll (и не только её), видимо из-за зависимостей она автоматом подгружает и User32.dll. Но по условию User32 должна отсутствовать в памяти процесса, и если я пытаюсь её выгрузить через FreeLibrary(), то система не даёт этого сделать. GetLastError() вообще никак не реагирует, возвращая код далеко предыдущей ошибки. Есть-ли способ в этой ситуации избавиться от User32.dll ?
Как же ты выгрузишь либу, если от неё зависят другие? Сделай так, чтобы твоя либа и её зависимости от user32 не зависели - и не будет она грузиться.
HoShiMin, в общем это тесты с LdrRegisterDllNotification() из Ntdll.dll Регаю 2 колбека - первый парсит загрузку, а второй выгрузку библиотек. В импорте прожки только Ntdll, Kernel32 и Msvcrt.dll, а дальше колбек исправно срабатывает при LoadLibrary(). В логе ниже я загружаю всего одну системную либу MF.dll, а она тянет за собой ещё 19 штук, среди которых есть и User32. Конкретно из MF.dll ни одна функция не обращается к User32, хотя некоторые форвардятся в Gdi32.dll, функции которой видимо и зависят от User32. Вот я и хочу оборвать как-то эту цепочку, но видимо не получится. Хотя когда выгружаю одну IMM32.dll, она на автомате прихватывает за собой и Msctf.dll: Код (Text): LdrpDllNotificationList ::: Ntdll base: 0x0000000076E80000 ---------------------------------------------------------- 1. Flink: 0x00000000000F5300 -> Blink: 0x0000000076FB2CD0. Callback: 0x0000000000402000 2. Flink: 0x0000000076FB2CD0 -> Blink: 0x00000000000F52B0. Callback: 0x0000000000402070 NotificationListHead offset from Ntdll: 0x0000000000132CD0 Dll loaded : 0x000007fee4c50000 4036 kByte mf.dll Dll loaded : 0x000007fefa900000 104 kByte ATL.DLL Dll loaded : 0x0000000076d80000 1004 kByte USER32.dll Dll loaded : 0x000007feff100000 416 kByte GDI32.dll Dll loaded : 0x000007fefd780000 56 kByte LPK.dll Dll loaded : 0x000007fefd4a0000 812 kByte USP10.dll Dll loaded : 0x000007fef9d70000 436 kByte MFPlat.DLL Dll loaded : 0x000007fefd1a0000 904 kByte ADVAPI32.dll Dll loaded : 0x000007fefeb00000 124 kByte sechost.dll Dll loaded : 0x000007fefeb20000 1172 kByte RPCRT4.dll Dll loaded : 0x000007fefd790000 308 kByte WS2_32.dll Dll loaded : 0x000007fefd7e0000 32 kByte NSI.dll Dll loaded : 0x000007fefd890000 2052 kByte ole32.dll Dll loaded : 0x000007fefcee0000 880 kByte OLEAUT32.dll Dll loaded : 0x000007fefd570000 452 kByte SHLWAPI.dll Dll loaded : 0x000007fefb2a0000 36 kByte AVRT.dll Dll loaded : 0x000007fefb940000 48 kByte VERSION.dll Dll loaded : 0x00000000745f0000 24 kByte ksuser.dll Dll loaded : 0x000007fefce10000 184 kByte IMM32.DLL Dll loaded : 0x000007fefd5f0000 1072 kByte MSCTF.dll Dll unloaded: ----> IMM32.DLL Dll unloaded: ----> MSCTF.dll
А если попробовать загрузить эту библиотеку вручную и слинковать все вручную без USER32.DLL. Это конечно, не тривиально, но должно помочь.
Когда процесс выполняет загрузку user, поток который это делает на стороне ядра прогружает теневую вторую сервисную таблицу. Такой тред уже может вызывать ядерные интерфейсы gui. В процесс отображается ядерная память окон, становятся доступными обратные ядерные вызовы. Это нельзя отменить.
MaKsIm, мне нужен LoadLibrary(), чтобы сработал колбек. Конечно в папке Win можно найти либу, которая грузится без зависимости от User32.dll, но как оказалось таких крайне мало и все они привлекают лишнее внимание. Строкой NotificationListHead offset from Ntdll: 0x0000000000132CD0 я получаю смещение списка LIST_ENTRY в секции-данных Ntdll - этот лист перечисляет все колбеки нотификации, и если вклиниться в него в удалённом процессе, то можно будет запустить свой шелл. Поэтому пытаюсь избавиться от всех лишних DLL, т.к. действие будет происходить в чужом консольном процессе без User32. Плохо то, что если чз LoadLibrary() попытаться загрузить уже имеющуюся в памяти DLL, то колбек не срабатывает, иначе вообще проблем-бы не было.
Если удалить юзер либу из памяти, то вероятно возникнет исключение при попытке обратного вызова гуя. Потому что массив ссылок на процедуры загружен при инит гуя. Гуи тоесть тень(shadow) по возможности вынесена в юзер мод. Это сделано для защиты от атак на ядро гуя. Если упадет процесс, не произойдет выполнения кода с ядерными правами. Что касается каких то списков нотифи - это что то странное. Обычно вектора протектятся используя ключи, которые нельзя получить из другого процесса, как например veh или процедурные фреймы. Что то я не помню про удаленную установку каких то обработчиков. Это невозможно из за разделений адресов(aslr). Что бы внести запись в список необходимо вначале открыть процесс, затем напрямую список изменить. Эти операции открытия-записи делают бессмысленными модификацию каких то списков. Если можно писать в память, можно что угодно изменить. --- Сообщение объединено, 6 фев 2025 --- > ни одна функция не обращается к User32 Загрузчик сконвертирует поток в гуи, такой поток отличается от нэйтив. Разумеется будет загружено окружение тени. На сколько помню есть маркер в пе формате.
Так ASLR меняет-же базу только при загрузке системы, и вплоть до последующего ребута она будет стабильной во всех процессах, т.к. сис.либы не загружаются в юм-процессы, а мапятся. Это легко проверить в ProcessHacker/Explorer - у всех процессов Ntdll лежит по одному адресу. Но даже если и менялась-бы база, у меня есть оффсет головы колбек-листа от начала либы. В общем с этим нет проблем. ..например дедовским способом стартовать шелл чз CreateRemoteThread()? в наше время любой авер с базами 80-х годов прибьёт эту технику ещё на взлёте, а потому я и пытаюсь реализовать что-то новое, чз те-же колбеки LDR - описание лежит тут: https://shorsec.io/blog/dll-notification-injection/
Знаю довольно хорошо про атаки на ядро нт. Вы не верно понимаете защиту, каждый процесс локален, его память и описатели. В целях защиты используется рандом. Два модуля не релоцируемы, так как в них находятся фиксированые адреса процедур, это нэйтив и тень. При попытке повторного отображения загрузчик выдаст окно с ошибкой и прокинет фаулт(исключение). Сурки паблик, посмотрите сами. --- Сообщение объединено, 6 фев 2025 --- Тут я немного поискал по LdrpXX, тема не новая. Если можно писать в память, то нет никакого смысла лезть в андок приват нэйтив механиизмы.
Не нравится мне (заказчику) этот Сенкевич. Ну не нравится не ешь. Переименуй все у себя в user33.dll и саму в своей соотв. Dir - у меня работает, без user33.dll соотв. ошибка.
ничего не понял, но обычный ренейм профита не даёт вот экзешник для тестов - просит ввести имя либы для загрузки/выгрузки (можно без расширения *.dll).
../LdrDllNotifi.EXE не является приложением Win32. Заказчик с подобным формальным требованием рискует оказаться - "одушевленным унитазом в женском туалете". "Выгрузить User32.dll через FreeLibrary()" про профит речи в 1-м посте нет. - т.е. User33.dll можно и оставить. В стартере-загрузчике Mew32.exe ссылки на User32.dll еще нужны, а в MEWLIB33.dll уже нет. как пример: P.S. Я не изучал Win - нет интереса, он только инструмент.
Cмена имени либы не даёт ровным счётом ничего, т.к. зависимости прописаны в секциях импорта/экспорта DLL. Может при статическом анализе либа и спрячется (хотя в данном случае user32 в обоих файлах торчит и в IDA), а при загрузке в память сразу-же всплывёт. Это видно в логах отладчика: Код (Text): CommandLine: "F:\User33\MEWLIB32.dll с user_32\Mew32.exe" 0:000> bp @$exentry *** WARNING: Unable to verify checksum for image00000000`00400000 0:000> g ModLoad: 00000000`77530000 00000000`7764f000 WOW64_IMAGE_SECTION ModLoad: 00000000`74ef0000 00000000`75000000 WOW64_IMAGE_SECTION ModLoad: 00000000`77530000 00000000`7764f000 NOT_AN_IMAGE ModLoad: 00000000`77430000 00000000`7752b000 NOT_AN_IMAGE ModLoad: 00000000`74ef0000 00000000`75000000 C:\Windows\syswow64\kernel32.dll ModLoad: 00000000`76920000 00000000`76967000 C:\Windows\syswow64\KERNELBASE.dll ModLoad: 00000000`75520000 00000000`75620000 C:\Windows\syswow64\user32.dll <--------------// ModLoad: 00000000`75620000 00000000`756b0000 C:\Windows\syswow64\GDI32.dll ModLoad: 00000000`769c0000 00000000`769ca000 C:\Windows\syswow64\LPK.dll ModLoad: 00000000`75200000 00000000`7529d000 C:\Windows\syswow64\USP10.dll ModLoad: 00000000`756c0000 00000000`7576c000 C:\Windows\syswow64\msvcrt.dll ModLoad: 00000000`76810000 00000000`768b4000 C:\Windows\syswow64\ADVAPI32.dll ModLoad: 00000000`769d0000 00000000`769e9000 C:\Windows\SysWOW64\sechost.dll ModLoad: 00000000`76c40000 00000000`76d30000 C:\Windows\syswow64\RPCRT4.dll ModLoad: 00000000`74e10000 00000000`74e70000 C:\Windows\syswow64\SspiCli.dll ModLoad: 00000000`74e00000 00000000`74e0c000 C:\Windows\syswow64\CRYPTBASE.dll ModLoad: 00000000`77180000 00000000`77212000 C:\Windows\syswow64\oleaut32.dll ModLoad: 00000000`766b0000 00000000`76810000 C:\Windows\syswow64\ole32.dll ************************************************************ CommandLine: "F:\User33\MEWLIB33.dll с user_33\Mew33.exe" 0:000> bp @$exentry *** WARNING: Unable to verify checksum for image00000000`00400000 0:000> g ModLoad: 00000000`77530000 00000000`7764f000 WOW64_IMAGE_SECTION ModLoad: 00000000`74ef0000 00000000`75000000 WOW64_IMAGE_SECTION ModLoad: 00000000`77530000 00000000`7764f000 NOT_AN_IMAGE ModLoad: 00000000`77430000 00000000`7752b000 NOT_AN_IMAGE ModLoad: 00000000`74ef0000 00000000`75000000 C:\Windows\syswow64\kernel32.dll ModLoad: 00000000`76920000 00000000`76967000 C:\Windows\syswow64\KERNELBASE.dll ModLoad: 00000000`75520000 00000000`75620000 C:\Windows\syswow64\user32.dll <-----------// ModLoad: 00000000`75620000 00000000`756b0000 C:\Windows\syswow64\GDI32.dll ModLoad: 00000000`769c0000 00000000`769ca000 C:\Windows\syswow64\LPK.dll ModLoad: 00000000`75200000 00000000`7529d000 C:\Windows\syswow64\USP10.dll ModLoad: 00000000`756c0000 00000000`7576c000 C:\Windows\syswow64\msvcrt.dll ModLoad: 00000000`76810000 00000000`768b4000 C:\Windows\syswow64\ADVAPI32.dll ModLoad: 00000000`769d0000 00000000`769e9000 C:\Windows\SysWOW64\sechost.dll ModLoad: 00000000`76c40000 00000000`76d30000 C:\Windows\syswow64\RPCRT4.dll ModLoad: 00000000`74e10000 00000000`74e70000 C:\Windows\syswow64\SspiCli.dll ModLoad: 00000000`74e00000 00000000`74e0c000 C:\Windows\syswow64\CRYPTBASE.dll ModLoad: 00000000`77180000 00000000`77212000 C:\Windows\syswow64\oleaut32.dll ModLoad: 00000000`766b0000 00000000`76810000 C:\Windows\syswow64\ole32.dll
Между прочим, имеется сервис NtAreMappedFilesTheSame, который был еще в wrk, а стало быть и сначала самой нт. Он проверяет два указателя на принадлежность одной файловой проекции. И предназначен для загрузчика, что бы из за имен не прогрузить вновь модуль. В новых версиях ос там огород куда больше.
Похоже мое не пройдет. Просто этот у меня уже был тестовый пример - как (ветвь графа) проще модифицировать, и т.к. там тоже речь шла о user32.dll ошибочно притянул сюда. Т.Е. "контейнер - докер" для user33.dll будет ~с весь Win32.