Гугл молчит, ида вот что выдает: Код (Text): .text:77F5CD7E LdrSetDllManifestProber proc near .text:77F5CD7E .text:77F5CD7E arg_0 = dword ptr 4 .text:77F5CD7E .text:77F5CD7E mov eax, [esp+arg_0] .text:77F5CD82 mov dword_77FC4390, eax .text:77F5CD87 retn 4 .text:77F5CD87 LdrSetDllManifestProber endp Это как-то связано с LdrLoadDll. Могет кто в курсе, что это за функция?
LdrpManifestProberRoutine equ dword_77FC4390 LdrLoadDll->LdrpWalkImportDescriptor->LdrpManifestProberRoutine
Странно, локально когда указываю на свой диспетчер, потом загружаю либу - норм сработывает. А когда делаю аналогичное удалённо, показывает только kernel32.dll и всё. Код (Text): .686 .model flat, stdcall option casemap :none include \masm32\include\ntdll.inc includelib \masm32\lib\ntdll.lib include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .code LdrpManifestProberRoutine proc param1,param2,param3:dword push ebx push edi push esi call @F @@: pop ebx sub ebx,offset @B mov esi,esp assume fs:nothing lea edx,[offset ShlSehNext + ebx] lea ecx,[offset RemoteSehHandler + ebx] push ebp push esp push edx push ecx push FS:[0] mov FS:[0],esp mov ebx,[esi+24h] mov edi,UNICODE_STRING.Buffer[ebx] push OptionOk;NoWait push ebx call Breake clc ShlSehNext: pop FS:[0] add esp,16 pop esi pop edi pop ebx xor eax,eax ret LdrpManifestProberRoutine endp RemoteSehHandler proc uses edx pExcept:DWORD,pFrame:DWORD,pContext:DWORD,pDispatch:DWORD mov edx,pFrame assume edx:ptr SEH mov eax,pContext assume eax:ptr CONTEXT push [edx].SafeOffset pop [eax].regEip push [edx].PrevEsp sub dword ptr [esp],16 pop [eax].regEsp push [edx].PrevEbp pop [eax].regEbp mov [eax].regEax,STATUS_INVALID_PARAMETER or [eax].regEFlags,1 xor eax,eax ret RemoteSehHandler endp Breake proc uses eax unString:PSTR,ResponceOption:dword Local HardErrorPointer:PVOID Local Response:dword mov eax,unString mov HardErrorPointer,eax lea eax,Response push eax push ResponceOption lea eax,HardErrorPointer push eax push 1 push 1 push STATUS_FATAL_APP_EXIT mov eax,00b6h ;ZwRaiseHardError mov edx,esp int 2Eh lea esp,[esp+6*4] ret Breake endp EndCode: FULLCODESIZE equ (offset EndCode - offset LdrpManifestProberRoutine) Start proc local LdrSetDllManifestProberProc[13]:byte local CodeBuffer[FULLCODESIZE]:byte local CodeAddress:dword local CodeSize:dword local su:STARTUPINFO local pi:PROCESS_INFORMATION ; Запускаем процесс суспенденным invoke GetStartupInfo,addr su invoke CreateProcessA,0,$CTA0("process.exe"),0,0,0,CREATE_SUSPENDED,0,0,addr su,addr pi ; Записываем код обработчика в тело удалённого процесса lea esi,LdrpManifestProberRoutine lea edi,CodeBuffer mov ecx,FULLCODESIZE cld rep movsb mov CodeAddress,0 mov CodeSize,FULLCODESIZE invoke ZwAllocateVirtualMemory,pi.ProcessHandle,addr CodeAddress,0,addr CodeSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE invoke ZwWriteVirtualMemory,pi.ProcessHandle,CodeAddress,addr CodeBuffer,FULLCODESIZE,0 ; Найти dword_7C97C30C и записать туда указатель на наш обработчик invoke GetModuleHandle,$CTA0("ntdll.dll") invoke GetProcAddress,eax,$CTA0("LdrSetDllManifestProber") mov ebx,eax invoke ZwReadVirtualMemory,pi.ProcessHandle,ebx,addr LdrSetDllManifestProberProc,13,0 lea ebx,LdrSetDllManifestProberProc mov ebx,dword ptr [ebx+9] invoke ZwWriteVirtualMemory,pi.ProcessHandle,ebx,addr CodeAddress,4,0 invoke ZwResumeThread,pi.ThreadHandle,0 invoke CloseHandle,pi.ProcessHandle invoke CloseHandle,pi.ThreadHandle invoke ExitProcess,0 Start endp end Start А код удалённого процесса выглядит так: Код (Text): .data Buffer db 1024 dup(0) hTest1 dd 0 hTest2 dd 0 .code start proc CCOUNTED_UNICODE_STRING "dbghelp.dll",szTest1,4 invoke LdrLoadDll,0,0,addr szTest1,addr hTest1 CCOUNTED_UNICODE_STRING "dimap.dll",szTest2,4 invoke LdrLoadDll,0,0,addr szTest2,addr hTest2 invoke wsprintfA,offset Buffer,$CTA0("0%08xh"),eax invoke MessageBox,0,offset Buffer,offset Buffer,0 invoke ExitProcess,0 start endp end start LdrpManifestProberRoutine вить корректно собран ?
- LdrpManifestProberRoutine апгрейтится при инициализации kernel32.dll Так как у тебя регистрация обработчика до инициализации этого модуля, то после тока как поток начнёт исполнять инициализацию kernel32 указатель на твой обработчик будет удалён(затёрт). - Необходимо чтоб общий размер стекового фрейма(локальных переменных) был кратен 4, в частном случае необходимо добавлять выравнивание на 4 байта к переменным, макро нужно переписать с учётом выравнивания: Код (Text): FULLCODESIZE equ (offset EndCode - offset LdrpManifestProberRoutine) FULLCODESIZEALIGN equ ((FULLCODESIZE + 3) And Not(3)) И юзать FULLCODESIZEALIGN. - Так обработчик может быть зарегистрирован, необходимо считать указатель и вызывать его после отработки кода перехватчика. - Не верная модель вызова. Процедура имеет 3 параметра: Код (Text): NTSYSAPI VOID NTAPI LdrSetDllManifestProber( IN PLDR_MANIFEST_PROBER_ROUTINE ManifestProberRoutine ); typedef NTSTATUS (NTAPI * PLDR_MANIFEST_PROBER_ROUTINE) ( IN PVOID DllBase, IN PCWSTR FullDllPath, OUT PVOID *ActivationContext ); - Соответственно неверно извлекается имя модуля.
Учитывая что регистрация выполняется при событии DLL_PROCESS_ATTACH, тоесть при загрузке модуля, можно подменить InitRoutine. Хэндлер будет подменять адрес возврата в стеке на код, который восстановит ManifestProberRoutine, вызывать оригинальный хэндлер и выполнить возврат. Либо из обработчика InitRoutine вызвать оригинальный хэндлер, что в принице тоже, пример для текущего процесса: Код (Text): PEB_CALLOUT_OFFSET equ (PAGE_SIZE - 10*4) Gl_LdrpManifestProberRoutineReference PVOID ? Gl_LdrpManifestProberRoutine PVOID ? RegisterKernel32Callout proc uses ebx Handler:PVOID assume fs:nothing mov ebx,fs:[TEB.Peb] mov eax,Handler mov edx,PEB.Ldr[ebx] mov edx,PEB_LDR_DATA.InLoadOrderModuleList[edx] assume edx:PLDR_DATA_TABLE_ENTRY mov edx,[edx].InLoadOrderModuleList.Flink ;ntdll.dll mov edx,[edx].InLoadOrderModuleList.Flink ;kernel32.dll lock xchg [edx].EntryPoint,eax mov dword ptr [ebx + PEB_CALLOUT_OFFSET],eax ret RegisterKernel32Callout endp $LOAD macro Reg32, Variable Local dt_ Call dt_ dt_: pop Reg32 mov Reg32,dword ptr [Reg32 + (offset Variable - offset dt_)] endm CalloutRoutine proc DllHandle:PVOID, Reason:ULONG, Context:PCONTEXT assume fs:nothing mov eax,fs:[TEB.Peb] push Context push Reason push DllHandle Call dword ptr [eax + PEB_CALLOUT_OFFSET] .if Reason == DLL_PROCESS_ATTACH $LOAD Ecx, Gl_LdrpManifestProberRoutineReference $LOAD Edx, Gl_LdrpManifestProberRoutine mov dword ptr [ecx],edx .endif ret CalloutRoutine endp Тоесть захват InitRoutine из RegisterKernel32Callout(), адрес оригинального хэндлера сохраняется в пеб.
Забыл: Код (Text): $GET_REF macro Reg32, Variable Local dt_ Call dt_ dt_: pop Reg32 lea Reg32,dword ptr [Reg32 + (offset Variable - offset dt_)] endm $GET_REF Eax, CalloutRoutine invoke RegisterKernel32Callout, Eax
Апну старого мамонта на память. Вот краткое описание работы с активационными контекстами в коде загрузчика Windows (ntdll.dll): При загрузке каждой DLL ntdll вызывает функцию LdrpManifestProberRoutine, передавая ей базовый адрес загружаемой DLL и ее имя. Эта функция ищет манифест зависимостей в ресурсах DLL и создает активационный контекст на его основе. Указатель на созданный активационный контекст сохраняется в поле EntryPointActivationContext структуры LDR_DATA_TABLE_ENTRY, описывающей загруженную DLL. При вызове импортируемых функций из DLL, ntdll активирует ее активационный контекст при помощи RtlActivateActivationContextUnsafeFast. Это позволяет разрешать зависимости во время выполнения. После вызова функций активационный контекст деактивируется через RtlDeactivateActivationContextUnsafeFast. При выгрузке DLL ее активационный контекст освобождается. Таким образом активационные контексты в ntdll используются для разрешения зависимостей импортируемых DLL на этапе выполнения программы. Это позволяет поддерживать изоляцию компонентов и side-by-side сборки в Windows.
--- Сообщение объединено, 7 ноя 2023 --- добавляю для удобства список всех вызовов и список оригинальных подпрограмм
Дополню инфу Функция LdrSetDllManifestProber используется для установки функции-обработчика, которая будет вызываться загрузчиком DLL при загрузке каждого DLL-модуля. Этот обработчик может анализировать ресурсы DLL-модуля и извлекать оттуда различную информацию, например манифест приложения. Подробнее: 1. Функция принимает один параметр - указатель на функцию-обработчик типа PLDR_MANIFEST_PROBER_ROUTINE. 2. Этот указатель сохраняется в глобальной переменной LdrpManifestProberRoutine. 3. При загрузке каждого DLL-модуля загрузчик проверяет, установлен ли этот обработчик. 4. Если обработчик установлен, загрузчик вызывает его, передавая базовый адрес загружаемого модуля и имя модуля. 5. Обработчик анализирует ресурсы модуля, возможно извлекает какие-то данные (например, манифест). 6. Обработчик может устанавливать для модуля контекст активации (activation context) в переменную EntryPointActivationContext. 7. Также обработчик может возвращать статус ошибки, если произошла ошибка при анализе модуля. Таким образом эта функция позволяет подключить к загрузчику DLL дополнительную логику для анализа загружаемых модулей.