Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

698 lines
22 KiB

/* dlcheck - verify that a DLL using delay-load calls APIs that have
* stubs in kernel32.dll (aka dload.lib)
*
* HISTORY:
* 25-Nov-98 barrybo Wrote it.
*/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <imagehlp.h>
#include <delayimp.h>
#include <dloaddef.h>
#include <shlwapi.h>
#include <strsafe.h>
#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
// Function Forward Parameters...
void Usage( void );
int __cdecl main( int, char ** );
int DloadBreakOnFail = FALSE;
extern int DloadDbgPrint = FALSE;
// implemented in kernel32p.lib
FARPROC
WINAPI
DelayLoadFailureHook (
LPCSTR pszDllName,
LPCSTR pszProcName
);
typedef FARPROC (WINAPI *PfnKernel32HookProc)(
LPCSTR pszDllName,
LPCSTR pszProcName
);
PfnKernel32HookProc __pfnFailureProc = DelayLoadFailureHook;
const char rgstrUsage[] = {
"Verify that delayloaded imports all have failure handlers in kernel32.\n"
"usage: dlcheck [switches] image-name\n"
"where: [-?] display this message\n"
" [-l] use the live version of kernel32.dll on the machine\n"
" [-s] use the static dload.lib linked into dlcheck\n"
" [-t] test the static dload.lib linked into dlcheck and exit\n"
" [-i <inifile>] use the information in inifile to check a binary\n"
" [-f] force check the binary (assumes -s)\n"
"\n"
};
HANDLE BaseDllHandle;
PLOADED_IMAGE g_pli;
PIMAGE_SECTION_HEADER g_DelaySection;
//
// Convert an absolute pointer that points into the image if the image
// was loaded as a DLL at its preferred base, into a pointer into the
// DLL as it was mapped by imagehlp.
//
void *
ConvertImagePointer(void * p)
{
if (!p) {
return NULL;
} else {
return (void *)((ULONG_PTR)(p) -
(ULONG_PTR)g_pli->FileHeader->OptionalHeader.ImageBase +
(ULONG_PTR)g_pli->MappedAddress -
(ULONG_PTR)g_DelaySection->VirtualAddress +
(ULONG_PTR)g_DelaySection->PointerToRawData);
}
}
void *
RvaToPtr(DWORD_PTR rva)
{
DWORD i;
PIMAGE_SECTION_HEADER pSect;
if (!rva)
return NULL;
for (i = 0; i < g_pli->NumberOfSections; i++) {
pSect = g_pli->Sections+i;
if (rva >= g_pli->Sections[i].VirtualAddress &&
rva <= (g_pli->Sections[i].VirtualAddress + g_pli->Sections[i].Misc.VirtualSize))
{
return (PVOID)
(g_pli->MappedAddress +
g_pli->Sections[i].PointerToRawData +
(rva - g_pli->Sections[i].VirtualAddress));
}
}
return NULL;
}
void Usage( void )
{
puts(rgstrUsage);
exit (1);
}
BOOLEAN ImageLinksToKernel32Handler( void )
{
PIMAGE_IMPORT_DESCRIPTOR Imports;
ULONG ImportSize;
PULONG_PTR pIAT;
PIMAGE_IMPORT_BY_NAME pImport;
Imports = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(g_pli->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportSize
);
if (!Imports) {
// Image has delayload imports, but no true imports.
return FALSE;
}
while (Imports->Name) {
char *szName;
szName = ImageRvaToVa(g_pli->FileHeader, (PVOID)g_pli->MappedAddress, Imports->Name, NULL);
if (szName && _stricmp(szName, "KERNEL32.DLL") == 0) {
pIAT = ImageRvaToVa(g_pli->FileHeader,
(PVOID)g_pli->MappedAddress,
Imports->OriginalFirstThunk,
NULL);
while (pIAT && *pIAT) {
pImport = ImageRvaToVa(g_pli->FileHeader,
(PVOID)g_pli->MappedAddress,
(ULONG) *pIAT,
NULL);
if (pImport && _stricmp(pImport->Name, "DelayLoadFailureHook") == 0) {
return TRUE;
}
pIAT++;
}
}
Imports++;
}
return FALSE;
}
//
// Validate that the statically-linked delayload stub table is not
// blatantly broken. The most common error is not listing the functions
// in the correct order so the binary search fails.
//
int ValidateStaticDelayloadStubs()
{
extern const DLOAD_DLL_MAP g_DllMap;
UINT i, j;
int Errors = 0;
//
// Ensure that the DLL map is in alphabetical order.
//
for (i = 1; i < g_DllMap.NumberOfEntries; i++)
{
if (strcmp(g_DllMap.pDllEntry[i-1].pszDll,
g_DllMap.pDllEntry[i].pszDll) >= 0)
{
fprintf(stderr, "DLCHECK : error DL000001 : Static delayload table is corrupted\n"
" %s and %s not in alphabetical order\n",
g_DllMap.pDllEntry[i-1].pszDll,
g_DllMap.pDllEntry[i].pszDll);
Errors = 1;
}
}
// For each DLL...
for (i = 0; i < g_DllMap.NumberOfEntries; i++)
{
const DLOAD_DLL_ENTRY *pEntry = &g_DllMap.pDllEntry[i];
//
// Name must be lowercase.
//
char szLower[MAX_PATH];
StringCchCopy(szLower, ARRAYSIZE(szLower), pEntry->pszDll);
_strlwr(szLower);
if (strcmp(szLower, pEntry->pszDll) != 0)
{
fprintf(stderr, "DLCHECK : error DL000002 : Static delayload table is corrupted\n"
" %s must be all-lowercase\n",
pEntry->pszDll);
Errors = 1;
}
//
// Ensure that the exports are in alphabetical order
//
{
const DLOAD_PROCNAME_MAP *pProcNameMap = pEntry->pProcNameMap;
if (pProcNameMap)
{
const DLOAD_PROCNAME_ENTRY *pProcNameEntry = pProcNameMap->pProcNameEntry;
for (j = 1; j < pProcNameMap->NumberOfEntries; j++)
{
if (strcmp(pProcNameEntry[j-1].pszProcName,
pProcNameEntry[j].pszProcName) >= 0)
{
fprintf(stderr, "DLCHECK : error DL000003 : Static delayload table is corrupted\n"
" %s.%s and %s.%s not in alphabetical order\n",
g_DllMap.pDllEntry[i].pszDll,
pProcNameEntry[j-1].pszProcName,
g_DllMap.pDllEntry[i].pszDll,
pProcNameEntry[j].pszProcName);
Errors = 1;
}
}
}
}
//
// Ensure that the ordinals are in alphabetical order
//
{
const DLOAD_ORDINAL_MAP *pOrdinalMap = pEntry->pOrdinalMap;
if (pOrdinalMap)
{
const DLOAD_ORDINAL_ENTRY *pOrdinalEntry = pOrdinalMap->pOrdinalEntry;
for (j = 1; j < pOrdinalMap->NumberOfEntries; j++)
{
if (pOrdinalEntry[j-1].dwOrdinal >= pOrdinalEntry[j].dwOrdinal)
{
fprintf(stderr, "DLCHECK : error DL000001 : Static delayload table is corrupted\n"
" %s.%d and %s.%d not in numeric order\n",
g_DllMap.pDllEntry[i].pszDll,
pOrdinalEntry[j-1].dwOrdinal,
g_DllMap.pDllEntry[i].pszDll,
pOrdinalEntry[j-1].dwOrdinal);
Errors = 1;
}
}
}
}
}
return Errors;
}
int CheckImage(char *szImageName, BOOL fForceCheckImage)
{
PImgDelayDescr Imports;
ULONG ImportSize;
char *szName;
PIMAGE_THUNK_DATA pINT;
DelayLoadInfo dlinfo;
FARPROC fp;
int ReturnValue;
BOOL fCallHandler;
BOOL fPE32;
g_pli = ImageLoad(szImageName, NULL);
if (!g_pli) {
fprintf(stderr, "DLCHECK : fatal error %d: loading '%s'\n", GetLastError(), szImageName);
return 1;
}
Imports = (PImgDelayDescr)
ImageDirectoryEntryToDataEx(g_pli->MappedAddress,
FALSE,
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT,
&ImportSize,
&g_DelaySection
);
if (!Imports) {
fprintf(stdout, "DLCHECK : warning DL000000: image '%s' has no delayload imports\n", szImageName);
return 0;
}
fPE32 = g_pli->FileHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC ? TRUE : FALSE;
if (fForceCheckImage)
{
fCallHandler = TRUE;
}
else
{
fCallHandler = ImageLinksToKernel32Handler();
}
if (!fCallHandler) {
fprintf(stderr, "DLCHECK : fatal errror : image '%s' doesn't import kernel32!DelayLoadFailureHook.\n"
"(use -f option to override)\n"
"\n", szImageName);
return 1;
}
//
// Walk each delayloaded DLL
//
ReturnValue = 0; // assume success
if (Imports->grAttrs & dlattrRva) {
PImgDelayDescrV2 pImportsV2 = (PImgDelayDescrV2)Imports;
szName = (char *)RvaToPtr(pImportsV2->rvaDLLName);
pINT = (PIMAGE_THUNK_DATA)RvaToPtr(pImportsV2->rvaINT);
} else {
PImgDelayDescrV1 pImportsV1 = (PImgDelayDescrV1)Imports;
szName = (char *)ConvertImagePointer((void *)pImportsV1->szName);
pINT = (PIMAGE_THUNK_DATA)ConvertImagePointer((void *)pImportsV1->pINT);
}
while (szName) {
// printf("DelayLoad DLL %s\n", szName);
char szModuleName[MAX_PATH];
char szImportName[MAX_PATH];
{
char* p;
// change "module.dll" to just "module"
StringCchCopy(szModuleName, ARRAYSIZE(szModuleName), szName);
p = szModuleName;
while (*p != '\0')
{
if (*p == '.')
{
*p = '\0';
break;
}
p++;
}
}
//
// Walk each function called from the delayloaded DLL
//
while (pINT->u1.AddressOfData) {
dlinfo.cb = sizeof(dlinfo);
dlinfo.pidd = NULL;
dlinfo.ppfn = NULL;
dlinfo.szDll = szName;
dlinfo.pfnCur = NULL;
dlinfo.dwLastError = ERROR_NOT_ENOUGH_MEMORY;
dlinfo.dlp.szProcName = NULL; // Make sure the upper 32 bits are zeroed out on win64.
if (
( fPE32 && IMAGE_SNAP_BY_ORDINAL32(((PIMAGE_THUNK_DATA32)pINT)->u1.AddressOfData)) ||
(!fPE32 && IMAGE_SNAP_BY_ORDINAL64(((PIMAGE_THUNK_DATA64)pINT)->u1.AddressOfData))
)
{
StringCchPrintf(szImportName, ARRAYSIZE(szImportName), TEXT("Ordinal%d"), IMAGE_ORDINAL(pINT->u1.AddressOfData));
dlinfo.dlp.fImportByName = FALSE;
dlinfo.dlp.dwOrdinal = IMAGE_ORDINAL((ULONG)pINT->u1.AddressOfData);
} else {
PIMAGE_IMPORT_BY_NAME pImport;
if (Imports->grAttrs & dlattrRva) {
pImport = (PIMAGE_IMPORT_BY_NAME)RvaToPtr(pINT->u1.AddressOfData);
} else {
pImport = (PIMAGE_IMPORT_BY_NAME)ConvertImagePointer((void *)pINT->u1.AddressOfData);
}
StringCchCopy(szImportName, ARRAYSIZE(szImportName), pImport->Name);
dlinfo.dlp.fImportByName = TRUE;
dlinfo.dlp.szProcName = pImport->Name;
}
if (fCallHandler) {
//
// Call the delayload handler and see what it does.
//
try {
fp = (*__pfnFailureProc)(dlinfo.szDll, dlinfo.dlp.szProcName);
if (!fp) {
fprintf(stderr, "DLCHECK : error DL000000: %s imports %s!%s which is not handled.\n", szImageName, szModuleName, szImportName);
ReturnValue = 1;
} else {
// printing success takes too much time
// printf("DLCHECK : %s imports %s!%s - OK.\n", szImageName, szModuleName, szImportName);
}
} except (EXCEPTION_EXECUTE_HANDLER) {
fprintf(stderr, "DLCHECK : error %x: %s imports %s!%s - handler threw an exception.\n", GetExceptionCode(), szImageName, szModuleName, szImportName);
ReturnValue = 1;
}
}
else
{
printf("DLCHECK : %s imports %s!%s - not checked.\n", szImageName, szModuleName, szImportName);
}
if (fPE32) {
pINT = (PIMAGE_THUNK_DATA)(((PIMAGE_THUNK_DATA32)pINT)++);
} else {
pINT = (PIMAGE_THUNK_DATA)(((PIMAGE_THUNK_DATA64)pINT)++);
}
}
if (Imports->grAttrs & dlattrRva) {
PImgDelayDescrV2 pImportsV2 = (PImgDelayDescrV2)Imports;
pImportsV2++;
Imports = (PImgDelayDescr)pImportsV2;
szName = (char *)RvaToPtr(pImportsV2->rvaDLLName);
pINT = (PIMAGE_THUNK_DATA)RvaToPtr(pImportsV2->rvaINT);
} else {
PImgDelayDescrV1 pImportsV1 = (PImgDelayDescrV1)Imports;
pImportsV1++;
Imports = (PImgDelayDescr)pImportsV1;
szName = (char *)ConvertImagePointer((void *)pImportsV1->szName);
pINT = (PIMAGE_THUNK_DATA)ConvertImagePointer((void *)pImportsV1->pINT);
}
}
if (ReturnValue == 0)
{
printf("DLCHECK : succeeded on %s\n", szImageName);
}
else
{
fprintf(stderr, "DLCHECK : failed on %s\n", szImageName);
}
return ReturnValue;
}
int CheckIniFile(char *pszFile, BOOL fForceCheckImage)
{
char szIniFile[MAX_PATH];
char szTemp[MAX_PATH];
char szTemp2[MAX_PATH];
char szImageName[MAX_PATH];
char szDelayLoadHandler[MAX_PATH];
int ReturnValue;
LPTSTR psz;
if ((GetFullPathName(pszFile, ARRAYSIZE(szIniFile), szIniFile, &psz) == 0) ||
(GetPrivateProfileString("Default",
"DelayLoadHandler",
"",
szDelayLoadHandler,
ARRAYSIZE(szDelayLoadHandler),
szIniFile) == 0))
{
fprintf(stderr, "DLCHECK : fatal error : failed to load %s\n", szIniFile);
return 1;
}
// foomodule.dll.ini -> foomodule.dll
StringCchCopy(szImageName, ARRAYSIZE(szImageName), psz);
_strlwr(szImageName);
psz = strstr(szImageName, ".ini");
if (psz)
{
*psz = '\0';
}
if (_stricmp(szDelayLoadHandler, "FORCE") == 0)
{
// if the delayload handler is set to FORCE, we check the binary as if it were
// using kernel32
fForceCheckImage = TRUE;
}
if ((_stricmp(szDelayLoadHandler, "kernel32") != 0) &&
(_stricmp(szDelayLoadHandler, "FORCE") != 0))
{
// currently only able to check dll's who use kernel32.dll for their delayload handler
fprintf(stdout, "DLCHECK : warning DL000000 : Unable to check delayload failure behavior\n"
" %s uses %s as a handler, not kernel32\n", szImageName, szDelayLoadHandler);
return 0;
}
// foomodule.dll -> d:\binaries.x86chk\foomodule.dll
if (ExpandEnvironmentStrings("%_NTPostBld%", szTemp, ARRAYSIZE(szTemp)) == 0)
{
fprintf(stderr, "DLCHECK : fatal error : _NTPostBld environment variable not set\n");
return 1;
}
if (GetPrivateProfileString("Default",
"DestinationDir",
"",
szTemp2,
ARRAYSIZE(szTemp2),
szIniFile) == 0)
{
fprintf(stderr, "DLCHECK : fatal error : failed to read 'DestinationDir' from %s\n", szIniFile);
return 1;
}
StringCchCat(szTemp, ARRAYSIZE(szTemp), TEXT("\\"));
StringCchCat(szTemp, ARRAYSIZE(szTemp), szTemp2);
StringCchCat(szTemp, ARRAYSIZE(szTemp), szImageName);
GetFullPathName(szTemp, ARRAYSIZE(szImageName), szImageName, NULL);
// Heck, lets always validate the static delay load stubs, its fast
ReturnValue = ValidateStaticDelayloadStubs();
if (szImageName[0] != '\0')
{
ReturnValue += CheckImage(szImageName, fForceCheckImage);
}
return ReturnValue;
}
BOOL PathIsDotOrDotDot(LPCSTR pszPath)
{
return ((pszPath[0] == '.') &&
((pszPath[1] == '\0') || ((pszPath[1] == '.') && (pszPath[2] == '\0'))));
}
BOOL PathIsWild(LPCSTR pszPath)
{
while (*pszPath)
{
if (*pszPath == TEXT('?') || *pszPath == TEXT('*'))
return TRUE;
pszPath = CharNext(pszPath);
}
return FALSE;
}
int CheckImageOrIniFileRecursive(char *szName, BOOL fForceCheckImage, BOOL fIniFile, int *piFiles)
{
HANDLE hfind;
WIN32_FIND_DATA fd;
char szPathName[MAX_PATH];
char *pszFileSpec;
int ReturnValue = 0;
pszFileSpec = PathFindFileName(szName);
// First find all files that match the file spec, ignoring directories
hfind = FindFirstFile(szName, &fd);
if (hfind != INVALID_HANDLE_VALUE)
{
do {
if (!PathIsDotOrDotDot(fd.cFileName))
{
StrCpyN(szPathName, szName, sizeof(szPathName));
PathRemoveFileSpec(szPathName);
PathAppend(szPathName, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// Ignore directories
}
else
{
(*piFiles)++;
if (fIniFile)
{
ReturnValue += CheckIniFile(szPathName, fForceCheckImage);
}
else
{
ReturnValue += CheckImage(szPathName, fForceCheckImage);
}
}
}
} while (FindNextFile(hfind, &fd));
FindClose(hfind);
}
if (PathIsWild(szName))
{
char szPathSearch[MAX_PATH];
// Now do all directories
StrCpyN(szPathSearch,szName,sizeof(szPathSearch));
PathRemoveFileSpec(szPathSearch);
PathAppend(szPathSearch,"*.*");
hfind = FindFirstFile(szPathSearch, &fd);
if (hfind != INVALID_HANDLE_VALUE)
{
do {
if (!PathIsDotOrDotDot(fd.cFileName))
{
StrCpyN(szPathName, szPathSearch, sizeof(szPathName));
PathRemoveFileSpec(szPathName);
PathAppend(szPathName, fd.cFileName);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
PathAppend(szPathName,pszFileSpec);
ReturnValue += CheckImageOrIniFileRecursive(szPathName, fForceCheckImage, fIniFile, piFiles);
}
else
{
// Only process directories
}
}
} while (FindNextFile(hfind, &fd));
FindClose(hfind);
}
}
return ReturnValue;
}
int
__cdecl
main (
int c,
char *v[]
)
{
int ReturnValue;
BOOL fIniFile = FALSE;
char szImageName[MAX_PATH];
BOOL fForceCheckImage = FALSE;
if (c < 2) {
Usage();
}
if (*v[1] == '-' || *v[1] == '/') {
switch ( *(v[1]+1) ) {
case 's':
case 'S':
if (c != 3) {
Usage();
}
StringCchCopy(szImageName, ARRAYSIZE(szImageName), v[2]);
break; // nothing needs to be done.
case 'l':
case 'L':
__pfnFailureProc = (PfnKernel32HookProc)GetProcAddress(GetModuleHandleA("kernel32.dll"), "DelayLoadFailureHook");
if (!__pfnFailureProc) {
fprintf(stderr, "DLCHECK : fatal error %d: looking up kernel32 delayload hook\n", GetLastError());
return 1;
}
if (c != 3) {
Usage();
}
StringCchCopy(szImageName, ARRAYSIZE(szImageName), v[2]);
break;
case 'i':
case 'I':
if (c != 3) {
Usage();
}
fIniFile = TRUE;
StringCchCopy(szImageName, ARRAYSIZE(szImageName), v[2]);
break;
case 't':
case 'T':
if (c != 2) {
Usage();
}
StringCchCopy(szImageName, ARRAYSIZE(szImageName), "");
break;
case 'f':
case 'F':
if (c != 3)
{
Usage();
}
fForceCheckImage = TRUE;
StringCchCopy(szImageName, ARRAYSIZE(szImageName), v[2]);
break; // nothing needs to be done.
default:
Usage();
}
} else {
Usage();
}
// Heck, lets always validate the static delay load stubs, its fast
ReturnValue = ValidateStaticDelayloadStubs();
if (szImageName[0] != '\0')
{
int iFiles = 0;
ReturnValue += CheckImageOrIniFileRecursive(szImageName, fForceCheckImage, fIniFile, &iFiles);
if (iFiles == 0)
{
fprintf(stderr, "DLCHECK : fatal error : no files found to process\n");
}
}
return ReturnValue;
}