1232 lines
38 KiB
C
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);
|
|
}
|