mirror of
https://gitlab.com/Mr_Goldberg/goldberg_emulator.git
synced 2024-10-30 21:20:10 +00:00
2218 lines
64 KiB
C++
2218 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 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.
|