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!
|
|