NT4/private/sdktools/imagehlp/imagechk.c
2020-09-30 17:12:29 +02:00

1232 lines
38 KiB
C

#ifdef __cplusplus
extern "C" {
#endif
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#ifdef __cplusplus
}
#endif
#include <errno.h>
#include <direct.h>
#include <cvinfo.h>
#include <private.h>
typedef struct List {
char Name[40];
unsigned long Attributes;
} List, *pList;
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);
BOOL
VerifyVersionResource(
PCHAR FileName
);
NTSTATUS
MiVerifyImageHeader (
IN PIMAGE_NT_HEADERS NtHeader,
IN PIMAGE_DOS_HEADER DosHeader,
IN DWORD NtHeaderSize
);
ULONG PageSize = 4096;
ULONG PageShift = 12;
#define X64K (64*1024)
#define MM_SIZE_OF_LARGEST_IMAGE ((ULONG)0x10000000)
#define MM_MAXIMUM_IMAGE_HEADER (2 * PageSize)
#define MM_HIGHEST_USER_ADDRESS ((PVOID)0x7FFE0000)
#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))
BOOL fRecurse;
BOOL fFileOut;
BOOL fNotCurrent;
BOOL fPattern;
BOOL fSingleFile;
BOOL fPathOverride;
BOOL fSingleSlash;
BOOL fDebugMapped;
BOOL fcheckbase = TRUE;
FILE* fout;
CHAR *szFileName = {"*.*"};
CHAR *pszRootDir;
CHAR *pszFileOut;
CHAR szDirectory[MAX_PATH] = {"."};
CHAR *szPattern;
int endpath, DirNum=1, ProcessedFiles;
typedef
NTSTATUS
(NTAPI *LPLDRVERIFYIMAGECHKSUM)(
IN HANDLE ImageFileHandle
);
LPLDRVERIFYIMAGECHKSUM lpOldLdrVerifyImageMatchesChecksum;
VOID __cdecl
main(
int argc,
char *argv[],
char *envp[]
)
{
OSVERSIONINFO VersionInformation;
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 ) ||
VersionInformation.dwPlatformId != VER_PLATFORM_WIN32_NT ||
VersionInformation.dwBuildNumber < 1230
) {
lpOldLdrVerifyImageMatchesChecksum = (LPLDRVERIFYIMAGECHKSUM)
GetProcAddress(LoadLibrary(TEXT("NTDLL.DLL")), TEXT("LdrVerifyImageMatchesChecksum"));
if (lpOldLdrVerifyImageMatchesChecksum == NULL) {
fprintf(stderr, "Incorrect operating system version.\n" );
exit(1);
}
} else {
lpOldLdrVerifyImageMatchesChecksum = NULL;
}
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(){
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, 46); // 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
/*************************************************************************************\
* Imagechk
\*************************************************************************************/
VOID
Imagechk(
List *rgpList,
TCHAR *szDirectory
)
{
HANDLE File;
HANDLE MemMap;
PIMAGE_DOS_HEADER DosHeader;
PIMAGE_NT_HEADERS NtHeader;
NTSTATUS Status;
BY_HANDLE_FILE_INFORMATION FileInfo;
ULONG NumberOfPtes;
ULONG SectionVirtualSize;
ULONG i;
PIMAGE_SECTION_HEADER SectionTableEntry;
ULONG SectorOffset;
ULONG NumberOfSubsections;
PCHAR ExtendedHeader = NULL;
ULONG PreferredImageBase;
ULONG NextVa;
ULONG ImageFileSize;
ULONG OffsetToSectionTable;
ULONG ImageAlignment;
ULONG PtesInSubsection;
ULONG StartingSector;
ULONG EndingSector;
LPSTR ImageName;
LPSTR MachineType;
BOOL MachineTypeMismatch;
BOOL ImageOk;
ImageName = rgpList->Name;
fprintf(stderr,"ImageChk: %s\\%s ", szDirectory, ImageName);
ProcessedFiles++;
DosHeader = NULL;
ImageOk = TRUE;
File = CreateFile (ImageName,
GENERIC_READ | FILE_EXECUTE,
FILE_SHARE_READ | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (File == INVALID_HANDLE_VALUE) {
fprintf(stderr, "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) {
fprintf(stderr, "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.
//
fprintf(stderr, "Image header not on Long boundary\n");
ImageOk = FALSE;
goto NeImage;
}
if ((ULONG)DosHeader->e_lfanew > FileInfo.nFileSizeLow) {
fprintf(stderr, "Image size bigger than size of file\n");
ImageOk = FALSE;
goto NeImage;
}
NtHeader = (PIMAGE_NT_HEADERS)((ULONG)DosHeader + (ULONG)DosHeader->e_lfanew);
if (NtHeader->Signature != IMAGE_NT_SIGNATURE) { //if not PE image
fprintf(stderr, "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.
//
switch (NtHeader->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386:
MachineType = "x86";
break;
case IMAGE_FILE_MACHINE_R3000:
MachineType = "MIPS R3000";
break;
case IMAGE_FILE_MACHINE_R4000:
MachineType = "MIPS R4000";
break;
case IMAGE_FILE_MACHINE_R10000:
MachineType = "MIPS R10000";
break;
case IMAGE_FILE_MACHINE_ALPHA:
MachineType = "Alpha";
PageSize = 8192;
PageShift = 13;
break;
case IMAGE_FILE_MACHINE_POWERPC:
MachineType = "PowerPC";
break;
default:
fprintf(stderr, "Unrecognized machine type x%lx\n",
NtHeader->FileHeader.Machine);
ImageOk = FALSE;
break;
}
if ((NtHeader->FileHeader.Machine < USER_SHARED_DATA->ImageNumberLow) ||
(NtHeader->FileHeader.Machine > USER_SHARED_DATA->ImageNumberHigh)) {
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.
//
fprintf(stderr, "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.
//
fprintf(stderr, "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++) {
SectorOffset += PageSize;
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)((ULONG)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)) {
fprintf(stderr, "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.
//
fprintf(stderr, "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.
//
fprintf(stderr, "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.
//
fprintf(stderr, "Image size in header not consistent with sections, needs %ld. pages\n",
PtesInSubsection - NumberOfPtes);
fprintf(stderr, "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;
SectorOffset = 0;
for (i = 0; i < PtesInSubsection; i++) {
//
// Set all the prototype PTEs to refer to the control section.
//
SectorOffset += PageSize;
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.
//
fprintf(stderr, "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, i;
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) {
fprintf(stderr,
"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) {
fprintf(stderr,
"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) {
fprintf(stderr,
"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 (DebugDirectory->AddressOfRawData != 0) {
if (!fDebugMapped) {
fprintf(stderr,
"Invalid debug directory entry[%d] - VA is non-zero (%X), but no .debug section exists\n",
i,
DebugDirectory->AddressOfRawData);
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (DebugDirectory->AddressOfRawData > ImageFileSize){
fprintf(stderr,
"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){
fprintf(stderr,
"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;
}
}
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)((DWORD)ImageBase + DebugDirectory->PointerToRawData);
if (pDebugMisc->DataType != IMAGE_DEBUG_MISC_EXENAME) {
fprintf(stderr, "MISC Debug has an invalid DataType\n");
ImageOk = FALSE;
goto BadPeImageSegment;
}
if (pDebugMisc->Length != DebugDirectory->SizeOfData) {
fprintf(stderr, "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') )
{
fprintf(stderr, "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))) {
fprintf(stderr, "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 *)((DWORD)ImageBase + DebugDirectory->PointerToRawData);
if (((*(PULONG)(CVDebug->Signature)) != '90BN') &&
((*(PULONG)(CVDebug->Signature)) != '01BN'))
{
fprintf(stderr, "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:
fprintf(stderr, "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.
//
fprintf(stderr, "invalid image - PTEs left %lx\n",
NumberOfPtes);
ImageOk = FALSE;
goto BadPeImageSegment;
}
//
// check checksum.
//
PeReturnSuccess:
if (NtHeader->OptionalHeader.CheckSum == 0) {
fprintf(stderr, "(checksum is zero) ");
} else {
__try {
if (lpOldLdrVerifyImageMatchesChecksum != NULL)
Status = (*lpOldLdrVerifyImageMatchesChecksum)(File);
else
Status = LdrVerifyImageMatchesChecksum (File, NULL, NULL, NULL);
if (NT_ERROR(Status)) {
fprintf(stderr, "checksum mismatch\n");
ImageOk = FALSE;
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
ImageOk = FALSE;
fprintf(stderr, "checksum mismatch\n");
}
}
ImageOk = VerifyVersionResource(ImageName);
NextImage:
BadPeImageSegment:
NeImage:
if ( ImageOk ) {
if (MachineTypeMismatch) {
fprintf(stderr," OK [%s]\n", MachineType);
} else {
fprintf(stderr," OK\n");
}
}
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:
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.
//
fprintf(stderr, "Image machine type and size of optional header bad\n");
return STATUS_INVALID_IMAGE_PROTECT;
}
if (!(NtHeader->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)) {
fprintf(stderr, "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) {
fprintf(stderr, "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)) {
fprintf(stderr, "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) {
fprintf(stderr, "file alignment not power of 2\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->OptionalHeader.SectionAlignment < NtHeader->OptionalHeader.FileAlignment) {
fprintf(stderr, "SectionAlignment < FileAlignment\n");
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->OptionalHeader.SizeOfImage > MM_SIZE_OF_LARGEST_IMAGE) {
fprintf(stderr, "Image too big %lx\n",NtHeader->OptionalHeader.SizeOfImage);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (NtHeader->FileHeader.NumberOfSections > MM_MAXIMUM_IMAGE_SECTIONS) {
fprintf(stderr, "Too many image sections %ld.\n",
NtHeader->FileHeader.NumberOfSections);
return STATUS_INVALID_IMAGE_FORMAT;
}
if (fcheckbase) {
if ((PVOID)NtHeader->OptionalHeader.ImageBase >= MM_HIGHEST_USER_ADDRESS) {
fprintf(stderr, "Image base is invalid %lx\n",
NtHeader->OptionalHeader.ImageBase);
return STATUS_INVALID_IMAGE_FORMAT;
}
}
return STATUS_SUCCESS;
}
VOID
ParseArgs(
int *pargc,
char **argv
)
{
CHAR cswitch, c, *p;
CHAR sztmp[MAX_PATH];
int argnum = 1, i=0, len=0, count=0;
BOOL fslashfound = FALSE;
while ( argnum < *pargc ) {
_strlwr(argv[argnum]);
cswitch = *(argv[argnum]+1);
if (cswitch == '/' || cswitch == '-') {
c = *(argv[argnum]+2);
switch (c) {
case '?':
Usage();
case 'r':
fRecurse = TRUE;
if (argv[argnum+1]) {
fPathOverride=TRUE;
strcpy(szDirectory, (argv[argnum+1]+1));
if (!(strcmp(szDirectory, "\\"))) { // if just '\'
fSingleSlash=TRUE;
}
//fprintf(stdout, "dir %s\n", szDirectory);
argnum++;
}
break;
case 'b':
fcheckbase = FALSE;
break;
default:
fprintf(stderr, "Invalid argument.\n");
Usage();
}
} else {
// Check for path\filename or wildcards
// begin at argv[argnum]+1 because first char is repeated
// Search for '\' in string
strcpy(sztmp, (argv[argnum]+1));
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, 46); //search for '.'
fPattern = TRUE;
}
} else { // no backslash found, assume filename without preceeding path
//
// filename or wildcard
//
if ( (*(argv[argnum]+1) == '*') && (*(argv[argnum]+2) == '.') && (*(argv[argnum]+3) != '*') ){
// *.xxx
szFileName = _strdup(argv[argnum]+1);
_strlwr(szFileName);
szPattern = strchr(szFileName, 46); //search for '.'
fPattern = TRUE;
} else if ( (*(argv[argnum]+1) == '*') && (*(argv[argnum]+2) == '.') && (*(argv[argnum]+3) == '*') ) {
// *.*
strcpy(szFileName, "*.*");
} else {
// probably a single filename
szFileName = _strdup(argv[argnum]+1);
_strlwr(szFileName);
fSingleFile = TRUE;
}
if (fRecurse && strchr(szFileName, 92) ) { // don't want path\filename when recursing
Usage();
}
}
//fprintf(stdout, "dir %s\nfile %s\n", szDirectory, szFileName);
}
++argnum;
}
if (szFileName[0] == '\0') {
Usage();
}
} // parseargs
/********************************************************************************************\
* CompFileAndDir
* Purpose: a comparision routine passed to QSort. It compares elem1 and elem2
* based upon their attribute, i.e., is it a file or directory.
\********************************************************************************************/
int __cdecl
CompFileAndDir(
const void *elem1,
const void *elem2
)
{
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
}
/********************************************************************************************\
* CompName is another compare routine passed to QSort that compares the two Name strings *
\********************************************************************************************/
int __cdecl
CompName(
const void *elem1,
const void *elem2
)
{
return strcmp( (*(List**)elem1)->Name, (*(List**)elem2)->Name );
}
LPSTR pszUsage =
"Usage: imagechk [/?] displays this message\n"
" [/r dir] recurse from directory dir\n"
" [/b] don't check image base address\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"
"";
VOID
Usage(VOID)
{
fprintf(stderr, pszUsage);
exit(1);
}
int __cdecl
_cwild()
{
return(0);
}
typedef DWORD (WINAPI *PFNGVS)(LPSTR, LPDWORD);
BOOL
VerifyVersionResource(
PCHAR FileName
)
{
HINSTANCE hVersion;
PFNGVS pfnGetFileVersionInfoSize;
DWORD dwSize;
DWORD dwReturn;
BOOL rc;
hVersion = LoadLibraryA("VERSION.DLL");
if (hVersion == NULL) {
return TRUE;
}
pfnGetFileVersionInfoSize = (PFNGVS) GetProcAddress(hVersion, "GetFileVersionInfoSizeA");
if (pfnGetFileVersionInfoSize == NULL) {
FreeLibrary(hVersion);
return TRUE;
}
if ((dwReturn = (*pfnGetFileVersionInfoSize)(FileName, &dwSize)) == 0) {
fprintf(stderr, "No version resource detected\n");
rc = FALSE;
} else {
rc = TRUE;
}
FreeLibrary(hVersion);
return(rc);
}