By: drizt
Recently, Some friend complained to me that their rootkit driver had been killed by anti-virus software like McAfee and Nod32.So I began to find why.
I found that these "heuristic anti-virus" based on the export function mentioned in one of Jonna's article(BTW:Give my respect to Jonna).
First I take a look at McAfee, It has a strange heuristic strategy. if it found an export symbol "KeServiceDescriptorTable" ,while it didn`t found some normal driver function like "IoCreateDevice", It report the virus. So I think the first method is to find the KeServiceDescriptorTable dynamically.
With 90210's article "A more stable way to locate real KiServiceTable"(http://www.rootkit.com/newsread.php?newsid=176) and his help, I can find the KeServiceDescriptorTable's ServiceTableBase, it is enough.(Thank you 90210).
But I find NOD32 is more restrice, it will detect ZW* function and reported your driver as virus. So I must find a more common ways to locate export functions and symbols. Fortunately I found some pieces in from SVEN B. SCHREIBER. This book is cool!! The code is here:
PVOID SpyMemoryCreate (DWORD dSize)
{
return ExAllocatePoolWithTag (PagedPool, max (dSize, 1),
SPY_TAG);
}
// -----------------------------------------------------------------
PVOID SpyMemoryDestroy (PVOID pData)
{
if (pData != NULL) ExFreePool (pData);
return NULL;
}
// ==============================================================
// MODULE INFO MANAGEMENT
// =================================================================
PMODULE_LIST SpyModuleList (PDWORD pdData,
PNTSTATUS pns)
{
DWORD dSize;
DWORD dData = 0;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PMODULE_LIST pml = NULL;
for (dSize = PAGE_SIZE; (pml == NULL) && dSize; dSize <<= 1)
{
if ((pml = SpyMemoryCreate (dSize)) == NULL)
{
ns = STATUS_NO_MEMORY;
break;
}
ns = ZwQuerySystemInformation (SystemModuleInformation,
pml, dSize, &dData);
if (ns != STATUS_SUCCESS)
{
pml = SpyMemoryDestroy (pml);
dData = 0;
if (ns != STATUS_INFO_LENGTH_MISMATCH) break;
}
}
if (pdData != NULL) *pdData = dData;
if (pns != NULL) *pns = ns;
return pml;
}
// -----------------------------------------------------------------
PMODULE_LIST SpyModuleFind (PBYTE pbModule,
PDWORD pdIndex,
PNTSTATUS pns)
{
DWORD i;
DWORD dIndex = -1;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PMODULE_LIST pml = NULL;
if ((pml = SpyModuleList (NULL, &ns)) != NULL)
{
for (i = 0; i < pml->dModules; i++)
{
if (!_stricmp (pml->aModules [i].abPath +
pml->aModules [i].wNameOffset,
pbModule))
{
dIndex = i;
break;
}
}
if (dIndex == -1)
{
pml = SpyMemoryDestroy (pml);
ns = STATUS_NO_SUCH_FILE;
}
}
if (pdIndex != NULL) *pdIndex = dIndex;
if (pns != NULL) *pns = ns;
return pml;
}
// -----------------------------------------------------------------
PVOID SpyModuleBase (PBYTE pbModule,
PNTSTATUS pns)
{
PMODULE_LIST pml;
DWORD dIndex;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PVOID pBase = NULL;
if ((pml = SpyModuleFind (pbModule, &dIndex, &ns)) != NULL)
{
pBase = pml->aModules [dIndex].pBase;
SpyMemoryDestroy (pml);
}
if (pns != NULL) *pns = ns;
return pBase;
}
// -----------------------------------------------------------------
PIMAGE_NT_HEADERS SpyModuleHeader (PBYTE pbModule,
PVOID *ppBase,
PNTSTATUS pns)
{
PVOID pBase = NULL;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PIMAGE_NT_HEADERS pinh = NULL;
if (((pBase = SpyModuleBase (pbModule, &ns)) != NULL) &&
((pinh = RtlImageNtHeader (pBase)) == NULL))
{
ns = STATUS_INVALID_IMAGE_FORMAT;
}
if (ppBase != NULL) *ppBase = pBase;
if (pns != NULL) *pns = ns;
return pinh;
}
// -----------------------------------------------------------------
PIMAGE_EXPORT_DIRECTORY SpyModuleExport (PBYTE pbModule,
PVOID *ppBase,
PNTSTATUS pns)
{
PIMAGE_NT_HEADERS pinh;
PIMAGE_DATA_DIRECTORY pidd;
PVOID pBase = NULL;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PIMAGE_EXPORT_DIRECTORY pied = NULL;
if ((pinh = SpyModuleHeader (pbModule, &pBase, &ns)) != NULL)
{
pidd = pinh->OptionalHeader.DataDirectory
+ IMAGE_DIRECTORY_ENTRY_EXPORT;
if (pidd->VirtualAddress &&
(pidd->Size >= IMAGE_EXPORT_DIRECTORY_))
{
pied = PTR_ADD (pBase, pidd->VirtualAddress);
}
else
{
ns = STATUS_DATA_ERROR;
}
}
if (ppBase != NULL) *ppBase = pBase;
if (pns != NULL) *pns = ns;
return pied;
}
// -----------------------------------------------------------------
PVOID SpyModuleSymbol (PBYTE pbModule,
PBYTE pbName,
PVOID *ppBase,
PNTSTATUS pns)
{
PIMAGE_EXPORT_DIRECTORY pied;
PDWORD pdNames, pdFunctions;
WORD *pwOrdinals;
DWORD i, j;
PVOID pBase = NULL;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PVOID pAddress = NULL;
if ((pied = SpyModuleExport (pbModule, &pBase, &ns)) != NULL)
{
pdNames = PTR_ADD (pBase, pied->AddressOfNames);
pdFunctions = PTR_ADD (pBase, pied->AddressOfFunctions);
pwOrdinals = PTR_ADD (pBase, pied->AddressOfNameOrdinals);
for (i = 0; i < pied->NumberOfNames; i++)
{
j = pwOrdinals [i];
if (!strcmp (PTR_ADD (pBase, pdNames [i]), pbName))
{
if (j < pied->NumberOfFunctions)
{
pAddress = PTR_ADD (pBase, pdFunctions [j]);
}
break;
}
}
if (pAddress == NULL)
{
ns = STATUS_PROCEDURE_NOT_FOUND;
}
}
if (ppBase != NULL) *ppBase = pBase;
if (pns != NULL) *pns = ns;
return pAddress;
}
// -----------------------------------------------------------------
PVOID SpyModuleSymbolEx (PBYTE pbSymbol,
PVOID *ppBase,
PNTSTATUS pns)
{
DWORD i;
BYTE abModule [MAXIMUM_FILENAME_LENGTH] = "ntoskrnl.exe";
PBYTE pbName = pbSymbol;
PVOID pBase = NULL;
NTSTATUS ns = STATUS_INVALID_PARAMETER;
PVOID pAddress = NULL;
for (i = 0; pbSymbol [i] && (pbSymbol [i] != '!'); i++);
if (pbSymbol [i++])
{
if (i <= MAXIMUM_FILENAME_LENGTH)
{
memcpy (abModule, pbSymbol, i);
pbName = pbSymbol + i;
}
else
{
pbName = NULL;
}
}
if (pbName != NULL)
{
pAddress = SpyModuleSymbol (abModule, pbName, &pBase, &ns);
}
if (ppBase != NULL) *ppBase = pBase;
if (pns != NULL) *pns = ns;
return pAddress;
}
So Now we can get symbol like this:
pKeServiceDescriptorTable = SpyModuleSymbolEx("KeServiceDescriptorTable", NULL, &ns);
cool!