/*++ Copyright (c) 2002 Microsoft Corporation Module Name: poolfind.cpp Abstract: This module contains the code to find the tags in a driver binary Author: Andrew Edwards (andred) Oct-2001 Revision History: Swetha Narayanaswamy (swethan) 19-Mar-2002 --*/ #if !defined (_WIN64) #pragma warning(disable: 4514) #include "windows.h" #include "msdis.h" #include #include #include #include #define TAGSIZE 5 #define DRIVERDIR "\\drivers\\" #define DRIVERFILEEXT "*.sys" #define MAXPATH 1024 int __cdecl main(int argc, char** argv); void ParseImageFile(PTCHAR szImage); CHAR localTagFile[MAXPATH] = "localtag.txt"; int cSystemDirectory = 0; //Used to form list of tags used by the same driver typedef struct _TAGLIST{ TCHAR Tag[TAGSIZE]; struct _TAGLIST *next; } TAGLIST, *PTAGLIST; BOOL GetTagFromCall ( const BYTE *pbCall, // pointer to call instruction PTCHAR ptchTag, //out param: pointer to tag DIS *pDis, int tagLocation) //location of tag in the parameter list /*++ Routine Description: This function disassembles the memory allocation function call. It finds the third push and determines the tag and places it in the ptchTag return parameter Return Value: FALSE: If unable to find tag TRUE: Otherwise --*/ { // try to backup 3 pushes: // FF /6 + modrm etc // 50-5F // 6A <8imm> // 68 <32imm> int cbMax = 200; const BYTE *pb = pbCall; const BYTE *pTag = NULL; if ((ptchTag == NULL) || (pbCall == NULL) || (pDis == NULL)) return FALSE; _tcscpy(ptchTag,""); while (cbMax--) { pb--; if (pb[0] == 0x68) { // disassemble forward const BYTE *pbInst = pb; size_t cb = 1; int cPush = 0; while (cb && pbInst < pbCall) { cb = pDis->CbDisassemble( (DWORD)pbInst, pbInst, pbCall - pbInst); if ( (pbInst[0] == 0xFF) && ((pbInst[1] & 0x38) == 0x30)) { // this looks like a push cPush++; } else if ((pbInst[0] & 0xF0) == 0x50) { // this looks like a push cPush++; } else if (pbInst[0] == 0x6A) { // this looks like a push cPush++; } else if (pbInst[0] == 0x68) { // this looks like a push cPush++; } pbInst += cb; } if (cPush == tagLocation && pbInst == pbCall) { _sntprintf(ptchTag,5,"%c%c%c%c", pb[1],pb[2],pb[3],( 0x7f &pb[4])); return TRUE; } } } return FALSE; } BOOL FindTags( const BYTE *pb, DWORD addr, PTAGLIST tagList, // out param: List of tags to append the new tags to DIS *pDis, int tagLocation) // tag location /*++ Routine Description: This function reads the bytes in the instruction and looks for indirect/direct calls. The tag is appended to the tagList parameter Return Value: FALSE: If there is an exception or lack of memory TRUE: Otherwise --*/ { TCHAR tag[TAGSIZE]; BOOL done = FALSE; PTAGLIST tempTagNode=NULL, prevTagNode=NULL, newTagNode=NULL; if ((pb==NULL) || (tagList == NULL) || (pDis == NULL)) return FALSE; // Get the raw bytes and size of the image try { for(;;) { done = FALSE; if (pb[0] == 0xFF && pb[1] == 0x15) { // Indirect call pb += 2; if (*(DWORD *)pb == (DWORD)addr) { // Found a call if ((GetTagFromCall(pb-2,tag,pDis, tagLocation)) == FALSE) { pb++; continue; } tempTagNode = prevTagNode = tagList; while (tempTagNode != NULL) { if ((_tcscmp(tempTagNode->Tag,"")) == 0) { // Insert here _tcsncpy(tempTagNode->Tag, tag, TAGSIZE); done = TRUE; break; } else if (!(memcmp(tempTagNode->Tag,tag,TAGSIZE))) { //Tag already exists in list done = TRUE; break; } prevTagNode = tempTagNode; tempTagNode = tempTagNode->next; } if (!done ) { newTagNode = (PTAGLIST)malloc(sizeof(TAGLIST)); if (!newTagNode) { printf("Poolmon: Insufficient memory: %d\n", GetLastError()); return FALSE; } //There is at least one node allocated so prevTagNode //will never be NULL prevTagNode->next = newTagNode; // Insert here _tcsncpy(newTagNode->Tag, tag, TAGSIZE); newTagNode->next = NULL; } } } pb++; } } catch (...) { return FALSE; } return TRUE; } unsigned RvaToPtr(unsigned rva, IMAGE_NT_HEADERS *pNtHeader) { int iSect = 0; for (PIMAGE_SECTION_HEADER pSect = IMAGE_FIRST_SECTION(pNtHeader); iSect < pNtHeader->FileHeader.NumberOfSections; pSect++, iSect++) { if (rva >= pSect->VirtualAddress && rva < pSect->VirtualAddress + pSect->SizeOfRawData) { rva -= pSect->VirtualAddress; rva += pSect->PointerToRawData; return rva; } } //error case return 0; } void ParseImageFile( PTCHAR szImage, PTAGLIST tagList) // Image file name /*++ Routine Description: This function opens the binary driver image file, reads it and looks for a memory allocation call Return Value: None --*/ { DIS *pDis = NULL; if ((tagList == NULL) || (szImage == NULL) ) return; // read szImage into memory FILE *pf = NULL; pf = _tfopen(szImage, "rb"); if (!pf) return; if ((fseek(pf, 0, SEEK_END )) != 0) goto exitParseImageFile; size_t cbMax = 0; cbMax = ftell(pf); if (cbMax == -1) goto exitParseImageFile; if ((fseek(pf, 0, SEEK_SET )) != 0) goto exitParseImageFile; BYTE *pbImage = NULL; pbImage = new BYTE[cbMax]; if (!pbImage) goto exitParseImageFile; if ((fread( pbImage, cbMax, 1, pf )) <= 0) goto exitParseImageFile; // find the import table IMAGE_DOS_HEADER *phdr = (IMAGE_DOS_HEADER *)pbImage; if (phdr->e_magic != IMAGE_DOS_SIGNATURE) { _tprintf("Poolmon: Bad image file %s\n", szImage); goto exitParseImageFile; } if (cbMax < offsetof(IMAGE_DOS_HEADER, e_lfanew) + sizeof(phdr->e_lfanew) ||phdr->e_lfanew == 0) { _tprintf("Poolmon: Bad image file %s\n", szImage); goto exitParseImageFile; } IMAGE_NT_HEADERS *pNtHeader = (IMAGE_NT_HEADERS *) (pbImage + phdr->e_lfanew); if (pNtHeader == NULL) goto exitParseImageFile; if (pNtHeader->Signature != IMAGE_NT_SIGNATURE || pNtHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) { _tprintf("Poolmon: Bad image file %s\n", szImage); goto exitParseImageFile; } // Find import tables IMAGE_DATA_DIRECTORY *pImports = (IMAGE_DATA_DIRECTORY *)&pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if (pImports == NULL) goto exitParseImageFile; unsigned rva = pImports->VirtualAddress; // need to adjust from rva to pointer accounting for section alignment rva = RvaToPtr(rva, pNtHeader); if (0 == rva) { goto exitParseImageFile; } IMAGE_IMPORT_DESCRIPTOR *pImportDescr = (IMAGE_IMPORT_DESCRIPTOR *) (pbImage+rva); if (NULL == pImportDescr) { goto exitParseImageFile; } //msdis130.dll depends on msvcr70.dll and msvcp70.dll //Try a loadlibrary on these 2 libraries, and then proceed to delayload msdis130.dll #pragma prefast(suppress:321, "user guide:Programmers developing on the .NET server can ignore this warning by filtering it out.") if ((!LoadLibrary("MSVCR70.DLL")) || (!LoadLibrary("MSVCP70.DLL"))) { printf("Unable to load msvcr70.dll/msvcp70.dll, cannot create local tag file\n"); if (pf) fclose(pf); if (pbImage) delete[](pbImage); exit(-1); } try { pDis = DIS::PdisNew( DIS::distX86 ); } catch (...) { printf("Poolmon: Unable to load msdis130.dll, cannot create local tag file\n"); if (pf) fclose(pf); if (pbImage) delete[](pbImage); exit(-1); } if (pDis == NULL) { goto exitParseImageFile; } // Find the import for ExAllocatePoolWithTag for (;pImportDescr->Name &&pImportDescr->FirstThunk;pImportDescr++) { if (0 == (rva = RvaToPtr(pImportDescr->FirstThunk, pNtHeader))) { goto exitParseImageFile; } IMAGE_THUNK_DATA32 *pIAT = (IMAGE_THUNK_DATA32 *) (pbImage + rva); if (pIAT == NULL) goto exitParseImageFile; IMAGE_THUNK_DATA32 *addrIAT = (IMAGE_THUNK_DATA32 *) (pNtHeader->OptionalHeader.ImageBase + pImportDescr->FirstThunk); if (addrIAT == NULL) goto exitParseImageFile; if (0 == (rva = RvaToPtr(pImportDescr->Characteristics, pNtHeader))) { goto exitParseImageFile; } IMAGE_THUNK_DATA32 *pINT= (IMAGE_THUNK_DATA32 *) (pbImage + rva); if (pINT == NULL) goto exitParseImageFile; for (;pIAT->u1.Ordinal;) { if (IMAGE_SNAP_BY_ORDINAL32(pINT->u1.Ordinal)) { // by ordinal? } else { if (0 == (rva = RvaToPtr((int)pINT->u1.AddressOfData, pNtHeader))) { goto exitParseImageFile; } IMAGE_IMPORT_BY_NAME* pIIN = (IMAGE_IMPORT_BY_NAME*) (pbImage + rva); if (NULL == pIIN) goto exitParseImageFile; char *name = (char*)pIIN->Name; if (0 == strcmp( name, "ExAllocatePoolWithTag" )) { FindTags(pbImage, (DWORD)addrIAT,tagList, pDis,3); } else if (0 == strcmp( name, "ExAllocatePoolWithQuotaTag" )) { FindTags(pbImage, (DWORD)addrIAT,tagList,pDis,3); } else if (0 == strcmp( name, "ExAllocatePoolWithTagPriority" )) { FindTags(pbImage, (DWORD)addrIAT,tagList,pDis,3); } //wrapper functions else if(0 == strcmp(name, "NdisAllocateMemoryWithTag")) { FindTags(pbImage, (DWORD)addrIAT,tagList,pDis,3); } else if(0 == strcmp(name,"VideoPortAllocatePool")) { FindTags(pbImage, (DWORD)addrIAT,tagList,pDis,4); } } addrIAT++; pIAT++; pINT++; } } exitParseImageFile: if (pf) fclose(pf); if (pbImage) delete[](pbImage); } extern "C" BOOL MakeLocalTagFile() /*++ Routine Description: This function finds files one by one in the system drivers directory and call ParseImageFile on the image Return Value: None --*/ { WIN32_FIND_DATA FileData; HANDLE hSearch=0; BOOL fFinished = FALSE; TCHAR sysdir[1024]; PTCHAR filename=NULL; TCHAR imageName[MAXPATH] = ""; PTAGLIST tagList = NULL; BOOL ret = TRUE; FILE *fpLocalTagFile = NULL; cSystemDirectory = GetSystemDirectory(sysdir, 0); if(!cSystemDirectory) { printf("Poolmon: Unable to get system directory: %d\n", GetLastError()); ret = FALSE; goto freeall; } filename = (PTCHAR)malloc(cSystemDirectory + (_tcslen(DRIVERDIR) + _tcslen(DRIVERFILEEXT) + 1) * sizeof(TCHAR)); if(!filename) { ret = FALSE; goto freeall; } GetSystemDirectory(filename, cSystemDirectory + 1); _tcscat(filename, DRIVERDIR); _tcscat(filename, DRIVERFILEEXT); // Search for .sys files hSearch = FindFirstFile(filename, &FileData); if (hSearch == INVALID_HANDLE_VALUE) { printf("Poolmon: No .sys files found\n"); ret = FALSE; goto freeall; } _tcsncpy(imageName, filename,MAXPATH); imageName[MAXPATH-1] = '\0'; tagList = (PTAGLIST)malloc(sizeof(TAGLIST)); if (!tagList) { ret = FALSE; goto freeall; } _tcscpy(tagList->Tag,""); tagList->next = NULL; int cImagePath = cSystemDirectory+ _tcslen(DRIVERDIR) -1; while (!fFinished) { // Do all the initializations for the next round // Remove the previous name imageName[cImagePath] = '\0'; // Initialize existing tagList PTAGLIST tempTagNode = tagList; while (tempTagNode != NULL) { _tcscpy(tempTagNode->Tag,""); tempTagNode = tempTagNode->next; } try { _tcsncat(imageName, FileData.cFileName,MAXPATH-cImagePath); ParseImageFile(imageName,tagList); //Remove .sys from szImage imageName[_tcslen(imageName) - 4] = '\0'; } catch(...) { _tprintf("Poolmon: Could not read tags from %s\n", imageName); } if (!fpLocalTagFile) { printf("Poolmon: Creating %s in current directory......\n", localTagFile); fpLocalTagFile = fopen(localTagFile, "w"); if (!fpLocalTagFile) { ret = FALSE; goto freeall; } } tempTagNode = tagList; while (tempTagNode != NULL) { if ((_tcscmp(tempTagNode->Tag,""))) { _ftprintf(fpLocalTagFile, "%s - %s\n", tempTagNode->Tag,imageName + cSystemDirectory + _tcslen(DRIVERDIR) -1); tempTagNode = tempTagNode->next; } else break; } if (!FindNextFile(hSearch, &FileData)) { if (GetLastError() == ERROR_NO_MORE_FILES) { fFinished = TRUE; } else { printf("Poolmon: Cannot find next .sys file\n"); } } } freeall: // Close the search handle. if (hSearch) { if (!FindClose(hSearch)) { printf("Poolmon: Unable to close search handle: %d\n", GetLastError()); } } if (filename) free(filename); if (fpLocalTagFile) fclose(fpLocalTagFile); //Free tagList memory PTAGLIST tempTagNode = tagList,prevTagNode = tagList; while (tempTagNode != NULL) { tempTagNode = tempTagNode->next; free(prevTagNode); prevTagNode = tempTagNode; } return ret; } FARPROC WINAPI PoolmonDLoadErrorHandler ( UINT unReason, PDelayLoadInfo pDelayInfo ) { printf("Poolmon: Unable to load required dlls, cannot create local tag file\n"); exit(-1); } PfnDliHook __pfnDliFailureHook2 = PoolmonDLoadErrorHandler; #endif