Repository: MiroKaku/DetoursX
Branch: main
Commit: b2466f0ee27f
Files: 36
Total size: 572.4 KB
Directory structure:
gitextract_reyt6s3d/
├── .gitignore
├── BuildAllTargets.cmd
├── BuildAllTargets.proj
├── Detours/
│ ├── api_thunks.cpp
│ ├── api_thunks.h
│ ├── creatwth.cpp
│ ├── detours.cpp
│ ├── detours.h
│ ├── detoursx.cpp
│ ├── detver.h
│ ├── disasm.cpp
│ ├── disolarm.cpp
│ ├── disolarm64.cpp
│ ├── disolia64.cpp
│ ├── disolx64.cpp
│ ├── disolx86.cpp
│ ├── image.cpp
│ ├── modules.cpp
│ └── uimports.cpp
├── Detours.StaticLibrary/
│ └── Detours.StaticLibrary.vcxproj
├── Detours.StaticLibraryForDriver/
│ └── Detours.StaticLibraryForDriver.vcxproj
├── Detours.Test/
│ ├── Detours.Test.vcxproj
│ ├── Main.cpp
│ └── Module.def
├── Detours.TestForDriver/
│ ├── Detours.TestForDriver.inf
│ ├── Detours.TestForDriver.vcxproj
│ └── Main.cpp
├── DetoursX.slnx
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.Cpp.props
├── InitializeVisualStudioEnvironment.cmd
├── LICENSE
├── README.md
├── README.zh-CN.md
└── global.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
.xmake/
vsxmake*/
vs20*/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
================================================
FILE: BuildAllTargets.cmd
================================================
@setlocal
@echo off
rem Change to the current folder.
cd "%~dp0"
rem Remove the output folder for a fresh compile.
rd /s /q Output
rem Initialize Visual Studio environment
call "%~dp0InitializeVisualStudioEnvironment.cmd"
rem Build all targets
MSBuild -binaryLogger:Output\BuildAllTargets.binlog -m BuildAllTargets.proj
@endlocal
================================================
FILE: BuildAllTargets.proj
================================================
$(MSBuildThisFileDirectory)*.slnx
Configuration=Debug;Platform=x64
Configuration=Release;Platform=x64
Configuration=Debug;Platform=ARM64
Configuration=Release;Platform=ARM64
================================================
FILE: Detours/api_thunks.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Detours Disassembler (disasm.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#if defined(_KERNEL_MODE)
#define DETOURS_KERNEL
#endif
#include
#ifdef DETOURS_KERNEL
namespace Detours::Thunks
{
//////////////////////////////////////////////////////////////////////////////
//
// Zw
NTSTATUS NTAPI ZwReadVirtualMemory(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress,
_Out_writes_bytes_(BufferSize) PVOID Buffer,
_In_ SIZE_T BufferSize,
_Out_opt_ PSIZE_T NumberOfBytesRead
)
{
SIZE_T BytesCopied = 0u;
KPROCESSOR_MODE PreviousMode = KernelMode;
PEPROCESS Process = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PETHREAD CurrentThread = NULL;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
//
// If the buffer size is not zero, then attempt to read data from the
// specified process address space into the current process address
// space.
//
BytesCopied = 0;
Status = STATUS_SUCCESS;
if (BufferSize != 0) {
//
// Reference the target process.
//
Status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_VM_READ,
*PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
//
// If the process was successfully referenced, then attempt to
// read the specified memory either by direct mapping or copying
// through nonpaged pool.
//
if (Status == STATUS_SUCCESS) {
Status = MmCopyVirtualMemory(
Process,
BaseAddress,
PsGetThreadProcess(CurrentThread),
Buffer,
BufferSize,
PreviousMode,
&BytesCopied);
//
// Dereference the target process.
//
ObDereferenceObject(Process);
}
}
//
// If requested, return the number of bytes read.
//
if (ARGUMENT_PRESENT(NumberOfBytesRead)) {
__try {
*NumberOfBytesRead = BytesCopied;
} __except(EXCEPTION_EXECUTE_HANDLER) {
NOTHING;
}
}
return Status;
}
NTSTATUS NTAPI ZwWriteVirtualMemory(
_In_ HANDLE ProcessHandle,
_In_opt_ PVOID BaseAddress,
_In_reads_bytes_(BufferSize) PVOID Buffer,
_In_ SIZE_T BufferSize,
_Out_opt_ PSIZE_T NumberOfBytesWritten
)
{
SIZE_T BytesCopied = 0u;
KPROCESSOR_MODE PreviousMode = KernelMode;
PEPROCESS Process = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PETHREAD CurrentThread = NULL;
PAGED_CODE();
CurrentThread = PsGetCurrentThread();
//
// If the buffer size is not zero, then attempt to write data from the
// current process address space into the target process address space.
//
BytesCopied = 0;
Status = STATUS_SUCCESS;
if (BufferSize != 0) {
//
// Reference the target process.
//
Status = ObReferenceObjectByHandle(
ProcessHandle,
PROCESS_VM_WRITE,
*PsProcessType,
PreviousMode,
(PVOID*)&Process,
NULL);
//
// If the process was successfully referenced, then attempt to
// write the specified memory either by direct mapping or copying
// through nonpaged pool.
//
if (Status == STATUS_SUCCESS) {
Status = MmCopyVirtualMemory(
PsGetThreadProcess(CurrentThread),
Buffer,
Process,
BaseAddress,
BufferSize,
PreviousMode,
&BytesCopied);
//
// Dereference the target process.
//
ObDereferenceObject(Process);
}
}
//
// If requested, return the number of bytes read.
//
if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {
__try {
*NumberOfBytesWritten = BytesCopied;
} __except(EXCEPTION_EXECUTE_HANDLER) {
NOTHING;
}
}
return Status;
}
//////////////////////////////////////////////////////////////////////////////
//
// Set/Get Last Error
static DWORD __Win32Error = NO_ERROR;
VOID WINAPI SetLastError(_In_ DWORD Win32Error)
{
__Win32Error = Win32Error;
}
DWORD WINAPI GetLastError(VOID)
{
return __Win32Error;
}
//////////////////////////////////////////////////////////////////////////////
//
// Process & Thread
HANDLE WINAPI GetCurrentProcess(VOID)
{
return ZwCurrentProcess();
}
HANDLE WINAPI GetCurrentThread(VOID)
{
return ZwCurrentThread();
}
DWORD WINAPI GetCurrentProcessId(VOID)
{
return (DWORD)(ULONG_PTR)PsGetCurrentProcessId();
}
DWORD WINAPI GetCurrentThreadId(VOID)
{
return (DWORD)(ULONG_PTR)PsGetCurrentThreadId();
}
BOOL WINAPI IsWow64Process(
_In_ HANDLE hProcess,
_Out_ PBOOL Wow64Process
)
{
NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;
ULONG_PTR Peb32 = 0u;
NtStatus = ZwQueryInformationProcess(
hProcess,
ProcessWow64Information,
&Peb32,
sizeof(Peb32),
NULL
);
*Wow64Process = FALSE;
if (!NT_SUCCESS(NtStatus)) {
SetLastError(NtStatus);
}
else {
if (Peb32 == 0) {
*Wow64Process = FALSE;
}
else {
*Wow64Process = TRUE;
}
}
return (NT_SUCCESS(NtStatus));
}
_Success_(return != FALSE)
BOOL WINAPI ReadProcessMemory(
_In_ HANDLE hProcess,
_In_ LPCVOID lpBaseAddress,
_Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T * lpNumberOfBytesRead
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
SIZE_T NtNumberOfBytesRead = 0u;
Status = ZwReadVirtualMemory(
hProcess,
(PVOID)lpBaseAddress,
lpBuffer,
nSize,
&NtNumberOfBytesRead
);
if (lpNumberOfBytesRead != NULL) {
*lpNumberOfBytesRead = NtNumberOfBytesRead;
}
if (!NT_SUCCESS(Status)) {
SetLastError(Status);
return FALSE;
}
else {
return TRUE;
}
}
_Success_(return != FALSE)
BOOL WINAPI WriteProcessMemory(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T * lpNumberOfBytesWritten
)
{
NTSTATUS Status;
ULONG OldProtect = PAGE_READWRITE;
SIZE_T RegionSize;
PVOID Base;
SIZE_T NtNumberOfBytesWritten = 0u;
//
// Set the protection to allow writes
//
RegionSize = nSize;
Base = lpBaseAddress;
Status = ZwProtectVirtualMemory(
hProcess,
&Base,
&RegionSize,
PAGE_READWRITE,
&OldProtect
);
if (NT_SUCCESS(Status)) {
//
// See if previous protection was writable. If so,
// then reset protection and do the write.
// Otherwise, see if previous protection was read-only or
// no access. In this case, don't do the write, just fail
//
if ((OldProtect & PAGE_READWRITE) == PAGE_READWRITE ||
(OldProtect & PAGE_WRITECOPY) == PAGE_WRITECOPY ||
(OldProtect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE ||
(OldProtect & PAGE_EXECUTE_WRITECOPY) == PAGE_EXECUTE_WRITECOPY) {
(void)ZwProtectVirtualMemory(
hProcess,
&Base,
&RegionSize,
OldProtect,
&OldProtect
);
Status = ZwWriteVirtualMemory(
hProcess,
lpBaseAddress,
(PVOID)lpBuffer,
nSize,
&NtNumberOfBytesWritten
);
if (lpNumberOfBytesWritten != NULL) {
*lpNumberOfBytesWritten = NtNumberOfBytesWritten;
}
if (!NT_SUCCESS(Status)) {
SetLastError(Status);
return FALSE;
}
ZwFlushInstructionCache(hProcess, lpBaseAddress, nSize);
return TRUE;
}
else {
//
// See if the previous protection was read only or no access. If
// this is the case, restore the previous protection and return
// an access violation error.
//
if ((OldProtect & PAGE_NOACCESS) == PAGE_NOACCESS ||
(OldProtect & PAGE_READONLY) == PAGE_READONLY) {
(void)ZwProtectVirtualMemory(
hProcess,
&Base,
&RegionSize,
OldProtect,
&OldProtect
);
SetLastError((DWORD)STATUS_ACCESS_VIOLATION);
return FALSE;
}
else {
//
// The previous protection must have been code and the caller
// is trying to set a breakpoint or edit the code. Do the write
// and then restore the previous protection.
//
Status = ZwWriteVirtualMemory(
hProcess,
lpBaseAddress,
(PVOID)lpBuffer,
nSize,
&NtNumberOfBytesWritten
);
if (lpNumberOfBytesWritten != NULL) {
*lpNumberOfBytesWritten = NtNumberOfBytesWritten;
}
(void)ZwProtectVirtualMemory(
hProcess,
&Base,
&RegionSize,
OldProtect,
&OldProtect
);
if (!NT_SUCCESS(Status)) {
SetLastError((DWORD)STATUS_ACCESS_VIOLATION);
return FALSE;
}
(void)ZwFlushInstructionCache(hProcess, lpBaseAddress, nSize);
return TRUE;
}
}
}
else {
SetLastError(Status);
return FALSE;
}
}
_Ret_maybenull_
_Post_writable_byte_size_(dwSize)
LPVOID WINAPI VirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
)
{
NTSTATUS Status;
__try {
Status = NtAllocateVirtualMemory(hProcess,
&lpAddress,
0,
&dwSize,
flAllocationType,
flProtect
);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
if (NT_SUCCESS(Status)) {
return(lpAddress);
}
SetLastError(Status);
return NULL;
}
BOOL WINAPI VirtualFreeEx(
_In_ HANDLE hProcess,
_Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD dwFreeType
)
{
NTSTATUS Status;
if ((dwFreeType & MEM_RELEASE) && dwSize != 0) {
SetLastError((DWORD)STATUS_INVALID_PARAMETER);
return FALSE;
}
Status = NtFreeVirtualMemory(
hProcess,
&lpAddress,
&dwSize,
dwFreeType
);
if (NT_SUCCESS(Status)) {
return(TRUE);
}
SetLastError(Status);
return FALSE;
}
_Success_(return != FALSE)
BOOL WINAPI VirtualProtectEx(
_In_ HANDLE hProcess,
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect)
{
NTSTATUS Status;
Status = ZwProtectVirtualMemory(
hProcess,
&lpAddress,
&dwSize,
flNewProtect,
lpflOldProtect);
if (NT_SUCCESS(Status)) {
return(TRUE);
}
SetLastError(Status);
return FALSE;
}
SIZE_T WINAPI VirtualQueryEx(
_In_ HANDLE hProcess,
_In_opt_ LPCVOID lpAddress,
_Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,
_In_ SIZE_T dwLength)
{
NTSTATUS Status;
SIZE_T ReturnLength = 0u;
Status = ZwQueryVirtualMemory(
hProcess,
(LPVOID)lpAddress,
MemoryBasicInformation,
(PMEMORY_BASIC_INFORMATION)lpBuffer,
dwLength,
&ReturnLength);
if (NT_SUCCESS(Status))
{
return(ReturnLength);
}
SetLastError(Status);
return 0;
}
}
#endif // DETOURS_KERNEL
================================================
FILE: Detours/api_thunks.h
================================================
#pragma once
namespace Detours::Thunks
{
VOID WINAPI SetLastError(_In_ DWORD Win32Error);
DWORD WINAPI GetLastError(VOID);
HANDLE WINAPI GetCurrentProcess(VOID);
HANDLE WINAPI GetCurrentThread(VOID);
DWORD WINAPI GetCurrentProcessId(VOID);
DWORD WINAPI GetCurrentThreadId(VOID);
BOOL WINAPI IsWow64Process(
_In_ HANDLE hProcess,
_Out_ PBOOL Wow64Process
);
BOOL WINAPI ReadProcessMemory(
_In_ HANDLE hProcess,
_In_ LPCVOID lpBaseAddress,
_Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T * lpNumberOfBytesRead
);
BOOL WINAPI WriteProcessMemory(
_In_ HANDLE hProcess,
_In_ LPVOID lpBaseAddress,
_In_reads_bytes_(nSize) LPCVOID lpBuffer,
_In_ SIZE_T nSize,
_Out_opt_ SIZE_T* lpNumberOfBytesWritten
);
LPVOID WINAPI VirtualAllocEx(
_In_ HANDLE hProcess,
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
BOOL WINAPI VirtualFreeEx(
_In_ HANDLE hProcess,
_Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD dwFreeType
);
BOOL WINAPI VirtualProtectEx(
_In_ HANDLE hProcess,
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flNewProtect,
_Out_ PDWORD lpflOldProtect
);
SIZE_T WINAPI VirtualQueryEx(
_In_ HANDLE hProcess,
_In_opt_ LPCVOID lpAddress,
_Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,
_In_ SIZE_T dwLength
);
}
#define SetLastError ::Detours::Thunks::SetLastError
#define GetLastError ::Detours::Thunks::GetLastError
#define GetCurrentProcess ::Detours::Thunks::GetCurrentProcess
#define GetCurrentThread ::Detours::Thunks::GetCurrentThread
#define GetCurrentProcessId ::Detours::Thunks::GetCurrentProcessId
#define GetCurrentThreadId ::Detours::Thunks::GetCurrentThreadId
#define IsWow64Process ::Detours::Thunks::IsWow64Process
#define ReadProcessMemory ::Detours::Thunks::ReadProcessMemory
#define WriteProcessMemory ::Detours::Thunks::WriteProcessMemory
#define VirtualAllocEx ::Detours::Thunks::VirtualAllocEx
#define VirtualFreeEx ::Detours::Thunks::VirtualFreeEx
#define VirtualProtectEx ::Detours::Thunks::VirtualProtectEx
#define VirtualQueryEx ::Detours::Thunks::VirtualQueryEx
================================================
FILE: Detours/creatwth.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Create a process with a DLL (creatwth.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#if defined(_KERNEL_MODE)
#define DETOURS_KERNEL
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include
#ifdef DETOURS_KERNEL
#include "api_thunks.h"
#endif
#include
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include
#pragma warning(pop)
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.h"
#if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH
#error detours.h version mismatch
#endif
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]
#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]
#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]
//////////////////////////////////////////////////////////////////////////////
//
const GUID DETOUR_EXE_RESTORE_GUID = {
0x2ed7a3ff, 0x3339, 0x4a8d,
{ 0x80, 0x5c, 0xd4, 0x98, 0x15, 0x3f, 0xc2, 0x8f } };
const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */
0xea0251b9, 0x5cde, 0x41b5,
{ 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }};
//////////////////////////////////////////////////////////////////////////////
//
// Enumate through modules in the target process.
//
static BOOL WINAPI LoadNtHeaderFromProcess(HANDLE hProcess,
HMODULE hModule,
PIMAGE_NT_HEADERS32 pNtHeader)
{
PBYTE pbModule = (PBYTE)hModule;
if (pbModule == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) {
return FALSE;
}
IMAGE_DOS_HEADER idh;
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
pbModule, pbModule + sizeof(idh), GetLastError()));
return FALSE;
}
if (idh.e_magic != IMAGE_DOS_SIGNATURE ||
(DWORD)idh.e_lfanew > mbi.RegionSize ||
(DWORD)idh.e_lfanew < sizeof(idh)) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew,
pNtHeader, sizeof(*pNtHeader), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %lu\n",
pbModule + idh.e_lfanew,
pbModule + idh.e_lfanew + sizeof(*pNtHeader),
pbModule,
GetLastError()));
return FALSE;
}
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
SetLastError(ERROR_BAD_EXE_FORMAT);
return FALSE;
}
return TRUE;
}
static HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess,
HMODULE hModuleLast,
PIMAGE_NT_HEADERS32 pNtHeader)
{
PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
// Find the next memory region that contains a mapped PE image.
//
for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
break;
}
// Usermode address space has such an unaligned region size always at the
// end and only at the end.
//
if ((mbi.RegionSize & 0xfff) == 0xfff) {
break;
}
if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) {
break;
}
// Skip uncommitted regions and guard pages.
//
if ((mbi.State != MEM_COMMIT) ||
((mbi.Protect & 0xff) == PAGE_NOACCESS) ||
(mbi.Protect & PAGE_GUARD)) {
continue;
}
if (LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader)) {
return (HMODULE)pbLast;
}
}
return NULL;
}
//////////////////////////////////////////////////////////////////////////////
//
// Find a region of memory in which we can create a replacement import table.
//
static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbBase, DWORD cbAlloc)
{
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi, sizeof(mbi));
PBYTE pbLast = pbBase;
for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {
if (GetLastError() == ERROR_INVALID_PARAMETER) {
break;
}
DETOUR_TRACE(("VirtualQueryEx(%p) failed: %lu\n",
pbLast, GetLastError()));
break;
}
// Usermode address space has such an unaligned region size always at the
// end and only at the end.
//
if ((mbi.RegionSize & 0xfff) == 0xfff) {
break;
}
// Skip anything other than a pure free region.
//
if (mbi.State != MEM_FREE) {
continue;
}
PBYTE pbAddress = (PBYTE)(((DWORD_PTR)mbi.BaseAddress + 0xffff) & ~(DWORD_PTR)0xffff);
#ifdef _WIN64
// The distance from pbBase to pbAddress must fit in 32 bits.
//
const size_t GB4 = ((((size_t)1) << 32) - 1);
if ((size_t)(pbAddress - pbBase) > GB4) {
DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress));
return NULL;
}
#endif
DETOUR_TRACE(("Free region %p..%p\n",
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize));
for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) {
PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pbAlloc == NULL) {
DETOUR_TRACE(("VirtualAllocEx(%p) failed: %lu\n", pbAddress, GetLastError()));
continue;
}
#ifdef _WIN64
// The distance from pbBase to pbAddress must fit in 32 bits.
//
if ((size_t)(pbAddress - pbBase) > GB4) {
DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress));
return NULL;
}
#endif
DETOUR_TRACE(("[%p..%p] Allocated for import table.\n",
pbAlloc, pbAlloc + cbAlloc));
return pbAlloc;
}
}
return NULL;
}
static inline DWORD PadToDword(DWORD dw)
{
return (dw + 3) & ~3u;
}
static inline DWORD PadToDwordPtr(DWORD dw)
{
return (dw + 7) & ~7u;
}
static inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest,
_In_ size_t cchDest,
_In_z_ LPCSTR pszSize)
{
if (cchDest == 0 || pszDest == NULL || pszSize == NULL ||
pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') {
// can not write into empty buffer or with string other than two chars.
return ERROR_INVALID_PARAMETER;
}
for (; cchDest >= 2; cchDest--, pszDest++) {
if (pszDest[0] == '?' && pszDest[1] == '?') {
pszDest[0] = pszSize[0];
pszDest[1] = pszSize[1];
break;
}
}
return S_OK;
}
static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der)
{
// Save the various headers for DetourRestoreAfterWith.
ZeroMemory(&der, sizeof(der));
der.cb = sizeof(der);
der.pidh = (PBYTE)hModule;
der.cbidh = sizeof(der.idh);
if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
der.pidh, der.pidh + der.cbidh, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh));
// We read the NT header in two passes to get the full size.
// First we read just the Signature and FileHeader.
der.pinh = der.pidh + der.idh.e_lfanew;
der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader);
if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
der.pinh, der.pinh + der.cbinh, GetLastError()));
return FALSE;
}
// Second we read the OptionalHeader and Section headers.
der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
der.inh.FileHeader.SizeOfOptionalHeader +
der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));
if (der.cbinh > sizeof(der.raw)) {
return FALSE;
}
if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
der.pinh, der.pinh + der.cbinh, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh));
// Third, we read the CLR header
if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 &&
der.inh32.CLR_DIRECTORY.Size != 0) {
DETOUR_TRACE(("CLR32.VirtAddr=%lx, CLR.Size=%lx\n",
der.inh32.CLR_DIRECTORY.VirtualAddress,
der.inh32.CLR_DIRECTORY.Size));
der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress;
}
}
else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 &&
der.inh64.CLR_DIRECTORY.Size != 0) {
DETOUR_TRACE(("CLR64.VirtAddr=%lx, CLR.Size=%lx\n",
der.inh64.CLR_DIRECTORY.VirtualAddress,
der.inh64.CLR_DIRECTORY.Size));
der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress;
}
}
if (der.pclr != 0) {
der.cbclr = sizeof(der.clr);
if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %lu\n",
der.pclr, der.pclr + der.cbclr, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
#if DETOURS_32BIT
#define DWORD_XX DWORD32
#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32
#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC
#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32
#define UPDATE_IMPORTS_XX UpdateImports32
#define DETOURS_BITS_XX 32
#include "uimports.cpp"
#undef DETOUR_EXE_RESTORE_FIELD_XX
#undef DWORD_XX
#undef IMAGE_NT_HEADERS_XX
#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
#undef IMAGE_ORDINAL_FLAG_XX
#undef UPDATE_IMPORTS_XX
#endif // DETOURS_32BIT
#if DETOURS_64BIT
#define DWORD_XX DWORD64
#define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64
#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC
#define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64
#define UPDATE_IMPORTS_XX UpdateImports64
#define DETOURS_BITS_XX 64
#include "uimports.cpp"
#undef DETOUR_EXE_RESTORE_FIELD_XX
#undef DWORD_XX
#undef IMAGE_NT_HEADERS_XX
#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX
#undef IMAGE_ORDINAL_FLAG_XX
#undef UPDATE_IMPORTS_XX
#endif // DETOURS_64BIT
//////////////////////////////////////////////////////////////////////////////
//
#if DETOURS_64BIT
C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16);
static BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine,
DETOUR_EXE_RESTORE& der)
{
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS32 inh32;
IMAGE_NT_HEADERS64 inh64;
IMAGE_SECTION_HEADER sects[32];
PBYTE pbModule = (PBYTE)hModule;
DWORD n;
ZeroMemory(&inh32, sizeof(inh32));
ZeroMemory(&inh64, sizeof(inh64));
ZeroMemory(sects, sizeof(sects));
DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine));
//////////////////////////////////////////////////////// Read old headers.
//
if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n",
pbModule, pbModule + sizeof(idh), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n",
pbModule, pbModule + sizeof(idh)));
PBYTE pnh = pbModule + idh.e_lfanew;
if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) {
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh32), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32)));
if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) {
return FALSE;
}
PBYTE psects = pnh +
FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
inh32.FileHeader.SizeOfOptionalHeader;
ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) {
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n",
psects, psects + cb, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb));
////////////////////////////////////////////////////////// Convert header.
//
inh64.Signature = inh32.Signature;
inh64.FileHeader = inh32.FileHeader;
inh64.FileHeader.Machine = machine;
inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion;
inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion;
inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode;
inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData;
inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData;
inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint;
inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode;
inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase;
inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment;
inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment;
inh64.OptionalHeader.MajorOperatingSystemVersion
= inh32.OptionalHeader.MajorOperatingSystemVersion;
inh64.OptionalHeader.MinorOperatingSystemVersion
= inh32.OptionalHeader.MinorOperatingSystemVersion;
inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion;
inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion;
inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion;
inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion;
inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue;
inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage;
inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders;
inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum;
inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem;
inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics;
inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve;
inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit;
inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve;
inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit;
inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags;
inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes;
for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) {
inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n];
}
/////////////////////////////////////////////////////// Write new headers.
//
DWORD dwProtect = 0;
if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
PAGE_EXECUTE_READWRITE, &dwProtect)) {
return FALSE;
}
if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh64), GetLastError()));
return FALSE;
}
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64)));
psects = pnh +
FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +
inh64.FileHeader.SizeOfOptionalHeader;
cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) {
DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %lu\n",
psects, psects + cb, GetLastError()));
return FALSE;
}
DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb));
// Record the updated headers.
if (!RecordExeRestore(hProcess, hModule, der)) {
return FALSE;
}
// Remove the import table.
if (der.pclr != NULL && (der.clr.Flags & 1)) {
inh64.IMPORT_DIRECTORY.VirtualAddress = 0;
inh64.IMPORT_DIRECTORY.Size = 0;
if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n",
pnh, pnh + sizeof(inh64), GetLastError()));
return FALSE;
}
}
DWORD dwOld = 0;
if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,
dwProtect, &dwOld)) {
return FALSE;
}
return TRUE;
}
#endif // DETOURS_64BIT
//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls)
{
// Find the next memory region that contains a mapped PE image.
//
BOOL bHas64BitDll = FALSE;
BOOL bHas32BitExe = FALSE;
BOOL bIs32BitProcess;
HMODULE hModule = NULL;
HMODULE hLast = NULL;
DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%lu)\n", hProcess, nDlls));
for (;;) {
IMAGE_NT_HEADERS32 inh;
if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == NULL) {
break;
}
DETOUR_TRACE(("%p machine=%04x magic=%04x\n",
(void*)hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic));
if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
hModule = hLast;
if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
&& inh.FileHeader.Machine != 0) {
bHas32BitExe = TRUE;
}
DETOUR_TRACE(("%p Found EXE\n", (void*)hLast));
}
else {
if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
&& inh.FileHeader.Machine != 0) {
bHas64BitDll = TRUE;
}
}
}
if (hModule == NULL) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
if (!bHas32BitExe) {
bIs32BitProcess = FALSE;
}
else if (!bHas64BitDll) {
bIs32BitProcess = TRUE;
}
else {
if (!IsWow64Process(hProcess, &bIs32BitProcess)) {
return FALSE;
}
}
DETOUR_TRACE((" 32BitExe=%d 32BitProcess=%d\n", bHas32BitExe, bIs32BitProcess));
return DetourUpdateProcessWithDllEx(hProcess,
hModule,
bIs32BitProcess,
rlpDlls,
nDlls);
}
BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,
_In_ HMODULE hModule,
_In_ BOOL bIs32BitProcess,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls)
{
// Find the next memory region that contains a mapped PE image.
//
BOOL bIs32BitExe = FALSE;
DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\n", hProcess, (void*)hModule, nDlls));
IMAGE_NT_HEADERS32 inh;
if (hModule == NULL || LoadNtHeaderFromProcess(hProcess, hModule, &inh) == FALSE) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
&& inh.FileHeader.Machine != 0) {
bIs32BitExe = TRUE;
}
DETOUR_TRACE((" 32BitExe=%d 32BitProcess=%d\n", bIs32BitExe, bIs32BitProcess));
if (hModule == NULL) {
SetLastError(ERROR_INVALID_OPERATION);
return FALSE;
}
// Save the various headers for DetourRestoreAfterWith.
//
DETOUR_EXE_RESTORE der;
if (!RecordExeRestore(hProcess, hModule, der)) {
return FALSE;
}
#if defined(DETOURS_64BIT)
// Try to convert a neutral 32-bit managed binary to a 64-bit managed binary.
if (bIs32BitExe && !bIs32BitProcess) {
if (!der.pclr // Native binary
|| (der.clr.Flags & 1) == 0 // Or mixed-mode MSIL
|| (der.clr.Flags & 2) != 0) { // Or 32BIT Required MSIL
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
if (!UpdateFrom32To64(hProcess, hModule,
#if defined(DETOURS_X64)
IMAGE_FILE_MACHINE_AMD64,
#elif defined(DETOURS_IA64)
IMAGE_FILE_MACHINE_IA64,
#elif defined(DETOURS_ARM64)
IMAGE_FILE_MACHINE_ARM64,
#else
#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit.
#endif
der)) {
return FALSE;
}
bIs32BitExe = FALSE;
}
#endif // DETOURS_64BIT
// Now decide if we can insert the detour.
#if defined(DETOURS_32BIT)
if (bIs32BitProcess) {
// 32-bit native or 32-bit managed process on any platform.
if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) {
return FALSE;
}
}
else {
// 64-bit native or 64-bit managed process.
//
// Can't detour a 64-bit process with 32-bit code.
// Note: This happens for 32-bit PE binaries containing only
// manage code that have been marked as 64-bit ready.
//
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
#elif defined(DETOURS_64BIT)
if (bIs32BitProcess || bIs32BitExe) {
// Can't detour a 32-bit process with 64-bit code.
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
else {
// 64-bit native or 64-bit managed process on any platform.
if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) {
return FALSE;
}
}
#else
#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT.
#endif // DETOURS_64BIT
/////////////////////////////////////////////////// Update the CLR header.
//
if (der.pclr != NULL) {
DETOUR_CLR_HEADER clr;
CopyMemory(&clr, &der.clr, sizeof(clr));
clr.Flags &= 0xfffffffe; // Clear the IL_ONLY flag.
DWORD dwProtect;
if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %lu\n", GetLastError()));
return FALSE;
}
if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) {
DETOUR_TRACE(("WriteProcessMemory(clr) failed: %lu\n", GetLastError()));
return FALSE;
}
if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) {
DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %lu\n", GetLastError()));
return FALSE;
}
DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr));
#if DETOURS_64BIT
if (der.clr.Flags & 0x2) { // Is the 32BIT Required Flag set?
// X64 never gets here because the process appears as a WOW64 process.
// However, on IA64, it doesn't appear to be a WOW process.
DETOUR_TRACE(("CLR Requires 32-bit, %p...%p\n", der.pclr, der.pclr + der.cbclr));
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
#endif // DETOURS_64BIT
}
//////////////////////////////// Save the undo data to the target process.
//
if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
return FALSE;
}
return TRUE;
}
#ifndef DETOURS_KERNEL
//////////////////////////////////////////////////////////////////////////////
//
BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
PROCESS_INFORMATION pi;
BOOL fResult = FALSE;
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
fResult = pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwMyCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
&pi);
if (lpProcessInformation != NULL) {
CopyMemory(lpProcessInformation, &pi, sizeof(pi));
}
if (!fResult) {
return FALSE;
}
LPCSTR rlpDlls[2];
DWORD nDlls = 0;
if (lpDllName != NULL) {
rlpDlls[nDlls++] = lpDllName;
}
if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
TerminateProcess(pi.hProcess, ~0u);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(pi.hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);
PROCESS_INFORMATION pi;
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
BOOL fResult = pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwMyCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
&pi);
if (lpProcessInformation) {
CopyMemory(lpProcessInformation, &pi, sizeof(pi));
}
if (!fResult) {
return FALSE;
}
LPCSTR rlpDlls[2];
DWORD nDlls = 0;
if (lpDllName != NULL) {
rlpDlls[nDlls++] = lpDllName;
}
if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {
TerminateProcess(pi.hProcess, ~0u);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(pi.hThread);
}
return TRUE;
}
#endif // DETOURS_KERNEL
BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_In_reads_bytes_(cbData) PVOID pvData,
_In_ DWORD cbData)
{
DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) +
sizeof(IMAGE_NT_HEADERS) +
sizeof(IMAGE_SECTION_HEADER) +
sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal,
MEM_COMMIT, PAGE_READWRITE);
if (pbBase == NULL) {
DETOUR_TRACE(("VirtualAllocEx(%lu) failed: %lu\n", cbTotal, GetLastError()));
return FALSE;
}
PBYTE pbTarget = pbBase;
IMAGE_DOS_HEADER idh;
IMAGE_NT_HEADERS inh;
IMAGE_SECTION_HEADER ish;
DETOUR_SECTION_HEADER dsh;
DETOUR_SECTION_RECORD dsr;
SIZE_T cbWrote = 0;
ZeroMemory(&idh, sizeof(idh));
idh.e_magic = IMAGE_DOS_SIGNATURE;
idh.e_lfanew = sizeof(idh);
if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) ||
cbWrote != sizeof(idh)) {
DETOUR_TRACE(("WriteProcessMemory(idh) failed: %lu\n", GetLastError()));
return FALSE;
}
pbTarget += sizeof(idh);
ZeroMemory(&inh, sizeof(inh));
inh.Signature = IMAGE_NT_SIGNATURE;
inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader);
inh.FileHeader.Characteristics = IMAGE_FILE_DLL;
inh.FileHeader.NumberOfSections = 1;
inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;
if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) ||
cbWrote != sizeof(inh)) {
return FALSE;
}
pbTarget += sizeof(inh);
ZeroMemory(&ish, sizeof(ish));
memcpy(ish.Name, ".detour", sizeof(ish.Name));
ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase);
ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) ||
cbWrote != sizeof(ish)) {
return FALSE;
}
pbTarget += sizeof(ish);
ZeroMemory(&dsh, sizeof(dsh));
dsh.cbHeaderSize = sizeof(dsh);
dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;
dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER);
dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) +
sizeof(DETOUR_SECTION_RECORD) +
cbData);
if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) ||
cbWrote != sizeof(dsh)) {
return FALSE;
}
pbTarget += sizeof(dsh);
ZeroMemory(&dsr, sizeof(dsr));
dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD);
dsr.nReserved = 0;
dsr.guid = rguid;
if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) ||
cbWrote != sizeof(dsr)) {
return FALSE;
}
pbTarget += sizeof(dsr);
if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) ||
cbWrote != cbData) {
return FALSE;
}
pbTarget += cbData;
DETOUR_TRACE(("Copied %lu byte payload into target process at %p\n",
cbTotal, pbTarget - cbTotal));
return TRUE;
}
#ifndef DETOURS_KERNEL
static BOOL s_fSearchedForHelper = FALSE;
static PDETOUR_EXE_HELPER s_pHelper = NULL;
VOID CALLBACK DetourFinishHelperProcess(_In_ HWND,
_In_ HINSTANCE,
_In_ LPSTR,
_In_ INT)
{
LPCSTR * rlpDlls = NULL;
DWORD Result = 9900;
HANDLE hProcess = nullptr;
DWORD cSize = 0;
DWORD cOffset = 0;
if (s_pHelper == NULL) {
DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n"));
Result = 9905;
goto Cleanup;
}
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid);
if (hProcess == NULL) {
DETOUR_TRACE(("OpenProcess(pid=%lu) failed: %lu\n",
s_pHelper->pid, GetLastError()));
Result = 9901;
goto Cleanup;
}
rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls];
cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER);
cOffset = 0;
for (DWORD n = 0; n < s_pHelper->nDlls; n++) {
size_t cchDest = 0;
HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest);
if (!SUCCEEDED(hr)) {
Result = 9902;
goto Cleanup;
}
rlpDlls[n] = &s_pHelper->rDlls[cOffset];
cOffset += (DWORD)cchDest + 1;
}
if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) {
DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%lu) failed: %lu\n",
s_pHelper->pid, GetLastError()));
Result = 9903;
goto Cleanup;
}
Result = 0;
Cleanup:
if (rlpDlls != NULL) {
delete[] rlpDlls;
rlpDlls = NULL;
}
ExitProcess(Result);
}
BOOL WINAPI DetourIsHelperProcess(VOID)
{
PVOID pvData;
DWORD cbData;
if (s_fSearchedForHelper) {
return (s_pHelper != NULL);
}
s_fSearchedForHelper = TRUE;
pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData);
if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) {
return FALSE;
}
s_pHelper = (PDETOUR_EXE_HELPER)pvData;
if (s_pHelper->cb < sizeof(*s_pHelper)) {
s_pHelper = NULL;
return FALSE;
}
return TRUE;
}
static
BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper,
_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls)
{
PDETOUR_EXE_HELPER Helper = NULL;
BOOL Result = FALSE;
DWORD cSize = 0;
_Field_range_(0, cSize - 4) DWORD cOffset = 0;
if (pHelper == NULL) {
goto Cleanup;
}
*pHelper = NULL;
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
cSize = 4;
for (DWORD n = 0; n < nDlls; n++) {
HRESULT hr;
size_t cchDest = 0;
hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
cSize += (DWORD)cchDest + 1;
}
Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize];
if (Helper == NULL) {
goto Cleanup;
}
Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize;
Helper->pid = dwTargetPid;
Helper->nDlls = nDlls;
for (DWORD n = 0; n < nDlls; n++) {
HRESULT hr;
size_t cchDest = 0;
if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) {
goto Cleanup;
}
if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) {
goto Cleanup;
}
_Analysis_assume_(cOffset + 1 < cSize);
_Analysis_assume_(cOffset < 0x10000);
_Analysis_assume_(cSize < 0x10000);
PCHAR psz = &Helper->rDlls[cOffset];
hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call.
// REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program.
#pragma warning(suppress:28020 28313)
hr = StringCchLengthA(psz, cSize - cOffset, &cchDest);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
// Replace "32." with "64." or "64." with "32."
for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) {
#if DETOURS_32BIT
if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') {
psz[c - 3] = '6'; psz[c - 2] = '4';
break;
}
#else
if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') {
psz[c - 3] = '3'; psz[c - 2] = '2';
break;
}
#endif
}
cOffset += (DWORD)cchDest + 1;
}
*pHelper = Helper;
Helper = NULL;
Result = TRUE;
Cleanup:
if (Helper != NULL) {
delete[] (PBYTE)Helper;
Helper = NULL;
}
return Result;
}
static
VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper)
{
if (*pHelper != NULL) {
delete[] (PBYTE)*pHelper;
*pHelper = NULL;
}
}
BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA);
}
BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
BOOL Result = FALSE;
PROCESS_INFORMATION pi;
STARTUPINFOA si;
CHAR szExe[MAX_PATH];
CHAR szCommand[MAX_PATH];
PDETOUR_EXE_HELPER helper = NULL;
HRESULT hr;
DWORD nLen = 0;
DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
goto Cleanup;
}
nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe));
if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
goto Cleanup;
}
#if DETOURS_OPTION_BITS
#if DETOURS_32BIT
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe");
#else // !DETOURS_32BIT
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe");
#endif // !DETOURS_32BIT
#else // DETOURS_OPTIONS_BITS
hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe");
#endif // DETOURS_OPTIONS_BITS
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),
"rundll32.exe \"%hs\",#1", &helper->rDlls[0]);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand));
if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi)) {
if (!DetourCopyPayloadToProcess(pi.hProcess,
DETOUR_EXE_HELPER_GUID,
helper, helper->cb)) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
TerminateProcess(pi.hProcess, ~0u);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
goto Cleanup;
}
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwResult = 500;
GetExitCodeProcess(pi.hProcess, &dwResult);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (dwResult != 0) {
DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
goto Cleanup;
}
Result = TRUE;
}
else {
DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
goto Cleanup;
}
Cleanup:
FreeExeHelper(&helper);
return Result;
}
BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW);
}
BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
BOOL Result = FALSE;
PROCESS_INFORMATION pi;
STARTUPINFOW si;
WCHAR szExe[MAX_PATH];
WCHAR szCommand[MAX_PATH];
PDETOUR_EXE_HELPER helper = NULL;
HRESULT hr;
DWORD nLen = 0;
DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls));
if (nDlls < 1 || nDlls > 4096) {
SetLastError(ERROR_INVALID_PARAMETER);
goto Cleanup;
}
if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {
goto Cleanup;
}
nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe));
if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {
goto Cleanup;
}
#if DETOURS_OPTION_BITS
#if DETOURS_32BIT
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe");
#else // !DETOURS_32BIT
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe");
#endif // !DETOURS_32BIT
#else // DETOURS_OPTIONS_BITS
hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe");
#endif // DETOURS_OPTIONS_BITS
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand),
L"rundll32.exe \"%hs\",#1", &helper->rDlls[0]);
if (!SUCCEEDED(hr)) {
goto Cleanup;
}
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand));
if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,
NULL, NULL, &si, &pi)) {
if (!DetourCopyPayloadToProcess(pi.hProcess,
DETOUR_EXE_HELPER_GUID,
helper, helper->cb)) {
DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError()));
TerminateProcess(pi.hProcess, ~0u);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
goto Cleanup;
}
ResumeThread(pi.hThread);
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwResult = 500;
GetExitCodeProcess(pi.hProcess, &dwResult);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (dwResult != 0) {
DETOUR_TRACE(("Rundll32.exe failed: result=%lu\n", dwResult));
goto Cleanup;
}
Result = TRUE;
}
else {
DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError()));
goto Cleanup;
}
Cleanup:
FreeExeHelper(&helper);
return Result;
}
BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
LPCSTR szDll = lpDllName;
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) &&
!DetourProcessViaHelperA(lpProcessInformation->dwProcessId,
lpDllName,
pfCreateProcessA)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
LPCSTR sz = lpDllName;
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) &&
!DetourProcessViaHelperW(lpProcessInformation->dwProcessId,
lpDllName,
pfCreateProcessW)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)
{
if (pfCreateProcessA == NULL) {
pfCreateProcessA = CreateProcessA;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
!DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId,
nDlls,
rlpDlls,
pfCreateProcessA)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
{
if (pfCreateProcessW == NULL) {
pfCreateProcessW = CreateProcessW;
}
PROCESS_INFORMATION backup;
if (lpProcessInformation == NULL) {
lpProcessInformation = &backup;
ZeroMemory(&backup, sizeof(backup));
}
if (!pfCreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation)) {
return FALSE;
}
if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&
!DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId,
nDlls,
rlpDlls,
pfCreateProcessW)) {
TerminateProcess(lpProcessInformation->hProcess, ~0u);
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
return FALSE;
}
if (!(dwCreationFlags & CREATE_SUSPENDED)) {
ResumeThread(lpProcessInformation->hThread);
}
if (lpProcessInformation == &backup) {
CloseHandle(lpProcessInformation->hProcess);
CloseHandle(lpProcessInformation->hThread);
}
return TRUE;
}
#endif // !DETOURS_KERNEL
//
///////////////////////////////////////////////////////////////// End of File.
================================================
FILE: Detours/detours.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include
#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
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define NOTHROW
//////////////////////////////////////////////////////////////////////////////
//
struct _DETOUR_ALIGN
{
BYTE obTarget : 3;
BYTE obTrampoline : 5;
};
C_ASSERT(sizeof(_DETOUR_ALIGN) == 1);
//////////////////////////////////////////////////////////////////////////////
//
// Region reserved for system DLLs, which cannot be used for trampolines.
//
static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000;
static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000;
//////////////////////////////////////////////////////////////////////////////
//
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
}
#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.")
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
return false;
}
return false;
}
inline ULONG_PTR detour_2gb_below(ULONG_PTR address)
{
return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;
}
inline ULONG_PTR detour_2gb_above(ULONG_PTR address)
{
#if defined(DETOURS_64BIT)
return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;
#else
return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;
#endif
}
///////////////////////////////////////////////////////////////////////// X86.
//
#ifdef DETOURS_X86
struct _DETOUR_TRAMPOLINE
{
BYTE rbCode[30]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%Ix..%p..%Ix]\n", lo, pbCode, hi));
// And, within +/- 2GB of relative jmp targets.
if (pbCode[0] == 0xe9) { // jmp +imm32
PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%Ix..%p..%Ix] +imm32\n", lo, pbCode, hi));
}
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X86
///////////////////////////////////////////////////////////////////////// X64.
//
#ifdef DETOURS_X64
struct _DETOUR_TRAMPOLINE
{
// An X64 instuction can be 15 bytes long.
// In practice 11 seems to be the limit.
BYTE rbCode[30]; // target code + jmp to pbRemain.
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[30]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
BYTE rbCodeIn[8]; // jmp [pbDetour]
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96);
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 6;
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%Ix..%p..%Ix]\n", lo, pbCode, hi));
// And, within +/- 2GB of relative jmp vectors.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%Ix..%p..%Ix] [+imm32]\n", lo, pbCode, hi));
}
// And, within +/- 2GB of relative jmp targets.
else if (pbCode[0] == 0xe9) { // jmp +imm32
PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%Ix..%p..%Ix] +imm32\n", lo, pbCode, hi));
}
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X64
//////////////////////////////////////////////////////////////////////// IA64.
//
#ifdef DETOURS_IA64
struct _DETOUR_TRAMPOLINE
{
// On the IA64, a trampoline is used for both incoming and outgoing calls.
//
// The trampoline contains the following bundles for the outgoing call:
// movl gp=target_gp;
//
// brl target_code;
//
// The trampoline contains the following bundles for the incoming call:
// alloc r41=ar.pfs, b, 0, 8, 0
// mov r40=rp
//
// adds r50=0, r39
// adds r49=0, r38
// adds r48=0, r37 ;;
//
// adds r47=0, r36
// adds r46=0, r35
// adds r45=0, r34
//
// adds r44=0, r33
// adds r43=0, r32
// adds r42=0, gp ;;
//
// movl gp=ffffffff`ffffffff ;;
//
// brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;;
//
// adds gp=0, r42
// mov rp=r40, +0 ;;
// mov.i ar.pfs=r41
//
// br.ret.sptk.many rp ;;
//
// This way, we only have to relocate a single bundle.
//
// The complicated incoming trampoline is required because we have to
// create an additional stack frame so that we save and restore the gp.
// We must do this because gp is a caller-saved register, but not saved
// if the caller thinks the target is in the same DLL, which changes
// when we insert a detour.
//
DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP
BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle.
DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain
// This must be adjacent to bBranchIslands.
// Each instruction in the moved bundle could be a IP-relative chk or branch or call.
// Any such instructions are changed to point to a brl in bBranchIslands.
// This must be adjacent to bBrlRemainEip -- see "pbPool".
DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE];
// Target of brl inserted in target function
DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame
DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39.
DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36.
DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33.
DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP.
DETOUR_IA64_BUNDLE bCallDetour; // call detour.
DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp.
DETOUR_IA64_BUNDLE bReturn; // return to caller.
PLABEL_DESCRIPTOR pldTrampoline;
BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle.
BYTE cbRestore; // size of original target code.
BYTE cbCode; // size of moved target code.
_DETOUR_ALIGN rAlign[14]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour]
PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour]
};
C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16);
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16);
enum {
SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE)
};
inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals)
{
PBYTE pGlobals = NULL;
PBYTE pbCode = NULL;
if (pPointer != NULL) {
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
}
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
if (pbCode == NULL) {
return NULL;
}
DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode;
// IA64 Local Import Jumps look like:
// addl r2=ffffffff`ffe021c0, gp ;;
// ld8 r2=[r2]
// nop.i 0 ;;
//
// ld8 r3=[r2], 8 ;;
// ld8 gp=[r2]
// mov b6=r3, +0
//
// nop.m 0
// nop.i 0
// br.cond.sptk.few b6
//
// 002024000200100b
if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&
pb[0].wide[1] == 0x0004000000203008 &&
pb[1].wide[0] == 0x001014180420180a &&
pb[1].wide[1] == 0x07000830c0203008 &&
pb[2].wide[0] == 0x0000000100000010 &&
pb[2].wide[1] == 0x0080006000000200) {
ULONG64 offset =
((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b
((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d
((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c
if (pb[0].wide[0] & 0x0000020000000000) { // sign
offset |= 0xffffffffffe00000;
}
PBYTE pbTarget = pGlobals + offset;
DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget));
if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) {
DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget));
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
(void)pbCode;
*ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;
*ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return 0;
}
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
struct _DETOUR_TRAMPOLINE
{
// A Thumb-2 instruction can be 2 or 4 bytes long.
BYTE rbCode[62]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);
enum {
SIZE_OF_JMP = 8
};
inline PBYTE align4(PBYTE pValue)
{
return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u);
}
inline ULONG fetch_thumb_opcode(PBYTE pbCode)
{
ULONG Opcode = *(UINT16 *)&pbCode[0];
if (Opcode >= 0xe800) {
Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];
}
return Opcode;
}
inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode)
{
if (Opcode >= 0x10000) {
*((UINT16*&)pbCode)++ = Opcode >> 16;
}
*((UINT16*&)pbCode)++ = (UINT16)Opcode;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 4;
pbLiteral = *ppPool;
}
else {
pbLiteral = align4(pbCode + 6);
}
*((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal);
LONG delta = pbLiteral - align4(pbCode + 4);
write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n]
if (ppPool == NULL) {
if (((ULONG)pbCode & 2) != 0) {
write_thumb_opcode(pbCode, 0xdefe); // BREAK
}
pbCode += 4;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_thumb_opcode(pbCode, 0xdefe);
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode);
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx
ULONG Opcode2 = fetch_thumb_opcode(pbCode+4);
if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx
ULONG Opcode3 = fetch_thumb_opcode(pbCode+8);
if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12]
PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) |
((Opcode2 << 1) & 0x08000000) |
((Opcode2 << 16) & 0x00ff0000) |
((Opcode >> 4) & 0x0000f700) |
((Opcode >> 15) & 0x00000800) |
((Opcode >> 0) & 0x000000ff));
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew);
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", lo, pbCode, hi));
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xffffff87) == 0x4700 || // bx
(Opcode & 0xf800d000) == 0xf0009000) { // b
return TRUE;
}
if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc}
__debugbreak();
return TRUE;
}
if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc}
__debugbreak();
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop.
return 2;
}
if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding.
return 2;
}
return 0;
}
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
struct _DETOUR_TRAMPOLINE
{
// An ARM64 instruction is 4 bytes long.
BYTE rbCode[64]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak[3]; // padding to make debugging easier.
BYTE rbRestore[24]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak[3]; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 120);
enum {
SIZE_OF_JMP = 8
};
inline ULONG fetch_opcode(PBYTE pbCode)
{
return *(ULONG *)pbCode;
}
inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
{
*(ULONG *)pbCode = Opcode;
pbCode += 4;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 8;
pbLiteral = *ppPool;
}
else {
pbLiteral = pbCode + 2*4;
}
*((PBYTE*&)pbLiteral) = pbJmpVal;
LONG delta = (LONG)(pbLiteral - pbCode);
write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n]
write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17
if (ppPool == NULL) {
pbCode += 8;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_opcode(pbCode, 0xd4100000 | (0xf000 << 5));
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)pbCode;
ULONG Opcode = fetch_opcode(pbCode);
if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT
ULONG Opcode2 = fetch_opcode(pbCode+4);
if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT]
ULONG Opcode3 = fetch_opcode(pbCode+8);
if (Opcode3 == 0xd61f0200) { // br x16
ULONG PageOffset = ((Opcode & 0x60000000) >> 29) | ((Opcode & 0x00ffffe0) >> 3);
PageOffset = (LONG)(Opcode << 11) >> 11;
PBYTE pbTarget = (PBYTE)(((ULONG64)pbCode & 0xfffffffffffff000ULL) + PageOffset +
((Opcode2 >> 10) & 0xfff));
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE* ppLower,
PDETOUR_TRAMPOLINE* ppUpper)
{
// The encoding used by detour_gen_jmp_indirect actually enables a
// displacement of +/- 4GiB. In the future, this could be changed to
// reflect that. For now, just reuse the x86 logic which is plenty.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
ULONG Opcode = fetch_opcode(pbCode);
if ((Opcode & 0xfffffc1f) == 0xd65f0000 || // br
(Opcode & 0xfc000000) == 0x14000000) { // b
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
if (*(ULONG *)pbCode == 0xd503201f) { // nop.
return 4;
}
if (*(ULONG *)pbCode == 0x00000000) { // zero-filled padding.
return 4;
}
return 0;
}
#endif // DETOURS_ARM64
//////////////////////////////////////////////// Trampoline Memory Management.
//
struct DETOUR_REGION
{
ULONG dwSignature;
DETOUR_REGION * pNext; // Next region in list of regions.
DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region.
};
typedef DETOUR_REGION * PDETOUR_REGION;
const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';
const ULONG DETOUR_REGION_SIZE = 0x10000;
const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE
/ sizeof(DETOUR_TRAMPOLINE)) - 1;
static PDETOUR_REGION s_pRegions = NULL; // List of all regions.
static PDETOUR_REGION s_pRegion = NULL; // Default region.
static DWORD detour_writable_trampoline_regions()
{
// Mark all of the regions as writable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
DWORD dwOld;
if (!VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld)) {
return GetLastError();
}
}
return NO_ERROR;
}
static void detour_runnable_trampoline_regions()
{
HANDLE hProcess = GetCurrentProcess();
// Mark all of the regions as executable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
DWORD dwOld;
VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld);
FlushInstructionCache(hProcess, pRegion, DETOUR_REGION_SIZE);
}
}
static PBYTE detour_alloc_round_down_to_region(PBYTE pbTry)
{
// WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.
ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
if (extra != 0) {
pbTry -= extra;
}
return pbTry;
}
static PBYTE detour_alloc_round_up_to_region(PBYTE pbTry)
{
// WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.
ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
if (extra != 0) {
ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;
pbTry += adjust;
}
return pbTry;
}
// Starting at pbLo, try to allocate a memory region, continue until pbHi.
static PVOID detour_alloc_region_from_lo(PBYTE pbLo, PBYTE pbHi)
{
PBYTE pbTry = detour_alloc_round_up_to_region(pbLo);
DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry));
for (; pbTry < pbHi;) {
MEMORY_BASIC_INFORMATION mbi;
if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) {
// Skip region reserved for system DLLs, but preserve address space entropy.
pbTry += 0x08000000;
continue;
}
ZeroMemory(&mbi, sizeof(mbi));
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
break;
}
DETOUR_TRACE((" Try %p => %p..%p %6lx\n",
pbTry,
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
mbi.State));
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
PVOID pv = VirtualAlloc(pbTry,
DETOUR_REGION_SIZE,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (pv != NULL) {
return pv;
}
pbTry += DETOUR_REGION_SIZE;
}
else {
pbTry = detour_alloc_round_up_to_region((PBYTE)mbi.BaseAddress + mbi.RegionSize);
}
}
return NULL;
}
// Starting at pbHi, try to allocate a memory region, continue until pbLo.
static PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi)
{
PBYTE pbTry = detour_alloc_round_down_to_region(pbHi - DETOUR_REGION_SIZE);
DETOUR_TRACE((" Looking for free region in %p..%p from %p:\n", pbLo, pbHi, pbTry));
for (; pbTry > pbLo;) {
MEMORY_BASIC_INFORMATION mbi;
DETOUR_TRACE((" Try %p\n", pbTry));
if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) {
// Skip region reserved for system DLLs, but preserve address space entropy.
pbTry -= 0x08000000;
continue;
}
ZeroMemory(&mbi, sizeof(mbi));
if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {
break;
}
DETOUR_TRACE((" Try %p => %p..%p %6lx\n",
pbTry,
mbi.BaseAddress,
(PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,
mbi.State));
if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {
PVOID pv = VirtualAlloc(pbTry,
DETOUR_REGION_SIZE,
MEM_COMMIT|MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (pv != NULL) {
return pv;
}
pbTry -= DETOUR_REGION_SIZE;
}
else {
pbTry = detour_alloc_round_down_to_region((PBYTE)mbi.AllocationBase
- DETOUR_REGION_SIZE);
}
}
return NULL;
}
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
{
// We have to place trampolines within +/- 2GB of target.
PDETOUR_TRAMPOLINE pLo;
PDETOUR_TRAMPOLINE pHi;
detour_find_jmp_bounds(pbTarget, &pLo, &pHi);
PDETOUR_TRAMPOLINE pTrampoline = NULL;
// Insure that there is a default region.
if (s_pRegion == NULL && s_pRegions != NULL) {
s_pRegion = s_pRegions;
}
// First check the default region for an valid free block.
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
found_region:
pTrampoline = s_pRegion->pFree;
// do a last sanity check on region.
if (pTrampoline < pLo || pTrampoline > pHi) {
return NULL;
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;
memset(pTrampoline, 0xcc, sizeof(*pTrampoline));
return pTrampoline;
}
// Then check the existing regions for a valid free block.
for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
goto found_region;
}
}
// We need to allocate a new region.
// Round pbTarget down to 64KB block.
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
PVOID pbTry = NULL;
// NB: We must always also start the search at an offset from pbTarget
// in order to maintain ASLR entropy.
#if defined(DETOURS_64BIT)
// Try looking 1GB below or lower.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);
}
// Try looking 1GB above or higher.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);
}
// Try looking 1GB below or higher.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);
}
// Try looking 1GB above or lower.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);
}
#endif
// Try anything below.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);
}
// try anything above.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);
}
if (pbTry != NULL) {
s_pRegion = (DETOUR_REGION*)pbTry;
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
s_pRegion->pFree = NULL;
s_pRegion->pNext = s_pRegions;
s_pRegions = s_pRegion;
DETOUR_TRACE((" Allocated region %p..%p\n\n",
(void*)s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));
// Put everything but the first trampoline on the free list.
PBYTE pFree = NULL;
pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;
for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {
pTrampoline[i].pbRemain = pFree;
pFree = (PBYTE)&pTrampoline[i];
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;
goto found_region;
}
DETOUR_TRACE(("Couldn't find available memory region!\n"));
return NULL;
}
static void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline)
{
PDETOUR_REGION pRegion = (PDETOUR_REGION)
((ULONG_PTR)pTrampoline & ~(ULONG_PTR)0xffff);
memset(pTrampoline, 0, sizeof(*pTrampoline));
pTrampoline->pbRemain = (PBYTE)pRegion->pFree;
pRegion->pFree = pTrampoline;
}
static BOOL detour_is_region_empty(PDETOUR_REGION pRegion)
{
// Stop if the region isn't a region (this would be bad).
if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) {
return FALSE;
}
PBYTE pbRegionBeg = (PBYTE)pRegion;
PBYTE pbRegionLim = pbRegionBeg + DETOUR_REGION_SIZE;
// Stop if any of the trampolines aren't free.
PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1;
for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) {
if (pTrampoline[i].pbRemain != NULL &&
(pTrampoline[i].pbRemain < pbRegionBeg ||
pTrampoline[i].pbRemain >= pbRegionLim)) {
return FALSE;
}
}
// OK, the region is empty.
return TRUE;
}
static void detour_free_unused_trampoline_regions()
{
PDETOUR_REGION *ppRegionBase = &s_pRegions;
PDETOUR_REGION pRegion = s_pRegions;
while (pRegion != NULL) {
if (detour_is_region_empty(pRegion)) {
*ppRegionBase = pRegion->pNext;
VirtualFree(pRegion, 0, MEM_RELEASE);
s_pRegion = NULL;
}
else {
ppRegionBase = &pRegion->pNext;
}
pRegion = *ppRegionBase;
}
}
///////////////////////////////////////////////////////// Transaction Structs.
//
struct DetourThread
{
DetourThread * pNext;
HANDLE hThread;
};
struct DetourOperation
{
DetourOperation * pNext;
BOOL fIsRemove;
PBYTE * ppbPointer;
PBYTE pbTarget;
PDETOUR_TRAMPOLINE pTrampoline;
ULONG dwPerm;
};
static BOOL s_fIgnoreTooSmall = FALSE;
static BOOL s_fRetainRegions = FALSE;
static LONG s_nPendingThreadId = 0; // Thread owning pending transaction.
static LONG s_nPendingError = NO_ERROR;
static PVOID * s_ppPendingError = NULL;
static DetourThread * s_pPendingThreads = NULL;
static DetourOperation * s_pPendingOperations = NULL;
//////////////////////////////////////////////////////////////////////////////
//
PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,
_Out_opt_ PVOID *ppGlobals)
{
return detour_skip_jmp((PBYTE)pPointer, ppGlobals);
}
//////////////////////////////////////////////////////////// Transaction APIs.
//
BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore)
{
BOOL fPrevious = s_fIgnoreTooSmall;
s_fIgnoreTooSmall = fIgnore;
return fPrevious;
}
BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain)
{
BOOL fPrevious = s_fRetainRegions;
s_fRetainRegions = fRetain;
return fPrevious;
}
PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound)
{
PVOID pPrevious = s_pSystemRegionLowerBound;
s_pSystemRegionLowerBound = pSystemRegionLowerBound;
return pPrevious;
}
PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound)
{
PVOID pPrevious = s_pSystemRegionUpperBound;
s_pSystemRegionUpperBound = pSystemRegionUpperBound;
return pPrevious;
}
LONG WINAPI DetourTransactionBegin()
{
// Only one transaction is allowed at a time.
_Benign_race_begin_
if (s_nPendingThreadId != 0) {
return ERROR_INVALID_OPERATION;
}
_Benign_race_end_
// Make sure only one thread can start a transaction.
if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {
return ERROR_INVALID_OPERATION;
}
s_pPendingOperations = NULL;
s_pPendingThreads = NULL;
s_ppPendingError = NULL;
// Make sure the trampoline pages are writable.
s_nPendingError = detour_writable_trampoline_regions();
return s_nPendingError;
}
LONG WINAPI DetourTransactionAbort()
{
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// Restore all of the page permissions.
for (DetourOperation *o = s_pPendingOperations; o != NULL;) {
// We don't care if this fails, because the code is still accessible.
DWORD dwOld;
VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore,
o->dwPerm, &dwOld);
if (!o->fIsRemove) {
if (o->pTrampoline) {
detour_free_trampoline(o->pTrampoline);
o->pTrampoline = NULL;
}
}
DetourOperation *n = o->pNext;
delete o;
o = n;
}
s_pPendingOperations = NULL;
// Make sure the trampoline pages are no longer writable.
detour_runnable_trampoline_regions();
// Resume any suspended threads.
for (DetourThread *t = s_pPendingThreads; t != NULL;) {
// There is nothing we can do if this fails.
ResumeThread(t->hThread);
DetourThread *n = t->pNext;
delete t;
t = n;
}
s_pPendingThreads = NULL;
s_nPendingThreadId = 0;
return NO_ERROR;
}
LONG WINAPI DetourTransactionCommit()
{
return DetourTransactionCommitEx(NULL);
}
static BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline)
{
for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) {
return pTrampoline->rAlign[n].obTarget;
}
}
return 0;
}
static LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget)
{
for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTarget == obTarget) {
return pTrampoline->rAlign[n].obTrampoline;
}
}
return 0;
}
LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)
{
if (pppFailedPointer != NULL) {
// Used to get the last error.
*pppFailedPointer = s_ppPendingError;
}
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we abort the whole transaction.
if (s_nPendingError != NO_ERROR) {
DETOUR_BREAK();
DetourTransactionAbort();
return s_nPendingError;
}
// Common variables.
DetourOperation *o;
DetourThread *t;
BOOL freed = FALSE;
// Insert or remove each of the detours.
for (o = s_pPendingOperations; o != NULL; o = o->pNext) {
if (o->fIsRemove) {
CopyMemory(o->pbTarget,
o->pTrampoline->rbRestore,
o->pTrampoline->cbRestore);
#ifdef DETOURS_IA64
*o->ppbPointer = (PBYTE)o->pTrampoline->ppldTarget;
#endif // DETOURS_IA64
#ifdef DETOURS_X86
*o->ppbPointer = o->pbTarget;
#endif // DETOURS_X86
#ifdef DETOURS_X64
*o->ppbPointer = o->pbTarget;
#endif // DETOURS_X64
#ifdef DETOURS_ARM
*o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pbTarget);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
*o->ppbPointer = o->pbTarget;
#endif // DETOURS_ARM
}
else {
DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\n",
(void*)o->pTrampoline,
o->pTrampoline->pbRemain,
o->pTrampoline->pbDetour,
o->pTrampoline->cbRestore));
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x [before]\n",
o->pbTarget,
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
#ifdef DETOURS_IA64
((DETOUR_IA64_BUNDLE*)o->pbTarget)
->SetBrl((UINT64)&o->pTrampoline->bAllocFrame);
*o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline;
#endif // DETOURS_IA64
#ifdef DETOURS_X64
detour_gen_jmp_indirect(o->pTrampoline->rbCodeIn, &o->pTrampoline->pbDetour);
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->rbCodeIn);
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_X64
#ifdef DETOURS_X86
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_X86
#ifdef DETOURS_ARM
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode);
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_ARM64
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x [after]\n",
o->pbTarget,
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
DETOUR_TRACE(("detours: pbTramp =%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
(void*)o->pTrampoline,
o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1],
o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3],
o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5],
o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7],
o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9],
o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11]));
#ifdef DETOURS_IA64
DETOUR_TRACE(("\n"));
DETOUR_TRACE(("detours: &pldTrampoline =%p\n",
&o->pTrampoline->pldTrampoline));
DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n",
&o->pTrampoline->bMovlTargetGp,
o->pTrampoline->bMovlTargetGp.GetMovlGp()));
DETOUR_TRACE(("detours: &rbCode =%p [%p]\n",
&o->pTrampoline->rbCode,
((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget()));
DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n",
&o->pTrampoline->bBrlRemainEip,
o->pTrampoline->bBrlRemainEip.GetBrlTarget()));
DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n",
&o->pTrampoline->bMovlDetourGp,
o->pTrampoline->bMovlDetourGp.GetMovlGp()));
DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n",
&o->pTrampoline->bCallDetour,
o->pTrampoline->bCallDetour.GetBrlTarget()));
DETOUR_TRACE(("detours: pldDetour =%p [%p]\n",
o->pTrampoline->ppldDetour->EntryPoint,
o->pTrampoline->ppldDetour->GlobalPointer));
DETOUR_TRACE(("detours: pldTarget =%p [%p]\n",
o->pTrampoline->ppldTarget->EntryPoint,
o->pTrampoline->ppldTarget->GlobalPointer));
DETOUR_TRACE(("detours: pbRemain =%p\n",
o->pTrampoline->pbRemain));
DETOUR_TRACE(("detours: pbDetour =%p\n",
o->pTrampoline->pbDetour));
DETOUR_TRACE(("\n"));
#endif // DETOURS_IA64
}
}
// Update any suspended threads.
for (t = s_pPendingThreads; t != NULL; t = t->pNext) {
CONTEXT cxt;
cxt.ContextFlags = CONTEXT_CONTROL;
#undef DETOURS_EIP
#ifdef DETOURS_X86
#define DETOURS_EIP Eip
#endif // DETOURS_X86
#ifdef DETOURS_X64
#define DETOURS_EIP Rip
#endif // DETOURS_X64
#ifdef DETOURS_IA64
#define DETOURS_EIP StIIP
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
#define DETOURS_EIP Pc
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
#define DETOURS_EIP Pc
#endif // DETOURS_ARM64
typedef ULONG_PTR DETOURS_EIP_TYPE;
if (GetThreadContext(t->hThread, &cxt)) {
for (o = s_pPendingOperations; o != NULL; o = o->pNext) {
if (o->fIsRemove) {
if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline &&
cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pTrampoline
+ sizeof(o->pTrampoline))
) {
cxt.DETOURS_EIP = (DETOURS_EIP_TYPE)
((ULONG_PTR)o->pbTarget
+ detour_align_from_trampoline(o->pTrampoline,
(BYTE)(cxt.DETOURS_EIP
- (DETOURS_EIP_TYPE)(ULONG_PTR)
o->pTrampoline)));
SetThreadContext(t->hThread, &cxt);
}
}
else {
if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget &&
cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pbTarget
+ o->pTrampoline->cbRestore)
) {
cxt.DETOURS_EIP = (DETOURS_EIP_TYPE)
((ULONG_PTR)o->pTrampoline
+ detour_align_from_target(o->pTrampoline,
(BYTE)(cxt.DETOURS_EIP
- (DETOURS_EIP_TYPE)(ULONG_PTR)
o->pbTarget)));
SetThreadContext(t->hThread, &cxt);
}
}
}
}
#undef DETOURS_EIP
}
// Restore all of the page permissions and flush the icache.
HANDLE hProcess = GetCurrentProcess();
for (o = s_pPendingOperations; o != NULL;) {
// We don't care if this fails, because the code is still accessible.
DWORD dwOld;
VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, o->dwPerm, &dwOld);
FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbRestore);
if (o->fIsRemove && o->pTrampoline) {
detour_free_trampoline(o->pTrampoline);
o->pTrampoline = NULL;
freed = true;
}
DetourOperation *n = o->pNext;
delete o;
o = n;
}
s_pPendingOperations = NULL;
// Free any trampoline regions that are now unused.
if (freed && !s_fRetainRegions) {
detour_free_unused_trampoline_regions();
}
// Make sure the trampoline pages are no longer writable.
detour_runnable_trampoline_regions();
// Resume any suspended threads.
for (t = s_pPendingThreads; t != NULL;) {
// There is nothing we can do if this fails.
ResumeThread(t->hThread);
DetourThread *n = t->pNext;
delete t;
t = n;
}
s_pPendingThreads = NULL;
s_nPendingThreadId = 0;
if (pppFailedPointer != NULL) {
*pppFailedPointer = s_ppPendingError;
}
return s_nPendingError;
}
LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread)
{
LONG error;
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
return s_nPendingError;
}
// Silently (and safely) drop any attempt to suspend our own thread.
if (hThread == GetCurrentThread()) {
return NO_ERROR;
}
DetourThread *t = new NOTHROW DetourThread;
if (t == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
if (t != NULL) {
delete t;
t = NULL;
}
s_nPendingError = error;
s_ppPendingError = NULL;
DETOUR_BREAK();
return error;
}
if (SuspendThread(hThread) == (DWORD)-1) {
error = (LONG)GetLastError();
DETOUR_BREAK();
goto fail;
}
t->hThread = hThread;
t->pNext = s_pPendingThreads;
s_pPendingThreads = t;
return NO_ERROR;
}
///////////////////////////////////////////////////////////// Transacted APIs.
//
LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour)
{
return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);
}
LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour,
_Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,
_Out_opt_ PVOID *ppRealTarget,
_Out_opt_ PVOID *ppRealDetour)
{
LONG error = NO_ERROR;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
if (ppRealTarget != NULL) {
*ppRealTarget = NULL;
}
if (ppRealDetour != NULL) {
*ppRealDetour = NULL;
}
if (pDetour == NULL) {
DETOUR_TRACE(("empty detour\n"));
return ERROR_INVALID_PARAMETER;
}
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
DETOUR_TRACE(("transaction conflict with thread id=%ld\n", s_nPendingThreadId));
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
DETOUR_TRACE(("pending transaction error=%ld\n", s_nPendingError));
return s_nPendingError;
}
if (ppPointer == NULL) {
DETOUR_TRACE(("ppPointer is null\n"));
return ERROR_INVALID_HANDLE;
}
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
s_nPendingError = error;
s_ppPendingError = ppPointer;
DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", (void*)ppPointer));
DETOUR_BREAK();
return error;
}
PBYTE pbTarget = (PBYTE)*ppPointer;
PDETOUR_TRAMPOLINE pTrampoline = NULL;
DetourOperation *o = NULL;
#ifdef DETOURS_IA64
PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;
PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget;
PVOID pDetourGlobals = NULL;
PVOID pTargetGlobals = NULL;
pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);
pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals);
DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n",
ppldDetour, pDetour, pDetourGlobals));
DETOUR_TRACE((" ppldTarget=%p, code=%p [gp=%p]\n",
ppldTarget, pbTarget, pTargetGlobals));
#else // DETOURS_IA64
pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
#endif // !DETOURS_IA64
// Don't follow a jump if its destination is the target function.
// This happens when the detour does nothing other than call the target.
if (pDetour == (PVOID)pbTarget) {
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (ppRealTarget != NULL) {
*ppRealTarget = pbTarget;
}
if (ppRealDetour != NULL) {
*ppRealDetour = pDetour;
}
o = new NOTHROW DetourOperation;
if (o == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
s_nPendingError = error;
DETOUR_BREAK();
stop:
if (pTrampoline != NULL) {
detour_free_trampoline(pTrampoline);
pTrampoline = NULL;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
}
if (o != NULL) {
delete o;
o = NULL;
}
s_ppPendingError = ppPointer;
return error;
}
pTrampoline = detour_alloc_trampoline(pbTarget);
if (pTrampoline == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
DETOUR_BREAK();
goto fail;
}
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = pTrampoline;
}
DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", (void*)pTrampoline, pDetour));
memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign));
// Determine the number of movable target instructions.
PBYTE pbSrc = pbTarget;
PBYTE pbTrampoline = pTrampoline->rbCode;
#ifdef DETOURS_IA64
PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1);
#else
PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode);
#endif
ULONG cbTarget = 0;
ULONG cbJump = SIZE_OF_JMP;
ULONG nAlign = 0;
#ifdef DETOURS_ARM
// On ARM, we need an extra instruction when the function isn't 32-bit aligned.
// Check if the existing code is another detour (or at least a similar
// "ldr pc, [PC+0]" jump.
if ((ULONG)pbTarget & 2) {
cbJump += 2;
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xbf00) {
op = fetch_thumb_opcode(pbSrc + 2);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
}
else {
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
#endif
while (cbTarget < cbJump) {
PBYTE pbOp = pbSrc;
LONG lExtra = 0;
DETOUR_TRACE((" DetourCopyInstruction(%p,%p)\n",
pbTrampoline, pbSrc));
pbSrc = (PBYTE)
DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra);
DETOUR_TRACE((" DetourCopyInstruction() = %p (%d bytes)\n",
pbSrc, (int)(pbSrc - pbOp)));
pbTrampoline += (pbSrc - pbOp) + lExtra;
cbTarget = (LONG)(pbSrc - pbTarget);
pTrampoline->rAlign[nAlign].obTarget = cbTarget;
pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode;
nAlign++;
if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) {
break;
}
if (detour_does_code_end_function(pbOp)) {
break;
}
}
// Consume, but don't duplicate padding if it is needed and available.
while (cbTarget < cbJump) {
LONG cFiller = detour_is_code_filler(pbSrc);
if (cFiller == 0) {
break;
}
pbSrc += cFiller;
cbTarget = (LONG)(pbSrc - pbTarget);
}
#if DETOUR_DEBUG
{
DETOUR_TRACE((" detours: rAlign ["));
LONG n = 0;
for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTarget == 0 &&
pTrampoline->rAlign[n].obTrampoline == 0) {
break;
}
DETOUR_TRACE((" %d/%d",
pTrampoline->rAlign[n].obTarget,
pTrampoline->rAlign[n].obTrampoline
));
}
DETOUR_TRACE((" ]\n"));
}
#endif
if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) {
// Too few instructions.
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (pbTrampoline > pbPool) {
__debugbreak();
}
pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode);
pTrampoline->cbRestore = (BYTE)cbTarget;
CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget);
#if !defined(DETOURS_IA64)
if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) {
// Too many instructions.
error = ERROR_INVALID_HANDLE;
DETOUR_BREAK();
goto fail;
}
#endif // !DETOURS_IA64
pTrampoline->pbRemain = pbTarget + cbTarget;
pTrampoline->pbDetour = (PBYTE)pDetour;
#ifdef DETOURS_IA64
pTrampoline->ppldDetour = ppldDetour;
pTrampoline->ppldTarget = ppldTarget;
pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp;
pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals;
((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop();
pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals);
pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain);
// Alloc frame: alloc r41=ar.pfs,11,0,8,0; mov r40=rp
pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c;
pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200;
// save r36, r37, r38.
pTrampoline->bSave37to39.wide[0] = 0x031021004e019001;
pTrampoline->bSave37to39.wide[1] = 0x8401280600420098;
// save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34
pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800;
pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c;
// save gp,r32,r33" adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;;
pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001;
pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080;
// set detour GP.
pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals);
// call detour: brl.call.sptk.few rp=detour ;;
pTrampoline->bCallDetour.wide[0] = 0x0000000100000005;
pTrampoline->bCallDetour.wide[1] = 0xd000001000000000;
pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour);
// pop frame & gp: adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41
pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802;
pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005;
// return to caller: br.ret.sptk.many rp ;;
pTrampoline->bReturn.wide[0] = 0x0000000100000019;
pTrampoline->bReturn.wide[1] = 0x0084000880000200;
DETOUR_TRACE(("detours: &bMovlTargetGp=%p\n", &pTrampoline->bMovlTargetGp));
DETOUR_TRACE(("detours: &bMovlDetourGp=%p\n", &pTrampoline->bMovlDetourGp));
#endif // DETOURS_IA64
pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode;
#ifdef DETOURS_X64
pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X64
#ifdef DETOURS_X86
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X86
#ifdef DETOURS_ARM
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM64
(void)pbTrampoline;
DWORD dwOld = 0;
if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) {
error = GetLastError();
DETOUR_BREAK();
goto fail;
}
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
pbTarget,
pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],
pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],
pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));
DETOUR_TRACE(("detours: pbTramp =%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
(void*)pTrampoline,
pTrampoline->rbCode[0], pTrampoline->rbCode[1],
pTrampoline->rbCode[2], pTrampoline->rbCode[3],
pTrampoline->rbCode[4], pTrampoline->rbCode[5],
pTrampoline->rbCode[6], pTrampoline->rbCode[7],
pTrampoline->rbCode[8], pTrampoline->rbCode[9],
pTrampoline->rbCode[10], pTrampoline->rbCode[11]));
o->fIsRemove = FALSE;
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->dwPerm = dwOld;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}
LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour)
{
LONG error = NO_ERROR;
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
return s_nPendingError;
}
if (pDetour == NULL) {
return ERROR_INVALID_PARAMETER;
}
if (ppPointer == NULL) {
return ERROR_INVALID_HANDLE;
}
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
s_nPendingError = error;
s_ppPendingError = ppPointer;
DETOUR_BREAK();
return error;
}
DetourOperation *o = new NOTHROW DetourOperation;
if (o == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
s_nPendingError = error;
DETOUR_BREAK();
stop:
if (o != NULL) {
delete o;
o = NULL;
}
s_ppPendingError = ppPointer;
return error;
}
#ifdef DETOURS_IA64
PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer;
PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;
PVOID pDetourGlobals = NULL;
PVOID pTrampoGlobals = NULL;
pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);
PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)
DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals);
DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n",
ppldDetour, pDetour, pDetourGlobals));
DETOUR_TRACE((" ppldTrampo=%p, code=%p [gp=%p]\n",
ppldTrampo, pTrampoline, pTrampoGlobals));
DETOUR_TRACE(("\n"));
DETOUR_TRACE(("detours: &pldTrampoline =%p\n",
&pTrampoline->pldTrampoline));
DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n",
&pTrampoline->bMovlTargetGp,
pTrampoline->bMovlTargetGp.GetMovlGp()));
DETOUR_TRACE(("detours: &rbCode =%p [%p]\n",
&pTrampoline->rbCode,
((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget()));
DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n",
&pTrampoline->bBrlRemainEip,
pTrampoline->bBrlRemainEip.GetBrlTarget()));
DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n",
&pTrampoline->bMovlDetourGp,
pTrampoline->bMovlDetourGp.GetMovlGp()));
DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n",
&pTrampoline->bCallDetour,
pTrampoline->bCallDetour.GetBrlTarget()));
DETOUR_TRACE(("detours: pldDetour =%p [%p]\n",
pTrampoline->ppldDetour->EntryPoint,
pTrampoline->ppldDetour->GlobalPointer));
DETOUR_TRACE(("detours: pldTarget =%p [%p]\n",
pTrampoline->ppldTarget->EntryPoint,
pTrampoline->ppldTarget->GlobalPointer));
DETOUR_TRACE(("detours: pbRemain =%p\n",
pTrampoline->pbRemain));
DETOUR_TRACE(("detours: pbDetour =%p\n",
pTrampoline->pbDetour));
DETOUR_TRACE(("\n"));
#else // !DETOURS_IA64
PDETOUR_TRAMPOLINE pTrampoline =
(PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
#endif // !DETOURS_IA64
////////////////////////////////////// Verify that Trampoline is in place.
//
LONG cbTarget = pTrampoline->cbRestore;
PBYTE pbTarget = pTrampoline->pbRemain - cbTarget;
if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) {
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (pTrampoline->pbDetour != pDetour) {
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
DWORD dwOld = 0;
if (!VirtualProtect(pbTarget, cbTarget,
PAGE_EXECUTE_READWRITE, &dwOld)) {
error = (LONG)GetLastError();
DETOUR_BREAK();
goto fail;
}
o->fIsRemove = TRUE;
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->dwPerm = dwOld;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}
//////////////////////////////////////////////////////////////////////////////
//
// Helpers for manipulating page protection.
//
// For reference:
// PAGE_NOACCESS 0x01
// PAGE_READONLY 0x02
// PAGE_READWRITE 0x04
// PAGE_WRITECOPY 0x08
// PAGE_EXECUTE 0x10
// PAGE_EXECUTE_READ 0x20
// PAGE_EXECUTE_READWRITE 0x40
// PAGE_EXECUTE_WRITECOPY 0x80
// PAGE_GUARD ...
// PAGE_NOCACHE ...
// PAGE_WRITECOMBINE ...
#define DETOUR_PAGE_EXECUTE_ALL (PAGE_EXECUTE | \
PAGE_EXECUTE_READ | \
PAGE_EXECUTE_READWRITE | \
PAGE_EXECUTE_WRITECOPY)
#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS | \
PAGE_READONLY | \
PAGE_READWRITE | \
PAGE_WRITECOPY)
#define DETOUR_PAGE_ATTRIBUTES (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL))
C_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL);
static DWORD DetourPageProtectAdjustExecute(_In_ DWORD dwOldProtect,
_In_ DWORD dwNewProtect)
// Copy EXECUTE from dwOldProtect to dwNewProtect.
{
bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);
bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);
if (fOldExecute && !fNewExecute) {
dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4)
| (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);
}
else if (!fOldExecute && fNewExecute) {
dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4)
| (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);
}
return dwNewProtect;
}
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess,
_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect)
// Some systems do not allow executability of a page to change. This function applies
// dwNewProtect to [pAddress, nSize), but preserving the previous executability.
// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx.
// When "restoring" page protection, there is no need to use this function.
{
MEMORY_BASIC_INFORMATION mbi;
// Query to get existing execute access.
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) {
return FALSE;
}
return VirtualProtectEx(hProcess, pAddress, nSize,
DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect),
pdwOldProtect);
}
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect)
{
return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(),
pAddress, nSize, dwNewProtect, pdwOldProtect);
}
// End of File
================================================
FILE: Detours/detours.h
================================================
/////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.h of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#pragma once
#ifndef _DETOURS_H_
#define _DETOURS_H_
#if defined(_KERNEL_MODE)
#define DETOURS_KERNEL
#endif
#ifdef DETOURS_KERNEL
#include
#include
#endif
#define DETOURS_VERSION 0x4c0c1 // 0xMAJORcMINORcPATCH
//////////////////////////////////////////////////////////////////////////////
//
#undef DETOURS_X64
#undef DETOURS_X86
#undef DETOURS_IA64
#undef DETOURS_ARM
#undef DETOURS_ARM64
#undef DETOURS_BITS
#undef DETOURS_32BIT
#undef DETOURS_64BIT
#if defined(_X86_)
#define DETOURS_X86
#define DETOURS_OPTION_BITS 64
#elif defined(_AMD64_)
#define DETOURS_X64
#define DETOURS_OPTION_BITS 32
#elif defined(_IA64_)
#define DETOURS_IA64
#define DETOURS_OPTION_BITS 32
#elif defined(_ARM_)
#define DETOURS_ARM
#elif defined(_ARM64_)
#define DETOURS_ARM64
#else
#error Unknown architecture (x86, amd64, ia64, arm, arm64)
#endif
#ifdef _WIN64
#undef DETOURS_32BIT
#define DETOURS_64BIT 1
#define DETOURS_BITS 64
// If all 64bit kernels can run one and only one 32bit architecture.
//#define DETOURS_OPTION_BITS 32
#else
#define DETOURS_32BIT 1
#undef DETOURS_64BIT
#define DETOURS_BITS 32
// If all 64bit kernels can run one and only one 32bit architecture.
//#define DETOURS_OPTION_BITS 32
#endif
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
//////////////////////////////////////////////////////////////////////////////
//
#if (_MSC_VER < 1299)
typedef LONG LONG_PTR;
typedef ULONG ULONG_PTR;
#endif
///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL.
//
// These definitions are include so that Detours will build even if the
// compiler doesn't have full SAL 2.0 support.
//
#ifndef DETOURS_DONT_REMOVE_SAL_20
#ifdef DETOURS_TEST_REMOVE_SAL_20
#undef _Analysis_assume_
#undef _Benign_race_begin_
#undef _Benign_race_end_
#undef _Field_range_
#undef _Field_size_
#undef _In_
#undef _In_bytecount_
#undef _In_count_
#undef _In_opt_
#undef _In_opt_bytecount_
#undef _In_opt_count_
#undef _In_opt_z_
#undef _In_range_
#undef _In_reads_
#undef _In_reads_bytes_
#undef _In_reads_opt_
#undef _In_reads_opt_bytes_
#undef _In_reads_or_z_
#undef _In_z_
#undef _Inout_
#undef _Inout_opt_
#undef _Inout_z_count_
#undef _Out_
#undef _Out_opt_
#undef _Out_writes_
#undef _Outptr_result_maybenull_
#undef _Readable_bytes_
#undef _Success_
#undef _Writable_bytes_
#undef _Pre_notnull_
#endif
#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_)
#define _Outptr_result_maybenull_ _Deref_out_opt_z_
#endif
#if defined(_In_count_) && !defined(_In_reads_)
#define _In_reads_(x) _In_count_(x)
#endif
#if defined(_In_opt_count_) && !defined(_In_reads_opt_)
#define _In_reads_opt_(x) _In_opt_count_(x)
#endif
#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_)
#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x)
#endif
#if defined(_In_bytecount_) && !defined(_In_reads_bytes_)
#define _In_reads_bytes_(x) _In_bytecount_(x)
#endif
#ifndef _In_
#define _In_
#endif
#ifndef _In_bytecount_
#define _In_bytecount_(x)
#endif
#ifndef _In_count_
#define _In_count_(x)
#endif
#ifndef _In_opt_
#define _In_opt_
#endif
#ifndef _In_opt_bytecount_
#define _In_opt_bytecount_(x)
#endif
#ifndef _In_opt_count_
#define _In_opt_count_(x)
#endif
#ifndef _In_opt_z_
#define _In_opt_z_
#endif
#ifndef _In_range_
#define _In_range_(x,y)
#endif
#ifndef _In_reads_
#define _In_reads_(x)
#endif
#ifndef _In_reads_bytes_
#define _In_reads_bytes_(x)
#endif
#ifndef _In_reads_opt_
#define _In_reads_opt_(x)
#endif
#ifndef _In_reads_opt_bytes_
#define _In_reads_opt_bytes_(x)
#endif
#ifndef _In_reads_or_z_
#define _In_reads_or_z_
#endif
#ifndef _In_z_
#define _In_z_
#endif
#ifndef _Inout_
#define _Inout_
#endif
#ifndef _Inout_opt_
#define _Inout_opt_
#endif
#ifndef _Inout_z_count_
#define _Inout_z_count_(x)
#endif
#ifndef _Out_
#define _Out_
#endif
#ifndef _Out_opt_
#define _Out_opt_
#endif
#ifndef _Out_writes_
#define _Out_writes_(x)
#endif
#ifndef _Outptr_result_maybenull_
#define _Outptr_result_maybenull_
#endif
#ifndef _Writable_bytes_
#define _Writable_bytes_(x)
#endif
#ifndef _Readable_bytes_
#define _Readable_bytes_(x)
#endif
#ifndef _Success_
#define _Success_(x)
#endif
#ifndef _Pre_notnull_
#define _Pre_notnull_
#endif
#ifdef DETOURS_INTERNAL
#pragma warning(disable:4615) // unknown warning type (suppress with older compilers)
#ifndef _Benign_race_begin_
#define _Benign_race_begin_
#endif
#ifndef _Benign_race_end_
#define _Benign_race_end_
#endif
#ifndef _Field_size_
#define _Field_size_(x)
#endif
#ifndef _Field_range_
#define _Field_range_(x,y)
#endif
#ifndef _Analysis_assume_
#define _Analysis_assume_(x)
#endif
#endif // DETOURS_INTERNAL
#endif // DETOURS_DONT_REMOVE_SAL_20
//////////////////////////////////////////////////////////////////////////////
//
#ifndef GUID_DEFINED
#define GUID_DEFINED
typedef struct _GUID
{
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[ 8 ];
} GUID;
#ifdef INITGUID
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
const GUID name \
= { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
#else
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
const GUID name
#endif // INITGUID
#endif // !GUID_DEFINED
#if defined(__cplusplus)
#ifndef _REFGUID_DEFINED
#define _REFGUID_DEFINED
#define REFGUID const GUID &
#endif // !_REFGUID_DEFINED
#else // !__cplusplus
#ifndef _REFGUID_DEFINED
#define _REFGUID_DEFINED
#define REFGUID const GUID * const
#endif // !_REFGUID_DEFINED
#endif // !__cplusplus
#ifndef ARRAYSIZE
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#endif
//
//////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
//
// Constants
//
#define MoveMemory RtlMoveMemory
#define CopyMemory RtlCopyMemory
#define FillMemory RtlFillMemory
#define ZeroMemory RtlZeroMemory
#undef NULL
#define NULL nullptr
/////////////////////////////////////////////////// Instruction Target Macros.
//
#define DETOUR_INSTRUCTION_TARGET_NONE ((PVOID)0)
#define DETOUR_INSTRUCTION_TARGET_DYNAMIC ((PVOID)(LONG_PTR)-1)
#define DETOUR_SECTION_HEADER_SIGNATURE 0x00727444 // "Dtr\0"
extern const GUID DETOUR_EXE_RESTORE_GUID;
extern const GUID DETOUR_EXE_HELPER_GUID;
#define DETOUR_TRAMPOLINE_SIGNATURE 0x21727444 // Dtr!
typedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE;
/////////////////////////////////////////////////////////// Binary Structures.
//
#pragma pack(push, 8)
typedef struct _DETOUR_SECTION_HEADER
{
DWORD cbHeaderSize;
DWORD nSignature;
DWORD nDataOffset;
DWORD cbDataSize;
DWORD nOriginalImportVirtualAddress;
DWORD nOriginalImportSize;
DWORD nOriginalBoundImportVirtualAddress;
DWORD nOriginalBoundImportSize;
DWORD nOriginalIatVirtualAddress;
DWORD nOriginalIatSize;
DWORD nOriginalSizeOfImage;
DWORD cbPrePE;
DWORD nOriginalClrFlags;
DWORD reserved1;
DWORD reserved2;
DWORD reserved3;
// Followed by cbPrePE bytes of data.
} DETOUR_SECTION_HEADER, *PDETOUR_SECTION_HEADER;
typedef struct _DETOUR_SECTION_RECORD
{
DWORD cbBytes;
DWORD nReserved;
GUID guid;
} DETOUR_SECTION_RECORD, *PDETOUR_SECTION_RECORD;
typedef struct _DETOUR_CLR_HEADER
{
// Header versioning
ULONG cb;
USHORT MajorRuntimeVersion;
USHORT MinorRuntimeVersion;
// Symbol table and startup information
IMAGE_DATA_DIRECTORY MetaData;
ULONG Flags;
// Followed by the rest of the IMAGE_COR20_HEADER
} DETOUR_CLR_HEADER, *PDETOUR_CLR_HEADER;
typedef struct _DETOUR_EXE_RESTORE
{
DWORD cb;
DWORD cbidh;
DWORD cbinh;
DWORD cbclr;
PBYTE pidh;
PBYTE pinh;
PBYTE pclr;
IMAGE_DOS_HEADER idh;
union {
IMAGE_NT_HEADERS inh;
IMAGE_NT_HEADERS32 inh32;
IMAGE_NT_HEADERS64 inh64;
BYTE raw[sizeof(IMAGE_NT_HEADERS64) +
sizeof(IMAGE_SECTION_HEADER) * 32];
};
DETOUR_CLR_HEADER clr;
} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE;
typedef struct _DETOUR_EXE_HELPER
{
DWORD cb;
DWORD pid;
DWORD nDlls;
CHAR rDlls[4];
} DETOUR_EXE_HELPER, *PDETOUR_EXE_HELPER;
#pragma pack(pop)
#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \
{ \
sizeof(DETOUR_SECTION_HEADER),\
DETOUR_SECTION_HEADER_SIGNATURE,\
sizeof(DETOUR_SECTION_HEADER),\
(cbSectionSize),\
\
0,\
0,\
0,\
0,\
\
0,\
0,\
0,\
0,\
}
/////////////////////////////////////////////////////////////// Helper Macros.
//
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define DETOURS_STRINGIFY_(x) #x
#ifndef DETOURS_KERNEL
///////////////////////////////////////////////////////////// Binary Typedefs.
//
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)(
_In_opt_ PVOID pContext,
_In_opt_ LPCSTR pszFile,
_Outptr_result_maybenull_ LPCSTR *ppszOutFile);
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)(
_In_opt_ PVOID pContext,
_In_ LPCSTR pszOrigFile,
_In_ LPCSTR pszFile,
_Outptr_result_maybenull_ LPCSTR *ppszOutFile);
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)(
_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);
typedef BOOL (CALLBACK *PF_DETOUR_BINARY_COMMIT_CALLBACK)(
_In_opt_ PVOID pContext);
typedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext,
_In_ ULONG nOrdinal,
_In_opt_ LPCSTR pszName,
_In_opt_ PVOID pCode);
typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext,
_In_opt_ HMODULE hModule,
_In_opt_ LPCSTR pszFile);
typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext,
_In_ DWORD nOrdinal,
_In_opt_ LPCSTR pszFunc,
_In_opt_ PVOID pvFunc);
// Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter.
typedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext,
_In_ DWORD nOrdinal,
_In_opt_ LPCSTR pszFunc,
_In_opt_ PVOID* ppvFunc);
typedef VOID * PDETOUR_BINARY;
typedef VOID * PDETOUR_LOADED_BINARY;
#endif //!DETOURS_KERNEL
//////////////////////////////////////////////////////////// Transaction APIs.
//
LONG WINAPI DetourTransactionBegin(VOID);
LONG WINAPI DetourTransactionAbort(VOID);
LONG WINAPI DetourTransactionCommit(VOID);
LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer);
LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread);
LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour);
LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour,
_Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,
_Out_opt_ PVOID *ppRealTarget,
_Out_opt_ PVOID *ppRealDetour);
LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour);
BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore);
BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain);
PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound);
PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound);
////////////////////////////////////////////////////////////// Code Functions.
//
PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,
_Out_opt_ PVOID *ppGlobals);
PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,
_Inout_opt_ PVOID *ppDstPool,
_In_ PVOID pSrc,
_Out_opt_ PVOID *ppTarget,
_Out_opt_ LONG *plExtra);
#ifndef DETOURS_KERNEL
PVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule,
_In_ LPCSTR pszFunction);
BOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule,
_In_ BOOL fLimitReferencesToModule);
#endif // !DETOURS_KERNEL
#ifndef DETOURS_KERNEL
///////////////////////////////////////////////////// Loaded Binary Functions.
//
HMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr);
HMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast);
PVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule);
ULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule);
BOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport);
BOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,
_In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc);
BOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule,
_In_opt_ PVOID pContext,
_In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,
_In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx);
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule,
_In_ REFGUID rguid,
_Out_ DWORD *pcbData);
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid,
_Out_ DWORD * pcbData);
DWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule);
#endif // !DETOURS_KERNEL
#ifndef DETOURS_KERNEL
///////////////////////////////////////////////// Persistent Binary Functions.
//
PDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE 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);
_Writable_bytes_(*pcbData)
_Readable_bytes_(*pcbData)
_Success_(return != NULL)
PVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary,
_In_ REFGUID rguid,
_Out_ DWORD *pcbData);
PVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary,
_In_ REFGUID rguid,
_In_reads_opt_(cbData) PVOID pData,
_In_ DWORD cbData);
BOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid);
BOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary);
BOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary);
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);
BOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile);
BOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary);
#endif // !DETOURS_KERNEL
#ifndef DETOURS_KERNEL
/////////////////////////////////////////////////// Create Process & Load Dll.
//
typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEA)(
_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation);
typedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEW)(
_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation);
BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);
BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);
#ifdef UNICODE
#define DetourCreateProcessWithDll DetourCreateProcessWithDllW
#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEW
#else
#define DetourCreateProcessWithDll DetourCreateProcessWithDllA
#define PDETOUR_CREATE_PROCESS_ROUTINE PDETOUR_CREATE_PROCESS_ROUTINEA
#endif // !UNICODE
BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);
BOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ LPCSTR lpDllName,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);
#ifdef UNICODE
#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExW
#else
#define DetourCreateProcessWithDllEx DetourCreateProcessWithDllExA
#endif // !UNICODE
BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,
_Inout_opt_ LPSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOA lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);
BOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,
_Inout_opt_ LPWSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCWSTR lpCurrentDirectory,
_In_ LPSTARTUPINFOW lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);
#ifdef UNICODE
#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsW
#else
#define DetourCreateProcessWithDlls DetourCreateProcessWithDllsA
#endif // !UNICODE
BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);
BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,
_In_ LPCSTR lpDllName,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);
#ifdef UNICODE
#define DetourProcessViaHelper DetourProcessViaHelperW
#else
#define DetourProcessViaHelper DetourProcessViaHelperA
#endif // !UNICODE
BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);
BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,
_In_ DWORD nDlls,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);
#ifdef UNICODE
#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsW
#else
#define DetourProcessViaHelperDlls DetourProcessViaHelperDllsA
#endif // !UNICODE
#endif // !DETOURS_KERNEL
BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls);
BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,
_In_ HMODULE hImage,
_In_ BOOL bIs32Bit,
_In_reads_(nDlls) LPCSTR *rlpDlls,
_In_ DWORD nDlls);
BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,
_In_ REFGUID rguid,
_In_reads_bytes_(cbData) PVOID pvData,
_In_ DWORD cbData);
#ifndef DETOURS_KERNEL
BOOL WINAPI DetourRestoreAfterWith(VOID);
BOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData,
_In_ DWORD cbData);
BOOL WINAPI DetourIsHelperProcess(VOID);
VOID CALLBACK DetourFinishHelperProcess(_In_ HWND,
_In_ HINSTANCE,
_In_ LPSTR,
_In_ INT);
#endif // !DETOURS_KERNEL
//
//////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
}
#endif // __cplusplus
//////////////////////////////////////////////// Detours Internal Definitions.
//
#ifdef __cplusplus
#ifdef DETOURS_INTERNAL
#define NOTHROW
// #define NOTHROW (nothrow)
//////////////////////////////////////////////////////////////////////////////
//
#ifndef DETOURS_KERNEL
#if (_MSC_VER < 1299)
#include
typedef IMAGEHLP_MODULE IMAGEHLP_MODULE64;
typedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64;
typedef IMAGEHLP_SYMBOL SYMBOL_INFO;
typedef PIMAGEHLP_SYMBOL PSYMBOL_INFO;
static inline
LONG InterlockedCompareExchange(_Inout_ LONG *ptr, _In_ LONG nval, _In_ LONG oval)
{
return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval);
}
#else
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#include
#pragma warning(pop)
#endif
#ifdef IMAGEAPI // defined by DBGHELP.H
typedef LPAPI_VERSION (NTAPI *PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion);
typedef BOOL (NTAPI *PF_SymInitialize)(_In_ HANDLE hProcess,
_In_opt_ LPCSTR UserSearchPath,
_In_ BOOL fInvadeProcess);
typedef DWORD (NTAPI *PF_SymSetOptions)(_In_ DWORD SymOptions);
typedef DWORD (NTAPI *PF_SymGetOptions)(VOID);
typedef DWORD64 (NTAPI *PF_SymLoadModule64)(_In_ HANDLE hProcess,
_In_opt_ HANDLE hFile,
_In_ LPSTR ImageName,
_In_opt_ LPSTR ModuleName,
_In_ DWORD64 BaseOfDll,
_In_opt_ DWORD SizeOfDll);
typedef BOOL (NTAPI *PF_SymGetModuleInfo64)(_In_ HANDLE hProcess,
_In_ DWORD64 qwAddr,
_Out_ PIMAGEHLP_MODULE64 ModuleInfo);
typedef BOOL (NTAPI *PF_SymFromName)(_In_ HANDLE hProcess,
_In_ LPSTR Name,
_Out_ PSYMBOL_INFO Symbol);
typedef struct _DETOUR_SYM_INFO
{
HANDLE hProcess;
HMODULE hDbgHelp;
PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx;
PF_SymInitialize pfSymInitialize;
PF_SymSetOptions pfSymSetOptions;
PF_SymGetOptions pfSymGetOptions;
PF_SymLoadModule64 pfSymLoadModule64;
PF_SymGetModuleInfo64 pfSymGetModuleInfo64;
PF_SymFromName pfSymFromName;
} DETOUR_SYM_INFO, *PDETOUR_SYM_INFO;
PDETOUR_SYM_INFO DetourLoadImageHlp(VOID);
#endif // IMAGEAPI
#endif // !DETOURS_KERNEL
#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS)
#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier)
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#ifndef DETOUR_TRACE
#if DETOUR_DEBUG && !defined(DETOURS_KERNEL)
#define DETOUR_TRACE(x) printf x
#define DETOUR_BREAK() __debugbreak()
#include
#include
#else
#define DETOUR_TRACE(x)
#define DETOUR_BREAK()
#endif
#endif
#if 1 || defined(DETOURS_IA64)
//
// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle.
//
#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3)
#define DETOUR_IA64_TEMPLATE_OFFSET (0)
#define DETOUR_IA64_TEMPLATE_SIZE (5)
#define DETOUR_IA64_INSTRUCTION_SIZE (41)
#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE)
#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE)
#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE)
C_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128);
__declspec(align(16)) struct DETOUR_IA64_BUNDLE
{
public:
union
{
BYTE data[16];
UINT64 wide[2];
};
enum {
A_UNIT = 1u,
I_UNIT = 2u,
M_UNIT = 3u,
B_UNIT = 4u,
F_UNIT = 5u,
L_UNIT = 6u,
X_UNIT = 7u,
};
struct DETOUR_IA64_METADATA
{
ULONG nTemplate : 8; // Instruction template.
ULONG nUnit0 : 4; // Unit for slot 0
ULONG nUnit1 : 4; // Unit for slot 1
ULONG nUnit2 : 4; // Unit for slot 2
};
protected:
static const DETOUR_IA64_METADATA s_rceCopyTable[33];
UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const;
bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst,
_In_ BYTE slot,
_Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const;
// 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0
// f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.
// 00
// f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.
// 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0]
// 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5]
// 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42]
// 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46]
// 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83]
// 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87]
// f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124]
BYTE GetTemplate() const;
// Get 4 bit opcodes.
BYTE GetInst0() const;
BYTE GetInst1() const;
BYTE GetInst2() const;
BYTE GetUnit(BYTE slot) const;
BYTE GetUnit0() const;
BYTE GetUnit1() const;
BYTE GetUnit2() const;
// Get 37 bit data.
UINT64 GetData0() const;
UINT64 GetData1() const;
UINT64 GetData2() const;
// Get/set the full 41 bit instructions.
UINT64 GetInstruction(BYTE slot) const;
UINT64 GetInstruction0() const;
UINT64 GetInstruction1() const;
UINT64 GetInstruction2() const;
void SetInstruction(BYTE slot, UINT64 instruction);
void SetInstruction0(UINT64 instruction);
void SetInstruction1(UINT64 instruction);
void SetInstruction2(UINT64 instruction);
// Get/set bitfields.
static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count);
static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field);
// Get specific read-only fields.
static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode
static UINT64 GetX(UINT64 instruction); // 1bit opcode extension
static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension
static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension
// Get/set specific fields.
static UINT64 GetImm7a(UINT64 instruction);
static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a);
static UINT64 GetImm13c(UINT64 instruction);
static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c);
static UINT64 GetSignBit(UINT64 instruction);
static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit);
static UINT64 GetImm20a(UINT64 instruction);
static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a);
static UINT64 GetImm20b(UINT64 instruction);
static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b);
static UINT64 SignExtend(UINT64 Value, UINT64 Offset);
BOOL IsMovlGp() const;
VOID SetInst(BYTE Slot, BYTE nInst);
VOID SetInst0(BYTE nInst);
VOID SetInst1(BYTE nInst);
VOID SetInst2(BYTE nInst);
VOID SetData(BYTE Slot, UINT64 nData);
VOID SetData0(UINT64 nData);
VOID SetData1(UINT64 nData);
VOID SetData2(UINT64 nData);
BOOL SetNop(BYTE Slot);
BOOL SetNop0();
BOOL SetNop1();
BOOL SetNop2();
public:
BOOL IsBrl() const;
VOID SetBrl();
VOID SetBrl(UINT64 target);
UINT64 GetBrlTarget() const;
VOID SetBrlTarget(UINT64 target);
VOID SetBrlImm(UINT64 imm);
UINT64 GetBrlImm() const;
UINT64 GetMovlGp() const;
VOID SetMovlGp(UINT64 gp);
VOID SetStop();
UINT Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const;
};
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1))
#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1))
#endif // DETOURS_ARM
//////////////////////////////////////////////////////////////////////////////
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define DETOUR_OFFLINE_LIBRARY(x) \
PVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst, \
_Inout_opt_ PVOID *ppDstPool, \
_In_ PVOID pSrc, \
_Out_opt_ PVOID *ppTarget, \
_Out_opt_ LONG *plExtra); \
\
BOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule, \
_In_ BOOL fLimitReferencesToModule); \
DETOUR_OFFLINE_LIBRARY(X86)
DETOUR_OFFLINE_LIBRARY(X64)
DETOUR_OFFLINE_LIBRARY(ARM)
DETOUR_OFFLINE_LIBRARY(ARM64)
DETOUR_OFFLINE_LIBRARY(IA64)
#undef DETOUR_OFFLINE_LIBRARY
//////////////////////////////////////////////////////////////////////////////
//
// Helpers for manipulating page protection.
//
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess,
_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect);
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect);
#ifdef __cplusplus
}
#endif // __cplusplus
//////////////////////////////////////////////////////////////////////////////
#define MM_ALLOCATION_GRANULARITY 0x10000
//////////////////////////////////////////////////////////////////////////////
#endif // DETOURS_INTERNAL
#endif // __cplusplus
#endif // _DETOURS_H_
//
//////////////////////////////////////////////////////////////// End of File.
================================================
FILE: Detours/detoursx.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Core Detours Functionality (detours.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#pragma warning(disable:4068) // unknown pragma (suppress)
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#if defined(_KERNEL_MODE)
#define DETOURS_KERNEL
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include
#ifdef DETOURS_KERNEL
#include "api_thunks.h"
#endif
#include
#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
#if _MSC_VER >= 1900
#pragma warning(pop)
#endif
#define NOTHROW
//////////////////////////////////////////////////////////////////////////////
//
struct _DETOUR_ALIGN
{
BYTE obTarget : 3;
BYTE obTrampoline : 5;
};
C_ASSERT(sizeof(_DETOUR_ALIGN) == 1);
//////////////////////////////////////////////////////////////////////////////
//
static void* detour_memory_alloc(size_t size)
{
#pragma warning(suppress: 4996)
return ExAllocatePoolWithTag(NonPagedPool, size, DETOUR_SECTION_HEADER_SIGNATURE);
}
static void detour_memory_free(void* p)
{
ExFreePoolWithTag(p, DETOUR_SECTION_HEADER_SIGNATURE);
}
static PMDL detour_remap_address(_In_ void* va, _In_ unsigned long size, _Out_ void** new_va)
{
PMDL Mdl = NULL;
void* NewVA = NULL;
for (;;) {
Mdl = IoAllocateMdl(va, size, FALSE, FALSE, NULL);
if (Mdl == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
__try {
MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl(Mdl), Mdl = NULL;
SetLastError(GetExceptionCode());
break;
}
NewVA = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
if (NewVA == NULL) {
MmUnlockPages(Mdl);
IoFreeMdl(Mdl), Mdl = NULL;
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
break;
}
*new_va = NewVA;
break;
}
return Mdl;
}
static void detour_unmap_address(_In_ PMDL mdl)
{
if (mdl) {
MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
}
//////////////////////////////////////////////////////////////////////////////
//
// Region reserved for system DLLs, which cannot be used for trampolines.
//
static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000;
static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000;
//////////////////////////////////////////////////////////////////////////////
//
static bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)
{
PVOID ImageBase = NULL;
RtlPcToFileHeader(pbCode, &ImageBase);
if (ImageBase == NULL) {
return false;
}
__try {
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +
pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
if (pbAddress >= ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&
pbAddress < ((PBYTE)pDosHeader +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +
pNtHeader->OptionalHeader
.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {
return true;
}
}
#pragma prefast(suppress:28940, "A bad pointer means this probably isn't a PE header.")
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
return false;
}
return false;
}
inline ULONG_PTR detour_2gb_below(ULONG_PTR address)
{
return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;
}
inline ULONG_PTR detour_2gb_above(ULONG_PTR address)
{
#if defined(DETOURS_64BIT)
return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;
#else
return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;
#endif
}
///////////////////////////////////////////////////////////////////////// X86.
//
#ifdef DETOURS_X86
struct _DETOUR_TRAMPOLINE
{
BYTE rbCode[30]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);
enum {
SIZE_OF_JMP = 5
};
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbFixedCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbFixedCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
// And, within +/- 2GB of relative jmp targets.
if (pbCode[0] == 0xe9) { // jmp +imm32
PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32*) & pbCode[1];
if (pbNew < pbCode) {
hi = detour_2gb_above((ULONG_PTR)pbNew);
}
else {
lo = detour_2gb_below((ULONG_PTR)pbNew);
}
DETOUR_TRACE(("[%p..%p..%p] +imm32\n", (PVOID)lo, pbCode, (PVOID)hi));
}
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X86
///////////////////////////////////////////////////////////////////////// X64.
//
#ifdef DETOURS_X64
struct _DETOUR_TRAMPOLINE
{
// An X64 instuction can be 15 bytes long.
// In practice 11 seems to be the limit.
BYTE rbCode[30]; // target code + jmp to pbRemain.
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[30]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
PBYTE pbCodeIn; // jmp [pbDetour]
PMDL pbCodeInMdl;
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);
enum {
SIZE_OF_JMP = 6 // jmp[+imm32]
};
static PIMAGE_SECTION_HEADER detour_va_to_section(PVOID base, PIMAGE_NT_HEADERS nt, PBYTE va)
{
PIMAGE_SECTION_HEADER NtSection = IMAGE_FIRST_SECTION(nt);
for (size_t i = 0u; i < nt->FileHeader.NumberOfSections; ++i){
if (va >= ((PBYTE)base + NtSection->VirtualAddress) &&
va < ((PBYTE)base + NtSection->VirtualAddress + NtSection->Misc.VirtualSize)) {
return NtSection;
}
++NtSection;
}
return NULL;
}
PBYTE detour_gen_codein(PBYTE pbCode, PVOID pDetour, PMDL* pMdl)
{
PVOID ImageBase = NULL;
PIMAGE_SECTION_HEADER NtSection = NULL;
PIMAGE_SECTION_HEADER NtSectionLast = NULL;
if (RtlPcToFileHeader(pbCode, &ImageBase) == NULL) {
return NULL;
}
PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader(ImageBase);
if (NtHeader == NULL) {
return NULL;
}
NtSection = detour_va_to_section(ImageBase, NtHeader, pbCode);
if (NtSection == NULL) {
return NULL;
}
NtSectionLast = NtSection + 1;
if (NtSectionLast >= (IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections)) {
NtSectionLast = NULL;
}
PBYTE CodeIn = (PBYTE)ImageBase + NtSection->VirtualAddress + NtSection->Misc.VirtualSize;
PBYTE CodeInEnd = (PBYTE)ImageBase + (NtSectionLast != NULL
? NtSectionLast->VirtualAddress
: NtHeader->OptionalHeader.SizeOfImage);
CodeIn = (PBYTE)(((ULONG_PTR)CodeIn / 0x10 + 1) * 0x10);
PBYTE NewCodeIn = NULL;
do
{
if (*(PVOID*)CodeIn == NULL) {
*pMdl = detour_remap_address(CodeIn, sizeof(void*), (PVOID*)&NewCodeIn);
if (NewCodeIn) {
*(PVOID*)NewCodeIn = pDetour;
return CodeIn;
}
}
CodeIn += sizeof(void*);
} while (CodeIn < CodeInEnd);
return NULL;
}
void detour_del_codein(PMDL pMdl)
{
if (pMdl) {
PVOID va = MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);
memset(va, 0, sizeof(void*));
detour_unmap_address(pMdl);
}
}
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbFixedCode, PBYTE pbJmpVal)
{
PBYTE pbJmpSrc = pbFixedCode + 5;
*pbCode++ = 0xE9; // jmp +imm32
*((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)
{
PBYTE pbJmpSrc = pbCode + 6;
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE pbFixedCode, PBYTE* ppbJmpVal)
{
PBYTE pbJmpSrc = pbFixedCode + 6;
*pbCode++ = 0xff; // jmp [+imm32]
*pbCode++ = 0x25;
*((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
*pbCode++ = 0xcc; // brk;
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Then, skip over a patch jump
if (pbCode[0] == 0xeb) { // jmp +imm8
PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over short jump.\n", pbCode, pbNew));
pbCode = pbNew;
// First, skip over the import vector if there is one.
if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
// Looks like an import alias jump, then get the code it points to.
PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];
if (detour_is_imported(pbCode, pbTarget)) {
pbNew = *(UNALIGNED PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
// Finally, skip over a long jump if it is the target of the patch jump.
else if (pbCode[0] == 0xe9) { // jmp +imm32
pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];
DETOUR_TRACE(("%p->%p: skipped over long jump.\n", pbCode, pbNew));
pbCode = pbNew;
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
(void)pbCode;
*ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;
*ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
if (pbCode[0] == 0xeb || // jmp +imm8
pbCode[0] == 0xe9 || // jmp +imm32
pbCode[0] == 0xe0 || // jmp eax
pbCode[0] == 0xc2 || // ret +imm8
pbCode[0] == 0xc3 || // ret
pbCode[0] == 0xcc) { // brk
return TRUE;
}
else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) { // rep ret
return TRUE;
}
else if (pbCode[0] == 0xff && pbCode[1] == 0x25) { // jmp [+imm32]
return TRUE;
}
else if ((pbCode[0] == 0x26 || // jmp es:
pbCode[0] == 0x2e || // jmp cs:
pbCode[0] == 0x36 || // jmp ss:
pbCode[0] == 0x3e || // jmp ds:
pbCode[0] == 0x64 || // jmp fs:
pbCode[0] == 0x65) && // jmp gs:
pbCode[1] == 0xff && // jmp [+imm32]
pbCode[2] == 0x25) {
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// 1-byte through 11-byte NOPs.
if (pbCode[0] == 0x90) {
return 1;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {
return 2;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {
return 3;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&
pbCode[3] == 0x00) {
return 4;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00) {
return 5;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {
return 6;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00) {
return 7;
}
if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&
pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00) {
return 8;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&
pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {
return 9;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&
pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00) {
return 10;
}
if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&
pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&
pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&
pbCode[9] == 0x00 && pbCode[10] == 0x00) {
return 11;
}
// int 3.
if (pbCode[0] == 0xcc) {
return 1;
}
return 0;
}
#endif // DETOURS_X64
//////////////////////////////////////////////////////////////////////// IA64.
//
#ifdef DETOURS_IA64
struct _DETOUR_TRAMPOLINE
{
// On the IA64, a trampoline is used for both incoming and outgoing calls.
//
// The trampoline contains the following bundles for the outgoing call:
// movl gp=target_gp;
//
// brl target_code;
//
// The trampoline contains the following bundles for the incoming call:
// alloc r41=ar.pfs, b, 0, 8, 0
// mov r40=rp
//
// adds r50=0, r39
// adds r49=0, r38
// adds r48=0, r37 ;;
//
// adds r47=0, r36
// adds r46=0, r35
// adds r45=0, r34
//
// adds r44=0, r33
// adds r43=0, r32
// adds r42=0, gp ;;
//
// movl gp=ffffffff`ffffffff ;;
//
// brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;;
//
// adds gp=0, r42
// mov rp=r40, +0 ;;
// mov.i ar.pfs=r41
//
// br.ret.sptk.many rp ;;
//
// This way, we only have to relocate a single bundle.
//
// The complicated incoming trampoline is required because we have to
// create an additional stack frame so that we save and restore the gp.
// We must do this because gp is a caller-saved register, but not saved
// if the caller thinks the target is in the same DLL, which changes
// when we insert a detour.
//
DETOUR_IA64_BUNDLE bMovlTargetGp; // Bundle which sets target GP
BYTE rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle.
DETOUR_IA64_BUNDLE bBrlRemainEip; // Brl to pbRemain
// This must be adjacent to bBranchIslands.
// Each instruction in the moved bundle could be a IP-relative chk or branch or call.
// Any such instructions are changed to point to a brl in bBranchIslands.
// This must be adjacent to bBrlRemainEip -- see "pbPool".
DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE];
// Target of brl inserted in target function
DETOUR_IA64_BUNDLE bAllocFrame; // alloc frame
DETOUR_IA64_BUNDLE bSave37to39; // save r37, r38, r39.
DETOUR_IA64_BUNDLE bSave34to36; // save r34, r35, r36.
DETOUR_IA64_BUNDLE bSaveGPto33; // save gp, r32, r33.
DETOUR_IA64_BUNDLE bMovlDetourGp; // set detour GP.
DETOUR_IA64_BUNDLE bCallDetour; // call detour.
DETOUR_IA64_BUNDLE bPopFrameGp; // pop frame and restore gp.
DETOUR_IA64_BUNDLE bReturn; // return to caller.
PLABEL_DESCRIPTOR pldTrampoline;
BYTE rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle.
BYTE cbRestore; // size of original target code.
BYTE cbCode; // size of moved target code.
_DETOUR_ALIGN rAlign[14]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
PPLABEL_DESCRIPTOR ppldDetour; // [pbDetour,gpDetour]
PPLABEL_DESCRIPTOR ppldTarget; // [pbTarget,gpDetour]
};
C_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16);
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16);
enum {
SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE)
};
inline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals)
{
PBYTE pGlobals = NULL;
PBYTE pbCode = NULL;
if (pPointer != NULL) {
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
}
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
if (pbCode == NULL) {
return NULL;
}
DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode;
// IA64 Local Import Jumps look like:
// addl r2=ffffffff`ffe021c0, gp ;;
// ld8 r2=[r2]
// nop.i 0 ;;
//
// ld8 r3=[r2], 8 ;;
// ld8 gp=[r2]
// mov b6=r3, +0
//
// nop.m 0
// nop.i 0
// br.cond.sptk.few b6
//
// 002024000200100b
if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&
pb[0].wide[1] == 0x0004000000203008 &&
pb[1].wide[0] == 0x001014180420180a &&
pb[1].wide[1] == 0x07000830c0203008 &&
pb[2].wide[0] == 0x0000000100000010 &&
pb[2].wide[1] == 0x0080006000000200) {
ULONG64 offset =
((pb[0].wide[0] & 0x0000000001fc0000) >> 18) | // imm7b
((pb[0].wide[0] & 0x000001ff00000000) >> 25) | // imm9d
((pb[0].wide[0] & 0x00000000f8000000) >> 11); // imm5c
if (pb[0].wide[0] & 0x0000020000000000) { // sign
offset |= 0xffffffffffe00000;
}
PBYTE pbTarget = pGlobals + offset;
DETOUR_TRACE(("%p: potential import jump, target=%p\n", pb, pbTarget));
if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) {
DETOUR_TRACE(("%p: is import jump, label=%p\n", pb, *(PBYTE *)pbTarget));
PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget;
pbCode = (PBYTE)ppld->EntryPoint;
pGlobals = (PBYTE)ppld->GlobalPointer;
if (ppGlobals != NULL) {
*ppGlobals = pGlobals;
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
(void)pbCode;
*ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;
*ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
// Routine not needed on IA64.
(void)pbCode;
return 0;
}
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
struct _DETOUR_TRAMPOLINE
{
// A Thumb-2 instruction can be 2 or 4 bytes long.
BYTE rbCode[62]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak; // padding to make debugging easier.
BYTE rbRestore[22]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);
enum {
SIZE_OF_JMP = 8
};
inline PBYTE align4(PBYTE pValue)
{
return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u);
}
inline ULONG fetch_thumb_opcode(PBYTE pbCode)
{
ULONG Opcode = *(UINT16 *)&pbCode[0];
if (Opcode >= 0xe800) {
Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];
}
return Opcode;
}
inline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode)
{
if (Opcode >= 0x10000) {
*((UINT16*&)pbCode)++ = Opcode >> 16;
}
*((UINT16*&)pbCode)++ = (UINT16)Opcode;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 4;
pbLiteral = *ppPool;
}
else {
pbLiteral = align4(pbCode + 6);
}
*((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal);
LONG delta = pbLiteral - align4(pbCode + 4);
write_thumb_opcode(pbCode, 0xf8dff000 | delta); // LDR PC,[PC+n]
if (ppPool == NULL) {
if (((ULONG)pbCode & 2) != 0) {
write_thumb_opcode(pbCode, 0xdefe); // BREAK
}
pbCode += 4;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_thumb_opcode(pbCode, 0xdefe);
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode);
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx
ULONG Opcode2 = fetch_thumb_opcode(pbCode+4);
if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) { // movt r12,#xxxx
ULONG Opcode3 = fetch_thumb_opcode(pbCode+8);
if (Opcode3 == 0xf8dcf000) { // ldr pc,[r12]
PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) |
((Opcode2 << 1) & 0x08000000) |
((Opcode2 << 16) & 0x00ff0000) |
((Opcode >> 4) & 0x0000f700) |
((Opcode >> 15) & 0x00000800) |
((Opcode >> 0) & 0x000000ff));
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew);
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE *ppLower,
PDETOUR_TRAMPOLINE *ppUpper)
{
// We have to place trampolines within +/- 2GB of code.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
ULONG Opcode = fetch_thumb_opcode(pbCode);
if ((Opcode & 0xffffff87) == 0x4700 || // bx
(Opcode & 0xf800d000) == 0xf0009000) { // b
return TRUE;
}
if ((Opcode & 0xffff8000) == 0xe8bd8000) { // pop {...,pc}
__debugbreak();
return TRUE;
}
if ((Opcode & 0xffffff00) == 0x0000bd00) { // pop {...,pc}
__debugbreak();
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop.
return 2;
}
if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding.
return 2;
}
return 0;
}
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
struct _DETOUR_TRAMPOLINE
{
// An ARM64 instruction is 4 bytes long.
BYTE rbCode[64]; // target code + jmp to pbRemain
BYTE cbCode; // size of moved target code.
BYTE cbCodeBreak[3]; // padding to make debugging easier.
BYTE rbRestore[24]; // original target code.
BYTE cbRestore; // size of original target code.
BYTE cbRestoreBreak[3]; // padding to make debugging easier.
_DETOUR_ALIGN rAlign[8]; // instruction alignment array.
PBYTE pbRemain; // first instruction after moved code. [free list]
PBYTE pbDetour; // first instruction of detour function.
};
C_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 120);
enum {
SIZE_OF_JMP = 8
};
inline ULONG fetch_opcode(PBYTE pbCode)
{
return *(ULONG *)pbCode;
}
inline void write_opcode(PBYTE &pbCode, ULONG Opcode)
{
*(ULONG *)pbCode = Opcode;
pbCode += 4;
}
PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)
{
PBYTE pbLiteral;
if (ppPool != NULL) {
*ppPool = *ppPool - 8;
pbLiteral = *ppPool;
}
else {
pbLiteral = pbCode + 2*4;
}
*((PBYTE*&)pbLiteral) = pbJmpVal;
LONG delta = (LONG)(pbLiteral - pbCode);
write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n]
write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17
if (ppPool == NULL) {
pbCode += 8;
}
return pbCode;
}
inline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)
{
while (pbCode < pbLimit) {
write_opcode(pbCode, 0xd4100000 | (0xf000 << 5));
}
return pbCode;
}
inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
{
if (pbCode == NULL) {
return NULL;
}
if (ppGlobals != NULL) {
*ppGlobals = NULL;
}
// Skip over the import jump if there is one.
pbCode = (PBYTE)pbCode;
ULONG Opcode = fetch_opcode(pbCode);
if ((Opcode & 0x9f00001f) == 0x90000010) { // adrp x16, IAT
ULONG Opcode2 = fetch_opcode(pbCode+4);
if ((Opcode2 & 0xffe003ff) == 0xf9400210) { // ldr x16, [x16, IAT]
ULONG Opcode3 = fetch_opcode(pbCode+8);
if (Opcode3 == 0xd61f0200) { // br x16
ULONG PageOffset = ((Opcode & 0x60000000) >> 29) | ((Opcode & 0x00ffffe0) >> 3);
PageOffset = (LONG)(Opcode << 11) >> 11;
PBYTE pbTarget = (PBYTE)(((ULONG64)pbCode & 0xfffffffffffff000ULL) + PageOffset +
((Opcode2 >> 10) & 0xfff));
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
return pbCode;
}
inline void detour_find_jmp_bounds(PBYTE pbCode,
PDETOUR_TRAMPOLINE* ppLower,
PDETOUR_TRAMPOLINE* ppUpper)
{
// The encoding used by detour_gen_jmp_indirect actually enables a
// displacement of +/- 4GiB. In the future, this could be changed to
// reflect that. For now, just reuse the x86 logic which is plenty.
ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);
ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);
DETOUR_TRACE(("[%p..%p..%p]\n", (PVOID)lo, pbCode, (PVOID)hi));
*ppLower = (PDETOUR_TRAMPOLINE)lo;
*ppUpper = (PDETOUR_TRAMPOLINE)hi;
}
inline BOOL detour_does_code_end_function(PBYTE pbCode)
{
ULONG Opcode = fetch_opcode(pbCode);
if ((Opcode & 0xfffffc1f) == 0xd65f0000 || // br
(Opcode & 0xfc000000) == 0x14000000) { // b
return TRUE;
}
return FALSE;
}
inline ULONG detour_is_code_filler(PBYTE pbCode)
{
if (*(ULONG *)pbCode == 0xd503201f) { // nop.
return 4;
}
if (*(ULONG *)pbCode == 0x00000000) { // zero-filled padding.
return 4;
}
return 0;
}
#endif // DETOURS_ARM64
//////////////////////////////////////////////// Trampoline Memory Management.
//
struct DETOUR_REGION
{
ULONG dwSignature;
PMDL pMdl; // MDL in this region.
DETOUR_REGION * pNext; // Next region in list of regions.
DETOUR_TRAMPOLINE * pFree; // List of free trampolines in this region.
};
typedef DETOUR_REGION * PDETOUR_REGION;
const ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';
const ULONG DETOUR_REGION_SIZE = PAGE_SIZE;
const ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE
/ sizeof(DETOUR_TRAMPOLINE)) - 1;
static PDETOUR_REGION s_pRegions = NULL; // List of all regions.
static PDETOUR_REGION s_pRegion = NULL; // Default region.
static DWORD detour_writable_trampoline_regions()
{
// Mark all of the regions as writable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
NTSTATUS Result = MmProtectMdlSystemAddress(pRegion->pMdl, PAGE_EXECUTE_READWRITE);
if (!NT_SUCCESS(Result)) {
return SetLastError(Result), Result;
}
}
return NO_ERROR;
}
static void detour_runnable_trampoline_regions()
{
// Mark all of the regions as executable.
for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {
MmProtectMdlSystemAddress(pRegion->pMdl, PAGE_EXECUTE_READ);
}
}
//static PBYTE detour_alloc_round_down_to_region(PBYTE pbTry)
//{
// // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.
// ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
// if (extra != 0) {
// pbTry -= extra;
// }
// return pbTry;
//}
//
//static PBYTE detour_alloc_round_up_to_region(PBYTE pbTry)
//{
// // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.
// ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);
// if (extra != 0) {
// ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;
// pbTry += adjust;
// }
// return pbTry;
//}
// Starting at pbLo, try to allocate a memory region, continue until pbHi.
static PVOID detour_alloc_region_from_lo(PMDL pMdl, PBYTE /*pbLo*/, PBYTE /*pbHi*/)
{
return MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);
}
// Starting at pbHi, try to allocate a memory region, continue until pbLo.
static PVOID detour_alloc_region_from_hi(PMDL pMdl, PBYTE /*pbLo*/, PBYTE /*pbHi*/)
{
return MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);
}
static PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)
{
// We have to place trampolines within +/- 2GB of target.
PDETOUR_TRAMPOLINE pLo;
PDETOUR_TRAMPOLINE pHi;
detour_find_jmp_bounds(pbTarget, &pLo, &pHi);
PDETOUR_TRAMPOLINE pTrampoline = NULL;
// Insure that there is a default region.
if (s_pRegion == NULL && s_pRegions != NULL) {
s_pRegion = s_pRegions;
}
// First check the default region for an valid free block.
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
found_region:
pTrampoline = s_pRegion->pFree;
// do a last sanity check on region.
if (pTrampoline < pLo || pTrampoline > pHi) {
return NULL;
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;
memset(pTrampoline, 0xcc, sizeof(*pTrampoline));
return pTrampoline;
}
// Then check the existing regions for a valid free block.
for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {
if (s_pRegion != NULL && s_pRegion->pFree != NULL &&
s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {
goto found_region;
}
}
// We need to allocate a new region.
// Round pbTarget down to 64KB block.
pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);
PMDL pMdl = NULL;
PVOID pbTry = NULL;
PHYSICAL_ADDRESS EmptyDesc = { 0 };
PHYSICAL_ADDRESS MaxAddress = { 0 };
MaxAddress.QuadPart = LONGLONG_MAX;
pMdl = MmAllocatePagesForMdlEx(
EmptyDesc, MaxAddress, EmptyDesc, DETOUR_REGION_SIZE,
MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS);
if (pMdl == NULL)
{
DETOUR_TRACE(("Couldn't alloc memory region!\n"));
return NULL;
}
// NB: We must always also start the search at an offset from pbTarget
// in order to maintain ASLR entropy.
#if defined(DETOURS_64BIT)
// Try looking 1GB below or lower.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_hi(pMdl, (PBYTE)pLo, pbTarget - 0x40000000);
}
// Try looking 1GB above or higher.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_lo(pMdl, pbTarget + 0x40000000, (PBYTE)pHi);
}
// Try looking 1GB below or higher.
if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {
pbTry = detour_alloc_region_from_lo(pMdl, pbTarget - 0x40000000, pbTarget);
}
// Try looking 1GB above or lower.
if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {
pbTry = detour_alloc_region_from_hi(pMdl, pbTarget, pbTarget + 0x40000000);
}
#endif
// Try anything below.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_hi(pMdl, (PBYTE)pLo, pbTarget);
}
// try anything above.
if (pbTry == NULL) {
pbTry = detour_alloc_region_from_lo(pMdl, pbTarget, (PBYTE)pHi);
}
if (pbTry != NULL) {
s_pRegion = (DETOUR_REGION*)pbTry;
s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;
s_pRegion->pMdl = pMdl;
s_pRegion->pFree = NULL;
s_pRegion->pNext = s_pRegions;
s_pRegions = s_pRegion;
DETOUR_TRACE((" Allocated region %p..%p\n\n",
s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));
// Put everything but the first trampoline on the free list.
PBYTE pFree = NULL;
pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;
for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {
pTrampoline[i].pbRemain = pFree;
pFree = (PBYTE)&pTrampoline[i];
}
s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;
goto found_region;
}
DETOUR_TRACE(("Couldn't find available memory region!\n"));
return NULL;
}
static void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline)
{
PDETOUR_REGION pRegion = (PDETOUR_REGION)
((ULONG_PTR)pTrampoline & ~(ULONG_PTR)(DETOUR_REGION_SIZE - 1));
memset(pTrampoline, 0, sizeof(*pTrampoline));
pTrampoline->pbRemain = (PBYTE)pRegion->pFree;
pRegion->pFree = pTrampoline;
}
static BOOL detour_is_region_empty(PDETOUR_REGION pRegion)
{
// Stop if the region isn't a region (this would be bad).
if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) {
return FALSE;
}
PBYTE pbRegionBeg = (PBYTE)pRegion;
PBYTE pbRegionLim = pbRegionBeg + DETOUR_REGION_SIZE;
// Stop if any of the trampolines aren't free.
PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1;
for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) {
if (pTrampoline[i].pbRemain != NULL &&
(pTrampoline[i].pbRemain < pbRegionBeg ||
pTrampoline[i].pbRemain >= pbRegionLim)) {
return FALSE;
}
}
// OK, the region is empty.
return TRUE;
}
static void detour_free_unused_trampoline_regions()
{
PDETOUR_REGION *ppRegionBase = &s_pRegions;
PDETOUR_REGION pRegion = s_pRegions;
while (pRegion != NULL) {
if (detour_is_region_empty(pRegion)) {
*ppRegionBase = pRegion->pNext;
PMDL pMdl = pRegion->pMdl;
MmUnmapLockedPages(pRegion, pMdl);
MmFreePagesFromMdl(pMdl);
ExFreePool(pMdl);
s_pRegion = NULL;
}
else {
ppRegionBase = &pRegion->pNext;
}
pRegion = *ppRegionBase;
}
}
///////////////////////////////////////////////////////// Transaction Structs.
//
struct DetourThread
{
DetourThread * pNext;
HANDLE hThread;
};
struct DetourOperation
{
DetourOperation * pNext;
BOOL fIsRemove;
PBYTE * ppbPointer;
PBYTE pbTarget;
PMDL pbTargetMdl;
PDETOUR_TRAMPOLINE pTrampoline;
};
static BOOL s_fIgnoreTooSmall = FALSE;
static BOOL s_fRetainRegions = FALSE;
static LONG s_nPendingThreadId = 0; // Thread owning pending transaction.
static LONG s_nPendingError = NO_ERROR;
static PVOID * s_ppPendingError = NULL;
static DetourThread * s_pPendingThreads = NULL;
static DetourOperation * s_pPendingOperations = NULL;
//////////////////////////////////////////////////////////////////////////////
//
PVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,
_Out_opt_ PVOID *ppGlobals)
{
return detour_skip_jmp((PBYTE)pPointer, ppGlobals);
}
//////////////////////////////////////////////////////////// Transaction APIs.
//
BOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore)
{
BOOL fPrevious = s_fIgnoreTooSmall;
s_fIgnoreTooSmall = fIgnore;
return fPrevious;
}
BOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain)
{
BOOL fPrevious = s_fRetainRegions;
s_fRetainRegions = fRetain;
return fPrevious;
}
PVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound)
{
PVOID pPrevious = s_pSystemRegionLowerBound;
s_pSystemRegionLowerBound = pSystemRegionLowerBound;
return pPrevious;
}
PVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound)
{
PVOID pPrevious = s_pSystemRegionUpperBound;
s_pSystemRegionUpperBound = pSystemRegionUpperBound;
return pPrevious;
}
LONG WINAPI DetourTransactionBegin()
{
// Only one transaction is allowed at a time.
_Benign_race_begin_
if (s_nPendingThreadId != 0) {
return ERROR_INVALID_OPERATION;
}
_Benign_race_end_
// Make sure only one thread can start a transaction.
if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {
return ERROR_INVALID_OPERATION;
}
s_pPendingOperations = NULL;
s_pPendingThreads = NULL;
s_ppPendingError = NULL;
// Make sure the trampoline pages are writable.
s_nPendingError = detour_writable_trampoline_regions();
return s_nPendingError;
}
LONG WINAPI DetourTransactionAbort()
{
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// Restore all of the page permissions.
for (DetourOperation *o = s_pPendingOperations; o != NULL;) {
// We don't care if this fails, because the code is still accessible.
detour_unmap_address(o->pbTargetMdl);
if (!o->fIsRemove) {
if (o->pTrampoline) {
detour_free_trampoline(o->pTrampoline);
o->pTrampoline = NULL;
}
}
DetourOperation *n = o->pNext;
detour_memory_free(o);
o = n;
}
s_pPendingOperations = NULL;
// Make sure the trampoline pages are no longer writable.
detour_runnable_trampoline_regions();
// Resume any suspended processor.
s_pPendingThreads = NULL;
s_nPendingThreadId = 0;
return NO_ERROR;
}
LONG WINAPI DetourTransactionCommit()
{
return DetourTransactionCommitEx(NULL);
}
#pragma warning(suppress: 4505)
static BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline)
{
for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) {
return pTrampoline->rAlign[n].obTarget;
}
}
return 0;
}
#pragma warning(suppress: 4505)
static LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget)
{
for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTarget == obTarget) {
return pTrampoline->rAlign[n].obTrampoline;
}
}
return 0;
}
LONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)
{
if (pppFailedPointer != NULL) {
// Used to get the last error.
*pppFailedPointer = s_ppPendingError;
}
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we abort the whole transaction.
if (s_nPendingError != NO_ERROR) {
DETOUR_BREAK();
DetourTransactionAbort();
return s_nPendingError;
}
KeGenericCallDpc([](PKDPC /*Dpc*/, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2)
{
// Common variables.
BOOL freed = FALSE;
DetourOperation* o = NULL;
ULONG PendingProcessorId = (ULONG)(ULONG_PTR)Context;
KeSignalCallDpcSynchronize(SystemArgument2);
if (PendingProcessorId == KeGetCurrentProcessorNumber()) {
// Insert or remove each of the detours.
for (o = s_pPendingOperations; o != NULL; o = o->pNext) {
if (o->fIsRemove) {
CopyMemory(o->pbTarget,
o->pTrampoline->rbRestore,
o->pTrampoline->cbRestore);
#ifdef DETOURS_IA64
* o->ppbPointer = (PBYTE)o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;
#endif // DETOURS_IA64
#ifdef DETOURS_X86
* o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;
#endif // DETOURS_X86
#ifdef DETOURS_X64
detour_del_codein(o->pTrampoline->pbCodeInMdl);
* o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;
#endif // DETOURS_X64
#ifdef DETOURS_ARM
* o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->pbRemain - o->pTrampoline->cbRestore);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
* o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;
#endif // DETOURS_ARM
}
else {
DETOUR_TRACE(("detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\n",
o->pTrampoline,
o->pTrampoline->pbRemain,
o->pTrampoline->pbDetour,
o->pTrampoline->cbRestore));
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x [before]\n",
o->pbTarget,
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
#ifdef DETOURS_IA64
((DETOUR_IA64_BUNDLE*)o->pbTarget)
->SetBrl((UINT64)&o->pTrampoline->bAllocFrame);
*o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline;
#endif // DETOURS_IA64
#ifdef DETOURS_X64
o->pTrampoline->pbCodeIn = detour_gen_codein(o->pTrampoline->pbRemain - o->pTrampoline->cbRestore,
(PVOID)o->pTrampoline->pbDetour, &o->pTrampoline->pbCodeInMdl);
PBYTE pbCode = detour_gen_jmp_indirect(o->pbTarget, o->pTrampoline->pbRemain - o->pTrampoline->cbRestore, (PBYTE*)o->pTrampoline->pbCodeIn);
pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_X64
#ifdef DETOURS_X86
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbRemain - o->pTrampoline->cbRestore, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_X86
#ifdef DETOURS_ARM
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);
*o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode);
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);
pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);
*o->ppbPointer = o->pTrampoline->rbCode;
UNREFERENCED_PARAMETER(pbCode);
#endif // DETOURS_ARM64
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x [after]\n",
o->pbTarget,
o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],
o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],
o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));
DETOUR_TRACE(("detours: pbTramp =%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
o->pTrampoline,
o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1],
o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3],
o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5],
o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7],
o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9],
o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11]));
#ifdef DETOURS_IA64
DETOUR_TRACE(("\n"));
DETOUR_TRACE(("detours: &pldTrampoline =%p\n",
&o->pTrampoline->pldTrampoline));
DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n",
&o->pTrampoline->bMovlTargetGp,
o->pTrampoline->bMovlTargetGp.GetMovlGp()));
DETOUR_TRACE(("detours: &rbCode =%p [%p]\n",
&o->pTrampoline->rbCode,
((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget()));
DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n",
&o->pTrampoline->bBrlRemainEip,
o->pTrampoline->bBrlRemainEip.GetBrlTarget()));
DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n",
&o->pTrampoline->bMovlDetourGp,
o->pTrampoline->bMovlDetourGp.GetMovlGp()));
DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n",
&o->pTrampoline->bCallDetour,
o->pTrampoline->bCallDetour.GetBrlTarget()));
DETOUR_TRACE(("detours: pldDetour =%p [%p]\n",
o->pTrampoline->ppldDetour->EntryPoint,
o->pTrampoline->ppldDetour->GlobalPointer));
DETOUR_TRACE(("detours: pldTarget =%p [%p]\n",
o->pTrampoline->ppldTarget->EntryPoint,
o->pTrampoline->ppldTarget->GlobalPointer));
DETOUR_TRACE(("detours: pbRemain =%p\n",
o->pTrampoline->pbRemain));
DETOUR_TRACE(("detours: pbDetour =%p\n",
o->pTrampoline->pbDetour));
DETOUR_TRACE(("\n"));
#endif // DETOURS_IA64
}
}
// Restore all of the page permissions and flush the icache.
for (o = s_pPendingOperations; o != NULL;) {
// We don't care if this fails, because the code is still accessible.
detour_unmap_address(o->pbTargetMdl);
if (o->fIsRemove && o->pTrampoline) {
detour_free_trampoline(o->pTrampoline);
o->pTrampoline = NULL;
freed = true;
}
DetourOperation* n = o->pNext;
detour_memory_free(o);
o = n;
}
s_pPendingOperations = NULL;
// Free any trampoline regions that are now unused.
if (freed && !s_fRetainRegions) {
detour_free_unused_trampoline_regions();
}
}
KeSignalCallDpcDone(SystemArgument1);
}, (PVOID)(ULONG_PTR)KeGetCurrentProcessorNumber());
// Make sure the trampoline pages are no longer writable.
detour_runnable_trampoline_regions();
// Resume any suspended threads.
s_pPendingThreads = NULL;
s_nPendingThreadId = 0;
if (pppFailedPointer != NULL) {
*pppFailedPointer = s_ppPendingError;
}
return s_nPendingError;
}
LONG WINAPI DetourUpdateThread(_In_ HANDLE /*hThread*/)
{
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
return s_nPendingError;
}
return NO_ERROR;
}
///////////////////////////////////////////////////////////// Transacted APIs.
//
LONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour)
{
return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);
}
LONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour,
_Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,
_Out_opt_ PVOID *ppRealTarget,
_Out_opt_ PVOID *ppRealDetour)
{
LONG error = NO_ERROR;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
if (ppRealTarget != NULL) {
*ppRealTarget = NULL;
}
if (ppRealDetour != NULL) {
*ppRealDetour = NULL;
}
if (pDetour == NULL) {
DETOUR_TRACE(("empty detour\n"));
return ERROR_INVALID_PARAMETER;
}
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
DETOUR_TRACE(("transaction conflict with thread id=%d\n", s_nPendingThreadId));
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
DETOUR_TRACE(("pending transaction error=%d\n", s_nPendingError));
return s_nPendingError;
}
if (ppPointer == NULL) {
DETOUR_TRACE(("ppPointer is null\n"));
return ERROR_INVALID_HANDLE;
}
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
s_nPendingError = error;
s_ppPendingError = ppPointer;
DETOUR_TRACE(("*ppPointer is null (ppPointer=%p)\n", ppPointer));
DETOUR_BREAK();
return error;
}
PBYTE pbTarget = (PBYTE)*ppPointer;
PDETOUR_TRAMPOLINE pTrampoline = NULL;
DetourOperation *o = NULL;
#ifdef DETOURS_IA64
PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;
PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget;
PVOID pDetourGlobals = NULL;
PVOID pTargetGlobals = NULL;
pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);
pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals);
DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n",
ppldDetour, pDetour, pDetourGlobals));
DETOUR_TRACE((" ppldTarget=%p, code=%p [gp=%p]\n",
ppldTarget, pbTarget, pTargetGlobals));
#else // DETOURS_IA64
pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
#endif // !DETOURS_IA64
// Don't follow a jump if its destination is the target function.
// This happens when the detour does nothing other than call the target.
if (pDetour == (PVOID)pbTarget) {
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (ppRealTarget != NULL) {
*ppRealTarget = pbTarget;
}
if (ppRealDetour != NULL) {
*ppRealDetour = pDetour;
}
o = (DetourOperation*)detour_memory_alloc(sizeof(DetourOperation));
if (o == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
s_nPendingError = error;
DETOUR_BREAK();
stop:
if (pTrampoline != NULL) {
detour_free_trampoline(pTrampoline);
pTrampoline = NULL;
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = NULL;
}
}
if (o != NULL) {
detour_memory_free(o);
o = NULL;
}
s_ppPendingError = ppPointer;
return error;
}
pTrampoline = detour_alloc_trampoline(pbTarget);
if (pTrampoline == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
DETOUR_BREAK();
goto fail;
}
if (ppRealTrampoline != NULL) {
*ppRealTrampoline = pTrampoline;
}
DETOUR_TRACE(("detours: pbTramp=%p, pDetour=%p\n", pTrampoline, pDetour));
memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign));
// Determine the number of movable target instructions.
PBYTE pbSrc = pbTarget;
PBYTE pbTrampoline = pTrampoline->rbCode;
#ifdef DETOURS_IA64
PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1);
#else
PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode);
#endif
ULONG cbTarget = 0;
ULONG cbJump = SIZE_OF_JMP;
ULONG nAlign = 0;
#ifdef DETOURS_ARM
// On ARM, we need an extra instruction when the function isn't 32-bit aligned.
// Check if the existing code is another detour (or at least a similar
// "ldr pc, [PC+0]" jump.
if ((ULONG)pbTarget & 2) {
cbJump += 2;
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xbf00) {
op = fetch_thumb_opcode(pbSrc + 2);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
}
else {
ULONG op = fetch_thumb_opcode(pbSrc);
if (op == 0xf8dff000) { // LDR PC,[PC]
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
*((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;
cbTarget = (LONG)(pbSrc - pbTarget);
// We will fall through the "while" because cbTarget is now >= cbJump.
}
}
#endif
while (cbTarget < cbJump) {
PBYTE pbOp = pbSrc;
LONG lExtra = 0;
DETOUR_TRACE((" DetourCopyInstruction(%p,%p)\n",
pbTrampoline, pbSrc));
pbSrc = (PBYTE)
DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra);
DETOUR_TRACE((" DetourCopyInstruction() = %p (%d bytes)\n",
pbSrc, (int)(pbSrc - pbOp)));
pbTrampoline += (pbSrc - pbOp) + lExtra;
cbTarget = (LONG)(pbSrc - pbTarget);
pTrampoline->rAlign[nAlign].obTarget = cbTarget;
pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode;
nAlign++;
if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) {
break;
}
if (detour_does_code_end_function(pbOp)) {
break;
}
}
// Consume, but don't duplicate padding if it is needed and available.
while (cbTarget < cbJump) {
LONG cFiller = detour_is_code_filler(pbSrc);
if (cFiller == 0) {
break;
}
pbSrc += cFiller;
cbTarget = (LONG)(pbSrc - pbTarget);
}
#if DETOUR_DEBUG
{
DETOUR_TRACE((" detours: rAlign ["));
LONG n = 0;
for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {
if (pTrampoline->rAlign[n].obTarget == 0 &&
pTrampoline->rAlign[n].obTrampoline == 0) {
break;
}
DETOUR_TRACE((" %d/%d",
pTrampoline->rAlign[n].obTarget,
pTrampoline->rAlign[n].obTrampoline
));
}
DETOUR_TRACE((" ]\n"));
}
#endif
if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) {
// Too few instructions.
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (pbTrampoline > pbPool) {
__debugbreak();
}
#ifdef DETOURS_X64
pTrampoline->pbCodeIn = NULL;
pTrampoline->pbCodeInMdl = NULL;
#endif
pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode);
pTrampoline->cbRestore = (BYTE)cbTarget;
CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget);
#if !defined(DETOURS_IA64)
if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) {
// Too many instructions.
error = ERROR_INVALID_HANDLE;
DETOUR_BREAK();
goto fail;
}
#endif // !DETOURS_IA64
pTrampoline->pbRemain = pbTarget + cbTarget;
pTrampoline->pbDetour = (PBYTE)pDetour;
#ifdef DETOURS_IA64
pTrampoline->ppldDetour = ppldDetour;
pTrampoline->ppldTarget = ppldTarget;
pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp;
pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals;
((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop();
pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals);
pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain);
// Alloc frame: alloc r41=ar.pfs,11,0,8,0; mov r40=rp
pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c;
pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200;
// save r36, r37, r38.
pTrampoline->bSave37to39.wide[0] = 0x031021004e019001;
pTrampoline->bSave37to39.wide[1] = 0x8401280600420098;
// save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34
pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800;
pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c;
// save gp,r32,r33" adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;;
pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001;
pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080;
// set detour GP.
pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals);
// call detour: brl.call.sptk.few rp=detour ;;
pTrampoline->bCallDetour.wide[0] = 0x0000000100000005;
pTrampoline->bCallDetour.wide[1] = 0xd000001000000000;
pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour);
// pop frame & gp: adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41
pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802;
pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005;
// return to caller: br.ret.sptk.many rp ;;
pTrampoline->bReturn.wide[0] = 0x0000000100000019;
pTrampoline->bReturn.wide[1] = 0x0084000880000200;
DETOUR_TRACE(("detours: &bMovlTargetGp=%p\n", &pTrampoline->bMovlTargetGp));
DETOUR_TRACE(("detours: &bMovlDetourGp=%p\n", &pTrampoline->bMovlDetourGp));
#endif // DETOURS_IA64
pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode;
#ifdef DETOURS_X64
pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X64
#ifdef DETOURS_X86
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_X86
#ifdef DETOURS_ARM
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM
#ifdef DETOURS_ARM64
pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);
pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);
#endif // DETOURS_ARM64
(void)pbTrampoline;
PMDL pbTargetMdl = detour_remap_address(pbTarget, cbTarget, (void**)&pbTarget);
if (pbTargetMdl == NULL) {
error = GetLastError();
DETOUR_BREAK();
goto fail;
}
DETOUR_TRACE(("detours: pbTarget=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
pbTarget,
pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],
pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],
pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));
DETOUR_TRACE(("detours: pbTramp =%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
pTrampoline,
pTrampoline->rbCode[0], pTrampoline->rbCode[1],
pTrampoline->rbCode[2], pTrampoline->rbCode[3],
pTrampoline->rbCode[4], pTrampoline->rbCode[5],
pTrampoline->rbCode[6], pTrampoline->rbCode[7],
pTrampoline->rbCode[8], pTrampoline->rbCode[9],
pTrampoline->rbCode[10], pTrampoline->rbCode[11]));
o->fIsRemove = FALSE;
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->pbTargetMdl = pbTargetMdl;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}
LONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,
_In_ PVOID pDetour)
{
LONG error = NO_ERROR;
if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {
return ERROR_INVALID_OPERATION;
}
// If any of the pending operations failed, then we don't need to do this.
if (s_nPendingError != NO_ERROR) {
return s_nPendingError;
}
if (pDetour == NULL) {
return ERROR_INVALID_PARAMETER;
}
if (ppPointer == NULL) {
return ERROR_INVALID_HANDLE;
}
if (*ppPointer == NULL) {
error = ERROR_INVALID_HANDLE;
s_nPendingError = error;
s_ppPendingError = ppPointer;
DETOUR_BREAK();
return error;
}
DetourOperation *o = (DetourOperation*)detour_memory_alloc(sizeof(DetourOperation));
if (o == NULL) {
error = ERROR_NOT_ENOUGH_MEMORY;
fail:
s_nPendingError = error;
DETOUR_BREAK();
stop:
if (o != NULL) {
detour_memory_free(o);
o = NULL;
}
s_ppPendingError = ppPointer;
return error;
}
#ifdef DETOURS_IA64
PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer;
PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;
PVOID pDetourGlobals = NULL;
PVOID pTrampoGlobals = NULL;
pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);
PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)
DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals);
DETOUR_TRACE((" ppldDetour=%p, code=%p [gp=%p]\n",
ppldDetour, pDetour, pDetourGlobals));
DETOUR_TRACE((" ppldTrampo=%p, code=%p [gp=%p]\n",
ppldTrampo, pTrampoline, pTrampoGlobals));
DETOUR_TRACE(("\n"));
DETOUR_TRACE(("detours: &pldTrampoline =%p\n",
&pTrampoline->pldTrampoline));
DETOUR_TRACE(("detours: &bMovlTargetGp =%p [%p]\n",
&pTrampoline->bMovlTargetGp,
pTrampoline->bMovlTargetGp.GetMovlGp()));
DETOUR_TRACE(("detours: &rbCode =%p [%p]\n",
&pTrampoline->rbCode,
((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget()));
DETOUR_TRACE(("detours: &bBrlRemainEip =%p [%p]\n",
&pTrampoline->bBrlRemainEip,
pTrampoline->bBrlRemainEip.GetBrlTarget()));
DETOUR_TRACE(("detours: &bMovlDetourGp =%p [%p]\n",
&pTrampoline->bMovlDetourGp,
pTrampoline->bMovlDetourGp.GetMovlGp()));
DETOUR_TRACE(("detours: &bBrlDetourEip =%p [%p]\n",
&pTrampoline->bCallDetour,
pTrampoline->bCallDetour.GetBrlTarget()));
DETOUR_TRACE(("detours: pldDetour =%p [%p]\n",
pTrampoline->ppldDetour->EntryPoint,
pTrampoline->ppldDetour->GlobalPointer));
DETOUR_TRACE(("detours: pldTarget =%p [%p]\n",
pTrampoline->ppldTarget->EntryPoint,
pTrampoline->ppldTarget->GlobalPointer));
DETOUR_TRACE(("detours: pbRemain =%p\n",
pTrampoline->pbRemain));
DETOUR_TRACE(("detours: pbDetour =%p\n",
pTrampoline->pbDetour));
DETOUR_TRACE(("\n"));
#else // !DETOURS_IA64
PDETOUR_TRAMPOLINE pTrampoline =
(PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL);
pDetour = DetourCodeFromPointer(pDetour, NULL);
#endif // !DETOURS_IA64
////////////////////////////////////// Verify that Trampoline is in place.
//
LONG cbTarget = pTrampoline->cbRestore;
PBYTE pbTarget = pTrampoline->pbRemain - cbTarget;
if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) {
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
if (pTrampoline->pbDetour != pDetour) {
error = ERROR_INVALID_BLOCK;
if (s_fIgnoreTooSmall) {
goto stop;
}
else {
DETOUR_BREAK();
goto fail;
}
}
PMDL pbTargetMdl = detour_remap_address(pbTarget, cbTarget, (void**)&pbTarget);
if (pbTargetMdl == nullptr)
{
error = GetLastError();
DETOUR_BREAK();
goto fail;
}
o->fIsRemove = TRUE;
o->ppbPointer = (PBYTE*)ppPointer;
o->pTrampoline = pTrampoline;
o->pbTarget = pbTarget;
o->pbTargetMdl = pbTargetMdl;
o->pNext = s_pPendingOperations;
s_pPendingOperations = o;
return NO_ERROR;
}
//////////////////////////////////////////////////////////////////////////////
//
// Helpers for manipulating page protection.
//
// For reference:
// PAGE_NOACCESS 0x01
// PAGE_READONLY 0x02
// PAGE_READWRITE 0x04
// PAGE_WRITECOPY 0x08
// PAGE_EXECUTE 0x10
// PAGE_EXECUTE_READ 0x20
// PAGE_EXECUTE_READWRITE 0x40
// PAGE_EXECUTE_WRITECOPY 0x80
// PAGE_GUARD ...
// PAGE_NOCACHE ...
// PAGE_WRITECOMBINE ...
#define DETOUR_PAGE_EXECUTE_ALL (PAGE_EXECUTE | \
PAGE_EXECUTE_READ | \
PAGE_EXECUTE_READWRITE | \
PAGE_EXECUTE_WRITECOPY)
#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS | \
PAGE_READONLY | \
PAGE_READWRITE | \
PAGE_WRITECOPY)
#define DETOUR_PAGE_ATTRIBUTES (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL))
C_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL);
#pragma warning(suppress: 4505)
static DWORD DetourPageProtectAdjustExecute(_In_ DWORD dwOldProtect,
_In_ DWORD dwNewProtect)
// Copy EXECUTE from dwOldProtect to dwNewProtect.
{
bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);
bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);
if (fOldExecute && !fNewExecute) {
dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4)
| (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);
}
else if (!fOldExecute && fNewExecute) {
dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4)
| (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);
}
return dwNewProtect;
}
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_ HANDLE hProcess,
_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect)
// Some systems do not allow executability of a page to change. This function applies
// dwNewProtect to [pAddress, nSize), but preserving the previous executability.
// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx.
// When "restoring" page protection, there is no need to use this function.
{
MEMORY_BASIC_INFORMATION mbi;
// Query to get existing execute access.
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) {
return FALSE;
}
return VirtualProtectEx(hProcess, pAddress, nSize,
DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect),
pdwOldProtect);
}
_Success_(return != FALSE)
BOOL WINAPI DetourVirtualProtectSameExecute(_In_ PVOID pAddress,
_In_ SIZE_T nSize,
_In_ DWORD dwNewProtect,
_Out_ PDWORD pdwOldProtect)
{
return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(),
pAddress, nSize, dwNewProtect, pdwOldProtect);
}
// End of File
================================================
FILE: Detours/detver.h
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Common version parameters.
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#define _USING_V110_SDK71_ 1
#include "winver.h"
#if 0
#include
#include
#else
#ifndef DETOURS_STRINGIFY
#define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x)
#define DETOURS_STRINGIFY_(x) #x
#endif
#define VER_FILEFLAGSMASK 0x3fL
#define VER_FILEFLAGS 0x0L
#define VER_FILEOS 0x00040004L
#define VER_FILETYPE 0x00000002L
#define VER_FILESUBTYPE 0x00000000L
#endif
#define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS)
================================================
FILE: Detours/disasm.cpp
================================================
//////////////////////////////////////////////////////////////////////////////
//
// Detours Disassembler (disasm.cpp of detours.lib)
//
// Microsoft Research Detours Package, Version 4.0.1
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#if _MSC_VER >= 1900
#pragma warning(push)
#pragma warning(disable:4091) // empty typedef
#endif
#if defined(_KERNEL_MODE)
#define DETOURS_KERNEL
#endif
#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1
#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1
#include
#ifdef DETOURS_KERNEL
#include "api_thunks.h"
#endif
#include
// #define DETOUR_DEBUG 1
#define DETOURS_INTERNAL
#include "detours.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)
//////////////////////////////////////////////////////////////////////////////
//
// Special macros to handle the case when we are building disassembler for
// offline processing.
//
#if defined(DETOURS_X86_OFFLINE_LIBRARY) \
|| defined(DETOURS_X64_OFFLINE_LIBRARY) \
|| defined(DETOURS_ARM_OFFLINE_LIBRARY) \
|| defined(DETOURS_ARM64_OFFLINE_LIBRARY) \
|| defined(DETOURS_IA64_OFFLINE_LIBRARY)
#undef DETOURS_X64
#undef DETOURS_X86
#undef DETOURS_IA64
#undef DETOURS_ARM
#undef DETOURS_ARM64
#if defined(DETOURS_X86_OFFLINE_LIBRARY)
#define DetourCopyInstruction DetourCopyInstructionX86
#define DetourSetCodeModule DetourSetCodeModuleX86
#define CDetourDis CDetourDisX86
#define DETOURS_X86
#elif defined(DETOURS_X64_OFFLINE_LIBRARY)
#if !defined(DETOURS_64BIT)
// Fix this as/if bugs are discovered.
//#error X64 disassembler can only build for 64-bit.
#endif
#define DetourCopyInstruction DetourCopyInstructionX64
#define DetourSetCodeModule DetourSetCodeModuleX64
#define CDetourDis CDetourDisX64
#define DETOURS_X64
#elif defined(DETOURS_ARM_OFFLINE_LIBRARY)
#define DetourCopyInstruction DetourCopyInstructionARM
#define DetourSetCodeModule DetourSetCodeModuleARM
#define CDetourDis CDetourDisARM
#define DETOURS_ARM
#elif defined(DETOURS_ARM64_OFFLINE_LIBRARY)
#define DetourCopyInstruction DetourCopyInstructionARM64
#define DetourSetCodeModule DetourSetCodeModuleARM64
#define CDetourDis CDetourDisARM64
#define DETOURS_ARM64
#elif defined(DETOURS_IA64_OFFLINE_LIBRARY)
#define DetourCopyInstruction DetourCopyInstructionIA64
#define DetourSetCodeModule DetourSetCodeModuleIA64
#define DETOURS_IA64
#else
#error
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// Function:
// DetourCopyInstruction(PVOID pDst,
// PVOID *ppDstPool
// PVOID pSrc,
// PVOID *ppTarget,
// LONG *plExtra)
// Purpose:
// Copy a single instruction from pSrc to pDst.
//
// Arguments:
// pDst:
// Destination address for the instruction. May be NULL in which
// case DetourCopyInstruction is used to measure an instruction.
// If not NULL then the source instruction is copied to the
// destination instruction and any relative arguments are adjusted.
// ppDstPool:
// Destination address for the end of the constant pool. The
// constant pool works backwards toward pDst. All memory between
// pDst and *ppDstPool must be available for use by this function.
// ppDstPool may be NULL if pDst is NULL.
// pSrc:
// Source address of the instruction.
// ppTarget:
// Out parameter for any target instruction address pointed to by
// the instruction. For example, a branch or a jump insruction has
// a target, but a load or store instruction doesn't. A target is
// another instruction that may be executed as a result of this
// instruction. ppTarget may be NULL.
// plExtra:
// Out parameter for the number of extra bytes needed by the
// instruction to reach the target. For example, lExtra = 3 if the
// instruction had an 8-bit relative offset, but needs a 32-bit
// relative offset.
//
// Returns:
// Returns the address of the next instruction (following in the source)
// instruction. By subtracting pSrc from the return value, the caller
// can determinte the size of the instruction copied.
//
// Comments:
// By following the pTarget, the caller can follow alternate
// instruction streams. However, it is not always possible to determine
// the target based on static analysis. For example, the destination of
// a jump relative to a register cannot be determined from just the
// instruction stream. The output value, pTarget, can have any of the
// following outputs:
// DETOUR_INSTRUCTION_TARGET_NONE:
// The instruction has no targets.
// DETOUR_INSTRUCTION_TARGET_DYNAMIC:
// The instruction has a non-deterministic (dynamic) target.
// (i.e. the jump is to an address held in a register.)
// Address: The instruction has the specified target.
//
// When copying instructions, DetourCopyInstruction insures that any
// targets remain constant. It does so by adjusting any IP relative
// offsets.
//
#pragma data_seg(".detourd")
#pragma const_seg(".detourc")
//////////////////////////////////////////////////// X86 and X64 Disassembler.
//
// Includes full support for all x86 chips prior to the Pentium III, and some newer stuff.
//
#if defined(DETOURS_X64) || defined(DETOURS_X86)
class CDetourDis
{
public:
CDetourDis(_Out_opt_ PBYTE *ppbTarget,
_Out_opt_ LONG *plExtra);
PBYTE CopyInstruction(PBYTE pbDst, PBYTE pbSrc);
static BOOL SanityCheckSystem();
static BOOL SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule);
public:
struct COPYENTRY;
typedef const COPYENTRY * REFCOPYENTRY;
typedef PBYTE (CDetourDis::* COPYFUNC)(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
// nFlagBits flags.
enum {
DYNAMIC = 0x1u,
ADDRESS = 0x2u,
NOENLARGE = 0x4u,
RAX = 0x8u,
};
// ModR/M Flags
enum {
SIB = 0x10u,
RIP = 0x20u,
NOTSIB = 0x0fu,
};
struct COPYENTRY
{
// Many of these fields are often ignored. See ENTRY_DataIgnored.
ULONG nOpcode : 8; // Opcode (ignored)
ULONG nFixedSize : 4; // Fixed size of opcode
ULONG nFixedSize16 : 4; // Fixed size when 16 bit operand
ULONG nModOffset : 4; // Offset to mod/rm byte (0=none)
ULONG nRelOffset : 4; // Offset to relative target.
ULONG nTargetBack : 4; // Offset back to absolute or rip target
ULONG nFlagBits : 4; // Flags for DYNAMIC, etc.
COPYFUNC pfCopy; // Function pointer.
};
protected:
// These macros define common uses of nFixedSize..pfCopy.
#define ENTRY_DataIgnored 0, 0, 0, 0, 0, 0,
#define ENTRY_CopyBytes1 1, 1, 0, 0, 0, 0, &CDetourDis::CopyBytes
#ifdef DETOURS_X64
#define ENTRY_CopyBytes1Address 9, 5, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes
#else
#define ENTRY_CopyBytes1Address 5, 3, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes
#endif
#define ENTRY_CopyBytes1Dynamic 1, 1, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2 2, 2, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2Jump ENTRY_DataIgnored &CDetourDis::CopyBytesJump
#define ENTRY_CopyBytes2CantJump 2, 2, 0, 1, 0, NOENLARGE, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2Dynamic 2, 2, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3 3, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Dynamic 3, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Or5 5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Or5Dynamic 5, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes // x86 only
#ifdef DETOURS_X64
#define ENTRY_CopyBytes3Or5Rax 5, 3, 0, 0, 0, RAX, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Or5Target 5, 5, 0, 1, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytesJumpToAbsolute 5, 5, 0, 1, 0, 0, &CDetourDis::CopyBytesJumpToAbsolute
#else
#define ENTRY_CopyBytes3Or5Rax 5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Or5Target 5, 3, 0, 1, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytesJumpToAbsolute ENTRY_CopyBytes3Or5Target
#endif
#define ENTRY_CopyBytes4 4, 4, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes5 5, 5, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes5Or7Dynamic 7, 5, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes7 7, 7, 0, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2Mod 2, 2, 1, 0, 0, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2ModDynamic 2, 2, 1, 0, 0, DYNAMIC, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2Mod1 3, 3, 1, 0, 1, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes2ModOperand 6, 4, 1, 0, 4, 0, &CDetourDis::CopyBytes
#define ENTRY_CopyBytes3Mod 3, 3, 2, 0, 0, 0, &CDetourDis::CopyBytes // SSE3 0F 38 opcode modrm
#define ENTRY_CopyBytes3Mod1 4, 4, 2, 0, 1, 0, &CDetourDis::CopyBytes // SSE3 0F 3A opcode modrm .. imm8
#define ENTRY_CopyBytesPrefix ENTRY_DataIgnored &CDetourDis::CopyBytesPrefix
#define ENTRY_CopyBytesSegment ENTRY_DataIgnored &CDetourDis::CopyBytesSegment
#define ENTRY_CopyBytesRax ENTRY_DataIgnored &CDetourDis::CopyBytesRax
#define ENTRY_CopyF2 ENTRY_DataIgnored &CDetourDis::CopyF2
#define ENTRY_CopyF3 ENTRY_DataIgnored &CDetourDis::CopyF3 // 32bit x86 only
#define ENTRY_Copy0F ENTRY_DataIgnored &CDetourDis::Copy0F
#define ENTRY_Copy0F78 ENTRY_DataIgnored &CDetourDis::Copy0F78
#define ENTRY_Copy0F00 ENTRY_DataIgnored &CDetourDis::Copy0F00 // 32bit x86 only
#define ENTRY_Copy0FB8 ENTRY_DataIgnored &CDetourDis::Copy0FB8 // 32bit x86 only
#define ENTRY_Copy66 ENTRY_DataIgnored &CDetourDis::Copy66
#define ENTRY_Copy67 ENTRY_DataIgnored &CDetourDis::Copy67
#define ENTRY_CopyF6 ENTRY_DataIgnored &CDetourDis::CopyF6
#define ENTRY_CopyF7 ENTRY_DataIgnored &CDetourDis::CopyF7
#define ENTRY_CopyFF ENTRY_DataIgnored &CDetourDis::CopyFF
#define ENTRY_CopyVex2 ENTRY_DataIgnored &CDetourDis::CopyVex2
#define ENTRY_CopyVex3 ENTRY_DataIgnored &CDetourDis::CopyVex3
#define ENTRY_Invalid ENTRY_DataIgnored &CDetourDis::Invalid
#define ENTRY_End ENTRY_DataIgnored NULL
PBYTE CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyBytesSegment(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyBytesRax(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyBytesJumpToAbsolute(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp,
UINT cbTargetOffset, UINT cbTargetSize);
protected:
PBYTE Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE Copy0F00(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7
PBYTE Copy0F78(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // vmread, 66/extrq/ib/ib, F2/insertq/ib/ib
PBYTE Copy0FB8(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // jmpe or F3/popcnt
PBYTE Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only
PBYTE CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);
PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc);
protected:
static const COPYENTRY s_rceCopyTable[257];
static const COPYENTRY s_rceCopyTable0F[257];
static const BYTE s_rbModRm[256];
static PBYTE s_pbModuleBeg;
static PBYTE s_pbModuleEnd;
static BOOL s_fLimitReferencesToModule;
protected:
BOOL m_bOperandOverride = FALSE;
BOOL m_bAddressOverride = FALSE;
BOOL m_bRaxOverride = FALSE; // AMD64 only
BOOL m_bVex = FALSE;
BOOL m_bF2 = FALSE;
BOOL m_bF3 = FALSE; // x86 only
BYTE m_nSegmentOverride = 0;
PBYTE * m_ppbTarget = NULL;
LONG * m_plExtra = NULL;
LONG m_lScratchExtra = 0;
PBYTE m_pbScratchTarget = NULL;
BYTE m_rbScratchDst[64] = { 0 };
};
PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,
_Inout_opt_ PVOID *ppDstPool,
_In_ PVOID pSrc,
_Out_opt_ PVOID *ppTarget,
_Out_opt_ LONG *plExtra)
{
UNREFERENCED_PARAMETER(ppDstPool); // x86 & x64 don't use a constant pool.
CDetourDis oDetourDisasm((PBYTE*)ppTarget, plExtra);
return oDetourDisasm.CopyInstruction((PBYTE)pDst, (PBYTE)pSrc);
}
/////////////////////////////////////////////////////////// Disassembler Code.
//
CDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra)
{
m_bOperandOverride = FALSE;
m_bAddressOverride = FALSE;
m_bRaxOverride = FALSE;
m_bF2 = FALSE;
m_bF3 = FALSE;
m_bVex = FALSE;
m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget;
m_plExtra = plExtra ? plExtra : &m_lScratchExtra;
*m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE;
*m_plExtra = 0;
}
PBYTE CDetourDis::CopyInstruction(PBYTE pbDst, PBYTE pbSrc)
{
// Configure scratch areas if real areas are not available.
if (NULL == pbDst) {
pbDst = m_rbScratchDst;
}
if (NULL == pbSrc) {
// We can't copy a non-existent instruction.
SetLastError(ERROR_INVALID_DATA);
return NULL;
}
// Figure out how big the instruction is, do the appropriate copy,
// and figure out what the target of the instruction is if any.
//
REFCOPYENTRY pEntry = &s_rceCopyTable[pbSrc[0]];
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
UINT nBytesFixed;
ASSERT(!m_bVex || pEntry->nFlagBits == 0);
ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16);
UINT const nModOffset = pEntry->nModOffset;
UINT const nFlagBits = pEntry->nFlagBits;
UINT const nFixedSize = pEntry->nFixedSize;
UINT const nFixedSize16 = pEntry->nFixedSize16;
if (nFlagBits & ADDRESS) {
nBytesFixed = m_bAddressOverride ? nFixedSize16 : nFixedSize;
}
#ifdef DETOURS_X64
// REX.W trumps 66
else if (m_bRaxOverride) {
nBytesFixed = nFixedSize + ((nFlagBits & RAX) ? 4 : 0);
}
#endif
else {
nBytesFixed = m_bOperandOverride ? nFixedSize16 : nFixedSize;
}
UINT nBytes = nBytesFixed;
UINT nRelOffset = pEntry->nRelOffset;
UINT cbTarget = nBytes - nRelOffset;
if (nModOffset > 0) {
ASSERT(nRelOffset == 0);
BYTE const bModRm = pbSrc[nModOffset];
BYTE const bFlags = s_rbModRm[bModRm];
nBytes += bFlags & NOTSIB;
if (bFlags & SIB) {
BYTE const bSib = pbSrc[nModOffset + 1];
if ((bSib & 0x07) == 0x05) {
if ((bModRm & 0xc0) == 0x00) {
nBytes += 4;
}
else if ((bModRm & 0xc0) == 0x40) {
nBytes += 1;
}
else if ((bModRm & 0xc0) == 0x80) {
nBytes += 4;
}
}
cbTarget = nBytes - nRelOffset;
}
#ifdef DETOURS_X64
else if (bFlags & RIP) {
UINT nTargetBack = pEntry->nTargetBack;
// nTargetBack describes immediate bytes at the end: 1, 2, or 4.
// 2 vs. 4 is selected via 66 operand size override.
ASSERT(nTargetBack == 0 || nTargetBack == 1 || nTargetBack == 4);
if (nTargetBack == 4 && m_bOperandOverride && !m_bRaxOverride) {
nTargetBack = 2;
}
nRelOffset = nBytes - (4 + nTargetBack);
cbTarget = 4;
}
#endif
}
CopyMemory(pbDst, pbSrc, nBytes);
if (nRelOffset) {
*m_ppbTarget = AdjustTarget(pbDst, pbSrc, nBytes, nRelOffset, cbTarget);
#ifdef DETOURS_X64
if (pEntry->nRelOffset == 0) {
// This is a data target, not a code target, so we shouldn't return it.
*m_ppbTarget = NULL;
}
#endif
}
if (nFlagBits & NOENLARGE) {
*m_plExtra = -*m_plExtra;
}
if (nFlagBits & DYNAMIC) {
*m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
}
return pbSrc + nBytes;
}
PBYTE CDetourDis::CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
pbDst[0] = pbSrc[0];
pEntry = &s_rceCopyTable[pbSrc[1]];
return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1);
}
PBYTE CDetourDis::CopyBytesSegment(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
{
m_nSegmentOverride = pbSrc[0];
return CopyBytesPrefix(NULL, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyBytesRax(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
{ // AMD64 only
if (pbSrc[0] & 0x8) {
m_bRaxOverride = TRUE;
}
return CopyBytesPrefix(NULL, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
(void)pEntry;
PVOID pvSrcAddr = &pbSrc[1];
PVOID pvDstAddr = NULL;
LONG_PTR nOldOffset = (LONG_PTR)*(signed char*&)pvSrcAddr;
LONG_PTR nNewOffset = 0;
*m_ppbTarget = pbSrc + 2 + nOldOffset;
if (pbSrc[0] == 0xeb) {
pbDst[0] = 0xe9;
pvDstAddr = &pbDst[1];
nNewOffset = nOldOffset - ((pbDst - pbSrc) + 3);
*(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset;
*m_plExtra = 3;
return pbSrc + 2;
}
ASSERT(pbSrc[0] >= 0x70 && pbSrc[0] <= 0x7f);
pbDst[0] = 0x0f;
pbDst[1] = 0x80 | (pbSrc[0] & 0xf);
pvDstAddr = &pbDst[2];
nNewOffset = nOldOffset - ((pbDst - pbSrc) + 4);
*(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset;
*m_plExtra = 4;
return pbSrc + 2;
}
PBYTE CDetourDis::CopyBytesJumpToAbsolute(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
#ifdef DETOURS_KERNEL
#ifdef DETOURS_X64
PVOID pvSrcAddr = &pbSrc[1];
LONG_PTR nOldOffset = (LONG_PTR) * (LONG*&)pvSrcAddr;
LONG_PTR nNewOffset = (LONG_PTR)(pbSrc + 5 + nOldOffset);
// call imm32
if (pbSrc[0] == 0xE8) {
// jmp rax, imm64
pbDst[0] = 0x48;
pbDst[1] = 0xB8;
*(UNALIGNED LONG_PTR*)& pbDst[2] = nNewOffset;
// call rax
pbDst[10] = 0xFF;
pbDst[11] = 0xD0;
*m_plExtra = 12 - 5;
*m_ppbTarget = (PBYTE)nNewOffset;
return pbSrc + 5;
}
// jmp imm32
if (pbSrc[0] == 0xE9) {
// jmp rax, imm64
pbDst[0] = 0x48;
pbDst[1] = 0xB8;
*(UNALIGNED LONG_PTR*)&pbDst[2] = nNewOffset;
// jmp rax
pbDst[10] = 0xFF;
pbDst[11] = 0xE0;
*m_plExtra = 12 - 5;
*m_ppbTarget = (PBYTE)nNewOffset;
return pbSrc + 5;
}
#endif // DETOURS_X64
#endif // DETOURS_KERNEL
return CopyBytes(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp,
UINT cbTargetOffset, UINT cbTargetSize)
{
PBYTE pbTarget = NULL;
#if 1 // fault injection to test test code
#if defined(DETOURS_X64)
typedef LONGLONG T;
#else
typedef LONG T;
#endif
T nOldOffset;
T nNewOffset;
PVOID pvTargetAddr = &pbDst[cbTargetOffset];
switch (cbTargetSize) {
case 1:
nOldOffset = *(signed char*&)pvTargetAddr;
break;
case 2:
nOldOffset = *(UNALIGNED SHORT*&)pvTargetAddr;
break;
case 4:
nOldOffset = *(UNALIGNED LONG*&)pvTargetAddr;
break;
#if defined(DETOURS_X64)
case 8:
nOldOffset = *(UNALIGNED LONGLONG*&)pvTargetAddr;
break;
#endif
default:
ASSERT(!"cbTargetSize is invalid.");
nOldOffset = 0;
break;
}
pbTarget = pbSrc + cbOp + nOldOffset;
nNewOffset = nOldOffset - (T)(pbDst - pbSrc);
switch (cbTargetSize) {
case 1:
*(CHAR*&)pvTargetAddr = (CHAR)nNewOffset;
if (nNewOffset < SCHAR_MIN || nNewOffset > SCHAR_MAX) {
*m_plExtra = sizeof(ULONG) - 1;
}
break;
case 2:
*(UNALIGNED SHORT*&)pvTargetAddr = (SHORT)nNewOffset;
if (nNewOffset < SHRT_MIN || nNewOffset > SHRT_MAX) {
*m_plExtra = sizeof(ULONG) - 2;
}
break;
case 4:
*(UNALIGNED LONG*&)pvTargetAddr = (LONG)nNewOffset;
if (nNewOffset < LONG_MIN || nNewOffset > LONG_MAX) {
*m_plExtra = sizeof(ULONG) - 4;
}
break;
#if defined(DETOURS_X64)
case 8:
*(UNALIGNED LONGLONG*&)pvTargetAddr = nNewOffset;
break;
#endif
}
#ifdef DETOURS_X64
// When we are only computing size, source and dest can be
// far apart, distance not encodable in 32bits. Ok.
// At least still check the lower 32bits.
if (pbDst >= m_rbScratchDst && pbDst < (sizeof(m_rbScratchDst) + m_rbScratchDst)) {
ASSERT((((size_t)pbDst + cbOp + nNewOffset) & 0xFFFFFFFF) == (((size_t)pbTarget) & 0xFFFFFFFF));
}
else
#endif
{
ASSERT(pbDst + cbOp + nNewOffset == pbTarget);
}
#endif
return pbTarget;
}
PBYTE CDetourDis::Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
(void)pbDst;
(void)pEntry;
ASSERT(!"Invalid Instruction");
return pbSrc + 1;
}
////////////////////////////////////////////////////// Individual Bytes Codes.
//
PBYTE CDetourDis::Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
pbDst[0] = pbSrc[0];
pEntry = &s_rceCopyTable0F[pbSrc[1]];
return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1);
}
PBYTE CDetourDis::Copy0F78(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
{
// vmread, 66/extrq, F2/insertq
static const COPYENTRY vmread = { 0x78, ENTRY_CopyBytes2Mod };
static const COPYENTRY extrq_insertq = { 0x78, ENTRY_CopyBytes4 };
ASSERT(!(m_bF2 && m_bOperandOverride));
// For insertq and presumably despite documentation extrq, mode must be 11, not checked.
// insertq/extrq/78 are followed by two immediate bytes, and given mode == 11, mod/rm byte is always one byte,
// and the 0x78 makes 4 bytes (not counting the 66/F2/F which are accounted for elsewhere)
REFCOPYENTRY const pEntry = ((m_bF2 || m_bOperandOverride) ? &extrq_insertq : &vmread);
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::Copy0F00(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
{
// jmpe is 32bit x86 only
// Notice that the sizes are the same either way, but jmpe is marked as "dynamic".
static const COPYENTRY other = { 0xB8, ENTRY_CopyBytes2Mod }; // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6 invalid/7
static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes2ModDynamic }; // jmpe/6 x86-on-IA64 syscalls
REFCOPYENTRY const pEntry = (((6 << 3) == ((7 << 3) & pbSrc[1])) ? &jmpe : &other);
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::Copy0FB8(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
{
// jmpe is 32bit x86 only
static const COPYENTRY popcnt = { 0xB8, ENTRY_CopyBytes2Mod };
static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes3Or5Dynamic }; // jmpe x86-on-IA64 syscalls
REFCOPYENTRY const pEntry = m_bF3 ? &popcnt : &jmpe;
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{ // Operand-size override prefix
m_bOperandOverride = TRUE;
return CopyBytesPrefix(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{ // Address size override prefix
m_bAddressOverride = TRUE;
return CopyBytesPrefix(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
m_bF2 = TRUE;
return CopyBytesPrefix(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{ // x86 only
m_bF3 = TRUE;
return CopyBytesPrefix(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
(void)pEntry;
// TEST BYTE /0
if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0
static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod1 };
return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
}
// DIV /6
// IDIV /7
// IMUL /5
// MUL /4
// NEG /3
// NOT /2
static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod };
return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{
(void)pEntry;
// TEST WORD /0
if (0x00 == (0x38 & pbSrc[1])) { // reg(bits 543) of ModR/M == 0
static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2ModOperand };
return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
}
// DIV /6
// IDIV /7
// IMUL /5
// MUL /4
// NEG /3
// NOT /2
static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2Mod };
return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
{ // INC /0
// DEC /1
// CALL /2
// CALL /3
// JMP /4
// JMP /5
// PUSH /6
// invalid/7
(void)pEntry;
static const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };
PBYTE pbOut = (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
BYTE const b1 = pbSrc[1];
if (0x15 == b1 || 0x25 == b1) { // CALL [], JMP []
#ifdef DETOURS_X64
// All segments but FS and GS are equivalent.
if (m_nSegmentOverride != 0x64 && m_nSegmentOverride != 0x65)
#else
if (m_nSegmentOverride == 0 || m_nSegmentOverride == 0x2E)
#endif
{
#ifdef DETOURS_X64
INT32 offset = *(UNALIGNED INT32*)&pbSrc[2];
PBYTE *ppbTarget = (PBYTE *)(pbSrc + 6 + offset);
#else
PBYTE *ppbTarget = (PBYTE *)(SIZE_T)*(UNALIGNED ULONG*)&pbSrc[2];
#endif
if (s_fLimitReferencesToModule &&
(ppbTarget < (PVOID)s_pbModuleBeg || ppbTarget >= (PVOID)s_pbModuleEnd)) {
*m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
}
else {
// This can access violate on random bytes. Use DetourSetCodeModule.
*m_ppbTarget = *ppbTarget;
}
}
else {
*m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
}
}
else if (0x10 == (0x30 & b1) || // CALL /2 or /3 --> reg(bits 543) of ModR/M == 010 or 011
0x20 == (0x30 & b1)) { // JMP /4 or /5 --> reg(bits 543) of ModR/M == 100 or 101
*m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
}
return pbOut;
}
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.
{
static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod };
static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 };
static const COPYENTRY Invalid = { 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
case 0: break;
case 1: m_bOperandOverride = TRUE; break;
case 2: m_bF3 = TRUE; break;
case 3: m_bF2 = TRUE; break;
}
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
PBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
// 3 byte VEX prefix 0xC4
{
#ifdef DETOURS_X86
const static COPYENTRY ceLES = { 0xC4, ENTRY_CopyBytes2Mod };
if ((pbSrc[1] & 0xC0) != 0xC0) {
REFCOPYENTRY pEntry = &ceLES;
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
#endif
pbDst[0] = pbSrc[0];
pbDst[1] = pbSrc[1];
pbDst[2] = pbSrc[2];
#ifdef DETOURS_X64
m_bRaxOverride |= !!(pbSrc[2] & 0x80); // w in last byte, see CopyBytesRax
#else
//
// TODO
//
// Usually the VEX.W bit changes the size of a general purpose register and is ignored for 32bit.
// Sometimes it is an opcode extension.
// Look in the Intel manual, in the instruction-by-instruction reference, for ".W1",
// without nearby wording saying it is ignored for 32bit.
// For example: "VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values".
//
// Then, go through each such case and determine if W0 vs. W1 affect the size of the instruction. Probably not.
// Look for the same encoding but with "W1" changed to "W0".
// Here is one such pairing:
// VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values
//
// VEX.DDS.128.66.0F38.W1 98 /r A V/V FMA Multiply packed double-precision floating-point values
// from xmm0 and xmm2/mem, add to xmm1 and
// put result in xmm0.
// VFMADD132PD xmm0, xmm1, xmm2/m128
//
// VFMADD132PS/VFMADD213PS/VFMADD231PS Fused Multiply-Add of Packed Single-Precision Floating-Point Values
// VEX.DDS.128.66.0F38.W0 98 /r A V/V FMA Multiply packed single-precision floating-point values
// from xmm0 and xmm2/mem, add to xmm1 and put
// result in xmm0.
// VFMADD132PS xmm0, xmm1, xmm2/m128
//
#endif
return CopyVexCommon(pbSrc[1] & 0x1F, pbDst + 3, pbSrc + 3);
}
PBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)
// 2 byte VEX prefix 0xC5
{
#ifdef DETOURS_X86
const static COPYENTRY ceLDS = { 0xC5, ENTRY_CopyBytes2Mod };
if ((pbSrc[1] & 0xC0) != 0xC0) {
REFCOPYENTRY pEntry = &ceLDS;
return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);
}
#endif
pbDst[0] = pbSrc[0];
pbDst[1] = pbSrc[1];
return CopyVexCommon(1, pbDst + 2, pbSrc + 2);
}
//////////////////////////////////////////////////////////////////////////////
//
PBYTE CDetourDis::s_pbModuleBeg = NULL;
PBYTE CDetourDis::s_pbModuleEnd = (PBYTE)~(ULONG_PTR)0;
BOOL CDetourDis::s_fLimitReferencesToModule = FALSE;
BOOL CDetourDis::SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule)
{
if (pbEnd < pbBeg) {
return FALSE;
}
s_pbModuleBeg = pbBeg;
s_pbModuleEnd = pbEnd;
s_fLimitReferencesToModule = fLimitReferencesToModule;
return TRUE;
}
///////////////////////////////////////////////////////// Disassembler Tables.
//
const BYTE CDetourDis::s_rbModRm[256] = {
0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 0x
0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 1x
0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 2x
0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 3x
1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 4x
1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 5x
1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 6x
1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 7x
4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 8x
4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 9x
4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Ax
4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Bx
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Cx
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Dx
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Ex
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // Fx
};
const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =
{
{ 0x00, ENTRY_CopyBytes2Mod }, // ADD /r
{ 0x01, ENTRY_CopyBytes2Mod }, // ADD /r
{ 0x02, ENTRY_CopyBytes2Mod }, // ADD /r
{ 0x03, ENTRY_CopyBytes2Mod }, // ADD /r
{ 0x04, ENTRY_CopyBytes2 }, // ADD ib
{ 0x05, ENTRY_CopyBytes3Or5 }, // ADD iw
#ifdef DETOURS_X64
{ 0x06, ENTRY_Invalid }, // Invalid
{ 0x07, ENTRY_Invalid }, // Invalid
#else
{ 0x06, ENTRY_CopyBytes1 }, // PUSH
{ 0x07, ENTRY_CopyBytes1 }, // POP
#endif
{ 0x08, ENTRY_CopyBytes2Mod }, // OR /r
{ 0x09, ENTRY_CopyBytes2Mod }, // OR /r
{ 0x0A, ENTRY_CopyBytes2Mod }, // OR /r
{ 0x0B, ENTRY_CopyBytes2Mod }, // OR /r
{ 0x0C, ENTRY_CopyBytes2 }, // OR ib
{ 0x0D, ENTRY_CopyBytes3Or5 }, // OR iw
#ifdef DETOURS_X64
{ 0x0E, ENTRY_Invalid }, // Invalid
#else
{ 0x0E, ENTRY_CopyBytes1 }, // PUSH
#endif
{ 0x0F, ENTRY_Copy0F }, // Extension Ops
{ 0x10, ENTRY_CopyBytes2Mod }, // ADC /r
{ 0x11, ENTRY_CopyBytes2Mod }, // ADC /r
{ 0x12, ENTRY_CopyBytes2Mod }, // ADC /r
{ 0x13, ENTRY_CopyBytes2Mod }, // ADC /r
{ 0x14, ENTRY_CopyBytes2 }, // ADC ib
{ 0x15, ENTRY_CopyBytes3Or5 }, // ADC id
#ifdef DETOURS_X64
{ 0x16, ENTRY_Invalid }, // Invalid
{ 0x17, ENTRY_Invalid }, // Invalid
#else
{ 0x16, ENTRY_CopyBytes1 }, // PUSH
{ 0x17, ENTRY_CopyBytes1 }, // POP
#endif
{ 0x18, ENTRY_CopyBytes2Mod }, // SBB /r
{ 0x19, ENTRY_CopyBytes2Mod }, // SBB /r
{ 0x1A, ENTRY_CopyBytes2Mod }, // SBB /r
{ 0x1B, ENTRY_CopyBytes2Mod }, // SBB /r
{ 0x1C, ENTRY_CopyBytes2 }, // SBB ib
{ 0x1D, ENTRY_CopyBytes3Or5 }, // SBB id
#ifdef DETOURS_X64
{ 0x1E, ENTRY_Invalid }, // Invalid
{ 0x1F, ENTRY_Invalid }, // Invalid
#else
{ 0x1E, ENTRY_CopyBytes1 }, // PUSH
{ 0x1F, ENTRY_CopyBytes1 }, // POP
#endif
{ 0x20, ENTRY_CopyBytes2Mod }, // AND /r
{ 0x21, ENTRY_CopyBytes2Mod }, // AND /r
{ 0x22, ENTRY_CopyBytes2Mod }, // AND /r
{ 0x23, ENTRY_CopyBytes2Mod }, // AND /r
{ 0x24, ENTRY_CopyBytes2 }, // AND ib
{ 0x25, ENTRY_CopyBytes3Or5 }, // AND id
{ 0x26, ENTRY_CopyBytesSegment }, // ES prefix
#ifdef DETOURS_X64
{ 0x27, ENTRY_Invalid }, // Invalid
#else
{ 0x27, ENTRY_CopyBytes1 }, // DAA
#endif
{ 0x28, ENTRY_CopyBytes2Mod }, // SUB /r
{ 0x29, ENTRY_CopyBytes2Mod }, // SUB /r
{ 0x2A, ENTRY_CopyBytes2Mod }, // SUB /r
{ 0x2B, ENTRY_CopyBytes2Mod }, // SUB /r
{ 0x2C, ENTRY_CopyBytes2 }, // SUB ib
{ 0x2D, ENTRY_CopyBytes3Or5 }, // SUB id
{ 0x2E, ENTRY_CopyBytesSegment }, // CS prefix
#ifdef DETOURS_X64
{ 0x2F, ENTRY_Invalid }, // Invalid
#else
{ 0x2F, ENTRY_CopyBytes1 }, // DAS
#endif
{ 0x30, ENTRY_CopyBytes2Mod }, // XOR /r
{ 0x31, ENTRY_CopyBytes2Mod }, // XOR /r
{ 0x32, ENTRY_CopyBytes2Mod }, // XOR /r
{ 0x33, ENTRY_CopyBytes2Mod }, // XOR /r
{ 0x34, ENTRY_CopyBytes2 }, // XOR ib
{ 0x35, ENTRY_CopyBytes3Or5 }, // XOR id
{ 0x36, ENTRY_CopyBytesSegment }, // SS prefix
#ifdef DETOURS_X64
{ 0x37, ENTRY_Invalid }, // Invalid
#else
{ 0x37, ENTRY_CopyBytes1 }, // AAA
#endif
{ 0x38, ENTRY_CopyBytes2Mod }, // CMP /r
{ 0x39, ENTRY_CopyBytes2Mod }, // CMP /r
{ 0x3A, ENTRY_CopyBytes2Mod }, // CMP /r
{ 0x3B, ENTRY_CopyBytes2Mod }, // CMP /r
{ 0x3C, ENTRY_CopyBytes2 }, // CMP ib
{ 0x3D, ENTRY_CopyBytes3Or5 }, // CMP id
{ 0x3E, ENTRY_CopyBytesSegment }, // DS prefix
#ifdef DETOURS_X64
{ 0x3F, ENTRY_Invalid }, // Invalid
#else
{ 0x3F, ENTRY_CopyBytes1 }, // AAS
#endif
#ifdef DETOURS_X64 // For Rax Prefix
{ 0x40, ENTRY_CopyBytesRax }, // Rax
{ 0x41, ENTRY_CopyBytesRax }, // Rax
{ 0x42, ENTRY_CopyBytesRax }, // Rax
{ 0x43, ENTRY_CopyBytesRax }, // Rax
{ 0x44, ENTRY_CopyBytesRax }, // Rax
{ 0x45, ENTRY_CopyBytesRax }, // Rax
{ 0x46, ENTRY_CopyBytesRax }, // Rax
{ 0x47, ENTRY_CopyBytesRax }, // Rax
{ 0x48, ENTRY_CopyBytesRax }, // Rax
{ 0x49, ENTRY_CopyBytesRax }, // Rax
{ 0x4A, ENTRY_CopyBytesRax }, // Rax
{ 0x4B, ENTRY_CopyBytesRax }, // Rax
{ 0x4C, ENTRY_CopyBytesRax }, // Rax
{ 0x4D, ENTRY_CopyBytesRax }, // Rax
{ 0x4E, ENTRY_CopyBytesRax }, // Rax
{ 0x4F, ENTRY_CopyBytesRax }, // Rax
#else
{ 0x40, ENTRY_CopyBytes1 }, // INC
{ 0x41, ENTRY_CopyBytes1 }, // INC
{ 0x42, ENTRY_CopyBytes1 }, // INC
{ 0x43, ENTRY_CopyBytes1 }, // INC
{ 0x44, ENTRY_CopyBytes1 }, // INC
{ 0x45, ENTRY_CopyBytes1 }, // INC
{ 0x46, ENTRY_CopyBytes1 }, // INC
{ 0x47, ENTRY_CopyBytes1 }, // INC
{ 0x48, ENTRY_CopyBytes1 }, // DEC
{ 0x49, ENTRY_CopyBytes1 }, // DEC
{ 0x4A, ENTRY_CopyBytes1 }, // DEC
{ 0x4B, ENTRY_CopyBytes1 }, // DEC
{ 0x4C, ENTRY_CopyBytes1 }, // DEC
{ 0x4D, ENTRY_CopyBytes1 }, // DEC
{ 0x4E, ENTRY_CopyBytes1 }, // DEC
{ 0x4F, ENTRY_CopyBytes1 }, // DEC
#endif
{ 0x50, ENTRY_CopyBytes1 }, // PUSH
{ 0x51, ENTRY_CopyBytes1 }, // PUSH
{ 0x52, ENTRY_CopyBytes1 }, // PUSH
{ 0x53, ENTRY_CopyBytes1 }, // PUSH
{ 0x54, ENTRY_CopyBytes1 }, // PUSH
{ 0x55, ENTRY_CopyBytes1 }, // PUSH
{ 0x56, ENTRY_CopyBytes1 }, // PUSH
{ 0x57, ENTRY_CopyBytes1 }, // PUSH
{ 0x58, ENTRY_CopyBytes1 }, // POP
{ 0x59, ENTRY_CopyBytes1 }, // POP
{ 0x5A, ENTRY_CopyBytes1 }, // POP
{ 0x5B, ENTRY_CopyBytes1 }, // POP
{ 0x5C, ENTRY_CopyBytes1 }, // POP
{ 0x5D, ENTRY_CopyBytes1 }, // POP
{ 0x5E, ENTRY_CopyBytes1 }, // POP
{ 0x5F, ENTRY_CopyBytes1 }, // POP
#ifdef DETOURS_X64
{ 0x60, ENTRY_Invalid }, // Invalid
{ 0x61, ENTRY_Invalid }, // Invalid
{ 0x62, ENTRY_Invalid }, // Invalid (not yet implemented Intel EVEX support)
#else
{ 0x60, ENTRY_CopyBytes1 }, // PUSHAD
{ 0x61, ENTRY_CopyBytes1 }, // POPAD
{ 0x62, ENTRY_CopyBytes2Mod }, // BOUND /r
#endif
{ 0x63, ENTRY_CopyBytes2Mod }, // 32bit ARPL /r, 64bit MOVSXD
{ 0x64, ENTRY_CopyBytesSegment }, // FS prefix
{ 0x65, ENTRY_CopyBytesSegment }, // GS prefix
{ 0x66, ENTRY_Copy66 }, // Operand Prefix
{ 0x67, ENTRY_Copy67 }, // Address Prefix
{ 0x68, ENTRY_CopyBytes3Or5 }, // PUSH
{ 0x69, ENTRY_CopyBytes2ModOperand }, // IMUL /r iz
{ 0x6A, ENTRY_CopyBytes2 }, // PUSH
{ 0x6B, ENTRY_CopyBytes2Mod1 }, // IMUL /r ib
{ 0x6C, ENTRY_CopyBytes1 }, // INS
{ 0x6D, ENTRY_CopyBytes1 }, // INS
{ 0x6E, ENTRY_CopyBytes1 }, // OUTS/OUTSB
{ 0x6F, ENTRY_CopyBytes1 }, // OUTS/OUTSW
{ 0x70, ENTRY_CopyBytes2Jump }, // JO // 0f80
{ 0x71, ENTRY_CopyBytes2Jump }, // JNO // 0f81
{ 0x72, ENTRY_CopyBytes2Jump }, // JB/JC/JNAE // 0f82
{ 0x73, ENTRY_CopyBytes2Jump }, // JAE/JNB/JNC // 0f83
{ 0x74, ENTRY_CopyBytes2Jump }, // JE/JZ // 0f84
{ 0x75, ENTRY_CopyBytes2Jump }, // JNE/JNZ // 0f85
{ 0x76, ENTRY_CopyBytes2Jump }, // JBE/JNA // 0f86
{ 0x77, ENTRY_CopyBytes2Jump }, // JA/JNBE // 0f87
{ 0x78, ENTRY_CopyBytes2Jump }, // JS // 0f88
{ 0x79, ENTRY_CopyBytes2Jump }, // JNS // 0f89
{ 0x7A, ENTRY_CopyBytes2Jump }, // JP/JPE // 0f8a
{ 0x7B, ENTRY_CopyBytes2Jump }, // JNP/JPO // 0f8b
{ 0x7C, ENTRY_CopyBytes2Jump }, // JL/JNGE // 0f8c
{ 0x7D, ENTRY_CopyBytes2Jump }, // JGE/JNL // 0f8d
{ 0x7E, ENTRY_CopyBytes2Jump }, // JLE/JNG // 0f8e
{ 0x7F, ENTRY_CopyBytes2Jump }, // JG/JNLE // 0f8f
{ 0x80, ENTRY_CopyBytes2Mod1 }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate byte
{ 0x81, ENTRY_CopyBytes2ModOperand }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate word or dword
#ifdef DETOURS_X64
{ 0x82, ENTRY_Invalid }, // Invalid
#else
{ 0x82, ENTRY_CopyBytes2Mod1 }, // MOV al,x
#endif
{ 0x83, ENTRY_CopyBytes2Mod1 }, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 reg, immediate byte
{ 0x84, ENTRY_CopyBytes2Mod }, // TEST /r
{ 0x85, ENTRY_CopyBytes2Mod }, // TEST /r
{ 0x86, ENTRY_CopyBytes2Mod }, // XCHG /r @todo
{ 0x87, ENTRY_CopyBytes2Mod }, // XCHG /r @todo
{ 0x88, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x89, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8A, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8B, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8C, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8D, ENTRY_CopyBytes2Mod }, // LEA /r
{ 0x8E, ENTRY_CopyBytes2Mod }, // MOV /r
{ 0x8F, ENTRY_CopyBytes2Mod }, // POP /0
{ 0x90, ENTRY_CopyBytes1 }, // NOP
{ 0x91, ENTRY_CopyBytes1 }, // XCHG
{ 0x92, ENTRY_CopyBytes1 }, // XCHG
{ 0x93, ENTRY_CopyBytes1 }, // XCHG
{ 0x94, ENTRY_CopyBytes1 }, // XCHG
{ 0x95, ENTRY_CopyBytes1 }, // XCHG
{ 0x96, ENTRY_CopyBytes1 }, // XCHG
{ 0x97, ENTRY_CopyBytes1 }, // XCHG
{ 0x98, ENTRY_CopyBytes1 }, // CWDE
{ 0x99, ENTRY_CopyBytes1 }, // CDQ
#ifdef DETOURS_X64
{ 0x9A, ENTRY_Invalid }, // Invalid
#else
{ 0x9A, ENTRY_CopyBytes5Or7Dynamic }, // CALL cp
#endif
{ 0x9B, ENTRY_CopyBytes1 }, // WAIT/FWAIT
{ 0x9C, ENTRY_CopyBytes1 }, // PUSHFD
{ 0x9D, ENTRY_CopyBytes1 }, // POPFD
{ 0x9E, ENTRY_CopyBytes1 }, // SAHF
{ 0x9F, ENTRY_CopyBytes1 }, // LAHF
{ 0xA0, ENTRY_CopyBytes1Address }, // MOV
{ 0xA1, ENTRY_CopyBytes1Address }, // MOV
{ 0xA2, ENTRY_CopyBytes1Address }, // MOV
{ 0xA3, ENTRY_CopyBytes1Address }, // MOV
{ 0xA4, ENTRY_CopyBytes1 }, // MOVS
{ 0xA5, ENTRY_CopyBytes1 }, // MOVS/MOVSD
{ 0xA6, ENTRY_CopyBytes1 }, // CMPS/CMPSB
{ 0xA7, ENTRY_CopyBytes1 }, // CMPS/CMPSW
{ 0xA8, ENTRY_CopyBytes2 }, // TEST
{ 0xA9, ENTRY_CopyBytes3Or5 }, // TEST
{ 0xAA, ENTRY_CopyBytes1 }, // STOS/STOSB
{ 0xAB, ENTRY_CopyBytes1 }, // STOS/STOSW
{ 0xAC, ENTRY_CopyBytes1 }, // LODS/LODSB
{ 0xAD, ENTRY_CopyBytes1 }, // LODS/LODSW
{ 0xAE, ENTRY_CopyBytes1 }, // SCAS/SCASB
{ 0xAF, ENTRY_CopyBytes1 }, // SCAS/SCASD
{ 0xB0, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB1, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB2, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB3, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB4, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB5, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB6, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB7, ENTRY_CopyBytes2 }, // MOV B0+rb
{ 0xB8, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xB9, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBA, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBB, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBC, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBD, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBE, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xBF, ENTRY_CopyBytes3Or5Rax }, // MOV B8+rb
{ 0xC0, ENTRY_CopyBytes2Mod1 }, // RCL/2 ib, etc.
{ 0xC1, ENTRY_CopyBytes2Mod1 }, // RCL/2 ib, etc.
{ 0xC2, ENTRY_CopyBytes3 }, // RET
{ 0xC3, ENTRY_CopyBytes1 }, // RET
{ 0xC4, ENTRY_CopyVex3 }, // LES, VEX 3-byte opcodes.
{ 0xC5, ENTRY_CopyVex2 }, // LDS, VEX 2-byte opcodes.
{ 0xC6, ENTRY_CopyBytes2Mod1 }, // MOV
{ 0xC7, ENTRY_CopyBytes2ModOperand }, // MOV/0 XBEGIN/7
{ 0xC8, ENTRY_CopyBytes4 }, // ENTER
{ 0xC9, ENTRY_CopyBytes1 }, // LEAVE
{ 0xCA, ENTRY_CopyBytes3Dynamic }, // RET
{ 0xCB, ENTRY_CopyBytes1Dynamic }, // RET
{ 0xCC, ENTRY_CopyBytes1Dynamic }, // INT 3
{ 0xCD, ENTRY_CopyBytes2Dynamic }, // INT ib
#ifdef DETOURS_X64
{ 0xCE, ENTRY_Invalid }, // Invalid
#else
{ 0xCE, ENTRY_CopyBytes1Dynamic }, // INTO
#endif
{ 0xCF, ENTRY_CopyBytes1Dynamic }, // IRET
{ 0xD0, ENTRY_CopyBytes2Mod }, // RCL/2, etc.
{ 0xD1, ENTRY_CopyBytes2Mod }, // RCL/2, etc.
{ 0xD2, ENTRY_CopyBytes2Mod }, // RCL/2, etc.
{ 0xD3, ENTRY_CopyBytes2Mod }, // RCL/2, etc.
#ifdef DETOURS_X64
{ 0xD4, ENTRY_Invalid }, // Invalid
{ 0xD5, ENTRY_Invalid }, // Invalid
#else
{ 0xD4, ENTRY_CopyBytes2 }, // AAM
{ 0xD5, ENTRY_CopyBytes2 }, // AAD
#endif
{ 0xD6, ENTRY_Invalid }, // Invalid
{ 0xD7, ENTRY_CopyBytes1 }, // XLAT/XLATB
{ 0xD8, ENTRY_CopyBytes2Mod }, // FADD, etc.
{ 0xD9, ENTRY_CopyBytes2Mod }, // F2XM1, etc.
{ 0xDA, ENTRY_CopyBytes2Mod }, // FLADD, etc.
{ 0xDB, ENTRY_CopyBytes2Mod }, // FCLEX, etc.
{ 0xDC, ENTRY_CopyBytes2Mod }, // FADD/0, etc.
{ 0xDD, ENTRY_CopyBytes2Mod }, // FFREE, etc.
{ 0xDE, ENTRY_CopyBytes2Mod }, // FADDP, etc.
{ 0xDF, ENTRY_CopyBytes2Mod }, // FBLD/4, etc.
{ 0xE0, ENTRY_CopyBytes2CantJump }, // LOOPNE cb
{ 0xE1, ENTRY_CopyBytes2CantJump }, // LOOPE cb
{ 0xE2, ENTRY_CopyBytes2CantJump }, // LOOP cb
{ 0xE3, ENTRY_CopyBytes2CantJump }, // JCXZ/JECXZ
{ 0xE4, ENTRY_CopyBytes2 }, // IN ib
{ 0xE5, ENTRY_CopyBytes2 }, // IN id
{ 0xE6, ENTRY_CopyBytes2 }, // OUT ib
{ 0xE7, ENTRY_CopyBytes2 }, // OUT ib
{ 0xE8, ENTRY_CopyBytesJumpToAbsolute }, // CALL cd
{ 0xE9, ENTRY_CopyBytesJumpToAbsolute }, // JMP cd
#ifdef DETOURS_X64
{ 0xEA, ENTRY_Invalid }, // Invalid
#else
{ 0xEA, ENTRY_CopyBytes5Or7Dynamic }, // JMP cp
#endif
{ 0xEB, ENTRY_CopyBytes2Jump }, // JMP cb
{ 0xEC, ENTRY_CopyBytes1 }, // IN ib
{ 0xED, ENTRY_CopyBytes1 }, // IN id
{ 0xEE, ENTRY_CopyBytes1 }, // OUT
{ 0xEF, ENTRY_CopyBytes1 }, // OUT
{ 0xF0, ENTRY_CopyBytesPrefix }, // LOCK prefix
{ 0xF1, ENTRY_CopyBytes1Dynamic }, // INT1 / ICEBP somewhat documented by AMD, not by Intel
{ 0xF2, ENTRY_CopyF2 }, // REPNE prefix
//#ifdef DETOURS_X86
{ 0xF3, ENTRY_CopyF3 }, // REPE prefix
//#else
// This does presently suffice for AMD64 but it requires tracing
// through a bunch of code to verify and seems not worth maintaining.
// { 0xF3, ENTRY_CopyBytesPrefix }, // REPE prefix
//#endif
{ 0xF4, ENTRY_CopyBytes1 }, // HLT
{ 0xF5, ENTRY_CopyBytes1 }, // CMC
{ 0xF6, ENTRY_CopyF6 }, // TEST/0, DIV/6
{ 0xF7, ENTRY_CopyF7 }, // TEST/0, DIV/6
{ 0xF8, ENTRY_CopyBytes1 }, // CLC
{ 0xF9, ENTRY_CopyBytes1 }, // STC
{ 0xFA, ENTRY_CopyBytes1 }, // CLI
{ 0xFB, ENTRY_CopyBytes1 }, // STI
{ 0xFC, ENTRY_CopyBytes1 }, // CLD
{ 0xFD, ENTRY_CopyBytes1 }, // STD
{ 0xFE, ENTRY_CopyBytes2Mod }, // DEC/1,INC/0
{ 0xFF, ENTRY_CopyFF }, // CALL/2
{ 0, ENTRY_End },
};
const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable0F[257] =
{
#ifdef DETOURS_X86
{ 0x00, ENTRY_Copy0F00 }, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7
#else
{ 0x00, ENTRY_CopyBytes2Mod }, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7
#endif
{ 0x01, ENTRY_CopyBytes2Mod }, // INVLPG/7, etc.
{ 0x02, ENTRY_CopyBytes2Mod }, // LAR/r
{ 0x03, ENTRY_CopyBytes2Mod }, // LSL/r
{ 0x04, ENTRY_Invalid }, // _04
{ 0x05, ENTRY_CopyBytes1 }, // SYSCALL
{ 0x06, ENTRY_CopyBytes1 }, // CLTS
{ 0x07, ENTRY_CopyBytes1 }, // SYSRET
{ 0x08, ENTRY_CopyBytes1 }, // INVD
{ 0x09, ENTRY_CopyBytes1 }, // WBINVD
{ 0x0A, ENTRY_Invalid }, // _0A
{ 0x0B, ENTRY_CopyBytes1 }, // UD2
{ 0x0C, ENTRY_Invalid }, // _0C
{ 0x0D, ENTRY_CopyBytes2Mod }, // PREFETCH
{ 0x0E, ENTRY_CopyBytes1 }, // FEMMS (3DNow -- not in Intel documentation)
{ 0x0F, ENTRY_CopyBytes2Mod1 }, // 3DNow Opcodes
{ 0x10, ENTRY_CopyBytes2Mod }, // MOVSS MOVUPD MOVSD
{ 0x11, ENTRY_CopyBytes2Mod }, // MOVSS MOVUPD MOVSD
{ 0x12, ENTRY_CopyBytes2Mod }, // MOVLPD
{ 0x13, ENTRY_CopyBytes2Mod }, // MOVLPD
{ 0x14, ENTRY_CopyBytes2Mod }, // UNPCKLPD
{ 0x15, ENTRY_CopyBytes2Mod }, // UNPCKHPD
{ 0x16, ENTRY_CopyBytes2Mod }, // MOVHPD
{ 0x17, ENTRY_CopyBytes2Mod }, // MOVHPD
{ 0x18, ENTRY_CopyBytes2Mod }, // PREFETCHINTA...
{ 0x19, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1A, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1B, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1C, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1D, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1E, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop, not documented by Intel, documented by AMD
{ 0x1F, ENTRY_CopyBytes2Mod }, // NOP/r multi byte nop
{ 0x20, ENTRY_CopyBytes2Mod }, // MOV/r
{ 0x21, ENTRY_CopyBytes2Mod }, // MOV/r
{ 0x22, ENTRY_CopyBytes2Mod }, // MOV/r
{ 0x23, ENTRY_CopyBytes2Mod }, // MOV/r
#ifdef DETOURS_X64
{ 0x24, ENTRY_Invalid }, // _24
#else
{ 0x24, ENTRY_CopyBytes2Mod }, // MOV/r,TR TR is test register on 80386 and 80486, removed in Pentium
#endif
{ 0x25, ENTRY_Invalid }, // _25
#ifdef DETOURS_X64
{ 0x26, ENTRY_Invalid }, // _26
#else
{ 0x26, ENTRY_CopyBytes2Mod }, // MOV TR/r TR is test register on 80386 and 80486, removed in Pentium
#endif
{ 0x27, ENTRY_Invalid }, // _27
{ 0x28, ENTRY_CopyBytes2Mod }, // MOVAPS MOVAPD
{ 0x29, ENTRY_CopyBytes2Mod }, // MOVAPS MOVAPD
{ 0x2A, ENTRY_CopyBytes2Mod }, // CVPI2PS &
{ 0x2B, ENTRY_CopyBytes2Mod }, // MOVNTPS MOVNTPD
{ 0x2C, ENTRY_CopyBytes2Mod }, // CVTTPS2PI &
{ 0x2D, ENTRY_CopyBytes2Mod }, // CVTPS2PI &
{ 0x2E, ENTRY_CopyBytes2Mod }, // UCOMISS UCOMISD
{ 0x2F, ENTRY_CopyBytes2Mod }, // COMISS COMISD
{ 0x30, ENTRY_CopyBytes1 }, // WRMSR
{ 0x31, ENTRY_CopyBytes1 }, // RDTSC
{ 0x32, ENTRY_CopyBytes1 }, // RDMSR
{ 0x33, ENTRY_CopyBytes1 }, // RDPMC
{ 0x34, ENTRY_CopyBytes1 }, // SYSENTER
{ 0x35, ENTRY_CopyBytes1 }, // SYSEXIT
{ 0x36, ENTRY_Invalid }, // _36
{ 0x37, ENTRY_CopyBytes1 }, // GETSEC
{ 0x38, ENTRY_CopyBytes3Mod }, // SSE3 Opcodes
{ 0x39, ENTRY_Invalid }, // _39
{ 0x3A, ENTRY_CopyBytes3Mod1 }, // SSE3 Opcodes
{ 0x3B, ENTRY_Invalid }, // _3B
{ 0x3C, ENTRY_Invalid }, // _3C
{ 0x3D, ENTRY_Invalid }, // _3D
{ 0x3E, ENTRY_Invalid }, // _3E
{ 0x3F, ENTRY_Invalid }, // _3F
{ 0x40, ENTRY_CopyBytes2Mod }, // CMOVO (0F 40)
{ 0x41, ENTRY_CopyBytes2Mod }, // CMOVNO (0F 41)
{ 0x42, ENTRY_CopyBytes2Mod }, // CMOVB & CMOVNE (0F 42)
{ 0x43, ENTRY_CopyBytes2Mod }, // CMOVAE & CMOVNB (0F 43)
{ 0x44, ENTRY_CopyBytes2Mod }, // CMOVE & CMOVZ (0F 44)
{ 0x45, ENTRY_CopyBytes2Mod }, // CMOVNE & CMOVNZ (0F 45)
{ 0x46, ENTRY_CopyBytes2Mod }, // CMOVBE & CMOVNA (0F 46)
{ 0x47, ENTRY_CopyBytes2Mod }, // CMOVA & CMOVNBE (0F 47)
{ 0x48, ENTRY_CopyBytes2Mod }, // CMOVS (0F 48)
{ 0x49, ENTRY_CopyBytes2Mod }, // CMOVNS (0F 49)
{ 0x4A, ENTRY_CopyBytes2Mod }, // CMOVP & CMOVPE (0F 4A)
{ 0x4B, ENTRY_CopyBytes2Mod }, // CMOVNP & CMOVPO (0F 4B)
{ 0x4C, ENTRY_CopyBytes2Mod }, // CMOVL & CMOVNGE (0F 4C)
{ 0x4D, ENTRY_CopyBytes2Mod }, // CMOVGE & CMOVNL (0F 4D)
{ 0x4E, ENTRY_CopyBytes2Mod }, // CMOVLE & CMOVNG (0F 4E)
{ 0x4F, ENTRY_CopyBytes2Mod }, // CMOVG & CMOVNLE (0F 4F)
{ 0x50, ENTRY_CopyBytes2Mod }, // MOVMSKPD MOVMSKPD
{ 0x51, ENTRY_CopyBytes2Mod }, // SQRTPS &
{ 0x52, ENTRY_CopyBytes2Mod }, // RSQRTTS RSQRTPS
{ 0x53, ENTRY_CopyBytes2Mod }, // RCPPS RCPSS
{ 0x54, ENTRY_CopyBytes2Mod }, // ANDPS ANDPD
{ 0x55, ENTRY_CopyBytes2Mod }, // ANDNPS ANDNPD
{ 0x56, ENTRY_CopyBytes2Mod }, // ORPS ORPD
{ 0x57, ENTRY_CopyBytes2Mod }, // XORPS XORPD
{ 0x58, ENTRY_CopyBytes2Mod }, // ADDPS &
{ 0x59, ENTRY_CopyBytes2Mod }, // MULPS &
{ 0x5A, ENTRY_CopyBytes2Mod }, // CVTPS2PD &
{ 0x5B, ENTRY_CopyBytes2Mod }, // CVTDQ2PS &
{ 0x5C, ENTRY_CopyBytes2Mod }, // SUBPS &
{ 0x5D, ENTRY_CopyBytes2Mod }, // MINPS &
{ 0x5E, ENTRY_CopyBytes2Mod }, // DIVPS &
{ 0x5F, ENTRY_CopyBytes2Mod }, // MASPS &
{ 0x60, ENTRY_CopyBytes2Mod }, // PUNPCKLBW/r
{ 0x61, ENTRY_CopyBytes2Mod }, // PUNPCKLWD/r
{ 0x62, ENTRY_CopyBytes2Mod }, // PUNPCKLWD/r
{ 0x63, ENTRY_CopyBytes2Mod }, // PACKSSWB/r
{ 0x64, ENTRY_CopyBytes2Mod }, // PCMPGTB/r
{ 0x65, ENTRY_CopyBytes2Mod }, // PCMPGTW/r
{ 0x66, ENTRY_CopyBytes2Mod }, // PCMPGTD/r
{ 0x67, ENTRY_CopyBytes2Mod }, // PACKUSWB/r
{ 0x68, ENTRY_CopyBytes2Mod }, // PUNPCKHBW/r
{ 0x69, ENTRY_CopyBytes2Mod }, // PUNPCKHWD/r
{ 0x6A, ENTRY_CopyBytes2Mod }, // PUNPCKHDQ/r
{ 0x6B, ENTRY_CopyBytes2Mod }, // PACKSSDW/r
{ 0x6C, ENTRY_CopyBytes2Mod }, // PUNPCKLQDQ
{ 0x6D, ENTRY_CopyBytes2Mod }, // PUNPCKHQDQ
{ 0x6E, ENTRY_CopyBytes2Mod }, // MOVD/r
{ 0x6F, ENTRY_CopyBytes2Mod }, // MOV/r
{ 0x70, ENTRY_CopyBytes2Mod1 }, // PSHUFW/r ib
{ 0x71, ENTRY_CopyBytes2Mod1 }, // PSLLW/6 ib,PSRAW/4 ib,PSRLW/2 ib
{ 0x72, ENTRY_CopyBytes2Mod1 }, // PSLLD/6 ib,PSRAD/4 ib,PSRLD/2 ib
{ 0x73, ENTRY_CopyBytes2Mod1 }, // PSLLQ/6 ib,PSRLQ/2 ib
{ 0x74, ENTRY_CopyBytes2Mod }, // PCMPEQB/r
{ 0x75, ENTRY_CopyBytes2Mod }, // PCMPEQW/r
{ 0x76, ENTRY_CopyBytes2Mod }, // PCMPEQD/r
{ 0x77, ENTRY_CopyBytes1 }, // EMMS
// extrq/insertq require mode=3 and are followed by two immediate bytes
{ 0x78, ENTRY_Copy0F78 }, // VMREAD/r, 66/EXTRQ/r/ib/ib, F2/INSERTQ/r/ib/ib
// extrq/insertq require mod=3, therefore ENTRY_CopyBytes2, but it ends up the same
{ 0x79, ENTRY_CopyBytes2Mod }, // VMWRITE/r, 66/EXTRQ/r, F2/INSERTQ/r
{ 0x7A, ENTRY_Invalid }, // _7A
{ 0x7B, ENTRY_Invalid }, // _7B
{ 0x7C, ENTRY_CopyBytes2Mod }, // HADDPS
{ 0x7D, ENTRY_CopyBytes2Mod }, // HSUBPS
{ 0x7E, ENTRY_CopyBytes2Mod }, // MOVD/r
{ 0x7F, ENTRY_CopyBytes2Mod }, // MOV/r
{ 0x80, ENTRY_CopyBytes3Or5Target }, // JO
{ 0x81, ENTRY_CopyBytes3Or5Target }, // JNO
{ 0x82, ENTRY_CopyBytes3Or5Target }, // JB,JC,JNAE
{ 0x83, ENTRY_CopyBytes3Or5Target }, // JAE,JNB,JNC
{ 0x84, ENTRY_CopyBytes3Or5Target }, // JE,JZ,JZ
{ 0x85, ENTRY_CopyBytes3Or5Target }, // JNE,JNZ
{ 0x86, ENTRY_CopyBytes3Or5Target }, // JBE,JNA
{ 0x87, ENTRY_CopyBytes3Or5Target }, // JA,JNBE
{ 0x88, ENTRY_CopyBytes3Or5Target }, // JS
{ 0x89, ENTRY_CopyBytes3Or5Target }, // JNS
{ 0x8A, ENTRY_CopyBytes3Or5Target }, // JP,JPE
{ 0x8B, ENTRY_CopyBytes3Or5Target }, // JNP,JPO
{ 0x8C, ENTRY_CopyBytes3Or5Target }, // JL,NGE
{ 0x8D, ENTRY_CopyBytes3Or5Target }, // JGE,JNL
{ 0x8E, ENTRY_CopyBytes3Or5Target }, // JLE,JNG
{ 0x8F, ENTRY_CopyBytes3Or5Target }, // JG,JNLE
{ 0x90, ENTRY_CopyBytes2Mod }, // CMOVO (0F 40)
{ 0x91, ENTRY_CopyBytes2Mod }, // CMOVNO (0F 41)
{ 0x92, ENTRY_CopyBytes2Mod }, // CMOVB & CMOVC & CMOVNAE (0F 42)
{ 0x93, ENTRY_CopyBytes2Mod }, // CMOVAE & CMOVNB & CMOVNC (0F 43)
{ 0x94, ENTRY_CopyBytes2Mod }, // CMOVE & CMOVZ (0F 44)
{ 0x95, ENTRY_CopyBytes2Mod }, // CMOVNE & CMOVNZ (0F 45)
{ 0x96, ENTRY_CopyBytes2Mod }, // CMOVBE & CMOVNA (0F 46)
{ 0x97, ENTRY_CopyBytes2Mod }, // CMOVA & CMOVNBE (0F 47)
{ 0x98, ENTRY_CopyBytes2Mod }, // CMOVS (0F 48)
{ 0x99, ENTRY_CopyBytes2Mod }, // CMOVNS (0F 49)
{ 0x9A, ENTRY_CopyBytes2Mod }, // CMOVP & CMOVPE (0F 4A)
{ 0x9B, ENTRY_CopyBytes2Mod }, // CMOVNP & CMOVPO (0F 4B)
{ 0x9C, ENTRY_CopyBytes2Mod }, // CMOVL & CMOVNGE (0F 4C)
{ 0x9D, ENTRY_CopyBytes2Mod }, // CMOVGE & CMOVNL (0F 4D)
{ 0x9E, ENTRY_CopyBytes2Mod }, // CMOVLE & CMOVNG (0F 4E)
{ 0x9F, ENTRY_CopyBytes2Mod }, // CMOVG & CMOVNLE (0F 4F)
{ 0xA0, ENTRY_CopyBytes1 }, // PUSH
{ 0xA1, ENTRY_CopyBytes1 }, // POP
{ 0xA2, ENTRY_CopyBytes1 }, // CPUID
{ 0xA3, ENTRY_CopyBytes2Mod }, // BT (0F A3)
{ 0xA4, ENTRY_CopyBytes2Mod1 }, // SHLD
{ 0xA5, ENTRY_CopyBytes2Mod }, // SHLD
{ 0xA6, ENTRY_CopyBytes2Mod }, // XBTS
{ 0xA7, ENTRY_CopyBytes2Mod }, // IBTS
{ 0xA8, ENTRY_CopyBytes1 }, // PUSH
{ 0xA9, ENTRY_CopyBytes1 }, // POP
{ 0xAA, ENTRY_CopyBytes1 }, // RSM
{ 0xAB, ENTRY_CopyBytes2Mod }, // BTS (0F AB)
{ 0xAC, ENTRY_CopyBytes2Mod1 }, // SHRD
{ 0xAD, ENTRY_CopyBytes2Mod }, // SHRD
// 0F AE mod76=mem mod543=0 fxsave
// 0F AE mod76=mem mod543=1 fxrstor
// 0F AE mod76=mem mod543=2 ldmxcsr
// 0F AE mod76=mem mod543=3 stmxcsr
// 0F AE mod76=mem mod543=4 xsave
// 0F AE mod76=mem mod543=5 xrstor
// 0F AE mod76=mem mod543=6 saveopt
// 0F AE mod76=mem mod543=7 clflush
// 0F AE mod76=11b mod543=5 lfence
// 0F AE mod76=11b mod543=6 mfence
// 0F AE mod76=11b mod543=7 sfence
// F3 0F AE mod76=11b mod543=0 rdfsbase
// F3 0F AE mod76=11b mod543=1 rdgsbase
// F3 0F AE mod76=11b mod543=2 wrfsbase
// F3 0F AE mod76=11b mod543=3 wrgsbase
{ 0xAE, ENTRY_CopyBytes2Mod }, // fxsave fxrstor ldmxcsr stmxcsr xsave xrstor saveopt clflush lfence mfence sfence rdfsbase rdgsbase wrfsbase wrgsbase
{ 0xAF, ENTRY_CopyBytes2Mod }, // IMUL (0F AF)
{ 0xB0, ENTRY_CopyBytes2Mod }, // CMPXCHG (0F B0)
{ 0xB1, ENTRY_CopyBytes2Mod }, // CMPXCHG (0F B1)
{ 0xB2, ENTRY_CopyBytes2Mod }, // LSS/r
{ 0xB3, ENTRY_CopyBytes2Mod }, // BTR (0F B3)
{ 0xB4, ENTRY_CopyBytes2Mod }, // LFS/r
{ 0xB5, ENTRY_CopyBytes2Mod }, // LGS/r
{ 0xB6, ENTRY_CopyBytes2Mod }, // MOVZX/r
{ 0xB7, ENTRY_CopyBytes2Mod }, // MOVZX/r
#ifdef DETOURS_X86
{ 0xB8, ENTRY_Copy0FB8 }, // jmpe f3/popcnt
#else
{ 0xB8, ENTRY_CopyBytes2Mod }, // f3/popcnt
#endif
{ 0xB9, ENTRY_Invalid }, // _B9
{ 0xBA, ENTRY_CopyBytes2Mod1 }, // BT & BTC & BTR & BTS (0F BA)
{ 0xBB, ENTRY_CopyBytes2Mod }, // BTC (0F BB)
{ 0xBC, ENTRY_CopyBytes2Mod }, // BSF (0F BC)
{ 0xBD, ENTRY_CopyBytes2Mod }, // BSR (0F BD)
{ 0xBE, ENTRY_CopyBytes2Mod }, // MOVSX/r
{ 0xBF, ENTRY_CopyBytes2Mod }, // MOVSX/r
{ 0xC0, ENTRY_CopyBytes2Mod }, // XADD/r
{ 0xC1, ENTRY_CopyBytes2Mod }, // XADD/r
{ 0xC2, ENTRY_CopyBytes2Mod1 }, // CMPPS &
{ 0xC3, ENTRY_CopyBytes2Mod }, // MOVNTI
{ 0xC4, ENTRY_CopyBytes2Mod1 }, // PINSRW /r ib
{ 0xC5, ENTRY_CopyBytes2Mod1 }, // PEXTRW /r ib
{ 0xC6, ENTRY_CopyBytes2Mod1 }, // SHUFPS & SHUFPD
{ 0xC7, ENTRY_CopyBytes2Mod }, // CMPXCHG8B (0F C7)
{ 0xC8, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xC9, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xCA, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xCB, ENTRY_CopyBytes1 }, // CVTPD2PI BSWAP 0F C8 + rd
{ 0xCC, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xCD, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xCE, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xCF, ENTRY_CopyBytes1 }, // BSWAP 0F C8 + rd
{ 0xD0, ENTRY_CopyBytes2Mod }, // ADDSUBPS (untestd)
{ 0xD1, ENTRY_CopyBytes2Mod }, // PSRLW/r
{ 0xD2, ENTRY_CopyBytes2Mod }, // PSRLD/r
{ 0xD3, ENTRY_CopyBytes2Mod }, // PSRLQ/r
{ 0xD4, ENTRY_CopyBytes2Mod }, // PADDQ
{ 0xD5, ENTRY_CopyBytes2Mod }, // PMULLW/r
{ 0xD6, ENTRY_CopyBytes2Mod }, // MOVDQ2Q / MOVQ2DQ
{ 0xD7, ENTRY_CopyBytes2Mod }, // PMOVMSKB/r
{ 0xD8, ENTRY_CopyBytes2Mod }, // PSUBUSB/r
{ 0xD9, ENTRY_CopyBytes2Mod }, // PSUBUSW/r
{ 0xDA, ENTRY_CopyBytes2Mod }, // PMINUB/r
{ 0xDB, ENTRY_CopyBytes2Mod }, // PAND/r
{ 0xDC, ENTRY_CopyBytes2Mod }, // PADDUSB/r
{ 0xDD, ENTRY_CopyBytes2Mod }, // PADDUSW/r
{ 0xDE, ENTRY_CopyBytes2Mod }, // PMAXUB/r
{ 0xDF, ENTRY_CopyBytes2Mod }, // PANDN/r
{ 0xE0, ENTRY_CopyBytes2Mod }, // PAVGB
{ 0xE1, ENTRY_CopyBytes2Mod }, // PSRAW/r
{ 0xE2, ENTRY_CopyBytes2Mod }, // PSRAD/r
{ 0xE3, ENTRY_CopyBytes2Mod }, // PAVGW
{ 0xE4, ENTRY_CopyBytes2Mod }, // PMULHUW/r
{ 0xE5, ENTRY_CopyBytes2Mod }, // PMULHW/r
{ 0xE6, ENTRY_CopyBytes2Mod }, // CTDQ2PD &
{ 0xE7, ENTRY_CopyBytes2Mod }, // MOVNTQ
{ 0xE8, ENTRY_CopyBytes2Mod }, // PSUBB/r
{ 0xE9, ENTRY_CopyBytes2Mod }, // PSUBW/r
{ 0xEA, ENTRY_CopyBytes2Mod }, // PMINSW/r
{ 0xEB, ENTRY_CopyBytes2Mod }, // POR/r
{ 0xEC, ENTRY_CopyBytes2Mod }, // PADDSB/r
{ 0xED, ENTRY_CopyBytes2Mod }, // PADDSW/r
{ 0xEE, ENTRY_CopyBytes2Mod }, // PMAXSW /r
{ 0xEF, ENTRY_CopyBytes2Mod }, // PXOR/r
{ 0xF0, ENTRY_CopyBytes2Mod }, // LDDQU
{ 0xF1, ENTRY_CopyBytes2Mod }, // PSLLW/r
{ 0xF2, ENTRY_CopyBytes2Mod }, // PSLLD/r
{ 0xF3, ENTRY_CopyBytes2Mod }, // PSLLQ/r
{ 0xF4, ENTRY_CopyBytes2Mod }, // PMULUDQ/r
{ 0xF5, ENTRY_CopyBytes2Mod }, // PMADDWD/r
{ 0xF6, ENTRY_CopyBytes2Mod }, // PSADBW/r
{ 0xF7, ENTRY_CopyBytes2Mod }, // MASKMOVQ
{ 0xF8, ENTRY_CopyBytes2Mod }, // PSUBB/r
{ 0xF9, ENTRY_CopyBytes2Mod }, // PSUBW/r
{ 0xFA, ENTRY_CopyBytes2Mod }, // PSUBD/r
{ 0xFB, ENTRY_CopyBytes2Mod }, // FSUBQ/r
{ 0xFC, ENTRY_CopyBytes2Mod }, // PADDB/r
{ 0xFD, ENTRY_CopyBytes2Mod }, // PADDW/r
{ 0xFE, ENTRY_CopyBytes2Mod }, // PADDD/r
{ 0xFF, ENTRY_Invalid }, // _FF
{ 0, ENTRY_End },
};
BOOL CDetourDis::SanityCheckSystem()
{
ULONG n = 0;
for (; n < 256; n++) {
REFCOPYENTRY pEntry = &s_rceCopyTable[n];
if (n != pEntry->nOpcode) {
ASSERT(n == pEntry->nOpcode);
return FALSE;
}
}
if (s_rceCopyTable[256].pfCopy != NULL) {
ASSERT(!"Missing end marker.");
return FALSE;
}
for (n = 0; n < 256; n++) {
REFCOPYENTRY pEntry = &s_rceCopyTable0F[n];
if (n != pEntry->nOpcode) {
ASSERT(n == pEntry->nOpcode);
return FALSE;
}
}
if (s_rceCopyTable0F[256].pfCopy != NULL) {
ASSERT(!"Missing end marker.");
return FALSE;
}
return TRUE;
}
#endif // defined(DETOURS_X64) || defined(DETOURS_X86)
/////////////////////////////////////////////////////////// IA64 Disassembler.
//
#ifdef DETOURS_IA64
#if defined(_IA64_) != defined(DETOURS_IA64_OFFLINE_LIBRARY)
// Compile DETOUR_IA64_BUNDLE for native IA64 or cross, but not both -- we get duplicates otherwise.
const DETOUR_IA64_BUNDLE::DETOUR_IA64_METADATA DETOUR_IA64_BUNDLE::s_rceCopyTable[33] =
{
{ 0x00, M_UNIT, I_UNIT, I_UNIT, },
{ 0x01, M_UNIT, I_UNIT, I_UNIT, },
{ 0x02, M_UNIT, I_UNIT, I_UNIT, },
{ 0x03, M_UNIT, I_UNIT, I_UNIT, },
{ 0x04, M_UNIT, L_UNIT, X_UNIT, },
{ 0x05, M_UNIT, L_UNIT, X_UNIT, },
{ 0x06, 0, 0, 0, },
{ 0x07, 0, 0, 0, },
{ 0x08, M_UNIT, M_UNIT, I_UNIT, },
{ 0x09, M_UNIT, M_UNIT, I_UNIT, },
{ 0x0a, M_UNIT, M_UNIT, I_UNIT, },
{ 0x0b, M_UNIT, M_UNIT, I_UNIT, },
{ 0x0c, M_UNIT, F_UNIT, I_UNIT, },
{ 0x0d, M_UNIT, F_UNIT, I_UNIT, },
{ 0x0e, M_UNIT, M_UNIT, F_UNIT, },
{ 0x0f, M_UNIT, M_UNIT, F_UNIT, },
{ 0x10, M_UNIT, I_UNIT, B_UNIT, },
{ 0x11, M_UNIT, I_UNIT, B_UNIT, },
{ 0x12, M_UNIT, B_UNIT, B_UNIT, },
{ 0x13, M_UNIT, B_UNIT, B_UNIT, },
{ 0x14, 0, 0, 0, },
{ 0x15, 0, 0, 0, },
{ 0x16, B_UNIT, B_UNIT, B_UNIT, },
{ 0x17, B_UNIT, B_UNIT, B_UNIT, },
{ 0x18, M_UNIT, M_UNIT, B_UNIT, },
{ 0x19, M_UNIT, M_UNIT, B_UNIT, },
{ 0x1a, 0, 0, 0, },
{ 0x1b, 0, 0, 0, },
{ 0x1c, M_UNIT, F_UNIT, B_UNIT, },
{ 0x1d, M_UNIT, F_UNIT, B_UNIT, },
{ 0x1e, 0, 0, 0, },
{ 0x1f, 0, 0, 0, },
{ 0x00, 0, 0, 0, },
};
// 120 112 104 96 88 80 72 64 56 48 40 32 24 16 8 0
// f. e. d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.
// 00
// f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.
// 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0]
// 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41.. 5]
// 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42]
// 0000 0000 0007 ffff ffff c000 0000 0000 : One [ 82.. 46]
// 0000 0000 0078 0000 0000 0000 0000 0000 : One [ 86.. 83]
// 0fff ffff ff80 0000 0000 0000 0000 0000 : Two [123.. 87]
// f000 0000 0000 0000 0000 0000 0000 0000 : Two [127..124]
BYTE DETOUR_IA64_BUNDLE::GetTemplate() const
{
return (data[0] & 0x1f);
}
BYTE DETOUR_IA64_BUNDLE::GetInst0() const
{
return ((data[5] & 0x3c) >> 2);
}
BYTE DETOUR_IA64_BUNDLE::GetInst1() const
{
return ((data[10] & 0x78) >> 3);
}
BYTE DETOUR_IA64_BUNDLE::GetInst2() const
{
return ((data[15] & 0xf0) >> 4);
}
BYTE DETOUR_IA64_BUNDLE::GetUnit(BYTE slot) const
{
switch (slot) {
case 0: return GetUnit0();
case 1: return GetUnit1();
case 2: return GetUnit2();
}
__debugbreak();
return 0;
}
BYTE DETOUR_IA64_BUNDLE::GetUnit0() const
{
return s_rceCopyTable[data[0] & 0x1f].nUnit0;
}
BYTE DETOUR_IA64_BUNDLE::GetUnit1() const
{
return s_rceCopyTable[data[0] & 0x1f].nUnit1;
}
BYTE DETOUR_IA64_BUNDLE::GetUnit2() const
{
return s_rceCopyTable[data[0] & 0x1f].nUnit2;
}
UINT64 DETOUR_IA64_BUNDLE::GetData0() const
{
return (((wide[0] & 0x000003ffffffffe0) >> 5));
}
UINT64 DETOUR_IA64_BUNDLE::GetData1() const
{
return (((wide[0] & 0xffffc00000000000) >> 46) |
((wide[1] & 0x000000000007ffff) << 18));
}
UINT64 DETOUR_IA64_BUNDLE::GetData2() const
{
return (((wide[1] & 0x0fffffffff800000) >> 23));
}
VOID DETOUR_IA64_BUNDLE::SetInst(BYTE slot, BYTE nInst)
{
switch (slot)
{
case 0: SetInst0(nInst); return;
case 1: SetInst1(nInst); return;
case 2: SetInst2(nInst); return;
}
__debugbreak();
}
VOID DETOUR_IA64_BUNDLE::SetInst0(BYTE nInst)
{
data[5] = (data[5] & ~0x3c) | ((nInst << 2) & 0x3c);
}
VOID DETOUR_IA64_BUNDLE::SetInst1(BYTE nInst)
{
data[10] = (data[10] & ~0x78) | ((nInst << 3) & 0x78);
}
VOID DETOUR_IA64_BUNDLE::SetInst2(BYTE nInst)
{
data[15] = (data[15] & ~0xf0) | ((nInst << 4) & 0xf0);
}
VOID DETOUR_IA64_BUNDLE::SetData(BYTE slot, UINT64 nData)
{
switch (slot)
{
case 0: SetData0(nData); return;
case 1: SetData1(nData); return;
case 2: SetData2(nData); return;
}
__debugbreak();
}
VOID DETOUR_IA64_BUNDLE::SetData0(UINT64 nData)
{
wide[0] = (wide[0] & ~0x000003ffffffffe0) | (( nData << 5) & 0x000003ffffffffe0);
}
VOID DETOUR_IA64_BUNDLE::SetData1(UINT64 nData)
{
wide[0] = (wide[0] & ~0xffffc00000000000) | ((nData << 46) & 0xffffc00000000000);
wide[1] = (wide[1] & ~0x000000000007ffff) | ((nData >> 18) & 0x000000000007ffff);
}
VOID DETOUR_IA64_BUNDLE::SetData2(UINT64 nData)
{
wide[1] = (wide[1] & ~0x0fffffffff800000) | ((nData << 23) & 0x0fffffffff800000);
}
UINT64 DETOUR_IA64_BUNDLE::GetInstruction(BYTE slot) const
{
switch (slot) {
case 0: return GetInstruction0();
case 1: return GetInstruction1();
case 2: return GetInstruction2();
}
__debugbreak();
return 0;
}
UINT64 DETOUR_IA64_BUNDLE::GetInstruction0() const
{
// 41 bits from wide[0], skipping the 5 bit template.
return GetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE);
}
UINT64 DETOUR_IA64_BUNDLE::GetInstruction1() const
{
// 64-46 bits from wide[0] and the rest from wide[1].
const UINT count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET;
const UINT count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0;
return GetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0) | (GetBits(wide[1], 0, count1) << count0);
}
UINT64 DETOUR_IA64_BUNDLE::GetInstruction2() const
{
// Upper 41 bits of wide[1].
return wide[1] >> (64 - DETOUR_IA64_INSTRUCTION_SIZE);
}
void DETOUR_IA64_BUNDLE::SetInstruction(BYTE slot, UINT64 instruction)
{
switch (slot) {
case 0: SetInstruction0(instruction); return;
case 1: SetInstruction1(instruction); return;
case 2: SetInstruction2(instruction); return;
}
__debugbreak();
}
void DETOUR_IA64_BUNDLE::SetInstruction0(UINT64 instruction)
{
wide[0] = SetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE, instruction);
}
void DETOUR_IA64_BUNDLE::SetInstruction1(UINT64 instruction)
{
UINT const count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET;
UINT const count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0;
UINT64 const wide0 = SetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0, instruction);
UINT64 const wide1 = SetBits(wide[1], 0, count1, instruction >> count0);
wide[0] = wide0;
wide[1] = wide1;
}
void DETOUR_IA64_BUNDLE::SetInstruction2(UINT64 instruction)
{
// Set upper 41 bits of wide[1].
wide[1] = SetBits(wide[1], 64 - DETOUR_IA64_INSTRUCTION_SIZE, DETOUR_IA64_INSTRUCTION_SIZE, instruction);
}
UINT64 DETOUR_IA64_BUNDLE::SignExtend(UINT64 Value, UINT64 Offset)
// This definition is from the IA64 manual.
{
if ((Value & (((UINT64)1) << (Offset - 1))) == 0)
return Value;
UINT64 const new_value = Value | ((~(UINT64)0) << Offset);
return new_value;
}
UINT64 DETOUR_IA64_BUNDLE::GetBits(UINT64 Value, UINT64 Offset, UINT64 Count)
{
UINT64 const new_value = (Value >> Offset) & ~(~((UINT64)0) << Count);
return new_value;
}
UINT64 DETOUR_IA64_BUNDLE::SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field)
{
UINT64 const mask = (~((~(UINT64)0) << Count)) << Offset;
UINT64 const new_value = (Value & ~mask) | ((Field << Offset) & mask);
return new_value;
}
UINT64 DETOUR_IA64_BUNDLE::GetOpcode(UINT64 instruction)
// Get 4bit primary opcode.
{
UINT64 const opcode = GetBits(instruction, DETOUR_IA64_INSTRUCTION_SIZE - 4, 4);
return opcode;
}
UINT64 DETOUR_IA64_BUNDLE::GetX(UINT64 instruction)
// Get 1bit opcode extension.
{
UINT64 const x = GetBits(instruction, 33, 1);
return x;
}
UINT64 DETOUR_IA64_BUNDLE::GetX3(UINT64 instruction)
// Get 3bit opcode extension.
{
UINT64 const x3 = GetBits(instruction, 33, 3);
return x3;
}
UINT64 DETOUR_IA64_BUNDLE::GetX6(UINT64 instruction)
// Get 6bit opcode extension.
{
UINT64 const x6 = GetBits(instruction, 27, 6);
return x6;
}
UINT64 DETOUR_IA64_BUNDLE::GetImm7a(UINT64 instruction)
{
UINT64 const imm7a = GetBits(instruction, 6, 7);
return imm7a;
}
UINT64 DETOUR_IA64_BUNDLE::SetImm7a(UINT64 instruction, UINT64 imm7a)
{
UINT64 const new_instruction = SetBits(instruction, 6, 7, imm7a);
return new_instruction;
}
UINT64 DETOUR_IA64_BUNDLE::GetImm13c(UINT64 instruction)
{
UINT64 const imm13c = GetBits(instruction, 20, 13);
return imm13c;
}
UINT64 DETOUR_IA64_BUNDLE::SetImm13c(UINT64 instruction, UINT64 imm13c)
{
UINT64 const new_instruction = SetBits(instruction, 20, 13, imm13c);
return new_instruction;
}
UINT64 DETOUR_IA64_BUNDLE::GetSignBit(UINT64 instruction)
{
UINT64 const signBit = GetBits(instruction, 36, 1);
return signBit;
}
UINT64 DETOUR_IA64_BUNDLE::SetSignBit(UINT64 instruction, UINT64 signBit)
{
UINT64 const new_instruction = SetBits(instruction, 36, 1, signBit);
return new_instruction;
}
UINT64 DETOUR_IA64_BUNDLE::GetImm20a(UINT64 instruction)
{
UINT64 const imm20a = GetBits(instruction, 6, 20);
return imm20a;
}
UINT64 DETOUR_IA64_BUNDLE::SetImm20a(UINT64 instruction, UINT64 imm20a)
{
UINT64 const new_instruction = SetBits(instruction, 6, 20, imm20a);
return new_instruction;
}
UINT64 DETOUR_IA64_BUNDLE::GetImm20b(UINT64 instruction)
{
UINT64 const imm20b = GetBits(instruction, 13, 20);
return imm20b;
}
UINT64 DETOUR_IA64_BUNDLE::SetImm20b(UINT64 instruction, UINT64 imm20b)
{
UINT64 const new_instruction = SetBits(instruction, 13, 20, imm20b);
return new_instruction;
}
bool DETOUR_IA64_BUNDLE::RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst,
_In_ BYTE slot,
_Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const
/*
If pBundleExtra is provided and instruction is IP-relative,
this function relocates instruction to target pBundleExtra,
pBundleExtra is set to brl the original target, and return true.
[Not used] If pBundleExtra is not provided and instruction is IP-relative, return true.
Else return false.
The following IP-relative forms are recognized:
br and br.call
chk.s.m integer and float
chk.a.nc integer and float
chk.a.clr integer and float
chk.s.i
fchkf
Brl is handled elsewhere, because the code was previously written.
Branch prediction hints are not relocated.
*/
{
UINT64 const instruction = GetInstruction(slot);
UINT64 const opcode = GetOpcode(instruction);
size_t const dest = (size_t)pDst;
size_t const extra = (size_t)pBundleExtra;
switch (GetUnit(slot)) {
case F_UNIT:
// F14 fchkf
if (opcode == 0 && GetX(instruction) == 0 && GetX6(instruction) == 8) {
goto imm20a;
}
return false;
case M_UNIT:
// M20 x3 == 1 integer chk.s.m
// M21 x3 == 3 floating point chk.s
if (opcode == 1) {
UINT64 const x3 = GetX3(instruction);
if (x3 == 1 || x3 == 3) {
goto imm13_7;
}
}
// M22 x3 == 4 integer chk.a.nc
// M22 x3 == 5 integer chk.a.clr
// M23 x3 == 6 floating point chk.a.nc
// M23 x3 == 7 floating point chk.a.clr
if (opcode == 0) {
UINT64 const x3 = GetX3(instruction);
if (x3 == 4 || x3 == 5 || x3 == 6 || x3 == 7) {
goto imm20b;
}
}
return false;
case I_UNIT:
// I20
if (opcode == 0 && GetX3(instruction) == 1) { // chk.s.i
goto imm13_7;
}
return false;
case B_UNIT:
// B1 B2 B3
// 4 br
// 5 br.call
if (opcode == 4 || opcode == 5) {
goto imm20b;
}
return false;
}
return false;
UINT64 imm;
UINT64 new_instruction;
imm13_7:
imm = SignExtend((GetSignBit(instruction) << 20) | (GetImm13c(instruction) << 7) | GetImm7a(instruction), 21) << 4;
new_instruction = SetSignBit(SetImm13c(SetImm7a(instruction, (extra - dest) >> 4), (extra - dest) >> 11), extra < dest);
goto set_brl;
imm20a:
imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20a(instruction), 21) << 4;
new_instruction = SetSignBit(SetImm20a(instruction, (extra - dest) >> 4), extra < dest);
goto set_brl;
imm20b:
imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20b(instruction), 21) << 4;
new_instruction = SetSignBit(SetImm20b(instruction, (extra - dest) >> 4), extra < dest);
goto set_brl;
set_brl:
if (pBundleExtra != NULL) {
pDst->SetInstruction(slot, new_instruction);
pBundleExtra->SetBrl((size_t)this + imm);
}
return true;
}
UINT DETOUR_IA64_BUNDLE::RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst,
_Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const
/*
Having already copied the bundle unchanged, then relocate its instructions one at a time.
Return how many extra bytes are required to relocate the bundle.
*/
{
UINT nExtraBytes = 0;
for (BYTE slot = 0; slot < DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE; ++slot) {
if (!RelocateInstruction(pDst, slot, pBundleExtra)) {
continue;
}
pBundleExtra -= !!pBundleExtra;
nExtraBytes += sizeof(DETOUR_IA64_BUNDLE);
}
return nExtraBytes;
}
BOOL DETOUR_IA64_BUNDLE::IsBrl() const
{
// f.e. d.c. b.a. 9.8. 7.6. 5. 4. 3. 2. 1. 0.
// c000 0070 0000 0000 0000 00 01 00 00 00 05 : brl.sptk.few
// c8ff fff0 007f fff0 ffff 00 01 00 00 00 05 : brl.sptk.few
// c000 0048 0000 0000 0001 00 00 00 00 00 05 : brl.sptk.many
return ((wide[0] & 0x000000000000001e) == 0x0000000000000004 && // 4 or 5.
(wide[1] & 0xe000000000000000) == 0xc000000000000000); // c or d.
}
VOID DETOUR_IA64_BUNDLE::SetBrl()
{
wide[0] = 0x0000000100000005; // few
//wide[0] = 0x0000000180000005; // many
wide[1] = 0xc000000800000000;
}
UINT64 DETOUR_IA64_BUNDLE::GetBrlImm() const
{
return (
// 0x0000000000fffff0
((wide[1] & 0x00fffff000000000) >> 32) | // all 20 bits of imm20b.
// 0x000000ffff000000
((wide[0] & 0xffff000000000000) >> 24) | // bottom 16 bits of imm39.
// 0x7fffff0000000000
((wide[1] & 0x00000000007fffff) << 40) | // top 23 bits of imm39.
// 0x8000000000000000
((wide[1] & 0x0800000000000000) << 4) // single bit of i.
);
}
VOID DETOUR_IA64_BUNDLE::SetBrlImm(UINT64 imm)
{
wide[0] = ((wide[0] & ~0xffff000000000000) |
// 0xffff000000000000
((imm & 0x000000ffff000000) << 24) // bottom 16 bits of imm39.
);
wide[1] = ((wide[1] & ~0x08fffff0007fffff) |
// 0x00fffff000000000
((imm & 0x0000000000fffff0) << 32) | // all 20 bits of imm20b.
// 0x00000000007fffff
((imm & 0x7fffff0000000000) >> 40) | // top 23 bits of imm39.
// 0x0800000000000000
((imm & 0x8000000000000000) >> 4) // single bit of i.
);
}
UINT64 DETOUR_IA64_BUNDLE::GetBrlTarget() const
{
return (UINT64)this + GetBrlImm();
}
VOID DETOUR_IA64_BUNDLE::SetBrl(UINT64 target)
{
UINT64 imm = target - (UINT64)this;
SetBrl();
SetBrlImm(imm);
}
VOID DETOUR_IA64_BUNDLE::SetBrlTarget(UINT64 target)
{
UINT64 imm = target - (UINT64)this;
SetBrlImm(imm);
}
BOOL DETOUR_IA64_BUNDLE::IsMovlGp() const
{
// f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.
// 6fff f7f0 207f ffff ffff c001 0000 0004
// 6000 0000 2000 0000 0000 0001 0000 0004
return ((wide[0] & 0x00003ffffffffffe) == 0x0000000100000004 &&
(wide[1] & 0xf000080fff800000) == 0x6000000020000000);
}
UINT64 DETOUR_IA64_BUNDLE::GetMovlGp() const
{
UINT64 raw = (
// 0x0000000000000070
((wide[1] & 0x000007f000000000) >> 36) |
// 0x000000000000ff80
((wide[1] & 0x07fc000000000000) >> 43) |
// 0x00000000001f0000
((wide[1] & 0x0003e00000000000) >> 29) |
// 0x0000000000200000
((wide[1] & 0x0000100000000000) >> 23) |
// 0x000000ffffc00000
((wide[0] & 0xffffc00000000000) >> 24) |
// 0x7fffff0000000000
((wide[1] & 0x00000000007fffff) << 40) |
// 0x8000000000000000
((wide[1] & 0x0800000000000000) << 4)
);
return (INT64)raw;
}
VOID DETOUR_IA64_BUNDLE::SetMovlGp(UINT64 gp)
{
UINT64 raw = (UINT64)gp;
wide[0] = (0x0000000100000005 |
// 0xffffc00000000000
((raw & 0x000000ffffc00000) << 24)
);
wide[1] = (
0x6000000020000000 |
// 0x0000070000000000
((raw & 0x0000000000000070) << 36) |
// 0x07fc000000000000
((raw & 0x000000000000ff80) << 43) |
// 0x0003e00000000000
((raw & 0x00000000001f0000) << 29) |
// 0x0000100000000000
((raw & 0x0000000000200000) << 23) |
// 0x00000000007fffff
((raw & 0x7fffff0000000000) >> 40) |
// 0x0800000000000000
((raw & 0x8000000000000000) >> 4)
);
}
UINT DETOUR_IA64_BUNDLE::Copy(_Out_ DETOUR_IA64_BUNDLE *pDst,
_Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const
{
// Copy the bytes unchanged.
#pragma warning(suppress:6001) // using uninitialized *pDst
pDst->wide[0] = wide[0];
pDst->wide[1] = wide[1];
// Relocate if necessary.
UINT nExtraBytes = RelocateBundle(pDst, pBundleExtra);
if (GetUnit1() == L_UNIT && IsBrl()) {
pDst->SetBrlTarget(GetBrlTarget());
}
return nExtraBytes;
}
BOOL DETOUR_IA64_BUNDLE::SetNop(BYTE slot)
{
switch (GetUnit(slot)) {
case I_UNIT:
case M_UNIT:
case F_UNIT:
SetInst(slot, 0);
SetData(slot, 0x8000000);
return true;
case B_UNIT:
SetInst(slot, 2);
SetData(slot, 0);
return true;
}
__debugbreak();
return false;
}
BOOL DETOUR_IA64_BUNDLE::SetNop0()
{
return SetNop(0);
}
BOOL DETOUR_IA64_BUNDLE::SetNop1()
{
return SetNop(1);
}
BOOL DETOUR_IA64_BUNDLE::SetNop2()
{
return SetNop(2);
}
VOID DETOUR_IA64_BUNDLE::SetStop()
{
data[0] |= 0x01;
}
#endif // DETOURS_IA64
PVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,
_Inout_opt_ PVOID *ppDstPool,
_In_ PVOID pSrc,
_Out_opt_ PVOID *ppTarget,
_Out_opt_ LONG *plExtra)
{
LONG nExtra;
DETOUR_IA64_BUNDLE bExtra;
DETOUR_IA64_BUNDLE *pbSrc = (DETOUR_IA64_BUNDLE *)pSrc;
DETOUR_IA64_BUNDLE *pbDst = pDst ? (DETOUR_IA64_BUNDLE *)pDst : &bExtra;
plExtra = plExtra ? plExtra : &nExtra;
*plExtra = 0;
if (ppTarget != NULL) {
if (pbSrc->IsBrl()) {
*ppTarget = (PVOID)pbSrc->GetBrlTarget();
}
else {
*ppTarget = DETOUR_INSTRUCTION_TARGET_NONE;
}
}
*plExtra = (LONG)pbSrc->Copy(pbDst, ppDstPool ? ((DETOUR_IA64_BUNDLE*)*ppDstPool) - 1 : (DETOUR_IA64_BUNDLE*)NULL);
return pbSrc + 1;
}
#endif // DETOURS_IA64
#ifdef DETOURS_ARM
#define DETOURS_PFUNC_TO_PBYTE(p) ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1))
#define DETOURS_PBYTE_TO_PFUNC(p) ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1))
#define c_PCAdjust 4 // The PC value of an instruction is the PC address plus 4.
#define c_PC 15 // The register number for the Program Counter
#define c_LR 14 // The register number for the Link Register
#define c_SP 13 // The register number for the Stack Pointer
#define c_NOP 0xbf00 // A nop instruction
#define c_BREAK 0xdefe // A nop instruction
class CDetourDis
{
public:
CDetourDis();
PBYTE CopyInstruction(PBYTE pDst,
PBYTE *ppDstPool,
PBYTE pSrc,
PBYTE *ppTarget,
LONG *plExtra);
public:
typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc);
struct COPYENTRY {
USHORT nOpcode;
COPYFUNC pfCopy;
};
typedef const COPYENTRY * REFCOPYENTRY;
struct Branch5
{
DWORD Register : 3;
DWORD Imm5 : 5;
DWORD Padding : 1;
DWORD I : 1;
DWORD OpCode : 6;
};
struct Branch5Target
{
DWORD Padding : 1;
DWORD Imm5 : 5;
DWORD I : 1;
DWORD Padding2 : 25;
};
struct Branch8
{
DWORD Imm8 : 8;
DWORD Condition : 4;
DWORD OpCode : 4;
};
struct Branch8Target
{
DWORD Padding : 1;
DWORD Imm8 : 8;
DWORD Padding2 : 23;
};
struct Branch11
{
DWORD Imm11 : 11;
DWORD OpCode : 5;
};
struct Branch11Target
{
DWORD Padding : 1;
DWORD Imm11 : 11;
DWORD Padding2 : 20;
};
struct Branch20
{
DWORD Imm11 : 11;
DWORD J2 : 1;
DWORD IT : 1;
DWORD J1 : 1;
DWORD Other : 2;
DWORD Imm6 : 6;
DWORD Condition : 4;
DWORD Sign : 1;
DWORD OpCode : 5;
};
struct Branch20Target
{
DWORD Padding : 1;
DWORD Imm11 : 11;
DWORD Imm6 : 6;
DWORD J1 : 1;
DWORD J2 : 1;
DWORD Sign : 1;
INT32 Padding2 : 11;
};
struct Branch24
{
DWORD Imm11 : 11;
DWORD J2 : 1;
DWORD InstructionSet : 1;
DWORD J1 : 1;
DWORD Link : 1;
DWORD Branch : 1;
DWORD Imm10 : 10;
DWORD Sign : 1;
DWORD OpCode : 5;
};
struct Branch24Target
{
DWORD Padding : 1;
DWORD Imm11 : 11;
DWORD Imm10 : 10;
DWORD I2 : 1;
DWORD I1 : 1;
DWORD Sign : 1;
INT32 Padding2 : 7;
};
struct LiteralLoad8
{
DWORD Imm8 : 8;
DWORD Register : 3;
DWORD OpCode : 5;
};
struct LiteralLoad8Target
{
DWORD Padding : 2;
DWORD Imm8 : 8;
DWORD Padding2 : 22;
};
struct LiteralLoad12
{
DWORD Imm12 : 12;
DWORD Register : 4;
DWORD OpCodeSuffix : 7;
DWORD Add : 1;
DWORD OpCodePrefix : 8;
};
struct LiteralLoad12Target
{
DWORD Imm12 : 12;
DWORD Padding : 20;
};
struct ImmediateRegisterLoad32
{
DWORD Imm12 : 12;
DWORD DestinationRegister : 4;
DWORD SourceRegister: 4;
DWORD OpCode : 12;
};
struct ImmediateRegisterLoad16
{
DWORD DestinationRegister : 3;
DWORD SourceRegister: 3;
DWORD OpCode : 10;
};
struct TableBranch
{
DWORD IndexRegister : 4;
DWORD HalfWord : 1;
DWORD OpCodeSuffix : 11;
DWORD BaseRegister : 4;
DWORD OpCodePrefix : 12;
};
struct Shift
{
DWORD Imm2 : 2;
DWORD Imm3 : 3;
};
struct Add32
{
DWORD SecondOperandRegister : 4;
DWORD Type : 2;
DWORD Imm2 : 2;
DWORD DestinationRegister : 4;
DWORD Imm3 : 3;
DWORD Padding : 1;
DWORD FirstOperandRegister : 4;
DWORD SetFlags : 1;
DWORD OpCode : 11;
};
struct LogicalShiftLeft32
{
DWORD SourceRegister : 4;
DWORD Padding : 2;
DWORD Imm2 : 2;
DWORD DestinationRegister : 4;
DWORD Imm3 : 3;
DWORD Padding2 : 5;
DWORD SetFlags : 1;
DWORD OpCode : 11;
};
struct StoreImmediate12
{
DWORD Imm12 : 12;
DWORD SourceRegister : 4;
DWORD BaseRegister : 4;
DWORD OpCode : 12;
};
protected:
BYTE PureCopy16(BYTE* pSource, BYTE* pDest);
BYTE PureCopy32(BYTE* pSource, BYTE* pDest);
BYTE CopyMiscellaneous16(BYTE* pSource, BYTE* pDest);
BYTE CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest);
BYTE CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest);
BYTE CopyLiteralLoad16(BYTE* pSource, BYTE* pDest);
BYTE CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest);
BYTE CopyBranch24(BYTE* pSource, BYTE* pDest);
BYTE CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest);
BYTE CopyLiteralLoad32(BYTE* pSource, BYTE* pDest);
BYTE CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest);
BYTE CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest);
BYTE CopyTableBranch(BYTE* pSource, BYTE* pDest);
BYTE BeginCopy32(BYTE* pSource, BYTE* pDest);
LONG DecodeBranch5(ULONG opcode);
USHORT EncodeBranch5(ULONG originalOpCode, LONG delta);
LONG DecodeBranch8(ULONG opcode);
USHORT EncodeBranch8(ULONG originalOpCode, LONG delta);
LONG DecodeBranch11(ULONG opcode);
USHORT EncodeBranch11(ULONG originalOpCode, LONG delta);
BYTE EmitBranch11(PUSHORT& pDest, LONG relativeAddress);
LONG DecodeBranch20(ULONG opcode);
ULONG EncodeBranch20(ULONG originalOpCode, LONG delta);
LONG DecodeBranch24(ULONG opcode, BOOL& fLink);
ULONG EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink);
LONG DecodeLiteralLoad8(ULONG instruction);
LONG DecodeLiteralLoad12(ULONG instruction);
BYTE EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral);
BYTE EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral);
BYTE EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg);
BYTE EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg);
BYTE EmitLongLiteralLoad(PUSHORT& pDest, BYTE reg, PVOID pTarget);
BYTE EmitLongBranch(PUSHORT& pDest, PVOID pTarget);
USHORT CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd);
protected:
ULONG GetLongInstruction(BYTE* pSource)
{
return (((PUSHORT)pSource)[0] << 16) | (((PUSHORT)pSource)[1]);
}
BYTE EmitLongInstruction(PUSHORT& pDstInst, ULONG instruction)
{
*pDstInst++ = (USHORT)(instruction >> 16);
*pDstInst++ = (USHORT)instruction;
return sizeof(ULONG);
}
BYTE EmitShortInstruction(PUSHORT& pDstInst, USHORT instruction)
{
*pDstInst++ = instruction;
return sizeof(USHORT);
}
PBYTE Align4(PBYTE pValue)
{
return (PBYTE)(((size_t)pValue) & ~(ULONG)3u);
}
PBYTE CalculateTarget(PBYTE pSource, LONG delta)
{
return (pSource + delta + c_PCAdjust);
}
LONG CalculateNewDelta(PBYTE pTarget, BYTE* pDest)
{
return (LONG)(pTarget - (pDest + c_PCAdjust));
}
BYTE EmitAdd32(PUSHORT& pDstInst, BYTE op1Reg, BYTE op2Reg, BYTE dstReg, BYTE shiftAmount)
{
Shift& shift = (Shift&)(shiftAmount);
const BYTE shiftType = 0x00; // LSL
Add32 add = { op2Reg, shiftType, shift.Imm2, dstReg, shift.Imm3,
0x0, op1Reg, 0x0, 0x758 };
return EmitLongInstruction(pDstInst, (ULONG&)add);
}
BYTE EmitLogicalShiftLeft32(PUSHORT& pDstInst, BYTE srcReg, BYTE dstReg, BYTE shiftAmount)
{
Shift& shift = (Shift&)(shiftAmount);
LogicalShiftLeft32 shiftLeft = { srcReg, 0x00, shift.Imm2, dstReg, shift.Imm3, 0x1E,
0x00, 0x752 };
return EmitLongInstruction(pDstInst, (ULONG&)shiftLeft);
}
BYTE EmitStoreImmediate12(PUSHORT& pDstInst, BYTE srcReg, BYTE baseReg, USHORT offset)
{
StoreImmediate12 store = { offset, srcReg, baseReg, 0xF8C };
return EmitLongInstruction(pDstInst, (ULONG&)store);
}
protected:
PBYTE m_pbTarget = NULL;
PBYTE m_pbPool = NULL;
LONG m_lExtra = 0;
BYTE m_rbScratchDst[64] = { 0 };
static const COPYENTRY s_rceCopyTable[33];
};
LONG CDetourDis::DecodeBranch5(ULONG opcode)
{
Branch5& branch = (Branch5&)(opcode);
Branch5Target target;
ZeroMemory(&target, sizeof(target));
target.Imm5 = branch.Imm5;
target.I = branch.I;
// Return zero-extended value
return (LONG&)target;
}
USHORT CDetourDis::EncodeBranch5(ULONG originalOpCode, LONG delta)
{
// Too large for a 5 bit branch (5 bit branches can be up to 7 bits due to I and the trailing 0)
if (delta < 0 || delta > 0x7F) {
return 0;
}
Branch5& branch = (Branch5&)(originalOpCode);
Branch5Target& target = (Branch5Target&)(delta);
branch.Imm5 = target.Imm5;
branch.I = target.I;
return (USHORT&)branch;
}
LONG CDetourDis::DecodeBranch8(ULONG opcode)
{
Branch8& branch = (Branch8&)(opcode);
Branch8Target target;
ZeroMemory(&target, sizeof(target));
target.Imm8 = branch.Imm8;
// Return sign extended value
return (((LONG&)target) << 23) >> 23;
}
USHORT CDetourDis::EncodeBranch8(ULONG originalOpCode, LONG delta)
{
// Too large for 8 bit branch (8 bit branches can be up to 9 bits due to the trailing 0)
if (delta < (-(int)0x100) || delta > 0xFF) {
return 0;
}
Branch8& branch = (Branch8&)(originalOpCode);
Branch8Target& target = (Branch8Target&)(delta);
branch.Imm8 = target.Imm8;
return (USHORT&)branch;
}
LONG CDetourDis::DecodeBranch11(ULONG opcode)
{
Branch11& branch = (Branch11&)(opcode);
Branch11Target target;
ZeroMemory(&target, sizeof(target));
target.Imm11 = branch.Imm11;
// Return sign extended value
return (((LONG&)target) << 20) >> 20;
}
USHORT CDetourDis::EncodeBranch11(ULONG originalOpCode, LONG delta)
{
// Too large for an 11 bit branch (11 bit branches can be up to 12 bits due to the trailing 0)
if (delta < (-(int)0x800) || delta > 0x7FF) {
return 0;
}
Branch11& branch = (Branch11&)(originalOpCode);
Branch11Target& target = (Branch11Target&)(delta);
branch.Imm11 = target.Imm11;
return (USHORT&)branch;
}
BYTE CDetourDis::EmitBranch11(PUSHORT& pDest, LONG relativeAddress)
{
Branch11Target& target = (Branch11Target&)(relativeAddress);
Branch11 branch11 = { target.Imm11, 0x1C };
*pDest++ = (USHORT&)branch11;
return sizeof(USHORT);
}
LONG CDetourDis::DecodeBranch20(ULONG opcode)
{
Branch20& branch = (Branch20&)(opcode);
Branch20Target target;
ZeroMemory(&target, sizeof(target));
target.Imm11 = branch.Imm11;
target.Imm6 = branch.Imm6;
target.Sign = branch.Sign;
target.J1 = branch.J1;
target.J2 = branch.J2;
// Sign extend
if (target.Sign) {
target.Padding2 = -1;
}
return (LONG&)target;
}
ULONG CDetourDis::EncodeBranch20(ULONG originalOpCode, LONG delta)
{
// Too large for 20 bit branch (20 bit branches can be up to 21 bits due to the trailing 0)
if (delta < (-(int)0x100000) || delta > 0xFFFFF) {
return 0;
}
Branch20& branch = (Branch20&)(originalOpCode);
Branch20Target& target = (Branch20Target&)(delta);
branch.Imm11 = target.Imm11;
branch.Imm6 = target.Imm6;
branch.Sign = target.Sign;
branch.J1 = target.J1;
branch.J2 = target.J2;
return (ULONG&)branch;
}
LONG CDetourDis::DecodeBranch24(ULONG opcode, BOOL& fLink)
{
Branch24& branch = (Branch24&)(opcode);
Branch24Target target;
ZeroMemory(&target, sizeof(target));
target.Imm11 = branch.Imm11;
target.Imm10 = branch.Imm10;
target.Sign = branch.Sign;
target.I1 = ~(branch.J1 ^ target.Sign);
target.I2 = ~(branch.J2 ^ target.Sign);
fLink = branch.Link;
// Sign extend
if (target.Sign) {
target.Padding2 = -1;
}
return (LONG&)target;
}
ULONG CDetourDis::EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink)
{
// Too large for 24 bit branch (24 bit branches can be up to 25 bits due to the trailing 0)
if (delta < static_cast(0xFF000000) || delta > static_cast(0xFFFFFF)) {
return 0;
}
Branch24& branch = (Branch24&)(originalOpCode);
Branch24Target& target = (Branch24Target&)(delta);
branch.Imm11 = target.Imm11;
branch.Imm10 = target.Imm10;
branch.Link = fLink;
branch.Sign = target.Sign;
branch.J1 = ~(target.I1 ^ branch.Sign);
branch.J2 = ~(target.I2 ^ branch.Sign);
return (ULONG&)branch;
}
LONG CDetourDis::DecodeLiteralLoad8(ULONG instruction)
{
LiteralLoad8& load = (LiteralLoad8&)(instruction);
LiteralLoad8Target target;
ZeroMemory(&target, sizeof(target));
target.Imm8 = load.Imm8;
return (LONG&)target;
}
BYTE CDetourDis::EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral)
{
// Note: We add 2 (which gets rounded down) because literals must be 32-bit
// aligned, but the ldr can be 16-bit aligned.
LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest);
LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0x3FF);
LiteralLoad8Target& target = (LiteralLoad8Target&)(relative);
LiteralLoad8 load = { target.Imm8, targetRegister, 0x9 };
return EmitShortInstruction(pDest, (USHORT&)load);
}
LONG CDetourDis::DecodeLiteralLoad12(ULONG instruction)
{
LiteralLoad12& load = (LiteralLoad12&)(instruction);
LiteralLoad12Target target;
ZeroMemory(&target, sizeof(target));
target.Imm12 = load.Imm12;
return (LONG&)target;
}
BYTE CDetourDis::EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral)
{
// Note: We add 2 (which gets rounded down) because literals must be 32-bit
// aligned, but the ldr can be 16-bit aligned.
LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest);
LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0xFFF);
LiteralLoad12Target& target = (LiteralLoad12Target&)(relative);
target.Imm12 -= target.Imm12 & 3;
LiteralLoad12 load = { target.Imm12, targetRegister, 0x5F, (DWORD)(newDelta > 0), 0xF8 };
return EmitLongInstruction(pDest, (ULONG&)load);
}
BYTE CDetourDis::EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg)
{
ImmediateRegisterLoad32 load = { 0, reg, reg, 0xF8D };
return EmitLongInstruction(pDest, (ULONG&)load);
}
BYTE CDetourDis::EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg)
{
ImmediateRegisterLoad16 load = { reg, reg, 0x680 >> 2 };
return EmitShortInstruction(pDest, (USHORT&)load);
}
BYTE CDetourDis::EmitLongLiteralLoad(PUSHORT& pDest, BYTE targetRegister, PVOID pTarget)
{
*--((PULONG&)m_pbPool) = (ULONG)(size_t)pTarget;
// ldr rn, target.
BYTE size = EmitLiteralLoad12(pDest, targetRegister, m_pbPool);
// This only makes sense if targetRegister != PC;
// otherwise, we would have branched with the previous instruction anyway
if (targetRegister != c_PC) {
// ldr rn, [rn]
if (targetRegister <= 7) {
size = (BYTE)(size + EmitImmediateRegisterLoad16(pDest, targetRegister));
}
else {
size = (BYTE)(size + EmitImmediateRegisterLoad32(pDest, targetRegister));
}
}
return size;
}
BYTE CDetourDis::EmitLongBranch(PUSHORT& pDest, PVOID pTarget)
{
// Emit a long literal load into PC
BYTE size = EmitLongLiteralLoad(pDest, c_PC, DETOURS_PBYTE_TO_PFUNC(pTarget));
return size;
}
BYTE CDetourDis::PureCopy16(BYTE* pSource, BYTE* pDest)
{
*(USHORT *)pDest = *(USHORT *)pSource;
return sizeof(USHORT);
}
BYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest)
{
*(UNALIGNED ULONG *)pDest = *(UNALIGNED ULONG*)pSource;
return sizeof(DWORD);
}
USHORT CDetourDis::CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd)
{
ULONG destinationLength = (ULONG)(pDestEnd - pDestStart);
return static_cast((destinationLength > sourceLength) ? (destinationLength - sourceLength) : 0);
}
BYTE CDetourDis::CopyMiscellaneous16(BYTE* pSource, BYTE* pDest)
{
USHORT instruction = *(PUSHORT)(pSource);
// Compare and branch imm5 (CBZ, CBNZ)
if ((instruction & 0x100) && !(instruction & 0x400)) { // (1011x0x1xxxxxxxx)
LONG oldDelta = DecodeBranch5(instruction);
PBYTE pTarget = CalculateTarget(pSource, oldDelta);
m_pbTarget = pTarget;
LONG newDelta = CalculateNewDelta(pTarget, pDest);
instruction = EncodeBranch5(instruction, newDelta);
if (instruction) {
// Copy the 16 bit instruction over
*(PUSHORT)(pDest) = instruction;
return sizeof(USHORT); // The source instruction was 16 bits
}
// If that fails, re-encode with 'conditional branch' logic, without using the condition flags
// For example, cbz r2,+0x56 (0x90432) becomes:
//
// 001df73a b92a cbnz r2,001df748
// 001df73c e002 b 001df744
// 001df73e bf00 nop
// 001df740 0432 dc.h 0432
// 001df742 0009 dc.h 0009
// 001df744 f85ff008 ldr pc,=0x90432
//
// Store where we will be writing our conditional branch, and move past it so we can emit a long branch
PUSHORT pDstInst = (PUSHORT)(pDest);
PUSHORT pConditionalBranchInstruction = pDstInst++;
// Emit the long branch instruction
BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget);
// Invert the CBZ/CBNZ instruction to move past our 'long branch' if the inverse comparison succeeds
// Write the CBZ/CBNZ instruction *before* the long branch we emitted above
// This had to be done out of order, since the size of a long branch can vary due to alignment restrictions
instruction = EncodeBranch5(*(PUSHORT)(pSource), longBranchSize - c_PCAdjust + sizeof(USHORT));
Branch5& branch = (Branch5&)(instruction);
branch.OpCode = (branch.OpCode & 0x02) ? 0x2C : 0x2E; // Invert the CBZ/CBNZ comparison
*pConditionalBranchInstruction = instruction;
// Compute the extra space needed for the branch sequence
m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));
return sizeof(USHORT); // The source instruction was 16 bits
}
// If-Then Instruction (IT)
if ((instruction >> 8 == 0xBF) && (instruction & 0xF)) { //(10111111xxxx(mask != 0b0000))
// ToDo: Implement IT handler
ASSERT(false);
return sizeof(USHORT);
}
// ADD/SUB, SXTH, SXTB, UXTH, UXTB, CBZ, CBNZ, PUSH, POP, REV, REV15, REVSH, NOP, YIELD, WFE, WFI, SEV, etc.
return PureCopy16(pSource, pDest);
}
BYTE CDetourDis::CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest)
{
USHORT instruction = *(PUSHORT)(pSource);
// Could be a conditional branch, an Undefined instruction or a Service System Call
// Only the former needs special logic
if ((instruction & 0xE00) != 0xE00) { // 1101(!=111x)xxxxxxxx
LONG oldDelta = DecodeBranch8(instruction);
PBYTE pTarget = CalculateTarget(pSource, oldDelta);
m_pbTarget = pTarget;
LONG newDelta = CalculateNewDelta(pTarget, pDest);
instruction = EncodeBranch8(instruction, newDelta);
if (instruction) {
// Copy the 16 bit instruction over
*(PUSHORT)(pDest) = instruction;
return sizeof(USHORT); // The source instruction was 16 bits
}
// If that fails, re-encode as a sequence of branches
// For example, bne +0x6E (0x90452) becomes:
//
// 001df758 d100 bne 001df75c
// 001df75a e005 b 001df768
// 001df75c e002 b 001df764
// 001df75e bf00 nop
// 001df760 0452 dc.h 0452
// 001df762 0009 dc.h 0009
// 001df764 f85ff008 ldr pc,=0x90452
//
// First, reuse the existing conditional branch to, if successful, branch down to a 'long branch' that we will emit below
USHORT newInstruction = EncodeBranch8(*(PUSHORT)(pSource), 0); // Due to the size of c_PCAdjust a zero-length branch moves 4 bytes forward, past the following unconditional branch
ASSERT(newInstruction);
PUSHORT pDstInst = (PUSHORT)(pDest);
*pDstInst++ = newInstruction;
// Next, prepare to insert an unconditional branch that will be hit if the condition above is not met. This branch will branch over the following 'long branch'
// We can't actually encode this branch yet though, because 'long branches' can vary in size
PUSHORT pUnconditionalBranchInstruction = pDstInst++;
// Then, emit a 'long branch' that will be hit if the original condition is met
BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget);
// Finally, encode and emit the unconditional branch that will be used to branch past the 'long branch' if the initial condition was not met
Branch11 branch11 = { 0x00, 0x1C };
newInstruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT));
ASSERT(newInstruction);
*pUnconditionalBranchInstruction = newInstruction;
// Compute the extra space needed for the branch sequence
m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));
return sizeof(USHORT); // The source instruction was 16 bits
}
return PureCopy16(pSource, pDest);
}
BYTE CDetourDis::CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest)
{
ULONG instruction = *(PUSHORT)(pSource);
LONG oldDelta = DecodeBranch11(instruction);
PBYTE pTarget = CalculateTarget(pSource, oldDelta);
m_pbTarget = pTarget;
LONG newDelta = CalculateNewDelta(pTarget, pDest);
instruction = EncodeBranch11(instruction, newDelta);
if (instruction) {
// Copy the 16 bit instruction over
*(PUSHORT)(pDest) = (USHORT)instruction;
return sizeof(USHORT); // The source instruction was 16 bits
}
// If that fails, re-encode as 32-bit
PUSHORT pDstInst = (PUSHORT)(pDest);
instruction = EncodeBranch24(0xf0009000, newDelta, FALSE);
if (instruction) {
// Copy both bytes of the instruction
EmitLongInstruction(pDstInst, instruction);
m_lExtra = sizeof(DWORD) - sizeof(USHORT); // The destination instruction was 32 bits
return sizeof(USHORT); // The source instruction was 16 bits
}
// If that fails, emit as a 'long branch'
if (!instruction) {
// For example, b +0x7FE (00090be6) becomes:
// 003f6d02 e001 b 003f6d08
// 003f6d04 0be6 dc.h 0be6
// 003f6d06 0009 dc.h 0009
// 003f6d08 f85ff008 ldr pc,=0x90BE6
EmitLongBranch(pDstInst, pTarget);
// Compute the extra space needed for the branch sequence
m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));
return sizeof(USHORT); // The source instruction was 16 bits
}
return sizeof(USHORT); // The source instruction was 16 bits
}
BYTE CDetourDis::CopyLiteralLoad16(BYTE* pSource, BYTE* pDest)
{
PBYTE pStart = pDest;
USHORT instruction = *(PUSHORT)(pSource);
LONG oldDelta = DecodeLiteralLoad8(instruction);
PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta);
// Re-encode as a 'long literal load'
// For example, ldr r0, [PC + 1E0] (0x905B4) becomes:
//
// 001df72c f85f0008 ldr r0,=0x905B4
// 001df730 f8d00000 ldr.w r0,[r0]
LiteralLoad8& load8 = (LiteralLoad8&)(instruction);
EmitLongLiteralLoad((PUSHORT&)pDest, load8.Register, pTarget);
m_lExtra = (LONG)(pDest - pStart - sizeof(USHORT));
return sizeof(USHORT); // The source instruction was 16 bits
}
BYTE CDetourDis::CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest)
{
ULONG instruction = *(PUSHORT)(pSource);
// BX
if ((instruction & 0xff80) == 0x4700) {
// The target is stored in a register
m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
}
// AND, LSR, TST, ADD, CMP, MOV
return PureCopy16(pSource, pDest);
}
const CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[33] =
{
// Shift by immediate, move register
// ToDo: Not handling moves from PC
/* 0b00000 */ { 0x00, &CDetourDis::PureCopy16 },
/* 0b00001 */ { 0x01, &CDetourDis::PureCopy16 },
/* 0b00010 */ { 0x02, &CDetourDis::PureCopy16 },
// Add/subtract register
// Add/subtract immediate
/* 0b00011 */ { 0x03, &CDetourDis::PureCopy16},
// Add/subtract/compare/move immediate
/* 0b00100 */ { 0x04, &CDetourDis::PureCopy16 },
/* 0b00101 */ { 0x05, &CDetourDis::PureCopy16 },
/* 0b00110 */ { 0x06, &CDetourDis::PureCopy16 },
/* 0b00111 */ { 0x07, &CDetourDis::PureCopy16 },
// Data-processing register
// Special data processing
// Branch/exchange instruction set
/* 0b01000 */ { 0x08, &CDetourDis::CopyBranchExchangeOrDataProcessing16 },
// Load from literal pool
/* 0b01001 */ { 0x09, &CDetourDis::CopyLiteralLoad16 },
// Load/store register offset
/* 0b01010 */ { 0x0a, &CDetourDis::PureCopy16 },
/* 0b01011 */ { 0x0b, &CDetourDis::PureCopy16 },
// Load/store word/byte immediate offset.
/* 0b01100 */ { 0x0c, &CDetourDis::PureCopy16 },
/* 0b01101 */ { 0x0d, &CDetourDis::PureCopy16 },
/* 0b01110 */ { 0x0e, &CDetourDis::PureCopy16 },
/* 0b01111 */ { 0x0f, &CDetourDis::PureCopy16 },
// Load/store halfword immediate offset.
/* 0b10000 */ { 0x10, &CDetourDis::PureCopy16 },
/* 0b10001 */ { 0x11, &CDetourDis::PureCopy16 },
// Load from or store to stack
/* 0b10010 */ { 0x12, &CDetourDis::PureCopy16 },
/* 0b10011 */ { 0x13, &CDetourDis::PureCopy16 },
// Add to SP or PC
/* 0b10100 */ { 0x14, &CDetourDis::PureCopy16 },
// ToDo: Is ADR (T1) blitt-able?
// It adds a value to PC and stores the result in a register.
// Does this count as a 'target' for detours?
/* 0b10101 */ { 0x15, &CDetourDis::PureCopy16 },
// Miscellaneous
/* 0b10110 */ { 0x16, &CDetourDis::CopyMiscellaneous16 },
/* 0b10111 */ { 0x17, &CDetourDis::CopyMiscellaneous16 },
// Load/store multiple
/* 0b11000 */ { 0x18, &CDetourDis::PureCopy16 },
/* 0b11001 */ { 0x19, &CDetourDis::PureCopy16 },
// ToDo: Are we sure these are all safe?
// LDMIA, for example, can include an 'embedded' branch.
// Does this count as a 'target' for detours?
// Conditional branch
/* 0b11010 */ { 0x1a, &CDetourDis::CopyConditionalBranchOrOther16 },
// Conditional branch
// Undefined instruction
// Service (system) call
/* 0b11011 */ { 0x1b, &CDetourDis::CopyConditionalBranchOrOther16 },
// Unconditional branch
/* 0b11100 */ { 0x1c, &CDetourDis::CopyUnConditionalBranch16 },
// 32-bit instruction
/* 0b11101 */ { 0x1d, &CDetourDis::BeginCopy32 },
/* 0b11110 */ { 0x1e, &CDetourDis::BeginCopy32 },
/* 0b11111 */ { 0x1f, &CDetourDis::BeginCopy32 },
{ 0, NULL }
};
BYTE CDetourDis::CopyBranch24(BYTE* pSource, BYTE* pDest)
{
ULONG instruction = GetLongInstruction(pSource);
BOOL fLink;
LONG oldDelta = DecodeBranch24(instruction, fLink);
PBYTE pTarget = CalculateTarget(pSource, oldDelta);
m_pbTarget = pTarget;
// Re-encode as 32-bit
PUSHORT pDstInst = (PUSHORT)(pDest);
LONG newDelta = CalculateNewDelta(pTarget, pDest);
instruction = EncodeBranch24(instruction, newDelta, fLink);
if (instruction) {
// Copy both bytes of the instruction
EmitLongInstruction(pDstInst, instruction);
return sizeof(DWORD);
}
// If that fails, re-encode as a 'long branch'
EmitLongBranch(pDstInst, pTarget);
// Compute the extra space needed for the instruction
m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst));
return sizeof(DWORD); // The source instruction was 32 bits
}
BYTE CDetourDis::CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest)
{
ULONG instruction = GetLongInstruction(pSource);
if ((instruction & 0xf800d000) == 0xf0008000) { // B.W