goldberg_emulator/detours/image.cpp

2227 lines
64 KiB
C++

//////////////////////////////////////////////////////////////////////////////
//
// 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 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 (pRecord->guid.Data1 == rguid.Data1 &&
pRecord->guid.Data2 == rguid.Data2 &&
pRecord->guid.Data3 == rguid.Data3 &&
pRecord->guid.Data4[0] == rguid.Data4[0] &&
pRecord->guid.Data4[1] == rguid.Data4[1] &&
pRecord->guid.Data4[2] == rguid.Data4[2] &&
pRecord->guid.Data4[3] == rguid.Data4[3] &&
pRecord->guid.Data4[4] == rguid.Data4[4] &&
pRecord->guid.Data4[5] == rguid.Data4[5] &&
pRecord->guid.Data4[6] == rguid.Data4[6] &&
pRecord->guid.Data4[7] == rguid.Data4[7]) {
*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.