Windows-Server-2003/sdktools/debuggers/imagehlp/imagechk.c

2530 lines
69 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
imagechk.c
Abstract:
this module implements a sanity check of certain image characteristics
Author:
NT Base
Revision History:
Notes:
--*/
#ifdef __cplusplus
extern "C" {
#endif
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#ifdef __cplusplus
}
#endif
//
// Calculate the size of a field in a structure of type type, without
// knowing or stating the type of the field.
//
#define RTL_FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
//
// Calculate the size of a structure of type type up through and
// including a field.
//
#define RTL_SIZEOF_THROUGH_FIELD(type, field) \
(FIELD_OFFSET(type, field) + RTL_FIELD_SIZE(type, field))
#include <errno.h>
#include <direct.h>
#include <cvinfo.h>
#include <private.h>
typedef struct _SYMMODLIST{
char *ModName;
void *ModBase;
struct _SYMMODLIST *Next;
} SYMMODLIST, *PSYMMODLIST;
typedef struct List {
char Name[40];
unsigned long Attributes;
} List, *pList;
typedef struct _LogListItem {
char *LogLine;
struct _LogListItem *Next;
} LogListItem, *pLogListItem;
//
// decarations
//
VOID
FindFiles();
VOID
Imagechk(
List *rgpList,
TCHAR *szDirectory
);
VOID
ParseArgs(
int *pargc,
char **argv
);
int
__cdecl
CompFileAndDir(
const void *elem1,
const void *elem2
);
int
__cdecl
CompName(
const void *elem1,
const void *elem2
);
VOID
Usage(
VOID
);
int
_cdecl
_cwild(
VOID
);
PSYMMODLIST
MakeModList(
HANDLE
);
void
FreeModList(
PSYMMODLIST
);
BOOL
CALLBACK
SymEnumerateModulesCallback(
LPSTR,
ULONG64,
PVOID
);
void *
GetModAddrFromName(
PSYMMODLIST,
char *
);
BOOL
VerifyVersionResource(
PCHAR FileName,
BOOL fSelfRegister
);
BOOL
ValidatePdata(
PIMAGE_DOS_HEADER DosHeader
);
BOOL
ImageNeedsOleSelfRegister(
PIMAGE_DOS_HEADER DosHeader
);
NTSTATUS
MiVerifyImageHeader (
IN PIMAGE_NT_HEADERS NtHeader,
IN PIMAGE_DOS_HEADER DosHeader,
IN DWORD NtHeaderSize
);
pLogListItem
LogAppend(
char *,
pLogListItem
);
void
LogOutAndClean(
BOOL
);
void
__cdecl
LogPrintf(
const char *format,
...
);
#define X64K (64*1024)
#define MM_SIZE_OF_LARGEST_IMAGE ((ULONG)0x10000000)
#define MM_MAXIMUM_IMAGE_HEADER (2 * PageSize)
#define MM_MAXIMUM_IMAGE_SECTIONS \
((MM_MAXIMUM_IMAGE_HEADER - (4096 + sizeof(IMAGE_NT_HEADERS))) / \
sizeof(IMAGE_SECTION_HEADER))
#define MMSECTOR_SHIFT 9 //MUST BE LESS THAN OR EQUAL TO PageShift
#define MMSECTOR_MASK 0x1ff
#define MI_ROUND_TO_SIZE(LENGTH,ALIGNMENT) \
(((ULONG)LENGTH + ALIGNMENT - 1) & ~(ALIGNMENT - 1))
#define BYTES_TO_PAGES(Size) (((ULONG)(Size) >> PageShift) + \
(((ULONG)(Size) & (PageSize - 1)) != 0))
#define ArgFlag_OK 1
#define ArgFlag_CKMZ 2
#define ArgFlag_SymCK 4
#define ArgFlag_OLESelf 8
#define ArgFlag_CKBase 16
//
// file global data
//
BOOL fRecurse;
BOOL fFileOut;
BOOL fNotCurrent;
BOOL fPattern;
BOOL fSingleFile;
BOOL fPathOverride;
BOOL fSingleSlash;
BOOL fDebugMapped;
FILE* fout;
CHAR *szFileName = {"*.*"};
CHAR *pszRootDir;
CHAR *pszFileOut;
CHAR szDirectory[MAX_PATH] = {"."};
CHAR szSympath[MAX_PATH] = {0};
CHAR *szPattern;
int endpath, DirNum=1, ProcessedFiles;
ULONG PageSize;
ULONG PageShift;
PVOID HighestUserAddress;
USHORT ValidMachineIDMin;
USHORT ValidMachineIDMax;
DWORD ArgFlag;
//
// logging support
//
pLogListItem pLogList = NULL;
pLogListItem pLogListTmp = NULL;
typedef
NTSTATUS
(NTAPI *LPLDRVERIFYIMAGECHKSUM)(
IN HANDLE ImageFileHandle
);
LPLDRVERIFYIMAGECHKSUM lpOldLdrVerifyImageMatchesChecksum;
typedef
NTSTATUS
(NTAPI *LPLDRVERIFYIMAGEMATCHESCHECKSUM) (
IN HANDLE ImageFileHandle,
IN PLDR_IMPORT_MODULE_CALLBACK ImportCallbackRoutine OPTIONAL,
IN PVOID ImportCallbackParameter,
OUT PUSHORT ImageCharacteristics OPTIONAL
);
LPLDRVERIFYIMAGEMATCHESCHECKSUM lpNewLdrVerifyImageMatchesChecksum;
typedef
NTSTATUS
(NTAPI *LPNTQUERYSYSTEMINFORMATION) (
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
LPNTQUERYSYSTEMINFORMATION lpNtQuerySystemInformation;
OSVERSIONINFO VersionInformation;
//
// function definitions
//
VOID __cdecl
main(
int argc,
char *argv[],
char *envp[]
)
/*++
Routine Description:
program entry
Arguments:
int argc,
char *argv[]
char *envp[]
Return Value:
none
Notes:
--*/
{
TCHAR CWD[MAX_PATH];
int dirlen=0;
if (argc < 2) {
Usage();
}
ParseArgs(&argc, argv);
GetCurrentDirectory(MAX_PATH, CWD);
VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);
if (!GetVersionEx( &VersionInformation )) {
fprintf(stderr, "Unable to detect OS version. Terminating.\n" );
exit(1);
}
if ((VersionInformation.dwPlatformId != VER_PLATFORM_WIN32_NT) ||
(VersionInformation.dwBuildNumber < 1230))
{
lpOldLdrVerifyImageMatchesChecksum = (LPLDRVERIFYIMAGECHKSUM)
GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("LdrVerifyImageMatchesChecksum"));
if (lpOldLdrVerifyImageMatchesChecksum == NULL) {
fprintf(stderr, "Incorrect operating system version.\n" );
exit(1);
}
} else {
lpOldLdrVerifyImageMatchesChecksum = NULL;
if ((VersionInformation.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
(VersionInformation.dwBuildNumber >= 1230))
{
lpNewLdrVerifyImageMatchesChecksum = (LPLDRVERIFYIMAGEMATCHESCHECKSUM)
GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("LdrVerifyImageMatchesChecksum"));
if (lpNewLdrVerifyImageMatchesChecksum == NULL) {
fprintf(stderr, "OS is screwed up. NTDLL doesn't export LdrVerifyImageMatchesChecksum.\n" );
exit(1);
}
}
}
if (VersionInformation.dwPlatformId == VER_PLATFORM_WIN32_NT) {
SYSTEM_BASIC_INFORMATION SystemInformation;
if (VersionInformation.dwBuildNumber <= 1465) {
goto UseWin9x;
}
ValidMachineIDMin = USER_SHARED_DATA->ImageNumberLow;
ValidMachineIDMax = USER_SHARED_DATA->ImageNumberHigh;
lpNtQuerySystemInformation = (LPNTQUERYSYSTEMINFORMATION)
GetProcAddress(GetModuleHandle(TEXT("NTDLL.DLL")), TEXT("NtQuerySystemInformation"));
if (!lpNtQuerySystemInformation) {
fprintf(stderr, "Incorrect operation system version.\n");
exit(1);
}
if (!NT_SUCCESS((*lpNtQuerySystemInformation)(SystemBasicInformation,
&SystemInformation,
sizeof(SystemInformation),
NULL))) {
fprintf(stderr, "OS is screwed up. NtQuerySystemInformation failed.\n");
exit(1);
}
HighestUserAddress = (PVOID)SystemInformation.MaximumUserModeAddress;
} else {
UseWin9x:
HighestUserAddress = (PVOID) 0x7FFE0000;
#ifdef _M_IX86
ValidMachineIDMin = IMAGE_FILE_MACHINE_I386;
ValidMachineIDMax = IMAGE_FILE_MACHINE_I386;
#elif defined(_M_AMD64)
ValidMachineIDMin = IMAGE_FILE_MACHINE_AMD64;
ValidMachineIDMax = IMAGE_FILE_MACHINE_AMD64;
#elif defined(_M_IA64)
ValidMachineIDMin = IMAGE_FILE_MACHINE_IA64;
ValidMachineIDMax = IMAGE_FILE_MACHINE_IA64;
#else
#error("Unknown machine type")
#endif
}
if (fPathOverride) {
if (_chdir(szDirectory) == -1){ // cd to dir
fprintf(stderr, "Path not found: %s\n", szDirectory);
Usage();
}
}
// remove trailing '\' needed only for above chdir, not for output formatting
if (fSingleSlash) {
dirlen = strlen(szDirectory);
szDirectory[dirlen-1] = '\0';
}
FindFiles();
fprintf(stdout, "%d files processed in %d directories\n", ProcessedFiles, DirNum);
}
VOID
FindFiles()
/*++
Routine Description:
make list of files to check, then check them
Arguments:
none
Return Value:
none
Notes:
--*/
{
HANDLE fh;
TCHAR CWD[MAX_PATH];
char *q;
WIN32_FIND_DATA *pfdata;
BOOL fFilesInDir=FALSE;
BOOL fDirsFound=FALSE;
int dnCounter=0, cNumDir=0, i=0, Length=0, NameSize=0, total=0, cNumFiles=0;
pList rgpList[5000];
pfdata = (WIN32_FIND_DATA*)malloc(sizeof(WIN32_FIND_DATA));
if (!pfdata) {
fprintf(stderr, "Not enough memory.\n");
return;
}
if (!fRecurse) {
fh = FindFirstFile(szFileName, pfdata); // find only filename (pattern) if not recursive
} else {
fh = FindFirstFile("*.*", pfdata); // find all if recursive in order to determine subdirectory names
}
if (fh == INVALID_HANDLE_VALUE) {
fprintf(fout==NULL? stderr : fout , "File not found: %s\n", szFileName);
return;
}
// loop to find all files and directories in current directory
// and copy pertinent data to individual List structures.
do {
if (strcmp(pfdata->cFileName, ".") && strcmp(pfdata->cFileName, "..")) { // skip . and ..
rgpList[dnCounter] = (pList)malloc(sizeof(List)); // allocate the memory
if (!rgpList[dnCounter]) {
fprintf(stderr, "Not enough memory.\n");
return;
}
if (!(pfdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { // if file
fFilesInDir=TRUE;
// see if given pattern wildcard extension matches pfdata->cFileName extension
if (fPattern) {
q = strchr(pfdata->cFileName, '.'); // find first instance of "." in filename
if (q == NULL) goto blah; // "." not found
_strlwr(q); // lowercase before compare
if (strcmp(q, szPattern)) goto blah; // if pattern and name doesn't match goto
} // OK, I used a goto, get over it.
if (fSingleFile) {
_strlwr(pfdata->cFileName);
_strlwr(szFileName);
if (strcmp(pfdata->cFileName, szFileName)) goto blah;
}
// if pattern && match || no pattern
strcpy(rgpList[dnCounter]->Name, pfdata->cFileName);
_strlwr(rgpList[dnCounter]->Name); // all lowercase for strcmp in CompName
memcpy(&(rgpList[dnCounter]->Attributes), &pfdata->dwFileAttributes, 4);
dnCounter++;
cNumFiles++;
} else {
if (pfdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { // if dir
fDirsFound=TRUE;
//cNumDir++;
if (fRecurse) {
strcpy(rgpList[dnCounter]->Name, pfdata->cFileName);
_strlwr(rgpList[dnCounter]->Name); // all lowercase for strcmp in CompName
memcpy(&(rgpList[dnCounter]->Attributes), &pfdata->dwFileAttributes, 4);
cNumDir++;
dnCounter++;
}
}
}
}
blah: ;
} while (FindNextFile(fh, pfdata));
FindClose(fh); // close the file handle
// Sort Array arranging FILE entries at top
qsort( (void *)rgpList, dnCounter, sizeof(List *), CompFileAndDir);
// Sort Array alphabetizing only FILE names
qsort( (void *)rgpList, dnCounter-cNumDir, sizeof(List *), CompName);
// Sort Array alphabetizing only DIRectory names
if (fRecurse) {
qsort( (void *)&rgpList[dnCounter-cNumDir], cNumDir, sizeof(List *), CompName);
}
// Process newly sorted structures.
for (i=0; i < dnCounter; ++i) {
if (rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY) { // if Dir
if (fRecurse) {
if (_chdir(rgpList[i]->Name) == -1){ // cd into subdir and check for error
fprintf(stderr, "Unable to change directory: %s\n", rgpList[i]->Name);
} else {
NameSize = strlen(rgpList[i]->Name);
strcat(szDirectory, "\\");
strcat(szDirectory, rgpList[i]->Name); //append name to directory path
total = strlen(szDirectory);
DirNum++; // directory counter
// start another iteration of FindFiles
FindFiles();
// get back to previous directory when above iteration returns
_chdir("..");
// cut off previously appended directory name - for output only
szDirectory[total-(NameSize+1)]='\0';
}
}
} else {
if (!(rgpList[i]->Attributes & FILE_ATTRIBUTE_DIRECTORY)) // check image if not dir
Imagechk(rgpList[i], szDirectory);
}
}
} // end FindFiles
VOID
Imagechk(
List *rgpList,
TCHAR *szDirectory
)
/*++
Routine Description:
check various things, including:
image type, header alignment, image size, machine type
alignment, some properties of various sections, checksum integrity
symbol / image file checksum agreement, existence of symbols, etc
Arguments:
List * rgpList,
TCHAR * szDirectory
Return Value:
none
Notes:
--*/
{
HANDLE File;
HANDLE MemMap;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS NtHeader;
NTSTATUS Status;
BY_HANDLE_FILE_INFORMATION FileInfo;
ULONG NumberOfPtes;
ULONG SectionVirtualSize = 0;
ULONG i;
PIMAGE_SECTION_HEADER SectionTableEntry;
ULONG NumberOfSubsections;
PCHAR ExtendedHeader = NULL;
ULONG_PTR PreferredImageBase;
ULONG_PTR NextVa;
ULONG ImageFileSize;
ULONG OffsetToSectionTable;
ULONG ImageAlignment;
ULONG PtesInSubsection;
ULONG StartingSector;
ULONG EndingSector;
LPSTR ImageName;
LPSTR MachineType = "Unknown";
BOOL MachineTypeMismatch;
BOOL ImageOk;
BOOL fHasPdata;
OSVERSIONINFO OSVerInfo;
ImageName = rgpList->Name;
OSVerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&OSVerInfo);
LogPrintf("ImageChk: %s\\%s \n", szDirectory, ImageName);
ProcessedFiles++;
DosHeader = NULL;
ImageOk = TRUE;
File = CreateFile (ImageName,
GENERIC_READ | FILE_EXECUTE,
OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ? (FILE_SHARE_READ | FILE_SHARE_DELETE) : FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (File == INVALID_HANDLE_VALUE) {
LogPrintf("Error, CreateFile() %d\n", GetLastError());
ImageOk = FALSE;
goto NextImage;
}
MemMap = CreateFileMapping (File,
NULL, // default security.
PAGE_READONLY, // file protection.
0, // high-order file size.
0,
NULL);
if (!GetFileInformationByHandle(File, &FileInfo)) {
fprintf(stderr,"Error, GetFileInfo() %d\n", GetLastError());
CloseHandle(File);
ImageOk = FALSE; goto NextImage;
}
DosHeader = (PIMAGE_DOS_HEADER) MapViewOfFile(MemMap,
FILE_MAP_READ,
0, // high
0, // low
0 // whole file
);
CloseHandle(MemMap);
if (!DosHeader) {
fprintf(stderr,"Error, MapViewOfFile() %d\n", GetLastError());
ImageOk = FALSE; goto NextImage;
}
//
// Check to determine if this is an NT image (PE format) or
// a DOS image, Win-16 image, or OS/2 image. If the image is
// not NT format, return an error indicating which image it
// appears to be.
//
if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
if (ArgFlag & ArgFlag_CKMZ) {
LogPrintf("MZ header not found\n");
ImageOk = FALSE;
}
goto NeImage;
}
if (((ULONG)DosHeader->e_lfanew & 3) != 0) {
//
// The image header is not aligned on a long boundary.
// Report this as an invalid protect mode image.
//
LogPrintf("Image header not on Long boundary\n");
ImageOk = FALSE;
goto NeImage;
}
if ((ULONG)DosHeader->e_lfanew > FileInfo.nFileSizeLow) {
LogPrintf("Image size bigger than size of file\n");
ImageOk = FALSE;
goto NeImage;
}
NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
if (NtHeader->Signature != IMAGE_NT_SIGNATURE) { //if not PE image
LogPrintf("Non 32-bit image");
ImageOk = TRUE;
goto NeImage;
}
//
// Check to see if this is an NT image or a DOS or OS/2 image.
//
Status = MiVerifyImageHeader (NtHeader, DosHeader, 50000);
if (Status != STATUS_SUCCESS) {
ImageOk = FALSE; //continue checking the image but don't print "OK"
}
//
// Verify machine type.
//
fHasPdata = TRUE; // Most do
switch (NtHeader->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386:
MachineType = "x86";
PageSize = 4096;
PageShift = 12;
fHasPdata = FALSE;
break;
case IMAGE_FILE_MACHINE_ALPHA:
MachineType = "Alpha";
PageSize = 8192;
PageShift = 13;
break;
case IMAGE_FILE_MACHINE_IA64:
MachineType = "Intel64";
PageSize = 8192;
PageShift = 13;
break;
case IMAGE_FILE_MACHINE_ALPHA64:
MachineType = "Alpha64";
PageSize = 8192;
PageShift = 13;
break;
default:
LogPrintf("Unrecognized machine type x%lx\n",
NtHeader->FileHeader.Machine);
ImageOk = FALSE;
break;
}
if ((NtHeader->FileHeader.Machine < ValidMachineIDMin) ||
(NtHeader->FileHeader.Machine > ValidMachineIDMax)) {
MachineTypeMismatch = TRUE;
} else {
MachineTypeMismatch = FALSE;
}
ImageAlignment = NtHeader->OptionalHeader.SectionAlignment;
NumberOfPtes = BYTES_TO_PAGES (NtHeader->OptionalHeader.SizeOfImage);
NextVa = NtHeader->OptionalHeader.ImageBase;
if ((NextVa & (X64K - 1)) != 0) {
//
// Image header is not aligned on a 64k boundary.
//
LogPrintf("image base not on 64k boundary %lx\n",NextVa);
ImageOk = FALSE;
goto BadPeImageSegment;
}
//BasedAddress = (PVOID)NextVa;
PtesInSubsection = MI_ROUND_TO_SIZE (
NtHeader->OptionalHeader.SizeOfHeaders,
ImageAlignment
) >> PageShift;
if (ImageAlignment >= PageSize) {
//
// Aligmment is PageSize of greater.
//
if (PtesInSubsection > NumberOfPtes) {
//
// Inconsistent image, size does not agree with header.
//
LogPrintf("Image size in header (%ld.) not consistent with sections (%ld.)\n",
NumberOfPtes, PtesInSubsection);
ImageOk = FALSE;
goto BadPeImageSegment;
}
NumberOfPtes -= PtesInSubsection;
EndingSector = NtHeader->OptionalHeader.SizeOfHeaders >> MMSECTOR_SHIFT;
for (i = 0; i < PtesInSubsection; i++) {
NextVa += PageSize;
}
}
//
// Build the next subsections.
//
NumberOfSubsections = NtHeader->FileHeader.NumberOfSections;
PreferredImageBase = NtHeader->OptionalHeader.ImageBase;
//
// At this point the object table is read in (if it was not
// already read in) and may displace the image header.
//
OffsetToSectionTable = sizeof(ULONG) +
sizeof(IMAGE_FILE_HEADER) +
NtHeader->FileHeader.SizeOfOptionalHeader;
SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader + OffsetToSectionTable);
if (ImageAlignment < PageSize) {
// The image header is no longer valid, TempPte is
// used to indicate that this image alignment is
// less than a PageSize.
//
// Loop through all sections and make sure there is no
// unitialized data.
//
while (NumberOfSubsections > 0) {
if (SectionTableEntry->Misc.VirtualSize == 0) {
SectionVirtualSize = SectionTableEntry->SizeOfRawData;
} else {
SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
}
//
// If the pointer to raw data is zero and the virtual size
// is zero, OR, the section goes past the end of file, OR
// the virtual size does not match the size of raw data, then
// return an error.
//
if (((SectionTableEntry->PointerToRawData !=
SectionTableEntry->VirtualAddress))
||
((SectionTableEntry->SizeOfRawData +
SectionTableEntry->PointerToRawData) >
FileInfo.nFileSizeLow)
||
(SectionVirtualSize > SectionTableEntry->SizeOfRawData)) {
LogPrintf("invalid BSS/Trailingzero section/file size\n");
ImageOk = FALSE;
goto NeImage;
}
SectionTableEntry += 1;
NumberOfSubsections -= 1;
}
goto PeReturnSuccess;
}
while (NumberOfSubsections > 0) {
//
// Handle case where virtual size is 0.
//
if (SectionTableEntry->Misc.VirtualSize == 0) {
SectionVirtualSize = SectionTableEntry->SizeOfRawData;
} else {
SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
}
if (!strcmp(SectionTableEntry->Name, ".debug")) {
fDebugMapped = TRUE;
}
if (SectionVirtualSize == 0) {
//
// The specified virtual address does not align
// with the next prototype PTE.
//
LogPrintf("Section virtual size is 0, NextVa for section %lx %lx\n",
SectionTableEntry->VirtualAddress, NextVa);
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (NextVa !=
(PreferredImageBase + SectionTableEntry->VirtualAddress)) {
//
// The specified virtual address does not align
// with the next prototype PTE.
//
LogPrintf("Section Va not set to alignment, NextVa for section %lx %lx\n",
SectionTableEntry->VirtualAddress, NextVa);
ImageOk = FALSE;
goto BadPeImageSegment;
}
PtesInSubsection =
MI_ROUND_TO_SIZE (SectionVirtualSize, ImageAlignment) >> PageShift;
if (PtesInSubsection > NumberOfPtes) {
//
// Inconsistent image, size does not agree with object tables.
//
LogPrintf("Image size in header not consistent with sections, needs %ld. pages\n",
PtesInSubsection - NumberOfPtes);
LogPrintf("va of bad section %lx\n",SectionTableEntry->VirtualAddress);
ImageOk = FALSE;
goto BadPeImageSegment;
}
NumberOfPtes -= PtesInSubsection;
StartingSector = SectionTableEntry->PointerToRawData >> MMSECTOR_SHIFT;
EndingSector =
(SectionTableEntry->PointerToRawData +
SectionVirtualSize);
EndingSector = EndingSector >> MMSECTOR_SHIFT;
ImageFileSize = SectionTableEntry->PointerToRawData +
SectionTableEntry->SizeOfRawData;
for (i = 0; i < PtesInSubsection; i++) {
//
// Set all the prototype PTEs to refer to the control section.
//
NextVa += PageSize;
}
SectionTableEntry += 1;
NumberOfSubsections -= 1;
}
//
// If the file size is not as big as the image claimed to be,
// return an error.
//
if (ImageFileSize > FileInfo.nFileSizeLow) {
//
// Invalid image size.
//
LogPrintf("invalid image size - file size %lx - image size %lx\n",
FileInfo.nFileSizeLow, ImageFileSize);
ImageOk = FALSE;
goto BadPeImageSegment;
}
{
// Validate the debug information (as much as we can).
PVOID ImageBase;
ULONG DebugDirectorySize, NumberOfDebugDirectories;
PIMAGE_DEBUG_DIRECTORY DebugDirectory;
ImageBase = (PVOID) DosHeader;
DebugDirectory = (PIMAGE_DEBUG_DIRECTORY)
ImageDirectoryEntryToData(
ImageBase,
FALSE,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&DebugDirectorySize );
if (!DebugDirectoryIsUseful(DebugDirectory, DebugDirectorySize)) {
// Not useful. Are they valid? (both s/b zero)
if (DebugDirectory || DebugDirectorySize) {
LogPrintf("Debug directory values [%x, %x] are invalid\n",
DebugDirectory,
DebugDirectorySize);
ImageOk = FALSE;
}
goto DebugDirsDone;
}
NumberOfDebugDirectories = DebugDirectorySize / sizeof( IMAGE_DEBUG_DIRECTORY );
for (i=0; i < NumberOfDebugDirectories; i++) {
if (DebugDirectory->PointerToRawData > FileInfo.nFileSizeLow) {
LogPrintf("Invalid debug directory entry[%d] - File Offset %x is beyond the end of the file\n",
i,
DebugDirectory->PointerToRawData
);
ImageOk = FALSE;
goto BadPeImageSegment;
}
if ((DebugDirectory->PointerToRawData + DebugDirectory->SizeOfData) > FileInfo.nFileSizeLow) {
LogPrintf("Invalid debug directory entry[%d] - File Offset (%X) + Size (%X) is beyond the end of the file (filesize: %X)\n",
i,
DebugDirectory->PointerToRawData,
DebugDirectory->SizeOfData,
FileInfo.nFileSizeLow
);
ImageOk = FALSE;
goto BadPeImageSegment;
}
#if 0
if (DebugDirectory->AddressOfRawData != 0) {
if (DebugDirectory->AddressOfRawData > ImageFileSize){
LogPrintf("Invalid debug directory entry[%d] - VA (%X) is beyond the end of the image VA (%X)\n",
i,
DebugDirectory->AddressOfRawData,
ImageFileSize);
ImageOk = FALSE;
goto BadPeImageSegment;
}
if ((DebugDirectory->AddressOfRawData + DebugDirectory->SizeOfData )> ImageFileSize){
LogPrintf("Invalid debug directory entry[%d] - VA (%X) + size (%X) is beyond the end of the image VA (%X)\n",
i,
DebugDirectory->AddressOfRawData,
DebugDirectory->SizeOfData,
ImageFileSize);
ImageOk = FALSE;
goto BadPeImageSegment;
}
}
#endif
if (DebugDirectory->Type <= 0x7fffffff) {
switch (DebugDirectory->Type) {
case IMAGE_DEBUG_TYPE_MISC:
{
PIMAGE_DEBUG_MISC pDebugMisc;
// MISC should point to an IMAGE_DEBUG_MISC structure
pDebugMisc = (PIMAGE_DEBUG_MISC)((PCHAR)ImageBase + DebugDirectory->PointerToRawData);
if (pDebugMisc->DataType != IMAGE_DEBUG_MISC_EXENAME) {
LogPrintf("MISC Debug has an invalid DataType\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (pDebugMisc->Length != DebugDirectory->SizeOfData) {
LogPrintf("MISC Debug has an invalid size.\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (!pDebugMisc->Unicode) {
i= 0;
while (i < pDebugMisc->Length - sizeof(IMAGE_DEBUG_MISC)) {
if (!isprint(pDebugMisc->Data[i]) &&
(pDebugMisc->Data[i] != '\0') )
{
LogPrintf("MISC Debug has unprintable characters... Possibly corrupt\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
i++;
}
// The data must be a null terminated string.
if (strlen(pDebugMisc->Data) > (pDebugMisc->Length - sizeof(IMAGE_DEBUG_MISC))) {
LogPrintf("MISC Debug has invalid data... Possibly corrupt\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
}
}
break;
case IMAGE_DEBUG_TYPE_CODEVIEW:
// CV will point to either a NB09 or an NB10 signature. Make sure it does.
{
OMFSignature * CVDebug;
CVDebug = (OMFSignature *)((PCHAR)ImageBase + DebugDirectory->PointerToRawData);
if (((*(PULONG)(CVDebug->Signature)) != '90BN') &&
((*(PULONG)(CVDebug->Signature)) != '01BN') &&
((*(PULONG)(CVDebug->Signature)) != 'SDSR'))
{
LogPrintf("CV Debug has an invalid signature\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
}
break;
case IMAGE_DEBUG_TYPE_COFF:
case IMAGE_DEBUG_TYPE_FPO:
case IMAGE_DEBUG_TYPE_EXCEPTION:
case IMAGE_DEBUG_TYPE_FIXUP:
case IMAGE_DEBUG_TYPE_OMAP_TO_SRC:
case IMAGE_DEBUG_TYPE_OMAP_FROM_SRC:
// Not much we can do about these now.
break;
default:
LogPrintf("Invalid debug directory type: %d\n", DebugDirectory->Type);
ImageOk = FALSE;
goto BadPeImageSegment;
break;
}
}
}
}
DebugDirsDone:
//
// The total number of PTEs was decremented as sections were built,
// make sure that there are less than 64ks worth at this point.
//
if (NumberOfPtes >= (ImageAlignment >> PageShift)) {
//
// Inconsistent image, size does not agree with object tables.
//
LogPrintf("invalid image - PTEs left %lx\n",
NumberOfPtes);
ImageOk = FALSE;
goto BadPeImageSegment;
}
// Verify LoadConfig data (if available)
{
PIMAGE_LOAD_CONFIG_DIRECTORY LoadConfigDirectory;
ULONG LoadConfigDirectorySize;
LoadConfigDirectory = (PIMAGE_LOAD_CONFIG_DIRECTORY)
ImageDirectoryEntryToData(
(PVOID) DosHeader,
FALSE,
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
&LoadConfigDirectorySize );
if (LoadConfigDirectory) {
if (!LoadConfigDirectorySize) {
// LOAD_CONFIG directory size in the image header is not set (s/b 0x40 on X86)
LogPrintf("LoadConfig pointer set, but size is not\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (LoadConfigDirectory->Size) {
// Version 2 load config - check the SEH fields.
if (LoadConfigDirectory->Size == RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerTable)) {
// SEH Handler table is on, but count isn't. Bad.
LogPrintf("LoadConfig pointer set, but size is not\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (LoadConfigDirectory->Size >= RTL_SIZEOF_THROUGH_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerCount)) {
// Have both a count and table - check them
if (LoadConfigDirectory->SEHandlerTable) {
// Table exists.
if (!LoadConfigDirectory->SEHandlerCount) {
LogPrintf("Loadconfig structure invalid - SEH table pointer exists, but count is zero\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (LoadConfigDirectory->SEHandlerTable < NtHeader->OptionalHeader.ImageBase ||
(LoadConfigDirectory->SEHandlerTable > (NtHeader->OptionalHeader.ImageBase + NtHeader->OptionalHeader.SizeOfImage))){
// SEH handler table isn't within the image bounds
LogPrintf("Loadconfig structure invalid - SEH table pointer exists, but count is zero\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
LogPrintf("Image is marked as NO_SEH, yet SEH pointer is non-null\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
}
if (LoadConfigDirectory->SEHandlerCount) {
// Count is non-zero
if (!LoadConfigDirectory->SEHandlerTable) {
LogPrintf("Loadconfig structure invalid - SEH count is non-zero but table pointer is null\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (NtHeader->OptionalHeader.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_SEH) {
LogPrintf("Image is marked as NO_SEH, yet SEH count is non-zero\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
}
}
}
}
}
//
// check checksum.
//
PeReturnSuccess:
if (NtHeader->OptionalHeader.CheckSum == 0) {
LogPrintf("(checksum is zero)\n");
} else {
__try {
if (lpOldLdrVerifyImageMatchesChecksum == NULL) {
if (lpNewLdrVerifyImageMatchesChecksum == NULL) {
Status = STATUS_SUCCESS;
LogPrintf("Unable to validate checksum\n");
} else {
Status = (*lpNewLdrVerifyImageMatchesChecksum)(File, NULL, NULL, NULL);
}
} else {
Status = (*lpOldLdrVerifyImageMatchesChecksum)(File);
}
if (NT_ERROR(Status)) {
DWORD HeaderSum, CheckSum;
MapFileAndCheckSum(ImageName, &HeaderSum, &CheckSum);
LogPrintf("checksum mismatch\tis: %x\ts/b: %x\n", NtHeader->OptionalHeader.CheckSum, CheckSum);
ImageOk = FALSE;
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
ImageOk = FALSE;
LogPrintf("checksum mismatch\n");
LogPrintf("LdrVerifyImageMatchesCheckSum AV'd\n");
}
}
if (fHasPdata && ImageOk) {
ImageOk = ValidatePdata(DosHeader);
}
if (ImageOk) {
ImageOk = VerifyVersionResource(ImageName, ImageNeedsOleSelfRegister(DosHeader));
}
//
// sanity test for symbols
// basically : if this does not work, debugging probably will not either
// these high-level debugging api's will also call a pdb validation routine
//
if(ArgFlag & ArgFlag_SymCK)
{
HANDLE hProcess = 0;
char Target[MAX_PATH] = {0};
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
IMAGEHLP_MODULE64 ModuleInfo = {0};
PSYMMODLIST ModList = 0;
void *vpAddr;
PLOADED_IMAGE pLImage = NULL;
DWORD64 symLMflag;
strcpy(Target, szDirectory);
strcat(Target, "\\");
strcat(Target, ImageName);
//
// set up for debugging
//
hProcess = GetCurrentProcess();
if(!SymInitialize(hProcess, szSympath, FALSE))
{
LogPrintf("ERROR:SymInitialize failed!\n");
hProcess = 0;
goto symckend;
}
//
// attempt to use symbols
//
_splitpath(Target, drive, dir, fname, ext );
symLMflag = SymLoadModule64(hProcess, NULL, Target, fname, 0, 0);
if(!symLMflag)
{
LogPrintf("ERROR:SymLoadModule failed! last error:0x%x\n", GetLastError());
goto symckend;
}
//
// identify module type
// find module, symgetmoduleinfo, check dbg type
//
ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
ModList = MakeModList(hProcess);
vpAddr = GetModAddrFromName(ModList, fname);
if(!SymGetModuleInfo64(hProcess, (DWORD64)vpAddr, &ModuleInfo))
{
LogPrintf("ERROR:SymGetModuleInfo failed! last error:0x%x\n", GetLastError());
goto symckend;
}
if(ModuleInfo.SymType != SymPdb)
{
LogPrintf("WARNING: No pdb info for file!\n");
switch(ModuleInfo.SymType){
case SymNone:
LogPrintf("symtype: SymNone\n");
break;
case SymCoff:
LogPrintf("symtype: SymCoff\n");
break;
case SymCv:
LogPrintf("symtype: SymCv\n");
break;
case SymPdb:
LogPrintf("symtype: SymPdb\n");
break;
case SymExport:
LogPrintf("symtype: SymExport\n");
break;
case SymDeferred:
LogPrintf("symtype: SymDeferred\n");
break;
case SymSym:
LogPrintf("symtype: SymSym\n");
break;
}
}
//
// get image, symbol checksum, compare
//
pLImage = ImageLoad(Target, NULL);
{
CHAR szDbgPath[_MAX_PATH];
HANDLE DbgFileHandle;
DbgFileHandle = FindDebugInfoFile(Target, szSympath, szDbgPath);
if (DbgFileHandle != INVALID_HANDLE_VALUE) {
IMAGE_SEPARATE_DEBUG_HEADER DbgHeader;
DWORD BytesRead;
BOOL ReadSuccess;
SetFilePointer(DbgFileHandle, 0, 0, FILE_BEGIN);
ReadSuccess = ReadFile(DbgFileHandle, &DbgHeader, sizeof(DbgHeader), &BytesRead, NULL);
if (ReadSuccess && (BytesRead == sizeof(DbgHeader))) {
// Got enough to check if it's a valid dbg file.
if(((PIMAGE_NT_HEADERS)pLImage->FileHeader)->OptionalHeader.CheckSum != DbgHeader.CheckSum) {
LogPrintf("ERROR! image / debug file checksum not equal\n");
ImageOk = FALSE;
}
}
CloseHandle(DbgFileHandle);
}
}
//
// cleanup
//
symckend:
if(ModList)
{
FreeModList(ModList);
}
if(pLImage)
{
ImageUnload(pLImage);
}
if(symLMflag)
{
SymUnloadModule64(hProcess, (DWORD)symLMflag);
}
if(hProcess)
{
SymCleanup(hProcess);
}
}
NextImage:
BadPeImageSegment:
NeImage:
if ( ImageOk && (ArgFlag & ArgFlag_OK)) {
if (MachineTypeMismatch) {
LogPrintf(" OK [%s]\n", MachineType);
} else {
LogPrintf(" OK\n");
}
}
//
// print out results
//
if (ImageOk)
{
LogOutAndClean((ArgFlag & ArgFlag_OK) ? TRUE : FALSE);
} else {
LogOutAndClean(TRUE);
}
if ( File != INVALID_HANDLE_VALUE ) {
CloseHandle(File);
}
if ( DosHeader ) {
UnmapViewOfFile(DosHeader);
}
}
NTSTATUS
MiVerifyImageHeader (
IN PIMAGE_NT_HEADERS NtHeader,
IN PIMAGE_DOS_HEADER DosHeader,
IN ULONG NtHeaderSize
)
/*++
Routine Description:
Checks image header for consistency.
Arguments:
IN PIMAGE_NT_HEADERS NtHeader
IN PIMAGE_DOS_HEADER DosHeader
IN ULONG NtHeaderSize
Return Value:
Returns the status value.
TBS
--*/
{
if ((NtHeader->FileHeader.Machine == 0) &&
(NtHeader->FileHeader.SizeOfOptionalHeader == 0)) {
//
// This is a bogus DOS app which has a 32-bit portion
// mascarading as a PE image.
//
LogPrintf("Image machine type and size of optional header bad\n");
return STATUS_INVALID_IMAGE_PROTECT;
}
if (!(NtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
LogPrintf("Characteristics not image file executable\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
#ifdef i386
//
// Make sure the image header is aligned on a Long word boundary.
//
if (((ULONG)NtHeader & 3) != 0) {
LogPrintf("NtHeader is not aligned on longword boundary\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
#endif
// Non-driver code must have file alignment set to a multiple of 512
if (((NtHeader->OptionalHeader.FileAlignment & 511) != 0) &&
(NtHeader->OptionalHeader.FileAlignment !=
NtHeader->OptionalHeader.SectionAlignment)) {
LogPrintf("file alignment is not multiple of 512 and power of 2\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
//
// File aligment must be power of 2.
//
if ((((NtHeader->OptionalHeader.FileAlignment << 1) - 1) &
NtHeader->OptionalHeader.FileAlignment) !=
NtHeader->OptionalHeader.FileAlignment) {
LogPrintf("file alignment not power of 2\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->OptionalHeader.SectionAlignment < NtHeader->OptionalHeader.FileAlignment) {
LogPrintf("SectionAlignment < FileAlignment\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->OptionalHeader.SizeOfImage > MM_SIZE_OF_LARGEST_IMAGE) {
LogPrintf("Image too big %lx\n",NtHeader->OptionalHeader.SizeOfImage);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->FileHeader.NumberOfSections > MM_MAXIMUM_IMAGE_SECTIONS) {
LogPrintf("Too many image sections %ld.\n",
NtHeader->FileHeader.NumberOfSections);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (ArgFlag & ArgFlag_CKBase) {
if ((PVOID)NtHeader->OptionalHeader.ImageBase >= HighestUserAddress) {
LogPrintf("Image base (%lx) is invalid on this machine\n",
NtHeader->OptionalHeader.ImageBase);
return STATUS_SUCCESS;
}
}
return STATUS_SUCCESS;
}
VOID
ParseArgs(
int *pargc,
char **argv
)
/*++
Routine Description:
parse arguments to this program
Arguments:
int *pargc
char **argv
Return Value:
none
Notes:
command line args:
(original)
case '?': call usage and exit
case 'b': check whether base address of image is in user space for this machine
case 's': /s <sympath> check symbols
case 'p': PE Errors only
case 'r': recurse subdirectories
(new)
case 'v': verbose - output "OK"
case 'o': output "OleSelfRegister not set"
--*/
{
CHAR cswitch, c, *p;
CHAR sztmp[MAX_PATH];
int argnum = 1, i=0, len=0, count=0;
BOOL fslashfound = FALSE;
//
// set default flags here
//
ArgFlag |= ArgFlag_CKBase;
while ( argnum < *pargc ) {
_strlwr(argv[argnum]);
cswitch = *(argv[argnum]);
if (cswitch == '/' || cswitch == '-') {
c = *(argv[argnum]+1);
switch (c) {
case 'o':
ArgFlag |= ArgFlag_OLESelf;
break;
case 'v':
ArgFlag |= ArgFlag_OK | ArgFlag_CKMZ | ArgFlag_OLESelf;
break;
case '?':
Usage();
break;
case 'b':
ArgFlag ^= ArgFlag_CKBase;
break;
case 's':
if (argv[argnum+1]) {
strcpy(szSympath, (argv[argnum+1]));
ArgFlag |= ArgFlag_SymCK;
argnum++;
}
break;
case 'p':
ArgFlag |= ArgFlag_CKMZ;
break;
case 'r':
fRecurse = TRUE;
if (argv[argnum+1]) {
fPathOverride=TRUE;
strcpy(szDirectory, (argv[argnum+1]));
if (!(strcmp(szDirectory, "\\"))) { // if just '\'
fSingleSlash=TRUE;
}
//LogPrintf("dir %s\n", szDirectory);
argnum++;
}
break;
default:
fprintf(stderr, "Invalid argument.\n");
Usage();
}
} else {
// Check for path\filename or wildcards
// Search for '\' in string
strcpy(sztmp, (argv[argnum]));
len = strlen(sztmp);
for (i=0; i < len; i++) {
if (sztmp[i]=='\\') {
count++;
endpath=i; // mark last '\' char found
fslashfound=TRUE; // found backslash, so must be a path\filename combination
}
}
if (fslashfound && !fRecurse) { // if backslash found and not a recursive operation
// seperate the directory and filename into two strings
fPathOverride=TRUE;
strcpy(szDirectory, sztmp);
if (!(strcmp(szDirectory, "\\"))) {
Usage();
}
szFileName = _strdup(&(sztmp[endpath+1]));
if (count == 1) { //&& szDirectory[1] == ':') { // if only one '\' char and drive letter indicated
fSingleSlash=TRUE;
szDirectory[endpath+1]='\0'; // keep trailing '\' in order to chdir properly
} else {
szDirectory[endpath]='\0';
}
if (szFileName[0] == '*' && szFileName[1] == '.' && szFileName[2] != '*') {
_strlwr(szFileName);
szPattern = strchr(szFileName, '.'); //search for '.'
fPattern = TRUE;
}
} else { // no backslash found, assume filename without preceeding path
szFileName = _strdup(argv[argnum]);
if (!szFileName) {
// drastic error, just bail
szFileName = "";
return;
}
//
// filename or wildcard
//
if ( (*(argv[argnum]) == '*') && (*(argv[argnum]+1) == '.') && (*(argv[argnum]+2) != '*') ){
// *.xxx
_strlwr(szFileName);
szPattern = strchr(szFileName, '.'); //search for '.'
fPattern = TRUE;
} else if ( (*(argv[argnum]) == '*') && (*(argv[argnum]+1) == '.') && (*(argv[argnum]+2) == '*') ) {
// *.*
} else {
// probably a single filename
_strlwr(szFileName);
fSingleFile = TRUE;
}
if (fRecurse && strchr(szFileName, '\\') ) { // don't want path\filename when recursing
Usage();
}
}
//fprintf(stdout, "dir %s\nfile %s\n", szDirectory, szFileName);
}
++argnum;
}
if (szFileName[0] == '\0') {
Usage();
}
} // parseargs
int
__cdecl
CompFileAndDir(
const void *elem1,
const void *elem2
)
/*++
Routine Description:
Purpose: a comparision routine passed to QSort. It compares elem1 and elem2
based upon their attribute, i.e., is it a file or directory.
Arguments:
const void *elem1,
const void *elem2
Return Value:
result of comparison function
Notes:
--*/
{
pList p1, p2;
// qsort passes a void universal pointer. Use a typecast (List**)
// so the compiler recognizes the data as a List structure.
// Typecast pointer-to-pointer-to-List and dereference ONCE
// leaving a pList. I don't dereference the remaining pointer
// in the p1 and p2 definitions to avoid copying the structure.
p1 = (*(List**)elem1);
p2 = (*(List**)elem2);
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
return 0;
//both dirs
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
return 0;
//both files
if ( (p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && !(p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
return 1;
// elem1 is dir and elem2 is file
if (!(p1->Attributes & FILE_ATTRIBUTE_DIRECTORY) && (p2->Attributes & FILE_ATTRIBUTE_DIRECTORY))
return -1;
// elem1 is file and elem2 is dir
return 0; // if none of the above
}
int
__cdecl
CompName(
const void *elem1,
const void *elem2
)
/*++
Routine Description:
another compare routine passed to QSort that compares the two Name strings
Arguments:
const void *elem1,
const void *elem2
Return Value:
result of comparison function
Notes:
this uses a noignore-case strcmp
--*/
{
return strcmp( (*(List**)elem1)->Name, (*(List**)elem2)->Name );
}
VOID
Usage(
VOID
)
/*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/
{
fputs("Usage: imagechk [/?] displays this message\n"
" [/r dir] recurse from directory dir\n"
" [/b] don't check image base address\n"
" [/v] verbose - output everything\n"
" [/o] output \"OleSelfRegister not set\" warning\n"
" [/p] output \"MZ header not found\"\n"
" [/s <sympath>] check pdb symbols\n"
" [filename] file to check\n"
" Accepts wildcard extensions such as *.exe\n"
" imagechk /r . \"*.exe\" check all *.exe recursing on current directory\n"
" imagechk /r \\ \"*.exe\" check all *.exe recursing from root of current drive\n"
" imagechk \"*.exe\" check all *.exe in current directory\n"
" imagechk c:\\bar.exe check c:\\bar.exe only\n",
stderr);
exit(1);
}
int
__cdecl
_cwild()
/*++
Routine Description:
Arguments:
Return Value:
Notes:
--*/
{
return(0);
}
typedef DWORD (WINAPI *PFNGVS)(LPSTR, LPDWORD);
typedef BOOL (WINAPI *PFNGVI)(LPTSTR, DWORD, DWORD, LPVOID);
typedef BOOL (WINAPI *PFNVQV)(const LPVOID, LPTSTR, LPVOID *, PUINT);
BOOL
VerifyVersionResource(
PCHAR FileName,
BOOL fSelfRegister
)
/*++
Routine Description:
validate the version resource in a file
Arguments:
PCHAR FileName
BOOL fSelfRegister
Return Value:
TRUE if: no version.dll found
FALSE if: version resource missing
Notes:
--*/
{
static HINSTANCE hVersion = NULL;
static PFNGVS pfnGetFileVersionInfoSize = NULL;
static PFNGVI pfnGetFileVersionInfo = NULL;
static PFNVQV pfnVerQueryValue = NULL;
DWORD dwSize;
DWORD lpInfoSize;
LPVOID lpData = NULL, lpInfo;
BOOL rc = FALSE;
DWORD dwDefLang = 0x00000409;
DWORD *pdwTranslation, uLen;
CHAR buf[60];
CHAR szVersionDll[_MAX_PATH];
if (GetSystemDirectory(szVersionDll, sizeof(szVersionDll))) {
strcat(szVersionDll, "\\version.dll");
} else {
strcpy(szVersionDll, "version.dll");
}
if (!hVersion) {
hVersion = LoadLibraryA(szVersionDll);
if (hVersion == NULL) {
return TRUE;
}
pfnGetFileVersionInfoSize = (PFNGVS) GetProcAddress(hVersion, "GetFileVersionInfoSizeA");
pfnGetFileVersionInfo = (PFNGVI) GetProcAddress(hVersion, "GetFileVersionInfoA");
pfnVerQueryValue = (PFNVQV) GetProcAddress(hVersion, "VerQueryValueA");
}
if (!pfnGetFileVersionInfoSize || !pfnGetFileVersionInfo || !pfnVerQueryValue) {
rc = TRUE;
goto cleanup;
}
if ((dwSize = (*pfnGetFileVersionInfoSize)(FileName, &dwSize)) == 0){
LogPrintf("No version resource detected\n");
goto cleanup;
}
if (!fSelfRegister) {
// All we need to do is see if the version resource exists. Ole Self Register not necessary.
rc = TRUE;
goto cleanup;
}
if ((lpData = malloc(dwSize)) == NULL) {
LogPrintf("Out of memory\n");
goto cleanup;
}
if (!(*pfnGetFileVersionInfo)(FileName, 0, dwSize, lpData)) {
LogPrintf("Unable to read version info\n - %d", GetLastError());
goto cleanup;
}
if(!pfnVerQueryValue(lpData, "\\VarFileInfo\\Translation", &pdwTranslation, &uLen)) {
pdwTranslation = &dwDefLang;
uLen = sizeof(DWORD);
}
sprintf(buf, "\\StringFileInfo\\%04x%04x\\OleSelfRegister", LOWORD(*pdwTranslation), HIWORD(*pdwTranslation));
if (!pfnVerQueryValue(lpData, buf, &lpInfo, &lpInfoSize) && (ArgFlag & ArgFlag_OLESelf )) {
LogPrintf("OleSelfRegister not set\n");
} else {
rc = TRUE;
}
cleanup:
if (lpData) {
free(lpData);
}
// No need to free the hVersion
return(rc);
}
BOOL
ValidatePdata(
PIMAGE_DOS_HEADER DosHeader
)
/*++
Routine Description:
validates the PIMAGE_RUNTIME_FUNCTION_ENTRY in the executable
Arguments:
PIMAGE_DOS_HEADER DosHeader
Return Value:
TRUE if:
FALSE if: no exception data
exception table size incorrect
exception table corrupt
Notes:
--*/
{
// The machine type indicates this image should have pdata (an exception table).
// Ensure it looks reasonable.
// Todo: Add a range check for exception handler and data
PIMAGE_RUNTIME_FUNCTION_ENTRY ExceptionTable;
DWORD ExceptionTableSize, i;
DWORD_PTR LastEnd;
BOOL fRc;
PIMAGE_NT_HEADERS NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
ULONG_PTR ImageBase = NtHeader->OptionalHeader.ImageBase;
DWORD PDataStart = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
DWORD PDataSize = NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
ExceptionTable = (PIMAGE_RUNTIME_FUNCTION_ENTRY)
ImageDirectoryEntryToData(
DosHeader,
FALSE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
&ExceptionTableSize );
if (!ExceptionTable ||
(ExceptionTable && (ExceptionTableSize == 0)))
{
// No Exception table.
return(TRUE);
}
if (ExceptionTableSize % sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) {
// The size isn't an even multiple.
LogPrintf("exception table size is not correct\n");
return(FALSE);
}
LastEnd = 0;
fRc = TRUE;
for (i=0; i < (ExceptionTableSize / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)); i++) {
if (!ExceptionTable[i].BeginAddress) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: zero value for BeginAddress\n",
i);
fRc = FALSE;
}
if (!ExceptionTable[i].EndAddress) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: zero value for EndAddress\n",
i);
fRc = FALSE;
}
#if defined(_IA64_)
if (!ExceptionTable[i].UnwindInfoAddress) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: zero value for UnwindInfoAddress\n",
i);
fRc = FALSE;
}
#elif defined(_ALPHA_) || defined(_AXP64_)
if (!ExceptionTable[i].PrologEndAddress) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: zero value for PrologEndAddress\n",
i);
fRc = FALSE;
}
#endif // defined(_IA64_)
if (ExceptionTable[i].BeginAddress < LastEnd) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: the begin address [%8.8x] is out of sequence. Prior end was [%8.8x]\n",
i,
ExceptionTable[i].BeginAddress,
LastEnd);
fRc = FALSE;
}
if (ExceptionTable[i].EndAddress < ExceptionTable[i].BeginAddress) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: the end address [%8.8x] is before the begin address[%8.8X]\n",
i,
ExceptionTable[i].EndAddress,
ExceptionTable[i].BeginAddress);
fRc = FALSE;
}
#if defined(_ALPHA_) || defined(_AXP64_)
if (!((ExceptionTable[i].PrologEndAddress >= ExceptionTable[i].BeginAddress) &&
(ExceptionTable[i].PrologEndAddress <= ExceptionTable[i].EndAddress)))
{
if (NtHeader->FileHeader.Machine == IMAGE_FILE_MACHINE_ALPHA) {
// Change this test. On Alpha, the PrologEndAddress is allowed to be
// outside the Function Start/End range. If this is true, the PrologEnd
// - ImageBase - pdata section VA s/b divisible by sizeof IMAGE_RUNTIME_FUNCTION_ENTRY
// AND within the bounds of the PdataSize. It's supposed to be an index into the
// pdata data that descibes the real scoping function.
LONG PrologAddress;
PrologAddress = (LONG) (ExceptionTable[i].PrologEndAddress - ImageBase - PDataStart);
if (PrologAddress % sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: the secondary prolog end address[%8.8x] does not evenly index into the exception table.\n",
i,
ExceptionTable[i].PrologEndAddress,
ExceptionTable[i].BeginAddress,
ExceptionTable[i].EndAddress
);
fRc = FALSE;
} else {
if ((PrologAddress < 0) || (PrologAddress > (LONG)(PDataStart - sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)))) {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: the secondary prolog end address[%8.8x] does not index into the exception table.\n",
i,
ExceptionTable[i].PrologEndAddress,
ExceptionTable[i].BeginAddress,
ExceptionTable[i].EndAddress
);
fRc = FALSE;
}
}
} else {
if (fRc != FALSE) {
LogPrintf("exception table is corrupt.\n");
}
LogPrintf("PDATA Entry[%d]: the prolog end address[%8.8x] is not within the bounds of the frame [%8.8X] - [%8.8X]\n",
i,
ExceptionTable[i].PrologEndAddress,
ExceptionTable[i].BeginAddress,
ExceptionTable[i].EndAddress
);
fRc = FALSE;
}
}
#endif // !defined(_IA64_)
LastEnd = ExceptionTable[i].EndAddress;
}
return(fRc);
}
BOOL
ImageNeedsOleSelfRegister(
PIMAGE_DOS_HEADER DosHeader
)
/*++
Routine Description:
Arguments:
PIMAGE_DOS_HEADER DosHeader
Return Value:
TRUE if DllRegisterServer or DllUnRegisterServer is exported
--*/
{
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PIMAGE_NT_HEADERS NtHeader;
PIMAGE_SECTION_HEADER SectionHeader;
DWORD ExportDirectorySize, i;
USHORT x;
PCHAR rvaDelta;
PULONG NameTable;
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
ImageDirectoryEntryToData(
DosHeader,
FALSE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportDirectorySize );
if (!ExportDirectory ||
!ExportDirectorySize ||
!ExportDirectory->NumberOfNames)
{
// No exports (no directory, no size, or no names).
return(FALSE);
}
// Walk the section headers and find the va/raw offsets.
NtHeader = (PIMAGE_NT_HEADERS)((PCHAR)DosHeader + (ULONG)DosHeader->e_lfanew);
SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
for (x = 0; x < NtHeader->FileHeader.NumberOfSections; x++) {
if (((ULONG)((PCHAR)ExportDirectory - (PCHAR)DosHeader) >= SectionHeader->PointerToRawData) &&
((ULONG)((PCHAR)ExportDirectory - (PCHAR)DosHeader) <
(SectionHeader->PointerToRawData + SectionHeader->SizeOfRawData))) {
break;
} else {
SectionHeader++;
}
}
if (x == NtHeader->FileHeader.NumberOfSections) {
// We didn't find the section that contained the export table. Assume it's not there.
return(FALSE);
}
rvaDelta = (PCHAR)DosHeader + SectionHeader->PointerToRawData - SectionHeader->VirtualAddress;
NameTable = (PULONG)(rvaDelta + ExportDirectory->AddressOfNames);
for (i = 0; i < ExportDirectory->NumberOfNames; i++) {
if (!strcmp("DllRegisterServer", rvaDelta + NameTable[i]) ||
!strcmp("DllUnRegisterServer", rvaDelta + NameTable[i]))
{
return(TRUE);
}
}
return(FALSE);
}
//
// support routines for symbol checker - could all
// be done without this using lower-level internal api's
//
PSYMMODLIST
MakeModList(
HANDLE hProcess
)
/*++
Routine Description:
build a list of loaded symbol modules and addresses
Arguments:
HANDLE hProcess
Return Value:
PSYMMODLIST
Notes:
--*/
{
PSYMMODLIST ModList;
ModList = (PSYMMODLIST)calloc(1, sizeof(SYMMODLIST));
SymEnumerateModules64(hProcess, SymEnumerateModulesCallback, ModList);
return(ModList);
}
BOOL
CALLBACK
SymEnumerateModulesCallback(
LPSTR ModuleName,
ULONG64 BaseOfDll,
PVOID UserContext
)
/*++
Routine Description:
callback routine for SymEnumerateModules
in this case, UserContext is a pointer to a head of a _SYMMODLIST struct
that will have a new item appended
We are avoiding global state for these lists so we can use several at once,
they will be short, so we will find the end each time we want to add
runs slower, simpler to maintain
Arguments:
LPSTR ModuleName
ULONG64 BaseOfDll
PVOID UserContext
Return Value:
TRUE
Notes:
--*/
{
PSYMMODLIST pSymModList;
//
// find end of list, key on pSymModList->ModBase
//
pSymModList = (PSYMMODLIST)UserContext;
while (pSymModList->ModBase)
{
pSymModList = pSymModList->Next;
}
//
// append entry
//
pSymModList->ModName = malloc(strlen(ModuleName) + 1);
if (!pSymModList->ModName)
return FALSE;
strcpy(pSymModList->ModName, ModuleName);
pSymModList->ModBase = (void *)BaseOfDll;
pSymModList->Next = (PSYMMODLIST)calloc(1, sizeof(SYMMODLIST));
return(TRUE);
}
void *
GetModAddrFromName(
PSYMMODLIST ModList,
char *ModName
)
/*++
Routine Description:
gets module address from a SYMMODLIST given module base name
Arguments:
PSYMMODLIST ModList
char * ModName
Return Value:
module address
--*/
{
while (ModList->Next != 0)
{
if (strcmp(ModList->ModName, ModName) == 0)
{
break;
}
ModList = ModList->Next;
}
return(ModList->ModBase);
}
void
FreeModList(
PSYMMODLIST ModList
)
/*++
Routine Description:
free a list of loaded symbol modules and addresses
Arguments:
PSYMMODLIST ModList
Return Value:
none
--*/
{
PSYMMODLIST ModListNext;
while (ModList)
{
if(ModList->ModName)
{
free(ModList->ModName);
}
ModListNext = ModList->Next;
free(ModList);
ModList = ModListNext;
}
}
pLogListItem LogAppend(
char *logitem,
pLogListItem plog
)
/*++
Routine Description:
add a log line to the linked list of log lines
Arguments:
char * logitem - a formatted line of text to be logged
pLogListItem plog - pointer to LogListItem
Return Value:
a pointer to the LogListItem allocated
the first call to this function should save this pointer and use
it for the head of the list, and it should be used when calling
LogOutAndClean() to print the list and free all the memory
you can call this with plog == head of list, or == to last item
if plog == 0, this means that the item being allocated is the head
of the list.
If plog == head of list, search for end of list
if plog == last item allocated, then the search is much faster
--*/
{
pLogListItem ptemp;
ptemp = plog;
if(plog)
{
while(ptemp->Next)
{
ptemp = ptemp->Next;
}
}
if(!ptemp)
{
ptemp = (pLogListItem)calloc(sizeof(LogListItem), 1);
if (!ptemp)
return NULL;
} else {
ptemp->Next = (pLogListItem)calloc(sizeof(LogListItem), 1);
if (!ptemp->Next)
return NULL;
ptemp = ptemp->Next;
}
ptemp->LogLine = (char *)malloc(strlen(logitem) + 1);
if (!ptemp->LogLine)
return NULL;
strcpy(ptemp->LogLine, logitem);
return (ptemp);
}
void LogOutAndClean(
BOOL print
)
/*++
Routine Description:
output the log output, and free all the items in the list
Arguments:
none
Return Value:
none
--*/
{
pLogListItem ptemp;
pLogListItem plog = pLogList;
while(plog)
{
ptemp = plog;
if(print)
{
fputs(plog->LogLine, stderr);
}
plog = plog->Next;
free(ptemp->LogLine);
free(ptemp);
}
if(print)
{
fprintf(stderr, "\n");
}
pLogListTmp = pLogList = NULL;
}
void
__cdecl
LogPrintf(
const char *format,
...
)
/*++
Routine Description:
logging wrapper for fprintf
Arguments:
none
Return Value:
none
--*/
{
va_list arglist;
char LogStr[1024];
va_start(arglist, format);
vsprintf(LogStr, format, arglist);
if(pLogList == NULL)
{
//
// initialize log
//
pLogListTmp = pLogList = LogAppend(LogStr, NULL);
} else {
//
// append to log
//
pLogListTmp = LogAppend(LogStr, pLogListTmp);
}
}