Archive

Posts Tagged ‘ntdll’

LdrpLoadedDllHandleCache

May 6th, 2009

LdrpLoadedDllHandleCache is the name of the cached module handle that NTDLL holds which was covered briefly in my previous post.

Just as a confirmation that it does infact exist and is still present, here is the entire body of LdrpUnloadDll from Vista x86 SP1 Untouched. (Generated by IDA v5.4 and HexRays)

int __stdcall LdrpUnloadDll(int a1, int a2)
{
int v2; // ebx@1
int v4; // eax@7
int v5; // esi@7
int v6; // eax@9
int v7; // ebx@10
int v8; // esi@10
int v9; // eax@13
int v10; // eax@16
int v11; // edi@16
int v12; // esi@16
int v13; // edi@31
int v14; // esi@34
int v15; // eax@36
int v16; // eax@1
int v17; // eax@7
int v18; // esi@7
int v19; // eax@10
int v20; // eax@10
int v21; // ecx@10
int v22; // edx@10
int v23; // ecx@10
int v24; // et0@11
char v25; // zf@11
int v26; // eax@14
int v27; // esi@14
int v28; // et0@20
char v29; // zf@20
int (__stdcall *v30)(_DWORD); // eax@26
int v31; // eax@33
int v32; // eax@36
int v33; // ecx@36
int v34; // eax@36
int v35; // ecx@36
int v36; // ecx@36
int v37; // edx@36
int v38; // ecx@37
int v39; // eax@44
int v40; // esi@44
int v41; // eax@50
int v42; // [sp+70h] [bp-28h]@1
int v43; // [sp+68h] [bp-30h]@1
int v44; // [sp+6Ch] [bp-2Ch]@1
int v45; // [sp+94h] [bp-4h]@1
int v46; // [sp+7Ch] [bp-1Ch]@2
int v47; // [sp+74h] [bp-24h]@7
int *v48; // [sp+78h] [bp-20h]@7
int *v49; // [sp+5Ch] [bp-3Ch]@10
int *v50; // [sp+58h] [bp-40h]@10
int v51; // [sp+64h] [bp-34h]@10
int v52; // [sp+10h] [bp-88h]@13
int v53; // [sp+14h] [bp-84h]@13
char v54; // [sp+18h] [bp-80h]@13
char v55; // [sp+60h] [bp-38h]@19
int v56; // [sp+34h] [bp-64h]@28
int v57; // [sp+38h] [bp-60h]@28
char v58; // [sp+3Ch] [bp-5Ch]@28

v42 = *(_DWORD *)a2;
v16 = *(_DWORD *)(*MK_FP(__FS__, 24) + 48);
v43 = *(_DWORD *)(*MK_FP(__FS__, 24) + 48);
v2 = 0;
v44 = 0;
v45 = 0;
++LdrpActiveUnloadCount;
if ( !*(_BYTE *)(*(_DWORD *)(v16 + 12) + 40) )
{
if ( (unsigned __int8)LdrpCheckForLoadedDllHandle(a1, &v46) )
{
if ( *(_WORD *)(v46 + 56) != -1 )
{
-*(_WORD *)(v46 + 56);
LdrpUpdateServiceTagCount(v46, 2);
LdrpCheckForwardedEntries(v46);
if ( *(_BYTE *)(v46 + 52) & 4 )
{
v56 = 36;
v57 = 1;
memset(&v58, 0, 0×1Cu);
RtlActivateActivationContextUnsafeFast();
v45 = 1;
LdrpUpdateLoadCount2(v46, 2);
v45 = 0;
sub_77F0982B();
}
if ( LdrpActiveUnloadCount == 1 )
{
dword_77F991A4 = (int)&LdrpUnloadHead;
LdrpUnloadHead = (int)&LdrpUnloadHead;
}
v13 = dword_77F94CE0;
v48 = (int *)dword_77F94CE0;
while ( v13 != (_DWORD)&dword_77F94CDC )
{
v31 = v13 - 16;
v46 = v13 - 16;
v13 = *(_DWORD *)(v13 + 4);
v48 = (int *)v13;
*(_DWORD *)(v31 + 52) &= 0xFFFFDFFFu;
if ( !*(_WORD *)(v46 + 56) )
{
LdrpModuleCacheUnloadNotification(v46);
v14 = v46;
if ( !*(_WORD *)(v46 + 56) )
{
v47 = v46;
if ( g_ShimsEnabled )
{
v30 = (int (__stdcall *)(_DWORD))RtlDecodeSystemPointer(g_pfnSE_DllUnloaded);
v30(v14);
}
v32 = *(_DWORD *)(v14 + 16);
v33 = *(_DWORD *)(v14 + 20);
*(_DWORD *)v33 = v32;
*(_DWORD *)(v32 + 4) = v33;
v34 = *(_DWORD *)(v14 + 8);
v35 = *(_DWORD *)(v14 + 12);
*(_DWORD *)v35 = v34;
*(_DWORD *)(v34 + 4) = v35;
v15 = v14 + 60;
v36 = *(_DWORD *)(v14 + 60);
v37 = *(_DWORD *)(v14 + 64);
*(_DWORD *)v37 = v36;
*(_DWORD *)(v36 + 4) = v37;
if ( (unsigned __int8)LdrpActiveUnloadCount > 1u )
{
LdrpLoadedDllHandleCache = 0;
*(_DWORD *)(v14 + = 0;
}
v38 = dword_77F991A4;
*(_DWORD *)v15 = &LdrpUnloadHead;
*(_DWORD *)(v14 + 64) = v38;
*(_DWORD *)v38 = v15;
dword_77F991A4 = v14 + 60;
}
}
}
if ( (unsigned __int8)LdrpActiveUnloadCount <= 1u )
{
v49 = (int *)&v50;
v50 = (int *)&v50;
v5 = 0;
v47 = 0;
v4 = LdrpUnloadHead;
v48 = (int *)LdrpUnloadHead;
while ( v4 != (_DWORD)&LdrpUnloadHead )
{
if ( v5 == v2 )
{
v6 = (int)v48;
}
else
{
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable, *(_DWORD *)(v5 + 24));
v39 = *(_DWORD *)v5;
v40 = *(_DWORD *)(v5 + 4);
*(_DWORD *)v40 = v39;
*(_DWORD *)(v39 + 4) = v40;
v47 = v2;
v6 = LdrpUnloadHead;
v48 = (int *)LdrpUnloadHead;
if ( LdrpUnloadHead == (_DWORD)&LdrpUnloadHead )
break;
}
v19 = v6 - 60;
v46 = v19;
LdrpRecordUnloadEvent(v19);
v8 = v46;
v47 = v46;
LdrpLoadedDllHandleCache = v2;
*(_DWORD *)(v46 + = v2;
v20 = v46 + 60;
v21 = *(_DWORD *)(v46 + 60);
v22 = *(_DWORD *)(v46 + 64);
*(_DWORD *)v22 = v21;
*(_DWORD *)(v21 + 4) = v22;
v23 = (int)v49;
*(_DWORD *)v20 = &v50;
*(_DWORD *)(v20 + 4) = v23;
*(_DWORD *)v23 = v20;
v49 = (int *)v20;
v7 = *(_DWORD *)(v46 + 28);
v51 = *(_DWORD *)(v46 + 28);
if ( !v7 || (v24 = *(_DWORD *)(v46 + 52), v24 &= 0×80000u, v25 = v24 == 0, v25) )
{
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable, *(_DWORD *)(v8 + 24));
v17 = *(_DWORD *)v8;
v18 = *(_DWORD *)(v8 + 4);
*(_DWORD *)v18 = v17;
*(_DWORD *)(v17 + 4) = v18;
v5 = 0;
v47 = 0;
v4 = LdrpUnloadHead;
v48 = (int *)LdrpUnloadHead;
}
else
{
v45 = 2;
JUMPOUT((dword_77F143C8 | 1) & ShowSnaps, *(unsigned int *)sub_77F41A51);
if ( ShowSnaps & dword_77F143CC )
DbgBreakPoint();
v52 = 36;
v53 = 1;
memset(&v54, 0, 0×1Cu);
RtlActivateActivationContextUnsafeFast();
v45 = 3;
v9 = v46;
if ( *(_WORD *)(v46 + 58) )
{
LdrpCallTlsInitializers(0, v46);
v9 = v46;
}
LdrpCallInitRoutine(v7, *(_DWORD *)(v9 + 24), 0, 0);
v45 = 2;
sub_77F0904D();
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable, *(_DWORD *)(v8 + 24));
v26 = *(_DWORD *)v8;
v27 = *(_DWORD *)(v8 + 4);
*(_DWORD *)v27 = v26;
*(_DWORD *)(v26 + 4) = v27;
v5 = 0;
v47 = 0;
v4 = LdrpUnloadHead;
v48 = (int *)LdrpUnloadHead;
v45 = 0;
}
v2 = 0;
}
v11 = (int)v50;
v48 = v50;
while ( v11 != (_DWORD)&v50 )
{
v10 = v11 - 60;
v46 = v11 - 60;
v11 = *(_DWORD *)v11;
v48 = (int *)v11;
v12 = v10;
v47 = v10;
if ( *(_WORD *)(v43 + 104) & 0×100 )
{
AVrfDllUnloadNotification(v10);
LOBYTE(v10) = v46;
}
if ( (dword_77F143C8 | 1) & ShowSnaps )
LdrpLogDbgPrint(
“d:\\rtm\\base\\ntdll\\ldrapi.c”,
1468,
“LdrpUnloadDll”,
2,
“Unmapping DLL \”%wZ\”\n”,
v10 + 36);
if ( ShowSnaps & dword_77F143CC )
DbgBreakPoint();
LdrUnloadAlternateResourceModule(*(_DWORD *)(v12 + 24));
if ( RtlImageDirectoryEntryToData(*(_DWORD *)(v12 + 24), 1, 14, &v55) != v2 )
LdrpCorUnloadImage(*(_DWORD *)(v12 + 24));
v28 = *(_DWORD *)(v12 + 52);
v28 &= 0×800000u;
v29 = v28 == 0;
if ( v29 )
v44 = NtUnmapViewOfSection(-1, *(_DWORD *)(v12 + 24));
LdrpSendDllNotifications(v12, 2, *(_BYTE *)(*(_DWORD *)(v43 + 12) + 40) != 0);
while ( *(_DWORD *)(v12 + 76) != v2 )
{
v41 = *(_DWORD *)(v12 + 76);
*(_DWORD *)(v12 + 76) = *(_DWORD *)(v41 + 12);
*(_DWORD *)(v41 + 12) = v42;
v42 = v41;
}
LdrpCheckForwardedEntries(v12);
LdrpFinalizeAndDeallocateDataTableEntry(v12);
if ( v12 == LdrpGetModuleHandleCache )
LdrpGetModuleHandleCache = v2;
}
LdrpModuleReferenceFreeCallback();
}
}
}
else
{
v44 = -1073741515;
}
}
v45 = -2;
sub_77F0E99C();
*(_DWORD *)a2 = v42;
return v44;
}

You can see referenced there LdrpLoadedDllHandleCache. Just wanted to point that out for anyone who had their doubts about its existence.

Erasing the Cached Module Pointer in NTDLL

May 5th, 2009

Darawk first documented this insecurity in his CloakDLL project but never fixed it, Shynd then fixed it in his HideModule class.

Here it is again, but upgraded for x64 and to work with my Cloaker class:

// TODO: Scan more than just the image. Scan the heap.
void Cloaker::Module::EraseCachedPointer()
{
// Exception handling
try
{
// SEH proxy
SehGuard Guard;

// Get handle to NTDLL
HMODULE Handle = GetModuleHandle(_T(”ntdll.dll”));

// Check handle is valid
if (!Handle)
throw std::runtime_error(”Cloaker::Module::EraseCachedPointer: Handle to ntdll.dll is invalid.”);

// Check DOS header is valid
PIMAGE_DOS_HEADER pDosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(Handle);
if (!pDosHeader || pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
throw std::runtime_error(”Cloaker::Module::EraseCachedPointer: DOS PE header is invalid.”);

// Check NT header is valid
PIMAGE_NT_HEADERS pNtHeader = reinterpret_cast<PIMAGE_NT_HEADERS>
((PCHAR)Handle + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
throw std::runtime_error(”Cloaker::Module::EraseCachedPointer: NT PE header is invalid.”);

// Get beginning and end of search region (entire region)
// TODO: Scan heap too
DWORD_PTR* Begin = reinterpret_cast<DWORD_PTR*>(pNtHeader->OptionalHeader.ImageBase);
DWORD_PTR* End = reinterpret_cast<DWORD_PTR*>(pNtHeader->OptionalHeader.ImageBase + pNtHeader->OptionalHeader.SizeOfImage);

// Search for cached pointers
DWORD_PTR* Searcher = NULL;
for (Searcher = Begin; Searcher < End; Searcher++)
{
// Check for cached pointer
if (*Searcher != reinterpret_cast<DWORD_PTR>(m_Handle))
continue;

// Get information for page of memory
MEMORY_BASIC_INFORMATION mbi = { 0 };
if (!VirtualQuery(Searcher, &mbi, sizeof(mbi)))
continue;

// Check page flags to verify we can write to it
if ((mbi.Protect & PAGE_READWRITE) == PAGE_READWRITE ||
(mbi.Protect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE)
*Searcher = 0;
}
}
// Catch access violations
catch (const SehException& e)
{
e;
TDBGOUT(_T(”SEH Error in Cloaker::EraseCachedPointer: “) << e << std::endl);
}
}

Credits to Darawk for documenting it, and Shynd for his HideModule class which I ripped the base of this function from.