//////////////////////////////////////////////////////////////////////////////
//
//  Image manipulation functions (image.cpp of detours.lib)
//
//  Microsoft Research Detours Package, Version 4.0.1
//
//  Copyright (c) Microsoft Corporation.  All rights reserved.
//
//  Used for for payloads, byways, and imports.
//
 
#if _MSC_VER < 1299
#pragma warning(disable: 4710)
#endif
 
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
 
#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
 
namespace Detour
{
//////////////////////////////////////////////////////////////////////////////
//
#ifndef _STRSAFE_H_INCLUDED_
_Must_inspect_result_
static inline HRESULT StringCchLengthA(
    _In_reads_or_z_(cchMax) LPCSTR psz,
    _In_
    _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax,
    _Out_opt_
    _Deref_out_range_(<, cchMax)
    _Deref_out_range_(<=, _String_length_(psz))
    _Out_ size_t* pcch)
{
    HRESULT hr = S_OK;
    size_t cchMaxPrev = cchMax;
 
    if (cchMax > 2147483647) {
        return ERROR_INVALID_PARAMETER;
    }
 
    while (cchMax && (*psz != '\0')) {
        psz++;
        cchMax--;
    }
 
    if (cchMax == 0) {
        // the string is longer than cchMax
        hr = ERROR_INVALID_PARAMETER;
    }
 
    if (SUCCEEDED(hr) && pcch) {
        *pcch = cchMaxPrev - cchMax;
    }
 
    return hr;
}
 
_Must_inspect_result_
static inline HRESULT StringCchCopyA(
    _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest,
    _In_ size_t cchDest,
    _In_ LPCSTR pszSrc)
{
    HRESULT hr = S_OK;
 
    if (cchDest == 0) {
        // can not null terminate a zero-byte dest buffer
        hr = ERROR_INVALID_PARAMETER;
    }
    else {
        while (cchDest && (*pszSrc != '\0')) {
            *pszDest++ = *pszSrc++;
            cchDest--;
        }
 
        if (cchDest == 0) {
            // we are going to truncate pszDest
            pszDest--;
            hr = ERROR_INVALID_PARAMETER;
        }
 
        *pszDest= '\0';
    }
 
    return hr;
}
 
_Must_inspect_result_
static inline HRESULT StringCchCatA(
    _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest,
    _In_ size_t cchDest,
    _In_ LPCSTR pszSrc)
{
    HRESULT hr;
    size_t cchDestCurrent;
 
    if (cchDest > 2147483647){
        return ERROR_INVALID_PARAMETER;
    }
 
    hr = StringCchLengthA(pszDest, cchDest, &cchDestCurrent);
 
    if (SUCCEEDED(hr) && cchDestCurrent < cchDest) {
        hr = StringCchCopyA(pszDest + cchDestCurrent,
                            cchDest - cchDestCurrent,
                            pszSrc);
    }
 
    return hr;
}
 
#endif
 
///////////////////////////////////////////////////////////////////////////////
//
class CImageData
{
    friend class CImage;
 
public:
    CImageData(PBYTE pbData, DWORD cbData);
    ~CImageData();
 
    PBYTE                   Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator);
    PBYTE                   Find(REFGUID rguid, DWORD *pcbData);
    PBYTE                   Set(REFGUID rguid, PBYTE pbData, DWORD cbData);
 
    BOOL                    Delete(REFGUID rguid);
    BOOL                    Purge();
 
    BOOL                    IsEmpty()           { return m_cbData == 0; }
    BOOL                    IsValid();
 
protected:
    BOOL                    SizeTo(DWORD cbData);
 
protected:
    _Field_size_(m_cbAlloc)
    PBYTE                   m_pbData;
    DWORD                   m_cbData;
    DWORD                   m_cbAlloc;
};
 
class CImageImportName;
 
class CImageImportFile
{
    friend class CImage;
    friend class CImageImportName;
 
public:
    CImageImportFile();
    ~CImageImportFile();
 
public:
    CImageImportFile *      m_pNextFile;
    BOOL                    m_fByway;
 
    _Field_size_(m_nImportNames)
    CImageImportName *      m_pImportNames;
    DWORD                   m_nImportNames;
 
    DWORD                   m_rvaOriginalFirstThunk;
    DWORD                   m_rvaFirstThunk;
 
    DWORD                   m_nForwarderChain;
    LPCSTR                  m_pszOrig;
    LPCSTR                  m_pszName;
};
 
class CImageImportName
{
    friend class CImage;
    friend class CImageImportFile;
 
public:
    CImageImportName();
    ~CImageImportName();
 
public:
    WORD        m_nHint;
    ULONG       m_nOrig;
    ULONG       m_nOrdinal;
    LPCSTR      m_pszOrig;
    LPCSTR      m_pszName;
};
 
class CImage
{
    friend class CImageThunks;
    friend class CImageChars;
    friend class CImageImportFile;
    friend class CImageImportName;
 
public:
    CImage();
    ~CImage();
 
    static CImage *         IsValid(PDETOUR_BINARY pBinary);
 
public:                                                 // File Functions
    BOOL                    Read(HANDLE hFile);
    BOOL                    Write(HANDLE hFile);
    BOOL                    Close();
 
public:                                                 // Manipulation Functions
    PBYTE                   DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator);
    PBYTE                   DataFind(REFGUID rguid, DWORD *pcbData);
    PBYTE                   DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData);
    BOOL                    DataDelete(REFGUID rguid);
    BOOL                    DataPurge();
 
    BOOL                    EditImports(PVOID pContext,
                                        PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,
                                        PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,
                                        PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,
                                        PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback);
 
protected:
    BOOL                    WriteFile(HANDLE hFile,
                                      LPCVOID lpBuffer,
                                      DWORD nNumberOfBytesToWrite,
                                      LPDWORD lpNumberOfBytesWritten);
    BOOL                    CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData);
    BOOL                    ZeroFileData(HANDLE hFile, DWORD cbData);
    BOOL                    AlignFileData(HANDLE hFile);
 
    BOOL                    SizeOutputBuffer(DWORD cbData);
    PBYTE                   AllocateOutput(DWORD cbData, DWORD *pnVirtAddr);
 
    PVOID                   RvaToVa(ULONG_PTR nRva);
    DWORD                   RvaToFileOffset(DWORD nRva);
 
    DWORD                   FileAlign(DWORD nAddr);
    DWORD                   SectionAlign(DWORD nAddr);
 
    BOOL                    CheckImportsNeeded(DWORD *pnTables,
                                               DWORD *pnThunks,
                                               DWORD *pnChars);
 
    CImageImportFile *      NewByway(_In_ LPCSTR pszName);
 
private:
    DWORD                   m_dwValidSignature;
    CImageData *            m_pImageData;               // Read & Write
 
    HANDLE                  m_hMap;                     // Read & Write
    PBYTE                   m_pMap;                     // Read & Write
 
    DWORD                   m_nNextFileAddr;            // Write
    DWORD                   m_nNextVirtAddr;            // Write
 
    IMAGE_DOS_HEADER        m_DosHeader;                // Read & Write
    IMAGE_NT_HEADERS        m_NtHeader;                 // Read & Write
    IMAGE_SECTION_HEADER    m_SectionHeaders[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
 
    DWORD                   m_nPrePE;
    DWORD                   m_cbPrePE;
    DWORD                   m_cbPostPE;
 
    DWORD                   m_nPeOffset;
    DWORD                   m_nSectionsOffset;
    DWORD                   m_nExtraOffset;
    DWORD                   m_nFileSize;
 
    DWORD                   m_nOutputVirtAddr;
    DWORD                   m_nOutputVirtSize;
    DWORD                   m_nOutputFileAddr;
 
    _Field_size_(m_cbOutputBuffer)
    PBYTE                   m_pbOutputBuffer;
    DWORD                   m_cbOutputBuffer;
 
    CImageImportFile *      m_pImportFiles;
    DWORD                   m_nImportFiles;
 
    BOOL                    m_fHadDetourSection;
 
private:
    enum {
        DETOUR_IMAGE_VALID_SIGNATURE = 0xfedcba01,      // "Dtr\0"
    };
};
 
//////////////////////////////////////////////////////////////////////////////
//
static BYTE s_rbDosCode[0x10] = {
    0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD,
    0x21,0xB8,0x01,0x4C,0xCD,0x21,'*','*'
};
 
static inline DWORD Max(DWORD a, DWORD b)
{
    return a > b ? a : b;
}
 
static inline DWORD Align(DWORD a, DWORD size)
{
    size--;
    return (a + size) & ~size;
}
 
static inline DWORD QuadAlign(DWORD a)
{
    return Align(a, 8);
}
 
static LPCSTR DuplicateString(_In_ LPCSTR pszIn)
{
    if (pszIn == NULL) {
        return NULL;
    }
 
    size_t cch;
    HRESULT hr = StringCchLengthA(pszIn, 8192, &cch);
    if (FAILED(hr)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return NULL;
    }
 
    PCHAR pszOut = new NOTHROW CHAR [cch + 1];
    if (pszOut == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        return NULL;
    }
 
    hr = StringCchCopyA(pszOut, cch + 1, pszIn);
    if (FAILED(hr)) {
        delete[] pszOut;
        return NULL;
    }
 
    return pszOut;
}
 
static VOID ReleaseString(_In_opt_ LPCSTR psz)
{
    if (psz != NULL) {
        delete[] psz;
    }
}
 
//////////////////////////////////////////////////////////////////////////////
//
CImageImportFile::CImageImportFile()
{
    m_pNextFile = NULL;
    m_fByway = FALSE;
 
    m_pImportNames = NULL;
    m_nImportNames = 0;
 
    m_rvaOriginalFirstThunk = 0;
    m_rvaFirstThunk = 0;
 
    m_nForwarderChain = (UINT)0;
    m_pszName = NULL;
    m_pszOrig = NULL;
}
 
CImageImportFile::~CImageImportFile()
{
    if (m_pNextFile) {
        delete m_pNextFile;
        m_pNextFile = NULL;
    }
    if (m_pImportNames) {
        delete[] m_pImportNames;
        m_pImportNames = NULL;
        m_nImportNames = 0;
    }
    if (m_pszName) {
        delete[] m_pszName;
        m_pszName = NULL;
    }
    if (m_pszOrig) {
        delete[] m_pszOrig;
        m_pszOrig = NULL;
    }
}
 
CImageImportName::CImageImportName()
{
    m_nOrig = 0;
    m_nOrdinal = 0;
    m_nHint = 0;
    m_pszName = NULL;
    m_pszOrig = NULL;
}
 
CImageImportName::~CImageImportName()
{
    if (m_pszName) {
        delete[] m_pszName;
        m_pszName = NULL;
    }
    if (m_pszOrig) {
        delete[] m_pszOrig;
        m_pszOrig = NULL;
    }
}
 
//////////////////////////////////////////////////////////////////////////////
//
CImageData::CImageData(PBYTE pbData, DWORD cbData)
{
    m_pbData = pbData;
    m_cbData = cbData;
    m_cbAlloc = 0;
}
 
CImageData::~CImageData()
{
    IsValid();
 
    if (m_cbAlloc == 0) {
        m_pbData = NULL;
    }
    if (m_pbData) {
        delete[] m_pbData;
        m_pbData = NULL;
    }
    m_cbData = 0;
    m_cbAlloc = 0;
}
 
BOOL CImageData::SizeTo(DWORD cbData)
{
    IsValid();
 
    if (cbData <= m_cbAlloc) {
        return TRUE;
    }
 
    PBYTE pbNew = new NOTHROW BYTE [cbData];
    if (pbNew == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        return FALSE;
    }
 
    if (m_pbData) {
        CopyMemory(pbNew, m_pbData, m_cbData);
        if (m_cbAlloc > 0) {
            delete[] m_pbData;
        }
        m_pbData = NULL;
    }
    m_pbData = pbNew;
    m_cbAlloc = cbData;
 
    IsValid();
 
    return TRUE;
}
 
BOOL CImageData::Purge()
{
    m_cbData = 0;
 
    IsValid();
 
    return TRUE;
}
 
BOOL CImageData::IsValid()
{
    if (m_pbData == NULL) {
        return TRUE;
    }
 
    PBYTE pbBeg = m_pbData;
    PBYTE pbEnd = m_pbData + m_cbData;
 
    for (PBYTE pbIter = pbBeg; pbIter < pbEnd;) {
        PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)pbIter;
 
        if (pRecord->cbBytes < sizeof(DETOUR_SECTION_RECORD)) {
            return FALSE;
        }
        if (pRecord->nReserved != 0) {
            return FALSE;
        }
 
        pbIter += pRecord->cbBytes;
    }
    return TRUE;
}
 
PBYTE CImageData::Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator)
{
    IsValid();
 
    if (pnIterator == NULL ||
        m_cbData < *pnIterator + sizeof(DETOUR_SECTION_RECORD)) {
 
        if (pcbData) {
            *pcbData = 0;
        }
        if (pGuid) {
            ZeroMemory(pGuid, sizeof(*pGuid));
        }
        return NULL;
    }
 
    PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + *pnIterator);
 
    if (pGuid) {
        *pGuid = pRecord->guid;
    }
    if (pcbData) {
        *pcbData = pRecord->cbBytes - sizeof(DETOUR_SECTION_RECORD);
    }
    *pnIterator = (LONG)(((PBYTE)pRecord - m_pbData) + pRecord->cbBytes);
 
    return (PBYTE)(pRecord + 1);
}
 
PBYTE CImageData::Find(REFGUID rguid, DWORD *pcbData)
{
    IsValid();
 
    DWORD cbBytes = sizeof(DETOUR_SECTION_RECORD);
    for (DWORD nOffset = 0; nOffset < m_cbData; nOffset += cbBytes) {
        PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + nOffset);
 
        cbBytes = pRecord->cbBytes;
        if (cbBytes > m_cbData) {
            break;
        }
        if (cbBytes < sizeof(DETOUR_SECTION_RECORD)) {
            continue;
        }
 
        if (DetourAreSameGuid(pRecord->guid, rguid)) {
            *pcbData = cbBytes - sizeof(DETOUR_SECTION_RECORD);
            return (PBYTE)(pRecord + 1);
        }
    }
 
    if (pcbData) {
        *pcbData = 0;
    }
    return NULL;
}
 
BOOL CImageData::Delete(REFGUID rguid)
{
    IsValid();
 
    PBYTE pbFound = NULL;
    DWORD cbFound = 0;
 
    pbFound = Find(rguid, &cbFound);
    if (pbFound == NULL) {
        SetLastError(ERROR_MOD_NOT_FOUND);
        return FALSE;
    }
 
    pbFound -= sizeof(DETOUR_SECTION_RECORD);
    cbFound += sizeof(DETOUR_SECTION_RECORD);
 
    PBYTE pbRestData = pbFound + cbFound;
    DWORD cbRestData = m_cbData - (LONG)(pbRestData - m_pbData);
 
    if (cbRestData) {
        MoveMemory(pbFound, pbRestData, cbRestData);
    }
    m_cbData -= cbFound;
 
    IsValid();
    return TRUE;
}
 
PBYTE CImageData::Set(REFGUID rguid, PBYTE pbData, DWORD cbData)
{
    IsValid();
    Delete(rguid);
 
    DWORD cbAlloc = QuadAlign(cbData);
 
    if (!SizeTo(m_cbData + cbAlloc + sizeof(DETOUR_SECTION_RECORD))) {
        return NULL;
    }
 
    PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + m_cbData);
    pRecord->cbBytes = cbAlloc + sizeof(DETOUR_SECTION_RECORD);
    pRecord->nReserved = 0;
    pRecord->guid = rguid;
 
    PBYTE pbDest = (PBYTE)(pRecord + 1);
    if (pbData) {
        CopyMemory(pbDest, pbData, cbData);
        if (cbData < cbAlloc) {
            ZeroMemory(pbDest + cbData, cbAlloc - cbData);
        }
    }
    else {
        if (cbAlloc > 0) {
            ZeroMemory(pbDest, cbAlloc);
        }
    }
 
    m_cbData += cbAlloc + sizeof(DETOUR_SECTION_RECORD);
 
    IsValid();
    return pbDest;
}
 
//////////////////////////////////////////////////////////////////////////////
//
class CImageThunks
{
private:
    CImage *            m_pImage;
    PIMAGE_THUNK_DATA   m_pThunks;
    DWORD               m_nThunks;
    DWORD               m_nThunksMax;
    DWORD               m_nThunkVirtAddr;
 
public:
    CImageThunks(CImage *pImage, DWORD nThunksMax, DWORD *pnAddr)
    {
        m_pImage = pImage;
        m_nThunks = 0;
        m_nThunksMax = nThunksMax;
        m_pThunks = (PIMAGE_THUNK_DATA)
            m_pImage->AllocateOutput(sizeof(IMAGE_THUNK_DATA) * nThunksMax,
                                     &m_nThunkVirtAddr);
        *pnAddr = m_nThunkVirtAddr;
    }
 
    PIMAGE_THUNK_DATA Current(DWORD *pnVirtAddr)
    {
        if (m_nThunksMax > 1) {
            *pnVirtAddr = m_nThunkVirtAddr;
            return m_pThunks;
        }
        *pnVirtAddr = 0;
        return NULL;
    }
 
    PIMAGE_THUNK_DATA Allocate(ULONG_PTR nData, DWORD *pnVirtAddr)
    {
        if (m_nThunks < m_nThunksMax) {
            *pnVirtAddr = m_nThunkVirtAddr;
 
            m_nThunks++;
            m_nThunkVirtAddr += sizeof(IMAGE_THUNK_DATA);
            m_pThunks->u1.Ordinal = nData;
            return m_pThunks++;
        }
        *pnVirtAddr = 0;
        return NULL;
    }
 
    DWORD   Size()
    {
        return m_nThunksMax * sizeof(IMAGE_THUNK_DATA);
    }
};
 
//////////////////////////////////////////////////////////////////////////////
//
class CImageChars
{
private:
    CImage *        m_pImage;
    PCHAR           m_pChars;
    DWORD           m_nChars;
    DWORD           m_nCharsMax;
    DWORD           m_nCharVirtAddr;
 
public:
    CImageChars(CImage *pImage, _In_ DWORD nCharsMax, _Out_ DWORD *pnAddr)
    {
        m_pImage = pImage;
        m_nChars = 0;
        m_nCharsMax = nCharsMax;
        m_pChars = (PCHAR)m_pImage->AllocateOutput(nCharsMax, &m_nCharVirtAddr);
        *pnAddr = m_nCharVirtAddr;
    }
 
    LPCSTR Allocate(_In_ LPCSTR pszString, _Out_ DWORD *pnVirtAddr)
    {
        DWORD nLen = (DWORD)strlen(pszString) + 1;
        nLen += (nLen & 1);
 
        if (m_nChars + nLen > m_nCharsMax) {
            *pnVirtAddr = 0;
            return NULL;
        }
 
        *pnVirtAddr = m_nCharVirtAddr;
        HRESULT hrRet = StringCchCopyA(m_pChars, m_nCharsMax, pszString);
 
        if (FAILED(hrRet)) {
            return NULL;
        }
 
        pszString = m_pChars;
 
        m_pChars += nLen;
        m_nChars += nLen;
        m_nCharVirtAddr += nLen;
 
        return pszString;
    }
 
    LPCSTR Allocate(_In_ LPCSTR pszString, _In_ DWORD nHint, _Out_ DWORD *pnVirtAddr)
    {
        DWORD nLen = (DWORD)strlen(pszString) + 1 + sizeof(USHORT);
        nLen += (nLen & 1);
 
        if (m_nChars + nLen > m_nCharsMax) {
            *pnVirtAddr = 0;
            return NULL;
        }
 
        *pnVirtAddr = m_nCharVirtAddr;
        *(USHORT *)m_pChars = (USHORT)nHint;
 
        HRESULT hrRet = StringCchCopyA(m_pChars + sizeof(USHORT), m_nCharsMax, pszString);
        if (FAILED(hrRet)) {
            return NULL;
        }
 
        pszString = m_pChars + sizeof(USHORT);
 
        m_pChars += nLen;
        m_nChars += nLen;
        m_nCharVirtAddr += nLen;
 
        return pszString;
    }
 
    DWORD Size()
    {
        return m_nChars;
    }
};
 
//////////////////////////////////////////////////////////////////////////////
//
CImage * CImage::IsValid(PDETOUR_BINARY pBinary)
{
    if (pBinary) {
        CImage *pImage = (CImage *)pBinary;
 
        if (pImage->m_dwValidSignature == DETOUR_IMAGE_VALID_SIGNATURE) {
            return pImage;
        }
    }
    SetLastError(ERROR_INVALID_HANDLE);
    return NULL;
}
 
CImage::CImage()
{
    m_dwValidSignature = (DWORD)DETOUR_IMAGE_VALID_SIGNATURE;
 
    m_hMap = NULL;
    m_pMap = NULL;
 
    m_nPeOffset = 0;
    m_nSectionsOffset = 0;
 
    m_pbOutputBuffer = NULL;
    m_cbOutputBuffer = 0;
 
    m_pImageData = NULL;
 
    m_pImportFiles = NULL;
    m_nImportFiles = 0;
 
    m_fHadDetourSection = FALSE;
}
 
CImage::~CImage()
{
    Close();
    m_dwValidSignature = 0;
}
 
BOOL CImage::Close()
{
    if (m_pImportFiles) {
        delete m_pImportFiles;
        m_pImportFiles = NULL;
        m_nImportFiles = 0;
    }
 
    if (m_pImageData) {
        delete m_pImageData;
        m_pImageData = NULL;
    }
 
    if (m_pMap != NULL) {
        UnmapViewOfFile(m_pMap);
        m_pMap = NULL;
    }
 
    if (m_hMap) {
        CloseHandle(m_hMap);
        m_hMap = NULL;
    }
 
    if (m_pbOutputBuffer) {
        delete[] m_pbOutputBuffer;
        m_pbOutputBuffer = NULL;
        m_cbOutputBuffer = 0;
    }
    return TRUE;
}
 
//////////////////////////////////////////////////////////////////////////////
//
PBYTE CImage::DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator)
{
    if (m_pImageData == NULL) {
        return NULL;
    }
    return m_pImageData->Enumerate(pGuid, pcbData, pnIterator);
}
 
PBYTE CImage::DataFind(REFGUID rguid, DWORD *pcbData)
{
    if (m_pImageData == NULL) {
        return NULL;
    }
    return m_pImageData->Find(rguid, pcbData);
}
 
PBYTE CImage::DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData)
{
    if (m_pImageData == NULL) {
        return NULL;
    }
    return m_pImageData->Set(rguid, pbData, cbData);
}
 
BOOL CImage::DataDelete(REFGUID rguid)
{
    if (m_pImageData == NULL) {
        return FALSE;
    }
    return m_pImageData->Delete(rguid);
}
 
BOOL CImage::DataPurge()
{
    if (m_pImageData == NULL) {
        return TRUE;
    }
    return m_pImageData->Purge();
}
 
//////////////////////////////////////////////////////////////////////////////
//
BOOL CImage::SizeOutputBuffer(DWORD cbData)
{
    if (m_cbOutputBuffer < cbData) {
        if (cbData < 1024) {//65536
            cbData = 1024;
        }
        cbData = FileAlign(cbData);
 
        PBYTE pOutput = new NOTHROW BYTE [cbData];
        if (pOutput == NULL) {
            SetLastError(ERROR_OUTOFMEMORY);
            return FALSE;
        }
 
        if (m_pbOutputBuffer) {
            CopyMemory(pOutput, m_pbOutputBuffer, m_cbOutputBuffer);
 
            delete[] m_pbOutputBuffer;
            m_pbOutputBuffer = NULL;
        }
 
        ZeroMemory(pOutput + m_cbOutputBuffer, cbData - m_cbOutputBuffer),
 
        m_pbOutputBuffer = pOutput;
        m_cbOutputBuffer = cbData;
    }
    return TRUE;
}
 
PBYTE CImage::AllocateOutput(DWORD cbData, DWORD *pnVirtAddr)
{
    cbData = QuadAlign(cbData);
 
    PBYTE pbData = m_pbOutputBuffer + m_nOutputVirtSize;
 
    *pnVirtAddr = m_nOutputVirtAddr + m_nOutputVirtSize;
    m_nOutputVirtSize += cbData;
 
    if (m_nOutputVirtSize > m_cbOutputBuffer) {
        SetLastError(ERROR_OUTOFMEMORY);
        return NULL;
    }
 
    ZeroMemory(pbData, cbData);
 
    return pbData;
}
 
//////////////////////////////////////////////////////////////////////////////
//
DWORD CImage::FileAlign(DWORD nAddr)
{
    return Align(nAddr, m_NtHeader.OptionalHeader.FileAlignment);
}
 
DWORD CImage::SectionAlign(DWORD nAddr)
{
    return Align(nAddr, m_NtHeader.OptionalHeader.SectionAlignment);
}
 
//////////////////////////////////////////////////////////////////////////////
//
PVOID CImage::RvaToVa(ULONG_PTR nRva)
{
    if (nRva == 0) {
        return NULL;
    }
 
    for (DWORD n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        DWORD vaStart = m_SectionHeaders[n].VirtualAddress;
        DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;
 
        if (nRva >= vaStart && nRva < vaEnd) {
            return (PBYTE)m_pMap
                + m_SectionHeaders[n].PointerToRawData
                + nRva - m_SectionHeaders[n].VirtualAddress;
        }
    }
    return NULL;
}
 
DWORD CImage::RvaToFileOffset(DWORD nRva)
{
    DWORD n;
    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        DWORD vaStart = m_SectionHeaders[n].VirtualAddress;
        DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;
 
        if (nRva >= vaStart && nRva < vaEnd) {
            return m_SectionHeaders[n].PointerToRawData
                + nRva - m_SectionHeaders[n].VirtualAddress;
        }
    }
    return 0;
}
 
//////////////////////////////////////////////////////////////////////////////
//
BOOL CImage::WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
                       LPDWORD lpNumberOfBytesWritten)
{
    return ::WriteFile(hFile,
                       lpBuffer,
                       nNumberOfBytesToWrite,
                       lpNumberOfBytesWritten,
                       NULL);
}
 
 
BOOL CImage::CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData)
{
    DWORD cbDone = 0;
    return WriteFile(hFile, m_pMap + nOldPos, cbData, &cbDone);
}
 
BOOL CImage::ZeroFileData(HANDLE hFile, DWORD cbData)
{
    if (!SizeOutputBuffer(4096)) {
        return FALSE;
    }
 
    ZeroMemory(m_pbOutputBuffer, 4096);
 
    for (DWORD cbLeft = cbData; cbLeft > 0;) {
        DWORD cbStep = cbLeft > sizeof(m_pbOutputBuffer)
            ? sizeof(m_pbOutputBuffer) : cbLeft;
        DWORD cbDone = 0;
 
        if (!WriteFile(hFile, m_pbOutputBuffer, cbStep, &cbDone)) {
            return FALSE;
        }
        if (cbDone == 0) {
            break;
        }
 
        cbLeft -= cbDone;
    }
    return TRUE;
}
 
BOOL CImage::AlignFileData(HANDLE hFile)
{
    DWORD nLastFileAddr = m_nNextFileAddr;
 
    m_nNextFileAddr = FileAlign(m_nNextFileAddr);
    m_nNextVirtAddr = SectionAlign(m_nNextVirtAddr);
 
    if (hFile != INVALID_HANDLE_VALUE) {
        if (m_nNextFileAddr > nLastFileAddr) {
            if (SetFilePointer(hFile, nLastFileAddr, NULL, FILE_BEGIN) == ~0u) {
                return FALSE;
            }
            return ZeroFileData(hFile, m_nNextFileAddr - nLastFileAddr);
        }
    }
    return TRUE;
}
 
BOOL CImage::Read(HANDLE hFile)
{
    DWORD n;
    PBYTE pbData = NULL;
    DWORD cbData = 0;
 
    if (hFile == INVALID_HANDLE_VALUE) {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }
 
    ///////////////////////////////////////////////////////// Create mapping.
    //
    m_nFileSize = GetFileSize(hFile, NULL);
    if (m_nFileSize == (DWORD)-1) {
        return FALSE;
    }
 
    m_hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (m_hMap == NULL) {
        return FALSE;
    }
 
    m_pMap = (PBYTE)MapViewOfFileEx(m_hMap, FILE_MAP_READ, 0, 0, 0, NULL);
    if (m_pMap == NULL) {
        return FALSE;
    }
 
    ////////////////////////////////////////////////////// Process DOS Header.
    //
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)m_pMap;
    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
        SetLastError(ERROR_BAD_EXE_FORMAT);
        return FALSE;
    }
    m_nPeOffset = pDosHeader->e_lfanew;
    m_nPrePE = 0;
    m_cbPrePE = QuadAlign(pDosHeader->e_lfanew);
 
    if (m_nPeOffset > m_nFileSize ||
        m_nPeOffset + sizeof(m_NtHeader) > m_nFileSize) {
 
        SetLastError(ERROR_BAD_EXE_FORMAT);
        return FALSE;
    }
 
    CopyMemory(&m_DosHeader, m_pMap + m_nPrePE, sizeof(m_DosHeader));
 
    /////////////////////////////////////////////////////// Process PE Header.
    //
    CopyMemory(&m_NtHeader, m_pMap + m_nPeOffset, sizeof(m_NtHeader));
    if (m_NtHeader.Signature != IMAGE_NT_SIGNATURE) {
        SetLastError(ERROR_INVALID_EXE_SIGNATURE);
        return FALSE;
    }
    if (m_NtHeader.FileHeader.SizeOfOptionalHeader == 0) {
        SetLastError(ERROR_EXE_MARKED_INVALID);
        return FALSE;
    }
    m_nSectionsOffset = m_nPeOffset
        + sizeof(m_NtHeader.Signature)
        + sizeof(m_NtHeader.FileHeader)
        + m_NtHeader.FileHeader.SizeOfOptionalHeader;
 
    ///////////////////////////////////////////////// Process Section Headers.
    //
    if (m_NtHeader.FileHeader.NumberOfSections > ARRAYSIZE(m_SectionHeaders)) {
        SetLastError(ERROR_EXE_MARKED_INVALID);
        return FALSE;
    }
    CopyMemory(&m_SectionHeaders,
               m_pMap + m_nSectionsOffset,
               sizeof(m_SectionHeaders[0]) * m_NtHeader.FileHeader.NumberOfSections);
 
    /////////////////////////////////////////////////// Parse .detour Section.
    //
    DWORD rvaOriginalImageDirectory = 0;
    DWORD rvaDetourBeg = 0;
    DWORD rvaDetourEnd = 0;
 
    _Analysis_assume_(m_NtHeader.FileHeader.NumberOfSections <= ARRAYSIZE(m_SectionHeaders));
 
    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        if (strcmp((PCHAR)m_SectionHeaders[n].Name, ".detour") == 0) {
            DETOUR_SECTION_HEADER dh;
            CopyMemory(&dh,
                       m_pMap + m_SectionHeaders[n].PointerToRawData,
                       sizeof(dh));
 
            rvaOriginalImageDirectory = dh.nOriginalImportVirtualAddress;
            if (dh.cbPrePE != 0) {
                m_nPrePE = m_SectionHeaders[n].PointerToRawData + sizeof(dh);
                m_cbPrePE = dh.cbPrePE;
            }
            rvaDetourBeg = m_SectionHeaders[n].VirtualAddress;
            rvaDetourEnd = rvaDetourBeg + m_SectionHeaders[n].SizeOfRawData;
        }
    }
 
    //////////////////////////////////////////////////////// Get Import Table.
    //
    DWORD rvaImageDirectory = m_NtHeader.OptionalHeader
        .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    PIMAGE_IMPORT_DESCRIPTOR iidp
        = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaImageDirectory);
    PIMAGE_IMPORT_DESCRIPTOR oidp
        = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaOriginalImageDirectory);
 
    if (oidp == NULL) {
        oidp = iidp;
    }
    if (iidp == NULL || oidp == NULL) {
        SetLastError(ERROR_EXE_MARKED_INVALID);
        return FALSE;
    }
 
    DWORD nFiles = 0;
    for (; iidp[nFiles].OriginalFirstThunk != 0 || iidp[nFiles].FirstThunk != 0; nFiles++) {
    }
 
    CImageImportFile **ppLastFile = &m_pImportFiles;
    m_pImportFiles = NULL;
 
    for (n = 0; n < nFiles; n++, iidp++) {
        ULONG_PTR rvaName = iidp->Name;
        PCHAR pszName = (PCHAR)RvaToVa(rvaName);
        if (pszName == NULL) {
            SetLastError(ERROR_EXE_MARKED_INVALID);
            goto fail;
        }
 
        CImageImportFile *pImportFile = new NOTHROW CImageImportFile;
        if (pImportFile == NULL) {
            SetLastError(ERROR_OUTOFMEMORY);
            goto fail;
        }
 
        *ppLastFile = pImportFile;
        ppLastFile = &pImportFile->m_pNextFile;
        m_nImportFiles++;
 
        pImportFile->m_pszName = DuplicateString(pszName);
        if (pImportFile->m_pszName == NULL) {
            goto fail;
        }
 
        pImportFile->m_rvaOriginalFirstThunk = iidp->OriginalFirstThunk;
        pImportFile->m_rvaFirstThunk = iidp->FirstThunk;
        pImportFile->m_nForwarderChain = iidp->ForwarderChain;
        pImportFile->m_pImportNames = NULL;
        pImportFile->m_nImportNames = 0;
        pImportFile->m_fByway = FALSE;
 
        if ((ULONG)iidp->FirstThunk >= rvaDetourBeg &&
            (ULONG)iidp->FirstThunk < rvaDetourEnd) {
 
            pImportFile->m_pszOrig = NULL;
            pImportFile->m_fByway = TRUE;
            continue;
        }
 
        rvaName = oidp->Name;
        pszName = (PCHAR)RvaToVa(rvaName);
        if (pszName == NULL) {
            SetLastError(ERROR_EXE_MARKED_INVALID);
            goto fail;
        }
        pImportFile->m_pszOrig = DuplicateString(pszName);
        if (pImportFile->m_pszOrig == NULL) {
            goto fail;
        }
 
        DWORD rvaThunk = iidp->OriginalFirstThunk;
        if( !rvaThunk ) {
            rvaThunk = iidp->FirstThunk;
        }
        PIMAGE_THUNK_DATA pAddrThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk);
        rvaThunk = oidp->OriginalFirstThunk;
        if( !rvaThunk ) {
            rvaThunk = oidp->FirstThunk;
        }
        PIMAGE_THUNK_DATA pLookThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk);
 
        DWORD nNames = 0;
        if (pAddrThunk) {
            for (; pAddrThunk[nNames].u1.Ordinal; nNames++) {
            }
        }
 
        if (pAddrThunk && nNames) {
            pImportFile->m_nImportNames = nNames;
            pImportFile->m_pImportNames = new NOTHROW CImageImportName [nNames];
            if (pImportFile->m_pImportNames == NULL) {
                SetLastError(ERROR_OUTOFMEMORY);
                goto fail;
            }
 
            CImageImportName *pImportName = &pImportFile->m_pImportNames[0];
 
            for (DWORD f = 0; f < nNames; f++, pImportName++) {
                pImportName->m_nOrig = 0;
                pImportName->m_nOrdinal = 0;
                pImportName->m_nHint = 0;
                pImportName->m_pszName = NULL;
                pImportName->m_pszOrig = NULL;
 
                rvaName = pAddrThunk[f].u1.Ordinal;
                if (rvaName & IMAGE_ORDINAL_FLAG) {
                    pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName);
                    pImportName->m_nOrdinal = pImportName->m_nOrig;
                }
                else {
                    PIMAGE_IMPORT_BY_NAME pName
                        = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName);
                    if (pName) {
                        pImportName->m_nHint = pName->Hint;
                        pImportName->m_pszName = DuplicateString((PCHAR)pName->Name);
                        if (pImportName->m_pszName == NULL) {
                            goto fail;
                        }
                    }
 
                    rvaName = pLookThunk[f].u1.Ordinal;
                    if (rvaName & IMAGE_ORDINAL_FLAG) {
                        pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName);
                        pImportName->m_nOrdinal = (ULONG)IMAGE_ORDINAL(rvaName);
                    }
                    else {
                        pName = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName);
                        if (pName) {
                            pImportName->m_pszOrig
                                = DuplicateString((PCHAR)pName->Name);
                            if (pImportName->m_pszOrig == NULL) {
                                goto fail;
                            }
                        }
                    }
                }
            }
        }
        oidp++;
    }
 
    ////////////////////////////////////////////////////////// Parse Sections.
    //
    m_nExtraOffset = 0;
    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData +
                             m_SectionHeaders[n].SizeOfRawData,
                             m_nExtraOffset);
 
        if (strcmp((PCHAR)m_SectionHeaders[n].Name, ".detour") == 0) {
            DETOUR_SECTION_HEADER dh;
            CopyMemory(&dh,
                       m_pMap + m_SectionHeaders[n].PointerToRawData,
                       sizeof(dh));
 
            if (dh.nDataOffset == 0) {
                dh.nDataOffset = dh.cbHeaderSize;
            }
 
            cbData = dh.cbDataSize - dh.nDataOffset;
            pbData = (m_pMap +
                      m_SectionHeaders[n].PointerToRawData +
                      dh.nDataOffset);
 
            m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData +
                                 m_SectionHeaders[n].SizeOfRawData,
                                 m_nExtraOffset);
 
            m_NtHeader.FileHeader.NumberOfSections--;
 
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
                = dh.nOriginalImportVirtualAddress;
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
                = dh.nOriginalImportSize;
 
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress
                = dh.nOriginalBoundImportVirtualAddress;
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size
                = dh.nOriginalBoundImportSize;
 
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress
                = dh.nOriginalIatVirtualAddress;
            m_NtHeader.OptionalHeader
                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size
                = dh.nOriginalIatSize;
 
            m_NtHeader.OptionalHeader.CheckSum = 0;
            m_NtHeader.OptionalHeader.SizeOfImage
                = dh.nOriginalSizeOfImage;
 
            m_fHadDetourSection = TRUE;
        }
    }
 
    m_pImageData = new NOTHROW CImageData(pbData, cbData);
    if (m_pImageData == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
    }
    return TRUE;
 
fail:
    return FALSE;
}
 
static inline BOOL strneq(_In_ LPCSTR pszOne, _In_ LPCSTR pszTwo)
{
    if (pszOne == pszTwo) {
        return FALSE;
    }
    if (!pszOne || !pszTwo) {
        return TRUE;
    }
    return (strcmp(pszOne, pszTwo) != 0);
}
 
BOOL CImage::CheckImportsNeeded(DWORD *pnTables, DWORD *pnThunks, DWORD *pnChars)
{
    DWORD nTables = 0;
    DWORD nThunks = 0;
    DWORD nChars = 0;
    BOOL fNeedDetourSection = FALSE;
 
    for (CImageImportFile *pImportFile = m_pImportFiles;
         pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {
 
        nChars += (int)strlen(pImportFile->m_pszName) + 1;
        nChars += nChars & 1;
 
        if (pImportFile->m_fByway) {
            fNeedDetourSection = TRUE;
            nThunks++;
        }
        else {
            if (!fNeedDetourSection &&
                strneq(pImportFile->m_pszName, pImportFile->m_pszOrig)) {
 
                fNeedDetourSection = TRUE;
            }
            for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) {
                CImageImportName *pImportName = &pImportFile->m_pImportNames[n];
 
                if (!fNeedDetourSection &&
                    strneq(pImportName->m_pszName, pImportName->m_pszOrig)) {
 
                    fNeedDetourSection = TRUE;
                }
 
                if (pImportName->m_pszName) {
                    nChars += sizeof(WORD);             // Hint
                    nChars += (int)strlen(pImportName->m_pszName) + 1;
                    nChars += nChars & 1;
                }
                nThunks++;
            }
        }
        nThunks++;
        nTables++;
    }
    nTables++;
 
    *pnTables = nTables;
    *pnThunks = nThunks;
    *pnChars = nChars;
 
    return fNeedDetourSection;
}
 
//////////////////////////////////////////////////////////////////////////////
//
CImageImportFile * CImage::NewByway(_In_ LPCSTR pszName)
{
    CImageImportFile *pImportFile = new NOTHROW CImageImportFile;
    if (pImportFile == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        goto fail;
    }
 
    pImportFile->m_pNextFile = NULL;
    pImportFile->m_fByway = TRUE;
 
    pImportFile->m_pszName = DuplicateString(pszName);
    if (pImportFile->m_pszName == NULL) {
        goto fail;
    }
 
    pImportFile->m_rvaOriginalFirstThunk = 0;
    pImportFile->m_rvaFirstThunk = 0;
    pImportFile->m_nForwarderChain = (UINT)0;
    pImportFile->m_pImportNames = NULL;
    pImportFile->m_nImportNames = 0;
 
    m_nImportFiles++;
    return pImportFile;
 
fail:
    if (pImportFile) {
        delete pImportFile;
        pImportFile = NULL;
    }
    return NULL;
}
 
BOOL CImage::EditImports(PVOID pContext,
                         PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,
                         PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,
                         PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,
                         PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback)
{
    CImageImportFile *pImportFile = NULL;
    CImageImportFile **ppLastFile = &m_pImportFiles;
 
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
 
    while ((pImportFile = *ppLastFile) != NULL) {
 
        if (pfBywayCallback != NULL) {
            LPCSTR pszFile = NULL;
            if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) {
                goto fail;
            }
 
            if (pszFile != NULL) {
                // Insert a new Byway.
                CImageImportFile *pByway = NewByway(pszFile);
                if (pByway == NULL) {
                    return FALSE;
                }
 
                pByway->m_pNextFile = pImportFile;
                *ppLastFile = pByway;
                ppLastFile = &pByway->m_pNextFile;
                continue;                               // Retry after Byway.
            }
        }
 
        if (pImportFile->m_fByway) {
            if (pfBywayCallback != NULL) {
                LPCSTR pszFile = NULL;
 
                if (!(*pfBywayCallback)(pContext, pImportFile->m_pszName, &pszFile)) {
                    goto fail;
                }
 
                if (pszFile != NULL) {
                    // Replace? Byway
                    if (pszFile != pImportFile->m_pszName) {
                        LPCSTR pszLast = pImportFile->m_pszName;
                        pImportFile->m_pszName = DuplicateString(pszFile);
                        ReleaseString(pszLast);
 
                        if (pImportFile->m_pszName == NULL) {
                            goto fail;
                        }
                    }
                }
                else {                                  // Delete Byway
                    *ppLastFile = pImportFile->m_pNextFile;
                    pImportFile->m_pNextFile = NULL;
                    delete pImportFile;
                    m_nImportFiles--;
                    continue;                           // Retry after delete.
                }
            }
        }
        else {
            if (pfFileCallback != NULL) {
                LPCSTR pszFile = NULL;
 
                if (!(*pfFileCallback)(pContext,
                                       pImportFile->m_pszOrig,
                                       pImportFile->m_pszName,
                                       &pszFile)) {
                    goto fail;
                }
 
                if (pszFile != NULL) {
                    if (pszFile != pImportFile->m_pszName) {
                        LPCSTR pszLast = pImportFile->m_pszName;
                        pImportFile->m_pszName = DuplicateString(pszFile);
                        ReleaseString(pszLast);
 
                        if (pImportFile->m_pszName == NULL) {
                            goto fail;
                        }
                    }
                }
            }
 
            if (pfSymbolCallback != NULL) {
                for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) {
                    CImageImportName *pImportName = &pImportFile->m_pImportNames[n];
 
                    LPCSTR pszName = NULL;
                    ULONG nOrdinal = 0;
                    if (!(*pfSymbolCallback)(pContext,
                                             pImportName->m_nOrig,
                                             pImportName->m_nOrdinal,
                                             &nOrdinal,
                                             pImportName->m_pszOrig,
                                             pImportName->m_pszName,
                                             &pszName)) {
                        goto fail;
                    }
 
                    if (pszName != NULL) {
                        if (pszName != pImportName->m_pszName) {
                            pImportName->m_nOrdinal = 0;
 
                            LPCSTR pszLast = pImportName->m_pszName;
                            pImportName->m_pszName = DuplicateString(pszName);
                            ReleaseString(pszLast);
 
                            if (pImportName->m_pszName == NULL) {
                                goto fail;
                            }
                        }
                    }
                    else if (nOrdinal != 0) {
                        pImportName->m_nOrdinal = nOrdinal;
 
                        if (pImportName->m_pszName != NULL) {
                            delete[] pImportName->m_pszName;
                            pImportName->m_pszName = NULL;
                        }
                    }
                }
            }
        }
 
        ppLastFile = &pImportFile->m_pNextFile;
        pImportFile = pImportFile->m_pNextFile;
    }
 
    for (;;) {
        if (pfBywayCallback != NULL) {
            LPCSTR pszFile = NULL;
            if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) {
                goto fail;
            }
            if (pszFile != NULL) {
                // Insert a new Byway.
                CImageImportFile *pByway = NewByway(pszFile);
                if (pByway == NULL) {
                    return FALSE;
                }
 
                pByway->m_pNextFile = pImportFile;
                *ppLastFile = pByway;
                ppLastFile = &pByway->m_pNextFile;
                continue;                               // Retry after Byway.
            }
        }
        break;
    }
 
    if (pfCommitCallback != NULL) {
        if (!(*pfCommitCallback)(pContext)) {
            goto fail;
        }
    }
 
    SetLastError(NO_ERROR);
    return TRUE;
 
  fail:
    return FALSE;
}
 
BOOL CImage::Write(HANDLE hFile)
{
    DWORD cbDone;
 
    if (hFile == INVALID_HANDLE_VALUE) {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }
 
    m_nNextFileAddr = 0;
    m_nNextVirtAddr = 0;
 
    DWORD nTables = 0;
    DWORD nThunks = 0;
    DWORD nChars = 0;
    BOOL fNeedDetourSection = CheckImportsNeeded(&nTables, &nThunks, &nChars);
 
    //////////////////////////////////////////////////////////// Copy Headers.
    //
    if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {
        return FALSE;
    }
    if (!CopyFileData(hFile, 0, m_NtHeader.OptionalHeader.SizeOfHeaders)) {
        return FALSE;
    }
 
    if (fNeedDetourSection || !m_pImageData->IsEmpty()) {
        // Replace the file's DOS header with our own.
        m_nPeOffset = sizeof(m_DosHeader) + sizeof(s_rbDosCode);
        m_nSectionsOffset = m_nPeOffset
            + sizeof(m_NtHeader.Signature)
            + sizeof(m_NtHeader.FileHeader)
            + m_NtHeader.FileHeader.SizeOfOptionalHeader;
        m_DosHeader.e_lfanew = m_nPeOffset;
 
        if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {
            return FALSE;
        }
        if (!WriteFile(hFile, &m_DosHeader, sizeof(m_DosHeader), &cbDone)) {
            return FALSE;
        }
        if (!WriteFile(hFile, &s_rbDosCode, sizeof(s_rbDosCode), &cbDone)) {
            return FALSE;
        }
    }
    else {
        // Restore the file's original DOS header.
        if (m_nPrePE != 0) {
            m_nPeOffset = m_cbPrePE;
            m_nSectionsOffset = m_nPeOffset
                + sizeof(m_NtHeader.Signature)
                + sizeof(m_NtHeader.FileHeader)
                + m_NtHeader.FileHeader.SizeOfOptionalHeader;
            m_DosHeader.e_lfanew = m_nPeOffset;
 
 
            if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {
                return FALSE;
            }
            if (!CopyFileData(hFile, m_nPrePE, m_cbPrePE)) {
                return FALSE;
            }
        }
    }
 
    m_nNextFileAddr = m_NtHeader.OptionalHeader.SizeOfHeaders;
    m_nNextVirtAddr = 0;
    if (!AlignFileData(hFile)) {
        return FALSE;
    }
 
    /////////////////////////////////////////////////////////// Copy Sections.
    //
    DWORD n = 0;
    for (; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        if (m_SectionHeaders[n].SizeOfRawData) {
            if (SetFilePointer(hFile,
                               m_SectionHeaders[n].PointerToRawData,
                               NULL, FILE_BEGIN) == ~0u) {
                return FALSE;
            }
            if (!CopyFileData(hFile,
                              m_SectionHeaders[n].PointerToRawData,
                              m_SectionHeaders[n].SizeOfRawData)) {
                return FALSE;
            }
        }
        m_nNextFileAddr = Max(m_SectionHeaders[n].PointerToRawData +
                              m_SectionHeaders[n].SizeOfRawData,
                              m_nNextFileAddr);
        // Old images have VirtualSize == 0 as a matter of course, e.g. NT 3.1.
        // In which case, use SizeOfRawData instead.
        m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +
                              (m_SectionHeaders[n].Misc.VirtualSize
                               ? m_SectionHeaders[n].Misc.VirtualSize
                               : SectionAlign(m_SectionHeaders[n].SizeOfRawData)),
                              m_nNextVirtAddr);
 
        m_nExtraOffset = Max(m_nNextFileAddr, m_nExtraOffset);
 
        if (!AlignFileData(hFile)) {
            return FALSE;
        }
    }
 
    if (fNeedDetourSection || !m_pImageData->IsEmpty()) {
 
        if (m_NtHeader.FileHeader.NumberOfSections >= ARRAYSIZE(m_SectionHeaders)) {
            SetLastError(ERROR_EXE_MARKED_INVALID);
            return FALSE;
        }
 
        ////////////////////////////////////////////// Insert .detour Section.
        //
        DWORD nSection = m_NtHeader.FileHeader.NumberOfSections++;
        DETOUR_SECTION_HEADER dh;
 
        ZeroMemory(&dh, sizeof(dh));
        ZeroMemory(&m_SectionHeaders[nSection], sizeof(m_SectionHeaders[nSection]));
 
        dh.cbHeaderSize = sizeof(DETOUR_SECTION_HEADER);
        dh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;
 
        dh.nOriginalImportVirtualAddress = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
        dh.nOriginalImportSize = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
 
        dh.nOriginalBoundImportVirtualAddress
            = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;
        dh.nOriginalBoundImportSize = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size;
 
        dh.nOriginalIatVirtualAddress = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
        dh.nOriginalIatSize = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
 
        dh.nOriginalSizeOfImage = m_NtHeader.OptionalHeader.SizeOfImage;
 
        DWORD clrAddr = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
        DWORD clrSize = m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;
        if (clrAddr && clrSize) {
            PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr);
            if (pHdr != NULL) {
                DETOUR_CLR_HEADER hdr;
                hdr = *pHdr;
 
                dh.nOriginalClrFlags = hdr.Flags;
            }
        }
 
        HRESULT hrRet = StringCchCopyA((PCHAR)m_SectionHeaders[nSection].Name, IMAGE_SIZEOF_SHORT_NAME , ".detour");
        if (FAILED(hrRet))
            return FALSE;
 
        m_SectionHeaders[nSection].Characteristics
            = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
 
        m_nOutputVirtAddr = m_nNextVirtAddr;
        m_nOutputVirtSize = 0;
        m_nOutputFileAddr = m_nNextFileAddr;
 
        dh.nDataOffset = 0;                     // pbData
        dh.cbDataSize = m_pImageData->m_cbData;
        dh.cbPrePE = m_cbPrePE;
 
        //////////////////////////////////////////////////////////////////////////
        //
 
        DWORD rvaImportTable = 0;
        DWORD rvaLookupTable = 0;
        DWORD rvaBoundTable = 0;
        DWORD rvaNameTable = 0;
        DWORD nImportTableSize = nTables * sizeof(IMAGE_IMPORT_DESCRIPTOR);
 
        if (!SizeOutputBuffer(QuadAlign(sizeof(dh))
                              + m_cbPrePE
                              + QuadAlign(m_pImageData->m_cbData)
                              + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks)
                              + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks)
                              + QuadAlign(nChars)
                              + QuadAlign(nImportTableSize))) {
            return FALSE;
        }
 
        DWORD vaHead = 0;
        PBYTE pbHead = NULL;
        DWORD vaPrePE = 0;
        PBYTE pbPrePE = NULL;
        DWORD vaData = 0;
        PBYTE pbData = NULL;
 
        if ((pbHead = AllocateOutput(sizeof(dh), &vaHead)) == NULL) {
            return FALSE;
        }
 
        if ((pbPrePE = AllocateOutput(m_cbPrePE, &vaPrePE)) == NULL) {
            return FALSE;
        }
 
        CImageThunks lookupTable(this, nThunks, &rvaLookupTable);
        CImageThunks boundTable(this, nThunks, &rvaBoundTable);
        CImageChars nameTable(this, nChars, &rvaNameTable);
 
        if ((pbData = AllocateOutput(m_pImageData->m_cbData, &vaData)) == NULL) {
            return FALSE;
        }
 
        dh.nDataOffset = vaData - vaHead;
        dh.cbDataSize = dh.nDataOffset + m_pImageData->m_cbData;
        CopyMemory(pbHead, &dh, sizeof(dh));
        CopyMemory(pbPrePE, m_pMap + m_nPrePE, m_cbPrePE);
        CopyMemory(pbData, m_pImageData->m_pbData, m_pImageData->m_cbData);
 
        PIMAGE_IMPORT_DESCRIPTOR piidDst = (PIMAGE_IMPORT_DESCRIPTOR)
            AllocateOutput(nImportTableSize, &rvaImportTable);
        if (piidDst == NULL) {
            return FALSE;
        }
 
        //////////////////////////////////////////////// Step Through Imports.
        //
        for (CImageImportFile *pImportFile = m_pImportFiles;
             pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {
 
            ZeroMemory(piidDst, sizeof(*piidDst));
            nameTable.Allocate(pImportFile->m_pszName, (DWORD *)&piidDst->Name);
            piidDst->TimeDateStamp = 0;
            piidDst->ForwarderChain = pImportFile->m_nForwarderChain;
 
            if (pImportFile->m_fByway) {
                ULONG rvaIgnored;
 
                lookupTable.Allocate(IMAGE_ORDINAL_FLAG+1,
                                     (DWORD *)&piidDst->OriginalFirstThunk);
                boundTable.Allocate(IMAGE_ORDINAL_FLAG+1,
                                    (DWORD *)&piidDst->FirstThunk);
 
                lookupTable.Allocate(0, &rvaIgnored);
                boundTable.Allocate(0, &rvaIgnored);
            }
            else {
                ULONG rvaIgnored;
 
                piidDst->FirstThunk = (ULONG)pImportFile->m_rvaFirstThunk;
                lookupTable.Current((DWORD *)&piidDst->OriginalFirstThunk);
 
                for (n = 0; n < pImportFile->m_nImportNames; n++) {
                    CImageImportName *pImportName = &pImportFile->m_pImportNames[n];
 
                    if (pImportName->m_pszName) {
                        ULONG nDstName = 0;
 
                        nameTable.Allocate(pImportName->m_pszName,
                                           pImportName->m_nHint,
                                           &nDstName);
                        lookupTable.Allocate(nDstName, &rvaIgnored);
                    }
                    else {
                        lookupTable.Allocate(IMAGE_ORDINAL_FLAG + pImportName->m_nOrdinal,
                                             &rvaIgnored);
                    }
                }
                lookupTable.Allocate(0, &rvaIgnored);
            }
            piidDst++;
        }
        ZeroMemory(piidDst, sizeof(*piidDst));
 
        //////////////////////////////////////////////////////////////////////////
        //
        m_nNextVirtAddr += m_nOutputVirtSize;
        m_nNextFileAddr += FileAlign(m_nOutputVirtSize);
 
        if (!AlignFileData(hFile)) {
            return FALSE;
        }
 
        //////////////////////////////////////////////////////////////////////////
        //
        m_SectionHeaders[nSection].VirtualAddress = m_nOutputVirtAddr;
        m_SectionHeaders[nSection].Misc.VirtualSize = m_nOutputVirtSize;
        m_SectionHeaders[nSection].PointerToRawData = m_nOutputFileAddr;
        m_SectionHeaders[nSection].SizeOfRawData = FileAlign(m_nOutputVirtSize);
 
        m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
            = rvaImportTable;
        m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size
            = nImportTableSize;
 
        m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
        m_NtHeader.OptionalHeader
            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
 
        //////////////////////////////////////////////////////////////////////////
        //
        if (SetFilePointer(hFile, m_SectionHeaders[nSection].PointerToRawData,
                           NULL, FILE_BEGIN) == ~0u) {
            return FALSE;
        }
        if (!WriteFile(hFile, m_pbOutputBuffer, m_SectionHeaders[nSection].SizeOfRawData,
                       &cbDone)) {
            return FALSE;
        }
    }
 
    ///////////////////////////////////////////////////// Adjust Extra Data.
    //
    LONG nExtraAdjust = m_nNextFileAddr - m_nExtraOffset;
    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {
        if (m_SectionHeaders[n].PointerToRawData > m_nExtraOffset) {
            m_SectionHeaders[n].PointerToRawData += nExtraAdjust;
        }
        if (m_SectionHeaders[n].PointerToRelocations > m_nExtraOffset) {
            m_SectionHeaders[n].PointerToRelocations += nExtraAdjust;
        }
        if (m_SectionHeaders[n].PointerToLinenumbers > m_nExtraOffset) {
            m_SectionHeaders[n].PointerToLinenumbers += nExtraAdjust;
        }
    }
    if (m_NtHeader.FileHeader.PointerToSymbolTable > m_nExtraOffset) {
        m_NtHeader.FileHeader.PointerToSymbolTable += nExtraAdjust;
    }
 
    m_NtHeader.OptionalHeader.CheckSum = 0;
    m_NtHeader.OptionalHeader.SizeOfImage = m_nNextVirtAddr;
 
    ////////////////////////////////////////////////// Adjust Debug Directory.
    //
    DWORD debugAddr = m_NtHeader.OptionalHeader
        .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
    DWORD debugSize = m_NtHeader.OptionalHeader
        .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
    if (debugAddr && debugSize) {
        DWORD nFileOffset = RvaToFileOffset(debugAddr);
        if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) {
            return FALSE;
        }
 
        PIMAGE_DEBUG_DIRECTORY pDir = (PIMAGE_DEBUG_DIRECTORY)RvaToVa(debugAddr);
        if (pDir == NULL) {
            return FALSE;
        }
 
        DWORD nEntries = debugSize / sizeof(*pDir);
        for (n = 0; n < nEntries; n++) {
            IMAGE_DEBUG_DIRECTORY dir = pDir[n];
 
            if (dir.PointerToRawData > m_nExtraOffset) {
                dir.PointerToRawData += nExtraAdjust;
            }
            if (!WriteFile(hFile, &dir, sizeof(dir), &cbDone)) {
                return FALSE;
            }
        }
    }
 
    /////////////////////////////////////////////////////// Adjust CLR Header.
    //
    DWORD clrAddr = m_NtHeader.OptionalHeader
        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;
    DWORD clrSize = m_NtHeader.OptionalHeader
        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;
    if (clrAddr && clrSize && fNeedDetourSection) {
        DWORD nFileOffset = RvaToFileOffset(clrAddr);
        if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) {
            return FALSE;
        }
 
        PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr);
        if (pHdr == NULL) {
            return FALSE;
        }
 
        DETOUR_CLR_HEADER hdr;
        hdr = *pHdr;
        hdr.Flags &= 0xfffffffe;    // Clear the IL_ONLY flag.
 
        if (!WriteFile(hFile, &hdr, sizeof(hdr), &cbDone)) {
            return FALSE;
        }
    }
 
    ///////////////////////////////////////////////// Copy Left-over Data.
    //
    if (m_nFileSize > m_nExtraOffset) {
        if (SetFilePointer(hFile, m_nNextFileAddr, NULL, FILE_BEGIN) == ~0u) {
            return FALSE;
        }
        if (!CopyFileData(hFile, m_nExtraOffset, m_nFileSize - m_nExtraOffset)) {
            return FALSE;
        }
    }
 
 
    //////////////////////////////////////////////////// Finalize Headers.
    //
 
    if (SetFilePointer(hFile, m_nPeOffset, NULL, FILE_BEGIN) == ~0u) {
        return FALSE;
    }
    if (!WriteFile(hFile, &m_NtHeader, sizeof(m_NtHeader), &cbDone)) {
        return FALSE;
    }
 
    if (SetFilePointer(hFile, m_nSectionsOffset, NULL, FILE_BEGIN) == ~0u) {
        return FALSE;
    }
    if (!WriteFile(hFile, &m_SectionHeaders,
                   sizeof(m_SectionHeaders[0])
                   * m_NtHeader.FileHeader.NumberOfSections,
                   &cbDone)) {
        return FALSE;
    }
 
    m_cbPostPE = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
    if (m_cbPostPE == ~0u) {
        return FALSE;
    }
    m_cbPostPE = m_NtHeader.OptionalHeader.SizeOfHeaders - m_cbPostPE;
 
    return TRUE;
}
 
};                                                      // namespace Detour
 
//////////////////////////////////////////////////////////////////////////////
//
PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile)
{
    Detour::CImage *pImage = new NOTHROW
        Detour::CImage;
    if (pImage == NULL) {
        SetLastError(ERROR_OUTOFMEMORY);
        return FALSE;
    }
 
    if (!pImage->Read(hFile)) {
        delete pImage;
        return FALSE;
    }
 
    return (PDETOUR_BINARY)pImage;
}
 
BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pdi,
                              _In_ HANDLE hFile)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pdi);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->Write(hFile);
}
 
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary,
                                           _Out_opt_ GUID *pGuid,
                                           _Out_ DWORD *pcbData,
                                           _Inout_ DWORD *pnIterator)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->DataEnum(pGuid, pcbData, pnIterator);
}
 
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary,
                                     _In_ REFGUID rguid,
                                     _Out_ DWORD *pcbData)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->DataFind(rguid, pcbData);
}
 
PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary,
                                    _In_ REFGUID rguid,
                                    _In_reads_opt_(cbData) PVOID pvData,
                                    _In_ DWORD cbData)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return NULL;
    }
 
    return pImage->DataSet(rguid, (PBYTE)pvData, cbData);
}
 
BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary,
                                      _In_ REFGUID rguid)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->DataDelete(rguid);
}
 
BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->DataPurge();
}
 
//////////////////////////////////////////////////////////////////////////////
//
static BOOL CALLBACK ResetBywayCallback(_In_opt_ PVOID pContext,
                                        _In_opt_ LPCSTR pszFile,
                                        _Outptr_result_maybenull_ LPCSTR *ppszOutFile)
{
    UNREFERENCED_PARAMETER(pContext);
    UNREFERENCED_PARAMETER(pszFile);
 
    *ppszOutFile = NULL;
    return TRUE;
}
 
static BOOL CALLBACK ResetFileCallback(_In_opt_ PVOID pContext,
                                       _In_ LPCSTR pszOrigFile,
                                       _In_ LPCSTR pszFile,
                                       _Outptr_result_maybenull_ LPCSTR *ppszOutFile)
{
    UNREFERENCED_PARAMETER(pContext);
    UNREFERENCED_PARAMETER(pszFile);
 
    *ppszOutFile = pszOrigFile;
    return TRUE;
}
 
static BOOL CALLBACK ResetSymbolCallback(_In_opt_ PVOID pContext,
                                         _In_ ULONG nOrigOrdinal,
                                         _In_ ULONG nOrdinal,
                                         _Out_ ULONG *pnOutOrdinal,
                                         _In_opt_ LPCSTR pszOrigSymbol,
                                         _In_opt_ LPCSTR pszSymbol,
                                         _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol)
{
    UNREFERENCED_PARAMETER(pContext);
    UNREFERENCED_PARAMETER(nOrdinal);
    UNREFERENCED_PARAMETER(pszSymbol);
 
    *pnOutOrdinal = nOrigOrdinal;
    *ppszOutSymbol = pszOrigSymbol;
    return TRUE;
}
 
BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->EditImports(NULL,
                               ResetBywayCallback,
                               ResetFileCallback,
                               ResetSymbolCallback,
                               NULL);
}
 
//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary,
                                    _In_opt_ PVOID pContext,
                                    _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway,
                                    _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile,
                                    _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol,
                                    _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    return pImage->EditImports(pContext,
                               pfByway,
                               pfFile,
                               pfSymbol,
                               pfCommit);
}
 
BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary)
{
    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);
    if (pImage == NULL) {
        return FALSE;
    }
 
    BOOL bSuccess = pImage->Close();
    delete pImage;
    pImage = NULL;
 
    return bSuccess;
}
 
//
///////////////////////////////////////////////////////////////// End of File.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'GetFileSize' is deprecated in the Win64 system. It is safer to use the 'GetFileSizeEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V303 The function 'SetFilePointer' is deprecated in the Win64 system. It is safer to use the 'SetFilePointerEx' function.

V595 The 'pcbData' pointer was utilized before it was verified against nullptr. Check lines: 540, 545.

V668 There is no sense in testing the 'pszOut' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pbNew' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pOutput' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pImportFile' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pImportFile->m_pImportNames' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'm_pImageData' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pImportFile' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pImportFile' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'pImage' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V522 There might be dereferencing of a potential null pointer 'pLookThunk'.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_nNextFileAddr, m_nNextVirtAddr, m_DosHeader, m_NtHeader, m_SectionHeaders, m_nPrePE, ...