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
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;
|
|
}
|