windows-nt/Source/XPSP1/NT/sdktools/restools/rlt32/delcert/delcert.cpp

508 lines
11 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//// DelCert - Delete all certificates in Win32 image
//
///// delCert Command line
//
//c delCert Executable
//
//p Executable: Win32 binary to update
//
#pragma warning( disable : 4786 ) // map creates some ridiculously long debug identifiers
#include "stdio.h"
#include "windows.h"
#include "imagehlp.h"
#include "time.h"
#define DBG 1
void __cdecl DebugMsg(char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt);
vfprintf(stderr, fmt, vargs);
}
BOOL g_fError = FALSE;
#define warn DebugMsg
#define MUST(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {if (!g_fError) DebugMsg b; g_fError = TRUE; return E_FAIL;};}
#define SHOULD(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {DebugMsg b; g_fWarn = TRUE; return S_FALSE;};}
#if DBG
#pragma message("Checked build")
#define OK(a) {HRESULT hr; hr = (a); if (hr!= S_OK) {DebugMsg("%s(%d): error RSRC999 : HRESULT not S_OK: "#a"\n", __FILE__, __LINE__); return hr;};}
#define ASSERT(a) {if (!(a)) {DebugMsg("%s(%d): error RSRC999 : Assertion failed: "#a"\n", __FILE__, __LINE__); return E_UNEXPECTED;};}
#else
#pragma message ("Free build")
#define OK(a) {HRESULT hr; hr = (a); if (hr != S_OK) return hr;}
#define ASSERT(a) {if (!(a)) {return E_UNEXPECTED;};}
#endif
//// Mapped files
//
// File mapping is used to read executable and token files.
//
// File mapping is also used to update in place checksum information
// in executable and symbol files.
class MappedFile {
HANDLE m_hFileMapping;
BOOL fRW; // True when writeable
char m_szFileName[MAX_PATH];
public:
BYTE *m_pStart;
BYTE *m_pLimit;
MappedFile() {m_hFileMapping = NULL;}
HRESULT Open(const char *pcFileName, BOOL fWrite) {
HANDLE hFile;
strcpy(m_szFileName, pcFileName);
hFile = CreateFileA(
pcFileName,
GENERIC_READ | (fWrite ? GENERIC_WRITE : 0),
FILE_SHARE_READ | (fWrite ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0 ),
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
ASSERT(hFile != INVALID_HANDLE_VALUE);
m_hFileMapping = CreateFileMapping(
hFile,
NULL,
fWrite ? PAGE_READWRITE : PAGE_WRITECOPY,
0,0, NULL);
ASSERT(m_hFileMapping != NULL);
m_pStart = (BYTE*) MapViewOfFile(
m_hFileMapping,
fWrite ? FILE_MAP_WRITE : FILE_MAP_READ,
0,0, 0);
ASSERT(m_pStart != NULL);
m_pLimit = m_pStart + GetFileSize(hFile, NULL);
CloseHandle(hFile);
fRW = fWrite;
return S_OK;
}
DWORD CalcChecksum() {
DWORD dwHeaderSum;
DWORD dwCheckSum;
if (CheckSumMappedFile((void*)m_pStart, (DWORD)(m_pLimit-m_pStart), &dwHeaderSum, &dwCheckSum) == NULL) {
return 0;
} else {
return dwCheckSum;
}
}
HRESULT Close() {
if (m_pStart) {
UnmapViewOfFile(m_pStart);
CloseHandle(m_hFileMapping);
m_hFileMapping = NULL;
m_pStart = NULL;
}
return S_OK;
}
};
class Win32Executable : public MappedFile {
IMAGE_NT_HEADERS *m_pNtHeader;
IMAGE_SECTION_HEADER *m_pSections;
public:
DWORD GetChecksum() const {return m_pNtHeader->OptionalHeader.CheckSum;}
void SetChecksum(DWORD dwChecksum) {m_pNtHeader->OptionalHeader.CheckSum=dwChecksum;}
void SetCertificateRVA (DWORD rva)
{
m_pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
.VirtualAddress
= rva;
}
HRESULT Open(const char *pcFileName, BOOL fWrite) {
int i;
OK(MappedFile::Open(pcFileName, fWrite));
MUST(( *(WORD*)m_pStart == IMAGE_DOS_SIGNATURE
&& *(WORD*)(m_pStart+0x18) >= 0x40) // WinVer >= 4
? S_OK : E_FAIL,
("RSRC : error RSRC501: %s is not an executable file\n", pcFileName));
m_pNtHeader = (IMAGE_NT_HEADERS*)(m_pStart + *(WORD*)(m_pStart+0x3c));
MUST((m_pNtHeader->Signature == IMAGE_NT_SIGNATURE)
? S_OK : E_FAIL,
("RSRC : error RSRC502: %s is not a Win32 executable file\n", pcFileName));
m_pSections = (IMAGE_SECTION_HEADER*)(m_pNtHeader+1);
return S_OK;
}
};
void DeleteCertificates(
const char *fileName
)
{
HANDLE fileHandle = CreateFile(
fileName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (fileHandle == INVALID_HANDLE_VALUE) {
warn("Could not access %s\n", fileName);
return;
}
DWORD certificateCount;
DWORD *certificateIndices;
if (!ImageEnumerateCertificates(
fileHandle,
CERT_SECTION_TYPE_ANY,
&certificateCount,
NULL,
0
)) {
warn("Could not enumerate certificates in %s.\n", fileName);
CloseHandle(fileHandle);
return;
}
if (certificateCount == 0) {
warn("No certificates found in %s.\n", fileName);
CloseHandle(fileHandle);
return;
}
certificateIndices = new DWORD[certificateCount];
if (certificateIndices == NULL) {
CloseHandle(fileHandle);
warn("Couldn't allocate buffer for %d certificate indices for %s.\n", certificateCount, fileName);
return;
}
ImageEnumerateCertificates(
fileHandle,
CERT_SECTION_TYPE_ANY,
&certificateCount,
certificateIndices,
certificateCount
);
if (certificateCount == 0) {
warn("Couldn't get certificate indices for %s.\n", fileName);
CloseHandle(fileHandle);
return;
}
for (DWORD i=0; i<certificateCount; i++) {
if (!ImageRemoveCertificate(fileHandle, certificateIndices[i])) {
warn("Could not remove certificate index %d from %s\n", certificateIndices[i], fileName);
CloseHandle(fileHandle);
return;
}
}
warn("All %d certificate(s) removed from %s.\n", certificateCount, fileName);
CloseHandle(fileHandle);
// Now clear the rva of any security certificates
Win32Executable w32x;
if (w32x.Open(fileName, TRUE) != S_OK) {
warn("Could not map %s to update security certificate RVA\n", fileName);
return;
}
w32x.SetCertificateRVA(0);
w32x.SetChecksum(w32x.CalcChecksum());
w32x.Close();
warn("Certificate section virtual address zeroed in %s section index.\n", fileName);
}
//// Parameter parsing
//
//
char g_cSwitch = '-'; // Switch character is recorded the first time one is seen
void SkipWhitespace(char** p, char* pE) {
while ((*p<pE) && ((**p==' ')||(**p==9))) (*p)++;
}
void ParseToken(char** p, char* pE, char* s, int l) {
// Parse up to whitespace into string s
// Guarantee zero terminator and modify no more than l chars
// Return with p beyond whitespace
if (*p < pE && **p == '\"') {
// Quoted parameter
(*p)++; // Skip over leading quote
while (l>0 && *p<pE && **p!='\"') {
*s=**p; s++; (*p)++; l--;
}
// Skip any part of token that didn't fit s
while (*p<pE && **p!='\"') { // Skip up to terminating quote
(*p)++;
}
if (*p<pE) { // Skip over terminating quote
(*p)++;
}
} else {
// Unquoted parameter
while ((l>0) && (*p<pE) && (**p>' ')) {
*s=**p; s++; (*p)++;
l--;
}
// Skip any part of token that didn't fit into s
while ((*p<pE) && (**p>' ')) (*p)++;
}
if (l>0)
*s++ = 0;
else
*(s-1) = 0;
SkipWhitespace(p, pE);
}
void ParseName(char** p, char* pE, char* s, int l) {
// Uses ParseToken to parse a name such as a filename.
// If the name starts with '/' or '-' it is assumed to be
// an option rather than a filename and ParseName returns
// a zero length string.
if (*p<pE && **p==g_cSwitch) {
// This is an option and should not be treated as a name argument
s[0] = 0;
} else {
ParseToken(p, pE, s, l);
}
}
void DisplayUsage() {
fprintf(stdout, "Usage: delCert executable\n");
}
DWORD g_dwOptions = 0;
#define OPTHELP 1
HRESULT ProcessParameters() {
char *p; // Current command line character
char *pE; // End of command line
char *pcStop;
char token [MAX_PATH];
char szExecutable [MAX_PATH];
int i,j;
int cFiles = 0;
BOOL fArgError = FALSE;
p = GetCommandLine();
pE = p+strlen((char *)p);
// Skip command name
ParseToken(&p, pE, token, sizeof(token));
while (p<pE) {
ParseToken(&p, pE, token, sizeof(token));
if ( token[0] == '-'
|| token[0] == '/') {
// Process command option(s)
i = 1;
g_cSwitch = token[0]; // Argument may start with the other switch character
CharLower((char*)token);
while (token[i]) {
switch (token[i]) {
case '?':
case 'h': g_dwOptions |= OPTHELP; break;
default:
fprintf(stderr, "Unrecognised argument '%c'.\n", token[i]);
fArgError = TRUE;
break;
}
i++;
}
} else {
// Process filename
switch (cFiles) {
case 0: strcpy(szExecutable, token); break;
}
cFiles++;
}
}
if ( fArgError
|| g_dwOptions & OPTHELP) {
DisplayUsage();
return S_OK;
} else if (cFiles != 1) {
DisplayUsage();
return E_INVALIDARG;
} else {
// We have valid parameters
DeleteCertificates(szExecutable);
return S_OK;
}
}
int _cdecl main(void) {
if (SUCCEEDED(ProcessParameters())) {
return 0; // No problems
} else {
return 2; // Error(s)
}
}