This is a simple lightweight utility to find which DLL/EXE/OCX in a folder uses a particular DLL.
My development environment usually includes large number of binaries. And I was working in some core library module which is used by many other DLLs. So whenever I change something in the header file of this code library, I have to rebuild all the dependent libraries. Rebuilding the entire environment was not practical all times. So I had to create such an utility so that I can find out who uses this code library and can rebuild those modules only.
How To Use
- In the “DLL Name” edit box, enter the name of the DLL for which we have to search.
- In the “Path” edit box, enter the folder in which we have to search for DLLs/EXE/OCX that uses DLL entered in the “DLL Name” edit box.
- Click “Start” button.
Upon clicking the Start button, the list control starts’ populating the DLL, OCX and EXEs that use the specified DLL. Please note that this application will not list dynamic dependencies and so we cannot enter OCX or COM DLL as “DLL Name”.
How It Works
When the start button is clicked, the application loop through each DLL, OCX and EXEs in the specified folder. It opens each binary, parses the PE (Portable Executable) file format.
For example, consider that user32.dll is specified in the "DLL name" edit box and the path is "c:\windows\system32". When user clicks on the “start” button, the application takes a loop to find EXEs, DLLs and OCXs in the specified folder. Intitally it enumarates all the EXEs. Suppose we get "calc.exe" as file name. The next thing to do is to map the binary file to memory. This is performed with the help of CreateFileMapping()
and MapViewOfFile()
function.
When we have the binary file in memory, we simply can parse the file. If you are not so faimiliar with the PE (Poratable Executable) file format, I recommond you have a look at the article An In-Depth Look into the Win32 Portable Executable File Format by Matt Pietrek. Our aim here is to find out the import address table.
CFile fl;
if( !fl.Open( csExeName, CFile::modeRead|CFile::shareDenyNone, 0 ))
{
CString csMsg;
csMsg.Format( _T(" Failed to open file %s"), csExeName );
AfxMessageBox( csMsg );
continue;
}
HANDLE hSM = CreateFileMapping
( (HANDLE)fl.m_hFile, 0,PAGE_READONLY,0,0, _T("some_sM"));
LPVOID pBinary = MapViewOfFile( hSM,FILE_MAP_READ,0,0, fl.GetLength());
if( CheckForDependecy( csDllName, pBinary ))
{
m_List.InsertItem( 0, fl.GetFileName() );
}
UnmapViewOfFile( pBinary );
CloseHandle( hSM );
fl.Close();
As you can see in the above code, after the binary is loaded to memory, the memory address is passed to the CheckForDependecy()
function. The function casts the memory address to a IMAGE_DOS_HEADER
pointer.
PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) lpStartAddress;
After this, it will try to get the IMAGE_NT_HEADERS
from pDOSHeader
. Usually the 16 bit binaries will not be having this header. So we will ignore such binaries at this point.
PIMAGE_NT_HEADERS pNTHeader =
MakePtr(PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew);
if( IsBadReadPtr( pNTHeader, sizeof( IMAGE_NT_HEADERS)))
{
return false;
}
Sometimes because of the property of data in the file, it may occur that the pNTHeader
points to a valid memory. So we will add one more checking to confirm that we are dealing with PE file (The famous DRWATSON.EXE fails at this checking!!! Ya, it is not a PE file).
The next task is to find the IMAGE_IMPORT_DESCRIPTOR
. The IMAGE_NT_HEADERS.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
. VirtualAddress
holds the relative address of IMAGE_IMPORT_DESCRIPTOR
from the start of the memory. We can use the ImageRvaToVa()
API to convert the relative adress to virtual address. There's one IMAGE_IMPORT_DESCRIPTOR
for each imported executable. So we will loop through each item and check whether it is for importing the specified DLL (“user32.dll” in our case).
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
(PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa( pNTHeader, lpStartAddress,
pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].
VirtualAddress, 0 );
while( pImportDesc && pImportDesc->Name )
{
PVOID pName = ImageRvaToVa( pNTHeader, lpStartAddress, pImportDesc->Name , 0 );
PSTR szCurrMod = (PSTR)pName;
if( IsBadReadPtr( szCurrMod, 1 ))
{
continue;
}
CString cs = szCurrMod;
if( 0 == cs.CollateNoCase( csDllName ))
{
return true;
}
pImportDesc++;
}
And for the records, let me say that, there are 1111 files in my system32 folder that have dependency with the user32.dll (Vista SP1).