It seems that since est. w10 build 15007 you can have more than one loaded 32bit ntdll.dll
Function LdrpLoadDelegatedNtdll query key DelegatedNtdll via LdrQueryImageFileKeyOption then appends this value to \\SystemRoot\\system32\\ and loads it. Sure this required changes in callbacks propagation logic
There is table LdrpDelegatedNtdllExports which just hold pairs of exported symbol and offset to it "delegated" ptr:
Lets see how this "delegated" pfns works
for example function KiUserExceptionDispatcher
Image base of "delegated" ntdll stored in LdrpDelegatedNtdllBase
Function LdrpLoadDelegatedNtdll query key DelegatedNtdll via LdrQueryImageFileKeyOption then appends this value to \\SystemRoot\\system32\\ and loads it. Sure this required changes in callbacks propagation logic
There is table LdrpDelegatedNtdllExports which just hold pairs of exported symbol and offset to it "delegated" ptr:
- LdrInitializeThunk -> LdrDelegatedLdrInitializeThunk
- RtlUserThreadStart -> LdrDelegatedRtlUserThreadStart
- RtlDispatchAPC -> LdrDelegatedRtlDispatchAPC
- KiUserExceptionDispatcher -> LdrDelegatedKiUserExceptionDispatcher
- KiUserApcDispatcher -> LdrDelegatedKiUserApcDispatcher
- KiUserCallbackDispatcher -> LdrDelegatedKiUserCallbackDispatcher
- KiRaiseUserExceptionDispatcher -> LdrDelegatedKiRaiseUserExceptionDispatcher
- LdrSystemDllInitBlock -> LdrDelegatedSystemDllInitBlock
- LdrpChildNtdll -> LdrpChildNtdllPointer
- LdrParentInterlockedPopEntrySList -> LdrpParentInterlockedPopEntrySListPointer
- LdrParentRtlInitializeNtUserPfn -> LdrpParentRtlInitializeNtUserPfnPointer
- LdrParentRtlResetNtUserPfn -> LdrpParentRtlResetNtUserPfnPointer
- LdrParentRtlRetrieveNtUserPfn -> LdrpParentRtlRetrieveNtUserPfnPointer
Lets see how this "delegated" pfns works
for example function KiUserExceptionDispatcher
_KiUserExceptionDispatcher@8 proc near
var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4
arg_0= dword ptr 4
cmp _LdrDelegatedKiUserExceptionDispatcher, 0
jz short loc_6A28DB27
mov ecx, _LdrDelegatedKiUserExceptionDispatcher
call ds:___guard_check_icall_fptr
jmp ecx
loc_6A28DB27:
...
Image base of "delegated" ntdll stored in LdrpDelegatedNtdllBase