|
|
|
@ -7,28 +7,15 @@
|
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#if _MSC_VER >= 1900
|
|
|
|
|
#pragma warning(push)
|
|
|
|
|
#pragma warning(disable:4091) // empty typedef
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
|
|
// #define DETOUR_DEBUG 1
|
|
|
|
|
#define DETOURS_INTERNAL
|
|
|
|
|
|
|
|
|
|
#include "detours.h"
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
|
|
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
|
|
|
|
|
#error detours.h version mismatch
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if _MSC_VER >= 1900
|
|
|
|
|
#pragma warning(pop)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#undef ASSERT
|
|
|
|
|
#define ASSERT(x)
|
|
|
|
|
|
|
|
|
@ -260,6 +247,11 @@ class CDetourDis
|
|
|
|
|
#define ENTRY_CopyFF ENTRY_DataIgnored &CDetourDis::CopyFF
|
|
|
|
|
#define ENTRY_CopyVex2 ENTRY_DataIgnored &CDetourDis::CopyVex2
|
|
|
|
|
#define ENTRY_CopyVex3 ENTRY_DataIgnored &CDetourDis::CopyVex3
|
|
|
|
|
#define ENTRY_CopyEvex ENTRY_DataIgnored &CDetourDis::CopyEvex // 62, 3 byte payload, then normal with implied prefixes like vex
|
|
|
|
|
#define ENTRY_CopyXop ENTRY_DataIgnored &CDetourDis::CopyXop // 0x8F ... POP /0 or AMD XOP
|
|
|
|
|
#define ENTRY_CopyBytesXop 5, 5, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm
|
|
|
|
|
#define ENTRY_CopyBytesXop1 6, 6, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm8
|
|
|
|
|
#define ENTRY_CopyBytesXop4 9, 9, 4, 0, 0, &CDetourDis::CopyBytes // 0x8F xop1 xop2 opcode modrm ... imm32
|
|
|
|
|
#define ENTRY_Invalid ENTRY_DataIgnored &CDetourDis::Invalid
|
|
|
|
|
#define ENTRY_End ENTRY_DataIgnored NULL
|
|
|
|
|
|
|
|
|
@ -289,6 +281,9 @@ class CDetourDis
|
|
|
|
|
PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
|
|
|
|
PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
|
|
|
|
PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc);
|
|
|
|
|
PBYTE CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p);
|
|
|
|
|
PBYTE CopyEvex(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
|
|
|
|
PBYTE CopyXop(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
static const COPYENTRY s_rceCopyTable[257];
|
|
|
|
@ -303,6 +298,7 @@ class CDetourDis
|
|
|
|
|
BOOL m_bAddressOverride;
|
|
|
|
|
BOOL m_bRaxOverride; // AMD64 only
|
|
|
|
|
BOOL m_bVex;
|
|
|
|
|
BOOL m_bEvex;
|
|
|
|
|
BOOL m_bF2;
|
|
|
|
|
BOOL m_bF3; // x86 only
|
|
|
|
|
BYTE m_nSegmentOverride;
|
|
|
|
@ -337,6 +333,7 @@ CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra)
|
|
|
|
|
m_bF2 = FALSE;
|
|
|
|
|
m_bF3 = FALSE;
|
|
|
|
|
m_bVex = FALSE;
|
|
|
|
|
m_bEvex = FALSE;
|
|
|
|
|
|
|
|
|
|
m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget;
|
|
|
|
|
m_plExtra = plExtra ? plExtra : &m_lScratchExtra;
|
|
|
|
@ -368,8 +365,11 @@ PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
{
|
|
|
|
|
UINT nBytesFixed;
|
|
|
|
|
|
|
|
|
|
ASSERT(!m_bVex || pEntry->nFlagBits == 0);
|
|
|
|
|
ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16);
|
|
|
|
|
if (m_bVex || m_bEvex)
|
|
|
|
|
{
|
|
|
|
|
ASSERT(pEntry->nFlagBits == 0);
|
|
|
|
|
ASSERT(pEntry->nFixedSize == pEntry->nFixedSize16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UINT const nModOffset = pEntry->nModOffset;
|
|
|
|
|
UINT const nFlagBits = pEntry->nFlagBits;
|
|
|
|
@ -748,33 +748,42 @@ PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
return pbOut;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
PBYTE CDetourDis::CopyVexEvexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc, BYTE p)
|
|
|
|
|
// m is first instead of last in the hopes of pbDst/pbSrc being
|
|
|
|
|
// passed along efficiently in the registers they were already in.
|
|
|
|
|
{
|
|
|
|
|
static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod };
|
|
|
|
|
static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 };
|
|
|
|
|
static const COPYENTRY Invalid = { 0xC4, ENTRY_Invalid };
|
|
|
|
|
static const COPYENTRY ceInvalid = { 0xC4, ENTRY_Invalid };
|
|
|
|
|
|
|
|
|
|
m_bVex = TRUE;
|
|
|
|
|
REFCOPYENTRY pEntry;
|
|
|
|
|
switch (m) {
|
|
|
|
|
default: pEntry = &Invalid; break;
|
|
|
|
|
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]]; break;
|
|
|
|
|
case 2: pEntry = &ceF38; break;
|
|
|
|
|
case 3: pEntry = &ceF3A; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (pbSrc[-1] & 3) { // p in last byte
|
|
|
|
|
switch (p & 3) {
|
|
|
|
|
case 0: break;
|
|
|
|
|
case 1: m_bOperandOverride = TRUE; break;
|
|
|
|
|
case 2: m_bF3 = TRUE; break;
|
|
|
|
|
case 3: m_bF2 = TRUE; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
REFCOPYENTRY pEntry;
|
|
|
|
|
|
|
|
|
|
switch (m) {
|
|
|
|
|
default: return Invalid(&ceInvalid, pbDst, pbSrc);
|
|
|
|
|
case 1: pEntry = &s_rceCopyTable0F[pbSrc[0]];
|
|
|
|
|
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
|
|
|
|
|
case 2: return CopyBytes(&ceF38, pbDst, pbSrc);
|
|
|
|
|
case 3: return CopyBytes(&ceF3A, pbDst, pbSrc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
// m is first instead of last in the hopes of pbDst/pbSrc being
|
|
|
|
|
// passed along efficiently in the registers they were already in.
|
|
|
|
|
{
|
|
|
|
|
m_bVex = TRUE;
|
|
|
|
|
BYTE const p = (BYTE)(pbSrc[-1] & 3); // p in last byte
|
|
|
|
|
return CopyVexEvexCommon(m, pbDst, pbSrc, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
// 3 byte VEX prefix 0xC4
|
|
|
|
|
{
|
|
|
|
@ -835,6 +844,78 @@ PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
return CopyVexCommon(1, pbDst + 2, pbSrc + 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PBYTE CDetourDis::CopyEvex(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
// 62, 3 byte payload, x86 with implied prefixes like Vex
|
|
|
|
|
// for 32bit, mode 0xC0 else fallback to bound /r
|
|
|
|
|
{
|
|
|
|
|
// NOTE: Intel and Wikipedia number these differently.
|
|
|
|
|
// Intel says 0-2, Wikipedia says 1-3.
|
|
|
|
|
|
|
|
|
|
BYTE const p0 = pbSrc[1];
|
|
|
|
|
|
|
|
|
|
#ifdef DETOURS_X86
|
|
|
|
|
const static COPYENTRY ceBound = { 0x62, ENTRY_CopyBytes2Mod };
|
|
|
|
|
if ((p0 & 0xC0) != 0xC0) {
|
|
|
|
|
return CopyBytes(&ceBound, pbDst, pbSrc);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static const COPYENTRY ceInvalid = { 0x62, ENTRY_Invalid };
|
|
|
|
|
|
|
|
|
|
if ((p0 & 0x0C) != 0)
|
|
|
|
|
return Invalid(&ceInvalid, pbDst, pbSrc);
|
|
|
|
|
|
|
|
|
|
BYTE const p1 = pbSrc[2];
|
|
|
|
|
|
|
|
|
|
if ((p1 & 0x04) != 0x04)
|
|
|
|
|
return Invalid(&ceInvalid, pbDst, pbSrc);
|
|
|
|
|
|
|
|
|
|
// Copy 4 byte prefix.
|
|
|
|
|
*(UNALIGNED ULONG *)pbDst = *(UNALIGNED ULONG*)pbSrc;
|
|
|
|
|
|
|
|
|
|
m_bEvex = TRUE;
|
|
|
|
|
|
|
|
|
|
#ifdef DETOURS_X64
|
|
|
|
|
m_bRaxOverride |= !!(p1 & 0x80); // w
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return CopyVexEvexCommon(p0 & 3u, pbDst + 4, pbSrc + 4, p1 & 3u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PBYTE CDetourDis::CopyXop(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
|
|
|
|
|
/* 3 byte AMD XOP prefix 0x8F
|
|
|
|
|
byte0: 0x8F
|
|
|
|
|
byte1: RXBmmmmm
|
|
|
|
|
byte2: WvvvvLpp
|
|
|
|
|
byte3: opcode
|
|
|
|
|
mmmmm >= 8, else pop
|
|
|
|
|
mmmmm only otherwise defined for 8, 9, A.
|
|
|
|
|
pp is like VEX but only instructions with 0 are defined
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
const static COPYENTRY cePop = { 0x8F, ENTRY_CopyBytes2Mod };
|
|
|
|
|
const static COPYENTRY ceXop = { 0x8F, ENTRY_CopyBytesXop };
|
|
|
|
|
const static COPYENTRY ceXop1 = { 0x8F, ENTRY_CopyBytesXop1 };
|
|
|
|
|
const static COPYENTRY ceXop4 = { 0x8F, ENTRY_CopyBytesXop4 };
|
|
|
|
|
|
|
|
|
|
BYTE const m = (BYTE)(pbSrc[1] & 0x1F);
|
|
|
|
|
ASSERT(m <= 10);
|
|
|
|
|
switch (m)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
return CopyBytes(&cePop, pbDst, pbSrc);
|
|
|
|
|
|
|
|
|
|
case 8: // modrm with 8bit immediate
|
|
|
|
|
return CopyBytes(&ceXop1, pbDst, pbSrc);
|
|
|
|
|
|
|
|
|
|
case 9: // modrm with no immediate
|
|
|
|
|
return CopyBytes(&ceXop, pbDst, pbSrc);
|
|
|
|
|
|
|
|
|
|
case 10: // modrm with 32bit immediate
|
|
|
|
|
return CopyBytes(&ceXop4, pbDst, pbSrc);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
PBYTE CDetourDis::s_pbModuleBeg = NULL;
|
|
|
|
@ -1030,11 +1111,11 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
|
|
|
|
|
#ifdef DETOURS_X64
|
|
|
|
|
{ 0x60, ENTRY_Invalid }, // Invalid
|
|
|
|
|
{ 0x61, ENTRY_Invalid }, // Invalid
|
|
|
|
|
{ 0x62, ENTRY_Invalid }, // Invalid (not yet implemented Intel EVEX support)
|
|
|
|
|
{ 0x62, ENTRY_CopyEvex }, // EVEX / AVX512
|
|
|
|
|
#else
|
|
|
|
|
{ 0x60, ENTRY_CopyBytes1 }, // PUSHAD
|
|
|
|
|
{ 0x61, ENTRY_CopyBytes1 }, // POPAD
|
|
|
|
|
{ 0x62, ENTRY_CopyBytes2Mod }, // BOUND /r
|
|
|
|
|
{ 0x62, ENTRY_CopyEvex }, // BOUND /r and EVEX / AVX512
|
|
|
|
|
#endif
|
|
|
|
|
{ 0x63, ENTRY_CopyBytes2Mod }, // 32bit ARPL /r, 64bit MOVSXD
|
|
|
|
|
{ 0x64, ENTRY_CopyBytesSegment }, // FS prefix
|
|
|
|
@ -1084,7 +1165,7 @@ const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
|
|
|
|
|
{ 0x8C, ENTRY_CopyBytes2Mod }, // MOV /r
|
|
|
|
|
{ 0x8D, ENTRY_CopyBytes2Mod }, // LEA /r
|
|
|
|
|
{ 0x8E, ENTRY_CopyBytes2Mod }, // MOV /r
|
|
|
|
|
{ 0x8F, ENTRY_CopyBytes2Mod }, // POP /0
|
|
|
|
|
{ 0x8F, ENTRY_CopyXop }, // POP /0 or AMD XOP
|
|
|
|
|
{ 0x90, ENTRY_CopyBytes1 }, // NOP
|
|
|
|
|
{ 0x91, ENTRY_CopyBytes1 }, // XCHG
|
|
|
|
|
{ 0x92, ENTRY_CopyBytes1 }, // XCHG
|
|
|
|
|