|
|
#include <windows.h>
#include "crc32.h"
#include "chksect.h"
#define CHECK_SECTION
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
#define RoundUp(n,scale) (scale * ((n + scale - 1) / scale))
#define IsMemZero(pv,cb) (!(*((char *)pv) || memcmp(pv,((char *)pv)+1,cb-1)))
#pragma intrinsic(memcpy,memcmp)
enum { EX_CHECKSUM, EX_SECURITY, EX_CRC32FILE, EX_EOF, MAX_EXCLUDE };
#define MAX_BUFFER (256*1024) /* must be even */
typedef struct { DWORD signature; DWORD crc32File; DWORD cbCabFile; } SELFTEST_SECTION;
#define SECTION_NAME "Ext_Cab1"
#define SECTION_SIGNATURE (0x4D584653)
#ifdef ADD_SECTION
SELFTEST_RESULT AddSection(char *pszEXEFileName,char *pszCABFileName) #else
#ifdef CHECK_SECTION
SELFTEST_RESULT CheckSection(char *pszEXEFileName) #else
SELFTEST_RESULT SelfTest(char *pszEXEFileName, unsigned long *poffCabinet,unsigned long *pcbCabinet) #endif
#endif
{ HANDLE hFile; // handle to the file we're updating
enum SELFTEST_RESULT result; // our return code
union { IMAGE_DOS_HEADER dos; IMAGE_NT_HEADERS nt; IMAGE_SECTION_HEADER section; } header; // used to examine the file
DWORD offNTHeader; // file offset to NT header
int cSections; // number of sections in the file
unsigned char *pBuffer; // general-purpose buffer
DWORD cbActual; // # of bytes actual read/written
#ifndef CHECK_SECTION
unsigned long crc32; // computed CRC-32
struct { DWORD offExclude; DWORD cbExclude; } excludeList[MAX_EXCLUDE]; // list of ranges to exclude from CRC
int iExclude; // exclude list index
DWORD offSelfTestSection; // file offset of our added section
SELFTEST_SECTION SelfTestSection; // added section header
DWORD cbFile; // number of bytes in file/region
DWORD cbChunk; // number of bytes in current chunk
DWORD offFile; // current file offset
#endif
#ifdef ADD_SECTION
DWORD offSectionHeader; // file offset of section header
DWORD offMaxVirtualAddress; // lowest unused virtual address
DWORD cbAlignVirtual; // virtual address alignment increment
DWORD cbAlignFile; // file address alignment increment
HANDLE hCABFile; // cabinet file handle
DWORD cbCABFile; // cabinet file size
DWORD checksum; // generated checksum
WORD *pBufferW; // used to generate checksum
#endif
#ifdef CHECK_SECTION
DWORD offSectionHeaderEnd; // first unused byte after section headers
DWORD offFirstSection; // first used byte after that
DWORD offImportStart; // where the import entries start
DWORD cbImport; // size of import entry data
#endif
#ifndef CHECK_SECTION
GenerateCRC32Table(); #endif
pBuffer = (void *) GlobalAlloc(GMEM_FIXED,MAX_BUFFER); if (pBuffer == NULL) { result = SELFTEST_NO_MEMORY; goto done_no_buffer; }
#ifdef ADD_SECTION
/* get size of cabinet */
hCABFile = CreateFile(pszCABFileName,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); if (hCABFile == INVALID_HANDLE_VALUE) { result = SELFTEST_FILE_NOT_FOUND; goto done_no_cab; }
cbCABFile = GetFileSize(hCABFile,NULL); #endif
/* open EXE image */
#ifdef ADD_SECTION
hFile = CreateFile(pszEXEFileName,GENERIC_READ|GENERIC_WRITE,0,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); #else
hFile = CreateFile(pszEXEFileName,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); #endif
if (hFile == INVALID_HANDLE_VALUE) { result = SELFTEST_FILE_NOT_FOUND; goto done_no_exe; }
/* read MS-DOS header */
if ((ReadFile(hFile,&header.dos,sizeof(IMAGE_DOS_HEADER),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_DOS_HEADER))) { result = SELFTEST_READ_ERROR; goto done; }
if (header.dos.e_magic != IMAGE_DOS_SIGNATURE) { offNTHeader = 0; } else { offNTHeader = header.dos.e_lfanew; }
/* read PE header */
SetFilePointer(hFile,offNTHeader,NULL,FILE_BEGIN);
if ((ReadFile(hFile,&header.nt,sizeof(IMAGE_NT_HEADERS),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_NT_HEADERS))) { result = SELFTEST_READ_ERROR; goto done; }
if (header.nt.Signature != IMAGE_NT_SIGNATURE) { result = SELFTEST_NOT_PE_FILE; goto done; }
cSections = header.nt.FileHeader.NumberOfSections;
#ifdef ADD_SECTION
cbAlignVirtual = header.nt.OptionalHeader.SectionAlignment; cbAlignFile = header.nt.OptionalHeader.FileAlignment; offMaxVirtualAddress = 0; #endif
#ifndef CHECK_SECTION
/* determine current file size */
cbFile = GetFileSize(hFile,NULL); if (cbFile == 0xFFFFFFFF) { result = SELFTEST_READ_ERROR; goto done; } #endif
#ifndef CHECK_SECTION
/* see if we've been signed */
if (header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress != 0) { #ifdef ADD_SECTION
result = SELFTEST_SIGNED; goto done; #else
/* make sure certificate is at the end of the file */
if (cbFile != (header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress + header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size)) { result = SELFTEST_FAILED; goto done; } else { /* ignore anything starting at the certificate */
cbFile = header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress; } #endif
} #endif
#ifdef ADD_SECTION
/* determine lowest un-used virtual address */ #else
/* locate our added section */ #endif
#ifndef CHECK_SECTION
offSelfTestSection = 0; #endif
#ifdef ADD_SECTION
offSectionHeader = offNTHeader + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + header.nt.FileHeader.SizeOfOptionalHeader + cSections * sizeof(IMAGE_SECTION_HEADER); #endif
SetFilePointer(hFile,(offNTHeader + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + header.nt.FileHeader.SizeOfOptionalHeader),NULL,FILE_BEGIN);
#ifdef CHECK_SECTION
offSectionHeaderEnd = offNTHeader + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + header.nt.FileHeader.SizeOfOptionalHeader + cSections * sizeof(IMAGE_SECTION_HEADER);
offImportStart = header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress; cbImport = header.nt.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size;
if ((ReadFile(hFile,&header.section,sizeof(IMAGE_SECTION_HEADER),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_SECTION_HEADER))) { result = SELFTEST_READ_ERROR; goto done; }
offFirstSection = header.section.PointerToRawData;
if ((offFirstSection - offSectionHeaderEnd) > 0) { SetFilePointer(hFile,offSectionHeaderEnd,NULL,FILE_BEGIN);
if ((ReadFile(hFile,pBuffer,(offFirstSection - offSectionHeaderEnd),&cbActual,NULL) != TRUE) || (cbActual != (DWORD) (offFirstSection - offSectionHeaderEnd))) { result = SELFTEST_READ_ERROR; goto done; }
if ((offImportStart >= offSectionHeaderEnd) && ((offImportStart + cbImport) <= offFirstSection)) { memset(pBuffer + (offImportStart - offSectionHeaderEnd),0,cbImport); }
if ((*pBuffer != '\0') || (((offFirstSection - offSectionHeaderEnd) > 1) && (memcmp(pBuffer,pBuffer + 1,(offFirstSection - offSectionHeaderEnd - 1)) != 0))) { result = SELFTEST_DIRTY; } else { result = SELFTEST_NO_ERROR; } } else { result = SELFTEST_NO_ERROR; } #else
while (cSections--) { if ((ReadFile(hFile,&header.section,sizeof(IMAGE_SECTION_HEADER),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_SECTION_HEADER))) { result = SELFTEST_READ_ERROR; goto done; }
if (!memcmp(header.section.Name,SECTION_NAME,sizeof(header.section.Name))) { /* found our added section */
#ifdef ADD_SECTION
result = SELFTEST_ALREADY; goto done; #else
offSelfTestSection = header.section.PointerToRawData;
break; #endif
}
#ifdef ADD_SECTION
if (offMaxVirtualAddress < (header.section.VirtualAddress + header.section.Misc.VirtualSize)) { offMaxVirtualAddress = (header.section.VirtualAddress + header.section.Misc.VirtualSize); } #endif
}
#ifdef ADD_SECTION
/* increase number of sections in the file; whack checksum */
SetFilePointer(hFile,offNTHeader,NULL,FILE_BEGIN);
if ((ReadFile(hFile,&header.nt,sizeof(IMAGE_NT_HEADERS),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_NT_HEADERS))) { result = SELFTEST_READ_ERROR; goto done; }
header.nt.FileHeader.NumberOfSections++; header.nt.OptionalHeader.CheckSum = 0; header.nt.OptionalHeader.SizeOfImage = RoundUp(offMaxVirtualAddress,cbAlignVirtual) + RoundUp((sizeof(SELFTEST_SECTION) + cbCABFile),cbAlignVirtual);
SetFilePointer(hFile,offNTHeader,NULL,FILE_BEGIN);
if ((WriteFile(hFile,&header.nt,sizeof(IMAGE_NT_HEADERS),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_NT_HEADERS))) { result = SELFTEST_WRITE_ERROR; goto done; }
/* make sure there's room for another section header */
SetFilePointer(hFile,offSectionHeader,NULL,FILE_BEGIN);
if ((ReadFile(hFile,&header.section,sizeof(IMAGE_SECTION_HEADER),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_SECTION_HEADER))) { result = SELFTEST_READ_ERROR; goto done; }
if (!IsMemZero(&header.section,sizeof(IMAGE_SECTION_HEADER))) { result = SELFTEST_NO_SECTION; goto done; }
/* create the new section header */
memcpy(header.section.Name,SECTION_NAME,sizeof(header.section.Name)); header.section.SizeOfRawData = RoundUp((sizeof(SELFTEST_SECTION) + cbCABFile),cbAlignFile); header.section.PointerToRawData = RoundUp(cbFile,cbAlignFile); header.section.VirtualAddress = RoundUp(offMaxVirtualAddress,cbAlignVirtual); header.section.Misc.VirtualSize = RoundUp((sizeof(SELFTEST_SECTION) + cbCABFile),cbAlignVirtual); header.section.Characteristics = (IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE | IMAGE_SCN_MEM_READ);
/* write the new section header */
SetFilePointer(hFile,offSectionHeader,NULL,FILE_BEGIN);
if ((WriteFile(hFile,&header.section,sizeof(IMAGE_SECTION_HEADER),&cbActual,NULL) != TRUE) || (cbActual != sizeof(IMAGE_SECTION_HEADER))) { result = SELFTEST_WRITE_ERROR; goto done; }
/* create the new section data */
memset(&SelfTestSection,0,sizeof(SelfTestSection)); SelfTestSection.signature = SECTION_SIGNATURE; SelfTestSection.cbCabFile = cbCABFile;
offSelfTestSection = header.section.PointerToRawData;
SetFilePointer(hFile,offSelfTestSection,NULL,FILE_BEGIN);
if ((WriteFile(hFile,&SelfTestSection,sizeof(SelfTestSection),&cbActual,NULL) != TRUE) || (cbActual != sizeof(SelfTestSection))) { result = SELFTEST_WRITE_ERROR; goto done; }
/* copy cabinet into section */
SetFilePointer(hCABFile,0,NULL,FILE_BEGIN);
cbFile = cbCABFile;
while (cbFile) { if (cbFile > MAX_BUFFER) { cbChunk = MAX_BUFFER; } else { cbChunk = cbFile; }
if ((ReadFile(hCABFile,pBuffer,cbChunk,&cbActual,NULL) != TRUE) || (cbActual != cbChunk)) { result = SELFTEST_READ_ERROR; goto done; }
if ((WriteFile(hFile,pBuffer,cbChunk,&cbActual,NULL) != TRUE) || (cbActual != cbChunk)) { result = SELFTEST_WRITE_ERROR; }
cbFile -= cbChunk; }
/* pad added section as needed */
cbChunk = header.section.SizeOfRawData - sizeof(SelfTestSection) - cbCABFile;
if (cbChunk != 0) { memset(pBuffer,0,cbChunk);
if ((WriteFile(hFile,pBuffer,cbChunk,&cbActual,NULL) != TRUE) || (cbActual != cbChunk)) { result = SELFTEST_WRITE_ERROR; } }
/* we've now increased total size of the file */
cbFile = offSelfTestSection + header.section.SizeOfRawData; #else
/* make sure our added section was found */
if (offSelfTestSection == 0) { result = SELFTEST_NO_SECTION; goto done; } #endif
/* If this EXE gets signed, the checksum will be changed. */
excludeList[EX_CHECKSUM].offExclude = offNTHeader + offsetof(IMAGE_NT_HEADERS,OptionalHeader.CheckSum); excludeList[EX_CHECKSUM].cbExclude = sizeof(header.nt.OptionalHeader.CheckSum);
/* If this EXE gets signed, the security entry will be changed. */
excludeList[EX_SECURITY].offExclude = offNTHeader + offsetof(IMAGE_NT_HEADERS, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]); excludeList[EX_SECURITY].cbExclude = sizeof(IMAGE_DATA_DIRECTORY);
/* Can't CRC our own CRC field. */
excludeList[EX_CRC32FILE].offExclude = offSelfTestSection + offsetof(SELFTEST_SECTION,crc32File); excludeList[EX_CRC32FILE].cbExclude = sizeof(SelfTestSection.crc32File);
/* Stop at end of known file. */
/* Note: current code assumes that the only thing which could */ /* be appended to the file after this is the certificate from */ /* codesigning, and that it will be pointed at by the security */ /* entry. If anything else appends, or padding is added before */ /* the certificate, we'll have to store this file size in the */ /* added section, and retrieve it before running the CRC. */
excludeList[EX_EOF].offExclude = cbFile;
/* Compute the CRC-32 of the file, skipping excluded extents. */ /* This code assumes excludeList is sorted by offExclude. */
crc32 = CRC32_INITIAL_VALUE; offFile = 0;
#ifdef ADD_SECTION
/* Along the way, compute the correct checksum for this new */ /* image. We know that each of the sections on the exclude */ /* list just happened to be zeroed right now, so they won't */ /* affect our checksum. But we will have to add our new CRC32 */ /* value to the checksum, because it will be in the file when */ /* we're done. It helps that we know that all the exclusions */ /* on the list are WORD aligned and have even lengths. */
/* The checksum in a PE file is a 16-bit sum of 16-bit words */ /* in the file, with wrap-around carry, while the checksum */ /* field is filled with zero. The file's length is added, */ /* yielding a 32-bit result. */
checksum = 0; #endif
for (iExclude = 0; iExclude < MAX_EXCLUDE; iExclude++) { SetFilePointer(hFile,offFile,NULL,FILE_BEGIN);
cbFile = excludeList[iExclude].offExclude - offFile;
while (cbFile) { if (cbFile > MAX_BUFFER) { cbChunk = MAX_BUFFER; } else { cbChunk = cbFile; }
if ((ReadFile(hFile,pBuffer,cbChunk,&cbActual,NULL) != TRUE) || (cbActual != cbChunk)) { result = SELFTEST_READ_ERROR; goto done; }
CRC32Update(&crc32,pBuffer,cbChunk);
offFile += cbChunk; cbFile -= cbChunk;
#ifdef ADD_SECTION
/* roll buffer into checksum */
pBufferW = (WORD *) pBuffer;
cbChunk >>= 1;
while (cbChunk--) { checksum += *pBufferW++;
if (checksum > 0x0000FFFF) { checksum -= 0x0000FFFF; } } #endif
/*
* INSERT PROGRESS GAUGE HERE: * %complete = (offFile * 100.0) / excludeList[EX_EOF].offExclude */ }
offFile += excludeList[iExclude].cbExclude; }
#ifdef ADD_SECTION
/* account for CRC32 value in checksum */
checksum += (WORD) crc32; checksum += (crc32 >> 16);
while (checksum > 0x0000FFFF) { checksum -= 0x0000FFFF; }
/* add file length to checksum */
checksum += excludeList[EX_EOF].offExclude;
/* update CRC-32 value in added section */
SetFilePointer(hFile,excludeList[EX_CRC32FILE].offExclude,NULL,FILE_BEGIN);
if ((WriteFile(hFile,&crc32,sizeof(crc32),&cbActual,NULL) != TRUE) || (cbActual != sizeof(crc32))) { result = SELFTEST_WRITE_ERROR; goto done; }
/* update checksum value in header */
SetFilePointer(hFile,excludeList[EX_CHECKSUM].offExclude,NULL,FILE_BEGIN);
if ((WriteFile(hFile,&checksum,sizeof(checksum),&cbActual,NULL) != TRUE) || (cbActual != sizeof(checksum))) { result = SELFTEST_WRITE_ERROR; goto done; }
/* done */
if (CloseHandle(hFile) != TRUE) { result = SELFTEST_WRITE_ERROR; } else { result = SELFTEST_NO_ERROR; }
goto done_no_exe; #else
/* read the header from the added section */
SetFilePointer(hFile,offSelfTestSection,NULL,FILE_BEGIN);
if ((ReadFile(hFile,&SelfTestSection,sizeof(SelfTestSection),&cbActual,NULL) != TRUE) || (cbActual != sizeof(SelfTestSection))) { result = SELFTEST_READ_ERROR; goto done; }
/* verify CRC-32 value in added section */
if ((SelfTestSection.signature != SECTION_SIGNATURE) || (crc32 != SelfTestSection.crc32File)) { result = SELFTEST_FAILED; } else { *poffCabinet = offSelfTestSection + sizeof(SelfTestSection); *pcbCabinet = SelfTestSection.cbCabFile;
result = SELFTEST_NO_ERROR; } #endif
#endif // CHECK_SECTION
done: CloseHandle(hFile);
done_no_exe:
#ifdef ADD_SECTION
CloseHandle(hCABFile);
done_no_cab: #endif
GlobalFree((HGLOBAL) pBuffer);
done_no_buffer:
#ifdef ADD_SECTION
/* destroy failed attempt */
if (result != SELFTEST_NO_ERROR) { DeleteFile(pszEXEFileName); } #endif
return(result); }
|