/////////////////////////////////////////////////////////////////////////////
//  FILE          : client.cxx                                             //
//  DESCRIPTION   : Crypto API interface                                   //
//  AUTHOR        :                                                        //
//  HISTORY       :                                                        //
//      Mar  8 1996 larrys  New                                            //
//                  dbarlow                                                //
//                                                                         //
//  Copyright (C) 1996 Microsoft Corporation   All Rights Reserved         //
/////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include "mainrpc.h"    // header file generated by the MIDL compiler
#include <imagehlp.h>
#include "des.h"
#include "modes.h"


#include "skrpc.h"
extern "C" {
#include "md5.h"
};

// designatred resource for in file signatures
#define CRYPT_SIG_RESOURCE_NUMBER   "#666"

// MAC in file
#define MAC_RESOURCE_NUMBER   "#667"

static DWORD dwMACInFileVersion = 0x100;

BYTE rgbDESKey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};


BOOL HashTheFile(
                 LPCSTR pszFile,
                 DWORD dwCRCOffset,
                 BYTE *pbHash,
                 DWORD *pcbHash,
                 DWORD cbImage
                 );

DWORD GetCRCOffset(
                   LPCSTR szFile,
                   DWORD cbImage,
                   DWORD *pdwCRCOffset
                   );

static DWORD dwSigInFileVersion = 0x100;

DWORD SetCryptSignatureResource(
                                LPCSTR szFile,
                                DWORD dwSigVersion,
                                DWORD dwCRCOffset,
                                PBYTE pbNewSig,
                                DWORD cbNewSig,
                                DWORD cbImage
                                );

void ShowHelp();
BOOL DumpFile(LPCTSTR szFile, const BYTE *pb, UINT cb);
void CallServer(void);

BYTE pbSignature[256];
BYTE pbDigest[80];
DWORD cbDigestLen = sizeof(pbDigest);
DWORD cbSignatureLen = sizeof(pbSignature);

// The function MACs the given bytes.
void MACBytes(
              IN DESTable *pDESKeyTable,
              IN BYTE *pbData,
              IN DWORD cbData,
              IN OUT BYTE *pbTmp,
              IN OUT DWORD *pcbTmp,
              IN OUT BYTE *pbMAC,
              IN BOOL fFinal
              )
{
    DWORD   cb = cbData;
    DWORD   cbMACed = 0;

    while (cb)
    {
        if ((cb + *pcbTmp) < DES_BLOCKLEN)
        {
            memcpy(pbTmp + *pcbTmp, pbData + cbMACed, cb);
            *pcbTmp += cb;
            break;
        }
        else
        {
            memcpy(pbTmp + *pcbTmp, pbData + cbMACed, DES_BLOCKLEN - *pcbTmp);
            CBC(des, DES_BLOCKLEN, pbMAC, pbTmp, pDESKeyTable,
                ENCRYPT, pbMAC);
            cbMACed = cbMACed + (DES_BLOCKLEN - *pcbTmp);
            cb = cb - (DES_BLOCKLEN - *pcbTmp);
            *pcbTmp = 0;
        }
    }
}

// Given hInst, allocs and returns pointers to MAC pulled from
// resource
BOOL GetResourcePtr(
                    IN HMODULE hInst,
                    IN LPSTR pszRsrcName,
                    OUT BYTE **ppbRsrcMAC,
                    OUT DWORD *pcbRsrcMAC
                    )
{
    HRSRC   hRsrc;
    BOOL    fRet = FALSE;

    // Nab resource handle for our signature
    if (NULL == (hRsrc = FindResourceA(hInst, pszRsrcName,
                                       RT_RCDATA)))
        goto Ret;

    // get a pointer to the actual signature data
    if (NULL == (*ppbRsrcMAC = (PBYTE)LoadResource(hInst, hRsrc)))
        goto Ret;

    // determine the size of the resource
    if (0 == (*pcbRsrcMAC = SizeofResource(hInst, hRsrc)))
        goto Ret;

    fRet = TRUE;
Ret:
    return fRet;
}

#define CSP_TO_BE_MACED_CHUNK  4096

// Given hFile, reads the specified number of bytes (cbToBeMACed) from the file
// and MACs these bytes.  The function does this in chunks.
BOOL MACBytesOfFile(
                     IN HANDLE hFile,
                     IN DWORD cbToBeMACed,
                     IN DESTable *pDESKeyTable,
                     IN BYTE *pbTmp,
                     IN DWORD *pcbTmp,
                     IN BYTE *pbMAC,
                     IN BYTE fFinal
                     )
{
    BYTE    rgbChunk[CSP_TO_BE_MACED_CHUNK];
    DWORD   cbRemaining = cbToBeMACed;
    DWORD   cbToRead;
    DWORD   dwBytesRead;
    BOOL    fRet = FALSE;

    //
    // loop over the file for the specified number of bytes
    // updating the hash as we go.
    //

    while (cbRemaining > 0)
    {
        if (cbRemaining < CSP_TO_BE_MACED_CHUNK)
            cbToRead = cbRemaining;
        else
            cbToRead = CSP_TO_BE_MACED_CHUNK;

        if(!ReadFile(hFile, rgbChunk, cbToRead, &dwBytesRead, NULL))
            goto Ret;
        if (dwBytesRead != cbToRead)
            goto Ret;

        MACBytes(pDESKeyTable, rgbChunk, dwBytesRead, pbTmp, pcbTmp,
                 pbMAC, fFinal);
        cbRemaining -= cbToRead;
    }

    fRet = TRUE;
Ret:
    return fRet;
}


BOOL MACTheFileWithSig(
                       LPCSTR pszImage,
                       DWORD cbImage,
                       IN DWORD dwMACVersion,
                       IN DWORD dwCRCOffset,
                       OUT BYTE *pbMAC
                       )
{
    HMODULE                     hInst = 0;
    MEMORY_BASIC_INFORMATION    MemInfo;
    BYTE                        *pbRsrcMAC;
    DWORD                       cbRsrcMAC;
    BYTE                        *pbRsrcSig;
    DWORD                       cbRsrcSig;
    BYTE                        *pbStart;
    BYTE                        rgbMAC[DES_BLOCKLEN];
    BYTE                        rgbZeroMAC[DES_BLOCKLEN + sizeof(DWORD) * 2];
    BYTE                        rgbZeroSig[144];
    BYTE                        *pbPostCRC;   // pointer to just after CRC
    DWORD                       cbCRCToRsrc1; // number of bytes from CRC to first rsrc
    DWORD                       cbRsrc1ToRsrc2; // number of bytes from first rsrc to second
    DWORD                       cbPostRsrc;    // size - (already hashed + signature size)
    BYTE                        *pbRsrc1ToRsrc2;
    BYTE                        *pbPostRsrc;
    BYTE                        *pbZeroRsrc1;
    BYTE                        *pbZeroRsrc2;
    DWORD                       cbZeroRsrc1;
    DWORD                       cbZeroRsrc2;
    DWORD                       dwZeroCRC = 0;
    DWORD                       dwBytesRead = 0;
    OFSTRUCT                    ImageInfoBuf;
    HFILE                       hFile = HFILE_ERROR;
    HANDLE                      hMapping = NULL;
    DESTable                    DESKeyTable;
    BYTE                        rgbTmp[DES_BLOCKLEN];
    DWORD                       cbTmp = 0;
    BOOL                        fRet = FALSE;

    memset(&MemInfo, 0, sizeof(MemInfo));
    memset(rgbMAC, 0, sizeof(rgbMAC));
    memset(rgbTmp, 0, sizeof(rgbTmp));

    // Load the file
    if (HFILE_ERROR == (hFile = OpenFile(pszImage, &ImageInfoBuf,
                                         OF_READ)))
    {
        goto Ret;
    }

    hMapping = CreateFileMapping((HANDLE)IntToPtr(hFile),
                                  NULL,
                                  PAGE_READONLY,
                                  0,
                                  0,
                                  NULL);
    if(hMapping == NULL)
    {
        goto Ret;
    }

    hInst = (HMODULE)MapViewOfFile(hMapping,
                          FILE_MAP_READ,
                          0,
                          0,
                          0);
    if(hInst == NULL)
    {
        goto Ret;
    }
    pbStart = (BYTE*)hInst;

    // Convert pointer to HMODULE, using the same scheme as
    // LoadLibrary (windows\base\client\module.c).
    *((ULONG_PTR*)&hInst) |= 0x00000001;

    // the MAC resource
    if (!GetResourcePtr(hInst, MAC_RESOURCE_NUMBER, &pbRsrcMAC, &cbRsrcMAC))
        goto Ret;

    // the MAC resource
    if (!GetResourcePtr(hInst, CRYPT_SIG_RESOURCE_NUMBER, &pbRsrcSig, &cbRsrcSig))
        goto Ret;

    if (cbRsrcMAC < (sizeof(DWORD) * 2))
        goto Ret;

    // create a zero byte MAC
    memset(rgbZeroMAC, 0, sizeof(rgbZeroMAC));

    // create a zero byte Sig
    memset(rgbZeroSig, 0, sizeof(rgbZeroSig));

    // set up the pointers
    pbPostCRC = pbStart + dwCRCOffset + sizeof(DWORD);
    if (pbRsrcSig > pbRsrcMAC)    // MAC is first Rsrc
    {
        cbCRCToRsrc1 = (DWORD)(pbRsrcMAC - pbPostCRC);
        pbRsrc1ToRsrc2 = pbRsrcMAC + cbRsrcMAC;
        cbRsrc1ToRsrc2 = (DWORD)(pbRsrcSig - pbRsrc1ToRsrc2);
        pbPostRsrc = pbRsrcSig + cbRsrcSig;
        cbPostRsrc = (cbImage - (DWORD)(pbPostRsrc - pbStart));

        // zero pointers
        pbZeroRsrc1 = rgbZeroMAC;
        cbZeroRsrc1 = cbRsrcMAC;
        pbZeroRsrc2 = rgbZeroSig;
        cbZeroRsrc2 = cbRsrcSig;
    }
    else                        // Sig is first Rsrc
    {
        cbCRCToRsrc1 = (DWORD)(pbRsrcSig - pbPostCRC);
        pbRsrc1ToRsrc2 = pbRsrcSig + cbRsrcSig;
        cbRsrc1ToRsrc2 = (DWORD)(pbRsrcMAC - pbRsrc1ToRsrc2);
        pbPostRsrc = pbRsrcMAC + cbRsrcMAC;
        cbPostRsrc = (cbImage - (DWORD)(pbPostRsrc - pbStart));

        // zero pointers
        pbZeroRsrc1 = rgbZeroSig;
        cbZeroRsrc1 = cbRsrcSig;
        pbZeroRsrc2 = rgbZeroMAC;
        cbZeroRsrc2 = cbRsrcMAC;
    }

    // init the key table
    deskey(&DESKeyTable, rgbDESKey);

    // MAC up to the CRC
    if (!MACBytesOfFile((HANDLE)IntToPtr(hFile), dwCRCOffset, &DESKeyTable, rgbTmp,
                        &cbTmp, rgbMAC, FALSE))
    {
        goto Ret;
    }

    // pretend CRC is zeroed
    MACBytes(&DESKeyTable, (BYTE*)&dwZeroCRC, sizeof(DWORD), rgbTmp, &cbTmp,
             rgbMAC, FALSE);
    if (!SetFilePointer((HANDLE)IntToPtr(hFile), sizeof(DWORD), NULL, FILE_CURRENT))
    {
        goto Ret;
    }

    // MAC from CRC to first resource
    if (!MACBytesOfFile((HANDLE)IntToPtr(hFile), cbCRCToRsrc1, &DESKeyTable, rgbTmp,
                        &cbTmp, rgbMAC, FALSE))
    {
        goto Ret;
    }

    // pretend image has zeroed first resource
    MACBytes(&DESKeyTable, (BYTE*)pbZeroRsrc1, cbZeroRsrc1, rgbTmp, &cbTmp,
             rgbMAC, FALSE);
    if (!SetFilePointer((HANDLE)IntToPtr(hFile), cbZeroRsrc1, NULL, FILE_CURRENT))
    {
        goto Ret;
    }

    // MAC from first resource to second
    if (!MACBytesOfFile((HANDLE)IntToPtr(hFile), cbRsrc1ToRsrc2, &DESKeyTable, rgbTmp,
                        &cbTmp, rgbMAC, FALSE))
    {
        goto Ret;
    }

    // pretend image has zeroed second Resource
    MACBytes(&DESKeyTable, (BYTE*)pbZeroRsrc2, cbZeroRsrc2, rgbTmp, &cbTmp,
             rgbMAC, FALSE);
    if (!SetFilePointer((HANDLE)IntToPtr(hFile), cbZeroRsrc2, NULL, FILE_CURRENT))
    {
        goto Ret;
    }

    // MAC after the resource
    if (!MACBytesOfFile((HANDLE)IntToPtr(hFile), cbPostRsrc, &DESKeyTable, rgbTmp, &cbTmp,
                        rgbMAC, TRUE))
    {
        goto Ret;
    }

    memcpy(pbMAC, rgbMAC, DES_BLOCKLEN);

    fRet = TRUE;
Ret:
    if(hInst)
        UnmapViewOfFile(hInst);
    if(hMapping)
        CloseHandle(hMapping);
    if (HFILE_ERROR != hFile)
        _lclose(hFile);

    return fRet;
}

// SetCryptMACResource
//
// slams MAC resource in file with the new MAC
//
DWORD SetCryptMACResource(
                          IN LPCSTR szFile,
                          IN DWORD dwMACVersion,
                          IN DWORD dwCRCOffset,
                          IN PBYTE pbNewMAC,
                          IN DWORD cbImage
                          )
{
    DWORD   dwErr = 0x1;

    HANDLE  hFileProv = NULL;
    HMODULE hInst = NULL;

    PBYTE   pbFilePtr = NULL;
    DWORD   cbImageSize, cbMACOffset;

    PBYTE   pbMAC;
    DWORD   cbMAC;

    MEMORY_BASIC_INFORMATION    MemInfo;
    BYTE                        *pbStart;

    DWORD               OldCheckSum;
    DWORD               NewCheckSum;
    PIMAGE_NT_HEADERS   pImageNTHdrs;

    HANDLE  hFileMap = NULL;

    memset(&MemInfo, 0, sizeof(MemInfo));

    // Load the file as a datafile
    if (NULL == (hInst = LoadLibraryEx(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE)))
    {
        printf("Couldn't load file\n");
        goto Ret;
    }
    if (!GetResourcePtr(hInst, MAC_RESOURCE_NUMBER, &pbMAC, &cbMAC))
    {
        printf("Couldn't find MAC placeholder\n");
        goto Ret;
    }

    // get image start address
    VirtualQuery(hInst, &MemInfo, sizeof(MemInfo));
    pbStart = (BYTE*)MemInfo.BaseAddress;

    FreeLibrary(hInst); hInst = NULL;

    cbMACOffset = (DWORD)(pbMAC - pbStart);

    if (cbMAC != (DES_BLOCKLEN + sizeof(DWORD) * 2))
    {
        printf("Attempt to replace %d zeros with new MAC!\n", cbMAC);
        goto Ret;
    }

    if (INVALID_HANDLE_VALUE == (hFileProv = CreateFile(szFile,
                                GENERIC_READ | GENERIC_WRITE,
                                0,  // don't share
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                0)))
    {
        printf("Couldn't CreateFile: 0x%x\n", GetLastError());
        goto Ret;
    }

    if (NULL == (hFileMap = CreateFileMapping(
                                hFileProv,
                                NULL,
                                PAGE_READWRITE,
                                0,
                                0,
                                NULL)))
    {
        printf("Couldn't map file\n");
        goto Ret;
    }

    if (NULL == (pbFilePtr = (PBYTE)MapViewOfFile(
                                hFileMap,
                                FILE_MAP_ALL_ACCESS,
                                0,
                                0,
                                0)))
    {
        printf("Couldn't create view\n");
        goto Ret;
    }

    // copy version, CRC offset and new sig
    CopyMemory(pbFilePtr+cbMACOffset, &dwMACVersion, sizeof(dwMACVersion));
    cbMACOffset += sizeof(dwMACVersion);
    CopyMemory(pbFilePtr+cbMACOffset, &dwCRCOffset, sizeof(dwCRCOffset));
    cbMACOffset += sizeof(dwCRCOffset);
    CopyMemory(pbFilePtr+cbMACOffset, pbNewMAC, DES_BLOCKLEN);

    // compute a new checksum
    if (NULL == (pImageNTHdrs = CheckSumMappedFile(pbFilePtr, cbImage,
                                                   &OldCheckSum, &NewCheckSum)))
        goto Ret;

    CopyMemory(&pImageNTHdrs->OptionalHeader.CheckSum, &NewCheckSum, sizeof(DWORD));

    if (NULL == (pImageNTHdrs = CheckSumMappedFile(pbFilePtr, cbImage,
                                                   &OldCheckSum, &NewCheckSum)))
        goto Ret;

    if (OldCheckSum != NewCheckSum)
        goto Ret;

    dwErr = ERROR_SUCCESS;
Ret:
    if (pbFilePtr)
        UnmapViewOfFile(pbFilePtr);

    if (hFileMap)
        CloseHandle(hFileMap);

    if (hInst)
        FreeLibrary(hInst);

    if (hFileProv)
        CloseHandle(hFileProv);

    return dwErr;
}

void MacCSP(
            LPCSTR pszInFile
            )
{
    DWORD   cbImage;
    DWORD   dwCRCOffset;
    HANDLE  hFileProv = INVALID_HANDLE_VALUE;
    BYTE    rgbMAC[DES_BLOCKLEN];
    HMODULE hInst = NULL;

    PBYTE   pbFilePtr = NULL;
    DWORD   cbImageSize, cbMACOffset;

    PBYTE   pbMAC;
    DWORD   cbMAC;

    memset(rgbMAC, 0, sizeof(rgbMAC));

    // check if the MAC resource is in the CSP and exit if not
    // Load the file as a datafile
    if (NULL == (hInst = LoadLibraryEx(pszInFile,
                                       NULL,
                                       LOAD_LIBRARY_AS_DATAFILE)))
    {
        printf("Couldn't load file\n");
        goto Ret;
    }
    if (!GetResourcePtr(hInst, MAC_RESOURCE_NUMBER, &pbMAC, &cbMAC))
    {
        goto Ret;
    }
    FreeLibrary(hInst);
    hInst = NULL;

    // get the file size
    if ((hFileProv = CreateFile(pszInFile,
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                0)) == INVALID_HANDLE_VALUE)
    {
        printf("CSP specified was not found!\n");
        goto Ret;
    }

    if (0xffffffff == (cbImage = GetFileSize(hFileProv, NULL)))
    {
        printf("CSP specified was not found!\n");
        goto Ret;
    }

    CloseHandle(hFileProv);
    hFileProv = NULL;

    if (0 != GetCRCOffset(pszInFile, cbImage, &dwCRCOffset))
    {
        printf("Unable to get CRC!\n");
        goto Ret;
    }

    // calculate the MAC
    if (!MACTheFileWithSig(pszInFile,
                           cbImage,
                           dwMACInFileVersion,
                           dwCRCOffset,
                           rgbMAC))
    {
        printf("MAC failed!\n");
        goto Ret;
    }

    //
    // Place the MAC into the resource in the file
    //

    if (ERROR_SUCCESS != SetCryptMACResource(pszInFile,
                                             dwMACInFileVersion,
                                             dwCRCOffset,
                                             rgbMAC,
                                             cbImage))
    {
        printf("Unable to set the MAC into the file resource!\n");
        goto Ret;
    }
Ret:
    if (hInst)
    {
        FreeLibrary(hInst);
    }

    if (INVALID_HANDLE_VALUE != hFileProv)
    {
        CloseHandle(hFileProv);
    }

    return;
}

/*++

main:

    This is the main entry point of the application.

Arguments:

    argc - Count of arguments
    argv - array of arguments

Return Value:

    0 - Success
    1 - Error

Author:

    Doug Barlow (dbarlow) 1/25/1996

--*/
extern "C" void __cdecl
main(
    int argc,
    char *argv[])
{
    DWORD exStatus = 1;
    DWORD index;
    LPCTSTR szBinFile = NULL;
    LPCTSTR szInFile = NULL;
    BOOL fOutput = FALSE;
    int status;
    DWORD ThreadId;
    HANDLE hThread;

    // RPC Specific variables.
    RPC_STATUS rpcStatus;
    unsigned char * pszUuid = NULL;
    char * pszProtocolSequence = "ncacn_np";
    unsigned char * pszNetworkAddress   = (LPBYTE)"\\\\enigma.ntdev.microsoft.com";
    char * pszEndpoint = "\\pipe\\sign";
    unsigned char * pszOptions      = NULL;
    unsigned char * pszStringBinding    = NULL;
    DWORD dwrt;
    DWORD i;
    DWORD cbImage;
    DWORD dwCRCOffset;
    HANDLE hFileProv = 0;

    //
    // Parse the command line.
    //

    if ((argc != 2) || (argv[1][0] == '?'))
    {
        ShowHelp();
        exStatus = 0;
        goto ErrorExit;
    }

    szInFile = &argv[1][0];

    //
    // Command consistency checks.
    //

    if (NULL == szInFile)
    {
        printf("No input file specified.\n");
        goto ErrorExit;
    }

    MacCSP(szInFile);

    // get the file size
    if ((hFileProv = CreateFile(szInFile,
                                GENERIC_READ,
                                FILE_SHARE_READ,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                0)) == INVALID_HANDLE_VALUE)
    {
        printf("CSP specified was not found!\n");
        goto ErrorExit;
    }

    if (0xffffffff == (cbImage = GetFileSize(hFileProv, NULL)))
    {
        printf("CSP specified was not found!\n");
        goto ErrorExit;
    }

    CloseHandle(hFileProv);
    hFileProv = NULL;

    if (ERROR_SUCCESS != GetCRCOffset(szInFile, cbImage, &dwCRCOffset))
    {
        printf("Unable to get the CRC offset on the file!\n");
        goto ErrorExit;
    }

    //
    // Compute the hash.
    //

    if (!HashTheFile(szInFile, dwCRCOffset, pbDigest, &cbDigestLen, cbImage))
    {
        printf("Unable to hash the file!\n");
        goto ErrorExit;
    }

    //
    // Get the signature.
    //

    // Try to make rpc connection
    rpcStatus = RpcStringBindingCompose(pszUuid,
                     (unsigned char *) pszProtocolSequence,
                     pszNetworkAddress,
                     (unsigned char *) pszEndpoint,
                     pszOptions,
                     &pszStringBinding);

#ifdef DEBUG
    printf("RpcStringBindingCompose returned 0x%x\n", rpcStatus);
    printf("pszStringBinding = %s\n", pszStringBinding);
#endif
    if (0 != rpcStatus)
    {
        printf("Failed to compose binding string for target RPC server.\n");
        goto ErrorExit;
    }

    /* Set the binding handle that will */
    /* be used to bind to the server  */
    rpcStatus = RpcBindingFromStringBinding(pszStringBinding,
                     &hello_IfHandle);

#ifdef DEBUG
    printf("RpcBindingFromStringBinding returned 0x%x\n", rpcStatus);
#endif
    if (0 != rpcStatus)
    {
        printf("Failed to bind to target RPC server.\n");
        goto ErrorExit;
    }

    if ((hThread = CreateThread(NULL,
                                0,
                                (LPTHREAD_START_ROUTINE) CallServer,
                                NULL,
                                0,
                                &ThreadId)) == NULL)
    {
        printf("Call to CreateThread failed\n");
        goto ErrorExit;
    }

    printf("Sending request to be signed, will wait 5 minutes\n");
    for (i = 0; i < 20; i++)
    {
        printf("Waited   %d  seconds\n", i*30);
        dwrt = WaitForSingleObject(hThread, 15000);
        if (dwrt == WAIT_OBJECT_0)
        {
            break;
        }
    }

    if (i == 20)
    {
        printf("Call to Server timed out\n");
        goto ErrorExit;
    }

    GetExitCodeThread(hThread, &dwrt);

    if (dwrt)
    {
        goto ErrorExit;
    }

    //
    // Place the signature into the resource in the file
    //

    if (ERROR_SUCCESS != SetCryptSignatureResource(szInFile, dwSigInFileVersion,
                                                   dwCRCOffset, pbSignature,
                                                   cbSignatureLen, cbImage))
    {
        printf("Unable to set the signature into the file resource!\n");
        goto ErrorExit;
    }

    //
    // Clean up and return.
    //

    exStatus = 0;


ErrorExit:
    exit(exStatus);

}

void CallServer(void)
{
    RpcTryExcept
    {
        cbSignatureLen = sizeof(pbSignature);
        if (GenSignature(pbDigest, &cbSignatureLen, pbSignature))
        {
            printf("GenSignature returned an error \n");
            ExitThread(TRUE);
        }
    }
    RpcExcept(1)
    {
        printf("RPC error -- exception code is  0x%X\n", RpcExceptionCode());
        ExitThread(RpcExceptionCode());
    }
    RpcEndExcept

    ExitThread(FALSE);

}

// Given hInst, allocs and returns pointers to signature pulled from
// resource
BOOL GetCryptSigResourcePtr(
                            HMODULE hInst,
                            BYTE **ppbRsrcSig,
                            DWORD *pcbRsrcSig
                            )
{
    HRSRC   hRsrc;
    BOOL    fRet = FALSE;

    // Nab resource handle for our signature
    if (NULL == (hRsrc = FindResource(hInst, CRYPT_SIG_RESOURCE_NUMBER,
                                      RT_RCDATA)))
        goto Ret;

    // get a pointer to the actual signature data
    if (NULL == (*ppbRsrcSig = (PBYTE)LoadResource(hInst, hRsrc)))
        goto Ret;

    // determine the size of the resource
    if (0 == (*pcbRsrcSig = SizeofResource(hInst, hRsrc)))
        goto Ret;

    fRet = TRUE;
Ret:
    return fRet;
}

// Given hInst, hashes over the file skipping, hashing zero bytes where the
// resource is
BOOL HashTheFile(
                 LPCSTR pszFile,
                 DWORD dwCRCOffset,
                 BYTE *pbHash,
                 DWORD *pcbHash,
                 DWORD cbImage
                 )
{
    MEMORY_BASIC_INFORMATION    MemInfo;
    BYTE                        *pbRsrcSig;
    DWORD                       cbRsrcSig;
    BYTE                        *pbStart;
    BYTE                        *pbZeroSig = NULL;
    MD5_CTX                     MD5Hash;
    DWORD                       dwZeroCRC = 0;
    DWORD                       cbPreCRC;     // start of CRC
    DWORD                       cbCRCToSig;   // end of CRC to start of sig
    BYTE                        *pbPostCRC;   // just after CRC
    DWORD                       cbPostSig;    // size - (already hashed + signature size)
    BYTE                        *pbPostSig;
    HMODULE                     hInst = 0;
    BOOL                        fRet = FALSE;

    memset(&MD5Hash, 0, sizeof(MD5Hash));
    memset(&MemInfo, 0, sizeof(MemInfo));

    // Load the file as a datafile
    if (NULL == (hInst = LoadLibraryEx(pszFile, NULL, LOAD_LIBRARY_AS_DATAFILE)))
        goto Ret;

    // get image start address
    VirtualQuery(hInst, &MemInfo, sizeof(MemInfo));
    pbStart = (BYTE*)MemInfo.BaseAddress;

    // the resources signature
    if (!GetCryptSigResourcePtr(hInst, &pbRsrcSig, &cbRsrcSig))
        goto Ret;

    // create a zero byte signature
    if (NULL == (pbZeroSig = (BYTE*)LocalAlloc(LMEM_ZEROINIT, cbRsrcSig)))
        goto Ret;

    // want to hash the version and the CRC offset
    CopyMemory(pbZeroSig, &dwSigInFileVersion, sizeof(dwSigInFileVersion));
    CopyMemory(pbZeroSig + sizeof(dwSigInFileVersion), &dwCRCOffset,
               sizeof(dwCRCOffset));

    // hash over the relevant data
    {
        pbPostCRC = pbStart + dwCRCOffset + sizeof(dwZeroCRC);
        cbCRCToSig = (DWORD)(pbRsrcSig - pbPostCRC);
        pbPostSig = pbRsrcSig + cbRsrcSig;
        cbPostSig = (cbImage - (DWORD)(pbPostSig - pbStart));

        MD5Init(&MD5Hash);

        MD5Update(&MD5Hash, pbStart, dwCRCOffset);

        // pretend CRC is zeroed
        MD5Update(&MD5Hash, (BYTE*)&dwZeroCRC, sizeof(dwZeroCRC));

        // pretend image has zeroed sig
        MD5Update(&MD5Hash, pbPostCRC, cbCRCToSig);

        // pretend image has zeroed sig
        MD5Update(&MD5Hash, pbZeroSig, cbRsrcSig);

        MD5Update(&MD5Hash, pbPostSig, cbPostSig);

        // Finish the hash
        MD5Final(&MD5Hash);

        *pcbHash = MD5DIGESTLEN;
        memcpy(pbHash, MD5Hash.digest, MD5DIGESTLEN);
    }

    fRet = TRUE;
Ret:
    if (hInst)
        FreeLibrary(hInst);
    if (pbZeroSig)
        LocalFree(pbZeroSig);
    return fRet;
}

DWORD GetCRCOffset(
                   LPCSTR szFile,
                   DWORD cbImage,
                   DWORD *pdwCRCOffset
                   )
{
    DWORD               dwErr = 0x1;

    HANDLE              hFileProv = NULL;
    PBYTE               pbFilePtr = NULL;
    DWORD               OldCheckSum;
    DWORD               NewCheckSum;
    PIMAGE_NT_HEADERS   pImageNTHdrs;

    HANDLE              hFileMap = NULL;


    if (INVALID_HANDLE_VALUE == (hFileProv = CreateFile(szFile,
                                GENERIC_READ | GENERIC_WRITE,
                                0,  // don't share
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                0)))
    {
        printf("Couldn't CreateFile: 0x%x\n", GetLastError());
        goto Ret;
    }

    if (NULL == (hFileMap = CreateFileMapping(
                                hFileProv,
                                NULL,
                                PAGE_READWRITE,
                                0,
                                0,
                                NULL)))
    {
        printf("Couldn't map file\n");
        goto Ret;
    }

    if (NULL == (pbFilePtr = (PBYTE)MapViewOfFile(
                                hFileMap,
                                FILE_MAP_ALL_ACCESS,
                                0,
                                0,
                                0)))
    {
        printf("Couldn't create view\n");
        goto Ret;
    }

    // compute a new checksum
    if (NULL == (pImageNTHdrs = CheckSumMappedFile(pbFilePtr, cbImage,
                                                   &OldCheckSum, &NewCheckSum)))
        goto Ret;

    *pdwCRCOffset = (DWORD)((BYTE*)&pImageNTHdrs->OptionalHeader.CheckSum - pbFilePtr);
    dwErr = ERROR_SUCCESS;
Ret:
    if (pbFilePtr)
        UnmapViewOfFile(pbFilePtr);

    if (hFileMap)
        CloseHandle(hFileMap);

    if (hFileProv)
        CloseHandle(hFileProv);

    return dwErr;
}

// SetCryptSignatureResource
//
// slams signature resource in file with the new signature
//
// szFile is file to modify
// pbNewSig is new signature
// cbNewSig is new signature length
DWORD SetCryptSignatureResource(
                                LPCSTR szFile,
                                DWORD dwSigVersion,
                                DWORD dwCRCOffset,
                                PBYTE pbNewSig,
                                DWORD cbNewSig,
                                DWORD cbImage
                                )
{
    DWORD   dwErr = 0x1;

    HANDLE  hFileProv = NULL;
    HMODULE hInst = NULL;

    PBYTE   pbFilePtr = NULL;
    DWORD   cbImageSize, cbSigOffset;

    PBYTE   pbSig;
    DWORD   cbSig;

    MEMORY_BASIC_INFORMATION    MemInfo;
    BYTE                        *pbStart;

    DWORD               OldCheckSum;
    DWORD               NewCheckSum;
    PIMAGE_NT_HEADERS   pImageNTHdrs;

    HANDLE  hFileMap = NULL;

    memset(&MemInfo, 0, sizeof(MemInfo));

    // Load the file as a datafile
    if (NULL == (hInst = LoadLibraryEx(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE)))
    {
        printf("Couldn't load file\n");
        goto Ret;
    }
    if (!GetCryptSigResourcePtr(hInst,
                                &pbSig,
                                &cbSig))
    {
        printf("Couldn't find signature placeholder\n");
        goto Ret;
    }

    // get image start address
    VirtualQuery(hInst, &MemInfo, sizeof(MemInfo));
    pbStart = (BYTE*)MemInfo.BaseAddress;

    FreeLibrary(hInst); hInst = NULL;

    cbSigOffset = (DWORD)(pbSig - pbStart);

    if (cbSig < (cbNewSig + (sizeof(DWORD) * 2)))
    {
        printf("Attempt to replace %d zeros with %d byte signature!\n", cbSig, cbNewSig);
        goto Ret;
    }

    if (INVALID_HANDLE_VALUE == (hFileProv = CreateFile(szFile,
                                GENERIC_READ | GENERIC_WRITE,
                                0,  // don't share
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                0)))
    {
        printf("Couldn't CreateFile: 0x%x\n", GetLastError());
        goto Ret;
    }

    if (NULL == (hFileMap = CreateFileMapping(
                                hFileProv,
                                NULL,
                                PAGE_READWRITE,
                                0,
                                0,
                                NULL)))
    {
        printf("Couldn't map file\n");
        goto Ret;
    }

    if (NULL == (pbFilePtr = (PBYTE)MapViewOfFile(
                                hFileMap,
                                FILE_MAP_ALL_ACCESS,
                                0,
                                0,
                                0)))
    {
        printf("Couldn't create view\n");
        goto Ret;
    }

    // copy version, CRC offset and new sig
    CopyMemory(pbFilePtr+cbSigOffset, &dwSigVersion, sizeof(dwSigVersion));
    cbSigOffset += sizeof(dwSigVersion);
    CopyMemory(pbFilePtr+cbSigOffset, &dwCRCOffset, sizeof(dwCRCOffset));
    cbSigOffset += sizeof(dwCRCOffset);
    CopyMemory(pbFilePtr+cbSigOffset, pbNewSig, cbNewSig);
    if (cbSig > (cbNewSig + (sizeof(DWORD) * 2)))
    {
        ZeroMemory(pbFilePtr+cbSigOffset+cbNewSig,
                   cbSig - (cbNewSig + (sizeof(DWORD) * 2)));
    }

    // compute a new checksum
    if (NULL == (pImageNTHdrs = CheckSumMappedFile(pbFilePtr, cbImage,
                                                   &OldCheckSum, &NewCheckSum)))
        goto Ret;

    CopyMemory(&pImageNTHdrs->OptionalHeader.CheckSum, &NewCheckSum, sizeof(DWORD));

    if (NULL == (pImageNTHdrs = CheckSumMappedFile(pbFilePtr, cbImage,
                                                   &OldCheckSum, &NewCheckSum)))
        goto Ret;

    if (OldCheckSum != NewCheckSum)
        goto Ret;

    dwErr = ERROR_SUCCESS;
Ret:
    if (pbFilePtr)
        UnmapViewOfFile(pbFilePtr);

    if (hFileMap)
        CloseHandle(hFileMap);

    if (hInst)
        FreeLibrary(hInst);

    if (hFileProv)
        CloseHandle(hFileProv);

    return dwErr;
}


/*++

ShowHelp:

    This routine displays a short help message to the given output stream.

Arguments:

    ostr - The output stream to receive the help message.

Return Value:

    None

Author:

    Doug Barlow (dbarlow) 7/21/1995

--*/

void
ShowHelp()
{
    printf("CryptoAPI Internal CSP Signing Utility\n");
    printf("signcsp <filename>\n");
}

/* MIDL allocate and free */

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    return(malloc(len));
}

void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    free(ptr);
}