[
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n.xmake/\nvsxmake*/\nvs20*/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n"
  },
  {
    "path": "BuildAllTargets.cmd",
    "content": "@setlocal\n@echo off\n\nrem Change to the current folder.\ncd \"%~dp0\"\n\nrem Remove the output folder for a fresh compile.\nrd /s /q Output\n\nrem Initialize Visual Studio environment\ncall \"%~dp0InitializeVisualStudioEnvironment.cmd\"\n\nrem Build all targets\nMSBuild -binaryLogger:Output\\BuildAllTargets.binlog -m BuildAllTargets.proj\n\n@endlocal"
  },
  {
    "path": "BuildAllTargets.proj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project \n  DefaultTargets=\"Restore;Build\"\n  xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <SolutionPath>$(MSBuildThisFileDirectory)*.slnx</SolutionPath>\n  </PropertyGroup>\n  <ItemGroup>\n    <!--\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Debug;Platform=x86</AdditionalProperties>   \n    </ProjectReference>\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Release;Platform=x86</AdditionalProperties>   \n    </ProjectReference>\n    -->\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Debug;Platform=x64</AdditionalProperties>   \n    </ProjectReference>\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Release;Platform=x64</AdditionalProperties>   \n    </ProjectReference>\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Debug;Platform=ARM64</AdditionalProperties>   \n    </ProjectReference>\n    <ProjectReference Include=\"$(SolutionPath)\">\n      <AdditionalProperties>Configuration=Release;Platform=ARM64</AdditionalProperties>   \n    </ProjectReference>\n  </ItemGroup>\n  <ItemGroup>\n    <!-- <PackagingProjectReference Include=\"$(MSBuildThisFileDirectory)Mile.Project.Windows\\Mile.Project.NuGetPackaging.proj\">\n      <AdditionalProperties>NuspecFile=$(MSBuildThisFileDirectory)SampleProject\\SampleProject.nuspec</AdditionalProperties>   \n    </PackagingProjectReference> -->\n  </ItemGroup>\n  <Target Name=\"Restore\" >\n    <MSBuild\n      Projects=\"@(ProjectReference)\"\n      Targets=\"Restore\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n    <MSBuild\n      Projects=\"@(PackagingProjectReference)\"\n      Targets=\"Restore\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n  </Target>\n  <Target Name=\"Build\" >\n    <MSBuild\n      Projects=\"@(ProjectReference)\"\n      Targets=\"Build\"\n      BuildInParallel=\"True\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n    <MSBuild\n      Projects=\"@(PackagingProjectReference)\"\n      Targets=\"Build\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n  </Target>\n  <Target Name=\"Rebuild\" >\n    <MSBuild\n      Projects=\"@(ProjectReference)\"\n      Targets=\"Rebuild\"\n      BuildInParallel=\"True\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n    <MSBuild\n      Projects=\"@(PackagingProjectReference)\"\n      Targets=\"Rebuild\"\n      StopOnFirstFailure=\"True\"\n      Properties=\"PreferredToolArchitecture=x64\" />\n  </Target>\n</Project>"
  },
  {
    "path": "Detours/api_thunks.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Detours Disassembler (disasm.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#if defined(_KERNEL_MODE)\n#define DETOURS_KERNEL\n#endif\n\n#include <Veil.h>\n\n#ifdef DETOURS_KERNEL\n\nnamespace Detours::Thunks\n{\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n// Zw\n\nNTSTATUS NTAPI ZwReadVirtualMemory(\n    _In_ HANDLE ProcessHandle,\n    _In_opt_ PVOID BaseAddress,\n    _Out_writes_bytes_(BufferSize) PVOID Buffer,\n    _In_ SIZE_T BufferSize,\n    _Out_opt_ PSIZE_T NumberOfBytesRead\n)\n{\n    SIZE_T BytesCopied = 0u;\n    KPROCESSOR_MODE PreviousMode = KernelMode;\n    PEPROCESS Process = NULL;\n    NTSTATUS Status = STATUS_UNSUCCESSFUL;\n    PETHREAD CurrentThread = NULL;\n\n    PAGED_CODE();\n\n    CurrentThread = PsGetCurrentThread();\n\n    //\n    // If the buffer size is not zero, then attempt to read data from the\n    // specified process address space into the current process address\n    // space.\n    //\n\n    BytesCopied = 0;\n    Status = STATUS_SUCCESS;\n    if (BufferSize != 0) {\n\n        //\n        // Reference the target process.\n        //\n\n        Status = ObReferenceObjectByHandle(\n            ProcessHandle,\n            PROCESS_VM_READ,\n            *PsProcessType,\n            PreviousMode,\n            (PVOID*)&Process,\n            NULL);\n\n        //\n        // If the process was successfully referenced, then attempt to\n        // read the specified memory either by direct mapping or copying\n        // through nonpaged pool.\n        //\n\n        if (Status == STATUS_SUCCESS) {\n\n            Status = MmCopyVirtualMemory(\n                Process,\n                BaseAddress,\n                PsGetThreadProcess(CurrentThread),\n                Buffer,\n                BufferSize,\n                PreviousMode,\n                &BytesCopied);\n\n            //\n            // Dereference the target process.\n            //\n\n            ObDereferenceObject(Process);\n        }\n    }\n\n    //\n    // If requested, return the number of bytes read.\n    //\n\n    if (ARGUMENT_PRESENT(NumberOfBytesRead)) {\n        __try {\n            *NumberOfBytesRead = BytesCopied;\n\n        } __except(EXCEPTION_EXECUTE_HANDLER) {\n            NOTHING;\n        }\n    }\n\n    return Status;\n}\n\nNTSTATUS NTAPI ZwWriteVirtualMemory(\n    _In_ HANDLE ProcessHandle,\n    _In_opt_ PVOID BaseAddress,\n    _In_reads_bytes_(BufferSize) PVOID Buffer,\n    _In_ SIZE_T BufferSize,\n    _Out_opt_ PSIZE_T NumberOfBytesWritten\n)\n{\n    SIZE_T BytesCopied = 0u;\n    KPROCESSOR_MODE PreviousMode = KernelMode;\n    PEPROCESS Process = NULL;\n    NTSTATUS Status = STATUS_UNSUCCESSFUL;\n    PETHREAD CurrentThread = NULL;\n\n    PAGED_CODE();\n\n    CurrentThread = PsGetCurrentThread();\n\n    //\n    // If the buffer size is not zero, then attempt to write data from the\n    // current process address space into the target process address space.\n    //\n\n    BytesCopied = 0;\n    Status = STATUS_SUCCESS;\n    if (BufferSize != 0) {\n\n        //\n        // Reference the target process.\n        //\n\n        Status = ObReferenceObjectByHandle(\n            ProcessHandle,\n            PROCESS_VM_WRITE,\n            *PsProcessType,\n            PreviousMode,\n            (PVOID*)&Process,\n            NULL);\n\n        //\n        // If the process was successfully referenced, then attempt to\n        // write the specified memory either by direct mapping or copying\n        // through nonpaged pool.\n        //\n\n        if (Status == STATUS_SUCCESS) {\n\n            Status = MmCopyVirtualMemory(\n                PsGetThreadProcess(CurrentThread),\n                Buffer,\n                Process,\n                BaseAddress,\n                BufferSize,\n                PreviousMode,\n                &BytesCopied);\n\n            //\n            // Dereference the target process.\n            //\n\n            ObDereferenceObject(Process);\n        }\n    }\n\n    //\n    // If requested, return the number of bytes read.\n    //\n\n    if (ARGUMENT_PRESENT(NumberOfBytesWritten)) {\n        __try {\n            *NumberOfBytesWritten = BytesCopied;\n\n        } __except(EXCEPTION_EXECUTE_HANDLER) {\n            NOTHING;\n        }\n    }\n\n    return Status;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n// Set/Get Last Error\n\nstatic DWORD __Win32Error = NO_ERROR;\n\nVOID WINAPI SetLastError(_In_ DWORD Win32Error)\n{\n    __Win32Error = Win32Error;\n}\n\nDWORD WINAPI GetLastError(VOID)\n{\n    return __Win32Error;\n}\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n// Process & Thread\n\nHANDLE WINAPI GetCurrentProcess(VOID)\n{\n    return ZwCurrentProcess();\n}\n\nHANDLE WINAPI GetCurrentThread(VOID)\n{\n    return ZwCurrentThread();\n}\n\nDWORD WINAPI GetCurrentProcessId(VOID)\n{\n    return (DWORD)(ULONG_PTR)PsGetCurrentProcessId();\n}\n\nDWORD WINAPI GetCurrentThreadId(VOID)\n{\n    return (DWORD)(ULONG_PTR)PsGetCurrentThreadId();\n}\n\nBOOL WINAPI IsWow64Process(\n    _In_ HANDLE hProcess,\n    _Out_ PBOOL Wow64Process\n)\n{\n    NTSTATUS NtStatus = STATUS_UNSUCCESSFUL;\n    ULONG_PTR Peb32 = 0u;\n\n    NtStatus = ZwQueryInformationProcess(\n        hProcess,\n        ProcessWow64Information,\n        &Peb32,\n        sizeof(Peb32),\n        NULL\n    );\n\n    *Wow64Process = FALSE;\n\n    if (!NT_SUCCESS(NtStatus)) {\n\n        SetLastError(NtStatus);\n    }\n    else {\n\n        if (Peb32 == 0) {\n            *Wow64Process = FALSE;\n        }\n        else {\n            *Wow64Process = TRUE;\n        }\n    }\n\n    return (NT_SUCCESS(NtStatus));\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI ReadProcessMemory(\n    _In_ HANDLE hProcess,\n    _In_ LPCVOID lpBaseAddress,\n    _Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,\n    _In_ SIZE_T nSize,\n    _Out_opt_ SIZE_T * lpNumberOfBytesRead\n)\n{\n    NTSTATUS Status = STATUS_UNSUCCESSFUL;\n    SIZE_T NtNumberOfBytesRead = 0u;\n\n    Status = ZwReadVirtualMemory(\n        hProcess,\n        (PVOID)lpBaseAddress,\n        lpBuffer,\n        nSize,\n        &NtNumberOfBytesRead\n    );\n\n    if (lpNumberOfBytesRead != NULL) {\n        *lpNumberOfBytesRead = NtNumberOfBytesRead;\n    }\n\n    if (!NT_SUCCESS(Status)) {\n        SetLastError(Status);\n        return FALSE;\n    }\n    else {\n        return TRUE;\n    }\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI WriteProcessMemory(\n    _In_ HANDLE hProcess,\n    _In_ LPVOID lpBaseAddress,\n    _In_reads_bytes_(nSize) LPCVOID lpBuffer,\n    _In_ SIZE_T nSize,\n    _Out_opt_ SIZE_T * lpNumberOfBytesWritten\n)\n{\n    NTSTATUS Status;\n    ULONG OldProtect = PAGE_READWRITE;\n    SIZE_T RegionSize;\n    PVOID Base;\n    SIZE_T NtNumberOfBytesWritten = 0u;\n\n    //\n    // Set the protection to allow writes\n    //\n\n    RegionSize = nSize;\n    Base = lpBaseAddress;\n    Status = ZwProtectVirtualMemory(\n        hProcess,\n        &Base,\n        &RegionSize,\n        PAGE_READWRITE,\n        &OldProtect\n    );\n    if (NT_SUCCESS(Status)) {\n\n        //\n        // See if previous protection was writable. If so,\n        // then reset protection and do the write.\n        // Otherwise, see if previous protection was read-only or\n        // no access. In this case, don't do the write, just fail\n        //\n\n        if ((OldProtect & PAGE_READWRITE) == PAGE_READWRITE ||\n            (OldProtect & PAGE_WRITECOPY) == PAGE_WRITECOPY ||\n            (OldProtect & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE ||\n            (OldProtect & PAGE_EXECUTE_WRITECOPY) == PAGE_EXECUTE_WRITECOPY) {\n\n            (void)ZwProtectVirtualMemory(\n                hProcess,\n                &Base,\n                &RegionSize,\n                OldProtect,\n                &OldProtect\n            );\n            Status = ZwWriteVirtualMemory(\n                hProcess,\n                lpBaseAddress,\n                (PVOID)lpBuffer,\n                nSize,\n                &NtNumberOfBytesWritten\n            );\n\n            if (lpNumberOfBytesWritten != NULL) {\n                *lpNumberOfBytesWritten = NtNumberOfBytesWritten;\n            }\n\n            if (!NT_SUCCESS(Status)) {\n                SetLastError(Status);\n                return FALSE;\n            }\n            ZwFlushInstructionCache(hProcess, lpBaseAddress, nSize);\n            return TRUE;\n        }\n        else {\n\n            //\n            // See if the previous protection was read only or no access. If\n            // this is the case, restore the previous protection and return\n            // an access violation error.\n            //\n            if ((OldProtect & PAGE_NOACCESS) == PAGE_NOACCESS ||\n                (OldProtect & PAGE_READONLY) == PAGE_READONLY) {\n\n                (void)ZwProtectVirtualMemory(\n                    hProcess,\n                    &Base,\n                    &RegionSize,\n                    OldProtect,\n                    &OldProtect\n                );\n                SetLastError((DWORD)STATUS_ACCESS_VIOLATION);\n                return FALSE;\n            }\n            else {\n\n                //\n                // The previous protection must have been code and the caller\n                // is trying to set a breakpoint or edit the code. Do the write\n                // and then restore the previous protection.\n                //\n\n                Status = ZwWriteVirtualMemory(\n                    hProcess,\n                    lpBaseAddress,\n                    (PVOID)lpBuffer,\n                    nSize,\n                    &NtNumberOfBytesWritten\n                );\n\n                if (lpNumberOfBytesWritten != NULL) {\n                    *lpNumberOfBytesWritten = NtNumberOfBytesWritten;\n                }\n\n                (void)ZwProtectVirtualMemory(\n                    hProcess,\n                    &Base,\n                    &RegionSize,\n                    OldProtect,\n                    &OldProtect\n                );\n                if (!NT_SUCCESS(Status)) {\n                    SetLastError((DWORD)STATUS_ACCESS_VIOLATION);\n                    return FALSE;\n                }\n                (void)ZwFlushInstructionCache(hProcess, lpBaseAddress, nSize);\n                return TRUE;\n            }\n        }\n    }\n    else {\n        SetLastError(Status);\n        return FALSE;\n    }\n}\n\n_Ret_maybenull_\n_Post_writable_byte_size_(dwSize)\nLPVOID WINAPI VirtualAllocEx(\n    _In_ HANDLE hProcess,\n    _In_opt_ LPVOID lpAddress,\n    _In_ SIZE_T dwSize,\n    _In_ DWORD flAllocationType,\n    _In_ DWORD flProtect\n)\n{\n    NTSTATUS Status;\n\n    __try {\n        Status = NtAllocateVirtualMemory(hProcess,\n            &lpAddress,\n            0,\n            &dwSize,\n            flAllocationType,\n            flProtect\n        );\n    }\n    __except (EXCEPTION_EXECUTE_HANDLER) {\n        Status = GetExceptionCode();\n    }\n\n    if (NT_SUCCESS(Status)) {\n        return(lpAddress);\n    }\n\n    SetLastError(Status);\n    return NULL;\n}\n\nBOOL WINAPI VirtualFreeEx(\n    _In_ HANDLE hProcess,\n    _Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,\n    _In_ SIZE_T dwSize,\n    _In_ DWORD dwFreeType\n)\n{\n    NTSTATUS Status;\n\n    if ((dwFreeType & MEM_RELEASE) && dwSize != 0) {\n        SetLastError((DWORD)STATUS_INVALID_PARAMETER);\n        return FALSE;\n    }\n\n    Status = NtFreeVirtualMemory(\n        hProcess,\n        &lpAddress,\n        &dwSize,\n        dwFreeType\n    );\n\n    if (NT_SUCCESS(Status)) {\n        return(TRUE);\n    }\n\n    SetLastError(Status);\n    return FALSE;\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI VirtualProtectEx(\n    _In_ HANDLE hProcess,\n    _In_ LPVOID lpAddress,\n    _In_ SIZE_T dwSize,\n    _In_ DWORD flNewProtect,\n    _Out_ PDWORD lpflOldProtect)\n{\n    NTSTATUS Status;\n\n    Status = ZwProtectVirtualMemory(\n        hProcess,\n        &lpAddress,\n        &dwSize,\n        flNewProtect,\n        lpflOldProtect);\n\n    if (NT_SUCCESS(Status)) {\n        return(TRUE);\n    }\n\n    SetLastError(Status);\n    return FALSE;\n}\n\nSIZE_T WINAPI VirtualQueryEx(\n    _In_ HANDLE hProcess,\n    _In_opt_ LPCVOID lpAddress,\n    _Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,\n    _In_ SIZE_T dwLength)\n{\n    NTSTATUS Status;\n    SIZE_T ReturnLength = 0u;\n\n    Status = ZwQueryVirtualMemory(\n        hProcess,\n        (LPVOID)lpAddress,\n        MemoryBasicInformation,\n        (PMEMORY_BASIC_INFORMATION)lpBuffer,\n        dwLength,\n        &ReturnLength);\n    if (NT_SUCCESS(Status))\n    {\n        return(ReturnLength);\n    }\n\n    SetLastError(Status);\n    return 0;\n}\n\n}\n\n#endif // DETOURS_KERNEL\n"
  },
  {
    "path": "Detours/api_thunks.h",
    "content": "#pragma once\n\nnamespace Detours::Thunks\n{\n    VOID WINAPI SetLastError(_In_ DWORD Win32Error);\n    DWORD WINAPI GetLastError(VOID);\n\n    HANDLE WINAPI GetCurrentProcess(VOID);\n    HANDLE WINAPI GetCurrentThread(VOID);\n    DWORD WINAPI GetCurrentProcessId(VOID);\n    DWORD WINAPI GetCurrentThreadId(VOID);\n\n    BOOL WINAPI IsWow64Process(\n        _In_ HANDLE hProcess,\n        _Out_ PBOOL Wow64Process\n    );\n\n    BOOL WINAPI ReadProcessMemory(\n        _In_ HANDLE hProcess,\n        _In_ LPCVOID lpBaseAddress,\n        _Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,\n        _In_ SIZE_T nSize,\n        _Out_opt_ SIZE_T * lpNumberOfBytesRead\n    );\n\n    BOOL WINAPI WriteProcessMemory(\n        _In_ HANDLE hProcess,\n        _In_ LPVOID lpBaseAddress,\n        _In_reads_bytes_(nSize) LPCVOID lpBuffer,\n        _In_ SIZE_T nSize,\n        _Out_opt_ SIZE_T* lpNumberOfBytesWritten\n    );\n\n    LPVOID WINAPI VirtualAllocEx(\n        _In_ HANDLE hProcess,\n        _In_opt_ LPVOID lpAddress,\n        _In_ SIZE_T dwSize,\n        _In_ DWORD flAllocationType,\n        _In_ DWORD flProtect\n    );\n\n    BOOL WINAPI VirtualFreeEx(\n        _In_ HANDLE hProcess,\n        _Pre_notnull_ _When_(dwFreeType == MEM_DECOMMIT, _Post_invalid_) _When_(dwFreeType == MEM_RELEASE, _Post_ptr_invalid_) LPVOID lpAddress,\n        _In_ SIZE_T dwSize,\n        _In_ DWORD dwFreeType\n    );\n\n    BOOL WINAPI VirtualProtectEx(\n        _In_ HANDLE hProcess,\n        _In_ LPVOID lpAddress,\n        _In_ SIZE_T dwSize,\n        _In_ DWORD flNewProtect,\n        _Out_ PDWORD lpflOldProtect\n    );\n\n    SIZE_T WINAPI VirtualQueryEx(\n        _In_ HANDLE hProcess,\n        _In_opt_ LPCVOID lpAddress,\n        _Out_writes_bytes_to_(dwLength, return) PMEMORY_BASIC_INFORMATION lpBuffer,\n        _In_ SIZE_T dwLength\n    );\n\n}\n\n#define SetLastError ::Detours::Thunks::SetLastError\n#define GetLastError ::Detours::Thunks::GetLastError\n\n#define GetCurrentProcess ::Detours::Thunks::GetCurrentProcess\n#define GetCurrentThread ::Detours::Thunks::GetCurrentThread\n#define GetCurrentProcessId ::Detours::Thunks::GetCurrentProcessId\n#define GetCurrentThreadId ::Detours::Thunks::GetCurrentThreadId\n\n#define IsWow64Process ::Detours::Thunks::IsWow64Process\n\n#define ReadProcessMemory ::Detours::Thunks::ReadProcessMemory\n#define WriteProcessMemory ::Detours::Thunks::WriteProcessMemory\n\n#define VirtualAllocEx ::Detours::Thunks::VirtualAllocEx\n#define VirtualFreeEx ::Detours::Thunks::VirtualFreeEx\n#define VirtualProtectEx ::Detours::Thunks::VirtualProtectEx\n#define VirtualQueryEx ::Detours::Thunks::VirtualQueryEx\n"
  },
  {
    "path": "Detours/creatwth.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Create a process with a DLL (creatwth.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#if defined(_KERNEL_MODE)\n#define DETOURS_KERNEL\n#endif\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n\n#include <Veil.h>\n\n#ifdef DETOURS_KERNEL\n#include \"api_thunks.h\"\n#endif\n\n#include <stddef.h>\n#pragma warning(push)\n#if _MSC_VER > 1400\n#pragma warning(disable:6102 6103) // /analyze warnings\n#endif\n#include <strsafe.h>\n#pragma warning(pop)\n\n// #define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\n#define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]\n#define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT]\n#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]\n#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]\n\n//////////////////////////////////////////////////////////////////////////////\n//\nconst GUID DETOUR_EXE_RESTORE_GUID = {\n    0x2ed7a3ff, 0x3339, 0x4a8d,\n    { 0x80, 0x5c, 0xd4, 0x98, 0x15, 0x3f, 0xc2, 0x8f } };\n\nconst GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */\n    0xea0251b9, 0x5cde, 0x41b5,\n    { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }};\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Enumate through modules in the target process.\n//\nstatic BOOL WINAPI LoadNtHeaderFromProcess(HANDLE hProcess,\n                                           HMODULE hModule,\n                                           PIMAGE_NT_HEADERS32 pNtHeader)\n{\n    PBYTE pbModule = (PBYTE)hModule;\n\n    if (pbModule == NULL) {\n        SetLastError(ERROR_INVALID_PARAMETER);\n        return FALSE;\n    }\n\n    MEMORY_BASIC_INFORMATION mbi;\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) {\n        return FALSE;\n    }\n\n    IMAGE_DOS_HEADER idh;\n\n    if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(idh@%p..%p) failed: %lu\\n\",\n                      pbModule, pbModule + sizeof(idh), GetLastError()));\n        return FALSE;\n    }\n\n    if (idh.e_magic != IMAGE_DOS_SIGNATURE ||\n        (DWORD)idh.e_lfanew > mbi.RegionSize ||\n        (DWORD)idh.e_lfanew < sizeof(idh)) {\n\n        SetLastError(ERROR_BAD_EXE_FORMAT);\n        return FALSE;\n    }\n\n    if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew,\n                           pNtHeader, sizeof(*pNtHeader), NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p:%p) failed: %lu\\n\",\n                      pbModule + idh.e_lfanew,\n                      pbModule + idh.e_lfanew + sizeof(*pNtHeader),\n                      pbModule,\n                      GetLastError()));\n        return FALSE;\n    }\n\n    if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n        SetLastError(ERROR_BAD_EXE_FORMAT);\n        return FALSE;\n    }\n\n    return TRUE;\n}\n\nstatic HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess,\n                                                HMODULE hModuleLast,\n                                                PIMAGE_NT_HEADERS32 pNtHeader)\n{\n    PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;\n\n    MEMORY_BASIC_INFORMATION mbi;\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    // Find the next memory region that contains a mapped PE image.\n    //\n\n    for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {\n        if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {\n            break;\n        }\n\n        // Usermode address space has such an unaligned region size always at the\n        // end and only at the end.\n        //\n        if ((mbi.RegionSize & 0xfff) == 0xfff) {\n            break;\n        }\n        if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) {\n            break;\n        }\n\n        // Skip uncommitted regions and guard pages.\n        //\n        if ((mbi.State != MEM_COMMIT) ||\n            ((mbi.Protect & 0xff) == PAGE_NOACCESS) ||\n            (mbi.Protect & PAGE_GUARD)) {\n            continue;\n        }\n\n        if (LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader)) {\n            return (HMODULE)pbLast;\n        }\n    }\n    return NULL;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Find a region of memory in which we can create a replacement import table.\n//\nstatic PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbBase, DWORD cbAlloc)\n{\n    MEMORY_BASIC_INFORMATION mbi;\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    PBYTE pbLast = pbBase;\n    for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {\n\n        ZeroMemory(&mbi, sizeof(mbi));\n        if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) {\n            if (GetLastError() == ERROR_INVALID_PARAMETER) {\n                break;\n            }\n            DETOUR_TRACE((\"VirtualQueryEx(%p) failed: %lu\\n\",\n                          pbLast, GetLastError()));\n            break;\n        }\n        // Usermode address space has such an unaligned region size always at the\n        // end and only at the end.\n        //\n        if ((mbi.RegionSize & 0xfff) == 0xfff) {\n            break;\n        }\n\n        // Skip anything other than a pure free region.\n        //\n        if (mbi.State != MEM_FREE) {\n            continue;\n        }\n\n        PBYTE pbAddress = (PBYTE)(((DWORD_PTR)mbi.BaseAddress + 0xffff) & ~(DWORD_PTR)0xffff);\n\n#ifdef _WIN64\n        // The distance from pbBase to pbAddress must fit in 32 bits.\n        //\n        const size_t GB4 = ((((size_t)1) << 32) - 1);\n        if ((size_t)(pbAddress - pbBase) > GB4) {\n            DETOUR_TRACE((\"FindAndAllocateNearBase(1) failing due to distance >4GB %p\\n\", pbAddress));\n            return NULL;\n        }\n#endif\n\n        DETOUR_TRACE((\"Free region %p..%p\\n\",\n                      mbi.BaseAddress,\n                      (PBYTE)mbi.BaseAddress + mbi.RegionSize));\n\n        for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) {\n            PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc,\n                                                  MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);\n            if (pbAlloc == NULL) {\n                DETOUR_TRACE((\"VirtualAllocEx(%p) failed: %lu\\n\", pbAddress, GetLastError()));\n                continue;\n            }\n#ifdef _WIN64\n            // The distance from pbBase to pbAddress must fit in 32 bits.\n            //\n            if ((size_t)(pbAddress - pbBase) > GB4) {\n                DETOUR_TRACE((\"FindAndAllocateNearBase(2) failing due to distance >4GB %p\\n\", pbAddress));\n                return NULL;\n            }\n#endif\n            DETOUR_TRACE((\"[%p..%p] Allocated for import table.\\n\",\n                          pbAlloc, pbAlloc + cbAlloc));\n            return pbAlloc;\n        }\n    }\n    return NULL;\n}\n\nstatic inline DWORD PadToDword(DWORD dw)\n{\n    return (dw + 3) & ~3u;\n}\n\nstatic inline DWORD PadToDwordPtr(DWORD dw)\n{\n    return (dw + 7) & ~7u;\n}\n\nstatic inline HRESULT ReplaceOptionalSizeA(_Inout_z_count_(cchDest) LPSTR pszDest,\n                                           _In_ size_t cchDest,\n                                           _In_z_ LPCSTR pszSize)\n{\n    if (cchDest == 0 || pszDest == NULL || pszSize == NULL ||\n        pszSize[0] == '\\0' || pszSize[1] == '\\0' || pszSize[2] != '\\0') {\n\n        // can not write into empty buffer or with string other than two chars.\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    for (; cchDest >= 2; cchDest--, pszDest++) {\n        if (pszDest[0] == '?' && pszDest[1] == '?') {\n            pszDest[0] = pszSize[0];\n            pszDest[1] = pszSize[1];\n            break;\n        }\n    }\n\n    return S_OK;\n}\n\nstatic BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der)\n{\n    // Save the various headers for DetourRestoreAfterWith.\n    ZeroMemory(&der, sizeof(der));\n    der.cb = sizeof(der);\n\n    der.pidh = (PBYTE)hModule;\n    der.cbidh = sizeof(der.idh);\n    if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(idh@%p..%p) failed: %lu\\n\",\n                      der.pidh, der.pidh + der.cbidh, GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"IDH: %p..%p\\n\", der.pidh, der.pidh + der.cbidh));\n\n    // We read the NT header in two passes to get the full size.\n    // First we read just the Signature and FileHeader.\n    der.pinh = der.pidh + der.idh.e_lfanew;\n    der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader);\n    if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p) failed: %lu\\n\",\n                      der.pinh, der.pinh + der.cbinh, GetLastError()));\n        return FALSE;\n    }\n\n    // Second we read the OptionalHeader and Section headers.\n    der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +\n                 der.inh.FileHeader.SizeOfOptionalHeader +\n                 der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));\n\n    if (der.cbinh > sizeof(der.raw)) {\n        return FALSE;\n    }\n\n    if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p) failed: %lu\\n\",\n                      der.pinh, der.pinh + der.cbinh, GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"INH: %p..%p\\n\", der.pinh, der.pinh + der.cbinh));\n\n    // Third, we read the CLR header\n\n    if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\n        if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 &&\n            der.inh32.CLR_DIRECTORY.Size != 0) {\n\n            DETOUR_TRACE((\"CLR32.VirtAddr=%lx, CLR.Size=%lx\\n\",\n                          der.inh32.CLR_DIRECTORY.VirtualAddress,\n                          der.inh32.CLR_DIRECTORY.Size));\n\n            der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress;\n        }\n    }\n    else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\n        if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 &&\n            der.inh64.CLR_DIRECTORY.Size != 0) {\n\n            DETOUR_TRACE((\"CLR64.VirtAddr=%lx, CLR.Size=%lx\\n\",\n                          der.inh64.CLR_DIRECTORY.VirtualAddress,\n                          der.inh64.CLR_DIRECTORY.Size));\n\n            der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress;\n        }\n    }\n\n    if (der.pclr != 0) {\n        der.cbclr = sizeof(der.clr);\n        if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) {\n            DETOUR_TRACE((\"ReadProcessMemory(clr@%p..%p) failed: %lu\\n\",\n                          der.pclr, der.pclr + der.cbclr, GetLastError()));\n            return FALSE;\n        }\n        DETOUR_TRACE((\"CLR: %p..%p\\n\", der.pclr, der.pclr + der.cbclr));\n    }\n\n    return TRUE;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n#if DETOURS_32BIT\n#define DWORD_XX                        DWORD32\n#define IMAGE_NT_HEADERS_XX             IMAGE_NT_HEADERS32\n#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX  IMAGE_NT_OPTIONAL_HDR32_MAGIC\n#define IMAGE_ORDINAL_FLAG_XX           IMAGE_ORDINAL_FLAG32\n#define UPDATE_IMPORTS_XX               UpdateImports32\n#define DETOURS_BITS_XX                 32\n#include \"uimports.cpp\"\n#undef DETOUR_EXE_RESTORE_FIELD_XX\n#undef DWORD_XX\n#undef IMAGE_NT_HEADERS_XX\n#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX\n#undef IMAGE_ORDINAL_FLAG_XX\n#undef UPDATE_IMPORTS_XX\n#endif // DETOURS_32BIT\n\n#if DETOURS_64BIT\n#define DWORD_XX                        DWORD64\n#define IMAGE_NT_HEADERS_XX             IMAGE_NT_HEADERS64\n#define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX  IMAGE_NT_OPTIONAL_HDR64_MAGIC\n#define IMAGE_ORDINAL_FLAG_XX           IMAGE_ORDINAL_FLAG64\n#define UPDATE_IMPORTS_XX               UpdateImports64\n#define DETOURS_BITS_XX                 64\n#include \"uimports.cpp\"\n#undef DETOUR_EXE_RESTORE_FIELD_XX\n#undef DWORD_XX\n#undef IMAGE_NT_HEADERS_XX\n#undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX\n#undef IMAGE_ORDINAL_FLAG_XX\n#undef UPDATE_IMPORTS_XX\n#endif // DETOURS_64BIT\n\n//////////////////////////////////////////////////////////////////////////////\n//\n#if DETOURS_64BIT\n\nC_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16);\n\nstatic BOOL UpdateFrom32To64(HANDLE hProcess, HMODULE hModule, WORD machine,\n                             DETOUR_EXE_RESTORE& der)\n{\n    IMAGE_DOS_HEADER idh;\n    IMAGE_NT_HEADERS32 inh32;\n    IMAGE_NT_HEADERS64 inh64;\n    IMAGE_SECTION_HEADER sects[32];\n    PBYTE pbModule = (PBYTE)hModule;\n    DWORD n;\n\n    ZeroMemory(&inh32, sizeof(inh32));\n    ZeroMemory(&inh64, sizeof(inh64));\n    ZeroMemory(sects, sizeof(sects));\n\n    DETOUR_TRACE((\"UpdateFrom32To64(%04x)\\n\", machine));\n    //////////////////////////////////////////////////////// Read old headers.\n    //\n    if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(idh@%p..%p) failed: %lu\\n\",\n                      pbModule, pbModule + sizeof(idh), GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"ReadProcessMemory(idh@%p..%p)\\n\",\n                  pbModule, pbModule + sizeof(idh)));\n\n    PBYTE pnh = pbModule + idh.e_lfanew;\n    if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p) failed: %lu\\n\",\n                      pnh, pnh + sizeof(inh32), GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p)\\n\", pnh, pnh + sizeof(inh32)));\n\n    if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) {\n        return FALSE;\n    }\n\n    PBYTE psects = pnh +\n        FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +\n        inh32.FileHeader.SizeOfOptionalHeader;\n    ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);\n    if (!ReadProcessMemory(hProcess, psects, &sects, cb, NULL)) {\n        DETOUR_TRACE((\"ReadProcessMemory(ish@%p..%p) failed: %lu\\n\",\n                      psects, psects + cb, GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"ReadProcessMemory(ish@%p..%p)\\n\", psects, psects + cb));\n\n    ////////////////////////////////////////////////////////// Convert header.\n    //\n    inh64.Signature = inh32.Signature;\n    inh64.FileHeader = inh32.FileHeader;\n    inh64.FileHeader.Machine = machine;\n    inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);\n\n    inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;\n    inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion;\n    inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion;\n    inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode;\n    inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData;\n    inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData;\n    inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint;\n    inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode;\n    inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase;\n    inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment;\n    inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment;\n    inh64.OptionalHeader.MajorOperatingSystemVersion\n        = inh32.OptionalHeader.MajorOperatingSystemVersion;\n    inh64.OptionalHeader.MinorOperatingSystemVersion\n        = inh32.OptionalHeader.MinorOperatingSystemVersion;\n    inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion;\n    inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion;\n    inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion;\n    inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion;\n    inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue;\n    inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage;\n    inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders;\n    inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum;\n    inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem;\n    inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics;\n    inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve;\n    inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit;\n    inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve;\n    inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit;\n    inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags;\n    inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes;\n    for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) {\n        inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n];\n    }\n\n    /////////////////////////////////////////////////////// Write new headers.\n    //\n    DWORD dwProtect = 0;\n    if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,\n                                           PAGE_EXECUTE_READWRITE, &dwProtect)) {\n        return FALSE;\n    }\n\n    if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {\n        DETOUR_TRACE((\"WriteProcessMemory(inh@%p..%p) failed: %lu\\n\",\n                      pnh, pnh + sizeof(inh64), GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"WriteProcessMemory(inh@%p..%p)\\n\", pnh, pnh + sizeof(inh64)));\n\n    psects = pnh +\n        FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) +\n        inh64.FileHeader.SizeOfOptionalHeader;\n    cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);\n    if (!WriteProcessMemory(hProcess, psects, &sects, cb, NULL)) {\n        DETOUR_TRACE((\"WriteProcessMemory(ish@%p..%p) failed: %lu\\n\",\n                      psects, psects + cb, GetLastError()));\n        return FALSE;\n    }\n    DETOUR_TRACE((\"WriteProcessMemory(ish@%p..%p)\\n\", psects, psects + cb));\n\n    // Record the updated headers.\n    if (!RecordExeRestore(hProcess, hModule, der)) {\n        return FALSE;\n    }\n\n    // Remove the import table.\n    if (der.pclr != NULL && (der.clr.Flags & 1)) {\n        inh64.IMPORT_DIRECTORY.VirtualAddress = 0;\n        inh64.IMPORT_DIRECTORY.Size = 0;\n\n        if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) {\n            DETOUR_TRACE((\"WriteProcessMemory(inh@%p..%p) failed: %lu\\n\",\n                          pnh, pnh + sizeof(inh64), GetLastError()));\n            return FALSE;\n        }\n    }\n\n    DWORD dwOld = 0;\n    if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders,\n                          dwProtect, &dwOld)) {\n        return FALSE;\n    }\n\n    return TRUE;\n}\n#endif // DETOURS_64BIT\n\n//////////////////////////////////////////////////////////////////////////////\n//\nBOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,\n                                       _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                       _In_ DWORD nDlls)\n{\n    // Find the next memory region that contains a mapped PE image.\n    //\n    BOOL bHas64BitDll = FALSE;\n    BOOL bHas32BitExe = FALSE;\n    BOOL bIs32BitProcess;\n    HMODULE hModule = NULL;\n    HMODULE hLast = NULL;\n\n    DETOUR_TRACE((\"DetourUpdateProcessWithDll(%p,dlls=%lu)\\n\", hProcess, nDlls));\n\n    for (;;) {\n        IMAGE_NT_HEADERS32 inh;\n\n        if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == NULL) {\n            break;\n        }\n\n        DETOUR_TRACE((\"%p  machine=%04x magic=%04x\\n\",\n                      (void*)hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic));\n\n        if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {\n            hModule = hLast;\n            if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC\n                && inh.FileHeader.Machine != 0) {\n\n                bHas32BitExe = TRUE;\n            }\n            DETOUR_TRACE((\"%p  Found EXE\\n\", (void*)hLast));\n        }\n        else {\n            if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC\n                && inh.FileHeader.Machine != 0) {\n\n                bHas64BitDll = TRUE;\n            }\n        }\n    }\n\n    if (hModule == NULL) {\n        SetLastError(ERROR_INVALID_OPERATION);\n        return FALSE;\n    }\n\n    if (!bHas32BitExe) {\n        bIs32BitProcess = FALSE;\n    }\n    else if (!bHas64BitDll) {\n        bIs32BitProcess = TRUE;\n    }\n    else {\n        if (!IsWow64Process(hProcess, &bIs32BitProcess)) {\n            return FALSE;\n        }\n    }\n\n    DETOUR_TRACE((\"    32BitExe=%d 32BitProcess=%d\\n\", bHas32BitExe, bIs32BitProcess));\n\n    return DetourUpdateProcessWithDllEx(hProcess,\n                                        hModule,\n                                        bIs32BitProcess,\n                                        rlpDlls,\n                                        nDlls);\n}\n\nBOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,\n                                         _In_ HMODULE hModule,\n                                         _In_ BOOL bIs32BitProcess,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_ DWORD nDlls)\n{\n    // Find the next memory region that contains a mapped PE image.\n    //\n    BOOL bIs32BitExe = FALSE;\n\n    DETOUR_TRACE((\"DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\\n\", hProcess, (void*)hModule, nDlls));\n\n    IMAGE_NT_HEADERS32 inh;\n\n    if (hModule == NULL || LoadNtHeaderFromProcess(hProcess, hModule, &inh) == FALSE) {\n        SetLastError(ERROR_INVALID_OPERATION);\n        return FALSE;\n    }\n\n    if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC\n        && inh.FileHeader.Machine != 0) {\n\n        bIs32BitExe = TRUE;\n    }\n\n    DETOUR_TRACE((\"    32BitExe=%d 32BitProcess=%d\\n\", bIs32BitExe, bIs32BitProcess));\n\n    if (hModule == NULL) {\n        SetLastError(ERROR_INVALID_OPERATION);\n        return FALSE;\n    }\n\n    // Save the various headers for DetourRestoreAfterWith.\n    //\n    DETOUR_EXE_RESTORE der;\n\n    if (!RecordExeRestore(hProcess, hModule, der)) {\n        return FALSE;\n    }\n\n#if defined(DETOURS_64BIT)\n    // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary.\n    if (bIs32BitExe && !bIs32BitProcess) {\n        if (!der.pclr                       // Native binary\n            || (der.clr.Flags & 1) == 0     // Or mixed-mode MSIL\n            || (der.clr.Flags & 2) != 0) {  // Or 32BIT Required MSIL\n\n            SetLastError(ERROR_INVALID_HANDLE);\n            return FALSE;\n        }\n\n        if (!UpdateFrom32To64(hProcess, hModule,\n#if defined(DETOURS_X64)\n                              IMAGE_FILE_MACHINE_AMD64,\n#elif defined(DETOURS_IA64)\n                              IMAGE_FILE_MACHINE_IA64,\n#elif defined(DETOURS_ARM64)\n                              IMAGE_FILE_MACHINE_ARM64,\n#else\n#error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit.\n#endif\n                              der)) {\n            return FALSE;\n        }\n        bIs32BitExe = FALSE;\n    }\n#endif // DETOURS_64BIT\n\n    // Now decide if we can insert the detour.\n\n#if defined(DETOURS_32BIT)\n    if (bIs32BitProcess) {\n        // 32-bit native or 32-bit managed process on any platform.\n        if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) {\n            return FALSE;\n        }\n    }\n    else {\n        // 64-bit native or 64-bit managed process.\n        //\n        // Can't detour a 64-bit process with 32-bit code.\n        // Note: This happens for 32-bit PE binaries containing only\n        // manage code that have been marked as 64-bit ready.\n        //\n        SetLastError(ERROR_INVALID_HANDLE);\n        return FALSE;\n    }\n#elif defined(DETOURS_64BIT)\n    if (bIs32BitProcess || bIs32BitExe) {\n        // Can't detour a 32-bit process with 64-bit code.\n        SetLastError(ERROR_INVALID_HANDLE);\n        return FALSE;\n    }\n    else {\n        // 64-bit native or 64-bit managed process on any platform.\n        if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) {\n            return FALSE;\n        }\n    }\n#else\n#pragma Must define one of DETOURS_32BIT or DETOURS_64BIT.\n#endif // DETOURS_64BIT\n\n    /////////////////////////////////////////////////// Update the CLR header.\n    //\n    if (der.pclr != NULL) {\n        DETOUR_CLR_HEADER clr;\n        CopyMemory(&clr, &der.clr, sizeof(clr));\n        clr.Flags &= 0xfffffffe;    // Clear the IL_ONLY flag.\n\n        DWORD dwProtect;\n        if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) {\n            DETOUR_TRACE((\"VirtualProtectEx(clr) write failed: %lu\\n\", GetLastError()));\n            return FALSE;\n        }\n\n        if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) {\n            DETOUR_TRACE((\"WriteProcessMemory(clr) failed: %lu\\n\", GetLastError()));\n            return FALSE;\n        }\n\n        if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) {\n            DETOUR_TRACE((\"VirtualProtectEx(clr) restore failed: %lu\\n\", GetLastError()));\n            return FALSE;\n        }\n        DETOUR_TRACE((\"CLR: %p..%p\\n\", der.pclr, der.pclr + der.cbclr));\n\n#if DETOURS_64BIT\n        if (der.clr.Flags & 0x2) { // Is the 32BIT Required Flag set?\n            // X64 never gets here because the process appears as a WOW64 process.\n            // However, on IA64, it doesn't appear to be a WOW process.\n            DETOUR_TRACE((\"CLR Requires 32-bit, %p...%p\\n\", der.pclr, der.pclr + der.cbclr));\n            SetLastError(ERROR_INVALID_HANDLE);\n            return FALSE;\n        }\n#endif // DETOURS_64BIT\n    }\n\n    //////////////////////////////// Save the undo data to the target process.\n    //\n    if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) {\n        DETOUR_TRACE((\"DetourCopyPayloadToProcess failed: %lu\\n\", GetLastError()));\n        return FALSE;\n    }\n    return TRUE;\n}\n\n#ifndef DETOURS_KERNEL\n//////////////////////////////////////////////////////////////////////////////\n//\nBOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,\n                                        _Inout_opt_ LPSTR lpCommandLine,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                        _In_ BOOL bInheritHandles,\n                                        _In_ DWORD dwCreationFlags,\n                                        _In_opt_ LPVOID lpEnvironment,\n                                        _In_opt_ LPCSTR lpCurrentDirectory,\n                                        _In_ LPSTARTUPINFOA lpStartupInfo,\n                                        _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                        _In_ LPCSTR lpDllName,\n                                        _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)\n{\n    DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);\n    PROCESS_INFORMATION pi;\n    BOOL fResult = FALSE;\n\n    if (pfCreateProcessA == NULL) {\n        pfCreateProcessA = CreateProcessA;\n    }\n\n    fResult = pfCreateProcessA(lpApplicationName,\n                               lpCommandLine,\n                               lpProcessAttributes,\n                               lpThreadAttributes,\n                               bInheritHandles,\n                               dwMyCreationFlags,\n                               lpEnvironment,\n                               lpCurrentDirectory,\n                               lpStartupInfo,\n                               &pi);\n\n    if (lpProcessInformation != NULL) {\n        CopyMemory(lpProcessInformation, &pi, sizeof(pi));\n    }\n\n    if (!fResult) {\n        return FALSE;\n    }\n\n    LPCSTR rlpDlls[2];\n    DWORD nDlls = 0;\n    if (lpDllName != NULL) {\n        rlpDlls[nDlls++] = lpDllName;\n    }\n\n    if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {\n        TerminateProcess(pi.hProcess, ~0u);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(pi.hThread);\n    }\n    return TRUE;\n}\n\n\nBOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,\n                                        _Inout_opt_ LPWSTR lpCommandLine,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                        _In_ BOOL bInheritHandles,\n                                        _In_ DWORD dwCreationFlags,\n                                        _In_opt_ LPVOID lpEnvironment,\n                                        _In_opt_ LPCWSTR lpCurrentDirectory,\n                                        _In_ LPSTARTUPINFOW lpStartupInfo,\n                                        _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                        _In_ LPCSTR lpDllName,\n                                        _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)\n{\n    DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED);\n    PROCESS_INFORMATION pi;\n\n    if (pfCreateProcessW == NULL) {\n        pfCreateProcessW = CreateProcessW;\n    }\n\n    BOOL fResult = pfCreateProcessW(lpApplicationName,\n                                    lpCommandLine,\n                                    lpProcessAttributes,\n                                    lpThreadAttributes,\n                                    bInheritHandles,\n                                    dwMyCreationFlags,\n                                    lpEnvironment,\n                                    lpCurrentDirectory,\n                                    lpStartupInfo,\n                                    &pi);\n\n    if (lpProcessInformation) {\n        CopyMemory(lpProcessInformation, &pi, sizeof(pi));\n    }\n\n    if (!fResult) {\n        return FALSE;\n    }\n\n    LPCSTR rlpDlls[2];\n    DWORD nDlls = 0;\n    if (lpDllName != NULL) {\n        rlpDlls[nDlls++] = lpDllName;\n    }\n\n    if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) {\n        TerminateProcess(pi.hProcess, ~0u);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(pi.hThread);\n    }\n    return TRUE;\n}\n#endif // DETOURS_KERNEL\n\nBOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,\n                                       _In_ REFGUID rguid,\n                                       _In_reads_bytes_(cbData) PVOID pvData,\n                                       _In_ DWORD cbData)\n{\n    DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) +\n                     sizeof(IMAGE_NT_HEADERS) +\n                     sizeof(IMAGE_SECTION_HEADER) +\n                     sizeof(DETOUR_SECTION_HEADER) +\n                     sizeof(DETOUR_SECTION_RECORD) +\n                     cbData);\n\n    PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal,\n                                         MEM_COMMIT, PAGE_READWRITE);\n    if (pbBase == NULL) {\n        DETOUR_TRACE((\"VirtualAllocEx(%lu) failed: %lu\\n\", cbTotal, GetLastError()));\n        return FALSE;\n    }\n\n    PBYTE pbTarget = pbBase;\n    IMAGE_DOS_HEADER idh;\n    IMAGE_NT_HEADERS inh;\n    IMAGE_SECTION_HEADER ish;\n    DETOUR_SECTION_HEADER dsh;\n    DETOUR_SECTION_RECORD dsr;\n    SIZE_T cbWrote = 0;\n\n    ZeroMemory(&idh, sizeof(idh));\n    idh.e_magic = IMAGE_DOS_SIGNATURE;\n    idh.e_lfanew = sizeof(idh);\n    if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) ||\n        cbWrote != sizeof(idh)) {\n        DETOUR_TRACE((\"WriteProcessMemory(idh) failed: %lu\\n\", GetLastError()));\n        return FALSE;\n    }\n    pbTarget += sizeof(idh);\n\n    ZeroMemory(&inh, sizeof(inh));\n    inh.Signature = IMAGE_NT_SIGNATURE;\n    inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader);\n    inh.FileHeader.Characteristics = IMAGE_FILE_DLL;\n    inh.FileHeader.NumberOfSections = 1;\n    inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC;\n    if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) ||\n        cbWrote != sizeof(inh)) {\n        return FALSE;\n    }\n    pbTarget += sizeof(inh);\n\n    ZeroMemory(&ish, sizeof(ish));\n    memcpy(ish.Name, \".detour\", sizeof(ish.Name));\n    ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase);\n    ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) +\n                         sizeof(DETOUR_SECTION_RECORD) +\n                         cbData);\n    if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) ||\n        cbWrote != sizeof(ish)) {\n        return FALSE;\n    }\n    pbTarget += sizeof(ish);\n\n    ZeroMemory(&dsh, sizeof(dsh));\n    dsh.cbHeaderSize = sizeof(dsh);\n    dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;\n    dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER);\n    dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) +\n                      sizeof(DETOUR_SECTION_RECORD) +\n                      cbData);\n    if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) ||\n        cbWrote != sizeof(dsh)) {\n        return FALSE;\n    }\n    pbTarget += sizeof(dsh);\n\n    ZeroMemory(&dsr, sizeof(dsr));\n    dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD);\n    dsr.nReserved = 0;\n    dsr.guid = rguid;\n    if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) ||\n        cbWrote != sizeof(dsr)) {\n        return FALSE;\n    }\n    pbTarget += sizeof(dsr);\n\n    if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) ||\n        cbWrote != cbData) {\n        return FALSE;\n    }\n    pbTarget += cbData;\n\n    DETOUR_TRACE((\"Copied %lu byte payload into target process at %p\\n\",\n                  cbTotal, pbTarget - cbTotal));\n    return TRUE;\n}\n\n#ifndef DETOURS_KERNEL\nstatic BOOL s_fSearchedForHelper = FALSE;\nstatic PDETOUR_EXE_HELPER s_pHelper = NULL;\n\nVOID CALLBACK DetourFinishHelperProcess(_In_ HWND,\n                                        _In_ HINSTANCE,\n                                        _In_ LPSTR,\n                                        _In_ INT)\n{\n    LPCSTR * rlpDlls = NULL;\n    DWORD Result = 9900;\n    HANDLE hProcess = nullptr;\n    DWORD cSize = 0;\n    DWORD cOffset = 0;\n\n    if (s_pHelper == NULL) {\n        DETOUR_TRACE((\"DetourFinishHelperProcess called with s_pHelper = NULL.\\n\"));\n        Result = 9905;\n        goto Cleanup;\n    }\n\n    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid);\n    if (hProcess == NULL) {\n        DETOUR_TRACE((\"OpenProcess(pid=%lu) failed: %lu\\n\",\n                      s_pHelper->pid, GetLastError()));\n        Result = 9901;\n        goto Cleanup;\n    }\n\n    rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls];\n    cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER);\n    cOffset = 0;\n    for (DWORD n = 0; n < s_pHelper->nDlls; n++) {\n        size_t cchDest = 0;\n        HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest);\n        if (!SUCCEEDED(hr)) {\n            Result = 9902;\n            goto Cleanup;\n        }\n\n        rlpDlls[n] = &s_pHelper->rDlls[cOffset];\n        cOffset += (DWORD)cchDest + 1;\n    }\n\n    if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) {\n        DETOUR_TRACE((\"DetourUpdateProcessWithDll(pid=%lu) failed: %lu\\n\",\n                      s_pHelper->pid, GetLastError()));\n        Result = 9903;\n        goto Cleanup;\n    }\n    Result = 0;\n\n  Cleanup:\n    if (rlpDlls != NULL) {\n        delete[] rlpDlls;\n        rlpDlls = NULL;\n    }\n\n    ExitProcess(Result);\n}\n\nBOOL WINAPI DetourIsHelperProcess(VOID)\n{\n    PVOID pvData;\n    DWORD cbData;\n\n    if (s_fSearchedForHelper) {\n        return (s_pHelper != NULL);\n    }\n\n    s_fSearchedForHelper = TRUE;\n    pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData);\n\n    if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) {\n        return FALSE;\n    }\n\n    s_pHelper = (PDETOUR_EXE_HELPER)pvData;\n    if (s_pHelper->cb < sizeof(*s_pHelper)) {\n        s_pHelper = NULL;\n        return FALSE;\n    }\n\n    return TRUE;\n}\n\nstatic\nBOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper,\n                           _In_ DWORD dwTargetPid,\n                           _In_ DWORD nDlls,\n                           _In_reads_(nDlls) LPCSTR *rlpDlls)\n{\n    PDETOUR_EXE_HELPER Helper = NULL;\n    BOOL Result = FALSE;\n    DWORD cSize = 0;\n    _Field_range_(0, cSize - 4) DWORD cOffset = 0;\n\n    if (pHelper == NULL) {\n        goto Cleanup;\n    }\n    *pHelper = NULL;\n\n    if (nDlls < 1 || nDlls > 4096) {\n        SetLastError(ERROR_INVALID_PARAMETER);\n        goto Cleanup;\n    }\n\n    cSize = 4;\n    for (DWORD n = 0; n < nDlls; n++) {\n        HRESULT hr;\n        size_t cchDest = 0;\n\n        hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest);\n        if (!SUCCEEDED(hr)) {\n            goto Cleanup;\n        }\n\n        cSize += (DWORD)cchDest + 1;\n    }\n\n    Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize];\n    if (Helper == NULL) {\n        goto Cleanup;\n    }\n\n    Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize;\n    Helper->pid = dwTargetPid;\n    Helper->nDlls = nDlls;\n\n    for (DWORD n = 0; n < nDlls; n++) {\n        HRESULT hr;\n        size_t cchDest = 0;\n\n        if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) {\n            goto Cleanup;\n        }\n\n        if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) {\n            goto Cleanup;\n        }\n\n        _Analysis_assume_(cOffset + 1 < cSize);\n        _Analysis_assume_(cOffset < 0x10000);\n        _Analysis_assume_(cSize < 0x10000);\n\n        PCHAR psz = &Helper->rDlls[cOffset];\n\n        hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]);\n        if (!SUCCEEDED(hr)) {\n            goto Cleanup;\n        }\n\n// REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call.\n// 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.\n#pragma warning(suppress:28020 28313)\n        hr = StringCchLengthA(psz, cSize - cOffset, &cchDest);\n        if (!SUCCEEDED(hr)) {\n            goto Cleanup;\n        }\n\n        // Replace \"32.\" with \"64.\" or \"64.\" with \"32.\"\n\n        for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) {\n#if DETOURS_32BIT\n            if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') {\n                psz[c - 3] = '6'; psz[c - 2] = '4';\n                break;\n            }\n#else\n            if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') {\n                psz[c - 3] = '3'; psz[c - 2] = '2';\n                break;\n            }\n#endif\n        }\n\n        cOffset += (DWORD)cchDest + 1;\n    }\n\n    *pHelper = Helper;\n    Helper = NULL;\n    Result = TRUE;\n\n  Cleanup:\n    if (Helper != NULL) {\n        delete[] (PBYTE)Helper;\n        Helper = NULL;\n    }\n    return Result;\n}\n\nstatic\nVOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper)\n{\n    if (*pHelper != NULL) {\n        delete[] (PBYTE)*pHelper;\n        *pHelper = NULL;\n    }\n}\n\nBOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,\n                                    _In_ LPCSTR lpDllName,\n                                    _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)\n{\n    return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA);\n}\n\n\nBOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,\n                                        _In_ DWORD nDlls,\n                                        _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                        _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)\n{\n    BOOL Result = FALSE;\n    PROCESS_INFORMATION pi;\n    STARTUPINFOA si;\n    CHAR szExe[MAX_PATH];\n    CHAR szCommand[MAX_PATH];\n    PDETOUR_EXE_HELPER helper = NULL;\n    HRESULT hr;\n    DWORD nLen = 0;\n\n    DETOUR_TRACE((\"DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\\n\", dwTargetPid, nDlls));\n    if (nDlls < 1 || nDlls > 4096) {\n        SetLastError(ERROR_INVALID_PARAMETER);\n        goto Cleanup;\n    }\n    if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {\n        goto Cleanup;\n    }\n\n    nLen = GetEnvironmentVariableA(\"WINDIR\", szExe, ARRAYSIZE(szExe));\n    if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {\n        goto Cleanup;\n    }\n\n#if DETOURS_OPTION_BITS\n#if DETOURS_32BIT\n    hr = StringCchCatA(szExe, ARRAYSIZE(szExe), \"\\\\sysnative\\\\rundll32.exe\");\n#else // !DETOURS_32BIT\n    hr = StringCchCatA(szExe, ARRAYSIZE(szExe), \"\\\\syswow64\\\\rundll32.exe\");\n#endif // !DETOURS_32BIT\n#else // DETOURS_OPTIONS_BITS\n    hr = StringCchCatA(szExe, ARRAYSIZE(szExe), \"\\\\system32\\\\rundll32.exe\");\n#endif // DETOURS_OPTIONS_BITS\n    if (!SUCCEEDED(hr)) {\n        goto Cleanup;\n    }\n\n    hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand),\n                          \"rundll32.exe \\\"%hs\\\",#1\", &helper->rDlls[0]);\n    if (!SUCCEEDED(hr)) {\n        goto Cleanup;\n    }\n\n    ZeroMemory(&pi, sizeof(pi));\n    ZeroMemory(&si, sizeof(si));\n    si.cb = sizeof(si);\n\n    DETOUR_TRACE((\"DetourProcessViaHelperDlls(\\\"%hs\\\", \\\"%hs\\\")\\n\", szExe, szCommand));\n    if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,\n                         NULL, NULL, &si, &pi)) {\n\n        if (!DetourCopyPayloadToProcess(pi.hProcess,\n                                        DETOUR_EXE_HELPER_GUID,\n                                        helper, helper->cb)) {\n            DETOUR_TRACE((\"DetourCopyPayloadToProcess failed: %lu\\n\", GetLastError()));\n            TerminateProcess(pi.hProcess, ~0u);\n            CloseHandle(pi.hProcess);\n            CloseHandle(pi.hThread);\n            goto Cleanup;\n        }\n\n        ResumeThread(pi.hThread);\n        WaitForSingleObject(pi.hProcess, INFINITE);\n\n        DWORD dwResult = 500;\n        GetExitCodeProcess(pi.hProcess, &dwResult);\n\n        CloseHandle(pi.hProcess);\n        CloseHandle(pi.hThread);\n\n        if (dwResult != 0) {\n            DETOUR_TRACE((\"Rundll32.exe failed: result=%lu\\n\", dwResult));\n            goto Cleanup;\n        }\n        Result = TRUE;\n    }\n    else {\n        DETOUR_TRACE((\"CreateProcess failed: %lu\\n\", GetLastError()));\n        goto Cleanup;\n    }\n\n  Cleanup:\n    FreeExeHelper(&helper);\n    return Result;\n}\n\nBOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,\n                                    _In_ LPCSTR lpDllName,\n                                    _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)\n{\n    return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW);\n}\n\nBOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,\n                                        _In_ DWORD nDlls,\n                                        _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                        _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)\n{\n    BOOL Result = FALSE;\n    PROCESS_INFORMATION pi;\n    STARTUPINFOW si;\n    WCHAR szExe[MAX_PATH];\n    WCHAR szCommand[MAX_PATH];\n    PDETOUR_EXE_HELPER helper = NULL;\n    HRESULT hr;\n    DWORD nLen = 0;\n\n    DETOUR_TRACE((\"DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\\n\", dwTargetPid, nDlls));\n    if (nDlls < 1 || nDlls > 4096) {\n        SetLastError(ERROR_INVALID_PARAMETER);\n        goto Cleanup;\n    }\n    if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) {\n        goto Cleanup;\n    }\n\n    nLen = GetEnvironmentVariableW(L\"WINDIR\", szExe, ARRAYSIZE(szExe));\n    if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) {\n        goto Cleanup;\n    }\n\n#if DETOURS_OPTION_BITS\n#if DETOURS_32BIT\n    hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L\"\\\\sysnative\\\\rundll32.exe\");\n#else // !DETOURS_32BIT\n    hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L\"\\\\syswow64\\\\rundll32.exe\");\n#endif // !DETOURS_32BIT\n#else // DETOURS_OPTIONS_BITS\n    hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L\"\\\\system32\\\\rundll32.exe\");\n#endif // DETOURS_OPTIONS_BITS\n    if (!SUCCEEDED(hr)) {\n        goto Cleanup;\n    }\n\n    hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand),\n                          L\"rundll32.exe \\\"%hs\\\",#1\", &helper->rDlls[0]);\n    if (!SUCCEEDED(hr)) {\n        goto Cleanup;\n    }\n\n    ZeroMemory(&pi, sizeof(pi));\n    ZeroMemory(&si, sizeof(si));\n    si.cb = sizeof(si);\n\n    DETOUR_TRACE((\"DetourProcessViaHelperDlls(\\\"%ls\\\", \\\"%ls\\\")\\n\", szExe, szCommand));\n    if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED,\n                         NULL, NULL, &si, &pi)) {\n\n        if (!DetourCopyPayloadToProcess(pi.hProcess,\n                                        DETOUR_EXE_HELPER_GUID,\n                                        helper, helper->cb)) {\n            DETOUR_TRACE((\"DetourCopyPayloadToProcess failed: %lu\\n\", GetLastError()));\n            TerminateProcess(pi.hProcess, ~0u);\n            CloseHandle(pi.hProcess);\n            CloseHandle(pi.hThread);\n            goto Cleanup;\n        }\n\n        ResumeThread(pi.hThread);\n\n        ResumeThread(pi.hThread);\n        WaitForSingleObject(pi.hProcess, INFINITE);\n\n        DWORD dwResult = 500;\n        GetExitCodeProcess(pi.hProcess, &dwResult);\n\n        CloseHandle(pi.hProcess);\n        CloseHandle(pi.hThread);\n\n        if (dwResult != 0) {\n            DETOUR_TRACE((\"Rundll32.exe failed: result=%lu\\n\", dwResult));\n            goto Cleanup;\n        }\n        Result = TRUE;\n    }\n    else {\n        DETOUR_TRACE((\"CreateProcess failed: %lu\\n\", GetLastError()));\n        goto Cleanup;\n    }\n\n  Cleanup:\n    FreeExeHelper(&helper);\n    return Result;\n}\n\nBOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,\n                                          _Inout_opt_ LPSTR lpCommandLine,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                          _In_ BOOL bInheritHandles,\n                                          _In_ DWORD dwCreationFlags,\n                                          _In_opt_ LPVOID lpEnvironment,\n                                          _In_opt_ LPCSTR lpCurrentDirectory,\n                                          _In_ LPSTARTUPINFOA lpStartupInfo,\n                                          _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                          _In_ LPCSTR lpDllName,\n                                          _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)\n{\n    if (pfCreateProcessA == NULL) {\n        pfCreateProcessA = CreateProcessA;\n    }\n\n    PROCESS_INFORMATION backup;\n    if (lpProcessInformation == NULL) {\n        lpProcessInformation = &backup;\n        ZeroMemory(&backup, sizeof(backup));\n    }\n\n    if (!pfCreateProcessA(lpApplicationName,\n                          lpCommandLine,\n                          lpProcessAttributes,\n                          lpThreadAttributes,\n                          bInheritHandles,\n                          dwCreationFlags | CREATE_SUSPENDED,\n                          lpEnvironment,\n                          lpCurrentDirectory,\n                          lpStartupInfo,\n                          lpProcessInformation)) {\n        return FALSE;\n    }\n\n    LPCSTR szDll = lpDllName;\n\n    if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) &&\n        !DetourProcessViaHelperA(lpProcessInformation->dwProcessId,\n                                 lpDllName,\n                                 pfCreateProcessA)) {\n\n        TerminateProcess(lpProcessInformation->hProcess, ~0u);\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(lpProcessInformation->hThread);\n    }\n\n    if (lpProcessInformation == &backup) {\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n    }\n\n    return TRUE;\n}\n\nBOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,\n                                          _Inout_opt_  LPWSTR lpCommandLine,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                          _In_ BOOL bInheritHandles,\n                                          _In_ DWORD dwCreationFlags,\n                                          _In_opt_ LPVOID lpEnvironment,\n                                          _In_opt_ LPCWSTR lpCurrentDirectory,\n                                          _In_ LPSTARTUPINFOW lpStartupInfo,\n                                          _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                          _In_ LPCSTR lpDllName,\n                                          _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)\n{\n    if (pfCreateProcessW == NULL) {\n        pfCreateProcessW = CreateProcessW;\n    }\n\n    PROCESS_INFORMATION backup;\n    if (lpProcessInformation == NULL) {\n        lpProcessInformation = &backup;\n        ZeroMemory(&backup, sizeof(backup));\n    }\n\n    if (!pfCreateProcessW(lpApplicationName,\n                          lpCommandLine,\n                          lpProcessAttributes,\n                          lpThreadAttributes,\n                          bInheritHandles,\n                          dwCreationFlags | CREATE_SUSPENDED,\n                          lpEnvironment,\n                          lpCurrentDirectory,\n                          lpStartupInfo,\n                          lpProcessInformation)) {\n        return FALSE;\n    }\n\n\n    LPCSTR sz = lpDllName;\n\n    if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) &&\n        !DetourProcessViaHelperW(lpProcessInformation->dwProcessId,\n                                 lpDllName,\n                                 pfCreateProcessW)) {\n\n        TerminateProcess(lpProcessInformation->hProcess, ~0u);\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(lpProcessInformation->hThread);\n    }\n\n    if (lpProcessInformation == &backup) {\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n    }\n    return TRUE;\n}\n\nBOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,\n                                         _Inout_opt_ LPSTR lpCommandLine,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                         _In_ BOOL bInheritHandles,\n                                         _In_ DWORD dwCreationFlags,\n                                         _In_opt_ LPVOID lpEnvironment,\n                                         _In_opt_ LPCSTR lpCurrentDirectory,\n                                         _In_ LPSTARTUPINFOA lpStartupInfo,\n                                         _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                         _In_ DWORD nDlls,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA)\n{\n    if (pfCreateProcessA == NULL) {\n        pfCreateProcessA = CreateProcessA;\n    }\n\n    PROCESS_INFORMATION backup;\n    if (lpProcessInformation == NULL) {\n        lpProcessInformation = &backup;\n        ZeroMemory(&backup, sizeof(backup));\n    }\n\n    if (!pfCreateProcessA(lpApplicationName,\n                          lpCommandLine,\n                          lpProcessAttributes,\n                          lpThreadAttributes,\n                          bInheritHandles,\n                          dwCreationFlags | CREATE_SUSPENDED,\n                          lpEnvironment,\n                          lpCurrentDirectory,\n                          lpStartupInfo,\n                          lpProcessInformation)) {\n        return FALSE;\n    }\n\n    if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&\n        !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId,\n                                     nDlls,\n                                     rlpDlls,\n                                     pfCreateProcessA)) {\n\n        TerminateProcess(lpProcessInformation->hProcess, ~0u);\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(lpProcessInformation->hThread);\n    }\n\n    if (lpProcessInformation == &backup) {\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n    }\n\n    return TRUE;\n}\n\nBOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,\n                                         _Inout_opt_ LPWSTR lpCommandLine,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                         _In_ BOOL bInheritHandles,\n                                         _In_ DWORD dwCreationFlags,\n                                         _In_opt_ LPVOID lpEnvironment,\n                                         _In_opt_ LPCWSTR lpCurrentDirectory,\n                                         _In_ LPSTARTUPINFOW lpStartupInfo,\n                                         _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                         _In_ DWORD nDlls,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)\n{\n    if (pfCreateProcessW == NULL) {\n        pfCreateProcessW = CreateProcessW;\n    }\n\n    PROCESS_INFORMATION backup;\n    if (lpProcessInformation == NULL) {\n        lpProcessInformation = &backup;\n        ZeroMemory(&backup, sizeof(backup));\n    }\n\n    if (!pfCreateProcessW(lpApplicationName,\n                          lpCommandLine,\n                          lpProcessAttributes,\n                          lpThreadAttributes,\n                          bInheritHandles,\n                          dwCreationFlags | CREATE_SUSPENDED,\n                          lpEnvironment,\n                          lpCurrentDirectory,\n                          lpStartupInfo,\n                          lpProcessInformation)) {\n        return FALSE;\n    }\n\n\n    if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) &&\n        !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId,\n                                     nDlls,\n                                     rlpDlls,\n                                     pfCreateProcessW)) {\n\n        TerminateProcess(lpProcessInformation->hProcess, ~0u);\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n        return FALSE;\n    }\n\n    if (!(dwCreationFlags & CREATE_SUSPENDED)) {\n        ResumeThread(lpProcessInformation->hThread);\n    }\n\n    if (lpProcessInformation == &backup) {\n        CloseHandle(lpProcessInformation->hProcess);\n        CloseHandle(lpProcessInformation->hThread);\n    }\n    return TRUE;\n}\n#endif // !DETOURS_KERNEL\n\n//\n///////////////////////////////////////////////////////////////// End of File.\n"
  },
  {
    "path": "Detours/detours.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Core Detours Functionality (detours.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#pragma warning(disable:4068) // unknown pragma (suppress)\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n\n#include <Veil.h>\n\n#if (_MSC_VER < 1299)\n#pragma warning(disable: 4710)\n#endif\n\n// #define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\n#define NOTHROW\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstruct _DETOUR_ALIGN\n{\n    BYTE    obTarget        : 3;\n    BYTE    obTrampoline    : 5;\n};\n\nC_ASSERT(sizeof(_DETOUR_ALIGN) == 1);\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Region reserved for system DLLs, which cannot be used for trampolines.\n//\nstatic PVOID    s_pSystemRegionLowerBound   = (PVOID)(ULONG_PTR)0x70000000;\nstatic PVOID    s_pSystemRegionUpperBound   = (PVOID)(ULONG_PTR)0x80000000;\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstatic bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)\n{\n    MEMORY_BASIC_INFORMATION mbi;\n    VirtualQuery((PVOID)pbCode, &mbi, sizeof(mbi));\n    __try {\n        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            return false;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            return false;\n        }\n\n        if (pbAddress >= ((PBYTE)pDosHeader +\n                          pNtHeader->OptionalHeader\n                          .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&\n            pbAddress < ((PBYTE)pDosHeader +\n                         pNtHeader->OptionalHeader\n                         .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +\n                         pNtHeader->OptionalHeader\n                         .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {\n            return true;\n        }\n    }\n#pragma prefast(suppress:28940, \"A bad pointer means this probably isn't a PE header.\")\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        return false;\n    }\n    return false;\n}\n\ninline ULONG_PTR detour_2gb_below(ULONG_PTR address)\n{\n    return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;\n}\n\ninline ULONG_PTR detour_2gb_above(ULONG_PTR address)\n{\n#if defined(DETOURS_64BIT)\n    return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;\n#else\n    return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;\n#endif\n}\n\n///////////////////////////////////////////////////////////////////////// X86.\n//\n#ifdef DETOURS_X86\n\nstruct _DETOUR_TRAMPOLINE\n{\n    BYTE            rbCode[30];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[22];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);\n\nenum {\n    SIZE_OF_JMP = 5\n};\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)\n{\n    *pbCode++ = 0xff;   // jmp [+imm32]\n    *pbCode++ = 0x25;\n    *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        *pbCode++ = 0xcc;   // brk;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // First, skip over the import vector if there is one.\n    if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [imm32]\n        // Looks like an import alias jump, then get the code it points to.\n        PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];\n        if (detour_is_imported(pbCode, pbTarget)) {\n            PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;\n            DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n\n    // Then, skip over a patch jump\n    if (pbCode[0] == 0xeb) {   // jmp +imm8\n        PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];\n        DETOUR_TRACE((\"%p->%p: skipped over short jump.\\n\", pbCode, pbNew));\n        pbCode = pbNew;\n\n        // First, skip over the import vector if there is one.\n        if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [imm32]\n            // Looks like an import alias jump, then get the code it points to.\n            PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];\n            if (detour_is_imported(pbCode, pbTarget)) {\n                pbNew = *(UNALIGNED PBYTE *)pbTarget;\n                DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                pbCode = pbNew;\n            }\n        }\n        // Finally, skip over a long jump if it is the target of the patch jump.\n        else if (pbCode[0] == 0xe9) {   // jmp +imm32\n            pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n            DETOUR_TRACE((\"%p->%p: skipped over long jump.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    // We have to place trampolines within +/- 2GB of code.\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%Ix..%p..%Ix]\\n\", lo, pbCode, hi));\n\n    // And, within +/- 2GB of relative jmp targets.\n    if (pbCode[0] == 0xe9) {   // jmp +imm32\n        PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n\n        if (pbNew < pbCode) {\n            hi = detour_2gb_above((ULONG_PTR)pbNew);\n        }\n        else {\n            lo = detour_2gb_below((ULONG_PTR)pbNew);\n        }\n        DETOUR_TRACE((\"[%Ix..%p..%Ix] +imm32\\n\", lo, pbCode, hi));\n    }\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    if (pbCode[0] == 0xeb ||    // jmp +imm8\n        pbCode[0] == 0xe9 ||    // jmp +imm32\n        pbCode[0] == 0xe0 ||    // jmp eax\n        pbCode[0] == 0xc2 ||    // ret +imm8\n        pbCode[0] == 0xc3 ||    // ret\n        pbCode[0] == 0xcc) {    // brk\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) {  // rep ret\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xff && pbCode[1] == 0x25) {  // jmp [+imm32]\n        return TRUE;\n    }\n    else if ((pbCode[0] == 0x26 ||      // jmp es:\n              pbCode[0] == 0x2e ||      // jmp cs:\n              pbCode[0] == 0x36 ||      // jmp ss:\n              pbCode[0] == 0x3e ||      // jmp ds:\n              pbCode[0] == 0x64 ||      // jmp fs:\n              pbCode[0] == 0x65) &&     // jmp gs:\n             pbCode[1] == 0xff &&       // jmp [+imm32]\n             pbCode[2] == 0x25) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // 1-byte through 11-byte NOPs.\n    if (pbCode[0] == 0x90) {\n        return 1;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {\n        return 2;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {\n        return 3;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&\n        pbCode[3] == 0x00) {\n        return 4;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00) {\n        return 5;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {\n        return 6;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00) {\n        return 7;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00) {\n        return 8;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {\n        return 9;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&\n        pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00) {\n        return 10;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&\n        pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00 && pbCode[10] == 0x00) {\n        return 11;\n    }\n\n    // int 3.\n    if (pbCode[0] == 0xcc) {\n        return 1;\n    }\n    return 0;\n}\n\n#endif // DETOURS_X86\n\n///////////////////////////////////////////////////////////////////////// X64.\n//\n#ifdef DETOURS_X64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // An X64 instuction can be 15 bytes long.\n    // In practice 11 seems to be the limit.\n    BYTE            rbCode[30];     // target code + jmp to pbRemain.\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[30];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n    BYTE            rbCodeIn[8];    // jmp [pbDetour]\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 96);\n\nenum {\n    SIZE_OF_JMP = 5\n};\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 6;\n    *pbCode++ = 0xff;   // jmp [+imm32]\n    *pbCode++ = 0x25;\n    *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        *pbCode++ = 0xcc;   // brk;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // First, skip over the import vector if there is one.\n    if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]\n        // Looks like an import alias jump, then get the code it points to.\n        PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];\n        if (detour_is_imported(pbCode, pbTarget)) {\n            PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;\n            DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n\n    // Then, skip over a patch jump\n    if (pbCode[0] == 0xeb) {   // jmp +imm8\n        PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];\n        DETOUR_TRACE((\"%p->%p: skipped over short jump.\\n\", pbCode, pbNew));\n        pbCode = pbNew;\n\n        // First, skip over the import vector if there is one.\n        if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]\n            // Looks like an import alias jump, then get the code it points to.\n            PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];\n            if (detour_is_imported(pbCode, pbTarget)) {\n                pbNew = *(UNALIGNED PBYTE *)pbTarget;\n                DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                pbCode = pbNew;\n            }\n        }\n        // Finally, skip over a long jump if it is the target of the patch jump.\n        else if (pbCode[0] == 0xe9) {   // jmp +imm32\n            pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n            DETOUR_TRACE((\"%p->%p: skipped over long jump.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    // We have to place trampolines within +/- 2GB of code.\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%Ix..%p..%Ix]\\n\", lo, pbCode, hi));\n\n    // And, within +/- 2GB of relative jmp vectors.\n    if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]\n        PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];\n\n        if (pbNew < pbCode) {\n            hi = detour_2gb_above((ULONG_PTR)pbNew);\n        }\n        else {\n            lo = detour_2gb_below((ULONG_PTR)pbNew);\n        }\n        DETOUR_TRACE((\"[%Ix..%p..%Ix] [+imm32]\\n\", lo, pbCode, hi));\n    }\n    // And, within +/- 2GB of relative jmp targets.\n    else if (pbCode[0] == 0xe9) {   // jmp +imm32\n        PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n\n        if (pbNew < pbCode) {\n            hi = detour_2gb_above((ULONG_PTR)pbNew);\n        }\n        else {\n            lo = detour_2gb_below((ULONG_PTR)pbNew);\n        }\n        DETOUR_TRACE((\"[%Ix..%p..%Ix] +imm32\\n\", lo, pbCode, hi));\n    }\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    if (pbCode[0] == 0xeb ||    // jmp +imm8\n        pbCode[0] == 0xe9 ||    // jmp +imm32\n        pbCode[0] == 0xe0 ||    // jmp eax\n        pbCode[0] == 0xc2 ||    // ret +imm8\n        pbCode[0] == 0xc3 ||    // ret\n        pbCode[0] == 0xcc) {    // brk\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) {  // rep ret\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xff && pbCode[1] == 0x25) {  // jmp [+imm32]\n        return TRUE;\n    }\n    else if ((pbCode[0] == 0x26 ||      // jmp es:\n              pbCode[0] == 0x2e ||      // jmp cs:\n              pbCode[0] == 0x36 ||      // jmp ss:\n              pbCode[0] == 0x3e ||      // jmp ds:\n              pbCode[0] == 0x64 ||      // jmp fs:\n              pbCode[0] == 0x65) &&     // jmp gs:\n             pbCode[1] == 0xff &&       // jmp [+imm32]\n             pbCode[2] == 0x25) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // 1-byte through 11-byte NOPs.\n    if (pbCode[0] == 0x90) {\n        return 1;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {\n        return 2;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {\n        return 3;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&\n        pbCode[3] == 0x00) {\n        return 4;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00) {\n        return 5;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {\n        return 6;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00) {\n        return 7;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00) {\n        return 8;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {\n        return 9;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&\n        pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00) {\n        return 10;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&\n        pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00 && pbCode[10] == 0x00) {\n        return 11;\n    }\n\n    // int 3.\n    if (pbCode[0] == 0xcc) {\n        return 1;\n    }\n    return 0;\n}\n\n#endif // DETOURS_X64\n\n//////////////////////////////////////////////////////////////////////// IA64.\n//\n#ifdef DETOURS_IA64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // On the IA64, a trampoline is used for both incoming and outgoing calls.\n    //\n    // The trampoline contains the following bundles for the outgoing call:\n    //      movl gp=target_gp;\n    //      <relocated target bundle>\n    //      brl  target_code;\n    //\n    // The trampoline contains the following bundles for the incoming call:\n    //      alloc  r41=ar.pfs, b, 0, 8, 0\n    //      mov    r40=rp\n    //\n    //      adds   r50=0, r39\n    //      adds   r49=0, r38\n    //      adds   r48=0, r37 ;;\n    //\n    //      adds   r47=0, r36\n    //      adds   r46=0, r35\n    //      adds   r45=0, r34\n    //\n    //      adds   r44=0, r33\n    //      adds   r43=0, r32\n    //      adds   r42=0, gp ;;\n    //\n    //      movl   gp=ffffffff`ffffffff ;;\n    //\n    //      brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;;\n    //\n    //      adds   gp=0, r42\n    //      mov    rp=r40, +0 ;;\n    //      mov.i  ar.pfs=r41\n    //\n    //      br.ret.sptk.many rp ;;\n    //\n    // This way, we only have to relocate a single bundle.\n    //\n    // The complicated incoming trampoline is required because we have to\n    // create an additional stack frame so that we save and restore the gp.\n    // We must do this because gp is a caller-saved register, but not saved\n    // if the caller thinks the target is in the same DLL, which changes\n    // when we insert a detour.\n    //\n    DETOUR_IA64_BUNDLE  bMovlTargetGp;  // Bundle which sets target GP\n    BYTE                rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle.\n    DETOUR_IA64_BUNDLE  bBrlRemainEip;  // Brl to pbRemain\n    // This must be adjacent to bBranchIslands.\n\n    // Each instruction in the moved bundle could be a IP-relative chk or branch or call.\n    // Any such instructions are changed to point to a brl in bBranchIslands.\n    // This must be adjacent to bBrlRemainEip -- see \"pbPool\".\n    DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE];\n\n    // Target of brl inserted in target function\n    DETOUR_IA64_BUNDLE  bAllocFrame;    // alloc frame\n    DETOUR_IA64_BUNDLE  bSave37to39;    // save r37, r38, r39.\n    DETOUR_IA64_BUNDLE  bSave34to36;    // save r34, r35, r36.\n    DETOUR_IA64_BUNDLE  bSaveGPto33;    // save gp, r32, r33.\n    DETOUR_IA64_BUNDLE  bMovlDetourGp;  // set detour GP.\n    DETOUR_IA64_BUNDLE  bCallDetour;    // call detour.\n    DETOUR_IA64_BUNDLE  bPopFrameGp;    // pop frame and restore gp.\n    DETOUR_IA64_BUNDLE  bReturn;        // return to caller.\n\n    PLABEL_DESCRIPTOR   pldTrampoline;\n\n    BYTE                rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle.\n    BYTE                cbRestore;      // size of original target code.\n    BYTE                cbCode;         // size of moved target code.\n    _DETOUR_ALIGN       rAlign[14];     // instruction alignment array.\n    PBYTE               pbRemain;       // first instruction after moved code. [free list]\n    PBYTE               pbDetour;       // first instruction of detour function.\n    PPLABEL_DESCRIPTOR  ppldDetour;     // [pbDetour,gpDetour]\n    PPLABEL_DESCRIPTOR  ppldTarget;     // [pbTarget,gpDetour]\n};\n\nC_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16);\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16);\n\nenum {\n    SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE)\n};\n\ninline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals)\n{\n    PBYTE pGlobals = NULL;\n    PBYTE pbCode = NULL;\n\n    if (pPointer != NULL) {\n        PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer;\n        pbCode = (PBYTE)ppld->EntryPoint;\n        pGlobals = (PBYTE)ppld->GlobalPointer;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = pGlobals;\n    }\n    if (pbCode == NULL) {\n        return NULL;\n    }\n\n    DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode;\n\n    // IA64 Local Import Jumps look like:\n    //      addl   r2=ffffffff`ffe021c0, gp ;;\n    //      ld8    r2=[r2]\n    //      nop.i  0 ;;\n    //\n    //      ld8    r3=[r2], 8 ;;\n    //      ld8    gp=[r2]\n    //      mov    b6=r3, +0\n    //\n    //      nop.m  0\n    //      nop.i  0\n    //      br.cond.sptk.few b6\n    //\n\n    //                     002024000200100b\n    if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&\n        pb[0].wide[1] == 0x0004000000203008 &&\n        pb[1].wide[0] == 0x001014180420180a &&\n        pb[1].wide[1] == 0x07000830c0203008 &&\n        pb[2].wide[0] == 0x0000000100000010 &&\n        pb[2].wide[1] == 0x0080006000000200) {\n\n        ULONG64 offset =\n            ((pb[0].wide[0] & 0x0000000001fc0000) >> 18) |  // imm7b\n            ((pb[0].wide[0] & 0x000001ff00000000) >> 25) |  // imm9d\n            ((pb[0].wide[0] & 0x00000000f8000000) >> 11);   // imm5c\n        if (pb[0].wide[0] & 0x0000020000000000) {           // sign\n            offset |= 0xffffffffffe00000;\n        }\n        PBYTE pbTarget = pGlobals + offset;\n        DETOUR_TRACE((\"%p: potential import jump, target=%p\\n\", pb, pbTarget));\n\n        if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) {\n            DETOUR_TRACE((\"%p: is import jump, label=%p\\n\", pb, *(PBYTE *)pbTarget));\n\n            PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget;\n            pbCode = (PBYTE)ppld->EntryPoint;\n            pGlobals = (PBYTE)ppld->GlobalPointer;\n            if (ppGlobals != NULL) {\n                *ppGlobals = pGlobals;\n            }\n        }\n    }\n    return pbCode;\n}\n\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    (void)pbCode;\n    *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;\n    *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    // Routine not needed on IA64.\n    (void)pbCode;\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // Routine not needed on IA64.\n    (void)pbCode;\n    return 0;\n}\n\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_ARM\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // A Thumb-2 instruction can be 2 or 4 bytes long.\n    BYTE            rbCode[62];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[22];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);\n\nenum {\n    SIZE_OF_JMP = 8\n};\n\ninline PBYTE align4(PBYTE pValue)\n{\n    return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u);\n}\n\ninline ULONG fetch_thumb_opcode(PBYTE pbCode)\n{\n    ULONG Opcode = *(UINT16 *)&pbCode[0];\n    if (Opcode >= 0xe800) {\n        Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];\n    }\n    return Opcode;\n}\n\ninline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode)\n{\n    if (Opcode >= 0x10000) {\n        *((UINT16*&)pbCode)++ = Opcode >> 16;\n    }\n    *((UINT16*&)pbCode)++ = (UINT16)Opcode;\n}\n\nPBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)\n{\n    PBYTE pbLiteral;\n    if (ppPool != NULL) {\n        *ppPool = *ppPool - 4;\n        pbLiteral = *ppPool;\n    }\n    else {\n        pbLiteral = align4(pbCode + 6);\n    }\n\n    *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal);\n    LONG delta = pbLiteral - align4(pbCode + 4);\n\n    write_thumb_opcode(pbCode, 0xf8dff000 | delta);     // LDR PC,[PC+n]\n\n    if (ppPool == NULL) {\n        if (((ULONG)pbCode & 2) != 0) {\n            write_thumb_opcode(pbCode, 0xdefe);         // BREAK\n        }\n        pbCode += 4;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        write_thumb_opcode(pbCode, 0xdefe);\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // Skip over the import jump if there is one.\n    pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode);\n    ULONG Opcode = fetch_thumb_opcode(pbCode);\n\n    if ((Opcode & 0xfbf08f00) == 0xf2400c00) {          // movw r12,#xxxx\n        ULONG Opcode2 = fetch_thumb_opcode(pbCode+4);\n\n        if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) {      // movt r12,#xxxx\n            ULONG Opcode3 = fetch_thumb_opcode(pbCode+8);\n            if (Opcode3 == 0xf8dcf000) {                 // ldr  pc,[r12]\n                PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) |\n                                         ((Opcode2 <<  1) & 0x08000000) |\n                                         ((Opcode2 << 16) & 0x00ff0000) |\n                                         ((Opcode  >>  4) & 0x0000f700) |\n                                         ((Opcode  >> 15) & 0x00000800) |\n                                         ((Opcode  >>  0) & 0x000000ff));\n                if (detour_is_imported(pbCode, pbTarget)) {\n                    PBYTE pbNew = *(PBYTE *)pbTarget;\n                    pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew);\n                    DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                    return pbNew;\n                }\n            }\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    // We have to place trampolines within +/- 2GB of code.\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%p..%p..%p]\\n\", lo, pbCode, hi));\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    ULONG Opcode = fetch_thumb_opcode(pbCode);\n    if ((Opcode & 0xffffff87) == 0x4700 ||          // bx <reg>\n        (Opcode & 0xf800d000) == 0xf0009000) {      // b <imm20>\n        return TRUE;\n    }\n    if ((Opcode & 0xffff8000) == 0xe8bd8000) {      // pop {...,pc}\n        __debugbreak();\n        return TRUE;\n    }\n    if ((Opcode & 0xffffff00) == 0x0000bd00) {      // pop {...,pc}\n        __debugbreak();\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop.\n        return 2;\n    }\n    if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding.\n        return 2;\n    }\n    return 0;\n}\n\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // An ARM64 instruction is 4 bytes long.\n    BYTE            rbCode[64];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak[3]; // padding to make debugging easier.\n    BYTE            rbRestore[24];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak[3]; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 120);\n\nenum {\n    SIZE_OF_JMP = 8\n};\n\ninline ULONG fetch_opcode(PBYTE pbCode)\n{\n    return *(ULONG *)pbCode;\n}\n\ninline void write_opcode(PBYTE &pbCode, ULONG Opcode)\n{\n    *(ULONG *)pbCode = Opcode;\n    pbCode += 4;\n}\n\nPBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)\n{\n    PBYTE pbLiteral;\n    if (ppPool != NULL) {\n        *ppPool = *ppPool - 8;\n        pbLiteral = *ppPool;\n    }\n    else {\n        pbLiteral = pbCode + 2*4;\n    }\n\n    *((PBYTE*&)pbLiteral) = pbJmpVal;\n    LONG delta = (LONG)(pbLiteral - pbCode);\n\n    write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5));  // LDR X17,[PC+n]\n    write_opcode(pbCode, 0xd61f0000 | (17 << 5));           // BR X17\n\n    if (ppPool == NULL) {\n        pbCode += 8;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        write_opcode(pbCode, 0xd4100000 | (0xf000 << 5));\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // Skip over the import jump if there is one.\n    pbCode = (PBYTE)pbCode;\n    ULONG Opcode = fetch_opcode(pbCode);\n    if ((Opcode & 0x9f00001f) == 0x90000010) {           // adrp  x16, IAT\n        ULONG Opcode2 = fetch_opcode(pbCode+4);\n\n        if ((Opcode2 & 0xffe003ff) == 0xf9400210) {      // ldr   x16, [x16, IAT]\n            ULONG Opcode3 = fetch_opcode(pbCode+8);\n\n            if (Opcode3 == 0xd61f0200) {                 // br    x16\n\n                ULONG PageOffset = ((Opcode & 0x60000000) >> 29) | ((Opcode & 0x00ffffe0) >> 3);\n                PageOffset = (LONG)(Opcode << 11) >> 11;\n\n                PBYTE pbTarget = (PBYTE)(((ULONG64)pbCode & 0xfffffffffffff000ULL) + PageOffset +\n                                         ((Opcode2 >> 10) & 0xfff));\n                if (detour_is_imported(pbCode, pbTarget)) {\n                    PBYTE pbNew = *(PBYTE *)pbTarget;\n                    DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                    return pbNew;\n                }\n            }\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n    PDETOUR_TRAMPOLINE* ppLower,\n    PDETOUR_TRAMPOLINE* ppUpper)\n{\n    // The encoding used by detour_gen_jmp_indirect actually enables a\n    // displacement of +/- 4GiB. In the future, this could be changed to\n    // reflect that. For now, just reuse the x86 logic which is plenty.\n\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%p..%p..%p]\\n\", (PVOID)lo, pbCode, (PVOID)hi));\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    ULONG Opcode = fetch_opcode(pbCode);\n    if ((Opcode & 0xfffffc1f) == 0xd65f0000 ||      // br <reg>\n        (Opcode & 0xfc000000) == 0x14000000) {      // b <imm26>\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    if (*(ULONG *)pbCode == 0xd503201f) {   // nop.\n        return 4;\n    }\n    if (*(ULONG *)pbCode == 0x00000000) {   // zero-filled padding.\n        return 4;\n    }\n    return 0;\n}\n\n#endif // DETOURS_ARM64\n\n//////////////////////////////////////////////// Trampoline Memory Management.\n//\nstruct DETOUR_REGION\n{\n    ULONG               dwSignature;\n    DETOUR_REGION *     pNext;  // Next region in list of regions.\n    DETOUR_TRAMPOLINE * pFree;  // List of free trampolines in this region.\n};\ntypedef DETOUR_REGION * PDETOUR_REGION;\n\nconst ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';\nconst ULONG DETOUR_REGION_SIZE = 0x10000;\nconst ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE\n                                             / sizeof(DETOUR_TRAMPOLINE)) - 1;\nstatic PDETOUR_REGION s_pRegions = NULL;            // List of all regions.\nstatic PDETOUR_REGION s_pRegion = NULL;             // Default region.\n\nstatic DWORD detour_writable_trampoline_regions()\n{\n    // Mark all of the regions as writable.\n    for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {\n        DWORD dwOld;\n        if (!VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READWRITE, &dwOld)) {\n            return GetLastError();\n        }\n    }\n    return NO_ERROR;\n}\n\nstatic void detour_runnable_trampoline_regions()\n{\n    HANDLE hProcess = GetCurrentProcess();\n\n    // Mark all of the regions as executable.\n    for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {\n        DWORD dwOld;\n        VirtualProtect(pRegion, DETOUR_REGION_SIZE, PAGE_EXECUTE_READ, &dwOld);\n        FlushInstructionCache(hProcess, pRegion, DETOUR_REGION_SIZE);\n    }\n}\n\nstatic PBYTE detour_alloc_round_down_to_region(PBYTE pbTry)\n{\n    // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.\n    ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);\n    if (extra != 0) {\n        pbTry -= extra;\n    }\n    return pbTry;\n}\n\nstatic PBYTE detour_alloc_round_up_to_region(PBYTE pbTry)\n{\n    // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.\n    ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);\n    if (extra != 0) {\n        ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;\n        pbTry += adjust;\n    }\n    return pbTry;\n}\n\n// Starting at pbLo, try to allocate a memory region, continue until pbHi.\n\nstatic PVOID detour_alloc_region_from_lo(PBYTE pbLo, PBYTE pbHi)\n{\n    PBYTE pbTry = detour_alloc_round_up_to_region(pbLo);\n\n    DETOUR_TRACE((\" Looking for free region in %p..%p from %p:\\n\", pbLo, pbHi, pbTry));\n\n    for (; pbTry < pbHi;) {\n        MEMORY_BASIC_INFORMATION mbi;\n\n        if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) {\n            // Skip region reserved for system DLLs, but preserve address space entropy.\n            pbTry += 0x08000000;\n            continue;\n        }\n\n        ZeroMemory(&mbi, sizeof(mbi));\n        if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {\n            break;\n        }\n\n        DETOUR_TRACE((\"  Try %p => %p..%p %6lx\\n\",\n                      pbTry,\n                      mbi.BaseAddress,\n                      (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,\n                      mbi.State));\n\n        if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {\n\n            PVOID pv = VirtualAlloc(pbTry,\n                                    DETOUR_REGION_SIZE,\n                                    MEM_COMMIT|MEM_RESERVE,\n                                    PAGE_EXECUTE_READWRITE);\n            if (pv != NULL) {\n                return pv;\n            }\n            pbTry += DETOUR_REGION_SIZE;\n        }\n        else {\n            pbTry = detour_alloc_round_up_to_region((PBYTE)mbi.BaseAddress + mbi.RegionSize);\n        }\n    }\n    return NULL;\n}\n\n// Starting at pbHi, try to allocate a memory region, continue until pbLo.\n\nstatic PVOID detour_alloc_region_from_hi(PBYTE pbLo, PBYTE pbHi)\n{\n    PBYTE pbTry = detour_alloc_round_down_to_region(pbHi - DETOUR_REGION_SIZE);\n\n    DETOUR_TRACE((\" Looking for free region in %p..%p from %p:\\n\", pbLo, pbHi, pbTry));\n\n    for (; pbTry > pbLo;) {\n        MEMORY_BASIC_INFORMATION mbi;\n\n        DETOUR_TRACE((\"  Try %p\\n\", pbTry));\n        if (pbTry >= s_pSystemRegionLowerBound && pbTry <= s_pSystemRegionUpperBound) {\n            // Skip region reserved for system DLLs, but preserve address space entropy.\n            pbTry -= 0x08000000;\n            continue;\n        }\n\n        ZeroMemory(&mbi, sizeof(mbi));\n        if (!VirtualQuery(pbTry, &mbi, sizeof(mbi))) {\n            break;\n        }\n\n        DETOUR_TRACE((\"  Try %p => %p..%p %6lx\\n\",\n                      pbTry,\n                      mbi.BaseAddress,\n                      (PBYTE)mbi.BaseAddress + mbi.RegionSize - 1,\n                      mbi.State));\n\n        if (mbi.State == MEM_FREE && mbi.RegionSize >= DETOUR_REGION_SIZE) {\n\n            PVOID pv = VirtualAlloc(pbTry,\n                                    DETOUR_REGION_SIZE,\n                                    MEM_COMMIT|MEM_RESERVE,\n                                    PAGE_EXECUTE_READWRITE);\n            if (pv != NULL) {\n                return pv;\n            }\n            pbTry -= DETOUR_REGION_SIZE;\n        }\n        else {\n            pbTry = detour_alloc_round_down_to_region((PBYTE)mbi.AllocationBase\n                                                      - DETOUR_REGION_SIZE);\n        }\n    }\n    return NULL;\n}\n\nstatic PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)\n{\n    // We have to place trampolines within +/- 2GB of target.\n\n    PDETOUR_TRAMPOLINE pLo;\n    PDETOUR_TRAMPOLINE pHi;\n\n    detour_find_jmp_bounds(pbTarget, &pLo, &pHi);\n\n    PDETOUR_TRAMPOLINE pTrampoline = NULL;\n\n    // Insure that there is a default region.\n    if (s_pRegion == NULL && s_pRegions != NULL) {\n        s_pRegion = s_pRegions;\n    }\n\n    // First check the default region for an valid free block.\n    if (s_pRegion != NULL && s_pRegion->pFree != NULL &&\n        s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {\n\n      found_region:\n        pTrampoline = s_pRegion->pFree;\n        // do a last sanity check on region.\n        if (pTrampoline < pLo || pTrampoline > pHi) {\n            return NULL;\n        }\n        s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;\n        memset(pTrampoline, 0xcc, sizeof(*pTrampoline));\n        return pTrampoline;\n    }\n\n    // Then check the existing regions for a valid free block.\n    for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {\n        if (s_pRegion != NULL && s_pRegion->pFree != NULL &&\n            s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {\n            goto found_region;\n        }\n    }\n\n    // We need to allocate a new region.\n\n    // Round pbTarget down to 64KB block.\n    pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);\n\n    PVOID pbTry = NULL;\n\n    // NB: We must always also start the search at an offset from pbTarget\n    //     in order to maintain ASLR entropy.\n\n#if defined(DETOURS_64BIT)\n    // Try looking 1GB below or lower.\n    if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {\n        pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget - 0x40000000);\n    }\n    // Try looking 1GB above or higher.\n    if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {\n        pbTry = detour_alloc_region_from_lo(pbTarget + 0x40000000, (PBYTE)pHi);\n    }\n    // Try looking 1GB below or higher.\n    if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {\n        pbTry = detour_alloc_region_from_lo(pbTarget - 0x40000000, pbTarget);\n    }\n    // Try looking 1GB above or lower.\n    if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {\n        pbTry = detour_alloc_region_from_hi(pbTarget, pbTarget + 0x40000000);\n    }\n#endif\n\n    // Try anything below.\n    if (pbTry == NULL) {\n        pbTry = detour_alloc_region_from_hi((PBYTE)pLo, pbTarget);\n    }\n    // try anything above.\n    if (pbTry == NULL) {\n        pbTry = detour_alloc_region_from_lo(pbTarget, (PBYTE)pHi);\n    }\n\n    if (pbTry != NULL) {\n        s_pRegion = (DETOUR_REGION*)pbTry;\n        s_pRegion->dwSignature = DETOUR_REGION_SIGNATURE;\n        s_pRegion->pFree = NULL;\n        s_pRegion->pNext = s_pRegions;\n        s_pRegions = s_pRegion;\n        DETOUR_TRACE((\"  Allocated region %p..%p\\n\\n\",\n                      (void*)s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));\n\n        // Put everything but the first trampoline on the free list.\n        PBYTE pFree = NULL;\n        pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;\n        for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {\n            pTrampoline[i].pbRemain = pFree;\n            pFree = (PBYTE)&pTrampoline[i];\n        }\n        s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;\n        goto found_region;\n    }\n\n    DETOUR_TRACE((\"Couldn't find available memory region!\\n\"));\n    return NULL;\n}\n\nstatic void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline)\n{\n    PDETOUR_REGION pRegion = (PDETOUR_REGION)\n        ((ULONG_PTR)pTrampoline & ~(ULONG_PTR)0xffff);\n\n    memset(pTrampoline, 0, sizeof(*pTrampoline));\n    pTrampoline->pbRemain = (PBYTE)pRegion->pFree;\n    pRegion->pFree = pTrampoline;\n}\n\nstatic BOOL detour_is_region_empty(PDETOUR_REGION pRegion)\n{\n    // Stop if the region isn't a region (this would be bad).\n    if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) {\n        return FALSE;\n    }\n\n    PBYTE pbRegionBeg = (PBYTE)pRegion;\n    PBYTE pbRegionLim  = pbRegionBeg + DETOUR_REGION_SIZE;\n\n    // Stop if any of the trampolines aren't free.\n    PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1;\n    for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) {\n        if (pTrampoline[i].pbRemain != NULL &&\n            (pTrampoline[i].pbRemain < pbRegionBeg ||\n             pTrampoline[i].pbRemain >= pbRegionLim)) {\n            return FALSE;\n        }\n    }\n\n    // OK, the region is empty.\n    return TRUE;\n}\n\nstatic void detour_free_unused_trampoline_regions()\n{\n    PDETOUR_REGION *ppRegionBase = &s_pRegions;\n    PDETOUR_REGION pRegion = s_pRegions;\n\n    while (pRegion != NULL) {\n        if (detour_is_region_empty(pRegion)) {\n            *ppRegionBase = pRegion->pNext;\n\n            VirtualFree(pRegion, 0, MEM_RELEASE);\n            s_pRegion = NULL;\n        }\n        else {\n            ppRegionBase = &pRegion->pNext;\n        }\n        pRegion = *ppRegionBase;\n    }\n}\n\n///////////////////////////////////////////////////////// Transaction Structs.\n//\nstruct DetourThread\n{\n    DetourThread *      pNext;\n    HANDLE              hThread;\n};\n\nstruct DetourOperation\n{\n    DetourOperation *   pNext;\n    BOOL                fIsRemove;\n    PBYTE *             ppbPointer;\n    PBYTE               pbTarget;\n    PDETOUR_TRAMPOLINE  pTrampoline;\n    ULONG               dwPerm;\n};\n\nstatic BOOL                 s_fIgnoreTooSmall       = FALSE;\nstatic BOOL                 s_fRetainRegions        = FALSE;\n\nstatic LONG                 s_nPendingThreadId      = 0; // Thread owning pending transaction.\nstatic LONG                 s_nPendingError         = NO_ERROR;\nstatic PVOID *              s_ppPendingError        = NULL;\nstatic DetourThread *       s_pPendingThreads       = NULL;\nstatic DetourOperation *    s_pPendingOperations    = NULL;\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,\n                                   _Out_opt_ PVOID *ppGlobals)\n{\n    return detour_skip_jmp((PBYTE)pPointer, ppGlobals);\n}\n\n//////////////////////////////////////////////////////////// Transaction APIs.\n//\nBOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore)\n{\n    BOOL fPrevious = s_fIgnoreTooSmall;\n    s_fIgnoreTooSmall = fIgnore;\n    return fPrevious;\n}\n\nBOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain)\n{\n    BOOL fPrevious = s_fRetainRegions;\n    s_fRetainRegions = fRetain;\n    return fPrevious;\n}\n\nPVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound)\n{\n    PVOID pPrevious = s_pSystemRegionLowerBound;\n    s_pSystemRegionLowerBound = pSystemRegionLowerBound;\n    return pPrevious;\n}\n\nPVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound)\n{\n    PVOID pPrevious = s_pSystemRegionUpperBound;\n    s_pSystemRegionUpperBound = pSystemRegionUpperBound;\n    return pPrevious;\n}\n\nLONG WINAPI DetourTransactionBegin()\n{\n    // Only one transaction is allowed at a time.\n_Benign_race_begin_\n    if (s_nPendingThreadId != 0) {\n        return ERROR_INVALID_OPERATION;\n    }\n_Benign_race_end_\n\n    // Make sure only one thread can start a transaction.\n    if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    s_pPendingOperations = NULL;\n    s_pPendingThreads = NULL;\n    s_ppPendingError = NULL;\n\n    // Make sure the trampoline pages are writable.\n    s_nPendingError = detour_writable_trampoline_regions();\n\n    return s_nPendingError;\n}\n\nLONG WINAPI DetourTransactionAbort()\n{\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // Restore all of the page permissions.\n    for (DetourOperation *o = s_pPendingOperations; o != NULL;) {\n        // We don't care if this fails, because the code is still accessible.\n        DWORD dwOld;\n        VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore,\n                       o->dwPerm, &dwOld);\n\n        if (!o->fIsRemove) {\n            if (o->pTrampoline) {\n                detour_free_trampoline(o->pTrampoline);\n                o->pTrampoline = NULL;\n            }\n        }\n\n        DetourOperation *n = o->pNext;\n        delete o;\n        o = n;\n    }\n    s_pPendingOperations = NULL;\n\n    // Make sure the trampoline pages are no longer writable.\n    detour_runnable_trampoline_regions();\n\n    // Resume any suspended threads.\n    for (DetourThread *t = s_pPendingThreads; t != NULL;) {\n        // There is nothing we can do if this fails.\n        ResumeThread(t->hThread);\n\n        DetourThread *n = t->pNext;\n        delete t;\n        t = n;\n    }\n    s_pPendingThreads = NULL;\n    s_nPendingThreadId = 0;\n\n    return NO_ERROR;\n}\n\nLONG WINAPI DetourTransactionCommit()\n{\n    return DetourTransactionCommitEx(NULL);\n}\n\nstatic BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline)\n{\n    for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n        if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) {\n            return pTrampoline->rAlign[n].obTarget;\n        }\n    }\n    return 0;\n}\n\nstatic LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget)\n{\n    for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n        if (pTrampoline->rAlign[n].obTarget == obTarget) {\n            return pTrampoline->rAlign[n].obTrampoline;\n        }\n    }\n    return 0;\n}\n\nLONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)\n{\n    if (pppFailedPointer != NULL) {\n        // Used to get the last error.\n        *pppFailedPointer = s_ppPendingError;\n    }\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we abort the whole transaction.\n    if (s_nPendingError != NO_ERROR) {\n        DETOUR_BREAK();\n        DetourTransactionAbort();\n        return s_nPendingError;\n    }\n\n    // Common variables.\n    DetourOperation *o;\n    DetourThread *t;\n    BOOL freed = FALSE;\n\n    // Insert or remove each of the detours.\n    for (o = s_pPendingOperations; o != NULL; o = o->pNext) {\n        if (o->fIsRemove) {\n            CopyMemory(o->pbTarget,\n                       o->pTrampoline->rbRestore,\n                       o->pTrampoline->cbRestore);\n#ifdef DETOURS_IA64\n            *o->ppbPointer = (PBYTE)o->pTrampoline->ppldTarget;\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_X86\n            *o->ppbPointer = o->pbTarget;\n#endif // DETOURS_X86\n\n#ifdef DETOURS_X64\n            *o->ppbPointer = o->pbTarget;\n#endif // DETOURS_X64\n\n#ifdef DETOURS_ARM\n            *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pbTarget);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n            *o->ppbPointer = o->pbTarget;\n#endif // DETOURS_ARM\n        }\n        else {\n            DETOUR_TRACE((\"detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\\n\",\n                          (void*)o->pTrampoline,\n                          o->pTrampoline->pbRemain,\n                          o->pTrampoline->pbDetour,\n                          o->pTrampoline->cbRestore));\n\n            DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x [before]\\n\",\n                          o->pbTarget,\n                          o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],\n                          o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],\n                          o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));\n\n#ifdef DETOURS_IA64\n            ((DETOUR_IA64_BUNDLE*)o->pbTarget)\n                ->SetBrl((UINT64)&o->pTrampoline->bAllocFrame);\n            *o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline;\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_X64\n            detour_gen_jmp_indirect(o->pTrampoline->rbCodeIn, &o->pTrampoline->pbDetour);\n            PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->rbCodeIn);\n            pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);\n            *o->ppbPointer = o->pTrampoline->rbCode;\n            UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_X64\n\n#ifdef DETOURS_X86\n            PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbDetour);\n            pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);\n            *o->ppbPointer = o->pTrampoline->rbCode;\n            UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_X86\n\n#ifdef DETOURS_ARM\n            PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);\n            pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);\n            *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode);\n            UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n            PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);\n            pbCode = detour_gen_brk(pbCode, o->pTrampoline->pbRemain);\n            *o->ppbPointer = o->pTrampoline->rbCode;\n            UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_ARM64\n\n            DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x [after]\\n\",\n                          o->pbTarget,\n                          o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],\n                          o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],\n                          o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));\n\n            DETOUR_TRACE((\"detours: pbTramp =%p: \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x \"\n                          \"%02x %02x %02x %02x\\n\",\n                          (void*)o->pTrampoline,\n                          o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1],\n                          o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3],\n                          o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5],\n                          o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7],\n                          o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9],\n                          o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11]));\n\n#ifdef DETOURS_IA64\n            DETOUR_TRACE((\"\\n\"));\n            DETOUR_TRACE((\"detours:  &pldTrampoline  =%p\\n\",\n                          &o->pTrampoline->pldTrampoline));\n            DETOUR_TRACE((\"detours:  &bMovlTargetGp  =%p [%p]\\n\",\n                          &o->pTrampoline->bMovlTargetGp,\n                          o->pTrampoline->bMovlTargetGp.GetMovlGp()));\n            DETOUR_TRACE((\"detours:  &rbCode         =%p [%p]\\n\",\n                          &o->pTrampoline->rbCode,\n                          ((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget()));\n            DETOUR_TRACE((\"detours:  &bBrlRemainEip  =%p [%p]\\n\",\n                          &o->pTrampoline->bBrlRemainEip,\n                          o->pTrampoline->bBrlRemainEip.GetBrlTarget()));\n            DETOUR_TRACE((\"detours:  &bMovlDetourGp  =%p [%p]\\n\",\n                          &o->pTrampoline->bMovlDetourGp,\n                          o->pTrampoline->bMovlDetourGp.GetMovlGp()));\n            DETOUR_TRACE((\"detours:  &bBrlDetourEip  =%p [%p]\\n\",\n                          &o->pTrampoline->bCallDetour,\n                          o->pTrampoline->bCallDetour.GetBrlTarget()));\n            DETOUR_TRACE((\"detours:  pldDetour       =%p [%p]\\n\",\n                          o->pTrampoline->ppldDetour->EntryPoint,\n                          o->pTrampoline->ppldDetour->GlobalPointer));\n            DETOUR_TRACE((\"detours:  pldTarget       =%p [%p]\\n\",\n                          o->pTrampoline->ppldTarget->EntryPoint,\n                          o->pTrampoline->ppldTarget->GlobalPointer));\n            DETOUR_TRACE((\"detours:  pbRemain        =%p\\n\",\n                          o->pTrampoline->pbRemain));\n            DETOUR_TRACE((\"detours:  pbDetour        =%p\\n\",\n                          o->pTrampoline->pbDetour));\n            DETOUR_TRACE((\"\\n\"));\n#endif // DETOURS_IA64\n        }\n    }\n\n    // Update any suspended threads.\n    for (t = s_pPendingThreads; t != NULL; t = t->pNext) {\n        CONTEXT cxt;\n        cxt.ContextFlags = CONTEXT_CONTROL;\n\n#undef DETOURS_EIP\n\n#ifdef DETOURS_X86\n#define DETOURS_EIP         Eip\n#endif // DETOURS_X86\n\n#ifdef DETOURS_X64\n#define DETOURS_EIP         Rip\n#endif // DETOURS_X64\n\n#ifdef DETOURS_IA64\n#define DETOURS_EIP         StIIP\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_ARM\n#define DETOURS_EIP         Pc\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n#define DETOURS_EIP         Pc\n#endif // DETOURS_ARM64\n\ntypedef ULONG_PTR DETOURS_EIP_TYPE;\n\n        if (GetThreadContext(t->hThread, &cxt)) {\n            for (o = s_pPendingOperations; o != NULL; o = o->pNext) {\n                if (o->fIsRemove) {\n                    if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pTrampoline &&\n                        cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pTrampoline\n                                                             + sizeof(o->pTrampoline))\n                       ) {\n\n                        cxt.DETOURS_EIP = (DETOURS_EIP_TYPE)\n                            ((ULONG_PTR)o->pbTarget\n                             + detour_align_from_trampoline(o->pTrampoline,\n                                                            (BYTE)(cxt.DETOURS_EIP\n                                                                   - (DETOURS_EIP_TYPE)(ULONG_PTR)\n                                                                   o->pTrampoline)));\n\n                        SetThreadContext(t->hThread, &cxt);\n                    }\n                }\n                else {\n                    if (cxt.DETOURS_EIP >= (DETOURS_EIP_TYPE)(ULONG_PTR)o->pbTarget &&\n                        cxt.DETOURS_EIP < (DETOURS_EIP_TYPE)((ULONG_PTR)o->pbTarget\n                                                             + o->pTrampoline->cbRestore)\n                       ) {\n\n                        cxt.DETOURS_EIP = (DETOURS_EIP_TYPE)\n                            ((ULONG_PTR)o->pTrampoline\n                             + detour_align_from_target(o->pTrampoline,\n                                                        (BYTE)(cxt.DETOURS_EIP\n                                                               - (DETOURS_EIP_TYPE)(ULONG_PTR)\n                                                               o->pbTarget)));\n\n                        SetThreadContext(t->hThread, &cxt);\n                    }\n                }\n            }\n        }\n#undef DETOURS_EIP\n    }\n\n    // Restore all of the page permissions and flush the icache.\n    HANDLE hProcess = GetCurrentProcess();\n    for (o = s_pPendingOperations; o != NULL;) {\n        // We don't care if this fails, because the code is still accessible.\n        DWORD dwOld;\n        VirtualProtect(o->pbTarget, o->pTrampoline->cbRestore, o->dwPerm, &dwOld);\n        FlushInstructionCache(hProcess, o->pbTarget, o->pTrampoline->cbRestore);\n\n        if (o->fIsRemove && o->pTrampoline) {\n            detour_free_trampoline(o->pTrampoline);\n            o->pTrampoline = NULL;\n            freed = true;\n        }\n\n        DetourOperation *n = o->pNext;\n        delete o;\n        o = n;\n    }\n    s_pPendingOperations = NULL;\n\n    // Free any trampoline regions that are now unused.\n    if (freed && !s_fRetainRegions) {\n        detour_free_unused_trampoline_regions();\n    }\n\n    // Make sure the trampoline pages are no longer writable.\n    detour_runnable_trampoline_regions();\n\n    // Resume any suspended threads.\n    for (t = s_pPendingThreads; t != NULL;) {\n        // There is nothing we can do if this fails.\n        ResumeThread(t->hThread);\n\n        DetourThread *n = t->pNext;\n        delete t;\n        t = n;\n    }\n    s_pPendingThreads = NULL;\n    s_nPendingThreadId = 0;\n\n    if (pppFailedPointer != NULL) {\n        *pppFailedPointer = s_ppPendingError;\n    }\n\n    return s_nPendingError;\n}\n\nLONG WINAPI DetourUpdateThread(_In_ HANDLE hThread)\n{\n    LONG error;\n\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        return s_nPendingError;\n    }\n\n    // Silently (and safely) drop any attempt to suspend our own thread.\n    if (hThread == GetCurrentThread()) {\n        return NO_ERROR;\n    }\n\n    DetourThread *t = new NOTHROW DetourThread;\n    if (t == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n      fail:\n        if (t != NULL) {\n            delete t;\n            t = NULL;\n        }\n        s_nPendingError = error;\n        s_ppPendingError = NULL;\n        DETOUR_BREAK();\n        return error;\n    }\n\n    if (SuspendThread(hThread) == (DWORD)-1) {\n        error = (LONG)GetLastError();\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    t->hThread = hThread;\n    t->pNext = s_pPendingThreads;\n    s_pPendingThreads = t;\n\n    return NO_ERROR;\n}\n\n///////////////////////////////////////////////////////////// Transacted APIs.\n//\nLONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour)\n{\n    return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);\n}\n\nLONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,\n                           _In_ PVOID pDetour,\n                           _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,\n                           _Out_opt_ PVOID *ppRealTarget,\n                           _Out_opt_ PVOID *ppRealDetour)\n{\n    LONG error = NO_ERROR;\n\n    if (ppRealTrampoline != NULL) {\n        *ppRealTrampoline = NULL;\n    }\n    if (ppRealTarget != NULL) {\n        *ppRealTarget = NULL;\n    }\n    if (ppRealDetour != NULL) {\n        *ppRealDetour = NULL;\n    }\n    if (pDetour == NULL) {\n        DETOUR_TRACE((\"empty detour\\n\"));\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        DETOUR_TRACE((\"transaction conflict with thread id=%ld\\n\", s_nPendingThreadId));\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        DETOUR_TRACE((\"pending transaction error=%ld\\n\", s_nPendingError));\n        return s_nPendingError;\n    }\n\n    if (ppPointer == NULL) {\n        DETOUR_TRACE((\"ppPointer is null\\n\"));\n        return ERROR_INVALID_HANDLE;\n    }\n    if (*ppPointer == NULL) {\n        error = ERROR_INVALID_HANDLE;\n        s_nPendingError = error;\n        s_ppPendingError = ppPointer;\n        DETOUR_TRACE((\"*ppPointer is null (ppPointer=%p)\\n\", (void*)ppPointer));\n        DETOUR_BREAK();\n        return error;\n    }\n\n    PBYTE pbTarget = (PBYTE)*ppPointer;\n    PDETOUR_TRAMPOLINE pTrampoline = NULL;\n    DetourOperation *o = NULL;\n\n#ifdef DETOURS_IA64\n    PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;\n    PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget;\n    PVOID pDetourGlobals = NULL;\n    PVOID pTargetGlobals = NULL;\n\n    pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);\n    pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals);\n    DETOUR_TRACE((\"  ppldDetour=%p, code=%p [gp=%p]\\n\",\n                  ppldDetour, pDetour, pDetourGlobals));\n    DETOUR_TRACE((\"  ppldTarget=%p, code=%p [gp=%p]\\n\",\n                  ppldTarget, pbTarget, pTargetGlobals));\n#else // DETOURS_IA64\n    pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);\n    pDetour = DetourCodeFromPointer(pDetour, NULL);\n#endif // !DETOURS_IA64\n\n    // Don't follow a jump if its destination is the target function.\n    // This happens when the detour does nothing other than call the target.\n    if (pDetour == (PVOID)pbTarget) {\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (ppRealTarget != NULL) {\n        *ppRealTarget = pbTarget;\n    }\n    if (ppRealDetour != NULL) {\n        *ppRealDetour = pDetour;\n    }\n\n    o = new NOTHROW DetourOperation;\n    if (o == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n      fail:\n        s_nPendingError = error;\n        DETOUR_BREAK();\n      stop:\n        if (pTrampoline != NULL) {\n            detour_free_trampoline(pTrampoline);\n            pTrampoline = NULL;\n            if (ppRealTrampoline != NULL) {\n                *ppRealTrampoline = NULL;\n            }\n        }\n        if (o != NULL) {\n            delete o;\n            o = NULL;\n        }\n        s_ppPendingError = ppPointer;\n        return error;\n    }\n\n    pTrampoline = detour_alloc_trampoline(pbTarget);\n    if (pTrampoline == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    if (ppRealTrampoline != NULL) {\n        *ppRealTrampoline = pTrampoline;\n    }\n\n    DETOUR_TRACE((\"detours: pbTramp=%p, pDetour=%p\\n\", (void*)pTrampoline, pDetour));\n\n    memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign));\n\n    // Determine the number of movable target instructions.\n    PBYTE pbSrc = pbTarget;\n    PBYTE pbTrampoline = pTrampoline->rbCode;\n#ifdef DETOURS_IA64\n    PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1);\n#else\n    PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode);\n#endif\n    ULONG cbTarget = 0;\n    ULONG cbJump = SIZE_OF_JMP;\n    ULONG nAlign = 0;\n\n#ifdef DETOURS_ARM\n    // On ARM, we need an extra instruction when the function isn't 32-bit aligned.\n    // Check if the existing code is another detour (or at least a similar\n    // \"ldr pc, [PC+0]\" jump.\n    if ((ULONG)pbTarget & 2) {\n        cbJump += 2;\n\n        ULONG op = fetch_thumb_opcode(pbSrc);\n        if (op == 0xbf00) {\n            op = fetch_thumb_opcode(pbSrc + 2);\n            if (op == 0xf8dff000) { // LDR PC,[PC]\n                *((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++;\n                *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n                *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n                cbTarget = (LONG)(pbSrc - pbTarget);\n                // We will fall through the \"while\" because cbTarget is now >= cbJump.\n            }\n        }\n    }\n    else {\n        ULONG op = fetch_thumb_opcode(pbSrc);\n        if (op == 0xf8dff000) { // LDR PC,[PC]\n            *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n            *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n            cbTarget = (LONG)(pbSrc - pbTarget);\n            // We will fall through the \"while\" because cbTarget is now >= cbJump.\n        }\n    }\n#endif\n\n    while (cbTarget < cbJump) {\n        PBYTE pbOp = pbSrc;\n        LONG lExtra = 0;\n\n        DETOUR_TRACE((\" DetourCopyInstruction(%p,%p)\\n\",\n                      pbTrampoline, pbSrc));\n        pbSrc = (PBYTE)\n            DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra);\n        DETOUR_TRACE((\" DetourCopyInstruction() = %p (%d bytes)\\n\",\n                      pbSrc, (int)(pbSrc - pbOp)));\n        pbTrampoline += (pbSrc - pbOp) + lExtra;\n        cbTarget = (LONG)(pbSrc - pbTarget);\n        pTrampoline->rAlign[nAlign].obTarget = cbTarget;\n        pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode;\n        nAlign++;\n\n        if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) {\n            break;\n        }\n\n        if (detour_does_code_end_function(pbOp)) {\n            break;\n        }\n    }\n\n    // Consume, but don't duplicate padding if it is needed and available.\n    while (cbTarget < cbJump) {\n        LONG cFiller = detour_is_code_filler(pbSrc);\n        if (cFiller == 0) {\n            break;\n        }\n\n        pbSrc += cFiller;\n        cbTarget = (LONG)(pbSrc - pbTarget);\n    }\n\n#if DETOUR_DEBUG\n    {\n        DETOUR_TRACE((\" detours: rAlign [\"));\n        LONG n = 0;\n        for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n            if (pTrampoline->rAlign[n].obTarget == 0 &&\n                pTrampoline->rAlign[n].obTrampoline == 0) {\n                break;\n            }\n            DETOUR_TRACE((\" %d/%d\",\n                          pTrampoline->rAlign[n].obTarget,\n                          pTrampoline->rAlign[n].obTrampoline\n                          ));\n\n        }\n        DETOUR_TRACE((\" ]\\n\"));\n    }\n#endif\n\n    if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) {\n        // Too few instructions.\n\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (pbTrampoline > pbPool) {\n        __debugbreak();\n    }\n\n    pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode);\n    pTrampoline->cbRestore = (BYTE)cbTarget;\n    CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget);\n\n#if !defined(DETOURS_IA64)\n    if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) {\n        // Too many instructions.\n        error = ERROR_INVALID_HANDLE;\n        DETOUR_BREAK();\n        goto fail;\n    }\n#endif // !DETOURS_IA64\n\n    pTrampoline->pbRemain = pbTarget + cbTarget;\n    pTrampoline->pbDetour = (PBYTE)pDetour;\n\n#ifdef DETOURS_IA64\n    pTrampoline->ppldDetour = ppldDetour;\n    pTrampoline->ppldTarget = ppldTarget;\n    pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp;\n    pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals;\n\n    ((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop();\n\n    pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals);\n    pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain);\n\n    // Alloc frame:      alloc r41=ar.pfs,11,0,8,0; mov r40=rp\n    pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c;\n    pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200;\n    // save r36, r37, r38.\n    pTrampoline->bSave37to39.wide[0] = 0x031021004e019001;\n    pTrampoline->bSave37to39.wide[1] = 0x8401280600420098;\n    // save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34\n    pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800;\n    pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c;\n    // save gp,r32,r33\"  adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;;\n    pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001;\n    pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080;\n    // set detour GP.\n    pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals);\n    // call detour:      brl.call.sptk.few rp=detour ;;\n    pTrampoline->bCallDetour.wide[0] = 0x0000000100000005;\n    pTrampoline->bCallDetour.wide[1] = 0xd000001000000000;\n    pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour);\n    // pop frame & gp:   adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41\n    pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802;\n    pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005;\n    // return to caller: br.ret.sptk.many rp ;;\n    pTrampoline->bReturn.wide[0] = 0x0000000100000019;\n    pTrampoline->bReturn.wide[1] = 0x0084000880000200;\n\n    DETOUR_TRACE((\"detours: &bMovlTargetGp=%p\\n\", &pTrampoline->bMovlTargetGp));\n    DETOUR_TRACE((\"detours: &bMovlDetourGp=%p\\n\", &pTrampoline->bMovlDetourGp));\n#endif // DETOURS_IA64\n\n    pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode;\n#ifdef DETOURS_X64\n    pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_X64\n\n#ifdef DETOURS_X86\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_X86\n\n#ifdef DETOURS_ARM\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_ARM64\n\n    (void)pbTrampoline;\n\n    DWORD dwOld = 0;\n    if (!VirtualProtect(pbTarget, cbTarget, PAGE_EXECUTE_READWRITE, &dwOld)) {\n        error = GetLastError();\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x\\n\",\n                  pbTarget,\n                  pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],\n                  pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],\n                  pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));\n    DETOUR_TRACE((\"detours: pbTramp =%p: \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x\\n\",\n                  (void*)pTrampoline,\n                  pTrampoline->rbCode[0], pTrampoline->rbCode[1],\n                  pTrampoline->rbCode[2], pTrampoline->rbCode[3],\n                  pTrampoline->rbCode[4], pTrampoline->rbCode[5],\n                  pTrampoline->rbCode[6], pTrampoline->rbCode[7],\n                  pTrampoline->rbCode[8], pTrampoline->rbCode[9],\n                  pTrampoline->rbCode[10], pTrampoline->rbCode[11]));\n\n    o->fIsRemove = FALSE;\n    o->ppbPointer = (PBYTE*)ppPointer;\n    o->pTrampoline = pTrampoline;\n    o->pbTarget = pbTarget;\n    o->dwPerm = dwOld;\n    o->pNext = s_pPendingOperations;\n    s_pPendingOperations = o;\n\n    return NO_ERROR;\n}\n\nLONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour)\n{\n    LONG error = NO_ERROR;\n\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        return s_nPendingError;\n    }\n\n    if (pDetour == NULL) {\n        return ERROR_INVALID_PARAMETER;\n    }\n    if (ppPointer == NULL) {\n        return ERROR_INVALID_HANDLE;\n    }\n    if (*ppPointer == NULL) {\n        error = ERROR_INVALID_HANDLE;\n        s_nPendingError = error;\n        s_ppPendingError = ppPointer;\n        DETOUR_BREAK();\n        return error;\n    }\n\n    DetourOperation *o = new NOTHROW DetourOperation;\n    if (o == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n      fail:\n        s_nPendingError = error;\n        DETOUR_BREAK();\n      stop:\n        if (o != NULL) {\n            delete o;\n            o = NULL;\n        }\n        s_ppPendingError = ppPointer;\n        return error;\n    }\n\n\n#ifdef DETOURS_IA64\n    PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer;\n    PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;\n    PVOID pDetourGlobals = NULL;\n    PVOID pTrampoGlobals = NULL;\n\n    pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);\n    PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)\n        DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals);\n    DETOUR_TRACE((\"  ppldDetour=%p, code=%p [gp=%p]\\n\",\n                  ppldDetour, pDetour, pDetourGlobals));\n    DETOUR_TRACE((\"  ppldTrampo=%p, code=%p [gp=%p]\\n\",\n                  ppldTrampo, pTrampoline, pTrampoGlobals));\n\n\n    DETOUR_TRACE((\"\\n\"));\n    DETOUR_TRACE((\"detours:  &pldTrampoline  =%p\\n\",\n                  &pTrampoline->pldTrampoline));\n    DETOUR_TRACE((\"detours:  &bMovlTargetGp  =%p [%p]\\n\",\n                  &pTrampoline->bMovlTargetGp,\n                  pTrampoline->bMovlTargetGp.GetMovlGp()));\n    DETOUR_TRACE((\"detours:  &rbCode         =%p [%p]\\n\",\n                  &pTrampoline->rbCode,\n                  ((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  &bBrlRemainEip  =%p [%p]\\n\",\n                  &pTrampoline->bBrlRemainEip,\n                  pTrampoline->bBrlRemainEip.GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  &bMovlDetourGp  =%p [%p]\\n\",\n                  &pTrampoline->bMovlDetourGp,\n                  pTrampoline->bMovlDetourGp.GetMovlGp()));\n    DETOUR_TRACE((\"detours:  &bBrlDetourEip  =%p [%p]\\n\",\n                  &pTrampoline->bCallDetour,\n                  pTrampoline->bCallDetour.GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  pldDetour       =%p [%p]\\n\",\n                  pTrampoline->ppldDetour->EntryPoint,\n                  pTrampoline->ppldDetour->GlobalPointer));\n    DETOUR_TRACE((\"detours:  pldTarget       =%p [%p]\\n\",\n                  pTrampoline->ppldTarget->EntryPoint,\n                  pTrampoline->ppldTarget->GlobalPointer));\n    DETOUR_TRACE((\"detours:  pbRemain        =%p\\n\",\n                  pTrampoline->pbRemain));\n    DETOUR_TRACE((\"detours:  pbDetour        =%p\\n\",\n                  pTrampoline->pbDetour));\n    DETOUR_TRACE((\"\\n\"));\n#else // !DETOURS_IA64\n    PDETOUR_TRAMPOLINE pTrampoline =\n        (PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL);\n    pDetour = DetourCodeFromPointer(pDetour, NULL);\n#endif // !DETOURS_IA64\n\n    ////////////////////////////////////// Verify that Trampoline is in place.\n    //\n    LONG cbTarget = pTrampoline->cbRestore;\n    PBYTE pbTarget = pTrampoline->pbRemain - cbTarget;\n    if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) {\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (pTrampoline->pbDetour != pDetour) {\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    DWORD dwOld = 0;\n    if (!VirtualProtect(pbTarget, cbTarget,\n                        PAGE_EXECUTE_READWRITE, &dwOld)) {\n        error = (LONG)GetLastError();\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    o->fIsRemove = TRUE;\n    o->ppbPointer = (PBYTE*)ppPointer;\n    o->pTrampoline = pTrampoline;\n    o->pbTarget = pbTarget;\n    o->dwPerm = dwOld;\n    o->pNext = s_pPendingOperations;\n    s_pPendingOperations = o;\n\n    return NO_ERROR;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Helpers for manipulating page protection.\n//\n\n// For reference:\n//   PAGE_NOACCESS          0x01\n//   PAGE_READONLY          0x02\n//   PAGE_READWRITE         0x04\n//   PAGE_WRITECOPY         0x08\n//   PAGE_EXECUTE           0x10\n//   PAGE_EXECUTE_READ      0x20\n//   PAGE_EXECUTE_READWRITE 0x40\n//   PAGE_EXECUTE_WRITECOPY 0x80\n//   PAGE_GUARD             ...\n//   PAGE_NOCACHE           ...\n//   PAGE_WRITECOMBINE      ...\n\n#define DETOUR_PAGE_EXECUTE_ALL    (PAGE_EXECUTE |              \\\n                                    PAGE_EXECUTE_READ |         \\\n                                    PAGE_EXECUTE_READWRITE |    \\\n                                    PAGE_EXECUTE_WRITECOPY)\n\n#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS |             \\\n                                    PAGE_READONLY |             \\\n                                    PAGE_READWRITE |            \\\n                                    PAGE_WRITECOPY)\n\n#define DETOUR_PAGE_ATTRIBUTES     (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL))\n\nC_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL);\n\nstatic DWORD DetourPageProtectAdjustExecute(_In_  DWORD dwOldProtect,\n                                            _In_  DWORD dwNewProtect)\n//  Copy EXECUTE from dwOldProtect to dwNewProtect.\n{\n    bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);\n    bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);\n\n    if (fOldExecute && !fNewExecute) {\n        dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4)\n            | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);\n    }\n    else if (!fOldExecute && fNewExecute) {\n        dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4)\n            | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);\n    }\n    return dwNewProtect;\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_  HANDLE hProcess,\n                                              _In_  PVOID pAddress,\n                                              _In_  SIZE_T nSize,\n                                              _In_  DWORD dwNewProtect,\n                                              _Out_ PDWORD pdwOldProtect)\n// Some systems do not allow executability of a page to change. This function applies\n// dwNewProtect to [pAddress, nSize), but preserving the previous executability.\n// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx.\n// When \"restoring\" page protection, there is no need to use this function.\n{\n    MEMORY_BASIC_INFORMATION mbi;\n\n    // Query to get existing execute access.\n\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) {\n        return FALSE;\n    }\n    return VirtualProtectEx(hProcess, pAddress, nSize,\n                            DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect),\n                            pdwOldProtect);\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecute(_In_  PVOID pAddress,\n                                            _In_  SIZE_T nSize,\n                                            _In_  DWORD dwNewProtect,\n                                            _Out_ PDWORD pdwOldProtect)\n{\n    return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(),\n                                             pAddress, nSize, dwNewProtect, pdwOldProtect);\n}\n\n//  End of File\n"
  },
  {
    "path": "Detours/detours.h",
    "content": "/////////////////////////////////////////////////////////////////////////////\n//\n//  Core Detours Functionality (detours.h of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#pragma once\n#ifndef _DETOURS_H_\n#define _DETOURS_H_\n\n#if defined(_KERNEL_MODE)\n#define DETOURS_KERNEL\n#endif\n\n#ifdef DETOURS_KERNEL\n#include <minwindef.h>\n#include <ntimage.h>\n#endif\n\n#define DETOURS_VERSION     0x4c0c1   // 0xMAJORcMINORcPATCH\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n#undef DETOURS_X64\n#undef DETOURS_X86\n#undef DETOURS_IA64\n#undef DETOURS_ARM\n#undef DETOURS_ARM64\n#undef DETOURS_BITS\n#undef DETOURS_32BIT\n#undef DETOURS_64BIT\n\n#if defined(_X86_)\n#define DETOURS_X86\n#define DETOURS_OPTION_BITS 64\n\n#elif defined(_AMD64_)\n#define DETOURS_X64\n#define DETOURS_OPTION_BITS 32\n\n#elif defined(_IA64_)\n#define DETOURS_IA64\n#define DETOURS_OPTION_BITS 32\n\n#elif defined(_ARM_)\n#define DETOURS_ARM\n\n#elif defined(_ARM64_)\n#define DETOURS_ARM64\n\n#else\n#error Unknown architecture (x86, amd64, ia64, arm, arm64)\n#endif\n\n#ifdef _WIN64\n#undef DETOURS_32BIT\n#define DETOURS_64BIT 1\n#define DETOURS_BITS 64\n// If all 64bit kernels can run one and only one 32bit architecture.\n//#define DETOURS_OPTION_BITS 32\n#else\n#define DETOURS_32BIT 1\n#undef DETOURS_64BIT\n#define DETOURS_BITS 32\n// If all 64bit kernels can run one and only one 32bit architecture.\n//#define DETOURS_OPTION_BITS 32\n#endif\n\n#define VER_DETOURS_BITS    DETOUR_STRINGIFY(DETOURS_BITS)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n\n#if (_MSC_VER < 1299)\ntypedef LONG LONG_PTR;\ntypedef ULONG ULONG_PTR;\n#endif\n\n///////////////////////////////////////////////// SAL 2.0 Annotations w/o SAL.\n//\n//  These definitions are include so that Detours will build even if the\n//  compiler doesn't have full SAL 2.0 support.\n//\n#ifndef DETOURS_DONT_REMOVE_SAL_20\n\n#ifdef DETOURS_TEST_REMOVE_SAL_20\n#undef _Analysis_assume_\n#undef _Benign_race_begin_\n#undef _Benign_race_end_\n#undef _Field_range_\n#undef _Field_size_\n#undef _In_\n#undef _In_bytecount_\n#undef _In_count_\n#undef _In_opt_\n#undef _In_opt_bytecount_\n#undef _In_opt_count_\n#undef _In_opt_z_\n#undef _In_range_\n#undef _In_reads_\n#undef _In_reads_bytes_\n#undef _In_reads_opt_\n#undef _In_reads_opt_bytes_\n#undef _In_reads_or_z_\n#undef _In_z_\n#undef _Inout_\n#undef _Inout_opt_\n#undef _Inout_z_count_\n#undef _Out_\n#undef _Out_opt_\n#undef _Out_writes_\n#undef _Outptr_result_maybenull_\n#undef _Readable_bytes_\n#undef _Success_\n#undef _Writable_bytes_\n#undef _Pre_notnull_\n#endif\n\n#if defined(_Deref_out_opt_z_) && !defined(_Outptr_result_maybenull_)\n#define _Outptr_result_maybenull_ _Deref_out_opt_z_\n#endif\n\n#if defined(_In_count_) && !defined(_In_reads_)\n#define _In_reads_(x) _In_count_(x)\n#endif\n\n#if defined(_In_opt_count_) && !defined(_In_reads_opt_)\n#define _In_reads_opt_(x) _In_opt_count_(x)\n#endif\n\n#if defined(_In_opt_bytecount_) && !defined(_In_reads_opt_bytes_)\n#define _In_reads_opt_bytes_(x) _In_opt_bytecount_(x)\n#endif\n\n#if defined(_In_bytecount_) && !defined(_In_reads_bytes_)\n#define _In_reads_bytes_(x) _In_bytecount_(x)\n#endif\n\n#ifndef _In_\n#define _In_\n#endif\n\n#ifndef _In_bytecount_\n#define _In_bytecount_(x)\n#endif\n\n#ifndef _In_count_\n#define _In_count_(x)\n#endif\n\n#ifndef _In_opt_\n#define _In_opt_\n#endif\n\n#ifndef _In_opt_bytecount_\n#define _In_opt_bytecount_(x)\n#endif\n\n#ifndef _In_opt_count_\n#define _In_opt_count_(x)\n#endif\n\n#ifndef _In_opt_z_\n#define _In_opt_z_\n#endif\n\n#ifndef _In_range_\n#define _In_range_(x,y)\n#endif\n\n#ifndef _In_reads_\n#define _In_reads_(x)\n#endif\n\n#ifndef _In_reads_bytes_\n#define _In_reads_bytes_(x)\n#endif\n\n#ifndef _In_reads_opt_\n#define _In_reads_opt_(x)\n#endif\n\n#ifndef _In_reads_opt_bytes_\n#define _In_reads_opt_bytes_(x)\n#endif\n\n#ifndef _In_reads_or_z_\n#define _In_reads_or_z_\n#endif\n\n#ifndef _In_z_\n#define _In_z_\n#endif\n\n#ifndef _Inout_\n#define _Inout_\n#endif\n\n#ifndef _Inout_opt_\n#define _Inout_opt_\n#endif\n\n#ifndef _Inout_z_count_\n#define _Inout_z_count_(x)\n#endif\n\n#ifndef _Out_\n#define _Out_\n#endif\n\n#ifndef _Out_opt_\n#define _Out_opt_\n#endif\n\n#ifndef _Out_writes_\n#define _Out_writes_(x)\n#endif\n\n#ifndef _Outptr_result_maybenull_\n#define _Outptr_result_maybenull_\n#endif\n\n#ifndef _Writable_bytes_\n#define _Writable_bytes_(x)\n#endif\n\n#ifndef _Readable_bytes_\n#define _Readable_bytes_(x)\n#endif\n\n#ifndef _Success_\n#define _Success_(x)\n#endif\n\n#ifndef _Pre_notnull_\n#define _Pre_notnull_\n#endif\n\n#ifdef DETOURS_INTERNAL\n\n#pragma warning(disable:4615) // unknown warning type (suppress with older compilers)\n\n#ifndef _Benign_race_begin_\n#define _Benign_race_begin_\n#endif\n\n#ifndef _Benign_race_end_\n#define _Benign_race_end_\n#endif\n\n#ifndef _Field_size_\n#define _Field_size_(x)\n#endif\n\n#ifndef _Field_range_\n#define _Field_range_(x,y)\n#endif\n\n#ifndef _Analysis_assume_\n#define _Analysis_assume_(x)\n#endif\n\n#endif // DETOURS_INTERNAL\n#endif // DETOURS_DONT_REMOVE_SAL_20\n\n//////////////////////////////////////////////////////////////////////////////\n//\n#ifndef GUID_DEFINED\n#define GUID_DEFINED\ntypedef struct  _GUID\n{\n    DWORD Data1;\n    WORD Data2;\n    WORD Data3;\n    BYTE Data4[ 8 ];\n} GUID;\n\n#ifdef INITGUID\n#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n        const GUID name \\\n                = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }\n#else\n#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n    const GUID name\n#endif // INITGUID\n#endif // !GUID_DEFINED\n\n#if defined(__cplusplus)\n#ifndef _REFGUID_DEFINED\n#define _REFGUID_DEFINED\n#define REFGUID             const GUID &\n#endif // !_REFGUID_DEFINED\n#else // !__cplusplus\n#ifndef _REFGUID_DEFINED\n#define _REFGUID_DEFINED\n#define REFGUID             const GUID * const\n#endif // !_REFGUID_DEFINED\n#endif // !__cplusplus\n\n#ifndef ARRAYSIZE\n#define ARRAYSIZE(x)    (sizeof(x)/sizeof(x[0]))\n#endif\n\n//\n//////////////////////////////////////////////////////////////////////////////\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif // __cplusplus\n\n//\n// Constants\n//\n#define MoveMemory RtlMoveMemory\n#define CopyMemory RtlCopyMemory\n#define FillMemory RtlFillMemory\n#define ZeroMemory RtlZeroMemory\n\n#undef  NULL\n#define NULL nullptr\n\n/////////////////////////////////////////////////// Instruction Target Macros.\n//\n#define DETOUR_INSTRUCTION_TARGET_NONE          ((PVOID)0)\n#define DETOUR_INSTRUCTION_TARGET_DYNAMIC       ((PVOID)(LONG_PTR)-1)\n#define DETOUR_SECTION_HEADER_SIGNATURE         0x00727444   // \"Dtr\\0\"\n\nextern const GUID DETOUR_EXE_RESTORE_GUID;\nextern const GUID DETOUR_EXE_HELPER_GUID;\n\n#define DETOUR_TRAMPOLINE_SIGNATURE             0x21727444  // Dtr!\ntypedef struct _DETOUR_TRAMPOLINE DETOUR_TRAMPOLINE, *PDETOUR_TRAMPOLINE;\n\n/////////////////////////////////////////////////////////// Binary Structures.\n//\n#pragma pack(push, 8)\ntypedef struct _DETOUR_SECTION_HEADER\n{\n    DWORD       cbHeaderSize;\n    DWORD       nSignature;\n    DWORD       nDataOffset;\n    DWORD       cbDataSize;\n\n    DWORD       nOriginalImportVirtualAddress;\n    DWORD       nOriginalImportSize;\n    DWORD       nOriginalBoundImportVirtualAddress;\n    DWORD       nOriginalBoundImportSize;\n\n    DWORD       nOriginalIatVirtualAddress;\n    DWORD       nOriginalIatSize;\n    DWORD       nOriginalSizeOfImage;\n    DWORD       cbPrePE;\n\n    DWORD       nOriginalClrFlags;\n    DWORD       reserved1;\n    DWORD       reserved2;\n    DWORD       reserved3;\n\n    // Followed by cbPrePE bytes of data.\n} DETOUR_SECTION_HEADER, *PDETOUR_SECTION_HEADER;\n\ntypedef struct _DETOUR_SECTION_RECORD\n{\n    DWORD       cbBytes;\n    DWORD       nReserved;\n    GUID        guid;\n} DETOUR_SECTION_RECORD, *PDETOUR_SECTION_RECORD;\n\ntypedef struct _DETOUR_CLR_HEADER\n{\n    // Header versioning\n    ULONG                   cb;\n    USHORT                  MajorRuntimeVersion;\n    USHORT                  MinorRuntimeVersion;\n\n    // Symbol table and startup information\n    IMAGE_DATA_DIRECTORY    MetaData;\n    ULONG                   Flags;\n\n    // Followed by the rest of the IMAGE_COR20_HEADER\n} DETOUR_CLR_HEADER, *PDETOUR_CLR_HEADER;\n\ntypedef struct _DETOUR_EXE_RESTORE\n{\n    DWORD               cb;\n    DWORD               cbidh;\n    DWORD               cbinh;\n    DWORD               cbclr;\n\n    PBYTE               pidh;\n    PBYTE               pinh;\n    PBYTE               pclr;\n\n    IMAGE_DOS_HEADER    idh;\n    union {\n        IMAGE_NT_HEADERS    inh;\n        IMAGE_NT_HEADERS32  inh32;\n        IMAGE_NT_HEADERS64  inh64;\n        BYTE                raw[sizeof(IMAGE_NT_HEADERS64) +\n                                sizeof(IMAGE_SECTION_HEADER) * 32];\n    };\n    DETOUR_CLR_HEADER   clr;\n\n} DETOUR_EXE_RESTORE, *PDETOUR_EXE_RESTORE;\n\ntypedef struct _DETOUR_EXE_HELPER\n{\n    DWORD               cb;\n    DWORD               pid;\n    DWORD               nDlls;\n    CHAR                rDlls[4];\n} DETOUR_EXE_HELPER, *PDETOUR_EXE_HELPER;\n\n#pragma pack(pop)\n\n#define DETOUR_SECTION_HEADER_DECLARE(cbSectionSize) \\\n{ \\\n      sizeof(DETOUR_SECTION_HEADER),\\\n      DETOUR_SECTION_HEADER_SIGNATURE,\\\n      sizeof(DETOUR_SECTION_HEADER),\\\n      (cbSectionSize),\\\n      \\\n      0,\\\n      0,\\\n      0,\\\n      0,\\\n      \\\n      0,\\\n      0,\\\n      0,\\\n      0,\\\n}\n\n/////////////////////////////////////////////////////////////// Helper Macros.\n//\n#define DETOURS_STRINGIFY(x)    DETOURS_STRINGIFY_(x)\n#define DETOURS_STRINGIFY_(x)    #x\n\n#ifndef DETOURS_KERNEL\n///////////////////////////////////////////////////////////// Binary Typedefs.\n//\ntypedef BOOL (CALLBACK *PF_DETOUR_BINARY_BYWAY_CALLBACK)(\n    _In_opt_ PVOID pContext,\n    _In_opt_ LPCSTR pszFile,\n    _Outptr_result_maybenull_ LPCSTR *ppszOutFile);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_BINARY_FILE_CALLBACK)(\n    _In_opt_ PVOID pContext,\n    _In_ LPCSTR pszOrigFile,\n    _In_ LPCSTR pszFile,\n    _Outptr_result_maybenull_ LPCSTR *ppszOutFile);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_BINARY_SYMBOL_CALLBACK)(\n    _In_opt_ PVOID pContext,\n    _In_ ULONG nOrigOrdinal,\n    _In_ ULONG nOrdinal,\n    _Out_ ULONG *pnOutOrdinal,\n    _In_opt_ LPCSTR pszOrigSymbol,\n    _In_opt_ LPCSTR pszSymbol,\n    _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_BINARY_COMMIT_CALLBACK)(\n    _In_opt_ PVOID pContext);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_ENUMERATE_EXPORT_CALLBACK)(_In_opt_ PVOID pContext,\n                                                             _In_ ULONG nOrdinal,\n                                                             _In_opt_ LPCSTR pszName,\n                                                             _In_opt_ PVOID pCode);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FILE_CALLBACK)(_In_opt_ PVOID pContext,\n                                                        _In_opt_ HMODULE hModule,\n                                                        _In_opt_ LPCSTR pszFile);\n\ntypedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK)(_In_opt_ PVOID pContext,\n                                                        _In_ DWORD nOrdinal,\n                                                        _In_opt_ LPCSTR pszFunc,\n                                                        _In_opt_ PVOID pvFunc);\n\n// Same as PF_DETOUR_IMPORT_FUNC_CALLBACK but extra indirection on last parameter.\ntypedef BOOL (CALLBACK *PF_DETOUR_IMPORT_FUNC_CALLBACK_EX)(_In_opt_ PVOID pContext,\n                                                           _In_ DWORD nOrdinal,\n                                                           _In_opt_ LPCSTR pszFunc,\n                                                           _In_opt_ PVOID* ppvFunc);\n\ntypedef VOID * PDETOUR_BINARY;\ntypedef VOID * PDETOUR_LOADED_BINARY;\n#endif //!DETOURS_KERNEL\n\n//////////////////////////////////////////////////////////// Transaction APIs.\n//\nLONG WINAPI DetourTransactionBegin(VOID);\nLONG WINAPI DetourTransactionAbort(VOID);\nLONG WINAPI DetourTransactionCommit(VOID);\nLONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer);\n\nLONG WINAPI DetourUpdateThread(_In_ HANDLE hThread);\n\nLONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour);\n\nLONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,\n                           _In_ PVOID pDetour,\n                           _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,\n                           _Out_opt_ PVOID *ppRealTarget,\n                           _Out_opt_ PVOID *ppRealDetour);\n\nLONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour);\n\nBOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore);\nBOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain);\nPVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound);\nPVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound);\n\n////////////////////////////////////////////////////////////// Code Functions.\n//\nPVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,\n                                   _Out_opt_ PVOID *ppGlobals);\nPVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,\n                                   _Inout_opt_ PVOID *ppDstPool,\n                                   _In_ PVOID pSrc,\n                                   _Out_opt_ PVOID *ppTarget,\n                                   _Out_opt_ LONG *plExtra);\n\n#ifndef DETOURS_KERNEL\nPVOID WINAPI DetourFindFunction(_In_ LPCSTR pszModule,\n    _In_ LPCSTR pszFunction);\nBOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule,\n                                _In_ BOOL fLimitReferencesToModule);\n#endif // !DETOURS_KERNEL\n\n#ifndef DETOURS_KERNEL\n///////////////////////////////////////////////////// Loaded Binary Functions.\n//\nHMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr);\nHMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast);\nPVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule);\nULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule);\nBOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule,\n                                   _In_opt_ PVOID pContext,\n                                   _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport);\nBOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule,\n                                   _In_opt_ PVOID pContext,\n                                   _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,\n                                   _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc);\n\nBOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule,\n                                     _In_opt_ PVOID pContext,\n                                     _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,\n                                     _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFuncEx);\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule,\n                               _In_ REFGUID rguid,\n                               _Out_ DWORD *pcbData);\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid,\n                                 _Out_ DWORD * pcbData);\n\nDWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule);\n#endif // !DETOURS_KERNEL\n\n#ifndef DETOURS_KERNEL\n///////////////////////////////////////////////// Persistent Binary Functions.\n//\n\nPDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile);\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary,\n                                           _Out_opt_ GUID *pGuid,\n                                           _Out_ DWORD *pcbData,\n                                           _Inout_ DWORD *pnIterator);\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary,\n                                     _In_ REFGUID rguid,\n                                     _Out_ DWORD *pcbData);\n\nPVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary,\n                                    _In_ REFGUID rguid,\n                                    _In_reads_opt_(cbData) PVOID pData,\n                                    _In_ DWORD cbData);\nBOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary, _In_ REFGUID rguid);\nBOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary);\nBOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary);\nBOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary,\n                                    _In_opt_ PVOID pContext,\n                                    _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway,\n                                    _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile,\n                                    _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol,\n                                    _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit);\nBOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pBinary, _In_ HANDLE hFile);\nBOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary);\n#endif // !DETOURS_KERNEL\n\n#ifndef DETOURS_KERNEL\n/////////////////////////////////////////////////// Create Process & Load Dll.\n//\ntypedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEA)(\n    _In_opt_ LPCSTR lpApplicationName,\n    _Inout_opt_ LPSTR lpCommandLine,\n    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n    _In_ BOOL bInheritHandles,\n    _In_ DWORD dwCreationFlags,\n    _In_opt_ LPVOID lpEnvironment,\n    _In_opt_ LPCSTR lpCurrentDirectory,\n    _In_ LPSTARTUPINFOA lpStartupInfo,\n    _Out_ LPPROCESS_INFORMATION lpProcessInformation);\n\ntypedef BOOL (WINAPI *PDETOUR_CREATE_PROCESS_ROUTINEW)(\n    _In_opt_ LPCWSTR lpApplicationName,\n    _Inout_opt_ LPWSTR lpCommandLine,\n    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n    _In_ BOOL bInheritHandles,\n    _In_ DWORD dwCreationFlags,\n    _In_opt_ LPVOID lpEnvironment,\n    _In_opt_ LPCWSTR lpCurrentDirectory,\n    _In_ LPSTARTUPINFOW lpStartupInfo,\n    _Out_ LPPROCESS_INFORMATION lpProcessInformation);\n\nBOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName,\n                                        _Inout_opt_ LPSTR lpCommandLine,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                        _In_ BOOL bInheritHandles,\n                                        _In_ DWORD dwCreationFlags,\n                                        _In_opt_ LPVOID lpEnvironment,\n                                        _In_opt_ LPCSTR lpCurrentDirectory,\n                                        _In_ LPSTARTUPINFOA lpStartupInfo,\n                                        _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                        _In_ LPCSTR lpDllName,\n                                        _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);\n\nBOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName,\n                                        _Inout_opt_ LPWSTR lpCommandLine,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                        _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                        _In_ BOOL bInheritHandles,\n                                        _In_ DWORD dwCreationFlags,\n                                        _In_opt_ LPVOID lpEnvironment,\n                                        _In_opt_ LPCWSTR lpCurrentDirectory,\n                                        _In_ LPSTARTUPINFOW lpStartupInfo,\n                                        _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                        _In_ LPCSTR lpDllName,\n                                        _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);\n\n#ifdef UNICODE\n#define DetourCreateProcessWithDll      DetourCreateProcessWithDllW\n#define PDETOUR_CREATE_PROCESS_ROUTINE  PDETOUR_CREATE_PROCESS_ROUTINEW\n#else\n#define DetourCreateProcessWithDll      DetourCreateProcessWithDllA\n#define PDETOUR_CREATE_PROCESS_ROUTINE  PDETOUR_CREATE_PROCESS_ROUTINEA\n#endif // !UNICODE\n\nBOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName,\n                                          _Inout_opt_ LPSTR lpCommandLine,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                          _In_ BOOL bInheritHandles,\n                                          _In_ DWORD dwCreationFlags,\n                                          _In_opt_ LPVOID lpEnvironment,\n                                          _In_opt_ LPCSTR lpCurrentDirectory,\n                                          _In_ LPSTARTUPINFOA lpStartupInfo,\n                                          _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                          _In_ LPCSTR lpDllName,\n                                          _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);\n\nBOOL WINAPI DetourCreateProcessWithDllExW(_In_opt_ LPCWSTR lpApplicationName,\n                                          _Inout_opt_  LPWSTR lpCommandLine,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                          _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                          _In_ BOOL bInheritHandles,\n                                          _In_ DWORD dwCreationFlags,\n                                          _In_opt_ LPVOID lpEnvironment,\n                                          _In_opt_ LPCWSTR lpCurrentDirectory,\n                                          _In_ LPSTARTUPINFOW lpStartupInfo,\n                                          _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                          _In_ LPCSTR lpDllName,\n                                          _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);\n\n#ifdef UNICODE\n#define DetourCreateProcessWithDllEx    DetourCreateProcessWithDllExW\n#else\n#define DetourCreateProcessWithDllEx    DetourCreateProcessWithDllExA\n#endif // !UNICODE\n\nBOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName,\n                                         _Inout_opt_ LPSTR lpCommandLine,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                         _In_ BOOL bInheritHandles,\n                                         _In_ DWORD dwCreationFlags,\n                                         _In_opt_ LPVOID lpEnvironment,\n                                         _In_opt_ LPCSTR lpCurrentDirectory,\n                                         _In_ LPSTARTUPINFOA lpStartupInfo,\n                                         _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                         _In_ DWORD nDlls,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);\n\nBOOL WINAPI DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName,\n                                         _Inout_opt_ LPWSTR lpCommandLine,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,\n                                         _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,\n                                         _In_ BOOL bInheritHandles,\n                                         _In_ DWORD dwCreationFlags,\n                                         _In_opt_ LPVOID lpEnvironment,\n                                         _In_opt_ LPCWSTR lpCurrentDirectory,\n                                         _In_ LPSTARTUPINFOW lpStartupInfo,\n                                         _Out_ LPPROCESS_INFORMATION lpProcessInformation,\n                                         _In_ DWORD nDlls,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);\n\n#ifdef UNICODE\n#define DetourCreateProcessWithDlls     DetourCreateProcessWithDllsW\n#else\n#define DetourCreateProcessWithDlls     DetourCreateProcessWithDllsA\n#endif // !UNICODE\n\nBOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid,\n                                    _In_ LPCSTR lpDllName,\n                                    _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);\n\nBOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid,\n                                    _In_ LPCSTR lpDllName,\n                                    _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);\n\n#ifdef UNICODE\n#define DetourProcessViaHelper          DetourProcessViaHelperW\n#else\n#define DetourProcessViaHelper          DetourProcessViaHelperA\n#endif // !UNICODE\n\nBOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid,\n                                        _In_ DWORD nDlls,\n                                        _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                        _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA);\n\nBOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid,\n                                        _In_ DWORD nDlls,\n                                        _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                        _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW);\n\n#ifdef UNICODE\n#define DetourProcessViaHelperDlls      DetourProcessViaHelperDllsW\n#else\n#define DetourProcessViaHelperDlls      DetourProcessViaHelperDllsA\n#endif // !UNICODE\n\n#endif // !DETOURS_KERNEL\n\nBOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess,\n                                       _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                       _In_ DWORD nDlls);\n\nBOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess,\n                                         _In_ HMODULE hImage,\n                                         _In_ BOOL bIs32Bit,\n                                         _In_reads_(nDlls) LPCSTR *rlpDlls,\n                                         _In_ DWORD nDlls);\n\nBOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess,\n                                       _In_ REFGUID rguid,\n                                       _In_reads_bytes_(cbData) PVOID pvData,\n                                       _In_ DWORD cbData);\n\n#ifndef DETOURS_KERNEL\nBOOL WINAPI DetourRestoreAfterWith(VOID);\nBOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData,\n                                     _In_ DWORD cbData);\nBOOL WINAPI DetourIsHelperProcess(VOID);\nVOID CALLBACK DetourFinishHelperProcess(_In_ HWND,\n                                        _In_ HINSTANCE,\n                                        _In_ LPSTR,\n                                        _In_ INT);\n#endif // !DETOURS_KERNEL\n\n//\n//////////////////////////////////////////////////////////////////////////////\n#ifdef __cplusplus\n}\n#endif // __cplusplus\n\n//////////////////////////////////////////////// Detours Internal Definitions.\n//\n#ifdef __cplusplus\n#ifdef DETOURS_INTERNAL\n\n#define NOTHROW\n// #define NOTHROW (nothrow)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n#ifndef DETOURS_KERNEL\n#if (_MSC_VER < 1299)\n#include <imagehlp.h>\ntypedef IMAGEHLP_MODULE IMAGEHLP_MODULE64;\ntypedef PIMAGEHLP_MODULE PIMAGEHLP_MODULE64;\ntypedef IMAGEHLP_SYMBOL SYMBOL_INFO;\ntypedef PIMAGEHLP_SYMBOL PSYMBOL_INFO;\n\nstatic inline\nLONG InterlockedCompareExchange(_Inout_ LONG *ptr, _In_ LONG nval, _In_ LONG oval)\n{\n    return (LONG)::InterlockedCompareExchange((PVOID*)ptr, (PVOID)nval, (PVOID)oval);\n}\n#else\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#include <dbghelp.h>\n#pragma warning(pop)\n#endif\n\n#ifdef IMAGEAPI // defined by DBGHELP.H\ntypedef LPAPI_VERSION (NTAPI *PF_ImagehlpApiVersionEx)(_In_ LPAPI_VERSION AppVersion);\n\ntypedef BOOL (NTAPI *PF_SymInitialize)(_In_ HANDLE hProcess,\n                                       _In_opt_ LPCSTR UserSearchPath,\n                                       _In_ BOOL fInvadeProcess);\ntypedef DWORD (NTAPI *PF_SymSetOptions)(_In_ DWORD SymOptions);\ntypedef DWORD (NTAPI *PF_SymGetOptions)(VOID);\ntypedef DWORD64 (NTAPI *PF_SymLoadModule64)(_In_ HANDLE hProcess,\n                                            _In_opt_ HANDLE hFile,\n                                            _In_ LPSTR ImageName,\n                                            _In_opt_ LPSTR ModuleName,\n                                            _In_ DWORD64 BaseOfDll,\n                                            _In_opt_ DWORD SizeOfDll);\ntypedef BOOL (NTAPI *PF_SymGetModuleInfo64)(_In_ HANDLE hProcess,\n                                            _In_ DWORD64 qwAddr,\n                                            _Out_ PIMAGEHLP_MODULE64 ModuleInfo);\ntypedef BOOL (NTAPI *PF_SymFromName)(_In_ HANDLE hProcess,\n                                     _In_ LPSTR Name,\n                                     _Out_ PSYMBOL_INFO Symbol);\n\ntypedef struct _DETOUR_SYM_INFO\n{\n    HANDLE                  hProcess;\n    HMODULE                 hDbgHelp;\n    PF_ImagehlpApiVersionEx pfImagehlpApiVersionEx;\n    PF_SymInitialize        pfSymInitialize;\n    PF_SymSetOptions        pfSymSetOptions;\n    PF_SymGetOptions        pfSymGetOptions;\n    PF_SymLoadModule64      pfSymLoadModule64;\n    PF_SymGetModuleInfo64   pfSymGetModuleInfo64;\n    PF_SymFromName          pfSymFromName;\n} DETOUR_SYM_INFO, *PDETOUR_SYM_INFO;\n\nPDETOUR_SYM_INFO DetourLoadImageHlp(VOID);\n\n#endif // IMAGEAPI\n#endif // !DETOURS_KERNEL\n\n#if defined(_INC_STDIO) && !defined(_CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS)\n#error detours.h must be included before stdio.h (or at least define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS earlier)\n#endif\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n\n#ifndef DETOUR_TRACE\n#if DETOUR_DEBUG && !defined(DETOURS_KERNEL)\n#define DETOUR_TRACE(x) printf x\n#define DETOUR_BREAK()  __debugbreak()\n#include <stdio.h>\n#include <limits.h>\n#else\n#define DETOUR_TRACE(x)\n#define DETOUR_BREAK()\n#endif\n#endif\n\n#if 1 || defined(DETOURS_IA64)\n\n//\n// IA64 instructions are 41 bits, 3 per bundle, plus 5 bit bundle template => 128 bits per bundle.\n//\n\n#define DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE (3)\n\n#define DETOUR_IA64_TEMPLATE_OFFSET (0)\n#define DETOUR_IA64_TEMPLATE_SIZE   (5)\n\n#define DETOUR_IA64_INSTRUCTION_SIZE (41)\n#define DETOUR_IA64_INSTRUCTION0_OFFSET (DETOUR_IA64_TEMPLATE_SIZE)\n#define DETOUR_IA64_INSTRUCTION1_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE)\n#define DETOUR_IA64_INSTRUCTION2_OFFSET (DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTION_SIZE + DETOUR_IA64_INSTRUCTION_SIZE)\n\nC_ASSERT(DETOUR_IA64_TEMPLATE_SIZE + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * DETOUR_IA64_INSTRUCTION_SIZE == 128);\n\n__declspec(align(16)) struct DETOUR_IA64_BUNDLE\n{\n  public:\n    union\n    {\n        BYTE    data[16];\n        UINT64  wide[2];\n    };\n\n    enum {\n        A_UNIT  = 1u,\n        I_UNIT  = 2u,\n        M_UNIT  = 3u,\n        B_UNIT  = 4u,\n        F_UNIT  = 5u,\n        L_UNIT  = 6u,\n        X_UNIT  = 7u,\n    };\n    struct DETOUR_IA64_METADATA\n    {\n        ULONG       nTemplate       : 8;    // Instruction template.\n        ULONG       nUnit0          : 4;    // Unit for slot 0\n        ULONG       nUnit1          : 4;    // Unit for slot 1\n        ULONG       nUnit2          : 4;    // Unit for slot 2\n    };\n\n  protected:\n    static const DETOUR_IA64_METADATA s_rceCopyTable[33];\n\n    UINT RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const;\n\n    bool RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst,\n                             _In_ BYTE slot,\n                             _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const;\n\n    // 120 112 104 96 88 80 72 64 56 48 40 32 24 16  8  0\n    //  f.  e.  d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.\n\n    //                                      00\n    // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.\n    // 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0]\n    // 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41..  5]\n    // 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42]\n    // 0000 0000 0007 ffff ffff c000 0000 0000 : One  [ 82.. 46]\n    // 0000 0000 0078 0000 0000 0000 0000 0000 : One  [ 86.. 83]\n    // 0fff ffff ff80 0000 0000 0000 0000 0000 : Two  [123.. 87]\n    // f000 0000 0000 0000 0000 0000 0000 0000 : Two  [127..124]\n    BYTE    GetTemplate() const;\n    // Get 4 bit opcodes.\n    BYTE    GetInst0() const;\n    BYTE    GetInst1() const;\n    BYTE    GetInst2() const;\n    BYTE    GetUnit(BYTE slot) const;\n    BYTE    GetUnit0() const;\n    BYTE    GetUnit1() const;\n    BYTE    GetUnit2() const;\n    // Get 37 bit data.\n    UINT64  GetData0() const;\n    UINT64  GetData1() const;\n    UINT64  GetData2() const;\n\n    // Get/set the full 41 bit instructions.\n    UINT64  GetInstruction(BYTE slot) const;\n    UINT64  GetInstruction0() const;\n    UINT64  GetInstruction1() const;\n    UINT64  GetInstruction2() const;\n    void    SetInstruction(BYTE slot, UINT64 instruction);\n    void    SetInstruction0(UINT64 instruction);\n    void    SetInstruction1(UINT64 instruction);\n    void    SetInstruction2(UINT64 instruction);\n\n    // Get/set bitfields.\n    static UINT64 GetBits(UINT64 Value, UINT64 Offset, UINT64 Count);\n    static UINT64 SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field);\n\n    // Get specific read-only fields.\n    static UINT64 GetOpcode(UINT64 instruction); // 4bit opcode\n    static UINT64 GetX(UINT64 instruction); // 1bit opcode extension\n    static UINT64 GetX3(UINT64 instruction); // 3bit opcode extension\n    static UINT64 GetX6(UINT64 instruction); // 6bit opcode extension\n\n    // Get/set specific fields.\n    static UINT64 GetImm7a(UINT64 instruction);\n    static UINT64 SetImm7a(UINT64 instruction, UINT64 imm7a);\n    static UINT64 GetImm13c(UINT64 instruction);\n    static UINT64 SetImm13c(UINT64 instruction, UINT64 imm13c);\n    static UINT64 GetSignBit(UINT64 instruction);\n    static UINT64 SetSignBit(UINT64 instruction, UINT64 signBit);\n    static UINT64 GetImm20a(UINT64 instruction);\n    static UINT64 SetImm20a(UINT64 instruction, UINT64 imm20a);\n    static UINT64 GetImm20b(UINT64 instruction);\n    static UINT64 SetImm20b(UINT64 instruction, UINT64 imm20b);\n\n    static UINT64 SignExtend(UINT64 Value, UINT64 Offset);\n\n    BOOL    IsMovlGp() const;\n\n    VOID    SetInst(BYTE Slot, BYTE nInst);\n    VOID    SetInst0(BYTE nInst);\n    VOID    SetInst1(BYTE nInst);\n    VOID    SetInst2(BYTE nInst);\n    VOID    SetData(BYTE Slot, UINT64 nData);\n    VOID    SetData0(UINT64 nData);\n    VOID    SetData1(UINT64 nData);\n    VOID    SetData2(UINT64 nData);\n    BOOL    SetNop(BYTE Slot);\n    BOOL    SetNop0();\n    BOOL    SetNop1();\n    BOOL    SetNop2();\n\n  public:\n    BOOL    IsBrl() const;\n    VOID    SetBrl();\n    VOID    SetBrl(UINT64 target);\n    UINT64  GetBrlTarget() const;\n    VOID    SetBrlTarget(UINT64 target);\n    VOID    SetBrlImm(UINT64 imm);\n    UINT64  GetBrlImm() const;\n\n    UINT64  GetMovlGp() const;\n    VOID    SetMovlGp(UINT64 gp);\n\n    VOID    SetStop();\n\n    UINT    Copy(_Out_ DETOUR_IA64_BUNDLE *pDst, _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra = NULL) const;\n};\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_ARM\n\n#define DETOURS_PFUNC_TO_PBYTE(p)  ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1))\n#define DETOURS_PBYTE_TO_PFUNC(p)  ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1))\n\n#endif // DETOURS_ARM\n\n//////////////////////////////////////////////////////////////////////////////\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif // __cplusplus\n\n#define DETOUR_OFFLINE_LIBRARY(x)                                       \\\nPVOID WINAPI DetourCopyInstruction##x(_In_opt_ PVOID pDst,              \\\n                                      _Inout_opt_ PVOID *ppDstPool,     \\\n                                      _In_ PVOID pSrc,                  \\\n                                      _Out_opt_ PVOID *ppTarget,        \\\n                                      _Out_opt_ LONG *plExtra);         \\\n                                                                        \\\nBOOL WINAPI DetourSetCodeModule##x(_In_ HMODULE hModule,                \\\n                                   _In_ BOOL fLimitReferencesToModule); \\\n\nDETOUR_OFFLINE_LIBRARY(X86)\nDETOUR_OFFLINE_LIBRARY(X64)\nDETOUR_OFFLINE_LIBRARY(ARM)\nDETOUR_OFFLINE_LIBRARY(ARM64)\nDETOUR_OFFLINE_LIBRARY(IA64)\n\n#undef DETOUR_OFFLINE_LIBRARY\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Helpers for manipulating page protection.\n//\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_  HANDLE hProcess,\n                                              _In_  PVOID pAddress,\n                                              _In_  SIZE_T nSize,\n                                              _In_  DWORD dwNewProtect,\n                                              _Out_ PDWORD pdwOldProtect);\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecute(_In_  PVOID pAddress,\n                                            _In_  SIZE_T nSize,\n                                            _In_  DWORD dwNewProtect,\n                                            _Out_ PDWORD pdwOldProtect);\n#ifdef __cplusplus\n}\n#endif // __cplusplus\n\n//////////////////////////////////////////////////////////////////////////////\n\n#define MM_ALLOCATION_GRANULARITY 0x10000\n\n//////////////////////////////////////////////////////////////////////////////\n\n#endif // DETOURS_INTERNAL\n#endif // __cplusplus\n\n#endif // _DETOURS_H_\n//\n////////////////////////////////////////////////////////////////  End of File.\n"
  },
  {
    "path": "Detours/detoursx.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Core Detours Functionality (detours.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#pragma warning(disable:4068) // unknown pragma (suppress)\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#if defined(_KERNEL_MODE)\n#define DETOURS_KERNEL\n#endif\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n\n#include <Veil.h>\n\n#ifdef DETOURS_KERNEL\n#include \"api_thunks.h\"\n#endif\n\n#include <intsafe.h>\n\n#if (_MSC_VER < 1299)\n#pragma warning(disable: 4710)\n#endif\n\n//#define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\n#define NOTHROW\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstruct _DETOUR_ALIGN\n{\n    BYTE    obTarget        : 3;\n    BYTE    obTrampoline    : 5;\n};\n\nC_ASSERT(sizeof(_DETOUR_ALIGN) == 1);\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstatic void* detour_memory_alloc(size_t size)\n{\n#pragma warning(suppress: 4996)\n    return ExAllocatePoolWithTag(NonPagedPool, size, DETOUR_SECTION_HEADER_SIGNATURE);\n}\n\nstatic void  detour_memory_free(void* p)\n{\n    ExFreePoolWithTag(p, DETOUR_SECTION_HEADER_SIGNATURE);\n}\n\nstatic PMDL detour_remap_address(_In_ void* va, _In_ unsigned long size, _Out_ void** new_va)\n{\n    PMDL  Mdl = NULL;\n    void* NewVA = NULL;\n\n    for (;;) {\n        Mdl = IoAllocateMdl(va, size, FALSE, FALSE, NULL);\n        if (Mdl == NULL) {\n            SetLastError(ERROR_NOT_ENOUGH_MEMORY);\n            break;\n        }\n\n        __try {\n            MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);\n        }\n        __except (EXCEPTION_EXECUTE_HANDLER) {\n            IoFreeMdl(Mdl), Mdl = NULL;\n            SetLastError(GetExceptionCode());\n            break;\n        }\n\n        NewVA = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);\n        if (NewVA == NULL) {\n            MmUnlockPages(Mdl);\n            IoFreeMdl(Mdl), Mdl = NULL;\n\n            SetLastError(ERROR_NOT_ENOUGH_MEMORY);\n            break;\n        }\n\n        *new_va = NewVA;\n        break;\n    }\n\n    return Mdl;\n}\n\nstatic void detour_unmap_address(_In_ PMDL mdl)\n{\n    if (mdl) {\n        MmUnlockPages(mdl);\n        IoFreeMdl(mdl);\n    }\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Region reserved for system DLLs, which cannot be used for trampolines.\n//\nstatic PVOID    s_pSystemRegionLowerBound   = (PVOID)(ULONG_PTR)0x70000000;\nstatic PVOID    s_pSystemRegionUpperBound   = (PVOID)(ULONG_PTR)0x80000000;\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstatic bool detour_is_imported(PBYTE pbCode, PBYTE pbAddress)\n{\n    PVOID ImageBase = NULL;\n\n    RtlPcToFileHeader(pbCode, &ImageBase);\n    if (ImageBase == NULL) {\n        return false;\n    }\n\n    __try {\n        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)ImageBase;\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            return false;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            return false;\n        }\n\n        if (pbAddress >= ((PBYTE)pDosHeader +\n                          pNtHeader->OptionalHeader\n                          .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) &&\n            pbAddress < ((PBYTE)pDosHeader +\n                         pNtHeader->OptionalHeader\n                         .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress +\n                         pNtHeader->OptionalHeader\n                         .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) {\n            return true;\n        }\n    }\n#pragma prefast(suppress:28940, \"A bad pointer means this probably isn't a PE header.\")\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        return false;\n    }\n    return false;\n}\n\ninline ULONG_PTR detour_2gb_below(ULONG_PTR address)\n{\n    return (address > (ULONG_PTR)0x7ff80000) ? address - 0x7ff80000 : 0x80000;\n}\n\ninline ULONG_PTR detour_2gb_above(ULONG_PTR address)\n{\n#if defined(DETOURS_64BIT)\n    return (address < (ULONG_PTR)0xffffffff80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfffffffffff80000;\n#else\n    return (address < (ULONG_PTR)0x80000000) ? address + 0x7ff80000 : (ULONG_PTR)0xfff80000;\n#endif\n}\n\n///////////////////////////////////////////////////////////////////////// X86.\n//\n#ifdef DETOURS_X86\n\nstruct _DETOUR_TRAMPOLINE\n{\n    BYTE            rbCode[30];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[22];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 72);\n\nenum {\n    SIZE_OF_JMP = 5\n};\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbFixedCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbFixedCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)\n{\n    *pbCode++ = 0xff;   // jmp [+imm32]\n    *pbCode++ = 0x25;\n    *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        *pbCode++ = 0xcc;   // brk;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // First, skip over the import vector if there is one.\n    if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [imm32]\n        // Looks like an import alias jump, then get the code it points to.\n        PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];\n        if (detour_is_imported(pbCode, pbTarget)) {\n            PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;\n            DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n\n    // Then, skip over a patch jump\n    if (pbCode[0] == 0xeb) {   // jmp +imm8\n        PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];\n        DETOUR_TRACE((\"%p->%p: skipped over short jump.\\n\", pbCode, pbNew));\n        pbCode = pbNew;\n\n        // First, skip over the import vector if there is one.\n        if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [imm32]\n            // Looks like an import alias jump, then get the code it points to.\n            PBYTE pbTarget = *(UNALIGNED PBYTE *)&pbCode[2];\n            if (detour_is_imported(pbCode, pbTarget)) {\n                pbNew = *(UNALIGNED PBYTE *)pbTarget;\n                DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                pbCode = pbNew;\n            }\n        }\n        // Finally, skip over a long jump if it is the target of the patch jump.\n        else if (pbCode[0] == 0xe9) {   // jmp +imm32\n            pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n            DETOUR_TRACE((\"%p->%p: skipped over long jump.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    // We have to place trampolines within +/- 2GB of code.\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%p..%p..%p]\\n\", (PVOID)lo, pbCode, (PVOID)hi));\n\n    // And, within +/- 2GB of relative jmp targets.\n    if (pbCode[0] == 0xe9) {   // jmp +imm32\n        PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32*) & pbCode[1];\n\n        if (pbNew < pbCode) {\n            hi = detour_2gb_above((ULONG_PTR)pbNew);\n        }\n        else {\n            lo = detour_2gb_below((ULONG_PTR)pbNew);\n        }\n        DETOUR_TRACE((\"[%p..%p..%p] +imm32\\n\", (PVOID)lo, pbCode, (PVOID)hi));\n    }\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    if (pbCode[0] == 0xeb ||    // jmp +imm8\n        pbCode[0] == 0xe9 ||    // jmp +imm32\n        pbCode[0] == 0xe0 ||    // jmp eax\n        pbCode[0] == 0xc2 ||    // ret +imm8\n        pbCode[0] == 0xc3 ||    // ret\n        pbCode[0] == 0xcc) {    // brk\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) {  // rep ret\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xff && pbCode[1] == 0x25) {  // jmp [+imm32]\n        return TRUE;\n    }\n    else if ((pbCode[0] == 0x26 ||      // jmp es:\n              pbCode[0] == 0x2e ||      // jmp cs:\n              pbCode[0] == 0x36 ||      // jmp ss:\n              pbCode[0] == 0x3e ||      // jmp ds:\n              pbCode[0] == 0x64 ||      // jmp fs:\n              pbCode[0] == 0x65) &&     // jmp gs:\n             pbCode[1] == 0xff &&       // jmp [+imm32]\n             pbCode[2] == 0x25) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // 1-byte through 11-byte NOPs.\n    if (pbCode[0] == 0x90) {\n        return 1;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {\n        return 2;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {\n        return 3;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&\n        pbCode[3] == 0x00) {\n        return 4;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00) {\n        return 5;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {\n        return 6;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00) {\n        return 7;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00) {\n        return 8;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {\n        return 9;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&\n        pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00) {\n        return 10;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&\n        pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00 && pbCode[10] == 0x00) {\n        return 11;\n    }\n\n    // int 3.\n    if (pbCode[0] == 0xcc) {\n        return 1;\n    }\n    return 0;\n}\n\n#endif // DETOURS_X86\n\n///////////////////////////////////////////////////////////////////////// X64.\n//\n#ifdef DETOURS_X64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // An X64 instuction can be 15 bytes long.\n    // In practice 11 seems to be the limit.\n    BYTE            rbCode[30];     // target code + jmp to pbRemain.\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[30];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n    PBYTE           pbCodeIn;       // jmp [pbDetour]\n    PMDL            pbCodeInMdl;\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);\n\nenum {\n    SIZE_OF_JMP = 6 // jmp[+imm32]\n};\n\nstatic PIMAGE_SECTION_HEADER detour_va_to_section(PVOID base, PIMAGE_NT_HEADERS nt, PBYTE va)\n{\n    PIMAGE_SECTION_HEADER NtSection = IMAGE_FIRST_SECTION(nt);\n    for (size_t i = 0u; i < nt->FileHeader.NumberOfSections; ++i){\n        if (va >= ((PBYTE)base + NtSection->VirtualAddress) &&\n            va < ((PBYTE)base + NtSection->VirtualAddress + NtSection->Misc.VirtualSize)) {\n\n            return NtSection;\n        }\n        ++NtSection;\n    }\n\n    return NULL;\n}\n\nPBYTE detour_gen_codein(PBYTE pbCode, PVOID pDetour, PMDL* pMdl)\n{\n    PVOID ImageBase = NULL;\n    PIMAGE_SECTION_HEADER NtSection = NULL;\n    PIMAGE_SECTION_HEADER NtSectionLast = NULL;\n\n    if (RtlPcToFileHeader(pbCode, &ImageBase) == NULL) {\n        return NULL;\n    }\n\n    PIMAGE_NT_HEADERS NtHeader = RtlImageNtHeader(ImageBase);\n    if (NtHeader == NULL) {\n        return NULL;\n    }\n\n    NtSection = detour_va_to_section(ImageBase, NtHeader, pbCode);\n    if (NtSection == NULL) {\n        return NULL;\n    }\n\n    NtSectionLast = NtSection + 1;\n    if (NtSectionLast >= (IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections)) {\n        NtSectionLast = NULL;\n    }\n\n    PBYTE CodeIn    = (PBYTE)ImageBase + NtSection->VirtualAddress + NtSection->Misc.VirtualSize;\n    PBYTE CodeInEnd = (PBYTE)ImageBase + (NtSectionLast != NULL\n                ? NtSectionLast->VirtualAddress\n                : NtHeader->OptionalHeader.SizeOfImage);\n\n    CodeIn = (PBYTE)(((ULONG_PTR)CodeIn / 0x10 + 1) * 0x10);\n\n    PBYTE NewCodeIn = NULL;\n    do\n    {\n        if (*(PVOID*)CodeIn == NULL) {\n            *pMdl = detour_remap_address(CodeIn, sizeof(void*), (PVOID*)&NewCodeIn);\n            if (NewCodeIn) {\n                *(PVOID*)NewCodeIn = pDetour;\n                return CodeIn;\n            }\n        }\n\n        CodeIn += sizeof(void*);\n    } while (CodeIn < CodeInEnd);\n\n    return NULL;\n}\n\nvoid detour_del_codein(PMDL pMdl)\n{\n    if (pMdl) {\n        PVOID va = MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);\n        memset(va, 0, sizeof(void*));\n\n        detour_unmap_address(pMdl);\n    }\n}\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE pbFixedCode, PBYTE pbJmpVal)\n{\n    PBYTE pbJmpSrc = pbFixedCode + 5;\n    *pbCode++ = 0xE9;   // jmp +imm32\n    *((INT32*&)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE *ppbJmpVal)\n{\n    PBYTE pbJmpSrc = pbCode + 6;\n    *pbCode++ = 0xff;   // jmp [+imm32]\n    *pbCode++ = 0x25;\n    *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_jmp_indirect(PBYTE pbCode, PBYTE pbFixedCode, PBYTE* ppbJmpVal)\n{\n    PBYTE pbJmpSrc = pbFixedCode + 6;\n    *pbCode++ = 0xff;   // jmp [+imm32]\n    *pbCode++ = 0x25;\n    *((INT32*&)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc);\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        *pbCode++ = 0xcc;   // brk;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // First, skip over the import vector if there is one.\n    if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]\n        // Looks like an import alias jump, then get the code it points to.\n        PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];\n        if (detour_is_imported(pbCode, pbTarget)) {\n            PBYTE pbNew = *(UNALIGNED PBYTE *)pbTarget;\n            DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n\n    // Then, skip over a patch jump\n    if (pbCode[0] == 0xeb) {   // jmp +imm8\n        PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];\n        DETOUR_TRACE((\"%p->%p: skipped over short jump.\\n\", pbCode, pbNew));\n        pbCode = pbNew;\n\n        // First, skip over the import vector if there is one.\n        if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]\n            // Looks like an import alias jump, then get the code it points to.\n            PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32 *)&pbCode[2];\n            if (detour_is_imported(pbCode, pbTarget)) {\n                pbNew = *(UNALIGNED PBYTE *)pbTarget;\n                DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                pbCode = pbNew;\n            }\n        }\n        // Finally, skip over a long jump if it is the target of the patch jump.\n        else if (pbCode[0] == 0xe9) {   // jmp +imm32\n            pbNew = pbCode + 5 + *(UNALIGNED INT32 *)&pbCode[1];\n            DETOUR_TRACE((\"%p->%p: skipped over long jump.\\n\", pbCode, pbNew));\n            pbCode = pbNew;\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    (void)pbCode;\n    *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;\n    *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    if (pbCode[0] == 0xeb ||    // jmp +imm8\n        pbCode[0] == 0xe9 ||    // jmp +imm32\n        pbCode[0] == 0xe0 ||    // jmp eax\n        pbCode[0] == 0xc2 ||    // ret +imm8\n        pbCode[0] == 0xc3 ||    // ret\n        pbCode[0] == 0xcc) {    // brk\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) {  // rep ret\n        return TRUE;\n    }\n    else if (pbCode[0] == 0xff && pbCode[1] == 0x25) {  // jmp [+imm32]\n        return TRUE;\n    }\n    else if ((pbCode[0] == 0x26 ||      // jmp es:\n              pbCode[0] == 0x2e ||      // jmp cs:\n              pbCode[0] == 0x36 ||      // jmp ss:\n              pbCode[0] == 0x3e ||      // jmp ds:\n              pbCode[0] == 0x64 ||      // jmp fs:\n              pbCode[0] == 0x65) &&     // jmp gs:\n             pbCode[1] == 0xff &&       // jmp [+imm32]\n             pbCode[2] == 0x25) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // 1-byte through 11-byte NOPs.\n    if (pbCode[0] == 0x90) {\n        return 1;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x90) {\n        return 2;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) {\n        return 3;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 &&\n        pbCode[3] == 0x00) {\n        return 4;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00) {\n        return 5;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x44 && pbCode[4] == 0x00 && pbCode[5] == 0x00) {\n        return 6;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00) {\n        return 7;\n    }\n    if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 &&\n        pbCode[3] == 0x00 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00) {\n        return 8;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F &&\n        pbCode[3] == 0x84 && pbCode[4] == 0x00 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) {\n        return 9;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F &&\n        pbCode[3] == 0x1F && pbCode[4] == 0x84 && pbCode[5] == 0x00 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00) {\n        return 10;\n    }\n    if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 &&\n        pbCode[3] == 0x0F && pbCode[4] == 0x1F && pbCode[5] == 0x84 &&\n        pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 &&\n        pbCode[9] == 0x00 && pbCode[10] == 0x00) {\n        return 11;\n    }\n\n    // int 3.\n    if (pbCode[0] == 0xcc) {\n        return 1;\n    }\n    return 0;\n}\n\n#endif // DETOURS_X64\n\n//////////////////////////////////////////////////////////////////////// IA64.\n//\n#ifdef DETOURS_IA64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // On the IA64, a trampoline is used for both incoming and outgoing calls.\n    //\n    // The trampoline contains the following bundles for the outgoing call:\n    //      movl gp=target_gp;\n    //      <relocated target bundle>\n    //      brl  target_code;\n    //\n    // The trampoline contains the following bundles for the incoming call:\n    //      alloc  r41=ar.pfs, b, 0, 8, 0\n    //      mov    r40=rp\n    //\n    //      adds   r50=0, r39\n    //      adds   r49=0, r38\n    //      adds   r48=0, r37 ;;\n    //\n    //      adds   r47=0, r36\n    //      adds   r46=0, r35\n    //      adds   r45=0, r34\n    //\n    //      adds   r44=0, r33\n    //      adds   r43=0, r32\n    //      adds   r42=0, gp ;;\n    //\n    //      movl   gp=ffffffff`ffffffff ;;\n    //\n    //      brl.call.sptk.few rp=disas!TestCodes+20e0 (00000000`00404ea0) ;;\n    //\n    //      adds   gp=0, r42\n    //      mov    rp=r40, +0 ;;\n    //      mov.i  ar.pfs=r41\n    //\n    //      br.ret.sptk.many rp ;;\n    //\n    // This way, we only have to relocate a single bundle.\n    //\n    // The complicated incoming trampoline is required because we have to\n    // create an additional stack frame so that we save and restore the gp.\n    // We must do this because gp is a caller-saved register, but not saved\n    // if the caller thinks the target is in the same DLL, which changes\n    // when we insert a detour.\n    //\n    DETOUR_IA64_BUNDLE  bMovlTargetGp;  // Bundle which sets target GP\n    BYTE                rbCode[sizeof(DETOUR_IA64_BUNDLE)]; // moved bundle.\n    DETOUR_IA64_BUNDLE  bBrlRemainEip;  // Brl to pbRemain\n    // This must be adjacent to bBranchIslands.\n\n    // Each instruction in the moved bundle could be a IP-relative chk or branch or call.\n    // Any such instructions are changed to point to a brl in bBranchIslands.\n    // This must be adjacent to bBrlRemainEip -- see \"pbPool\".\n    DETOUR_IA64_BUNDLE bBranchIslands[DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE];\n\n    // Target of brl inserted in target function\n    DETOUR_IA64_BUNDLE  bAllocFrame;    // alloc frame\n    DETOUR_IA64_BUNDLE  bSave37to39;    // save r37, r38, r39.\n    DETOUR_IA64_BUNDLE  bSave34to36;    // save r34, r35, r36.\n    DETOUR_IA64_BUNDLE  bSaveGPto33;    // save gp, r32, r33.\n    DETOUR_IA64_BUNDLE  bMovlDetourGp;  // set detour GP.\n    DETOUR_IA64_BUNDLE  bCallDetour;    // call detour.\n    DETOUR_IA64_BUNDLE  bPopFrameGp;    // pop frame and restore gp.\n    DETOUR_IA64_BUNDLE  bReturn;        // return to caller.\n\n    PLABEL_DESCRIPTOR   pldTrampoline;\n\n    BYTE                rbRestore[sizeof(DETOUR_IA64_BUNDLE)]; // original target bundle.\n    BYTE                cbRestore;      // size of original target code.\n    BYTE                cbCode;         // size of moved target code.\n    _DETOUR_ALIGN       rAlign[14];     // instruction alignment array.\n    PBYTE               pbRemain;       // first instruction after moved code. [free list]\n    PBYTE               pbDetour;       // first instruction of detour function.\n    PPLABEL_DESCRIPTOR  ppldDetour;     // [pbDetour,gpDetour]\n    PPLABEL_DESCRIPTOR  ppldTarget;     // [pbTarget,gpDetour]\n};\n\nC_ASSERT(sizeof(DETOUR_IA64_BUNDLE) == 16);\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 256 + DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE * 16);\n\nenum {\n    SIZE_OF_JMP = sizeof(DETOUR_IA64_BUNDLE)\n};\n\ninline PBYTE detour_skip_jmp(PBYTE pPointer, PVOID *ppGlobals)\n{\n    PBYTE pGlobals = NULL;\n    PBYTE pbCode = NULL;\n\n    if (pPointer != NULL) {\n        PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)pPointer;\n        pbCode = (PBYTE)ppld->EntryPoint;\n        pGlobals = (PBYTE)ppld->GlobalPointer;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = pGlobals;\n    }\n    if (pbCode == NULL) {\n        return NULL;\n    }\n\n    DETOUR_IA64_BUNDLE *pb = (DETOUR_IA64_BUNDLE *)pbCode;\n\n    // IA64 Local Import Jumps look like:\n    //      addl   r2=ffffffff`ffe021c0, gp ;;\n    //      ld8    r2=[r2]\n    //      nop.i  0 ;;\n    //\n    //      ld8    r3=[r2], 8 ;;\n    //      ld8    gp=[r2]\n    //      mov    b6=r3, +0\n    //\n    //      nop.m  0\n    //      nop.i  0\n    //      br.cond.sptk.few b6\n    //\n\n    //                     002024000200100b\n    if ((pb[0].wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&\n        pb[0].wide[1] == 0x0004000000203008 &&\n        pb[1].wide[0] == 0x001014180420180a &&\n        pb[1].wide[1] == 0x07000830c0203008 &&\n        pb[2].wide[0] == 0x0000000100000010 &&\n        pb[2].wide[1] == 0x0080006000000200) {\n\n        ULONG64 offset =\n            ((pb[0].wide[0] & 0x0000000001fc0000) >> 18) |  // imm7b\n            ((pb[0].wide[0] & 0x000001ff00000000) >> 25) |  // imm9d\n            ((pb[0].wide[0] & 0x00000000f8000000) >> 11);   // imm5c\n        if (pb[0].wide[0] & 0x0000020000000000) {           // sign\n            offset |= 0xffffffffffe00000;\n        }\n        PBYTE pbTarget = pGlobals + offset;\n        DETOUR_TRACE((\"%p: potential import jump, target=%p\\n\", pb, pbTarget));\n\n        if (detour_is_imported(pbCode, pbTarget) && *(PBYTE*)pbTarget != NULL) {\n            DETOUR_TRACE((\"%p: is import jump, label=%p\\n\", pb, *(PBYTE *)pbTarget));\n\n            PPLABEL_DESCRIPTOR ppld = (PPLABEL_DESCRIPTOR)*(PBYTE *)pbTarget;\n            pbCode = (PBYTE)ppld->EntryPoint;\n            pGlobals = (PBYTE)ppld->GlobalPointer;\n            if (ppGlobals != NULL) {\n                *ppGlobals = pGlobals;\n            }\n        }\n    }\n    return pbCode;\n}\n\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    (void)pbCode;\n    *ppLower = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0x0000000000080000;\n    *ppUpper = (PDETOUR_TRAMPOLINE)(ULONG_PTR)0xfffffffffff80000;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    // Routine not needed on IA64.\n    (void)pbCode;\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    // Routine not needed on IA64.\n    (void)pbCode;\n    return 0;\n}\n\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_ARM\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // A Thumb-2 instruction can be 2 or 4 bytes long.\n    BYTE            rbCode[62];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak;    // padding to make debugging easier.\n    BYTE            rbRestore[22];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 104);\n\nenum {\n    SIZE_OF_JMP = 8\n};\n\ninline PBYTE align4(PBYTE pValue)\n{\n    return (PBYTE)(((ULONG)pValue) & ~(ULONG)3u);\n}\n\ninline ULONG fetch_thumb_opcode(PBYTE pbCode)\n{\n    ULONG Opcode = *(UINT16 *)&pbCode[0];\n    if (Opcode >= 0xe800) {\n        Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];\n    }\n    return Opcode;\n}\n\ninline void write_thumb_opcode(PBYTE &pbCode, ULONG Opcode)\n{\n    if (Opcode >= 0x10000) {\n        *((UINT16*&)pbCode)++ = Opcode >> 16;\n    }\n    *((UINT16*&)pbCode)++ = (UINT16)Opcode;\n}\n\nPBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)\n{\n    PBYTE pbLiteral;\n    if (ppPool != NULL) {\n        *ppPool = *ppPool - 4;\n        pbLiteral = *ppPool;\n    }\n    else {\n        pbLiteral = align4(pbCode + 6);\n    }\n\n    *((PBYTE*&)pbLiteral) = DETOURS_PBYTE_TO_PFUNC(pbJmpVal);\n    LONG delta = pbLiteral - align4(pbCode + 4);\n\n    write_thumb_opcode(pbCode, 0xf8dff000 | delta);     // LDR PC,[PC+n]\n\n    if (ppPool == NULL) {\n        if (((ULONG)pbCode & 2) != 0) {\n            write_thumb_opcode(pbCode, 0xdefe);         // BREAK\n        }\n        pbCode += 4;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        write_thumb_opcode(pbCode, 0xdefe);\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // Skip over the import jump if there is one.\n    pbCode = (PBYTE)DETOURS_PFUNC_TO_PBYTE(pbCode);\n    ULONG Opcode = fetch_thumb_opcode(pbCode);\n\n    if ((Opcode & 0xfbf08f00) == 0xf2400c00) {          // movw r12,#xxxx\n        ULONG Opcode2 = fetch_thumb_opcode(pbCode+4);\n\n        if ((Opcode2 & 0xfbf08f00) == 0xf2c00c00) {      // movt r12,#xxxx\n            ULONG Opcode3 = fetch_thumb_opcode(pbCode+8);\n            if (Opcode3 == 0xf8dcf000) {                 // ldr  pc,[r12]\n                PBYTE pbTarget = (PBYTE)(((Opcode2 << 12) & 0xf7000000) |\n                                         ((Opcode2 <<  1) & 0x08000000) |\n                                         ((Opcode2 << 16) & 0x00ff0000) |\n                                         ((Opcode  >>  4) & 0x0000f700) |\n                                         ((Opcode  >> 15) & 0x00000800) |\n                                         ((Opcode  >>  0) & 0x000000ff));\n                if (detour_is_imported(pbCode, pbTarget)) {\n                    PBYTE pbNew = *(PBYTE *)pbTarget;\n                    pbNew = DETOURS_PFUNC_TO_PBYTE(pbNew);\n                    DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                    return pbNew;\n                }\n            }\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n                                   PDETOUR_TRAMPOLINE *ppLower,\n                                   PDETOUR_TRAMPOLINE *ppUpper)\n{\n    // We have to place trampolines within +/- 2GB of code.\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%p..%p..%p]\\n\", (PVOID)lo, pbCode, (PVOID)hi));\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    ULONG Opcode = fetch_thumb_opcode(pbCode);\n    if ((Opcode & 0xffffff87) == 0x4700 ||          // bx <reg>\n        (Opcode & 0xf800d000) == 0xf0009000) {      // b <imm20>\n        return TRUE;\n    }\n    if ((Opcode & 0xffff8000) == 0xe8bd8000) {      // pop {...,pc}\n        __debugbreak();\n        return TRUE;\n    }\n    if ((Opcode & 0xffffff00) == 0x0000bd00) {      // pop {...,pc}\n        __debugbreak();\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    if (pbCode[0] == 0x00 && pbCode[1] == 0xbf) { // nop.\n        return 2;\n    }\n    if (pbCode[0] == 0x00 && pbCode[1] == 0x00) { // zero-filled padding.\n        return 2;\n    }\n    return 0;\n}\n\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n\nstruct _DETOUR_TRAMPOLINE\n{\n    // An ARM64 instruction is 4 bytes long.\n    BYTE            rbCode[64];     // target code + jmp to pbRemain\n    BYTE            cbCode;         // size of moved target code.\n    BYTE            cbCodeBreak[3]; // padding to make debugging easier.\n    BYTE            rbRestore[24];  // original target code.\n    BYTE            cbRestore;      // size of original target code.\n    BYTE            cbRestoreBreak[3]; // padding to make debugging easier.\n    _DETOUR_ALIGN   rAlign[8];      // instruction alignment array.\n    PBYTE           pbRemain;       // first instruction after moved code. [free list]\n    PBYTE           pbDetour;       // first instruction of detour function.\n};\n\nC_ASSERT(sizeof(_DETOUR_TRAMPOLINE) == 120);\n\nenum {\n    SIZE_OF_JMP = 8\n};\n\ninline ULONG fetch_opcode(PBYTE pbCode)\n{\n    return *(ULONG *)pbCode;\n}\n\ninline void write_opcode(PBYTE &pbCode, ULONG Opcode)\n{\n    *(ULONG *)pbCode = Opcode;\n    pbCode += 4;\n}\n\nPBYTE detour_gen_jmp_immediate(PBYTE pbCode, PBYTE *ppPool, PBYTE pbJmpVal)\n{\n    PBYTE pbLiteral;\n    if (ppPool != NULL) {\n        *ppPool = *ppPool - 8;\n        pbLiteral = *ppPool;\n    }\n    else {\n        pbLiteral = pbCode + 2*4;\n    }\n\n    *((PBYTE*&)pbLiteral) = pbJmpVal;\n    LONG delta = (LONG)(pbLiteral - pbCode);\n\n    write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5));  // LDR X17,[PC+n]\n    write_opcode(pbCode, 0xd61f0000 | (17 << 5));           // BR X17\n\n    if (ppPool == NULL) {\n        pbCode += 8;\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_gen_brk(PBYTE pbCode, PBYTE pbLimit)\n{\n    while (pbCode < pbLimit) {\n        write_opcode(pbCode, 0xd4100000 | (0xf000 << 5));\n    }\n    return pbCode;\n}\n\ninline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)\n{\n    if (pbCode == NULL) {\n        return NULL;\n    }\n    if (ppGlobals != NULL) {\n        *ppGlobals = NULL;\n    }\n\n    // Skip over the import jump if there is one.\n    pbCode = (PBYTE)pbCode;\n    ULONG Opcode = fetch_opcode(pbCode);\n    if ((Opcode & 0x9f00001f) == 0x90000010) {           // adrp  x16, IAT\n        ULONG Opcode2 = fetch_opcode(pbCode+4);\n\n        if ((Opcode2 & 0xffe003ff) == 0xf9400210) {      // ldr   x16, [x16, IAT]\n            ULONG Opcode3 = fetch_opcode(pbCode+8);\n\n            if (Opcode3 == 0xd61f0200) {                 // br    x16\n\n                ULONG PageOffset = ((Opcode & 0x60000000) >> 29) | ((Opcode & 0x00ffffe0) >> 3);\n                PageOffset = (LONG)(Opcode << 11) >> 11;\n\n                PBYTE pbTarget = (PBYTE)(((ULONG64)pbCode & 0xfffffffffffff000ULL) + PageOffset +\n                                         ((Opcode2 >> 10) & 0xfff));\n                if (detour_is_imported(pbCode, pbTarget)) {\n                    PBYTE pbNew = *(PBYTE *)pbTarget;\n                    DETOUR_TRACE((\"%p->%p: skipped over import table.\\n\", pbCode, pbNew));\n                    return pbNew;\n                }\n            }\n        }\n    }\n    return pbCode;\n}\n\ninline void detour_find_jmp_bounds(PBYTE pbCode,\n    PDETOUR_TRAMPOLINE* ppLower,\n    PDETOUR_TRAMPOLINE* ppUpper)\n{\n    // The encoding used by detour_gen_jmp_indirect actually enables a\n    // displacement of +/- 4GiB. In the future, this could be changed to\n    // reflect that. For now, just reuse the x86 logic which is plenty.\n\n    ULONG_PTR lo = detour_2gb_below((ULONG_PTR)pbCode);\n    ULONG_PTR hi = detour_2gb_above((ULONG_PTR)pbCode);\n    DETOUR_TRACE((\"[%p..%p..%p]\\n\", (PVOID)lo, pbCode, (PVOID)hi));\n\n    *ppLower = (PDETOUR_TRAMPOLINE)lo;\n    *ppUpper = (PDETOUR_TRAMPOLINE)hi;\n}\n\ninline BOOL detour_does_code_end_function(PBYTE pbCode)\n{\n    ULONG Opcode = fetch_opcode(pbCode);\n    if ((Opcode & 0xfffffc1f) == 0xd65f0000 ||      // br <reg>\n        (Opcode & 0xfc000000) == 0x14000000) {      // b <imm26>\n        return TRUE;\n    }\n    return FALSE;\n}\n\ninline ULONG detour_is_code_filler(PBYTE pbCode)\n{\n    if (*(ULONG *)pbCode == 0xd503201f) {   // nop.\n        return 4;\n    }\n    if (*(ULONG *)pbCode == 0x00000000) {   // zero-filled padding.\n        return 4;\n    }\n    return 0;\n}\n\n#endif // DETOURS_ARM64\n\n//////////////////////////////////////////////// Trampoline Memory Management.\n//\nstruct DETOUR_REGION\n{\n    ULONG               dwSignature;\n    PMDL                pMdl;   // MDL in this region.\n    DETOUR_REGION *     pNext;  // Next region in list of regions.\n    DETOUR_TRAMPOLINE * pFree;  // List of free trampolines in this region.\n};\ntypedef DETOUR_REGION * PDETOUR_REGION;\n\nconst ULONG DETOUR_REGION_SIGNATURE = 'Rrtd';\nconst ULONG DETOUR_REGION_SIZE = PAGE_SIZE;\nconst ULONG DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE\n                                             / sizeof(DETOUR_TRAMPOLINE)) - 1;\nstatic PDETOUR_REGION s_pRegions = NULL;            // List of all regions.\nstatic PDETOUR_REGION s_pRegion = NULL;             // Default region.\n\nstatic DWORD detour_writable_trampoline_regions()\n{\n    // Mark all of the regions as writable.\n    for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {\n        NTSTATUS Result = MmProtectMdlSystemAddress(pRegion->pMdl, PAGE_EXECUTE_READWRITE);\n        if (!NT_SUCCESS(Result)) {\n            return SetLastError(Result), Result;\n        }\n    }\n    return NO_ERROR;\n}\n\nstatic void detour_runnable_trampoline_regions()\n{\n    // Mark all of the regions as executable.\n    for (PDETOUR_REGION pRegion = s_pRegions; pRegion != NULL; pRegion = pRegion->pNext) {\n\n        MmProtectMdlSystemAddress(pRegion->pMdl, PAGE_EXECUTE_READ);\n    }\n}\n\n//static PBYTE detour_alloc_round_down_to_region(PBYTE pbTry)\n//{\n//    // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.\n//    ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);\n//    if (extra != 0) {\n//        pbTry -= extra;\n//    }\n//    return pbTry;\n//}\n//\n//static PBYTE detour_alloc_round_up_to_region(PBYTE pbTry)\n//{\n//    // WinXP64 returns free areas that aren't REGION aligned to 32-bit applications.\n//    ULONG_PTR extra = ((ULONG_PTR)pbTry) & (DETOUR_REGION_SIZE - 1);\n//    if (extra != 0) {\n//        ULONG_PTR adjust = DETOUR_REGION_SIZE - extra;\n//        pbTry += adjust;\n//    }\n//    return pbTry;\n//}\n\n// Starting at pbLo, try to allocate a memory region, continue until pbHi.\n\nstatic PVOID detour_alloc_region_from_lo(PMDL pMdl, PBYTE /*pbLo*/, PBYTE /*pbHi*/)\n{\n    return MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);\n}\n\n// Starting at pbHi, try to allocate a memory region, continue until pbLo.\n\nstatic PVOID detour_alloc_region_from_hi(PMDL pMdl, PBYTE /*pbLo*/, PBYTE /*pbHi*/)\n{\n    return MmGetSystemAddressForMdlSafe(pMdl, HighPagePriority);\n}\n\nstatic PDETOUR_TRAMPOLINE detour_alloc_trampoline(PBYTE pbTarget)\n{\n    // We have to place trampolines within +/- 2GB of target.\n\n    PDETOUR_TRAMPOLINE pLo;\n    PDETOUR_TRAMPOLINE pHi;\n\n    detour_find_jmp_bounds(pbTarget, &pLo, &pHi);\n\n    PDETOUR_TRAMPOLINE pTrampoline = NULL;\n\n    // Insure that there is a default region.\n    if (s_pRegion == NULL && s_pRegions != NULL) {\n        s_pRegion = s_pRegions;\n    }\n\n    // First check the default region for an valid free block.\n    if (s_pRegion != NULL && s_pRegion->pFree != NULL &&\n        s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {\n\n      found_region:\n        pTrampoline = s_pRegion->pFree;\n        // do a last sanity check on region.\n        if (pTrampoline < pLo || pTrampoline > pHi) {\n            return NULL;\n        }\n        s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pTrampoline->pbRemain;\n        memset(pTrampoline, 0xcc, sizeof(*pTrampoline));\n        return pTrampoline;\n    }\n\n    // Then check the existing regions for a valid free block.\n    for (s_pRegion = s_pRegions; s_pRegion != NULL; s_pRegion = s_pRegion->pNext) {\n        if (s_pRegion != NULL && s_pRegion->pFree != NULL &&\n            s_pRegion->pFree >= pLo && s_pRegion->pFree <= pHi) {\n            goto found_region;\n        }\n    }\n\n    // We need to allocate a new region.\n\n    // Round pbTarget down to 64KB block.\n    pbTarget = pbTarget - (PtrToUlong(pbTarget) & 0xffff);\n\n    PMDL  pMdl = NULL;\n    PVOID pbTry = NULL;\n\n    PHYSICAL_ADDRESS EmptyDesc = { 0 };\n    PHYSICAL_ADDRESS MaxAddress = { 0 };\n    MaxAddress.QuadPart = LONGLONG_MAX;\n\n    pMdl = MmAllocatePagesForMdlEx(\n        EmptyDesc, MaxAddress, EmptyDesc, DETOUR_REGION_SIZE,\n        MmCached, MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS);\n    if (pMdl == NULL)\n    {\n        DETOUR_TRACE((\"Couldn't alloc memory region!\\n\"));\n        return NULL;\n    }\n\n    // NB: We must always also start the search at an offset from pbTarget\n    //     in order to maintain ASLR entropy.\n\n#if defined(DETOURS_64BIT)\n    // Try looking 1GB below or lower.\n    if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {\n        pbTry = detour_alloc_region_from_hi(pMdl, (PBYTE)pLo, pbTarget - 0x40000000);\n    }\n    // Try looking 1GB above or higher.\n    if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {\n        pbTry = detour_alloc_region_from_lo(pMdl, pbTarget + 0x40000000, (PBYTE)pHi);\n    }\n    // Try looking 1GB below or higher.\n    if (pbTry == NULL && pbTarget > (PBYTE)0x40000000) {\n        pbTry = detour_alloc_region_from_lo(pMdl, pbTarget - 0x40000000, pbTarget);\n    }\n    // Try looking 1GB above or lower.\n    if (pbTry == NULL && pbTarget < (PBYTE)0xffffffff40000000) {\n        pbTry = detour_alloc_region_from_hi(pMdl, pbTarget, pbTarget + 0x40000000);\n    }\n#endif\n\n    // Try anything below.\n    if (pbTry == NULL) {\n        pbTry = detour_alloc_region_from_hi(pMdl, (PBYTE)pLo, pbTarget);\n    }\n    // try anything above.\n    if (pbTry == NULL) {\n        pbTry = detour_alloc_region_from_lo(pMdl, pbTarget, (PBYTE)pHi);\n    }\n\n    if (pbTry != NULL) {\n        s_pRegion = (DETOUR_REGION*)pbTry;\n        s_pRegion->dwSignature  = DETOUR_REGION_SIGNATURE;\n        s_pRegion->pMdl         = pMdl;\n        s_pRegion->pFree        = NULL;\n        s_pRegion->pNext        = s_pRegions;\n        s_pRegions = s_pRegion;\n        DETOUR_TRACE((\"  Allocated region %p..%p\\n\\n\",\n                      s_pRegion, ((PBYTE)s_pRegion) + DETOUR_REGION_SIZE - 1));\n\n        // Put everything but the first trampoline on the free list.\n        PBYTE pFree = NULL;\n        pTrampoline = ((PDETOUR_TRAMPOLINE)s_pRegion) + 1;\n        for (int i = DETOUR_TRAMPOLINES_PER_REGION - 1; i > 1; i--) {\n            pTrampoline[i].pbRemain = pFree;\n            pFree = (PBYTE)&pTrampoline[i];\n        }\n        s_pRegion->pFree = (PDETOUR_TRAMPOLINE)pFree;\n        goto found_region;\n    }\n\n    DETOUR_TRACE((\"Couldn't find available memory region!\\n\"));\n    return NULL;\n}\n\nstatic void detour_free_trampoline(PDETOUR_TRAMPOLINE pTrampoline)\n{\n    PDETOUR_REGION pRegion = (PDETOUR_REGION)\n        ((ULONG_PTR)pTrampoline & ~(ULONG_PTR)(DETOUR_REGION_SIZE - 1));\n\n    memset(pTrampoline, 0, sizeof(*pTrampoline));\n    pTrampoline->pbRemain = (PBYTE)pRegion->pFree;\n    pRegion->pFree = pTrampoline;\n}\n\nstatic BOOL detour_is_region_empty(PDETOUR_REGION pRegion)\n{\n    // Stop if the region isn't a region (this would be bad).\n    if (pRegion->dwSignature != DETOUR_REGION_SIGNATURE) {\n        return FALSE;\n    }\n\n    PBYTE pbRegionBeg = (PBYTE)pRegion;\n    PBYTE pbRegionLim  = pbRegionBeg + DETOUR_REGION_SIZE;\n\n    // Stop if any of the trampolines aren't free.\n    PDETOUR_TRAMPOLINE pTrampoline = ((PDETOUR_TRAMPOLINE)pRegion) + 1;\n    for (int i = 0; i < DETOUR_TRAMPOLINES_PER_REGION; i++) {\n        if (pTrampoline[i].pbRemain != NULL &&\n            (pTrampoline[i].pbRemain < pbRegionBeg ||\n             pTrampoline[i].pbRemain >= pbRegionLim)) {\n            return FALSE;\n        }\n    }\n\n    // OK, the region is empty.\n    return TRUE;\n}\n\nstatic void detour_free_unused_trampoline_regions()\n{\n    PDETOUR_REGION *ppRegionBase = &s_pRegions;\n    PDETOUR_REGION pRegion = s_pRegions;\n\n    while (pRegion != NULL) {\n        if (detour_is_region_empty(pRegion)) {\n            *ppRegionBase = pRegion->pNext;\n\n            PMDL pMdl = pRegion->pMdl;\n\n            MmUnmapLockedPages(pRegion, pMdl);\n            MmFreePagesFromMdl(pMdl);\n            ExFreePool(pMdl);\n\n            s_pRegion = NULL;\n        }\n        else {\n            ppRegionBase = &pRegion->pNext;\n        }\n        pRegion = *ppRegionBase;\n    }\n}\n\n///////////////////////////////////////////////////////// Transaction Structs.\n//\nstruct DetourThread\n{\n    DetourThread *      pNext;\n    HANDLE              hThread;\n};\n\nstruct DetourOperation\n{\n    DetourOperation *   pNext;\n    BOOL                fIsRemove;\n    PBYTE *             ppbPointer;\n    PBYTE               pbTarget;\n    PMDL                pbTargetMdl;\n    PDETOUR_TRAMPOLINE  pTrampoline;\n};\n\nstatic BOOL                 s_fIgnoreTooSmall       = FALSE;\nstatic BOOL                 s_fRetainRegions        = FALSE;\n\nstatic LONG                 s_nPendingThreadId      = 0; // Thread owning pending transaction.\nstatic LONG                 s_nPendingError         = NO_ERROR;\nstatic PVOID *              s_ppPendingError        = NULL;\nstatic DetourThread *       s_pPendingThreads       = NULL;\nstatic DetourOperation *    s_pPendingOperations    = NULL;\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPVOID WINAPI DetourCodeFromPointer(_In_ PVOID pPointer,\n                                   _Out_opt_ PVOID *ppGlobals)\n{\n    return detour_skip_jmp((PBYTE)pPointer, ppGlobals);\n}\n\n//////////////////////////////////////////////////////////// Transaction APIs.\n//\nBOOL WINAPI DetourSetIgnoreTooSmall(_In_ BOOL fIgnore)\n{\n    BOOL fPrevious = s_fIgnoreTooSmall;\n    s_fIgnoreTooSmall = fIgnore;\n    return fPrevious;\n}\n\nBOOL WINAPI DetourSetRetainRegions(_In_ BOOL fRetain)\n{\n    BOOL fPrevious = s_fRetainRegions;\n    s_fRetainRegions = fRetain;\n    return fPrevious;\n}\n\nPVOID WINAPI DetourSetSystemRegionLowerBound(_In_ PVOID pSystemRegionLowerBound)\n{\n    PVOID pPrevious = s_pSystemRegionLowerBound;\n    s_pSystemRegionLowerBound = pSystemRegionLowerBound;\n    return pPrevious;\n}\n\nPVOID WINAPI DetourSetSystemRegionUpperBound(_In_ PVOID pSystemRegionUpperBound)\n{\n    PVOID pPrevious = s_pSystemRegionUpperBound;\n    s_pSystemRegionUpperBound = pSystemRegionUpperBound;\n    return pPrevious;\n}\n\nLONG WINAPI DetourTransactionBegin()\n{\n    // Only one transaction is allowed at a time.\n_Benign_race_begin_\n    if (s_nPendingThreadId != 0) {\n        return ERROR_INVALID_OPERATION;\n    }\n_Benign_race_end_\n\n    // Make sure only one thread can start a transaction.\n    if (InterlockedCompareExchange(&s_nPendingThreadId, (LONG)GetCurrentThreadId(), 0) != 0) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    s_pPendingOperations = NULL;\n    s_pPendingThreads = NULL;\n    s_ppPendingError = NULL;\n\n    // Make sure the trampoline pages are writable.\n    s_nPendingError = detour_writable_trampoline_regions();\n\n    return s_nPendingError;\n}\n\nLONG WINAPI DetourTransactionAbort()\n{\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // Restore all of the page permissions.\n    for (DetourOperation *o = s_pPendingOperations; o != NULL;) {\n        // We don't care if this fails, because the code is still accessible.\n\n        detour_unmap_address(o->pbTargetMdl);\n\n        if (!o->fIsRemove) {\n            if (o->pTrampoline) {\n                detour_free_trampoline(o->pTrampoline);\n                o->pTrampoline = NULL;\n            }\n        }\n\n        DetourOperation *n = o->pNext;\n        detour_memory_free(o);\n        o = n;\n    }\n    s_pPendingOperations = NULL;\n\n    // Make sure the trampoline pages are no longer writable.\n    detour_runnable_trampoline_regions();\n\n    // Resume any suspended processor.\n    s_pPendingThreads = NULL;\n    s_nPendingThreadId = 0;\n\n    return NO_ERROR;\n}\n\nLONG WINAPI DetourTransactionCommit()\n{\n    return DetourTransactionCommitEx(NULL);\n}\n\n#pragma warning(suppress: 4505)\nstatic BYTE detour_align_from_trampoline(PDETOUR_TRAMPOLINE pTrampoline, BYTE obTrampoline)\n{\n    for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n        if (pTrampoline->rAlign[n].obTrampoline == obTrampoline) {\n            return pTrampoline->rAlign[n].obTarget;\n        }\n    }\n    return 0;\n}\n\n#pragma warning(suppress: 4505)\nstatic LONG detour_align_from_target(PDETOUR_TRAMPOLINE pTrampoline, LONG obTarget)\n{\n    for (LONG n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n        if (pTrampoline->rAlign[n].obTarget == obTarget) {\n            return pTrampoline->rAlign[n].obTrampoline;\n        }\n    }\n    return 0;\n}\n\nLONG WINAPI DetourTransactionCommitEx(_Out_opt_ PVOID **pppFailedPointer)\n{\n    if (pppFailedPointer != NULL) {\n        // Used to get the last error.\n        *pppFailedPointer = s_ppPendingError;\n    }\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we abort the whole transaction.\n    if (s_nPendingError != NO_ERROR) {\n        DETOUR_BREAK();\n        DetourTransactionAbort();\n        return s_nPendingError;\n    }\n\n    KeGenericCallDpc([](PKDPC /*Dpc*/, PVOID Context, PVOID SystemArgument1, PVOID SystemArgument2)\n        {\n            // Common variables.\n            BOOL freed = FALSE;\n            DetourOperation* o = NULL;\n            ULONG PendingProcessorId = (ULONG)(ULONG_PTR)Context;\n\n            KeSignalCallDpcSynchronize(SystemArgument2);\n\n            if (PendingProcessorId == KeGetCurrentProcessorNumber()) {\n\n                // Insert or remove each of the detours.\n                for (o = s_pPendingOperations; o != NULL; o = o->pNext) {\n                    if (o->fIsRemove) {\n                        CopyMemory(o->pbTarget,\n                            o->pTrampoline->rbRestore,\n                            o->pTrampoline->cbRestore);\n\n#ifdef DETOURS_IA64\n                        * o->ppbPointer = (PBYTE)o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_X86\n                        * o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;\n#endif // DETOURS_X86\n\n#ifdef DETOURS_X64\n                        detour_del_codein(o->pTrampoline->pbCodeInMdl);\n                        * o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;\n#endif // DETOURS_X64\n\n#ifdef DETOURS_ARM\n                        * o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->pbRemain - o->pTrampoline->cbRestore);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n                        * o->ppbPointer = o->pTrampoline->pbRemain - o->pTrampoline->cbRestore;\n#endif // DETOURS_ARM\n                    }\n                    else {\n                        DETOUR_TRACE((\"detours: pbTramp =%p, pbRemain=%p, pbDetour=%p, cbRestore=%d\\n\",\n                            o->pTrampoline,\n                            o->pTrampoline->pbRemain,\n                            o->pTrampoline->pbDetour,\n                            o->pTrampoline->cbRestore));\n\n                        DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x [before]\\n\",\n                            o->pbTarget,\n                            o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],\n                            o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],\n                            o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));\n\n#ifdef DETOURS_IA64\n                        ((DETOUR_IA64_BUNDLE*)o->pbTarget)\n                            ->SetBrl((UINT64)&o->pTrampoline->bAllocFrame);\n                        *o->ppbPointer = (PBYTE)&o->pTrampoline->pldTrampoline;\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_X64\n                        o->pTrampoline->pbCodeIn = detour_gen_codein(o->pTrampoline->pbRemain - o->pTrampoline->cbRestore,\n                            (PVOID)o->pTrampoline->pbDetour, &o->pTrampoline->pbCodeInMdl);\n                        PBYTE pbCode = detour_gen_jmp_indirect(o->pbTarget, o->pTrampoline->pbRemain - o->pTrampoline->cbRestore, (PBYTE*)o->pTrampoline->pbCodeIn);\n                        pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);\n                        *o->ppbPointer = o->pTrampoline->rbCode;\n                        UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_X64\n\n#ifdef DETOURS_X86\n                        PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, o->pTrampoline->pbRemain - o->pTrampoline->cbRestore, o->pTrampoline->pbDetour);\n                        pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);\n                        *o->ppbPointer = o->pTrampoline->rbCode;\n                        UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_X86\n\n#ifdef DETOURS_ARM\n                        PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);\n                        pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);\n                        *o->ppbPointer = DETOURS_PBYTE_TO_PFUNC(o->pTrampoline->rbCode);\n                        UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n                        PBYTE pbCode = detour_gen_jmp_immediate(o->pbTarget, NULL, o->pTrampoline->pbDetour);\n                        pbCode = detour_gen_brk(pbCode, o->pbTarget + o->pTrampoline->cbRestore /*o->pTrampoline->pbRemain*/);\n                        *o->ppbPointer = o->pTrampoline->rbCode;\n                        UNREFERENCED_PARAMETER(pbCode);\n#endif // DETOURS_ARM64\n\n                        DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x [after]\\n\",\n                            o->pbTarget,\n                            o->pbTarget[0], o->pbTarget[1], o->pbTarget[2], o->pbTarget[3],\n                            o->pbTarget[4], o->pbTarget[5], o->pbTarget[6], o->pbTarget[7],\n                            o->pbTarget[8], o->pbTarget[9], o->pbTarget[10], o->pbTarget[11]));\n\n                        DETOUR_TRACE((\"detours: pbTramp =%p: \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x \"\n                            \"%02x %02x %02x %02x\\n\",\n                            o->pTrampoline,\n                            o->pTrampoline->rbCode[0], o->pTrampoline->rbCode[1],\n                            o->pTrampoline->rbCode[2], o->pTrampoline->rbCode[3],\n                            o->pTrampoline->rbCode[4], o->pTrampoline->rbCode[5],\n                            o->pTrampoline->rbCode[6], o->pTrampoline->rbCode[7],\n                            o->pTrampoline->rbCode[8], o->pTrampoline->rbCode[9],\n                            o->pTrampoline->rbCode[10], o->pTrampoline->rbCode[11]));\n\n#ifdef DETOURS_IA64\n                        DETOUR_TRACE((\"\\n\"));\n                        DETOUR_TRACE((\"detours:  &pldTrampoline  =%p\\n\",\n                            &o->pTrampoline->pldTrampoline));\n                        DETOUR_TRACE((\"detours:  &bMovlTargetGp  =%p [%p]\\n\",\n                            &o->pTrampoline->bMovlTargetGp,\n                            o->pTrampoline->bMovlTargetGp.GetMovlGp()));\n                        DETOUR_TRACE((\"detours:  &rbCode         =%p [%p]\\n\",\n                            &o->pTrampoline->rbCode,\n                            ((DETOUR_IA64_BUNDLE&)o->pTrampoline->rbCode).GetBrlTarget()));\n                        DETOUR_TRACE((\"detours:  &bBrlRemainEip  =%p [%p]\\n\",\n                            &o->pTrampoline->bBrlRemainEip,\n                            o->pTrampoline->bBrlRemainEip.GetBrlTarget()));\n                        DETOUR_TRACE((\"detours:  &bMovlDetourGp  =%p [%p]\\n\",\n                            &o->pTrampoline->bMovlDetourGp,\n                            o->pTrampoline->bMovlDetourGp.GetMovlGp()));\n                        DETOUR_TRACE((\"detours:  &bBrlDetourEip  =%p [%p]\\n\",\n                            &o->pTrampoline->bCallDetour,\n                            o->pTrampoline->bCallDetour.GetBrlTarget()));\n                        DETOUR_TRACE((\"detours:  pldDetour       =%p [%p]\\n\",\n                            o->pTrampoline->ppldDetour->EntryPoint,\n                            o->pTrampoline->ppldDetour->GlobalPointer));\n                        DETOUR_TRACE((\"detours:  pldTarget       =%p [%p]\\n\",\n                            o->pTrampoline->ppldTarget->EntryPoint,\n                            o->pTrampoline->ppldTarget->GlobalPointer));\n                        DETOUR_TRACE((\"detours:  pbRemain        =%p\\n\",\n                            o->pTrampoline->pbRemain));\n                        DETOUR_TRACE((\"detours:  pbDetour        =%p\\n\",\n                            o->pTrampoline->pbDetour));\n                        DETOUR_TRACE((\"\\n\"));\n#endif // DETOURS_IA64\n                    }\n                }\n\n                // Restore all of the page permissions and flush the icache.\n                for (o = s_pPendingOperations; o != NULL;) {\n                    // We don't care if this fails, because the code is still accessible.\n\n                    detour_unmap_address(o->pbTargetMdl);\n\n                    if (o->fIsRemove && o->pTrampoline) {\n                        detour_free_trampoline(o->pTrampoline);\n                        o->pTrampoline = NULL;\n                        freed = true;\n                    }\n\n                    DetourOperation* n = o->pNext;\n                    detour_memory_free(o);\n                    o = n;\n                }\n                s_pPendingOperations = NULL;\n\n                // Free any trampoline regions that are now unused.\n                if (freed && !s_fRetainRegions) {\n                    detour_free_unused_trampoline_regions();\n                }\n            }\n\n            KeSignalCallDpcDone(SystemArgument1);\n\n        }, (PVOID)(ULONG_PTR)KeGetCurrentProcessorNumber());\n\n    // Make sure the trampoline pages are no longer writable.\n    detour_runnable_trampoline_regions();\n\n    // Resume any suspended threads.\n    s_pPendingThreads = NULL;\n    s_nPendingThreadId = 0;\n\n    if (pppFailedPointer != NULL) {\n        *pppFailedPointer = s_ppPendingError;\n    }\n\n    return s_nPendingError;\n}\n\nLONG WINAPI DetourUpdateThread(_In_ HANDLE /*hThread*/)\n{\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        return s_nPendingError;\n    }\n\n    return NO_ERROR;\n}\n\n///////////////////////////////////////////////////////////// Transacted APIs.\n//\nLONG WINAPI DetourAttach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour)\n{\n    return DetourAttachEx(ppPointer, pDetour, NULL, NULL, NULL);\n}\n\nLONG WINAPI DetourAttachEx(_Inout_ PVOID *ppPointer,\n                           _In_ PVOID pDetour,\n                           _Out_opt_ PDETOUR_TRAMPOLINE *ppRealTrampoline,\n                           _Out_opt_ PVOID *ppRealTarget,\n                           _Out_opt_ PVOID *ppRealDetour)\n{\n    LONG error = NO_ERROR;\n\n    if (ppRealTrampoline != NULL) {\n        *ppRealTrampoline = NULL;\n    }\n    if (ppRealTarget != NULL) {\n        *ppRealTarget = NULL;\n    }\n    if (ppRealDetour != NULL) {\n        *ppRealDetour = NULL;\n    }\n    if (pDetour == NULL) {\n        DETOUR_TRACE((\"empty detour\\n\"));\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        DETOUR_TRACE((\"transaction conflict with thread id=%d\\n\", s_nPendingThreadId));\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        DETOUR_TRACE((\"pending transaction error=%d\\n\", s_nPendingError));\n        return s_nPendingError;\n    }\n\n    if (ppPointer == NULL) {\n        DETOUR_TRACE((\"ppPointer is null\\n\"));\n        return ERROR_INVALID_HANDLE;\n    }\n    if (*ppPointer == NULL) {\n        error = ERROR_INVALID_HANDLE;\n        s_nPendingError = error;\n        s_ppPendingError = ppPointer;\n        DETOUR_TRACE((\"*ppPointer is null (ppPointer=%p)\\n\", ppPointer));\n        DETOUR_BREAK();\n        return error;\n    }\n\n    PBYTE pbTarget = (PBYTE)*ppPointer;\n    PDETOUR_TRAMPOLINE pTrampoline = NULL;\n    DetourOperation *o = NULL;\n\n#ifdef DETOURS_IA64\n    PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;\n    PPLABEL_DESCRIPTOR ppldTarget = (PPLABEL_DESCRIPTOR)pbTarget;\n    PVOID pDetourGlobals = NULL;\n    PVOID pTargetGlobals = NULL;\n\n    pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);\n    pbTarget = (PBYTE)DetourCodeFromPointer(ppldTarget, &pTargetGlobals);\n    DETOUR_TRACE((\"  ppldDetour=%p, code=%p [gp=%p]\\n\",\n                  ppldDetour, pDetour, pDetourGlobals));\n    DETOUR_TRACE((\"  ppldTarget=%p, code=%p [gp=%p]\\n\",\n                  ppldTarget, pbTarget, pTargetGlobals));\n#else // DETOURS_IA64\n    pbTarget = (PBYTE)DetourCodeFromPointer(pbTarget, NULL);\n    pDetour = DetourCodeFromPointer(pDetour, NULL);\n#endif // !DETOURS_IA64\n\n    // Don't follow a jump if its destination is the target function.\n    // This happens when the detour does nothing other than call the target.\n    if (pDetour == (PVOID)pbTarget) {\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (ppRealTarget != NULL) {\n        *ppRealTarget = pbTarget;\n    }\n    if (ppRealDetour != NULL) {\n        *ppRealDetour = pDetour;\n    }\n\n    o = (DetourOperation*)detour_memory_alloc(sizeof(DetourOperation));\n    if (o == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n      fail:\n        s_nPendingError = error;\n        DETOUR_BREAK();\n      stop:\n        if (pTrampoline != NULL) {\n            detour_free_trampoline(pTrampoline);\n            pTrampoline = NULL;\n            if (ppRealTrampoline != NULL) {\n                *ppRealTrampoline = NULL;\n            }\n        }\n        if (o != NULL) {\n            detour_memory_free(o);\n            o = NULL;\n        }\n        s_ppPendingError = ppPointer;\n        return error;\n    }\n\n    pTrampoline = detour_alloc_trampoline(pbTarget);\n    if (pTrampoline == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    if (ppRealTrampoline != NULL) {\n        *ppRealTrampoline = pTrampoline;\n    }\n\n    DETOUR_TRACE((\"detours: pbTramp=%p, pDetour=%p\\n\", pTrampoline, pDetour));\n\n    memset(pTrampoline->rAlign, 0, sizeof(pTrampoline->rAlign));\n\n    // Determine the number of movable target instructions.\n    PBYTE pbSrc = pbTarget;\n    PBYTE pbTrampoline = pTrampoline->rbCode;\n#ifdef DETOURS_IA64\n    PBYTE pbPool = (PBYTE)(&pTrampoline->bBranchIslands + 1);\n#else\n    PBYTE pbPool = pbTrampoline + sizeof(pTrampoline->rbCode);\n#endif\n    ULONG cbTarget = 0;\n    ULONG cbJump = SIZE_OF_JMP;\n    ULONG nAlign = 0;\n\n#ifdef DETOURS_ARM\n    // On ARM, we need an extra instruction when the function isn't 32-bit aligned.\n    // Check if the existing code is another detour (or at least a similar\n    // \"ldr pc, [PC+0]\" jump.\n    if ((ULONG)pbTarget & 2) {\n        cbJump += 2;\n\n        ULONG op = fetch_thumb_opcode(pbSrc);\n        if (op == 0xbf00) {\n            op = fetch_thumb_opcode(pbSrc + 2);\n            if (op == 0xf8dff000) { // LDR PC,[PC]\n                *((PUSHORT&)pbTrampoline)++ = *((PUSHORT&)pbSrc)++;\n                *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n                *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n                cbTarget = (LONG)(pbSrc - pbTarget);\n                // We will fall through the \"while\" because cbTarget is now >= cbJump.\n            }\n        }\n    }\n    else {\n        ULONG op = fetch_thumb_opcode(pbSrc);\n        if (op == 0xf8dff000) { // LDR PC,[PC]\n            *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n            *((PULONG&)pbTrampoline)++ = *((PULONG&)pbSrc)++;\n            cbTarget = (LONG)(pbSrc - pbTarget);\n            // We will fall through the \"while\" because cbTarget is now >= cbJump.\n        }\n    }\n#endif\n\n    while (cbTarget < cbJump) {\n        PBYTE pbOp = pbSrc;\n        LONG lExtra = 0;\n\n        DETOUR_TRACE((\" DetourCopyInstruction(%p,%p)\\n\",\n                      pbTrampoline, pbSrc));\n        pbSrc = (PBYTE)\n            DetourCopyInstruction(pbTrampoline, (PVOID*)&pbPool, pbSrc, NULL, &lExtra);\n        DETOUR_TRACE((\" DetourCopyInstruction() = %p (%d bytes)\\n\",\n                      pbSrc, (int)(pbSrc - pbOp)));\n        pbTrampoline += (pbSrc - pbOp) + lExtra;\n        cbTarget = (LONG)(pbSrc - pbTarget);\n        pTrampoline->rAlign[nAlign].obTarget = cbTarget;\n        pTrampoline->rAlign[nAlign].obTrampoline = pbTrampoline - pTrampoline->rbCode;\n        nAlign++;\n\n        if (nAlign >= ARRAYSIZE(pTrampoline->rAlign)) {\n            break;\n        }\n\n        if (detour_does_code_end_function(pbOp)) {\n            break;\n        }\n    }\n\n    // Consume, but don't duplicate padding if it is needed and available.\n    while (cbTarget < cbJump) {\n        LONG cFiller = detour_is_code_filler(pbSrc);\n        if (cFiller == 0) {\n            break;\n        }\n\n        pbSrc += cFiller;\n        cbTarget = (LONG)(pbSrc - pbTarget);\n    }\n\n#if DETOUR_DEBUG\n    {\n        DETOUR_TRACE((\" detours: rAlign [\"));\n        LONG n = 0;\n        for (n = 0; n < ARRAYSIZE(pTrampoline->rAlign); n++) {\n            if (pTrampoline->rAlign[n].obTarget == 0 &&\n                pTrampoline->rAlign[n].obTrampoline == 0) {\n                break;\n            }\n            DETOUR_TRACE((\" %d/%d\",\n                          pTrampoline->rAlign[n].obTarget,\n                          pTrampoline->rAlign[n].obTrampoline\n                          ));\n\n        }\n        DETOUR_TRACE((\" ]\\n\"));\n    }\n#endif\n\n    if (cbTarget < cbJump || nAlign > ARRAYSIZE(pTrampoline->rAlign)) {\n        // Too few instructions.\n\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (pbTrampoline > pbPool) {\n        __debugbreak();\n    }\n\n#ifdef DETOURS_X64\n    pTrampoline->pbCodeIn = NULL;\n    pTrampoline->pbCodeInMdl = NULL;\n#endif\n\n    pTrampoline->cbCode = (BYTE)(pbTrampoline - pTrampoline->rbCode);\n    pTrampoline->cbRestore = (BYTE)cbTarget;\n    CopyMemory(pTrampoline->rbRestore, pbTarget, cbTarget);\n\n#if !defined(DETOURS_IA64)\n    if (cbTarget > sizeof(pTrampoline->rbCode) - cbJump) {\n        // Too many instructions.\n        error = ERROR_INVALID_HANDLE;\n        DETOUR_BREAK();\n        goto fail;\n    }\n#endif // !DETOURS_IA64\n\n    pTrampoline->pbRemain = pbTarget + cbTarget;\n    pTrampoline->pbDetour = (PBYTE)pDetour;\n\n#ifdef DETOURS_IA64\n    pTrampoline->ppldDetour = ppldDetour;\n    pTrampoline->ppldTarget = ppldTarget;\n    pTrampoline->pldTrampoline.EntryPoint = (UINT64)&pTrampoline->bMovlTargetGp;\n    pTrampoline->pldTrampoline.GlobalPointer = (UINT64)pDetourGlobals;\n\n    ((DETOUR_IA64_BUNDLE *)pTrampoline->rbCode)->SetStop();\n\n    pTrampoline->bMovlTargetGp.SetMovlGp((UINT64)pTargetGlobals);\n    pTrampoline->bBrlRemainEip.SetBrl((UINT64)pTrampoline->pbRemain);\n\n    // Alloc frame:      alloc r41=ar.pfs,11,0,8,0; mov r40=rp\n    pTrampoline->bAllocFrame.wide[0] = 0x00000580164d480c;\n    pTrampoline->bAllocFrame.wide[1] = 0x00c4000500000200;\n    // save r36, r37, r38.\n    pTrampoline->bSave37to39.wide[0] = 0x031021004e019001;\n    pTrampoline->bSave37to39.wide[1] = 0x8401280600420098;\n    // save r34,r35,r36: adds r47=0,r36; adds r46=0,r35; adds r45=0,r34\n    pTrampoline->bSave34to36.wide[0] = 0x02e0210048017800;\n    pTrampoline->bSave34to36.wide[1] = 0x84011005a042008c;\n    // save gp,r32,r33\"  adds r44=0,r33; adds r43=0,r32; adds r42=0,gp ;;\n    pTrampoline->bSaveGPto33.wide[0] = 0x02b0210042016001;\n    pTrampoline->bSaveGPto33.wide[1] = 0x8400080540420080;\n    // set detour GP.\n    pTrampoline->bMovlDetourGp.SetMovlGp((UINT64)pDetourGlobals);\n    // call detour:      brl.call.sptk.few rp=detour ;;\n    pTrampoline->bCallDetour.wide[0] = 0x0000000100000005;\n    pTrampoline->bCallDetour.wide[1] = 0xd000001000000000;\n    pTrampoline->bCallDetour.SetBrlTarget((UINT64)pDetour);\n    // pop frame & gp:   adds gp=0,r42; mov rp=r40,+0;; mov.i ar.pfs=r41\n    pTrampoline->bPopFrameGp.wide[0] = 0x4000210054000802;\n    pTrampoline->bPopFrameGp.wide[1] = 0x00aa029000038005;\n    // return to caller: br.ret.sptk.many rp ;;\n    pTrampoline->bReturn.wide[0] = 0x0000000100000019;\n    pTrampoline->bReturn.wide[1] = 0x0084000880000200;\n\n    DETOUR_TRACE((\"detours: &bMovlTargetGp=%p\\n\", &pTrampoline->bMovlTargetGp));\n    DETOUR_TRACE((\"detours: &bMovlDetourGp=%p\\n\", &pTrampoline->bMovlDetourGp));\n#endif // DETOURS_IA64\n\n    pbTrampoline = pTrampoline->rbCode + pTrampoline->cbCode;\n#ifdef DETOURS_X64\n    pbTrampoline = detour_gen_jmp_indirect(pbTrampoline, &pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_X64\n\n#ifdef DETOURS_X86\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_X86\n\n#ifdef DETOURS_ARM\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n    pbTrampoline = detour_gen_jmp_immediate(pbTrampoline, &pbPool, pTrampoline->pbRemain);\n    pbTrampoline = detour_gen_brk(pbTrampoline, pbPool);\n#endif // DETOURS_ARM64\n\n    (void)pbTrampoline;\n\n    PMDL pbTargetMdl = detour_remap_address(pbTarget, cbTarget, (void**)&pbTarget);\n    if (pbTargetMdl == NULL) {\n        error = GetLastError();\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    DETOUR_TRACE((\"detours: pbTarget=%p: \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x\\n\",\n                  pbTarget,\n                  pbTarget[0], pbTarget[1], pbTarget[2], pbTarget[3],\n                  pbTarget[4], pbTarget[5], pbTarget[6], pbTarget[7],\n                  pbTarget[8], pbTarget[9], pbTarget[10], pbTarget[11]));\n    DETOUR_TRACE((\"detours: pbTramp =%p: \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x \"\n                  \"%02x %02x %02x %02x\\n\",\n                  pTrampoline,\n                  pTrampoline->rbCode[0], pTrampoline->rbCode[1],\n                  pTrampoline->rbCode[2], pTrampoline->rbCode[3],\n                  pTrampoline->rbCode[4], pTrampoline->rbCode[5],\n                  pTrampoline->rbCode[6], pTrampoline->rbCode[7],\n                  pTrampoline->rbCode[8], pTrampoline->rbCode[9],\n                  pTrampoline->rbCode[10], pTrampoline->rbCode[11]));\n\n    o->fIsRemove    = FALSE;\n    o->ppbPointer   = (PBYTE*)ppPointer;\n    o->pTrampoline  = pTrampoline;\n    o->pbTarget     = pbTarget;\n    o->pbTargetMdl  = pbTargetMdl;\n    o->pNext        = s_pPendingOperations;\n    s_pPendingOperations = o;\n\n    return NO_ERROR;\n}\n\nLONG WINAPI DetourDetach(_Inout_ PVOID *ppPointer,\n                         _In_ PVOID pDetour)\n{\n    LONG error = NO_ERROR;\n\n    if (s_nPendingThreadId != (LONG)GetCurrentThreadId()) {\n        return ERROR_INVALID_OPERATION;\n    }\n\n    // If any of the pending operations failed, then we don't need to do this.\n    if (s_nPendingError != NO_ERROR) {\n        return s_nPendingError;\n    }\n\n    if (pDetour == NULL) {\n        return ERROR_INVALID_PARAMETER;\n    }\n    if (ppPointer == NULL) {\n        return ERROR_INVALID_HANDLE;\n    }\n    if (*ppPointer == NULL) {\n        error = ERROR_INVALID_HANDLE;\n        s_nPendingError = error;\n        s_ppPendingError = ppPointer;\n        DETOUR_BREAK();\n        return error;\n    }\n\n    DetourOperation *o = (DetourOperation*)detour_memory_alloc(sizeof(DetourOperation));\n    if (o == NULL) {\n        error = ERROR_NOT_ENOUGH_MEMORY;\n      fail:\n        s_nPendingError = error;\n        DETOUR_BREAK();\n      stop:\n        if (o != NULL) {\n            detour_memory_free(o);\n            o = NULL;\n        }\n        s_ppPendingError = ppPointer;\n        return error;\n    }\n\n\n#ifdef DETOURS_IA64\n    PPLABEL_DESCRIPTOR ppldTrampo = (PPLABEL_DESCRIPTOR)*ppPointer;\n    PPLABEL_DESCRIPTOR ppldDetour = (PPLABEL_DESCRIPTOR)pDetour;\n    PVOID pDetourGlobals = NULL;\n    PVOID pTrampoGlobals = NULL;\n\n    pDetour = (PBYTE)DetourCodeFromPointer(ppldDetour, &pDetourGlobals);\n    PDETOUR_TRAMPOLINE pTrampoline = (PDETOUR_TRAMPOLINE)\n        DetourCodeFromPointer(ppldTrampo, &pTrampoGlobals);\n    DETOUR_TRACE((\"  ppldDetour=%p, code=%p [gp=%p]\\n\",\n                  ppldDetour, pDetour, pDetourGlobals));\n    DETOUR_TRACE((\"  ppldTrampo=%p, code=%p [gp=%p]\\n\",\n                  ppldTrampo, pTrampoline, pTrampoGlobals));\n\n\n    DETOUR_TRACE((\"\\n\"));\n    DETOUR_TRACE((\"detours:  &pldTrampoline  =%p\\n\",\n                  &pTrampoline->pldTrampoline));\n    DETOUR_TRACE((\"detours:  &bMovlTargetGp  =%p [%p]\\n\",\n                  &pTrampoline->bMovlTargetGp,\n                  pTrampoline->bMovlTargetGp.GetMovlGp()));\n    DETOUR_TRACE((\"detours:  &rbCode         =%p [%p]\\n\",\n                  &pTrampoline->rbCode,\n                  ((DETOUR_IA64_BUNDLE&)pTrampoline->rbCode).GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  &bBrlRemainEip  =%p [%p]\\n\",\n                  &pTrampoline->bBrlRemainEip,\n                  pTrampoline->bBrlRemainEip.GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  &bMovlDetourGp  =%p [%p]\\n\",\n                  &pTrampoline->bMovlDetourGp,\n                  pTrampoline->bMovlDetourGp.GetMovlGp()));\n    DETOUR_TRACE((\"detours:  &bBrlDetourEip  =%p [%p]\\n\",\n                  &pTrampoline->bCallDetour,\n                  pTrampoline->bCallDetour.GetBrlTarget()));\n    DETOUR_TRACE((\"detours:  pldDetour       =%p [%p]\\n\",\n                  pTrampoline->ppldDetour->EntryPoint,\n                  pTrampoline->ppldDetour->GlobalPointer));\n    DETOUR_TRACE((\"detours:  pldTarget       =%p [%p]\\n\",\n                  pTrampoline->ppldTarget->EntryPoint,\n                  pTrampoline->ppldTarget->GlobalPointer));\n    DETOUR_TRACE((\"detours:  pbRemain        =%p\\n\",\n                  pTrampoline->pbRemain));\n    DETOUR_TRACE((\"detours:  pbDetour        =%p\\n\",\n                  pTrampoline->pbDetour));\n    DETOUR_TRACE((\"\\n\"));\n#else // !DETOURS_IA64\n    PDETOUR_TRAMPOLINE pTrampoline =\n        (PDETOUR_TRAMPOLINE)DetourCodeFromPointer(*ppPointer, NULL);\n    pDetour = DetourCodeFromPointer(pDetour, NULL);\n#endif // !DETOURS_IA64\n\n    ////////////////////////////////////// Verify that Trampoline is in place.\n    //\n    LONG cbTarget = pTrampoline->cbRestore;\n    PBYTE pbTarget = pTrampoline->pbRemain - cbTarget;\n    if (cbTarget == 0 || cbTarget > sizeof(pTrampoline->rbCode)) {\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    if (pTrampoline->pbDetour != pDetour) {\n        error = ERROR_INVALID_BLOCK;\n        if (s_fIgnoreTooSmall) {\n            goto stop;\n        }\n        else {\n            DETOUR_BREAK();\n            goto fail;\n        }\n    }\n\n    PMDL pbTargetMdl = detour_remap_address(pbTarget, cbTarget, (void**)&pbTarget);\n    if (pbTargetMdl == nullptr)\n    {\n        error = GetLastError();\n        DETOUR_BREAK();\n        goto fail;\n    }\n\n    o->fIsRemove    = TRUE;\n    o->ppbPointer   = (PBYTE*)ppPointer;\n    o->pTrampoline  = pTrampoline;\n    o->pbTarget     = pbTarget;\n    o->pbTargetMdl  = pbTargetMdl;\n    o->pNext        = s_pPendingOperations;\n    s_pPendingOperations = o;\n\n    return NO_ERROR;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// Helpers for manipulating page protection.\n//\n\n// For reference:\n//   PAGE_NOACCESS          0x01\n//   PAGE_READONLY          0x02\n//   PAGE_READWRITE         0x04\n//   PAGE_WRITECOPY         0x08\n//   PAGE_EXECUTE           0x10\n//   PAGE_EXECUTE_READ      0x20\n//   PAGE_EXECUTE_READWRITE 0x40\n//   PAGE_EXECUTE_WRITECOPY 0x80\n//   PAGE_GUARD             ...\n//   PAGE_NOCACHE           ...\n//   PAGE_WRITECOMBINE      ...\n\n#define DETOUR_PAGE_EXECUTE_ALL    (PAGE_EXECUTE |              \\\n                                    PAGE_EXECUTE_READ |         \\\n                                    PAGE_EXECUTE_READWRITE |    \\\n                                    PAGE_EXECUTE_WRITECOPY)\n\n#define DETOUR_PAGE_NO_EXECUTE_ALL (PAGE_NOACCESS |             \\\n                                    PAGE_READONLY |             \\\n                                    PAGE_READWRITE |            \\\n                                    PAGE_WRITECOPY)\n\n#define DETOUR_PAGE_ATTRIBUTES     (~(DETOUR_PAGE_EXECUTE_ALL | DETOUR_PAGE_NO_EXECUTE_ALL))\n\nC_ASSERT((DETOUR_PAGE_NO_EXECUTE_ALL << 4) == DETOUR_PAGE_EXECUTE_ALL);\n\n#pragma warning(suppress: 4505)\nstatic DWORD DetourPageProtectAdjustExecute(_In_  DWORD dwOldProtect,\n                                            _In_  DWORD dwNewProtect)\n//  Copy EXECUTE from dwOldProtect to dwNewProtect.\n{\n    bool const fOldExecute = ((dwOldProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);\n    bool const fNewExecute = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) != 0);\n\n    if (fOldExecute && !fNewExecute) {\n        dwNewProtect = ((dwNewProtect & DETOUR_PAGE_NO_EXECUTE_ALL) << 4)\n            | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);\n    }\n    else if (!fOldExecute && fNewExecute) {\n        dwNewProtect = ((dwNewProtect & DETOUR_PAGE_EXECUTE_ALL) >> 4)\n            | (dwNewProtect & DETOUR_PAGE_ATTRIBUTES);\n    }\n    return dwNewProtect;\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecuteEx(_In_  HANDLE hProcess,\n                                              _In_  PVOID pAddress,\n                                              _In_  SIZE_T nSize,\n                                              _In_  DWORD dwNewProtect,\n                                              _Out_ PDWORD pdwOldProtect)\n// Some systems do not allow executability of a page to change. This function applies\n// dwNewProtect to [pAddress, nSize), but preserving the previous executability.\n// This function is meant to be a drop-in replacement for some uses of VirtualProtectEx.\n// When \"restoring\" page protection, there is no need to use this function.\n{\n    MEMORY_BASIC_INFORMATION mbi;\n\n    // Query to get existing execute access.\n\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    if (VirtualQueryEx(hProcess, pAddress, &mbi, sizeof(mbi)) == 0) {\n        return FALSE;\n    }\n    return VirtualProtectEx(hProcess, pAddress, nSize,\n                            DetourPageProtectAdjustExecute(mbi.Protect, dwNewProtect),\n                            pdwOldProtect);\n}\n\n_Success_(return != FALSE)\nBOOL WINAPI DetourVirtualProtectSameExecute(_In_  PVOID pAddress,\n                                            _In_  SIZE_T nSize,\n                                            _In_  DWORD dwNewProtect,\n                                            _Out_ PDWORD pdwOldProtect)\n{\n    return DetourVirtualProtectSameExecuteEx(GetCurrentProcess(),\n                                             pAddress, nSize, dwNewProtect, pdwOldProtect);\n}\n\n//  End of File\n"
  },
  {
    "path": "Detours/detver.h",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Common version parameters.\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#define _USING_V110_SDK71_ 1\n#include \"winver.h\"\n#if 0\n#include <windows.h>\n#include <detours.h>\n#else\n#ifndef DETOURS_STRINGIFY\n#define DETOURS_STRINGIFY(x)    DETOURS_STRINGIFY_(x)\n#define DETOURS_STRINGIFY_(x)    #x\n#endif\n\n#define VER_FILEFLAGSMASK   0x3fL\n#define VER_FILEFLAGS       0x0L\n#define VER_FILEOS          0x00040004L\n#define VER_FILETYPE        0x00000002L\n#define VER_FILESUBTYPE     0x00000000L\n#endif\n#define VER_DETOURS_BITS    DETOUR_STRINGIFY(DETOURS_BITS)\n"
  },
  {
    "path": "Detours/disasm.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Detours Disassembler (disasm.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#if defined(_KERNEL_MODE)\n#define DETOURS_KERNEL\n#endif\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n\n#include <Veil.h>\n\n#ifdef DETOURS_KERNEL\n#include \"api_thunks.h\"\n#endif\n\n#include <limits.h>\n\n// #define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\n#undef ASSERT\n#define ASSERT(x)\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  Special macros to handle the case when we are building disassembler for\n//  offline processing.\n//\n\n\n#if defined(DETOURS_X86_OFFLINE_LIBRARY) \\\n || defined(DETOURS_X64_OFFLINE_LIBRARY) \\\n || defined(DETOURS_ARM_OFFLINE_LIBRARY) \\\n || defined(DETOURS_ARM64_OFFLINE_LIBRARY) \\\n || defined(DETOURS_IA64_OFFLINE_LIBRARY)\n\n#undef DETOURS_X64\n#undef DETOURS_X86\n#undef DETOURS_IA64\n#undef DETOURS_ARM\n#undef DETOURS_ARM64\n\n#if defined(DETOURS_X86_OFFLINE_LIBRARY)\n\n#define DetourCopyInstruction   DetourCopyInstructionX86\n#define DetourSetCodeModule     DetourSetCodeModuleX86\n#define CDetourDis              CDetourDisX86\n#define DETOURS_X86\n\n#elif defined(DETOURS_X64_OFFLINE_LIBRARY)\n\n#if !defined(DETOURS_64BIT)\n// Fix this as/if bugs are discovered.\n//#error X64 disassembler can only build for 64-bit.\n#endif\n\n#define DetourCopyInstruction   DetourCopyInstructionX64\n#define DetourSetCodeModule     DetourSetCodeModuleX64\n#define CDetourDis              CDetourDisX64\n#define DETOURS_X64\n\n#elif defined(DETOURS_ARM_OFFLINE_LIBRARY)\n\n#define DetourCopyInstruction   DetourCopyInstructionARM\n#define DetourSetCodeModule     DetourSetCodeModuleARM\n#define CDetourDis              CDetourDisARM\n#define DETOURS_ARM\n\n#elif defined(DETOURS_ARM64_OFFLINE_LIBRARY)\n\n#define DetourCopyInstruction   DetourCopyInstructionARM64\n#define DetourSetCodeModule     DetourSetCodeModuleARM64\n#define CDetourDis              CDetourDisARM64\n#define DETOURS_ARM64\n\n#elif defined(DETOURS_IA64_OFFLINE_LIBRARY)\n\n#define DetourCopyInstruction   DetourCopyInstructionIA64\n#define DetourSetCodeModule     DetourSetCodeModuleIA64\n#define DETOURS_IA64\n\n#else\n\n#error\n\n#endif\n#endif\n\n//////////////////////////////////////////////////////////////////////////////\n//\n//  Function:\n//      DetourCopyInstruction(PVOID pDst,\n//                            PVOID *ppDstPool\n//                            PVOID pSrc,\n//                            PVOID *ppTarget,\n//                            LONG *plExtra)\n//  Purpose:\n//      Copy a single instruction from pSrc to pDst.\n//\n//  Arguments:\n//      pDst:\n//          Destination address for the instruction.  May be NULL in which\n//          case DetourCopyInstruction is used to measure an instruction.\n//          If not NULL then the source instruction is copied to the\n//          destination instruction and any relative arguments are adjusted.\n//      ppDstPool:\n//          Destination address for the end of the constant pool.  The\n//          constant pool works backwards toward pDst.  All memory between\n//          pDst and *ppDstPool must be available for use by this function.\n//          ppDstPool may be NULL if pDst is NULL.\n//      pSrc:\n//          Source address of the instruction.\n//      ppTarget:\n//          Out parameter for any target instruction address pointed to by\n//          the instruction.  For example, a branch or a jump insruction has\n//          a target, but a load or store instruction doesn't.  A target is\n//          another instruction that may be executed as a result of this\n//          instruction.  ppTarget may be NULL.\n//      plExtra:\n//          Out parameter for the number of extra bytes needed by the\n//          instruction to reach the target.  For example, lExtra = 3 if the\n//          instruction had an 8-bit relative offset, but needs a 32-bit\n//          relative offset.\n//\n//  Returns:\n//      Returns the address of the next instruction (following in the source)\n//      instruction.  By subtracting pSrc from the return value, the caller\n//      can determinte the size of the instruction copied.\n//\n//  Comments:\n//      By following the pTarget, the caller can follow alternate\n//      instruction streams.  However, it is not always possible to determine\n//      the target based on static analysis.  For example, the destination of\n//      a jump relative to a register cannot be determined from just the\n//      instruction stream.  The output value, pTarget, can have any of the\n//      following outputs:\n//          DETOUR_INSTRUCTION_TARGET_NONE:\n//              The instruction has no targets.\n//          DETOUR_INSTRUCTION_TARGET_DYNAMIC:\n//              The instruction has a non-deterministic (dynamic) target.\n//              (i.e. the jump is to an address held in a register.)\n//          Address:   The instruction has the specified target.\n//\n//      When copying instructions, DetourCopyInstruction insures that any\n//      targets remain constant.  It does so by adjusting any IP relative\n//      offsets.\n//\n\n#pragma data_seg(\".detourd\")\n#pragma const_seg(\".detourc\")\n\n//////////////////////////////////////////////////// X86 and X64 Disassembler.\n//\n//  Includes full support for all x86 chips prior to the Pentium III, and some newer stuff.\n//\n#if defined(DETOURS_X64) || defined(DETOURS_X86)\n\nclass CDetourDis\n{\n  public:\n    CDetourDis(_Out_opt_ PBYTE *ppbTarget,\n               _Out_opt_ LONG *plExtra);\n\n    PBYTE   CopyInstruction(PBYTE pbDst, PBYTE pbSrc);\n    static BOOL SanityCheckSystem();\n    static BOOL SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule);\n\n  public:\n    struct COPYENTRY;\n    typedef const COPYENTRY * REFCOPYENTRY;\n\n    typedef PBYTE (CDetourDis::* COPYFUNC)(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n\n    // nFlagBits flags.\n    enum {\n        DYNAMIC     = 0x1u,\n        ADDRESS     = 0x2u,\n        NOENLARGE   = 0x4u,\n        RAX         = 0x8u,\n    };\n\n    // ModR/M Flags\n    enum {\n        SIB         = 0x10u,\n        RIP         = 0x20u,\n        NOTSIB      = 0x0fu,\n    };\n\n    struct COPYENTRY\n    {\n        // Many of these fields are often ignored. See ENTRY_DataIgnored.\n        ULONG       nOpcode         : 8;    // Opcode (ignored)\n        ULONG       nFixedSize      : 4;    // Fixed size of opcode\n        ULONG       nFixedSize16    : 4;    // Fixed size when 16 bit operand\n        ULONG       nModOffset      : 4;    // Offset to mod/rm byte (0=none)\n        ULONG       nRelOffset      : 4;    // Offset to relative target.\n        ULONG       nTargetBack     : 4;    // Offset back to absolute or rip target\n        ULONG       nFlagBits       : 4;    // Flags for DYNAMIC, etc.\n        COPYFUNC    pfCopy;                 // Function pointer.\n    };\n\n  protected:\n    // These macros define common uses of nFixedSize..pfCopy.\n#define ENTRY_DataIgnored           0, 0, 0, 0, 0, 0,\n#define ENTRY_CopyBytes1            1, 1, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#ifdef DETOURS_X64\n#define ENTRY_CopyBytes1Address     9, 5, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes\n#else\n#define ENTRY_CopyBytes1Address     5, 3, 0, 0, 0, ADDRESS, &CDetourDis::CopyBytes\n#endif\n#define ENTRY_CopyBytes1Dynamic     1, 1, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2            2, 2, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2Jump        ENTRY_DataIgnored &CDetourDis::CopyBytesJump\n#define ENTRY_CopyBytes2CantJump    2, 2, 0, 1, 0, NOENLARGE, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2Dynamic     2, 2, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3            3, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Dynamic     3, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Or5         5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Or5Dynamic  5, 3, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes // x86 only\n#ifdef DETOURS_X64\n#define ENTRY_CopyBytes3Or5Rax      5, 3, 0, 0, 0, RAX, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Or5Target   5, 5, 0, 1, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytesJumpToAbsolute  5, 5, 0, 1, 0, 0, &CDetourDis::CopyBytesJumpToAbsolute\n#else\n#define ENTRY_CopyBytes3Or5Rax      5, 3, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Or5Target   5, 3, 0, 1, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytesJumpToAbsolute ENTRY_CopyBytes3Or5Target\n#endif\n#define ENTRY_CopyBytes4            4, 4, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes5            5, 5, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes5Or7Dynamic  7, 5, 0, 0, 0, DYNAMIC, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes7            7, 7, 0, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2Mod         2, 2, 1, 0, 0, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2ModDynamic  2, 2, 1, 0, 0, DYNAMIC, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2Mod1        3, 3, 1, 0, 1, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes2ModOperand  6, 4, 1, 0, 4, 0, &CDetourDis::CopyBytes\n#define ENTRY_CopyBytes3Mod         3, 3, 2, 0, 0, 0, &CDetourDis::CopyBytes // SSE3 0F 38 opcode modrm\n#define ENTRY_CopyBytes3Mod1        4, 4, 2, 0, 1, 0, &CDetourDis::CopyBytes // SSE3 0F 3A opcode modrm .. imm8\n#define ENTRY_CopyBytesPrefix       ENTRY_DataIgnored &CDetourDis::CopyBytesPrefix\n#define ENTRY_CopyBytesSegment      ENTRY_DataIgnored &CDetourDis::CopyBytesSegment\n#define ENTRY_CopyBytesRax          ENTRY_DataIgnored &CDetourDis::CopyBytesRax\n#define ENTRY_CopyF2                ENTRY_DataIgnored &CDetourDis::CopyF2\n#define ENTRY_CopyF3                ENTRY_DataIgnored &CDetourDis::CopyF3   // 32bit x86 only\n#define ENTRY_Copy0F                ENTRY_DataIgnored &CDetourDis::Copy0F\n#define ENTRY_Copy0F78              ENTRY_DataIgnored &CDetourDis::Copy0F78\n#define ENTRY_Copy0F00              ENTRY_DataIgnored &CDetourDis::Copy0F00 // 32bit x86 only\n#define ENTRY_Copy0FB8              ENTRY_DataIgnored &CDetourDis::Copy0FB8 // 32bit x86 only\n#define ENTRY_Copy66                ENTRY_DataIgnored &CDetourDis::Copy66\n#define ENTRY_Copy67                ENTRY_DataIgnored &CDetourDis::Copy67\n#define ENTRY_CopyF6                ENTRY_DataIgnored &CDetourDis::CopyF6\n#define ENTRY_CopyF7                ENTRY_DataIgnored &CDetourDis::CopyF7\n#define ENTRY_CopyFF                ENTRY_DataIgnored &CDetourDis::CopyFF\n#define ENTRY_CopyVex2              ENTRY_DataIgnored &CDetourDis::CopyVex2\n#define ENTRY_CopyVex3              ENTRY_DataIgnored &CDetourDis::CopyVex3\n#define ENTRY_Invalid               ENTRY_DataIgnored &CDetourDis::Invalid\n#define ENTRY_End                   ENTRY_DataIgnored NULL\n\n    PBYTE CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyBytesSegment(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyBytesRax(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyBytesJumpToAbsolute(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n\n    PBYTE Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n\n    PBYTE AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp,\n                       UINT cbTargetOffset, UINT cbTargetSize);\n\n  protected:\n    PBYTE Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    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\n    PBYTE Copy0F78(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // vmread, 66/extrq/ib/ib, F2/insertq/ib/ib\n    PBYTE Copy0FB8(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // jmpe or F3/popcnt\n    PBYTE Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc); // x86 only\n    PBYTE CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyVex2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyVex3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc);\n    PBYTE CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc);\n\n  protected:\n    static const COPYENTRY  s_rceCopyTable[257];\n    static const COPYENTRY  s_rceCopyTable0F[257];\n    static const BYTE       s_rbModRm[256];\n    static PBYTE            s_pbModuleBeg;\n    static PBYTE            s_pbModuleEnd;\n    static BOOL             s_fLimitReferencesToModule;\n\n  protected:\n    BOOL                m_bOperandOverride = FALSE;\n    BOOL                m_bAddressOverride = FALSE;\n    BOOL                m_bRaxOverride = FALSE; // AMD64 only\n    BOOL                m_bVex = FALSE;\n    BOOL                m_bF2 = FALSE;\n    BOOL                m_bF3 = FALSE; // x86 only\n    BYTE                m_nSegmentOverride = 0;\n\n    PBYTE *             m_ppbTarget = NULL;\n    LONG *              m_plExtra = NULL;\n\n    LONG                m_lScratchExtra = 0;\n    PBYTE               m_pbScratchTarget = NULL;\n    BYTE                m_rbScratchDst[64] = { 0 };\n};\n\nPVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,\n                                   _Inout_opt_ PVOID *ppDstPool,\n                                   _In_ PVOID pSrc,\n                                   _Out_opt_ PVOID *ppTarget,\n                                   _Out_opt_ LONG *plExtra)\n{\n    UNREFERENCED_PARAMETER(ppDstPool);  // x86 & x64 don't use a constant pool.\n\n    CDetourDis oDetourDisasm((PBYTE*)ppTarget, plExtra);\n    return oDetourDisasm.CopyInstruction((PBYTE)pDst, (PBYTE)pSrc);\n}\n\n/////////////////////////////////////////////////////////// Disassembler Code.\n//\nCDetourDis::CDetourDis(_Out_opt_ PBYTE *ppbTarget, _Out_opt_ LONG *plExtra)\n{\n    m_bOperandOverride = FALSE;\n    m_bAddressOverride = FALSE;\n    m_bRaxOverride = FALSE;\n    m_bF2 = FALSE;\n    m_bF3 = FALSE;\n    m_bVex = FALSE;\n\n    m_ppbTarget = ppbTarget ? ppbTarget : &m_pbScratchTarget;\n    m_plExtra = plExtra ? plExtra : &m_lScratchExtra;\n\n    *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE;\n    *m_plExtra = 0;\n}\n\nPBYTE CDetourDis::CopyInstruction(PBYTE pbDst, PBYTE pbSrc)\n{\n    // Configure scratch areas if real areas are not available.\n    if (NULL == pbDst) {\n        pbDst = m_rbScratchDst;\n    }\n    if (NULL == pbSrc) {\n        // We can't copy a non-existent instruction.\n        SetLastError(ERROR_INVALID_DATA);\n        return NULL;\n    }\n\n    // Figure out how big the instruction is, do the appropriate copy,\n    // and figure out what the target of the instruction is if any.\n    //\n    REFCOPYENTRY pEntry = &s_rceCopyTable[pbSrc[0]];\n    return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyBytes(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    UINT nBytesFixed;\n\n    ASSERT(!m_bVex || pEntry->nFlagBits == 0);\n    ASSERT(!m_bVex || pEntry->nFixedSize == pEntry->nFixedSize16);\n\n    UINT const nModOffset = pEntry->nModOffset;\n    UINT const nFlagBits = pEntry->nFlagBits;\n    UINT const nFixedSize = pEntry->nFixedSize;\n    UINT const nFixedSize16 = pEntry->nFixedSize16;\n\n    if (nFlagBits & ADDRESS) {\n        nBytesFixed = m_bAddressOverride ? nFixedSize16 : nFixedSize;\n    }\n#ifdef DETOURS_X64\n    // REX.W trumps 66\n    else if (m_bRaxOverride) {\n        nBytesFixed = nFixedSize + ((nFlagBits & RAX) ? 4 : 0);\n    }\n#endif\n    else {\n        nBytesFixed = m_bOperandOverride ? nFixedSize16 : nFixedSize;\n    }\n\n    UINT nBytes = nBytesFixed;\n    UINT nRelOffset = pEntry->nRelOffset;\n    UINT cbTarget = nBytes - nRelOffset;\n    if (nModOffset > 0) {\n        ASSERT(nRelOffset == 0);\n        BYTE const bModRm = pbSrc[nModOffset];\n        BYTE const bFlags = s_rbModRm[bModRm];\n\n        nBytes += bFlags & NOTSIB;\n\n        if (bFlags & SIB) {\n            BYTE const bSib = pbSrc[nModOffset + 1];\n\n            if ((bSib & 0x07) == 0x05) {\n                if ((bModRm & 0xc0) == 0x00) {\n                    nBytes += 4;\n                }\n                else if ((bModRm & 0xc0) == 0x40) {\n                    nBytes += 1;\n                }\n                else if ((bModRm & 0xc0) == 0x80) {\n                    nBytes += 4;\n                }\n            }\n            cbTarget = nBytes - nRelOffset;\n        }\n#ifdef DETOURS_X64\n        else if (bFlags & RIP) {\n            UINT nTargetBack = pEntry->nTargetBack;\n            // nTargetBack describes immediate bytes at the end: 1, 2, or 4.\n            // 2 vs. 4 is selected via 66 operand size override.\n            ASSERT(nTargetBack == 0 || nTargetBack == 1 || nTargetBack == 4);\n            if (nTargetBack == 4 && m_bOperandOverride && !m_bRaxOverride) {\n                nTargetBack = 2;\n            }\n\n            nRelOffset = nBytes - (4 + nTargetBack);\n            cbTarget = 4;\n        }\n#endif\n    }\n    CopyMemory(pbDst, pbSrc, nBytes);\n\n    if (nRelOffset) {\n        *m_ppbTarget = AdjustTarget(pbDst, pbSrc, nBytes, nRelOffset, cbTarget);\n#ifdef DETOURS_X64\n        if (pEntry->nRelOffset == 0) {\n            // This is a data target, not a code target, so we shouldn't return it.\n            *m_ppbTarget = NULL;\n        }\n#endif\n    }\n    if (nFlagBits & NOENLARGE) {\n        *m_plExtra = -*m_plExtra;\n    }\n    if (nFlagBits & DYNAMIC) {\n        *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    }\n    return pbSrc + nBytes;\n}\n\nPBYTE CDetourDis::CopyBytesPrefix(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    pbDst[0] = pbSrc[0];\n    pEntry = &s_rceCopyTable[pbSrc[1]];\n    return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1);\n}\n\nPBYTE CDetourDis::CopyBytesSegment(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n{\n    m_nSegmentOverride = pbSrc[0];\n    return CopyBytesPrefix(NULL, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyBytesRax(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n{ // AMD64 only\n    if (pbSrc[0] & 0x8) {\n        m_bRaxOverride = TRUE;\n    }\n    return CopyBytesPrefix(NULL, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyBytesJump(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    (void)pEntry;\n\n    PVOID pvSrcAddr = &pbSrc[1];\n    PVOID pvDstAddr = NULL;\n    LONG_PTR nOldOffset = (LONG_PTR)*(signed char*&)pvSrcAddr;\n    LONG_PTR nNewOffset = 0;\n\n    *m_ppbTarget = pbSrc + 2 + nOldOffset;\n\n    if (pbSrc[0] == 0xeb) {\n        pbDst[0] = 0xe9;\n        pvDstAddr = &pbDst[1];\n        nNewOffset = nOldOffset - ((pbDst - pbSrc) + 3);\n        *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset;\n\n        *m_plExtra = 3;\n        return pbSrc + 2;\n    }\n\n    ASSERT(pbSrc[0] >= 0x70 && pbSrc[0] <= 0x7f);\n\n    pbDst[0] = 0x0f;\n    pbDst[1] = 0x80 | (pbSrc[0] & 0xf);\n    pvDstAddr = &pbDst[2];\n    nNewOffset = nOldOffset - ((pbDst - pbSrc) + 4);\n    *(UNALIGNED LONG*&)pvDstAddr = (LONG)nNewOffset;\n\n    *m_plExtra = 4;\n    return pbSrc + 2;\n}\n\n\nPBYTE CDetourDis::CopyBytesJumpToAbsolute(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n#ifdef DETOURS_KERNEL\n#ifdef DETOURS_X64\n    PVOID pvSrcAddr = &pbSrc[1];\n    LONG_PTR nOldOffset = (LONG_PTR) * (LONG*&)pvSrcAddr;\n    LONG_PTR nNewOffset = (LONG_PTR)(pbSrc + 5 + nOldOffset);\n\n    // call imm32 <offset>\n    if (pbSrc[0] == 0xE8) {\n        // jmp rax, imm64 <address>\n        pbDst[0] = 0x48;\n        pbDst[1] = 0xB8;\n        *(UNALIGNED LONG_PTR*)& pbDst[2] = nNewOffset;\n\n        // call rax\n        pbDst[10] = 0xFF;\n        pbDst[11] = 0xD0;\n\n        *m_plExtra = 12 - 5;\n        *m_ppbTarget = (PBYTE)nNewOffset;\n        return pbSrc + 5;\n    }\n\n    // jmp imm32 <offset>\n    if (pbSrc[0] == 0xE9) {\n        // jmp rax, imm64 <address>\n        pbDst[0] = 0x48;\n        pbDst[1] = 0xB8;\n        *(UNALIGNED LONG_PTR*)&pbDst[2] = nNewOffset;\n\n        // jmp rax\n        pbDst[10] = 0xFF;\n        pbDst[11] = 0xE0;\n\n        *m_plExtra = 12 - 5;\n        *m_ppbTarget = (PBYTE)nNewOffset;\n        return pbSrc + 5;\n    }\n#endif // DETOURS_X64\n#endif // DETOURS_KERNEL\n\n    return CopyBytes(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::AdjustTarget(PBYTE pbDst, PBYTE pbSrc, UINT cbOp,\n                               UINT cbTargetOffset, UINT cbTargetSize)\n{\n    PBYTE pbTarget = NULL;\n#if 1 // fault injection to test test code\n#if defined(DETOURS_X64)\n    typedef LONGLONG T;\n#else\n    typedef LONG T;\n#endif\n    T nOldOffset;\n    T nNewOffset;\n    PVOID pvTargetAddr = &pbDst[cbTargetOffset];\n\n    switch (cbTargetSize) {\n      case 1:\n        nOldOffset = *(signed char*&)pvTargetAddr;\n        break;\n      case 2:\n        nOldOffset = *(UNALIGNED SHORT*&)pvTargetAddr;\n        break;\n      case 4:\n        nOldOffset = *(UNALIGNED LONG*&)pvTargetAddr;\n        break;\n#if defined(DETOURS_X64)\n      case 8:\n        nOldOffset = *(UNALIGNED LONGLONG*&)pvTargetAddr;\n        break;\n#endif\n      default:\n        ASSERT(!\"cbTargetSize is invalid.\");\n        nOldOffset = 0;\n        break;\n    }\n\n    pbTarget = pbSrc + cbOp + nOldOffset;\n    nNewOffset = nOldOffset - (T)(pbDst - pbSrc);\n\n    switch (cbTargetSize) {\n      case 1:\n        *(CHAR*&)pvTargetAddr = (CHAR)nNewOffset;\n        if (nNewOffset < SCHAR_MIN || nNewOffset > SCHAR_MAX) {\n            *m_plExtra = sizeof(ULONG) - 1;\n        }\n        break;\n      case 2:\n        *(UNALIGNED SHORT*&)pvTargetAddr = (SHORT)nNewOffset;\n        if (nNewOffset < SHRT_MIN || nNewOffset > SHRT_MAX) {\n            *m_plExtra = sizeof(ULONG) - 2;\n        }\n        break;\n      case 4:\n        *(UNALIGNED LONG*&)pvTargetAddr = (LONG)nNewOffset;\n        if (nNewOffset < LONG_MIN || nNewOffset > LONG_MAX) {\n            *m_plExtra = sizeof(ULONG) - 4;\n        }\n        break;\n#if defined(DETOURS_X64)\n      case 8:\n        *(UNALIGNED LONGLONG*&)pvTargetAddr = nNewOffset;\n        break;\n#endif\n    }\n#ifdef DETOURS_X64\n    // When we are only computing size, source and dest can be\n    // far apart, distance not encodable in 32bits. Ok.\n    // At least still check the lower 32bits.\n\n    if (pbDst >= m_rbScratchDst && pbDst < (sizeof(m_rbScratchDst) + m_rbScratchDst)) {\n        ASSERT((((size_t)pbDst + cbOp + nNewOffset) & 0xFFFFFFFF) == (((size_t)pbTarget) & 0xFFFFFFFF));\n    }\n    else\n#endif\n    {\n        ASSERT(pbDst + cbOp + nNewOffset == pbTarget);\n    }\n#endif\n    return pbTarget;\n}\n\nPBYTE CDetourDis::Invalid(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    (void)pbDst;\n    (void)pEntry;\n    ASSERT(!\"Invalid Instruction\");\n    return pbSrc + 1;\n}\n\n////////////////////////////////////////////////////// Individual Bytes Codes.\n//\nPBYTE CDetourDis::Copy0F(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    pbDst[0] = pbSrc[0];\n    pEntry = &s_rceCopyTable0F[pbSrc[1]];\n    return (this->*pEntry->pfCopy)(pEntry, pbDst + 1, pbSrc + 1);\n}\n\nPBYTE CDetourDis::Copy0F78(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n{\n    // vmread, 66/extrq, F2/insertq\n\n    static const COPYENTRY vmread = { 0x78, ENTRY_CopyBytes2Mod };\n    static const COPYENTRY extrq_insertq = { 0x78, ENTRY_CopyBytes4 };\n\n    ASSERT(!(m_bF2 && m_bOperandOverride));\n\n    // For insertq and presumably despite documentation extrq, mode must be 11, not checked.\n    // insertq/extrq/78 are followed by two immediate bytes, and given mode == 11, mod/rm byte is always one byte,\n    // and the 0x78 makes 4 bytes (not counting the 66/F2/F which are accounted for elsewhere)\n\n    REFCOPYENTRY const pEntry = ((m_bF2 || m_bOperandOverride) ? &extrq_insertq : &vmread);\n\n    return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::Copy0F00(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n{\n    // jmpe is 32bit x86 only\n    // Notice that the sizes are the same either way, but jmpe is marked as \"dynamic\".\n\n    static const COPYENTRY other = { 0xB8, ENTRY_CopyBytes2Mod }; // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6 invalid/7\n    static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes2ModDynamic }; // jmpe/6 x86-on-IA64 syscalls\n\n    REFCOPYENTRY const pEntry = (((6 << 3) == ((7 << 3) & pbSrc[1])) ?  &jmpe : &other);\n    return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::Copy0FB8(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n{\n    // jmpe is 32bit x86 only\n\n    static const COPYENTRY popcnt = { 0xB8, ENTRY_CopyBytes2Mod };\n    static const COPYENTRY jmpe = { 0xB8, ENTRY_CopyBytes3Or5Dynamic }; // jmpe x86-on-IA64 syscalls\n    REFCOPYENTRY const pEntry = m_bF3 ? &popcnt : &jmpe;\n    return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::Copy66(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{   // Operand-size override prefix\n    m_bOperandOverride = TRUE;\n    return CopyBytesPrefix(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::Copy67(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{   // Address size override prefix\n    m_bAddressOverride = TRUE;\n    return CopyBytesPrefix(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyF2(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    m_bF2 = TRUE;\n    return CopyBytesPrefix(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyF3(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{ // x86 only\n    m_bF3 = TRUE;\n    return CopyBytesPrefix(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyF6(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    (void)pEntry;\n\n    // TEST BYTE /0\n    if (0x00 == (0x38 & pbSrc[1])) {    // reg(bits 543) of ModR/M == 0\n        static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod1 };\n        return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);\n    }\n    // DIV /6\n    // IDIV /7\n    // IMUL /5\n    // MUL /4\n    // NEG /3\n    // NOT /2\n\n    static const COPYENTRY ce = { 0xf6, ENTRY_CopyBytes2Mod };\n    return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyF7(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{\n    (void)pEntry;\n\n    // TEST WORD /0\n    if (0x00 == (0x38 & pbSrc[1])) {    // reg(bits 543) of ModR/M == 0\n        static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2ModOperand };\n        return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);\n    }\n\n    // DIV /6\n    // IDIV /7\n    // IMUL /5\n    // MUL /4\n    // NEG /3\n    // NOT /2\n    static const COPYENTRY ce = { 0xf7, ENTRY_CopyBytes2Mod };\n    return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)\n{   // INC /0\n    // DEC /1\n    // CALL /2\n    // CALL /3\n    // JMP /4\n    // JMP /5\n    // PUSH /6\n    // invalid/7\n    (void)pEntry;\n\n    static const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };\n    PBYTE pbOut = (this->*ce.pfCopy)(&ce, pbDst, pbSrc);\n\n    BYTE const b1 = pbSrc[1];\n\n    if (0x15 == b1 || 0x25 == b1) {         // CALL [], JMP []\n#ifdef DETOURS_X64\n        // All segments but FS and GS are equivalent.\n        if (m_nSegmentOverride != 0x64 && m_nSegmentOverride != 0x65)\n#else\n        if (m_nSegmentOverride == 0 || m_nSegmentOverride == 0x2E)\n#endif\n        {\n#ifdef DETOURS_X64\n            INT32 offset = *(UNALIGNED INT32*)&pbSrc[2];\n            PBYTE *ppbTarget = (PBYTE *)(pbSrc + 6 + offset);\n#else\n            PBYTE *ppbTarget = (PBYTE *)(SIZE_T)*(UNALIGNED ULONG*)&pbSrc[2];\n#endif\n            if (s_fLimitReferencesToModule &&\n                (ppbTarget < (PVOID)s_pbModuleBeg || ppbTarget >= (PVOID)s_pbModuleEnd)) {\n\n                *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n            }\n            else {\n                // This can access violate on random bytes. Use DetourSetCodeModule.\n                *m_ppbTarget = *ppbTarget;\n            }\n        }\n        else {\n            *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n        }\n    }\n    else if (0x10 == (0x30 & b1) || // CALL /2 or /3  --> reg(bits 543) of ModR/M == 010 or 011\n             0x20 == (0x30 & b1)) { // JMP /4 or /5 --> reg(bits 543) of ModR/M == 100 or 101\n        *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    }\n    return pbOut;\n}\n\nPBYTE CDetourDis::CopyVexCommon(BYTE m, PBYTE pbDst, PBYTE pbSrc)\n// m is first instead of last in the hopes of pbDst/pbSrc being\n// passed along efficiently in the registers they were already in.\n{\n    static const COPYENTRY ceF38 = { 0x38, ENTRY_CopyBytes2Mod };\n    static const COPYENTRY ceF3A = { 0x3A, ENTRY_CopyBytes2Mod1 };\n    static const COPYENTRY Invalid = { 0xC4, ENTRY_Invalid };\n\n    m_bVex = TRUE;\n    REFCOPYENTRY pEntry;\n    switch (m) {\n    default: pEntry = &Invalid; break;\n    case 1:  pEntry = &s_rceCopyTable0F[pbSrc[0]]; break;\n    case 2:  pEntry = &ceF38; break;\n    case 3:  pEntry = &ceF3A; break;\n    }\n\n    switch (pbSrc[-1] & 3) { // p in last byte\n    case 0: break;\n    case 1: m_bOperandOverride = TRUE; break;\n    case 2: m_bF3 = TRUE; break;\n    case 3: m_bF2 = TRUE; break;\n    }\n\n    return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n}\n\nPBYTE CDetourDis::CopyVex3(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n// 3 byte VEX prefix 0xC4\n{\n#ifdef DETOURS_X86\n    const static COPYENTRY ceLES = { 0xC4, ENTRY_CopyBytes2Mod };\n    if ((pbSrc[1] & 0xC0) != 0xC0) {\n        REFCOPYENTRY pEntry = &ceLES;\n        return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n    }\n#endif\n    pbDst[0] = pbSrc[0];\n    pbDst[1] = pbSrc[1];\n    pbDst[2] = pbSrc[2];\n#ifdef DETOURS_X64\n    m_bRaxOverride |= !!(pbSrc[2] & 0x80); // w in last byte, see CopyBytesRax\n#else\n    //\n    // TODO\n    //\n    // Usually the VEX.W bit changes the size of a general purpose register and is ignored for 32bit.\n    // Sometimes it is an opcode extension.\n    // Look in the Intel manual, in the instruction-by-instruction reference, for \".W1\",\n    // without nearby wording saying it is ignored for 32bit.\n    // For example: \"VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values\".\n    //\n    // Then, go through each such case and determine if W0 vs. W1 affect the size of the instruction. Probably not.\n    // Look for the same encoding but with \"W1\" changed to \"W0\".\n    // Here is one such pairing:\n    // VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values\n    //\n    // VEX.DDS.128.66.0F38.W1 98 /r A V/V FMA Multiply packed double-precision floating-point values\n    // from xmm0 and xmm2/mem, add to xmm1 and\n    // put result in xmm0.\n    // VFMADD132PD xmm0, xmm1, xmm2/m128\n    //\n    // VFMADD132PS/VFMADD213PS/VFMADD231PS Fused Multiply-Add of Packed Single-Precision Floating-Point Values\n    // VEX.DDS.128.66.0F38.W0 98 /r A V/V FMA Multiply packed single-precision floating-point values\n    // from xmm0 and xmm2/mem, add to xmm1 and put\n    // result in xmm0.\n    // VFMADD132PS xmm0, xmm1, xmm2/m128\n    //\n#endif\n    return CopyVexCommon(pbSrc[1] & 0x1F, pbDst + 3, pbSrc + 3);\n}\n\nPBYTE CDetourDis::CopyVex2(REFCOPYENTRY, PBYTE pbDst, PBYTE pbSrc)\n// 2 byte VEX prefix 0xC5\n{\n#ifdef DETOURS_X86\n    const static COPYENTRY ceLDS = { 0xC5, ENTRY_CopyBytes2Mod };\n    if ((pbSrc[1] & 0xC0) != 0xC0) {\n        REFCOPYENTRY pEntry = &ceLDS;\n        return (this->*pEntry->pfCopy)(pEntry, pbDst, pbSrc);\n    }\n#endif\n    pbDst[0] = pbSrc[0];\n    pbDst[1] = pbSrc[1];\n    return CopyVexCommon(1, pbDst + 2, pbSrc + 2);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPBYTE CDetourDis::s_pbModuleBeg = NULL;\nPBYTE CDetourDis::s_pbModuleEnd = (PBYTE)~(ULONG_PTR)0;\nBOOL CDetourDis::s_fLimitReferencesToModule = FALSE;\n\nBOOL CDetourDis::SetCodeModule(PBYTE pbBeg, PBYTE pbEnd, BOOL fLimitReferencesToModule)\n{\n    if (pbEnd < pbBeg) {\n        return FALSE;\n    }\n\n    s_pbModuleBeg = pbBeg;\n    s_pbModuleEnd = pbEnd;\n    s_fLimitReferencesToModule = fLimitReferencesToModule;\n\n    return TRUE;\n}\n\n///////////////////////////////////////////////////////// Disassembler Tables.\n//\nconst BYTE CDetourDis::s_rbModRm[256] = {\n    0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 0x\n    0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 1x\n    0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 2x\n    0,0,0,0, SIB|1,RIP|4,0,0, 0,0,0,0, SIB|1,RIP|4,0,0, // 3x\n    1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1,                 // 4x\n    1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1,                 // 5x\n    1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1,                 // 6x\n    1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1,                 // 7x\n    4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4,                 // 8x\n    4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4,                 // 9x\n    4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4,                 // Ax\n    4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4,                 // Bx\n    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,                 // Cx\n    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,                 // Dx\n    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,                 // Ex\n    0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0                  // Fx\n};\n\nconst CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[257] =\n{\n    { 0x00, ENTRY_CopyBytes2Mod },                      // ADD /r\n    { 0x01, ENTRY_CopyBytes2Mod },                      // ADD /r\n    { 0x02, ENTRY_CopyBytes2Mod },                      // ADD /r\n    { 0x03, ENTRY_CopyBytes2Mod },                      // ADD /r\n    { 0x04, ENTRY_CopyBytes2 },                         // ADD ib\n    { 0x05, ENTRY_CopyBytes3Or5 },                      // ADD iw\n#ifdef DETOURS_X64\n    { 0x06, ENTRY_Invalid },                            // Invalid\n    { 0x07, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x06, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x07, ENTRY_CopyBytes1 },                         // POP\n#endif\n    { 0x08, ENTRY_CopyBytes2Mod },                      // OR /r\n    { 0x09, ENTRY_CopyBytes2Mod },                      // OR /r\n    { 0x0A, ENTRY_CopyBytes2Mod },                      // OR /r\n    { 0x0B, ENTRY_CopyBytes2Mod },                      // OR /r\n    { 0x0C, ENTRY_CopyBytes2 },                         // OR ib\n    { 0x0D, ENTRY_CopyBytes3Or5 },                      // OR iw\n#ifdef DETOURS_X64\n    { 0x0E, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x0E, ENTRY_CopyBytes1 },                         // PUSH\n#endif\n    { 0x0F, ENTRY_Copy0F },                             // Extension Ops\n    { 0x10, ENTRY_CopyBytes2Mod },                      // ADC /r\n    { 0x11, ENTRY_CopyBytes2Mod },                      // ADC /r\n    { 0x12, ENTRY_CopyBytes2Mod },                      // ADC /r\n    { 0x13, ENTRY_CopyBytes2Mod },                      // ADC /r\n    { 0x14, ENTRY_CopyBytes2 },                         // ADC ib\n    { 0x15, ENTRY_CopyBytes3Or5 },                      // ADC id\n#ifdef DETOURS_X64\n    { 0x16, ENTRY_Invalid },                            // Invalid\n    { 0x17, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x16, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x17, ENTRY_CopyBytes1 },                         // POP\n#endif\n    { 0x18, ENTRY_CopyBytes2Mod },                      // SBB /r\n    { 0x19, ENTRY_CopyBytes2Mod },                      // SBB /r\n    { 0x1A, ENTRY_CopyBytes2Mod },                      // SBB /r\n    { 0x1B, ENTRY_CopyBytes2Mod },                      // SBB /r\n    { 0x1C, ENTRY_CopyBytes2 },                         // SBB ib\n    { 0x1D, ENTRY_CopyBytes3Or5 },                      // SBB id\n#ifdef DETOURS_X64\n    { 0x1E, ENTRY_Invalid },                            // Invalid\n    { 0x1F, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x1E, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x1F, ENTRY_CopyBytes1 },                         // POP\n#endif\n    { 0x20, ENTRY_CopyBytes2Mod },                      // AND /r\n    { 0x21, ENTRY_CopyBytes2Mod },                      // AND /r\n    { 0x22, ENTRY_CopyBytes2Mod },                      // AND /r\n    { 0x23, ENTRY_CopyBytes2Mod },                      // AND /r\n    { 0x24, ENTRY_CopyBytes2 },                         // AND ib\n    { 0x25, ENTRY_CopyBytes3Or5 },                      // AND id\n    { 0x26, ENTRY_CopyBytesSegment },                   // ES prefix\n#ifdef DETOURS_X64\n    { 0x27, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x27, ENTRY_CopyBytes1 },                         // DAA\n#endif\n    { 0x28, ENTRY_CopyBytes2Mod },                      // SUB /r\n    { 0x29, ENTRY_CopyBytes2Mod },                      // SUB /r\n    { 0x2A, ENTRY_CopyBytes2Mod },                      // SUB /r\n    { 0x2B, ENTRY_CopyBytes2Mod },                      // SUB /r\n    { 0x2C, ENTRY_CopyBytes2 },                         // SUB ib\n    { 0x2D, ENTRY_CopyBytes3Or5 },                      // SUB id\n    { 0x2E, ENTRY_CopyBytesSegment },                   // CS prefix\n#ifdef DETOURS_X64\n    { 0x2F, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x2F, ENTRY_CopyBytes1 },                         // DAS\n#endif\n    { 0x30, ENTRY_CopyBytes2Mod },                      // XOR /r\n    { 0x31, ENTRY_CopyBytes2Mod },                      // XOR /r\n    { 0x32, ENTRY_CopyBytes2Mod },                      // XOR /r\n    { 0x33, ENTRY_CopyBytes2Mod },                      // XOR /r\n    { 0x34, ENTRY_CopyBytes2 },                         // XOR ib\n    { 0x35, ENTRY_CopyBytes3Or5 },                      // XOR id\n    { 0x36, ENTRY_CopyBytesSegment },                   // SS prefix\n#ifdef DETOURS_X64\n    { 0x37, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x37, ENTRY_CopyBytes1 },                         // AAA\n#endif\n    { 0x38, ENTRY_CopyBytes2Mod },                      // CMP /r\n    { 0x39, ENTRY_CopyBytes2Mod },                      // CMP /r\n    { 0x3A, ENTRY_CopyBytes2Mod },                      // CMP /r\n    { 0x3B, ENTRY_CopyBytes2Mod },                      // CMP /r\n    { 0x3C, ENTRY_CopyBytes2 },                         // CMP ib\n    { 0x3D, ENTRY_CopyBytes3Or5 },                      // CMP id\n    { 0x3E, ENTRY_CopyBytesSegment },                   // DS prefix\n#ifdef DETOURS_X64\n    { 0x3F, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x3F, ENTRY_CopyBytes1 },                         // AAS\n#endif\n#ifdef DETOURS_X64 // For Rax Prefix\n    { 0x40, ENTRY_CopyBytesRax },                       // Rax\n    { 0x41, ENTRY_CopyBytesRax },                       // Rax\n    { 0x42, ENTRY_CopyBytesRax },                       // Rax\n    { 0x43, ENTRY_CopyBytesRax },                       // Rax\n    { 0x44, ENTRY_CopyBytesRax },                       // Rax\n    { 0x45, ENTRY_CopyBytesRax },                       // Rax\n    { 0x46, ENTRY_CopyBytesRax },                       // Rax\n    { 0x47, ENTRY_CopyBytesRax },                       // Rax\n    { 0x48, ENTRY_CopyBytesRax },                       // Rax\n    { 0x49, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4A, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4B, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4C, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4D, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4E, ENTRY_CopyBytesRax },                       // Rax\n    { 0x4F, ENTRY_CopyBytesRax },                       // Rax\n#else\n    { 0x40, ENTRY_CopyBytes1 },                         // INC\n    { 0x41, ENTRY_CopyBytes1 },                         // INC\n    { 0x42, ENTRY_CopyBytes1 },                         // INC\n    { 0x43, ENTRY_CopyBytes1 },                         // INC\n    { 0x44, ENTRY_CopyBytes1 },                         // INC\n    { 0x45, ENTRY_CopyBytes1 },                         // INC\n    { 0x46, ENTRY_CopyBytes1 },                         // INC\n    { 0x47, ENTRY_CopyBytes1 },                         // INC\n    { 0x48, ENTRY_CopyBytes1 },                         // DEC\n    { 0x49, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4A, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4B, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4C, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4D, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4E, ENTRY_CopyBytes1 },                         // DEC\n    { 0x4F, ENTRY_CopyBytes1 },                         // DEC\n#endif\n    { 0x50, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x51, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x52, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x53, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x54, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x55, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x56, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x57, ENTRY_CopyBytes1 },                         // PUSH\n    { 0x58, ENTRY_CopyBytes1 },                         // POP\n    { 0x59, ENTRY_CopyBytes1 },                         // POP\n    { 0x5A, ENTRY_CopyBytes1 },                         // POP\n    { 0x5B, ENTRY_CopyBytes1 },                         // POP\n    { 0x5C, ENTRY_CopyBytes1 },                         // POP\n    { 0x5D, ENTRY_CopyBytes1 },                         // POP\n    { 0x5E, ENTRY_CopyBytes1 },                         // POP\n    { 0x5F, ENTRY_CopyBytes1 },                         // POP\n#ifdef DETOURS_X64\n    { 0x60, ENTRY_Invalid },                            // Invalid\n    { 0x61, ENTRY_Invalid },                            // Invalid\n    { 0x62, ENTRY_Invalid },                            // Invalid (not yet implemented Intel EVEX support)\n#else\n    { 0x60, ENTRY_CopyBytes1 },                         // PUSHAD\n    { 0x61, ENTRY_CopyBytes1 },                         // POPAD\n    { 0x62, ENTRY_CopyBytes2Mod },                      // BOUND /r\n#endif\n    { 0x63, ENTRY_CopyBytes2Mod },                      // 32bit ARPL /r, 64bit MOVSXD\n    { 0x64, ENTRY_CopyBytesSegment },                   // FS prefix\n    { 0x65, ENTRY_CopyBytesSegment },                   // GS prefix\n    { 0x66, ENTRY_Copy66 },                             // Operand Prefix\n    { 0x67, ENTRY_Copy67 },                             // Address Prefix\n    { 0x68, ENTRY_CopyBytes3Or5 },                      // PUSH\n    { 0x69, ENTRY_CopyBytes2ModOperand },               // IMUL /r iz\n    { 0x6A, ENTRY_CopyBytes2 },                         // PUSH\n    { 0x6B, ENTRY_CopyBytes2Mod1 },                     // IMUL /r ib\n    { 0x6C, ENTRY_CopyBytes1 },                         // INS\n    { 0x6D, ENTRY_CopyBytes1 },                         // INS\n    { 0x6E, ENTRY_CopyBytes1 },                         // OUTS/OUTSB\n    { 0x6F, ENTRY_CopyBytes1 },                         // OUTS/OUTSW\n    { 0x70, ENTRY_CopyBytes2Jump },                     // JO           // 0f80\n    { 0x71, ENTRY_CopyBytes2Jump },                     // JNO          // 0f81\n    { 0x72, ENTRY_CopyBytes2Jump },                     // JB/JC/JNAE   // 0f82\n    { 0x73, ENTRY_CopyBytes2Jump },                     // JAE/JNB/JNC  // 0f83\n    { 0x74, ENTRY_CopyBytes2Jump },                     // JE/JZ        // 0f84\n    { 0x75, ENTRY_CopyBytes2Jump },                     // JNE/JNZ      // 0f85\n    { 0x76, ENTRY_CopyBytes2Jump },                     // JBE/JNA      // 0f86\n    { 0x77, ENTRY_CopyBytes2Jump },                     // JA/JNBE      // 0f87\n    { 0x78, ENTRY_CopyBytes2Jump },                     // JS           // 0f88\n    { 0x79, ENTRY_CopyBytes2Jump },                     // JNS          // 0f89\n    { 0x7A, ENTRY_CopyBytes2Jump },                     // JP/JPE       // 0f8a\n    { 0x7B, ENTRY_CopyBytes2Jump },                     // JNP/JPO      // 0f8b\n    { 0x7C, ENTRY_CopyBytes2Jump },                     // JL/JNGE      // 0f8c\n    { 0x7D, ENTRY_CopyBytes2Jump },                     // JGE/JNL      // 0f8d\n    { 0x7E, ENTRY_CopyBytes2Jump },                     // JLE/JNG      // 0f8e\n    { 0x7F, ENTRY_CopyBytes2Jump },                     // JG/JNLE      // 0f8f\n    { 0x80, ENTRY_CopyBytes2Mod1 },                     // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate byte\n    { 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\n#ifdef DETOURS_X64\n    { 0x82, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x82, ENTRY_CopyBytes2Mod1 },                     // MOV al,x\n#endif\n    { 0x83, ENTRY_CopyBytes2Mod1 },                     // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 reg, immediate byte\n    { 0x84, ENTRY_CopyBytes2Mod },                      // TEST /r\n    { 0x85, ENTRY_CopyBytes2Mod },                      // TEST /r\n    { 0x86, ENTRY_CopyBytes2Mod },                      // XCHG /r @todo\n    { 0x87, ENTRY_CopyBytes2Mod },                      // XCHG /r @todo\n    { 0x88, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x89, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x8A, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x8B, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x8C, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x8D, ENTRY_CopyBytes2Mod },                      // LEA /r\n    { 0x8E, ENTRY_CopyBytes2Mod },                      // MOV /r\n    { 0x8F, ENTRY_CopyBytes2Mod },                      // POP /0\n    { 0x90, ENTRY_CopyBytes1 },                         // NOP\n    { 0x91, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x92, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x93, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x94, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x95, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x96, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x97, ENTRY_CopyBytes1 },                         // XCHG\n    { 0x98, ENTRY_CopyBytes1 },                         // CWDE\n    { 0x99, ENTRY_CopyBytes1 },                         // CDQ\n#ifdef DETOURS_X64\n    { 0x9A, ENTRY_Invalid },                            // Invalid\n#else\n    { 0x9A, ENTRY_CopyBytes5Or7Dynamic },               // CALL cp\n#endif\n    { 0x9B, ENTRY_CopyBytes1 },                         // WAIT/FWAIT\n    { 0x9C, ENTRY_CopyBytes1 },                         // PUSHFD\n    { 0x9D, ENTRY_CopyBytes1 },                         // POPFD\n    { 0x9E, ENTRY_CopyBytes1 },                         // SAHF\n    { 0x9F, ENTRY_CopyBytes1 },                         // LAHF\n    { 0xA0, ENTRY_CopyBytes1Address },                  // MOV\n    { 0xA1, ENTRY_CopyBytes1Address },                  // MOV\n    { 0xA2, ENTRY_CopyBytes1Address },                  // MOV\n    { 0xA3, ENTRY_CopyBytes1Address },                  // MOV\n    { 0xA4, ENTRY_CopyBytes1 },                         // MOVS\n    { 0xA5, ENTRY_CopyBytes1 },                         // MOVS/MOVSD\n    { 0xA6, ENTRY_CopyBytes1 },                         // CMPS/CMPSB\n    { 0xA7, ENTRY_CopyBytes1 },                         // CMPS/CMPSW\n    { 0xA8, ENTRY_CopyBytes2 },                         // TEST\n    { 0xA9, ENTRY_CopyBytes3Or5 },                      // TEST\n    { 0xAA, ENTRY_CopyBytes1 },                         // STOS/STOSB\n    { 0xAB, ENTRY_CopyBytes1 },                         // STOS/STOSW\n    { 0xAC, ENTRY_CopyBytes1 },                         // LODS/LODSB\n    { 0xAD, ENTRY_CopyBytes1 },                         // LODS/LODSW\n    { 0xAE, ENTRY_CopyBytes1 },                         // SCAS/SCASB\n    { 0xAF, ENTRY_CopyBytes1 },                         // SCAS/SCASD\n    { 0xB0, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB1, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB2, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB3, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB4, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB5, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB6, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB7, ENTRY_CopyBytes2 },                         // MOV B0+rb\n    { 0xB8, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xB9, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBA, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBB, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBC, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBD, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBE, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xBF, ENTRY_CopyBytes3Or5Rax },                   // MOV B8+rb\n    { 0xC0, ENTRY_CopyBytes2Mod1 },                     // RCL/2 ib, etc.\n    { 0xC1, ENTRY_CopyBytes2Mod1 },                     // RCL/2 ib, etc.\n    { 0xC2, ENTRY_CopyBytes3 },                         // RET\n    { 0xC3, ENTRY_CopyBytes1 },                         // RET\n    { 0xC4, ENTRY_CopyVex3 },                           // LES, VEX 3-byte opcodes.\n    { 0xC5, ENTRY_CopyVex2 },                           // LDS, VEX 2-byte opcodes.\n    { 0xC6, ENTRY_CopyBytes2Mod1 },                     // MOV\n    { 0xC7, ENTRY_CopyBytes2ModOperand },               // MOV/0 XBEGIN/7\n    { 0xC8, ENTRY_CopyBytes4 },                         // ENTER\n    { 0xC9, ENTRY_CopyBytes1 },                         // LEAVE\n    { 0xCA, ENTRY_CopyBytes3Dynamic },                  // RET\n    { 0xCB, ENTRY_CopyBytes1Dynamic },                  // RET\n    { 0xCC, ENTRY_CopyBytes1Dynamic },                  // INT 3\n    { 0xCD, ENTRY_CopyBytes2Dynamic },                  // INT ib\n#ifdef DETOURS_X64\n    { 0xCE, ENTRY_Invalid },                            // Invalid\n#else\n    { 0xCE, ENTRY_CopyBytes1Dynamic },                  // INTO\n#endif\n    { 0xCF, ENTRY_CopyBytes1Dynamic },                  // IRET\n    { 0xD0, ENTRY_CopyBytes2Mod },                      // RCL/2, etc.\n    { 0xD1, ENTRY_CopyBytes2Mod },                      // RCL/2, etc.\n    { 0xD2, ENTRY_CopyBytes2Mod },                      // RCL/2, etc.\n    { 0xD3, ENTRY_CopyBytes2Mod },                      // RCL/2, etc.\n#ifdef DETOURS_X64\n    { 0xD4, ENTRY_Invalid },                            // Invalid\n    { 0xD5, ENTRY_Invalid },                            // Invalid\n#else\n    { 0xD4, ENTRY_CopyBytes2 },                         // AAM\n    { 0xD5, ENTRY_CopyBytes2 },                         // AAD\n#endif\n    { 0xD6, ENTRY_Invalid },                            // Invalid\n    { 0xD7, ENTRY_CopyBytes1 },                         // XLAT/XLATB\n    { 0xD8, ENTRY_CopyBytes2Mod },                      // FADD, etc.\n    { 0xD9, ENTRY_CopyBytes2Mod },                      // F2XM1, etc.\n    { 0xDA, ENTRY_CopyBytes2Mod },                      // FLADD, etc.\n    { 0xDB, ENTRY_CopyBytes2Mod },                      // FCLEX, etc.\n    { 0xDC, ENTRY_CopyBytes2Mod },                      // FADD/0, etc.\n    { 0xDD, ENTRY_CopyBytes2Mod },                      // FFREE, etc.\n    { 0xDE, ENTRY_CopyBytes2Mod },                      // FADDP, etc.\n    { 0xDF, ENTRY_CopyBytes2Mod },                      // FBLD/4, etc.\n    { 0xE0, ENTRY_CopyBytes2CantJump },                 // LOOPNE cb\n    { 0xE1, ENTRY_CopyBytes2CantJump },                 // LOOPE cb\n    { 0xE2, ENTRY_CopyBytes2CantJump },                 // LOOP cb\n    { 0xE3, ENTRY_CopyBytes2CantJump },                 // JCXZ/JECXZ\n    { 0xE4, ENTRY_CopyBytes2 },                         // IN ib\n    { 0xE5, ENTRY_CopyBytes2 },                         // IN id\n    { 0xE6, ENTRY_CopyBytes2 },                         // OUT ib\n    { 0xE7, ENTRY_CopyBytes2 },                         // OUT ib\n    { 0xE8, ENTRY_CopyBytesJumpToAbsolute },            // CALL cd\n    { 0xE9, ENTRY_CopyBytesJumpToAbsolute },            // JMP cd\n#ifdef DETOURS_X64\n    { 0xEA, ENTRY_Invalid },                            // Invalid\n#else\n    { 0xEA, ENTRY_CopyBytes5Or7Dynamic },               // JMP cp\n#endif\n    { 0xEB, ENTRY_CopyBytes2Jump },                     // JMP cb\n    { 0xEC, ENTRY_CopyBytes1 },                         // IN ib\n    { 0xED, ENTRY_CopyBytes1 },                         // IN id\n    { 0xEE, ENTRY_CopyBytes1 },                         // OUT\n    { 0xEF, ENTRY_CopyBytes1 },                         // OUT\n    { 0xF0, ENTRY_CopyBytesPrefix },                    // LOCK prefix\n    { 0xF1, ENTRY_CopyBytes1Dynamic },                  // INT1 / ICEBP somewhat documented by AMD, not by Intel\n    { 0xF2, ENTRY_CopyF2 },                             // REPNE prefix\n//#ifdef DETOURS_X86\n    { 0xF3, ENTRY_CopyF3 },                             // REPE prefix\n//#else\n// This does presently suffice for AMD64 but it requires tracing\n// through a bunch of code to verify and seems not worth maintaining.\n//  { 0xF3, ENTRY_CopyBytesPrefix },                    // REPE prefix\n//#endif\n    { 0xF4, ENTRY_CopyBytes1 },                         // HLT\n    { 0xF5, ENTRY_CopyBytes1 },                         // CMC\n    { 0xF6, ENTRY_CopyF6 },                             // TEST/0, DIV/6\n    { 0xF7, ENTRY_CopyF7 },                             // TEST/0, DIV/6\n    { 0xF8, ENTRY_CopyBytes1 },                         // CLC\n    { 0xF9, ENTRY_CopyBytes1 },                         // STC\n    { 0xFA, ENTRY_CopyBytes1 },                         // CLI\n    { 0xFB, ENTRY_CopyBytes1 },                         // STI\n    { 0xFC, ENTRY_CopyBytes1 },                         // CLD\n    { 0xFD, ENTRY_CopyBytes1 },                         // STD\n    { 0xFE, ENTRY_CopyBytes2Mod },                      // DEC/1,INC/0\n    { 0xFF, ENTRY_CopyFF },                             // CALL/2\n    { 0, ENTRY_End },\n};\n\nconst CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable0F[257] =\n{\n#ifdef DETOURS_X86\n    { 0x00, ENTRY_Copy0F00 },                           // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7\n#else\n    { 0x00, ENTRY_CopyBytes2Mod },                      // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7\n#endif\n    { 0x01, ENTRY_CopyBytes2Mod },                      // INVLPG/7, etc.\n    { 0x02, ENTRY_CopyBytes2Mod },                      // LAR/r\n    { 0x03, ENTRY_CopyBytes2Mod },                      // LSL/r\n    { 0x04, ENTRY_Invalid },                            // _04\n    { 0x05, ENTRY_CopyBytes1 },                         // SYSCALL\n    { 0x06, ENTRY_CopyBytes1 },                         // CLTS\n    { 0x07, ENTRY_CopyBytes1 },                         // SYSRET\n    { 0x08, ENTRY_CopyBytes1 },                         // INVD\n    { 0x09, ENTRY_CopyBytes1 },                         // WBINVD\n    { 0x0A, ENTRY_Invalid },                            // _0A\n    { 0x0B, ENTRY_CopyBytes1 },                         // UD2\n    { 0x0C, ENTRY_Invalid },                            // _0C\n    { 0x0D, ENTRY_CopyBytes2Mod },                      // PREFETCH\n    { 0x0E, ENTRY_CopyBytes1 },                         // FEMMS (3DNow -- not in Intel documentation)\n    { 0x0F, ENTRY_CopyBytes2Mod1 },                     // 3DNow Opcodes\n    { 0x10, ENTRY_CopyBytes2Mod },                      // MOVSS MOVUPD MOVSD\n    { 0x11, ENTRY_CopyBytes2Mod },                      // MOVSS MOVUPD MOVSD\n    { 0x12, ENTRY_CopyBytes2Mod },                      // MOVLPD\n    { 0x13, ENTRY_CopyBytes2Mod },                      // MOVLPD\n    { 0x14, ENTRY_CopyBytes2Mod },                      // UNPCKLPD\n    { 0x15, ENTRY_CopyBytes2Mod },                      // UNPCKHPD\n    { 0x16, ENTRY_CopyBytes2Mod },                      // MOVHPD\n    { 0x17, ENTRY_CopyBytes2Mod },                      // MOVHPD\n    { 0x18, ENTRY_CopyBytes2Mod },                      // PREFETCHINTA...\n    { 0x19, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1A, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1B, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1C, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1D, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1E, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop, not documented by Intel, documented by AMD\n    { 0x1F, ENTRY_CopyBytes2Mod },                      // NOP/r multi byte nop\n    { 0x20, ENTRY_CopyBytes2Mod },                      // MOV/r\n    { 0x21, ENTRY_CopyBytes2Mod },                      // MOV/r\n    { 0x22, ENTRY_CopyBytes2Mod },                      // MOV/r\n    { 0x23, ENTRY_CopyBytes2Mod },                      // MOV/r\n#ifdef DETOURS_X64\n    { 0x24, ENTRY_Invalid },                            // _24\n#else\n    { 0x24, ENTRY_CopyBytes2Mod },                      // MOV/r,TR TR is test register on 80386 and 80486, removed in Pentium\n#endif\n    { 0x25, ENTRY_Invalid },                            // _25\n#ifdef DETOURS_X64\n    { 0x26, ENTRY_Invalid },                            // _26\n#else\n    { 0x26, ENTRY_CopyBytes2Mod },                      // MOV TR/r TR is test register on 80386 and 80486, removed in Pentium\n#endif\n    { 0x27, ENTRY_Invalid },                            // _27\n    { 0x28, ENTRY_CopyBytes2Mod },                      // MOVAPS MOVAPD\n    { 0x29, ENTRY_CopyBytes2Mod },                      // MOVAPS MOVAPD\n    { 0x2A, ENTRY_CopyBytes2Mod },                      // CVPI2PS &\n    { 0x2B, ENTRY_CopyBytes2Mod },                      // MOVNTPS MOVNTPD\n    { 0x2C, ENTRY_CopyBytes2Mod },                      // CVTTPS2PI &\n    { 0x2D, ENTRY_CopyBytes2Mod },                      // CVTPS2PI &\n    { 0x2E, ENTRY_CopyBytes2Mod },                      // UCOMISS UCOMISD\n    { 0x2F, ENTRY_CopyBytes2Mod },                      // COMISS COMISD\n    { 0x30, ENTRY_CopyBytes1 },                         // WRMSR\n    { 0x31, ENTRY_CopyBytes1 },                         // RDTSC\n    { 0x32, ENTRY_CopyBytes1 },                         // RDMSR\n    { 0x33, ENTRY_CopyBytes1 },                         // RDPMC\n    { 0x34, ENTRY_CopyBytes1 },                         // SYSENTER\n    { 0x35, ENTRY_CopyBytes1 },                         // SYSEXIT\n    { 0x36, ENTRY_Invalid },                            // _36\n    { 0x37, ENTRY_CopyBytes1 },                         // GETSEC\n    { 0x38, ENTRY_CopyBytes3Mod },                      // SSE3 Opcodes\n    { 0x39, ENTRY_Invalid },                            // _39\n    { 0x3A, ENTRY_CopyBytes3Mod1 },                      // SSE3 Opcodes\n    { 0x3B, ENTRY_Invalid },                            // _3B\n    { 0x3C, ENTRY_Invalid },                            // _3C\n    { 0x3D, ENTRY_Invalid },                            // _3D\n    { 0x3E, ENTRY_Invalid },                            // _3E\n    { 0x3F, ENTRY_Invalid },                            // _3F\n    { 0x40, ENTRY_CopyBytes2Mod },                      // CMOVO (0F 40)\n    { 0x41, ENTRY_CopyBytes2Mod },                      // CMOVNO (0F 41)\n    { 0x42, ENTRY_CopyBytes2Mod },                      // CMOVB & CMOVNE (0F 42)\n    { 0x43, ENTRY_CopyBytes2Mod },                      // CMOVAE & CMOVNB (0F 43)\n    { 0x44, ENTRY_CopyBytes2Mod },                      // CMOVE & CMOVZ (0F 44)\n    { 0x45, ENTRY_CopyBytes2Mod },                      // CMOVNE & CMOVNZ (0F 45)\n    { 0x46, ENTRY_CopyBytes2Mod },                      // CMOVBE & CMOVNA (0F 46)\n    { 0x47, ENTRY_CopyBytes2Mod },                      // CMOVA & CMOVNBE (0F 47)\n    { 0x48, ENTRY_CopyBytes2Mod },                      // CMOVS (0F 48)\n    { 0x49, ENTRY_CopyBytes2Mod },                      // CMOVNS (0F 49)\n    { 0x4A, ENTRY_CopyBytes2Mod },                      // CMOVP & CMOVPE (0F 4A)\n    { 0x4B, ENTRY_CopyBytes2Mod },                      // CMOVNP & CMOVPO (0F 4B)\n    { 0x4C, ENTRY_CopyBytes2Mod },                      // CMOVL & CMOVNGE (0F 4C)\n    { 0x4D, ENTRY_CopyBytes2Mod },                      // CMOVGE & CMOVNL (0F 4D)\n    { 0x4E, ENTRY_CopyBytes2Mod },                      // CMOVLE & CMOVNG (0F 4E)\n    { 0x4F, ENTRY_CopyBytes2Mod },                      // CMOVG & CMOVNLE (0F 4F)\n    { 0x50, ENTRY_CopyBytes2Mod },                      // MOVMSKPD MOVMSKPD\n    { 0x51, ENTRY_CopyBytes2Mod },                      // SQRTPS &\n    { 0x52, ENTRY_CopyBytes2Mod },                      // RSQRTTS RSQRTPS\n    { 0x53, ENTRY_CopyBytes2Mod },                      // RCPPS RCPSS\n    { 0x54, ENTRY_CopyBytes2Mod },                      // ANDPS ANDPD\n    { 0x55, ENTRY_CopyBytes2Mod },                      // ANDNPS ANDNPD\n    { 0x56, ENTRY_CopyBytes2Mod },                      // ORPS ORPD\n    { 0x57, ENTRY_CopyBytes2Mod },                      // XORPS XORPD\n    { 0x58, ENTRY_CopyBytes2Mod },                      // ADDPS &\n    { 0x59, ENTRY_CopyBytes2Mod },                      // MULPS &\n    { 0x5A, ENTRY_CopyBytes2Mod },                      // CVTPS2PD &\n    { 0x5B, ENTRY_CopyBytes2Mod },                      // CVTDQ2PS &\n    { 0x5C, ENTRY_CopyBytes2Mod },                      // SUBPS &\n    { 0x5D, ENTRY_CopyBytes2Mod },                      // MINPS &\n    { 0x5E, ENTRY_CopyBytes2Mod },                      // DIVPS &\n    { 0x5F, ENTRY_CopyBytes2Mod },                      // MASPS &\n    { 0x60, ENTRY_CopyBytes2Mod },                      // PUNPCKLBW/r\n    { 0x61, ENTRY_CopyBytes2Mod },                      // PUNPCKLWD/r\n    { 0x62, ENTRY_CopyBytes2Mod },                      // PUNPCKLWD/r\n    { 0x63, ENTRY_CopyBytes2Mod },                      // PACKSSWB/r\n    { 0x64, ENTRY_CopyBytes2Mod },                      // PCMPGTB/r\n    { 0x65, ENTRY_CopyBytes2Mod },                      // PCMPGTW/r\n    { 0x66, ENTRY_CopyBytes2Mod },                      // PCMPGTD/r\n    { 0x67, ENTRY_CopyBytes2Mod },                      // PACKUSWB/r\n    { 0x68, ENTRY_CopyBytes2Mod },                      // PUNPCKHBW/r\n    { 0x69, ENTRY_CopyBytes2Mod },                      // PUNPCKHWD/r\n    { 0x6A, ENTRY_CopyBytes2Mod },                      // PUNPCKHDQ/r\n    { 0x6B, ENTRY_CopyBytes2Mod },                      // PACKSSDW/r\n    { 0x6C, ENTRY_CopyBytes2Mod },                      // PUNPCKLQDQ\n    { 0x6D, ENTRY_CopyBytes2Mod },                      // PUNPCKHQDQ\n    { 0x6E, ENTRY_CopyBytes2Mod },                      // MOVD/r\n    { 0x6F, ENTRY_CopyBytes2Mod },                      // MOV/r\n    { 0x70, ENTRY_CopyBytes2Mod1 },                     // PSHUFW/r ib\n    { 0x71, ENTRY_CopyBytes2Mod1 },                     // PSLLW/6 ib,PSRAW/4 ib,PSRLW/2 ib\n    { 0x72, ENTRY_CopyBytes2Mod1 },                     // PSLLD/6 ib,PSRAD/4 ib,PSRLD/2 ib\n    { 0x73, ENTRY_CopyBytes2Mod1 },                     // PSLLQ/6 ib,PSRLQ/2 ib\n    { 0x74, ENTRY_CopyBytes2Mod },                      // PCMPEQB/r\n    { 0x75, ENTRY_CopyBytes2Mod },                      // PCMPEQW/r\n    { 0x76, ENTRY_CopyBytes2Mod },                      // PCMPEQD/r\n    { 0x77, ENTRY_CopyBytes1 },                         // EMMS\n    // extrq/insertq require mode=3 and are followed by two immediate bytes\n    { 0x78, ENTRY_Copy0F78 },                           // VMREAD/r, 66/EXTRQ/r/ib/ib, F2/INSERTQ/r/ib/ib\n    // extrq/insertq require mod=3, therefore ENTRY_CopyBytes2, but it ends up the same\n    { 0x79, ENTRY_CopyBytes2Mod },                      // VMWRITE/r, 66/EXTRQ/r, F2/INSERTQ/r\n    { 0x7A, ENTRY_Invalid },                            // _7A\n    { 0x7B, ENTRY_Invalid },                            // _7B\n    { 0x7C, ENTRY_CopyBytes2Mod },                      // HADDPS\n    { 0x7D, ENTRY_CopyBytes2Mod },                      // HSUBPS\n    { 0x7E, ENTRY_CopyBytes2Mod },                      // MOVD/r\n    { 0x7F, ENTRY_CopyBytes2Mod },                      // MOV/r\n    { 0x80, ENTRY_CopyBytes3Or5Target },                // JO\n    { 0x81, ENTRY_CopyBytes3Or5Target },                // JNO\n    { 0x82, ENTRY_CopyBytes3Or5Target },                // JB,JC,JNAE\n    { 0x83, ENTRY_CopyBytes3Or5Target },                // JAE,JNB,JNC\n    { 0x84, ENTRY_CopyBytes3Or5Target },                // JE,JZ,JZ\n    { 0x85, ENTRY_CopyBytes3Or5Target },                // JNE,JNZ\n    { 0x86, ENTRY_CopyBytes3Or5Target },                // JBE,JNA\n    { 0x87, ENTRY_CopyBytes3Or5Target },                // JA,JNBE\n    { 0x88, ENTRY_CopyBytes3Or5Target },                // JS\n    { 0x89, ENTRY_CopyBytes3Or5Target },                // JNS\n    { 0x8A, ENTRY_CopyBytes3Or5Target },                // JP,JPE\n    { 0x8B, ENTRY_CopyBytes3Or5Target },                // JNP,JPO\n    { 0x8C, ENTRY_CopyBytes3Or5Target },                // JL,NGE\n    { 0x8D, ENTRY_CopyBytes3Or5Target },                // JGE,JNL\n    { 0x8E, ENTRY_CopyBytes3Or5Target },                // JLE,JNG\n    { 0x8F, ENTRY_CopyBytes3Or5Target },                // JG,JNLE\n    { 0x90, ENTRY_CopyBytes2Mod },                      // CMOVO (0F 40)\n    { 0x91, ENTRY_CopyBytes2Mod },                      // CMOVNO (0F 41)\n    { 0x92, ENTRY_CopyBytes2Mod },                      // CMOVB & CMOVC & CMOVNAE (0F 42)\n    { 0x93, ENTRY_CopyBytes2Mod },                      // CMOVAE & CMOVNB & CMOVNC (0F 43)\n    { 0x94, ENTRY_CopyBytes2Mod },                      // CMOVE & CMOVZ (0F 44)\n    { 0x95, ENTRY_CopyBytes2Mod },                      // CMOVNE & CMOVNZ (0F 45)\n    { 0x96, ENTRY_CopyBytes2Mod },                      // CMOVBE & CMOVNA (0F 46)\n    { 0x97, ENTRY_CopyBytes2Mod },                      // CMOVA & CMOVNBE (0F 47)\n    { 0x98, ENTRY_CopyBytes2Mod },                      // CMOVS (0F 48)\n    { 0x99, ENTRY_CopyBytes2Mod },                      // CMOVNS (0F 49)\n    { 0x9A, ENTRY_CopyBytes2Mod },                      // CMOVP & CMOVPE (0F 4A)\n    { 0x9B, ENTRY_CopyBytes2Mod },                      // CMOVNP & CMOVPO (0F 4B)\n    { 0x9C, ENTRY_CopyBytes2Mod },                      // CMOVL & CMOVNGE (0F 4C)\n    { 0x9D, ENTRY_CopyBytes2Mod },                      // CMOVGE & CMOVNL (0F 4D)\n    { 0x9E, ENTRY_CopyBytes2Mod },                      // CMOVLE & CMOVNG (0F 4E)\n    { 0x9F, ENTRY_CopyBytes2Mod },                      // CMOVG & CMOVNLE (0F 4F)\n    { 0xA0, ENTRY_CopyBytes1 },                         // PUSH\n    { 0xA1, ENTRY_CopyBytes1 },                         // POP\n    { 0xA2, ENTRY_CopyBytes1 },                         // CPUID\n    { 0xA3, ENTRY_CopyBytes2Mod },                      // BT  (0F A3)\n    { 0xA4, ENTRY_CopyBytes2Mod1 },                     // SHLD\n    { 0xA5, ENTRY_CopyBytes2Mod },                      // SHLD\n    { 0xA6, ENTRY_CopyBytes2Mod },                      // XBTS\n    { 0xA7, ENTRY_CopyBytes2Mod },                      // IBTS\n    { 0xA8, ENTRY_CopyBytes1 },                         // PUSH\n    { 0xA9, ENTRY_CopyBytes1 },                         // POP\n    { 0xAA, ENTRY_CopyBytes1 },                         // RSM\n    { 0xAB, ENTRY_CopyBytes2Mod },                      // BTS (0F AB)\n    { 0xAC, ENTRY_CopyBytes2Mod1 },                     // SHRD\n    { 0xAD, ENTRY_CopyBytes2Mod },                      // SHRD\n\n    // 0F AE mod76=mem mod543=0 fxsave\n    // 0F AE mod76=mem mod543=1 fxrstor\n    // 0F AE mod76=mem mod543=2 ldmxcsr\n    // 0F AE mod76=mem mod543=3 stmxcsr\n    // 0F AE mod76=mem mod543=4 xsave\n    // 0F AE mod76=mem mod543=5 xrstor\n    // 0F AE mod76=mem mod543=6 saveopt\n    // 0F AE mod76=mem mod543=7 clflush\n    // 0F AE mod76=11b mod543=5 lfence\n    // 0F AE mod76=11b mod543=6 mfence\n    // 0F AE mod76=11b mod543=7 sfence\n    // F3 0F AE mod76=11b mod543=0 rdfsbase\n    // F3 0F AE mod76=11b mod543=1 rdgsbase\n    // F3 0F AE mod76=11b mod543=2 wrfsbase\n    // F3 0F AE mod76=11b mod543=3 wrgsbase\n    { 0xAE, ENTRY_CopyBytes2Mod },                      // fxsave fxrstor ldmxcsr stmxcsr xsave xrstor saveopt clflush lfence mfence sfence rdfsbase rdgsbase wrfsbase wrgsbase\n    { 0xAF, ENTRY_CopyBytes2Mod },                      // IMUL (0F AF)\n    { 0xB0, ENTRY_CopyBytes2Mod },                      // CMPXCHG (0F B0)\n    { 0xB1, ENTRY_CopyBytes2Mod },                      // CMPXCHG (0F B1)\n    { 0xB2, ENTRY_CopyBytes2Mod },                      // LSS/r\n    { 0xB3, ENTRY_CopyBytes2Mod },                      // BTR (0F B3)\n    { 0xB4, ENTRY_CopyBytes2Mod },                      // LFS/r\n    { 0xB5, ENTRY_CopyBytes2Mod },                      // LGS/r\n    { 0xB6, ENTRY_CopyBytes2Mod },                      // MOVZX/r\n    { 0xB7, ENTRY_CopyBytes2Mod },                      // MOVZX/r\n#ifdef DETOURS_X86\n    { 0xB8, ENTRY_Copy0FB8 },                           // jmpe f3/popcnt\n#else\n    { 0xB8, ENTRY_CopyBytes2Mod },                      // f3/popcnt\n#endif\n    { 0xB9, ENTRY_Invalid },                            // _B9\n    { 0xBA, ENTRY_CopyBytes2Mod1 },                     // BT & BTC & BTR & BTS (0F BA)\n    { 0xBB, ENTRY_CopyBytes2Mod },                      // BTC (0F BB)\n    { 0xBC, ENTRY_CopyBytes2Mod },                      // BSF (0F BC)\n    { 0xBD, ENTRY_CopyBytes2Mod },                      // BSR (0F BD)\n    { 0xBE, ENTRY_CopyBytes2Mod },                      // MOVSX/r\n    { 0xBF, ENTRY_CopyBytes2Mod },                      // MOVSX/r\n    { 0xC0, ENTRY_CopyBytes2Mod },                      // XADD/r\n    { 0xC1, ENTRY_CopyBytes2Mod },                      // XADD/r\n    { 0xC2, ENTRY_CopyBytes2Mod1 },                     // CMPPS &\n    { 0xC3, ENTRY_CopyBytes2Mod },                      // MOVNTI\n    { 0xC4, ENTRY_CopyBytes2Mod1 },                     // PINSRW /r ib\n    { 0xC5, ENTRY_CopyBytes2Mod1 },                     // PEXTRW /r ib\n    { 0xC6, ENTRY_CopyBytes2Mod1 },                     // SHUFPS & SHUFPD\n    { 0xC7, ENTRY_CopyBytes2Mod },                      // CMPXCHG8B (0F C7)\n    { 0xC8, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xC9, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xCA, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xCB, ENTRY_CopyBytes1 },                         // CVTPD2PI BSWAP 0F C8 + rd\n    { 0xCC, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xCD, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xCE, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xCF, ENTRY_CopyBytes1 },                         // BSWAP 0F C8 + rd\n    { 0xD0, ENTRY_CopyBytes2Mod },                      // ADDSUBPS (untestd)\n    { 0xD1, ENTRY_CopyBytes2Mod },                      // PSRLW/r\n    { 0xD2, ENTRY_CopyBytes2Mod },                      // PSRLD/r\n    { 0xD3, ENTRY_CopyBytes2Mod },                      // PSRLQ/r\n    { 0xD4, ENTRY_CopyBytes2Mod },                      // PADDQ\n    { 0xD5, ENTRY_CopyBytes2Mod },                      // PMULLW/r\n    { 0xD6, ENTRY_CopyBytes2Mod },                      // MOVDQ2Q / MOVQ2DQ\n    { 0xD7, ENTRY_CopyBytes2Mod },                      // PMOVMSKB/r\n    { 0xD8, ENTRY_CopyBytes2Mod },                      // PSUBUSB/r\n    { 0xD9, ENTRY_CopyBytes2Mod },                      // PSUBUSW/r\n    { 0xDA, ENTRY_CopyBytes2Mod },                      // PMINUB/r\n    { 0xDB, ENTRY_CopyBytes2Mod },                      // PAND/r\n    { 0xDC, ENTRY_CopyBytes2Mod },                      // PADDUSB/r\n    { 0xDD, ENTRY_CopyBytes2Mod },                      // PADDUSW/r\n    { 0xDE, ENTRY_CopyBytes2Mod },                      // PMAXUB/r\n    { 0xDF, ENTRY_CopyBytes2Mod },                      // PANDN/r\n    { 0xE0, ENTRY_CopyBytes2Mod  },                     // PAVGB\n    { 0xE1, ENTRY_CopyBytes2Mod },                      // PSRAW/r\n    { 0xE2, ENTRY_CopyBytes2Mod },                      // PSRAD/r\n    { 0xE3, ENTRY_CopyBytes2Mod },                      // PAVGW\n    { 0xE4, ENTRY_CopyBytes2Mod },                      // PMULHUW/r\n    { 0xE5, ENTRY_CopyBytes2Mod },                      // PMULHW/r\n    { 0xE6, ENTRY_CopyBytes2Mod },                      // CTDQ2PD &\n    { 0xE7, ENTRY_CopyBytes2Mod },                      // MOVNTQ\n    { 0xE8, ENTRY_CopyBytes2Mod },                      // PSUBB/r\n    { 0xE9, ENTRY_CopyBytes2Mod },                      // PSUBW/r\n    { 0xEA, ENTRY_CopyBytes2Mod },                      // PMINSW/r\n    { 0xEB, ENTRY_CopyBytes2Mod },                      // POR/r\n    { 0xEC, ENTRY_CopyBytes2Mod },                      // PADDSB/r\n    { 0xED, ENTRY_CopyBytes2Mod },                      // PADDSW/r\n    { 0xEE, ENTRY_CopyBytes2Mod },                      // PMAXSW /r\n    { 0xEF, ENTRY_CopyBytes2Mod },                      // PXOR/r\n    { 0xF0, ENTRY_CopyBytes2Mod },                      // LDDQU\n    { 0xF1, ENTRY_CopyBytes2Mod },                      // PSLLW/r\n    { 0xF2, ENTRY_CopyBytes2Mod },                      // PSLLD/r\n    { 0xF3, ENTRY_CopyBytes2Mod },                      // PSLLQ/r\n    { 0xF4, ENTRY_CopyBytes2Mod },                      // PMULUDQ/r\n    { 0xF5, ENTRY_CopyBytes2Mod },                      // PMADDWD/r\n    { 0xF6, ENTRY_CopyBytes2Mod },                      // PSADBW/r\n    { 0xF7, ENTRY_CopyBytes2Mod },                      // MASKMOVQ\n    { 0xF8, ENTRY_CopyBytes2Mod },                      // PSUBB/r\n    { 0xF9, ENTRY_CopyBytes2Mod },                      // PSUBW/r\n    { 0xFA, ENTRY_CopyBytes2Mod },                      // PSUBD/r\n    { 0xFB, ENTRY_CopyBytes2Mod },                      // FSUBQ/r\n    { 0xFC, ENTRY_CopyBytes2Mod },                      // PADDB/r\n    { 0xFD, ENTRY_CopyBytes2Mod },                      // PADDW/r\n    { 0xFE, ENTRY_CopyBytes2Mod },                      // PADDD/r\n    { 0xFF, ENTRY_Invalid },                            // _FF\n    { 0, ENTRY_End },\n};\n\nBOOL CDetourDis::SanityCheckSystem()\n{\n    ULONG n = 0;\n    for (; n < 256; n++) {\n        REFCOPYENTRY pEntry = &s_rceCopyTable[n];\n\n        if (n != pEntry->nOpcode) {\n            ASSERT(n == pEntry->nOpcode);\n            return FALSE;\n        }\n    }\n    if (s_rceCopyTable[256].pfCopy != NULL) {\n        ASSERT(!\"Missing end marker.\");\n        return FALSE;\n    }\n\n    for (n = 0; n < 256; n++) {\n        REFCOPYENTRY pEntry = &s_rceCopyTable0F[n];\n\n        if (n != pEntry->nOpcode) {\n            ASSERT(n == pEntry->nOpcode);\n            return FALSE;\n        }\n    }\n    if (s_rceCopyTable0F[256].pfCopy != NULL) {\n        ASSERT(!\"Missing end marker.\");\n        return FALSE;\n    }\n\n    return TRUE;\n}\n#endif // defined(DETOURS_X64) || defined(DETOURS_X86)\n\n/////////////////////////////////////////////////////////// IA64 Disassembler.\n//\n#ifdef DETOURS_IA64\n\n#if defined(_IA64_) != defined(DETOURS_IA64_OFFLINE_LIBRARY)\n// Compile DETOUR_IA64_BUNDLE for native IA64 or cross, but not both -- we get duplicates otherwise.\nconst DETOUR_IA64_BUNDLE::DETOUR_IA64_METADATA DETOUR_IA64_BUNDLE::s_rceCopyTable[33] =\n{\n    { 0x00, M_UNIT,      I_UNIT,      I_UNIT,   },\n    { 0x01, M_UNIT,      I_UNIT,      I_UNIT,   },\n    { 0x02, M_UNIT,      I_UNIT,      I_UNIT,   },\n    { 0x03, M_UNIT,      I_UNIT,      I_UNIT,   },\n    { 0x04, M_UNIT,      L_UNIT,      X_UNIT,   },\n    { 0x05, M_UNIT,      L_UNIT,      X_UNIT,   },\n    { 0x06, 0,           0,           0,        },\n    { 0x07, 0,           0,           0,        },\n    { 0x08, M_UNIT,      M_UNIT,      I_UNIT,   },\n    { 0x09, M_UNIT,      M_UNIT,      I_UNIT,   },\n    { 0x0a, M_UNIT,      M_UNIT,      I_UNIT,   },\n    { 0x0b, M_UNIT,      M_UNIT,      I_UNIT,   },\n    { 0x0c, M_UNIT,      F_UNIT,      I_UNIT,   },\n    { 0x0d, M_UNIT,      F_UNIT,      I_UNIT,   },\n    { 0x0e, M_UNIT,      M_UNIT,      F_UNIT,   },\n    { 0x0f, M_UNIT,      M_UNIT,      F_UNIT,   },\n    { 0x10, M_UNIT,      I_UNIT,      B_UNIT,   },\n    { 0x11, M_UNIT,      I_UNIT,      B_UNIT,   },\n    { 0x12, M_UNIT,      B_UNIT,      B_UNIT,   },\n    { 0x13, M_UNIT,      B_UNIT,      B_UNIT,   },\n    { 0x14, 0,           0,           0,        },\n    { 0x15, 0,           0,           0,        },\n    { 0x16, B_UNIT,      B_UNIT,      B_UNIT,   },\n    { 0x17, B_UNIT,      B_UNIT,      B_UNIT,   },\n    { 0x18, M_UNIT,      M_UNIT,      B_UNIT,   },\n    { 0x19, M_UNIT,      M_UNIT,      B_UNIT,   },\n    { 0x1a, 0,           0,           0,        },\n    { 0x1b, 0,           0,           0,        },\n    { 0x1c, M_UNIT,      F_UNIT,      B_UNIT,   },\n    { 0x1d, M_UNIT,      F_UNIT,      B_UNIT,   },\n    { 0x1e, 0,           0,           0,        },\n    { 0x1f, 0,           0,           0,        },\n    { 0x00, 0,           0,           0,        },\n};\n\n// 120 112 104 96 88 80 72 64 56 48 40 32 24 16  8  0\n//  f.  e.  d. c. b. a. 9. 8. 7. 6. 5. 4. 3. 2. 1. 0.\n\n//                                      00\n// f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.\n// 0000 0000 0000 0000 0000 0000 0000 001f : Template [4..0]\n// 0000 0000 0000 0000 0000 03ff ffff ffe0 : Zero [ 41..  5]\n// 0000 0000 0000 0000 0000 3c00 0000 0000 : Zero [ 45.. 42]\n// 0000 0000 0007 ffff ffff c000 0000 0000 : One  [ 82.. 46]\n// 0000 0000 0078 0000 0000 0000 0000 0000 : One  [ 86.. 83]\n// 0fff ffff ff80 0000 0000 0000 0000 0000 : Two  [123.. 87]\n// f000 0000 0000 0000 0000 0000 0000 0000 : Two  [127..124]\nBYTE DETOUR_IA64_BUNDLE::GetTemplate() const\n{\n    return (data[0] & 0x1f);\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetInst0() const\n{\n    return ((data[5] & 0x3c) >> 2);\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetInst1() const\n{\n    return ((data[10] & 0x78) >> 3);\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetInst2() const\n{\n    return ((data[15] & 0xf0) >> 4);\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetUnit(BYTE slot) const\n{\n    switch (slot) {\n    case 0: return GetUnit0();\n    case 1: return GetUnit1();\n    case 2: return GetUnit2();\n    }\n    __debugbreak();\n    return 0;\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetUnit0() const\n{\n    return s_rceCopyTable[data[0] & 0x1f].nUnit0;\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetUnit1() const\n{\n    return s_rceCopyTable[data[0] & 0x1f].nUnit1;\n}\n\nBYTE DETOUR_IA64_BUNDLE::GetUnit2() const\n{\n    return s_rceCopyTable[data[0] & 0x1f].nUnit2;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetData0() const\n{\n    return (((wide[0] & 0x000003ffffffffe0) >> 5));\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetData1() const\n{\n    return (((wide[0] & 0xffffc00000000000) >> 46) |\n            ((wide[1] & 0x000000000007ffff) << 18));\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetData2() const\n{\n    return (((wide[1] & 0x0fffffffff800000) >> 23));\n}\n\nVOID DETOUR_IA64_BUNDLE::SetInst(BYTE slot, BYTE nInst)\n{\n    switch (slot)\n    {\n    case 0: SetInst0(nInst); return;\n    case 1: SetInst1(nInst); return;\n    case 2: SetInst2(nInst); return;\n    }\n    __debugbreak();\n}\n\nVOID DETOUR_IA64_BUNDLE::SetInst0(BYTE nInst)\n{\n    data[5] = (data[5] & ~0x3c) | ((nInst << 2) & 0x3c);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetInst1(BYTE nInst)\n{\n    data[10] = (data[10] & ~0x78) | ((nInst << 3) & 0x78);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetInst2(BYTE nInst)\n{\n    data[15] = (data[15] & ~0xf0) | ((nInst << 4) & 0xf0);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetData(BYTE slot, UINT64 nData)\n{\n    switch (slot)\n    {\n    case 0: SetData0(nData); return;\n    case 1: SetData1(nData); return;\n    case 2: SetData2(nData); return;\n    }\n    __debugbreak();\n}\n\nVOID DETOUR_IA64_BUNDLE::SetData0(UINT64 nData)\n{\n    wide[0] = (wide[0] & ~0x000003ffffffffe0) | (( nData << 5)  & 0x000003ffffffffe0);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetData1(UINT64 nData)\n{\n    wide[0] = (wide[0] & ~0xffffc00000000000) | ((nData << 46) & 0xffffc00000000000);\n    wide[1] = (wide[1] & ~0x000000000007ffff) | ((nData >> 18) & 0x000000000007ffff);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetData2(UINT64 nData)\n{\n    wide[1] = (wide[1] & ~0x0fffffffff800000) | ((nData << 23) & 0x0fffffffff800000);\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetInstruction(BYTE slot) const\n{\n    switch (slot) {\n    case 0: return GetInstruction0();\n    case 1: return GetInstruction1();\n    case 2: return GetInstruction2();\n    }\n    __debugbreak();\n    return 0;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetInstruction0() const\n{\n    // 41 bits from wide[0], skipping the 5 bit template.\n    return GetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE);\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetInstruction1() const\n{\n    // 64-46 bits from wide[0] and the rest from wide[1].\n    const UINT count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET;\n    const UINT count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0;\n    return GetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0) | (GetBits(wide[1], 0, count1) << count0);\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetInstruction2() const\n{\n    // Upper 41 bits of wide[1].\n    return wide[1] >> (64 - DETOUR_IA64_INSTRUCTION_SIZE);\n}\n\nvoid DETOUR_IA64_BUNDLE::SetInstruction(BYTE slot, UINT64 instruction)\n{\n    switch (slot) {\n    case 0: SetInstruction0(instruction); return;\n    case 1: SetInstruction1(instruction); return;\n    case 2: SetInstruction2(instruction); return;\n    }\n    __debugbreak();\n}\n\nvoid DETOUR_IA64_BUNDLE::SetInstruction0(UINT64 instruction)\n{\n    wide[0] = SetBits(wide[0], DETOUR_IA64_INSTRUCTION0_OFFSET, DETOUR_IA64_INSTRUCTION_SIZE, instruction);\n}\n\nvoid DETOUR_IA64_BUNDLE::SetInstruction1(UINT64 instruction)\n{\n    UINT const count0 = 64 - DETOUR_IA64_INSTRUCTION1_OFFSET;\n    UINT const count1 = DETOUR_IA64_INSTRUCTION_SIZE - count0;\n    UINT64 const wide0 = SetBits(wide[0], DETOUR_IA64_INSTRUCTION1_OFFSET, count0, instruction);\n    UINT64 const wide1 = SetBits(wide[1], 0, count1, instruction >> count0);\n    wide[0] = wide0;\n    wide[1] = wide1;\n}\n\nvoid DETOUR_IA64_BUNDLE::SetInstruction2(UINT64 instruction)\n{\n    // Set upper 41 bits of wide[1].\n    wide[1] = SetBits(wide[1], 64 - DETOUR_IA64_INSTRUCTION_SIZE, DETOUR_IA64_INSTRUCTION_SIZE, instruction);\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SignExtend(UINT64 Value, UINT64 Offset)\n// This definition is from the IA64 manual.\n{\n    if ((Value & (((UINT64)1) << (Offset - 1))) == 0)\n        return Value;\n    UINT64 const new_value = Value | ((~(UINT64)0) << Offset);\n    return new_value;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetBits(UINT64 Value, UINT64 Offset, UINT64 Count)\n{\n    UINT64 const new_value = (Value >> Offset) & ~(~((UINT64)0) << Count);\n    return new_value;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetBits(UINT64 Value, UINT64 Offset, UINT64 Count, UINT64 Field)\n{\n    UINT64 const mask = (~((~(UINT64)0) << Count)) << Offset;\n    UINT64 const new_value = (Value & ~mask) | ((Field << Offset) & mask);\n    return new_value;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetOpcode(UINT64 instruction)\n// Get 4bit primary opcode.\n{\n    UINT64 const opcode = GetBits(instruction, DETOUR_IA64_INSTRUCTION_SIZE - 4, 4);\n    return opcode;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetX(UINT64 instruction)\n// Get 1bit opcode extension.\n{\n    UINT64 const x = GetBits(instruction, 33, 1);\n    return x;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetX3(UINT64 instruction)\n// Get 3bit opcode extension.\n{\n    UINT64 const x3 = GetBits(instruction, 33, 3);\n    return x3;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetX6(UINT64 instruction)\n// Get 6bit opcode extension.\n{\n    UINT64 const x6 = GetBits(instruction, 27, 6);\n    return x6;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetImm7a(UINT64 instruction)\n{\n    UINT64 const imm7a = GetBits(instruction, 6, 7);\n    return imm7a;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetImm7a(UINT64 instruction, UINT64 imm7a)\n{\n    UINT64 const new_instruction = SetBits(instruction, 6, 7, imm7a);\n    return new_instruction;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetImm13c(UINT64 instruction)\n{\n    UINT64 const imm13c = GetBits(instruction, 20, 13);\n    return imm13c;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetImm13c(UINT64 instruction, UINT64 imm13c)\n{\n    UINT64 const new_instruction = SetBits(instruction, 20, 13, imm13c);\n    return new_instruction;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetSignBit(UINT64 instruction)\n{\n    UINT64 const signBit = GetBits(instruction, 36, 1);\n    return signBit;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetSignBit(UINT64 instruction, UINT64 signBit)\n{\n    UINT64 const new_instruction = SetBits(instruction, 36, 1, signBit);\n    return new_instruction;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetImm20a(UINT64 instruction)\n{\n    UINT64 const imm20a = GetBits(instruction, 6, 20);\n    return imm20a;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetImm20a(UINT64 instruction, UINT64 imm20a)\n{\n    UINT64 const new_instruction = SetBits(instruction, 6, 20, imm20a);\n    return new_instruction;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetImm20b(UINT64 instruction)\n{\n    UINT64 const imm20b = GetBits(instruction, 13, 20);\n    return imm20b;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::SetImm20b(UINT64 instruction, UINT64 imm20b)\n{\n    UINT64 const new_instruction = SetBits(instruction, 13, 20, imm20b);\n    return new_instruction;\n}\n\nbool DETOUR_IA64_BUNDLE::RelocateInstruction(_Inout_ DETOUR_IA64_BUNDLE* pDst,\n                                             _In_ BYTE slot,\n                                             _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const\n/*\n    If pBundleExtra is provided and instruction is IP-relative,\n    this function relocates instruction to target pBundleExtra,\n    pBundleExtra is set to brl the original target, and return true.\n\n    [Not used] If pBundleExtra is not provided and instruction is IP-relative, return true.\n\n    Else return false.\n\n    The following IP-relative forms are recognized:\n        br and br.call\n        chk.s.m integer and float\n        chk.a.nc integer and float\n        chk.a.clr integer and float\n        chk.s.i\n        fchkf\n\n    Brl is handled elsewhere, because the code was previously written.\n\n    Branch prediction hints are not relocated.\n*/\n{\n    UINT64 const instruction = GetInstruction(slot);\n    UINT64 const opcode = GetOpcode(instruction);\n    size_t const dest = (size_t)pDst;\n    size_t const extra = (size_t)pBundleExtra;\n\n    switch (GetUnit(slot)) {\n    case F_UNIT:\n        // F14 fchkf\n        if (opcode == 0 && GetX(instruction) == 0 && GetX6(instruction) == 8) {\n            goto imm20a;\n        }\n        return false;\n\n    case M_UNIT:\n        // M20 x3 == 1 integer chk.s.m\n        // M21 x3 == 3 floating point chk.s\n        if (opcode == 1) {\n            UINT64 const x3 = GetX3(instruction);\n            if (x3 == 1 || x3 == 3) {\n                goto imm13_7;\n            }\n        }\n\n        // M22 x3 == 4 integer chk.a.nc\n        // M22 x3 == 5 integer chk.a.clr\n        // M23 x3 == 6 floating point chk.a.nc\n        // M23 x3 == 7 floating point chk.a.clr\n        if (opcode == 0) {\n            UINT64 const x3 = GetX3(instruction);\n            if (x3 == 4 || x3 == 5 || x3 == 6 || x3 == 7) {\n                goto imm20b;\n            }\n        }\n        return false;\n    case I_UNIT:\n        // I20\n        if (opcode == 0 && GetX3(instruction) == 1) { // chk.s.i\n            goto imm13_7;\n        }\n        return false;\n    case B_UNIT:\n        // B1 B2 B3\n        // 4 br\n        // 5 br.call\n        if (opcode == 4 || opcode == 5) {\n            goto imm20b;\n        }\n        return false;\n    }\n    return false;\n\n    UINT64 imm;\n    UINT64 new_instruction;\n\nimm13_7:\n    imm = SignExtend((GetSignBit(instruction) << 20) | (GetImm13c(instruction) << 7) | GetImm7a(instruction), 21) << 4;\n    new_instruction = SetSignBit(SetImm13c(SetImm7a(instruction, (extra - dest) >> 4), (extra - dest) >> 11), extra < dest);\n    goto set_brl;\n\nimm20a:\n    imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20a(instruction), 21) << 4;\n    new_instruction = SetSignBit(SetImm20a(instruction, (extra - dest) >> 4), extra < dest);\n    goto set_brl;\n\nimm20b:\n    imm = SignExtend((GetSignBit(instruction) << 20) | GetImm20b(instruction), 21) << 4;\n    new_instruction = SetSignBit(SetImm20b(instruction, (extra - dest) >> 4), extra < dest);\n    goto set_brl;\n\nset_brl:\n    if (pBundleExtra != NULL) {\n        pDst->SetInstruction(slot, new_instruction);\n        pBundleExtra->SetBrl((size_t)this + imm);\n    }\n    return true;\n}\n\nUINT DETOUR_IA64_BUNDLE::RelocateBundle(_Inout_ DETOUR_IA64_BUNDLE* pDst,\n                                        _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const\n/*\n    Having already copied the bundle unchanged, then relocate its instructions one at a time.\n    Return how many extra bytes are required to relocate the bundle.\n*/\n{\n    UINT nExtraBytes = 0;\n    for (BYTE slot = 0; slot < DETOUR_IA64_INSTRUCTIONS_PER_BUNDLE; ++slot) {\n        if (!RelocateInstruction(pDst, slot, pBundleExtra)) {\n            continue;\n        }\n        pBundleExtra -= !!pBundleExtra;\n        nExtraBytes += sizeof(DETOUR_IA64_BUNDLE);\n    }\n    return nExtraBytes;\n}\n\nBOOL DETOUR_IA64_BUNDLE::IsBrl() const\n{\n    // f.e. d.c. b.a. 9.8. 7.6. 5. 4. 3. 2. 1. 0.\n    // c000 0070 0000 0000 0000 00 01 00 00 00 05 : brl.sptk.few\n    // c8ff fff0 007f fff0 ffff 00 01 00 00 00 05 : brl.sptk.few\n    // c000 0048 0000 0000 0001 00 00 00 00 00 05 : brl.sptk.many\n    return ((wide[0] & 0x000000000000001e) == 0x0000000000000004 && // 4 or 5.\n            (wide[1] & 0xe000000000000000) == 0xc000000000000000);  // c or d.\n}\n\nVOID DETOUR_IA64_BUNDLE::SetBrl()\n{\n    wide[0] = 0x0000000100000005;   // few\n    //wide[0] = 0x0000000180000005; // many\n    wide[1] = 0xc000000800000000;\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetBrlImm() const\n{\n    return (\n            //          0x0000000000fffff0\n            ((wide[1] & 0x00fffff000000000) >> 32) |    // all 20 bits of imm20b.\n            //          0x000000ffff000000\n            ((wide[0] & 0xffff000000000000) >> 24) |    // bottom 16 bits of imm39.\n            //          0x7fffff0000000000\n            ((wide[1] & 0x00000000007fffff) << 40) |    // top 23 bits of imm39.\n            //          0x8000000000000000\n            ((wide[1] & 0x0800000000000000) <<  4)      // single bit of i.\n           );\n}\n\nVOID DETOUR_IA64_BUNDLE::SetBrlImm(UINT64 imm)\n{\n    wide[0] = ((wide[0] & ~0xffff000000000000) |\n               //      0xffff000000000000\n               ((imm & 0x000000ffff000000) << 24)       // bottom 16 bits of imm39.\n              );\n    wide[1] = ((wide[1] & ~0x08fffff0007fffff) |\n               //      0x00fffff000000000\n               ((imm & 0x0000000000fffff0) << 32) |     // all 20 bits of imm20b.\n               //      0x00000000007fffff\n               ((imm & 0x7fffff0000000000) >> 40) |     // top 23 bits of imm39.\n               //      0x0800000000000000\n               ((imm & 0x8000000000000000) >>  4)       // single bit of i.\n              );\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetBrlTarget() const\n{\n    return (UINT64)this + GetBrlImm();\n}\n\nVOID DETOUR_IA64_BUNDLE::SetBrl(UINT64 target)\n{\n    UINT64 imm = target - (UINT64)this;\n    SetBrl();\n    SetBrlImm(imm);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetBrlTarget(UINT64 target)\n{\n    UINT64 imm = target - (UINT64)this;\n    SetBrlImm(imm);\n}\n\nBOOL DETOUR_IA64_BUNDLE::IsMovlGp() const\n{\n    // f.e. d.c. b.a. 9.8. 7.6. 5.4. 3.2. 1.0.\n    // 6fff f7f0 207f ffff ffff c001 0000 0004\n    // 6000 0000 2000 0000 0000 0001 0000 0004\n    return ((wide[0] & 0x00003ffffffffffe) == 0x0000000100000004 &&\n            (wide[1] & 0xf000080fff800000) == 0x6000000020000000);\n}\n\nUINT64 DETOUR_IA64_BUNDLE::GetMovlGp() const\n{\n    UINT64 raw = (\n                  //          0x0000000000000070\n                  ((wide[1] & 0x000007f000000000) >> 36) |\n                  //          0x000000000000ff80\n                  ((wide[1] & 0x07fc000000000000) >> 43) |\n                  //          0x00000000001f0000\n                  ((wide[1] & 0x0003e00000000000) >> 29) |\n                  //          0x0000000000200000\n                  ((wide[1] & 0x0000100000000000) >> 23) |\n                  //          0x000000ffffc00000\n                  ((wide[0] & 0xffffc00000000000) >> 24) |\n                  //          0x7fffff0000000000\n                  ((wide[1] & 0x00000000007fffff) << 40) |\n                  //          0x8000000000000000\n                  ((wide[1] & 0x0800000000000000) <<  4)\n                 );\n\n    return (INT64)raw;\n}\n\nVOID DETOUR_IA64_BUNDLE::SetMovlGp(UINT64 gp)\n{\n    UINT64 raw = (UINT64)gp;\n\n    wide[0] = (0x0000000100000005 |\n               //      0xffffc00000000000\n               ((raw & 0x000000ffffc00000) << 24)\n              );\n    wide[1] = (\n               0x6000000020000000 |\n               //      0x0000070000000000\n               ((raw & 0x0000000000000070) << 36) |\n               //      0x07fc000000000000\n               ((raw & 0x000000000000ff80) << 43) |\n               //      0x0003e00000000000\n               ((raw & 0x00000000001f0000) << 29) |\n               //      0x0000100000000000\n               ((raw & 0x0000000000200000) << 23) |\n               //      0x00000000007fffff\n               ((raw & 0x7fffff0000000000) >> 40) |\n               //      0x0800000000000000\n               ((raw & 0x8000000000000000) >>  4)\n              );\n}\n\nUINT DETOUR_IA64_BUNDLE::Copy(_Out_ DETOUR_IA64_BUNDLE *pDst,\n                              _Inout_opt_ DETOUR_IA64_BUNDLE* pBundleExtra) const\n{\n    // Copy the bytes unchanged.\n\n#pragma warning(suppress:6001) // using uninitialized *pDst\n    pDst->wide[0] = wide[0];\n    pDst->wide[1] = wide[1];\n\n    // Relocate if necessary.\n\n    UINT nExtraBytes = RelocateBundle(pDst, pBundleExtra);\n\n    if (GetUnit1() == L_UNIT && IsBrl()) {\n        pDst->SetBrlTarget(GetBrlTarget());\n    }\n\n    return nExtraBytes;\n}\n\nBOOL DETOUR_IA64_BUNDLE::SetNop(BYTE slot)\n{\n    switch (GetUnit(slot)) {\n      case I_UNIT:\n      case M_UNIT:\n      case F_UNIT:\n        SetInst(slot, 0);\n        SetData(slot, 0x8000000);\n        return true;\n      case B_UNIT:\n        SetInst(slot, 2);\n        SetData(slot, 0);\n        return true;\n    }\n    __debugbreak();\n    return false;\n}\n\nBOOL DETOUR_IA64_BUNDLE::SetNop0()\n{\n    return SetNop(0);\n}\n\nBOOL DETOUR_IA64_BUNDLE::SetNop1()\n{\n    return SetNop(1);\n}\n\nBOOL DETOUR_IA64_BUNDLE::SetNop2()\n{\n    return SetNop(2);\n}\n\nVOID DETOUR_IA64_BUNDLE::SetStop()\n{\n    data[0] |= 0x01;\n}\n\n#endif // DETOURS_IA64\n\nPVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,\n                                   _Inout_opt_ PVOID *ppDstPool,\n                                   _In_ PVOID pSrc,\n                                   _Out_opt_ PVOID *ppTarget,\n                                   _Out_opt_ LONG *plExtra)\n{\n    LONG nExtra;\n    DETOUR_IA64_BUNDLE bExtra;\n    DETOUR_IA64_BUNDLE *pbSrc = (DETOUR_IA64_BUNDLE *)pSrc;\n    DETOUR_IA64_BUNDLE *pbDst = pDst ? (DETOUR_IA64_BUNDLE *)pDst : &bExtra;\n\n    plExtra = plExtra ? plExtra : &nExtra;\n    *plExtra = 0;\n\n    if (ppTarget != NULL) {\n        if (pbSrc->IsBrl()) {\n            *ppTarget = (PVOID)pbSrc->GetBrlTarget();\n        }\n        else {\n            *ppTarget = DETOUR_INSTRUCTION_TARGET_NONE;\n        }\n    }\n    *plExtra = (LONG)pbSrc->Copy(pbDst, ppDstPool ? ((DETOUR_IA64_BUNDLE*)*ppDstPool) - 1 : (DETOUR_IA64_BUNDLE*)NULL);\n    return pbSrc + 1;\n}\n\n#endif // DETOURS_IA64\n\n#ifdef DETOURS_ARM\n\n#define DETOURS_PFUNC_TO_PBYTE(p)  ((PBYTE)(((ULONG_PTR)(p)) & ~(ULONG_PTR)1))\n#define DETOURS_PBYTE_TO_PFUNC(p)  ((PBYTE)(((ULONG_PTR)(p)) | (ULONG_PTR)1))\n\n#define c_PCAdjust  4       // The PC value of an instruction is the PC address plus 4.\n#define c_PC        15      // The register number for the Program Counter\n#define c_LR        14      // The register number for the Link Register\n#define c_SP        13      // The register number for the Stack Pointer\n#define c_NOP       0xbf00  // A nop instruction\n#define c_BREAK     0xdefe  // A nop instruction\n\nclass CDetourDis\n{\n  public:\n    CDetourDis();\n\n    PBYTE   CopyInstruction(PBYTE pDst,\n                            PBYTE *ppDstPool,\n                            PBYTE pSrc,\n                            PBYTE *ppTarget,\n                            LONG *plExtra);\n\n  public:\n    typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc);\n\n    struct COPYENTRY {\n        USHORT      nOpcode;\n        COPYFUNC    pfCopy;\n    };\n\n    typedef const COPYENTRY * REFCOPYENTRY;\n\n    struct Branch5\n    {\n        DWORD Register : 3;\n        DWORD Imm5 : 5;\n        DWORD Padding : 1;\n        DWORD I : 1;\n        DWORD OpCode : 6;\n    };\n\n    struct Branch5Target\n    {\n        DWORD Padding : 1;\n        DWORD Imm5 : 5;\n        DWORD I : 1;\n        DWORD Padding2 : 25;\n    };\n\n    struct Branch8\n    {\n        DWORD Imm8 : 8;\n        DWORD Condition : 4;\n        DWORD OpCode : 4;\n    };\n\n    struct Branch8Target\n    {\n        DWORD Padding : 1;\n        DWORD Imm8 : 8;\n        DWORD Padding2 : 23;\n    };\n\n    struct Branch11\n    {\n        DWORD Imm11 : 11;\n        DWORD OpCode : 5;\n    };\n\n    struct Branch11Target\n    {\n        DWORD Padding : 1;\n        DWORD Imm11 : 11;\n        DWORD Padding2 : 20;\n    };\n\n    struct Branch20\n    {\n        DWORD Imm11 : 11;\n        DWORD J2 : 1;\n        DWORD IT : 1;\n        DWORD J1 : 1;\n        DWORD Other : 2;\n        DWORD Imm6 : 6;\n        DWORD Condition : 4;\n        DWORD Sign : 1;\n        DWORD OpCode : 5;\n    };\n\n    struct Branch20Target\n    {\n        DWORD Padding : 1;\n        DWORD Imm11 : 11;\n        DWORD Imm6 : 6;\n        DWORD J1 : 1;\n        DWORD J2 : 1;\n        DWORD Sign : 1;\n        INT32 Padding2 : 11;\n    };\n\n    struct Branch24\n    {\n        DWORD Imm11             : 11;\n        DWORD J2                : 1;\n        DWORD InstructionSet    : 1;\n        DWORD J1                : 1;\n        DWORD Link              : 1;\n        DWORD Branch            : 1;\n        DWORD Imm10             : 10;\n        DWORD Sign              : 1;\n        DWORD OpCode            : 5;\n    };\n\n    struct Branch24Target\n    {\n        DWORD Padding : 1;\n        DWORD Imm11 : 11;\n        DWORD Imm10 : 10;\n        DWORD I2 : 1;\n        DWORD I1 : 1;\n        DWORD Sign : 1;\n        INT32 Padding2 : 7;\n    };\n\n    struct LiteralLoad8\n    {\n        DWORD Imm8 : 8;\n        DWORD Register : 3;\n        DWORD OpCode : 5;\n    };\n\n    struct LiteralLoad8Target\n    {\n        DWORD Padding : 2;\n        DWORD Imm8 : 8;\n        DWORD Padding2 : 22;\n    };\n\n    struct LiteralLoad12\n    {\n        DWORD Imm12 : 12;\n        DWORD Register : 4;\n        DWORD OpCodeSuffix : 7;\n        DWORD Add : 1;\n        DWORD OpCodePrefix : 8;\n    };\n\n    struct LiteralLoad12Target\n    {\n        DWORD Imm12 : 12;\n        DWORD Padding : 20;\n    };\n\n    struct ImmediateRegisterLoad32\n    {\n        DWORD Imm12 : 12;\n        DWORD DestinationRegister : 4;\n        DWORD SourceRegister: 4;\n        DWORD OpCode : 12;\n    };\n\n    struct ImmediateRegisterLoad16\n    {\n        DWORD DestinationRegister : 3;\n        DWORD SourceRegister: 3;\n        DWORD OpCode : 10;\n    };\n\n    struct TableBranch\n    {\n        DWORD IndexRegister : 4;\n        DWORD HalfWord : 1;\n        DWORD OpCodeSuffix : 11;\n        DWORD BaseRegister : 4;\n        DWORD OpCodePrefix : 12;\n    };\n\n    struct Shift\n    {\n        DWORD Imm2 : 2;\n        DWORD Imm3 : 3;\n    };\n\n    struct Add32\n    {\n        DWORD SecondOperandRegister : 4;\n        DWORD Type : 2;\n        DWORD Imm2 : 2;\n        DWORD DestinationRegister : 4;\n        DWORD Imm3 : 3;\n        DWORD Padding : 1;\n        DWORD FirstOperandRegister : 4;\n        DWORD SetFlags : 1;\n        DWORD OpCode : 11;\n    };\n\n    struct LogicalShiftLeft32\n    {\n        DWORD SourceRegister : 4;\n        DWORD Padding : 2;\n        DWORD Imm2 : 2;\n        DWORD DestinationRegister : 4;\n        DWORD Imm3 : 3;\n        DWORD Padding2 : 5;\n        DWORD SetFlags : 1;\n        DWORD OpCode : 11;\n    };\n\n    struct StoreImmediate12\n    {\n        DWORD Imm12 : 12;\n        DWORD SourceRegister : 4;\n        DWORD BaseRegister : 4;\n        DWORD OpCode : 12;\n    };\n\n  protected:\n    BYTE    PureCopy16(BYTE* pSource, BYTE* pDest);\n    BYTE    PureCopy32(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyMiscellaneous16(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyLiteralLoad16(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyBranch24(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyLiteralLoad32(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest);\n    BYTE    CopyTableBranch(BYTE* pSource, BYTE* pDest);\n    BYTE    BeginCopy32(BYTE* pSource, BYTE* pDest);\n\n    LONG    DecodeBranch5(ULONG opcode);\n    USHORT  EncodeBranch5(ULONG originalOpCode, LONG delta);\n    LONG    DecodeBranch8(ULONG opcode);\n    USHORT  EncodeBranch8(ULONG originalOpCode, LONG delta);\n    LONG    DecodeBranch11(ULONG opcode);\n    USHORT  EncodeBranch11(ULONG originalOpCode, LONG delta);\n    BYTE    EmitBranch11(PUSHORT& pDest, LONG relativeAddress);\n    LONG    DecodeBranch20(ULONG opcode);\n    ULONG   EncodeBranch20(ULONG originalOpCode, LONG delta);\n    LONG    DecodeBranch24(ULONG opcode, BOOL& fLink);\n    ULONG   EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink);\n    LONG    DecodeLiteralLoad8(ULONG instruction);\n    LONG    DecodeLiteralLoad12(ULONG instruction);\n    BYTE    EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral);\n    BYTE    EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral);\n    BYTE    EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg);\n    BYTE    EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg);\n    BYTE    EmitLongLiteralLoad(PUSHORT& pDest, BYTE reg, PVOID pTarget);\n    BYTE    EmitLongBranch(PUSHORT& pDest, PVOID pTarget);\n    USHORT  CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd);\n\n  protected:\n    ULONG GetLongInstruction(BYTE* pSource)\n    {\n        return (((PUSHORT)pSource)[0] << 16) | (((PUSHORT)pSource)[1]);\n    }\n\n    BYTE EmitLongInstruction(PUSHORT& pDstInst, ULONG instruction)\n    {\n        *pDstInst++ = (USHORT)(instruction >> 16);\n        *pDstInst++ = (USHORT)instruction;\n        return sizeof(ULONG);\n    }\n\n    BYTE EmitShortInstruction(PUSHORT& pDstInst, USHORT instruction)\n    {\n        *pDstInst++ = instruction;\n        return sizeof(USHORT);\n    }\n\n    PBYTE Align4(PBYTE pValue)\n    {\n        return (PBYTE)(((size_t)pValue) & ~(ULONG)3u);\n    }\n\n    PBYTE CalculateTarget(PBYTE pSource, LONG delta)\n    {\n        return (pSource + delta + c_PCAdjust);\n    }\n\n    LONG CalculateNewDelta(PBYTE pTarget, BYTE* pDest)\n    {\n        return (LONG)(pTarget - (pDest + c_PCAdjust));\n    }\n\n    BYTE    EmitAdd32(PUSHORT& pDstInst, BYTE op1Reg, BYTE op2Reg, BYTE dstReg, BYTE shiftAmount)\n    {\n        Shift& shift = (Shift&)(shiftAmount);\n        const BYTE shiftType = 0x00; // LSL\n        Add32 add = { op2Reg, shiftType, shift.Imm2, dstReg, shift.Imm3,\n                      0x0, op1Reg, 0x0, 0x758 };\n        return EmitLongInstruction(pDstInst, (ULONG&)add);\n    }\n\n    BYTE    EmitLogicalShiftLeft32(PUSHORT& pDstInst, BYTE srcReg, BYTE dstReg, BYTE shiftAmount)\n    {\n        Shift& shift = (Shift&)(shiftAmount);\n        LogicalShiftLeft32 shiftLeft = { srcReg, 0x00, shift.Imm2, dstReg, shift.Imm3, 0x1E,\n                                         0x00, 0x752 };\n        return EmitLongInstruction(pDstInst, (ULONG&)shiftLeft);\n    }\n\n    BYTE    EmitStoreImmediate12(PUSHORT& pDstInst, BYTE srcReg, BYTE baseReg, USHORT offset)\n    {\n        StoreImmediate12 store = { offset, srcReg, baseReg, 0xF8C };\n        return EmitLongInstruction(pDstInst, (ULONG&)store);\n    }\n\n  protected:\n    PBYTE   m_pbTarget = NULL;\n    PBYTE   m_pbPool = NULL;\n    LONG    m_lExtra = 0;\n\n    BYTE    m_rbScratchDst[64] = { 0 };\n\n    static const COPYENTRY s_rceCopyTable[33];\n};\n\nLONG CDetourDis::DecodeBranch5(ULONG opcode)\n{\n    Branch5& branch = (Branch5&)(opcode);\n\n    Branch5Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm5 = branch.Imm5;\n    target.I = branch.I;\n\n    // Return zero-extended value\n    return (LONG&)target;\n}\n\nUSHORT CDetourDis::EncodeBranch5(ULONG originalOpCode, LONG delta)\n{\n    // Too large for a 5 bit branch (5 bit branches can be up to 7 bits due to I and the trailing 0)\n    if (delta < 0 || delta > 0x7F) {\n        return 0;\n    }\n\n    Branch5& branch = (Branch5&)(originalOpCode);\n    Branch5Target& target = (Branch5Target&)(delta);\n\n    branch.Imm5 = target.Imm5;\n    branch.I = target.I;\n\n    return (USHORT&)branch;\n}\n\nLONG CDetourDis::DecodeBranch8(ULONG opcode)\n{\n    Branch8& branch = (Branch8&)(opcode);\n\n    Branch8Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm8 = branch.Imm8;\n\n    // Return sign extended value\n    return (((LONG&)target) << 23) >> 23;\n}\n\nUSHORT CDetourDis::EncodeBranch8(ULONG originalOpCode, LONG delta)\n{\n    // Too large for 8 bit branch (8 bit branches can be up to 9 bits due to the trailing 0)\n    if (delta < (-(int)0x100) || delta > 0xFF) {\n        return 0;\n    }\n\n    Branch8& branch = (Branch8&)(originalOpCode);\n    Branch8Target& target = (Branch8Target&)(delta);\n\n    branch.Imm8 = target.Imm8;\n\n    return (USHORT&)branch;\n}\n\nLONG CDetourDis::DecodeBranch11(ULONG opcode)\n{\n    Branch11& branch = (Branch11&)(opcode);\n\n    Branch11Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm11 = branch.Imm11;\n\n    // Return sign extended value\n    return (((LONG&)target) << 20) >> 20;\n}\n\nUSHORT CDetourDis::EncodeBranch11(ULONG originalOpCode, LONG delta)\n{\n    // Too large for an 11 bit branch (11 bit branches can be up to 12 bits due to the trailing 0)\n    if (delta < (-(int)0x800) || delta > 0x7FF) {\n        return 0;\n    }\n\n    Branch11& branch = (Branch11&)(originalOpCode);\n    Branch11Target& target = (Branch11Target&)(delta);\n\n    branch.Imm11 = target.Imm11;\n\n    return (USHORT&)branch;\n}\n\nBYTE CDetourDis::EmitBranch11(PUSHORT& pDest, LONG relativeAddress)\n{\n    Branch11Target& target = (Branch11Target&)(relativeAddress);\n    Branch11 branch11 = { target.Imm11, 0x1C };\n\n    *pDest++ = (USHORT&)branch11;\n    return sizeof(USHORT);\n}\n\nLONG CDetourDis::DecodeBranch20(ULONG opcode)\n{\n    Branch20& branch = (Branch20&)(opcode);\n\n    Branch20Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm11 = branch.Imm11;\n    target.Imm6 = branch.Imm6;\n    target.Sign = branch.Sign;\n    target.J1 = branch.J1;\n    target.J2 = branch.J2;\n\n    // Sign extend\n    if (target.Sign) {\n        target.Padding2 = -1;\n    }\n\n    return (LONG&)target;\n}\n\nULONG CDetourDis::EncodeBranch20(ULONG originalOpCode, LONG delta)\n{\n    // Too large for 20 bit branch (20 bit branches can be up to 21 bits due to the trailing 0)\n    if (delta < (-(int)0x100000) || delta > 0xFFFFF) {\n        return 0;\n    }\n\n    Branch20& branch = (Branch20&)(originalOpCode);\n    Branch20Target& target = (Branch20Target&)(delta);\n\n    branch.Imm11 = target.Imm11;\n    branch.Imm6 = target.Imm6;\n    branch.Sign = target.Sign;\n    branch.J1 = target.J1;\n    branch.J2 = target.J2;\n\n    return (ULONG&)branch;\n}\n\nLONG CDetourDis::DecodeBranch24(ULONG opcode, BOOL& fLink)\n{\n    Branch24& branch = (Branch24&)(opcode);\n\n    Branch24Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm11 = branch.Imm11;\n    target.Imm10 = branch.Imm10;\n    target.Sign = branch.Sign;\n    target.I1 = ~(branch.J1 ^ target.Sign);\n    target.I2 = ~(branch.J2 ^ target.Sign);\n    fLink = branch.Link;\n\n    // Sign extend\n    if (target.Sign) {\n        target.Padding2 = -1;\n    }\n\n    return (LONG&)target;\n}\n\nULONG CDetourDis::EncodeBranch24(ULONG originalOpCode, LONG delta, BOOL fLink)\n{\n    // Too large for 24 bit branch (24 bit branches can be up to 25 bits due to the trailing 0)\n    if (delta < static_cast<int>(0xFF000000) || delta > static_cast<int>(0xFFFFFF)) {\n        return 0;\n    }\n\n    Branch24& branch = (Branch24&)(originalOpCode);\n    Branch24Target& target = (Branch24Target&)(delta);\n\n    branch.Imm11 = target.Imm11;\n    branch.Imm10 = target.Imm10;\n    branch.Link = fLink;\n    branch.Sign = target.Sign;\n    branch.J1 = ~(target.I1 ^ branch.Sign);\n    branch.J2 = ~(target.I2 ^ branch.Sign);\n\n    return (ULONG&)branch;\n}\n\nLONG CDetourDis::DecodeLiteralLoad8(ULONG instruction)\n{\n    LiteralLoad8& load = (LiteralLoad8&)(instruction);\n\n    LiteralLoad8Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm8 = load.Imm8;\n\n    return (LONG&)target;\n}\n\nBYTE CDetourDis::EmitLiteralLoad8(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral)\n{\n    // Note: We add 2 (which gets rounded down) because literals must be 32-bit\n    //       aligned, but the ldr can be 16-bit aligned.\n    LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest);\n    LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0x3FF);\n\n    LiteralLoad8Target& target = (LiteralLoad8Target&)(relative);\n    LiteralLoad8 load = { target.Imm8, targetRegister, 0x9 };\n\n    return EmitShortInstruction(pDest, (USHORT&)load);\n}\n\nLONG CDetourDis::DecodeLiteralLoad12(ULONG instruction)\n{\n    LiteralLoad12& load = (LiteralLoad12&)(instruction);\n\n    LiteralLoad12Target target;\n    ZeroMemory(&target, sizeof(target));\n    target.Imm12 = load.Imm12;\n\n    return (LONG&)target;\n}\n\nBYTE CDetourDis::EmitLiteralLoad12(PUSHORT& pDest, BYTE targetRegister, PBYTE pLiteral)\n{\n    // Note: We add 2 (which gets rounded down) because literals must be 32-bit\n    //       aligned, but the ldr can be 16-bit aligned.\n    LONG newDelta = CalculateNewDelta((PBYTE)pLiteral + 2, (PBYTE)pDest);\n    LONG relative = ((newDelta > 0 ? newDelta : -newDelta) & 0xFFF);\n\n    LiteralLoad12Target& target = (LiteralLoad12Target&)(relative);\n    target.Imm12 -= target.Imm12 & 3;\n    LiteralLoad12 load = { target.Imm12, targetRegister, 0x5F, (DWORD)(newDelta > 0),  0xF8 };\n\n    return EmitLongInstruction(pDest, (ULONG&)load);\n}\n\nBYTE CDetourDis::EmitImmediateRegisterLoad32(PUSHORT& pDest, BYTE reg)\n{\n    ImmediateRegisterLoad32 load = { 0, reg, reg, 0xF8D };\n    return EmitLongInstruction(pDest, (ULONG&)load);\n}\n\nBYTE CDetourDis::EmitImmediateRegisterLoad16(PUSHORT& pDest, BYTE reg)\n{\n    ImmediateRegisterLoad16 load = { reg, reg, 0x680 >> 2 };\n    return EmitShortInstruction(pDest, (USHORT&)load);\n}\n\nBYTE CDetourDis::EmitLongLiteralLoad(PUSHORT& pDest, BYTE targetRegister, PVOID pTarget)\n{\n    *--((PULONG&)m_pbPool) = (ULONG)(size_t)pTarget;\n\n    // ldr rn, target.\n    BYTE size = EmitLiteralLoad12(pDest, targetRegister, m_pbPool);\n\n    // This only makes sense if targetRegister != PC;\n    // otherwise, we would have branched with the previous instruction anyway\n    if (targetRegister != c_PC) {\n        // ldr rn, [rn]\n        if (targetRegister <= 7) {\n            size = (BYTE)(size + EmitImmediateRegisterLoad16(pDest, targetRegister));\n        }\n        else {\n            size = (BYTE)(size + EmitImmediateRegisterLoad32(pDest, targetRegister));\n        }\n    }\n\n    return size;\n}\n\nBYTE CDetourDis::EmitLongBranch(PUSHORT& pDest, PVOID pTarget)\n{\n    // Emit a long literal load into PC\n    BYTE size = EmitLongLiteralLoad(pDest, c_PC, DETOURS_PBYTE_TO_PFUNC(pTarget));\n    return size;\n}\n\nBYTE CDetourDis::PureCopy16(BYTE* pSource, BYTE* pDest)\n{\n    *(USHORT *)pDest = *(USHORT *)pSource;\n    return sizeof(USHORT);\n}\n\nBYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest)\n{\n    *(UNALIGNED ULONG *)pDest = *(UNALIGNED ULONG*)pSource;\n    return sizeof(DWORD);\n}\n\nUSHORT CDetourDis::CalculateExtra(BYTE sourceLength, BYTE* pDestStart, BYTE* pDestEnd)\n{\n    ULONG destinationLength = (ULONG)(pDestEnd - pDestStart);\n    return static_cast<USHORT>((destinationLength > sourceLength) ? (destinationLength - sourceLength) : 0);\n}\n\nBYTE CDetourDis::CopyMiscellaneous16(BYTE* pSource, BYTE* pDest)\n{\n    USHORT instruction = *(PUSHORT)(pSource);\n\n    // Compare and branch imm5 (CBZ, CBNZ)\n    if ((instruction & 0x100) && !(instruction & 0x400)) { // (1011x0x1xxxxxxxx)\n        LONG oldDelta = DecodeBranch5(instruction);\n        PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n        m_pbTarget = pTarget;\n\n        LONG newDelta = CalculateNewDelta(pTarget, pDest);\n        instruction = EncodeBranch5(instruction, newDelta);\n\n        if (instruction) {\n            // Copy the 16 bit instruction over\n            *(PUSHORT)(pDest) = instruction;\n            return sizeof(USHORT); // The source instruction was 16 bits\n        }\n\n        // If that fails, re-encode with 'conditional branch' logic, without using the condition flags\n        // For example, cbz r2,+0x56 (0x90432) becomes:\n        //\n        //  001df73a b92a     cbnz        r2,001df748\n        //  001df73c e002     b           001df744\n        //  001df73e bf00     nop\n        //  001df740 0432     dc.h        0432\n        //  001df742 0009     dc.h        0009\n        //  001df744 f85ff008 ldr         pc,=0x90432\n        //\n\n        // Store where we will be writing our conditional branch, and move past it so we can emit a long branch\n        PUSHORT pDstInst = (PUSHORT)(pDest);\n        PUSHORT pConditionalBranchInstruction = pDstInst++;\n\n        // Emit the long branch instruction\n        BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget);\n\n        // Invert the CBZ/CBNZ instruction to move past our 'long branch' if the inverse comparison succeeds\n        // Write the CBZ/CBNZ instruction *before* the long branch we emitted above\n        // This had to be done out of order, since the size of a long branch can vary due to alignment restrictions\n        instruction = EncodeBranch5(*(PUSHORT)(pSource), longBranchSize - c_PCAdjust + sizeof(USHORT));\n        Branch5& branch = (Branch5&)(instruction);\n        branch.OpCode = (branch.OpCode & 0x02) ? 0x2C : 0x2E; // Invert the CBZ/CBNZ comparison\n        *pConditionalBranchInstruction = instruction;\n\n        // Compute the extra space needed for the branch sequence\n        m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));\n        return sizeof(USHORT); // The source instruction was 16 bits\n    }\n\n    // If-Then Instruction (IT)\n    if ((instruction >> 8 == 0xBF) && (instruction & 0xF)) { //(10111111xxxx(mask != 0b0000))\n        // ToDo: Implement IT handler\n        ASSERT(false);\n        return sizeof(USHORT);\n    }\n\n    // ADD/SUB, SXTH, SXTB, UXTH, UXTB, CBZ, CBNZ, PUSH, POP, REV, REV15, REVSH, NOP, YIELD, WFE, WFI, SEV, etc.\n    return PureCopy16(pSource, pDest);\n}\n\nBYTE CDetourDis::CopyConditionalBranchOrOther16(BYTE* pSource, BYTE* pDest)\n{\n    USHORT instruction = *(PUSHORT)(pSource);\n\n    // Could be a conditional branch, an Undefined instruction or a Service System Call\n    // Only the former needs special logic\n    if ((instruction & 0xE00) != 0xE00) { // 1101(!=111x)xxxxxxxx\n        LONG oldDelta = DecodeBranch8(instruction);\n        PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n        m_pbTarget = pTarget;\n\n        LONG newDelta = CalculateNewDelta(pTarget, pDest);\n        instruction = EncodeBranch8(instruction, newDelta);\n        if (instruction) {\n            // Copy the 16 bit instruction over\n            *(PUSHORT)(pDest) = instruction;\n            return sizeof(USHORT); // The source instruction was 16 bits\n        }\n\n        // If that fails, re-encode as a sequence of branches\n        // For example, bne +0x6E (0x90452) becomes:\n        //\n        // 001df758 d100     bne         001df75c\n        // 001df75a e005     b           001df768\n        // 001df75c e002     b           001df764\n        // 001df75e bf00     nop\n        // 001df760 0452     dc.h        0452\n        // 001df762 0009     dc.h        0009\n        // 001df764 f85ff008 ldr         pc,=0x90452\n        //\n\n        // First, reuse the existing conditional branch to, if successful, branch down to a 'long branch' that we will emit below\n        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\n        ASSERT(newInstruction);\n        PUSHORT pDstInst = (PUSHORT)(pDest);\n        *pDstInst++ = newInstruction;\n\n        // 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'\n        // We can't actually encode this branch yet though, because 'long branches' can vary in size\n        PUSHORT pUnconditionalBranchInstruction = pDstInst++;\n\n        // Then, emit a 'long branch' that will be hit if the original condition is met\n        BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget);\n\n        // Finally, encode and emit the unconditional branch that will be used to branch past the 'long branch' if the initial condition was not met\n        Branch11 branch11 = { 0x00, 0x1C };\n        newInstruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT));\n        ASSERT(newInstruction);\n        *pUnconditionalBranchInstruction = newInstruction;\n\n        // Compute the extra space needed for the branch sequence\n        m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));\n        return sizeof(USHORT); // The source instruction was 16 bits\n    }\n\n    return PureCopy16(pSource, pDest);\n}\n\nBYTE CDetourDis::CopyUnConditionalBranch16(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = *(PUSHORT)(pSource);\n\n    LONG oldDelta = DecodeBranch11(instruction);\n    PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n    m_pbTarget = pTarget;\n\n    LONG newDelta = CalculateNewDelta(pTarget, pDest);\n    instruction = EncodeBranch11(instruction, newDelta);\n    if (instruction) {\n        // Copy the 16 bit instruction over\n        *(PUSHORT)(pDest) = (USHORT)instruction;\n        return sizeof(USHORT); // The source instruction was 16 bits\n    }\n\n    // If that fails, re-encode as 32-bit\n    PUSHORT pDstInst = (PUSHORT)(pDest);\n    instruction = EncodeBranch24(0xf0009000, newDelta, FALSE);\n    if (instruction) {\n        // Copy both bytes of the instruction\n        EmitLongInstruction(pDstInst, instruction);\n\n        m_lExtra = sizeof(DWORD) - sizeof(USHORT); // The destination instruction was 32 bits\n        return sizeof(USHORT); // The source instruction was 16 bits\n    }\n\n    // If that fails, emit as a 'long branch'\n    if (!instruction) {\n        // For example, b +0x7FE (00090be6) becomes:\n        // 003f6d02 e001     b           003f6d08\n        // 003f6d04 0be6     dc.h        0be6\n        // 003f6d06 0009     dc.h        0009\n        // 003f6d08 f85ff008 ldr         pc,=0x90BE6\n        EmitLongBranch(pDstInst, pTarget);\n\n        // Compute the extra space needed for the branch sequence\n        m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));\n        return sizeof(USHORT); // The source instruction was 16 bits\n    }\n\n    return sizeof(USHORT); // The source instruction was 16 bits\n}\n\nBYTE CDetourDis::CopyLiteralLoad16(BYTE* pSource, BYTE* pDest)\n{\n    PBYTE pStart = pDest;\n    USHORT instruction = *(PUSHORT)(pSource);\n\n    LONG oldDelta = DecodeLiteralLoad8(instruction);\n    PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta);\n\n    // Re-encode as a 'long literal load'\n    // For example, ldr r0, [PC + 1E0] (0x905B4) becomes:\n    //\n    // 001df72c f85f0008 ldr         r0,=0x905B4\n    // 001df730 f8d00000 ldr.w       r0,[r0]\n    LiteralLoad8& load8 = (LiteralLoad8&)(instruction);\n    EmitLongLiteralLoad((PUSHORT&)pDest, load8.Register, pTarget);\n\n    m_lExtra = (LONG)(pDest - pStart - sizeof(USHORT));\n    return sizeof(USHORT); // The source instruction was 16 bits\n}\n\nBYTE CDetourDis::CopyBranchExchangeOrDataProcessing16(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = *(PUSHORT)(pSource);\n\n    // BX\n    if ((instruction & 0xff80) == 0x4700) {\n        // The target is stored in a register\n        m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    }\n\n    // AND, LSR, TST, ADD, CMP, MOV\n    return PureCopy16(pSource, pDest);\n}\n\nconst CDetourDis::COPYENTRY CDetourDis::s_rceCopyTable[33] =\n{\n    // Shift by immediate, move register\n    // ToDo: Not handling moves from PC\n    /* 0b00000 */ { 0x00, &CDetourDis::PureCopy16 },\n    /* 0b00001 */ { 0x01, &CDetourDis::PureCopy16 },\n    /* 0b00010 */ { 0x02, &CDetourDis::PureCopy16 },\n\n    // Add/subtract register\n    // Add/subtract immediate\n    /* 0b00011 */ { 0x03, &CDetourDis::PureCopy16},\n\n    // Add/subtract/compare/move immediate\n    /* 0b00100 */ { 0x04, &CDetourDis::PureCopy16 },\n    /* 0b00101 */ { 0x05, &CDetourDis::PureCopy16 },\n    /* 0b00110 */ { 0x06, &CDetourDis::PureCopy16 },\n    /* 0b00111 */ { 0x07, &CDetourDis::PureCopy16 },\n\n    // Data-processing register\n    // Special data processing\n    // Branch/exchange instruction set\n    /* 0b01000 */ { 0x08, &CDetourDis::CopyBranchExchangeOrDataProcessing16 },\n\n    // Load from literal pool\n    /* 0b01001 */ { 0x09, &CDetourDis::CopyLiteralLoad16 },\n\n    // Load/store register offset\n    /* 0b01010 */ { 0x0a, &CDetourDis::PureCopy16 },\n    /* 0b01011 */ { 0x0b, &CDetourDis::PureCopy16 },\n\n    //  Load/store word/byte immediate offset.\n    /* 0b01100 */ { 0x0c, &CDetourDis::PureCopy16 },\n    /* 0b01101 */ { 0x0d, &CDetourDis::PureCopy16 },\n    /* 0b01110 */ { 0x0e, &CDetourDis::PureCopy16 },\n    /* 0b01111 */ { 0x0f, &CDetourDis::PureCopy16 },\n\n    //  Load/store halfword immediate offset.\n    /* 0b10000 */ { 0x10, &CDetourDis::PureCopy16 },\n    /* 0b10001 */ { 0x11, &CDetourDis::PureCopy16 },\n\n    // Load from or store to stack\n    /* 0b10010 */ { 0x12, &CDetourDis::PureCopy16 },\n    /* 0b10011 */ { 0x13, &CDetourDis::PureCopy16 },\n\n    // Add to SP or PC\n    /* 0b10100 */ { 0x14, &CDetourDis::PureCopy16 },\n    //   ToDo: Is ADR (T1) blitt-able?\n    //     It adds a value to PC and stores the result in a register.\n    //     Does this count as a 'target' for detours?\n    /* 0b10101 */ { 0x15, &CDetourDis::PureCopy16 },\n\n    // Miscellaneous\n    /* 0b10110 */ { 0x16, &CDetourDis::CopyMiscellaneous16 },\n    /* 0b10111 */ { 0x17, &CDetourDis::CopyMiscellaneous16 },\n\n    // Load/store multiple\n    /* 0b11000 */ { 0x18, &CDetourDis::PureCopy16 },\n    /* 0b11001 */ { 0x19, &CDetourDis::PureCopy16 },\n    //   ToDo: Are we sure these are all safe?\n    //     LDMIA, for example, can include an 'embedded' branch.\n    //     Does this count as a 'target' for detours?\n\n    // Conditional branch\n    /* 0b11010 */ { 0x1a, &CDetourDis::CopyConditionalBranchOrOther16 },\n\n    // Conditional branch\n    // Undefined instruction\n    // Service (system) call\n    /* 0b11011 */ { 0x1b, &CDetourDis::CopyConditionalBranchOrOther16 },\n\n    // Unconditional branch\n    /* 0b11100 */ { 0x1c, &CDetourDis::CopyUnConditionalBranch16 },\n\n    // 32-bit instruction\n    /* 0b11101 */ { 0x1d, &CDetourDis::BeginCopy32 },\n    /* 0b11110 */ { 0x1e, &CDetourDis::BeginCopy32 },\n    /* 0b11111 */ { 0x1f, &CDetourDis::BeginCopy32 },\n    { 0, NULL }\n};\n\nBYTE CDetourDis::CopyBranch24(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = GetLongInstruction(pSource);\n    BOOL fLink;\n    LONG oldDelta = DecodeBranch24(instruction, fLink);\n    PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n    m_pbTarget = pTarget;\n\n    // Re-encode as 32-bit\n    PUSHORT pDstInst = (PUSHORT)(pDest);\n    LONG newDelta = CalculateNewDelta(pTarget, pDest);\n    instruction = EncodeBranch24(instruction, newDelta, fLink);\n    if (instruction) {\n        // Copy both bytes of the instruction\n        EmitLongInstruction(pDstInst, instruction);\n        return sizeof(DWORD);\n    }\n\n    // If that fails, re-encode as a 'long branch'\n    EmitLongBranch(pDstInst, pTarget);\n\n    // Compute the extra space needed for the instruction\n    m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst));\n    return sizeof(DWORD); // The source instruction was 32 bits\n}\n\nBYTE CDetourDis::CopyBranchOrMiscellaneous32(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = GetLongInstruction(pSource);\n    if ((instruction & 0xf800d000) == 0xf0008000) { // B<c>.W <label>\n        LONG oldDelta = DecodeBranch20(instruction);\n        PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n        m_pbTarget = pTarget;\n\n        // Re-encode as 32-bit\n        PUSHORT pDstInst = (PUSHORT)(pDest);\n        LONG newDelta = CalculateNewDelta(pTarget, pDest);\n        instruction = EncodeBranch20(instruction, newDelta);\n        if (instruction) {\n            // Copy both bytes of the instruction\n            EmitLongInstruction(pDstInst, instruction);\n            return sizeof(DWORD);\n        }\n\n        // If that fails, re-encode as a sequence of branches\n        // For example, bls.w +0x86 (00090480)| becomes:\n        //\n        // 001df788 f2408001 bls.w       001df78e\n        // 001df78c e004     b           001df798\n        // 001df78e e001     b           001df794\n        // 001df790 0480     dc.h        0480\n        // 001df792 0009     dc.h        0009\n        // 001df794 f85ff008 ldr         pc,=0x90480\n        //\n\n        // First, reuse the existing conditional branch to, if successful,\n        // branch down to a 'long branch' that we will emit below\n        instruction = EncodeBranch20(GetLongInstruction(pSource), 2);\n        // Due to the size of c_PCAdjust a two-length branch moves 6 bytes forward,\n        // past the following unconditional branch\n        ASSERT(instruction);\n        EmitLongInstruction(pDstInst, instruction);\n\n        // Next, prepare to insert an unconditional branch that will be hit\n        // if the condition above is not met.  This branch will branch over\n        // the following 'long branch'\n        // We can't actually encode this branch yet though, because\n        // 'long branches' can vary in size\n        PUSHORT pUnconditionalBranchInstruction = pDstInst++;\n\n        // Then, emit a 'long branch' that will be hit if the original condition is met\n        BYTE longBranchSize = EmitLongBranch(pDstInst, pTarget);\n\n        // Finally, encode and emit the unconditional branch that will be used\n        // to branch past the 'long branch' if the initial condition was not met\n        Branch11 branch11 = { 0x00, 0x1C };\n        instruction = EncodeBranch11(*(DWORD*)(&branch11), longBranchSize - c_PCAdjust + sizeof(USHORT));\n        ASSERT(instruction);\n        *pUnconditionalBranchInstruction = static_cast<USHORT>(instruction);\n\n        // Compute the extra space needed for the instruction\n        m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst));\n        return sizeof(DWORD); // The source instruction was 32 bits\n    }\n\n    if ((instruction & 0xf800d000) == 0xf0009000) { // B.W <label>\n        // B <label>  11110xxxxxxxxxxx10xxxxxxxxxxxxxx\n        return CopyBranch24(pSource, pDest);\n    }\n\n    if ((instruction & 0xf800d000) == 0xf000d000) { // BL.W <label>\n        // B <label>  11110xxxxxxxxxxx10xxxxxxxxxxxxxx\n\n        PUSHORT pDstInst = (PUSHORT)(pDest);\n        BOOL fLink;\n        LONG oldDelta = DecodeBranch24(instruction, fLink);\n        PBYTE pTarget = CalculateTarget(pSource, oldDelta);\n        m_pbTarget = pTarget;\n\n        *--((PULONG&)m_pbPool) = (ULONG)(size_t)DETOURS_PBYTE_TO_PFUNC(pTarget);\n\n        // ldr lr, target.\n        EmitLiteralLoad12(pDstInst, c_LR, m_pbPool);\n        // blx lr\n        EmitShortInstruction(pDstInst, 0x47f0);\n\n        // Compute the extra space needed for the instruction\n        m_lExtra = CalculateExtra(sizeof(DWORD), pDest, (BYTE*)(pDstInst));\n        return sizeof(DWORD); // The source instruction was 32 bits\n    }\n\n    if ((instruction & 0xFFF0FFFF) == 0xF3C08F00) {\n        // BXJ 111100111100xxxx1000111100000000\n        // BXJ switches to Jazelle mode, which is not supported\n        ASSERT(false);\n    }\n\n    if ((instruction & 0xFFFFFF00) == 0xF3DE8F00) {\n        // SUBS PC, LR 111100111101111010001111xxxxxxxx\n        m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    }\n\n    // Everything else should be blitt-able\n    return PureCopy32(pSource, pDest);\n}\n\nBYTE CDetourDis::CopyLiteralLoad32(BYTE* pSource, BYTE* pDest)\n{\n    BYTE* pStart = pDest;\n    ULONG instruction = GetLongInstruction(pSource);\n\n    LONG oldDelta = DecodeLiteralLoad12(instruction);\n    PBYTE pTarget = CalculateTarget(Align4(pSource), oldDelta);\n\n    LiteralLoad12& load = (LiteralLoad12&)(instruction);\n\n    EmitLongLiteralLoad((PUSHORT&)pDest, load.Register, pTarget);\n\n    m_lExtra = (LONG)(pDest - pStart - sizeof(DWORD));\n\n    return sizeof(DWORD); // The source instruction was 32 bits\n}\n\nBYTE CDetourDis::CopyLoadAndStoreSingle(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = GetLongInstruction(pSource);\n\n    // Note: The following masks only look at the interesting bits\n    // (not the opCode prefix, since that check was performed in\n    // order to get to this function)\n    if (!(instruction & 0x100000)) {\n        // 1111 100x xxx0 xxxxxxxxxxxxxxxxxxxx : STR, STRB, STRH, etc.\n        return PureCopy32(pSource, pDest);\n    }\n\n    if ((instruction & 0xF81F0000) == 0xF81F0000) {\n        // 1111100xxxx11111xxxxxxxxxxxxxxxx : PC +/- Imm12\n        return CopyLiteralLoad32(pSource, pDest);\n    }\n\n    if ((instruction & 0xFE70F000) == 0xF81FF000) {\n        // 1111100xx001xxxx1111xxxxxxxxxxxx : PLD, PLI\n        // Convert PC-Relative PLD/PLI instructions to noops (1111100Xx00111111111xxxxxxxxxxxx)\n        if ((instruction & 0xFE7FF000) == 0xF81FF000) {\n            PUSHORT pDstInst = (PUSHORT)(pDest);\n            *pDstInst++ = c_NOP;\n            *pDstInst++ = c_NOP;\n            return sizeof(DWORD);  // The source instruction was 32 bits\n        }\n\n        // All other PLD/PLI instructions are blitt-able\n        return PureCopy32(pSource, pDest);\n    }\n\n    // If the load is writing to PC\n    if ((instruction & 0xF950F000) == 0xF850F000) {\n        m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    }\n\n    // All other loads LDR (immediate), etc.\n    return PureCopy32(pSource, pDest);\n}\n\nBYTE CDetourDis::CopyLoadAndStoreMultipleAndSRS(BYTE* pSource, BYTE* pDest)\n{\n    // Probably all blitt-able, although not positive since some of these can result in a branch (LDMIA, POP, etc.)\n    return PureCopy32(pSource, pDest);\n}\n\nBYTE CDetourDis::CopyTableBranch(BYTE* pSource, BYTE* pDest)\n{\n    m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n    ULONG instruction = GetLongInstruction(pSource);\n    TableBranch& tableBranch = (TableBranch&)(instruction);\n\n    // If the base register is anything other than PC, we can simply copy the instruction\n    if (tableBranch.BaseRegister != c_PC) {\n        return PureCopy32(pSource, pDest);\n    }\n\n    __debugbreak();\n\n    // If the base register is PC, we need to manually perform the table lookup\n    // For example, this:\n    //\n    //        7ef40000 e8dff002 tbb         [pc,r2]\n    //\n    // becomes this:\n    //\n    //        7ef40404 b401     push        {r0}            ; pushed as a placeholder for the target address\n    //        7ef40406 e92d0005 push.w      {r0,r2}         ; scratch register and another register are pushed; there's a minimum of two registers in the list for push.w\n    //        7ef40410 4820     ldr         r0,=0x7EF40004  ; load the table address from the literal pool\n    //        7ef40414 eb000042 add         r0,r0,r2,lsl #1 ; add the index value to the address of the table to get the table entry; lsl only used if it's a TBH instruction\n    //        7ef40418 f8d00000 ldr.w       r0,[r0]         ; dereference the table entry to get the value of the target\n    //        7ef4041c ea4f0040 lsl         r0,r0,#1        ; multiply the offset by 2 (per the spec)\n    //        7ef40420 eb00000f add.w       r0,r0,pc        ; Add the offset to pc to get the target address\n    //        7ef40424 f8cd000c str.w       r0,[sp,#0xC]    ; store the target address on the stack (into the first push)\n    //        7ef40428 e8bd0005 pop.w       {r0,r2}         ; scratch register and another register are popped; there's a minimum of two registers in the list for pop.w\n    //        7ef4042c bd00     pop         {pc}            ; pop the address into pc\n    //\n\n    // Push r0 to make room for our jump address on the stack\n    PUSHORT pDstInst = (PUSHORT)(pDest);\n    *pDstInst++ = 0xb401;\n\n    // Locate a scratch register\n    BYTE scrReg = 0;\n    while (scrReg == tableBranch.IndexRegister) {\n        ++scrReg;\n    }\n\n    // Push scrReg and tableBranch.IndexRegister (push.w doesn't support pushing just 1 register)\n    DWORD pushInstruction = 0xe92d0000;\n    pushInstruction |= 1 << scrReg;\n    pushInstruction |= 1 << tableBranch.IndexRegister;\n    EmitLongInstruction(pDstInst, pushInstruction);\n\n    // Write the target address out to the 'literal pool';\n    // when the base register of a TBB/TBH is PC,\n    // the branch table immediately follows the instruction\n    BYTE* pTarget = CalculateTarget(pSource, 0);\n    *--((PUSHORT&)m_pbPool) = (USHORT)((size_t)pTarget & 0xffff);\n    *--((PUSHORT&)m_pbPool) = (USHORT)((size_t)pTarget >> 16);\n\n    // Load the literal pool value into our scratch register (this contains the address of the branch table)\n    // ldr rn, target\n    EmitLiteralLoad8(pDstInst, scrReg, m_pbPool);\n\n    // Add the index offset to the address of the branch table; the result will be the value within the table that contains the branch offset\n    // We need to multiply the index by two if we are using halfword indexing\n    // Will shift tableBranch.IndexRegister by 1 (multiply by 2) if using a TBH\n    EmitAdd32(pDstInst, scrReg, tableBranch.IndexRegister, scrReg, tableBranch.HalfWord);\n\n    // Dereference rn into rn, to load the value within the table\n    // ldr rn, [rn]\n    if (scrReg < 0x7) {\n        EmitImmediateRegisterLoad16(pDstInst, scrReg);\n    }\n    else {\n        EmitImmediateRegisterLoad32(pDstInst, scrReg);\n    }\n\n    // Multiply the offset by two to get the true offset value (as per the spec)\n    EmitLogicalShiftLeft32(pDstInst, scrReg, scrReg, 1);\n\n    // Add the offset to PC to get the target\n    EmitAdd32(pDstInst, scrReg, c_PC, scrReg, 0);\n\n    // Now write the contents of scrReg to the stack, so we can pop it into PC\n    // Write the address of the branch table entry to the stack, so we can pop it into PC\n    EmitStoreImmediate12(pDstInst, scrReg, c_SP, sizeof(DWORD) * 3);\n\n    // Pop scrReg and tableBranch.IndexRegister (pop.w doesn't support popping just 1 register)\n    DWORD popInstruction = 0xe8bd0000;\n    popInstruction |= 1 << scrReg;\n    popInstruction |= 1 << tableBranch.IndexRegister;\n    EmitLongInstruction(pDstInst, popInstruction);\n\n    // Pop PC\n    *pDstInst++ = 0xbd00;\n\n    // Compute the extra space needed for the branch sequence\n    m_lExtra = CalculateExtra(sizeof(USHORT), pDest, (BYTE*)(pDstInst));\n    return sizeof(DWORD);\n}\n\nBYTE CDetourDis::BeginCopy32(BYTE* pSource, BYTE* pDest)\n{\n    ULONG instruction = GetLongInstruction(pSource);\n\n    // Immediate data processing instructions; ADD, SUB, MOV, MOVN, ADR, MOVT, BFC, SSAT16, etc.\n    if ((instruction & 0xF8008000) == 0xF0000000) { // 11110xxxxxxxxxxx0xxxxxxxxxxxxxxx\n        // Should all be blitt-able\n        // ToDo: What about ADR?  Is it safe to do a straight-copy?\n        // ToDo: Not handling moves to or from PC\n        return PureCopy32(pSource, pDest);\n    }\n\n    // Non-Immediate data processing instructions; ADD, EOR, TST, etc.\n    if ((instruction & 0xEE000000) == 0xEA000000) { // 111x101xxxxxxxxxxxxxxxxxxxxxxx\n        // Should all be blitt-able\n        return PureCopy32(pSource, pDest);\n    }\n\n    // Load and store single data item, memory hints\n    if ((instruction & 0xFE000000) == 0xF8000000) { // 1111100xxxxxxxxxxxxxxxxxxxxxxxxx\n        return CopyLoadAndStoreSingle(pSource, pDest);\n    }\n\n    // Load and store, double and exclusive, and table branch\n    if ((instruction & 0xFE400000) == 0xE8400000) { // 1110100xx1xxxxxxxxxxxxxxxxxxxxxx\n        // Load and store double\n        if (instruction & 0x1200000) {\n            // LDRD, STRD (immediate) : xxxxxxxPxxWxxxxxxxxxxxxxxxxxxxxx where PW != 0b00\n            // The source register is PC\n            if ((instruction & 0xF0000) == 0xF0000) {\n                // ToDo: If the source register is PC, what should we do?\n                ASSERT(false);\n            }\n\n            // If either target registers are PC\n            if (((instruction & 0xF000) == 0xF000) ||\n                ((instruction & 0xF00) == 0xF00)) {\n                m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n            }\n\n            return PureCopy32(pSource, pDest);\n        }\n\n        // Load and store exclusive\n        if (!(instruction & 0x800000)) { // LDREX, STREX : xxxxxxxx0xxxxxxxxxxxxxxxxxxxxxxx\n            if ((instruction & 0xF000) == 0xF000) { // xxxxxxxxxxxx1111xxxxxxxxxxxx\n                m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;\n            }\n            return PureCopy32(pSource, pDest);\n        }\n\n        // Table branch\n        if ((instruction & 0x1000F0) == 0x100000 ||  // TBB : xxxxxxxxxxx1xxxxxxxxxxxx0000xxxx\n            (instruction & 0x1000F0) == 0x100010) { // TBH : xxxxxxxxxxx1xxxxxxxxxxxx0001xxxx\n            return CopyTableBranch(pSource, pDest);\n        }\n\n        // Load and store exclusive byte, halfword, doubleword (LDREXB, LDREXH, LDREXD, STREXB, STREXH, STREXD, etc.)\n        return PureCopy32(pSource, pDest);\n    }\n\n    // Load and store multiple, RFE and SRS\n    if ((instruction & 0xFE400000) == 0xE8000000) { // 1110100xx0xxxxxxxxxxxxxxxxxxxxxx\n        // Return from exception (RFE)\n        if ((instruction & 0xE9900000) == 0xE9900000 || // 1110100110x1xxxxxxxxxxxxxxxxxxxx\n            (instruction & 0xE8100000) == 0xE8100000) { // 1110100000x1xxxxxxxxxxxxxxxxxxxx\n            return PureCopy32(pSource, pDest);\n        }\n\n        return CopyLoadAndStoreMultipleAndSRS(pSource, pDest);\n    }\n\n    // Branches, miscellaneous control\n    if ((instruction & 0xF8008000) == 0xF0008000) { // 11110xxxxxxxxxxx0xxxxxxxxxxxxxxx\n        // Branches, miscellaneous control\n        return CopyBranchOrMiscellaneous32(pSource, pDest);\n    }\n\n    // Coprocessor instructions\n    if ((instruction & 0xEC000000) == 0xEC000000) { // 111x11xxxxxxxxxxxxxxxxxxxxxxxxxx\n        return PureCopy32(pSource, pDest);\n    }\n\n    // Unhandled instruction; should never make it this far\n    ASSERT(false);\n    return PureCopy32(pSource, pDest);\n}\n\n/////////////////////////////////////////////////////////// Disassembler Code.\n//\nCDetourDis::CDetourDis()\n{\n    m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE;\n    m_pbPool = NULL;\n    m_lExtra = 0;\n}\n\nPBYTE CDetourDis::CopyInstruction(PBYTE pDst,\n                                  PBYTE *ppDstPool,\n                                  PBYTE pSrc,\n                                  PBYTE *ppTarget,\n                                  LONG *plExtra)\n{\n    if (pDst && ppDstPool && ppDstPool != NULL) {\n        m_pbPool = (PBYTE)*ppDstPool;\n    }\n    else {\n        pDst = m_rbScratchDst;\n        m_pbPool = m_rbScratchDst + sizeof(m_rbScratchDst);\n    }\n    // Make sure the constant pool is 32-bit aligned.\n    m_pbPool -= ((ULONG_PTR)m_pbPool) & 3;\n\n    REFCOPYENTRY pEntry = &s_rceCopyTable[pSrc[1] >> 3];\n    ULONG size = (this->*pEntry->pfCopy)(pSrc, pDst);\n\n    pSrc += size;\n\n    // If the target is needed, store our target\n    if (ppTarget) {\n        *ppTarget = m_pbTarget;\n    }\n    if (plExtra) {\n        *plExtra = m_lExtra;\n    }\n    if (ppDstPool) {\n        *ppDstPool = m_pbPool;\n    }\n\n    return pSrc;\n}\n\n\nPVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,\n                                   _Inout_opt_ PVOID *ppDstPool,\n                                   _In_ PVOID pSrc,\n                                   _Out_opt_ PVOID *ppTarget,\n                                   _Out_opt_ LONG *plExtra)\n{\n    CDetourDis state;\n    return (PVOID)state.CopyInstruction((PBYTE)pDst,\n                                        (PBYTE*)ppDstPool,\n                                        (PBYTE)pSrc,\n                                        (PBYTE*)ppTarget,\n                                        plExtra);\n}\n\n#endif // DETOURS_ARM\n\n#ifdef DETOURS_ARM64\n\n#define c_LR        30          // The register number for the Link Register\n#define c_SP        31          // The register number for the Stack Pointer\n#define c_NOP       0xd503201f  // A nop instruction\n#define c_BREAK     (0xd4200000 | (0xf000 << 5)) // A break instruction\n\n//\n// Problematic instructions:\n//\n// ADR     0ll10000 hhhhhhhh hhhhhhhh hhhddddd  & 0x9f000000 == 0x10000000  (l = low, h = high, d = Rd)\n// ADRP    1ll10000 hhhhhhhh hhhhhhhh hhhddddd  & 0x9f000000 == 0x90000000  (l = low, h = high, d = Rd)\n//\n// B.cond  01010100 iiiiiiii iiiiiiii iii0cccc  & 0xff000010 == 0x54000000  (i = delta = SignExtend(imm19:00, 64), c = cond)\n//\n// B       000101ii iiiiiiii iiiiiiii iiiiiiii  & 0xfc000000 == 0x14000000  (i = delta = SignExtend(imm26:00, 64))\n// BL      100101ii iiiiiiii iiiiiiii iiiiiiii  & 0xfc000000 == 0x94000000  (i = delta = SignExtend(imm26:00, 64))\n//\n// CBNZ    z0110101 iiiiiiii iiiiiiii iiittttt  & 0x7f000000 == 0x35000000  (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt)\n// CBZ     z0110100 iiiiiiii iiiiiiii iiittttt  & 0x7f000000 == 0x34000000  (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt)\n//\n// LDR Wt  00011000 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x18000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDR Xt  01011000 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x58000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDRSW   10011000 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x98000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// PRFM    11011000 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0xd8000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDR St  00011100 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x1c000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDR Dt  01011100 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x5c000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDR Qt  10011100 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0x9c000000  (i = SignExtend(imm19:00, 64), t = Rt)\n// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt  & 0xff000000 == 0xdc000000  (i = SignExtend(imm19:00, 64), t = Rt)\n//\n// TBNZ    z0110111 bbbbbiii iiiiiiii iiittttt  & 0x7f000000 == 0x37000000  (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt)\n// TBZ     z0110110 bbbbbiii iiiiiiii iiittttt  & 0x7f000000 == 0x36000000  (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt)\n//\n\nclass CDetourDis\n{\n  public:\n    CDetourDis();\n\n    PBYTE   CopyInstruction(PBYTE pDst,\n                            PBYTE pSrc,\n                            PBYTE *ppTarget,\n                            LONG *plExtra);\n\n  public:\n    typedef BYTE (CDetourDis::* COPYFUNC)(PBYTE pbDst, PBYTE pbSrc);\n\n    union AddImm12\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rd : 5;           // Destination register\n            DWORD Rn : 5;           // Source register\n            DWORD Imm12 : 12;       // 12-bit immediate\n            DWORD Shift : 2;        // shift (must be 0 or 1)\n            DWORD Opcode1 : 7;      // Must be 0010001 == 0x11\n            DWORD Size : 1;         // 0 = 32-bit, 1 = 64-bit\n        } s;\n        static DWORD Assemble(DWORD size, DWORD rd, DWORD rn, ULONG imm, DWORD shift)\n        {\n            AddImm12 temp;\n            temp.s.Rd = rd;\n            temp.s.Rn = rn;\n            temp.s.Imm12 = imm & 0xfff;\n            temp.s.Shift = shift;\n            temp.s.Opcode1 = 0x11;\n            temp.s.Size = size;\n            return temp.Assembled;\n        }\n        static DWORD AssembleAdd32(DWORD rd, DWORD rn, ULONG imm, DWORD shift) { return Assemble(0, rd, rn, imm, shift); }\n        static DWORD AssembleAdd64(DWORD rd, DWORD rn, ULONG imm, DWORD shift) { return Assemble(1, rd, rn, imm, shift); }\n    };\n\n    union Adr19\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rd : 5;           // Destination register\n            DWORD Imm19 : 19;       // 19-bit upper immediate\n            DWORD Opcode1 : 5;      // Must be 10000 == 0x10\n            DWORD Imm2 : 2;         // 2-bit lower immediate\n            DWORD Type : 1;         // 0 = ADR, 1 = ADRP\n        } s;\n        inline LONG Imm() const { DWORD Imm = (s.Imm19 << 2) | s.Imm2; return (LONG)(Imm << 11) >> 11; }\n        static DWORD Assemble(DWORD type, DWORD rd, LONG delta)\n        {\n            Adr19 temp;\n            temp.s.Rd = rd;\n            temp.s.Imm19 = (delta >> 2) & 0x7ffff;\n            temp.s.Opcode1 = 0x10;\n            temp.s.Imm2 = delta & 3;\n            temp.s.Type = type;\n            return temp.Assembled;\n        }\n        static DWORD AssembleAdr(DWORD rd, LONG delta) { return Assemble(0, rd, delta); }\n        static DWORD AssembleAdrp(DWORD rd, LONG delta) { return Assemble(1, rd, delta); }\n    };\n\n    union Bcc19\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Condition : 4;    // Condition\n            DWORD Opcode1 : 1;      // Must be 0\n            DWORD Imm19 : 19;       // 19-bit immediate\n            DWORD Opcode2 : 8;      // Must be 01010100 == 0x54\n        } s;\n        inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; }\n        static DWORD AssembleBcc(DWORD condition, LONG delta)\n        {\n            Bcc19 temp;\n            temp.s.Condition = condition;\n            temp.s.Opcode1 = 0;\n            temp.s.Imm19 = delta >> 2;\n            temp.s.Opcode2 = 0x54;\n            return temp.Assembled;\n        }\n    };\n\n    union Branch26\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Imm26 : 26;       // 26-bit immediate\n            DWORD Opcode1 : 5;      // Must be 00101 == 0x5\n            DWORD Link : 1;         // 0 = B, 1 = BL\n        } s;\n        inline LONG Imm() const { return (LONG)(s.Imm26 << 6) >> 4; }\n        static DWORD Assemble(DWORD link, LONG delta)\n        {\n            Branch26 temp;\n            temp.s.Imm26 = delta >> 2;\n            temp.s.Opcode1 = 0x5;\n            temp.s.Link = link;\n            return temp.Assembled;\n        }\n        static DWORD AssembleB(LONG delta) { return Assemble(0, delta); }\n        static DWORD AssembleBl(LONG delta) { return Assemble(1, delta); }\n    };\n\n    union Br\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Opcode1 : 5;      // Must be 00000 == 0\n            DWORD Rn : 5;           // Register number\n            DWORD Opcode2 : 22;     // Must be 1101011000011111000000 == 0x3587c0\n        } s;\n        static DWORD AssembleBr(DWORD rn)\n        {\n            Br temp;\n            temp.s.Opcode1 = 0;\n            temp.s.Rn = rn;\n            temp.s.Opcode2 = 0x3587c0;\n            return temp.Assembled;\n        }\n    };\n\n    union Cbz19\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rt : 5;           // Register to test\n            DWORD Imm19 : 19;       // 19-bit immediate\n            DWORD Nz : 1;           // 0 = CBZ, 1 = CBNZ\n            DWORD Opcode1 : 6;      // Must be 011010 == 0x1a\n            DWORD Size : 1;         // 0 = 32-bit, 1 = 64-bit\n        } s;\n        inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; }\n        static DWORD Assemble(DWORD size, DWORD nz, DWORD rt, LONG delta)\n        {\n            Cbz19 temp;\n            temp.s.Rt = rt;\n            temp.s.Imm19 = delta >> 2;\n            temp.s.Nz = nz;\n            temp.s.Opcode1 = 0x1a;\n            temp.s.Size = size;\n            return temp.Assembled;\n        }\n    };\n\n    union LdrLit19\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rt : 5;           // Destination register\n            DWORD Imm19 : 19;       // 19-bit immediate\n            DWORD Opcode1 : 2;      // Must be 0\n            DWORD FpNeon : 1;       // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt\n            DWORD Opcode2 : 3;      // Must be 011 = 3\n            DWORD Size : 2;         // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid\n        } s;\n        inline LONG Imm() const { return (LONG)(s.Imm19 << 13) >> 11; }\n        static DWORD Assemble(DWORD size, DWORD fpneon, DWORD rt, LONG delta)\n        {\n            LdrLit19 temp;\n            temp.s.Rt = rt;\n            temp.s.Imm19 = delta >> 2;\n            temp.s.Opcode1 = 0;\n            temp.s.FpNeon = fpneon;\n            temp.s.Opcode2 = 3;\n            temp.s.Size = size;\n            return temp.Assembled;\n        }\n    };\n\n    union LdrFpNeonImm9\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rt : 5;           // Destination register\n            DWORD Rn : 5;           // Base register\n            DWORD Imm12 : 12;       // 12-bit immediate\n            DWORD Opcode1 : 1;      // Must be 1 == 1\n            DWORD Opc : 1;          // Part of size\n            DWORD Opcode2 : 6;      // Must be 111101 == 0x3d\n            DWORD Size : 2;         // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit)\n        } s;\n        static DWORD Assemble(DWORD size, DWORD rt, DWORD rn, ULONG imm)\n        {\n            LdrFpNeonImm9 temp;\n            temp.s.Rt = rt;\n            temp.s.Rn = rn;\n            temp.s.Imm12 = imm;\n            temp.s.Opcode1 = 1;\n            temp.s.Opc = size >> 2;\n            temp.s.Opcode2 = 0x3d;\n            temp.s.Size = size & 3;\n            return temp.Assembled;\n        }\n    };\n\n    union Mov16\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rd : 5;           // Destination register\n            DWORD Imm16 : 16;       // Immediate\n            DWORD Shift : 2;        // Shift amount (0=0, 1=16, 2=32, 3=48)\n            DWORD Opcode : 6;       // Must be 100101 == 0x25\n            DWORD Type : 2;         // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK\n            DWORD Size : 1;         // 0 = 32-bit, 1 = 64-bit\n        } s;\n        static DWORD Assemble(DWORD size, DWORD type, DWORD rd, DWORD imm, DWORD shift)\n        {\n            Mov16 temp;\n            temp.s.Rd = rd;\n            temp.s.Imm16 = imm;\n            temp.s.Shift = shift;\n            temp.s.Opcode = 0x25;\n            temp.s.Type = type;\n            temp.s.Size = size;\n            return temp.Assembled;\n        }\n        static DWORD AssembleMovn32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 0, rd, imm, shift); }\n        static DWORD AssembleMovn64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 0, rd, imm, shift); }\n        static DWORD AssembleMovz32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 2, rd, imm, shift); }\n        static DWORD AssembleMovz64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 2, rd, imm, shift); }\n        static DWORD AssembleMovk32(DWORD rd, DWORD imm, DWORD shift) { return Assemble(0, 3, rd, imm, shift); }\n        static DWORD AssembleMovk64(DWORD rd, DWORD imm, DWORD shift) { return Assemble(1, 3, rd, imm, shift); }\n    };\n\n    union Tbz14\n    {\n        DWORD Assembled;\n        struct\n        {\n            DWORD Rt : 5;           // Register to test\n            DWORD Imm14 : 14;       // 14-bit immediate\n            DWORD Bit : 5;          // 5-bit index\n            DWORD Nz : 1;           // 0 = TBZ, 1 = TBNZ\n            DWORD Opcode1 : 6;      // Must be 011011 == 0x1b\n            DWORD Size : 1;         // 0 = 32-bit, 1 = 64-bit\n        } s;\n        inline LONG Imm() const { return (LONG)(s.Imm14 << 18) >> 16; }\n        static DWORD Assemble(DWORD size, DWORD nz, DWORD rt, DWORD bit, LONG delta)\n        {\n            Tbz14 temp;\n            temp.s.Rt = rt;\n            temp.s.Imm14 = delta >> 2;\n            temp.s.Bit = bit;\n            temp.s.Nz = nz;\n            temp.s.Opcode1 = 0x1b;\n            temp.s.Size = size;\n            return temp.Assembled;\n        }\n    };\n\n\n  protected:\n    BYTE    PureCopy32(BYTE* pSource, BYTE* pDest);\n    BYTE    EmitMovImmediate(PULONG& pDstInst, BYTE rd, UINT64 immediate);\n    BYTE    CopyAdr(BYTE* pSource, BYTE* pDest, ULONG instruction);\n    BYTE    CopyBcc(BYTE* pSource, BYTE* pDest, ULONG instruction);\n    BYTE    CopyB(BYTE* pSource, BYTE* pDest, ULONG instruction);\n    BYTE    CopyCbz(BYTE* pSource, BYTE* pDest, ULONG instruction);\n    BYTE    CopyTbz(BYTE* pSource, BYTE* pDest, ULONG instruction);\n    BYTE    CopyLdrLiteral(BYTE* pSource, BYTE* pDest, ULONG instruction);\n\n  protected:\n    ULONG GetInstruction(BYTE* pSource)\n    {\n        return ((PULONG)pSource)[0];\n    }\n\n    BYTE EmitInstruction(PULONG& pDstInst, ULONG instruction)\n    {\n        *pDstInst++ = instruction;\n        return sizeof(ULONG);\n    }\n\n  protected:\n    PBYTE   m_pbTarget = NULL;\n    BYTE    m_rbScratchDst[64] = { 0 };\n};\n\nBYTE CDetourDis::PureCopy32(BYTE* pSource, BYTE* pDest)\n{\n    *(ULONG *)pDest = *(ULONG*)pSource;\n    return sizeof(DWORD);\n}\n\n/////////////////////////////////////////////////////////// Disassembler Code.\n//\nCDetourDis::CDetourDis()\n{\n    m_pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE;\n}\n\nPBYTE CDetourDis::CopyInstruction(PBYTE pDst,\n                                  PBYTE pSrc,\n                                  PBYTE *ppTarget,\n                                  LONG *plExtra)\n{\n    if (pDst == NULL) {\n        pDst = m_rbScratchDst;\n    }\n\n    DWORD Instruction = GetInstruction(pSrc);\n\n    ULONG CopiedSize;\n    if ((Instruction & 0x1f000000) == 0x10000000) {\n        CopiedSize = CopyAdr(pSrc, pDst, Instruction);\n    } else if ((Instruction & 0xff000010) == 0x54000000) {\n        CopiedSize = CopyBcc(pSrc, pDst, Instruction);\n    } else if ((Instruction & 0x7c000000) == 0x14000000) {\n        CopiedSize = CopyB(pSrc, pDst, Instruction);\n    } else if ((Instruction & 0x7e000000) == 0x34000000) {\n        CopiedSize = CopyCbz(pSrc, pDst, Instruction);\n    } else if ((Instruction & 0x7e000000) == 0x36000000) {\n        CopiedSize = CopyTbz(pSrc, pDst, Instruction);\n    } else if ((Instruction & 0x3b000000) == 0x18000000) {\n        CopiedSize = CopyLdrLiteral(pSrc, pDst, Instruction);\n    } else {\n        CopiedSize = PureCopy32(pSrc, pDst);\n    }\n\n    // If the target is needed, store our target\n    if (ppTarget) {\n        *ppTarget = m_pbTarget;\n    }\n    if (plExtra) {\n        *plExtra = CopiedSize - sizeof(DWORD);\n    }\n\n    return pSrc + 4;\n}\n\nBYTE CDetourDis::EmitMovImmediate(PULONG& pDstInst, BYTE rd, UINT64 immediate)\n{\n    DWORD piece[4];\n    piece[3] = (DWORD)((immediate >> 48) & 0xffff);\n    piece[2] = (DWORD)((immediate >> 32) & 0xffff);\n    piece[1] = (DWORD)((immediate >> 16) & 0xffff);\n    piece[0] = (DWORD)((immediate >> 0) & 0xffff);\n    int count = 0;\n\n    // special case: MOVN with 32-bit dest\n    if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff)\n    {\n        EmitInstruction(pDstInst, Mov16::AssembleMovn32(rd, piece[0] ^ 0xffff, 0));\n        count++;\n    }\n\n    // MOVN/MOVZ with 64-bit dest\n    else\n    {\n        int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000);\n        int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff);\n        DWORD defaultPiece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000;\n        bool first = true;\n        for (int pieceNum = 3; pieceNum >= 0; pieceNum--)\n        {\n            DWORD curPiece = piece[pieceNum];\n            if (curPiece != defaultPiece || (pieceNum == 0 && first))\n            {\n                count++;\n                if (first)\n                {\n                    if (defaultPiece == 0xffff)\n                    {\n                        EmitInstruction(pDstInst, Mov16::AssembleMovn64(rd, curPiece ^ 0xffff, pieceNum));\n                    }\n                    else\n                    {\n                        EmitInstruction(pDstInst, Mov16::AssembleMovz64(rd, curPiece, pieceNum));\n                    }\n                    first = false;\n                }\n                else\n                {\n                    EmitInstruction(pDstInst, Mov16::AssembleMovk64(rd, curPiece, pieceNum));\n                }\n            }\n        }\n    }\n    return (BYTE)(count * sizeof(DWORD));\n}\n\nBYTE CDetourDis::CopyAdr(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    Adr19& decoded = (Adr19&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    // ADR case\n    if (decoded.s.Type == 0)\n    {\n        BYTE* pTarget = pSource + decoded.Imm();\n        LONG64 delta = pTarget - pDest;\n        LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12);\n\n        // output as ADR\n        if (delta >= -(1 << 20) && delta < (1 << 20))\n        {\n            EmitInstruction(pDstInst, Adr19::AssembleAdr(decoded.s.Rd, (LONG)delta));\n        }\n\n        // output as ADRP; ADD\n        else if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20)))\n        {\n            EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (LONG)deltaPage));\n            EmitInstruction(pDstInst, AddImm12::AssembleAdd32(decoded.s.Rd, decoded.s.Rd, ((ULONG)(ULONG_PTR)pTarget) & 0xfff, 0));\n        }\n\n        // output as immediate move\n        else\n        {\n            EmitMovImmediate(pDstInst, decoded.s.Rd, (ULONG_PTR)pTarget);\n        }\n    }\n\n    // ADRP case\n    else\n    {\n        BYTE* pTarget = (BYTE*)((((ULONG_PTR)pSource >> 12) + decoded.Imm()) << 12);\n        LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12);\n\n        // output as ADRP\n        if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20)))\n        {\n            EmitInstruction(pDstInst, Adr19::AssembleAdrp(decoded.s.Rd, (LONG)deltaPage));\n        }\n\n        // output as immediate move\n        else\n        {\n            EmitMovImmediate(pDstInst, decoded.s.Rd, (ULONG_PTR)pTarget);\n        }\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\nBYTE CDetourDis::CopyBcc(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    Bcc19& decoded = (Bcc19&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    BYTE* pTarget = pSource + decoded.Imm();\n    m_pbTarget = pTarget;\n    LONG64 delta = pTarget - pDest;\n    LONG64 delta4 = pTarget - (pDest + 4);\n\n    // output as BCC\n    if (delta >= -(1 << 20) && delta < (1 << 20))\n    {\n        EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition, (LONG)delta));\n    }\n\n    // output as BCC <skip>; B\n    else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))\n    {\n        EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8));\n        EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4));\n    }\n\n    // output as MOV x17, Target; BCC <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!)\n    else\n    {\n        EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget);\n        EmitInstruction(pDstInst, Bcc19::AssembleBcc(decoded.s.Condition ^ 1, 8));\n        EmitInstruction(pDstInst, Br::AssembleBr(17));\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\nBYTE CDetourDis::CopyB(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    Branch26& decoded = (Branch26&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    BYTE* pTarget = pSource + decoded.Imm();\n    m_pbTarget = pTarget;\n    LONG64 delta = pTarget - pDest;\n\n    // output as B\n    if (delta >= -(1 << 27) && (delta < (1 << 27)))\n    {\n        EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta));\n    }\n\n    // output as MOV x17, Target; BR x17 (BIG assumption that x17 isn't being used for anything!!)\n    else\n    {\n        EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget);\n        EmitInstruction(pDstInst, Br::AssembleBr(17));\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\nBYTE CDetourDis::CopyCbz(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    Cbz19& decoded = (Cbz19&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    BYTE* pTarget = pSource + decoded.Imm();\n    m_pbTarget = pTarget;\n    LONG64 delta = pTarget - pDest;\n    LONG64 delta4 = pTarget - (pDest + 4);\n\n    // output as CBZ/NZ\n    if (delta >= -(1 << 20) && delta < (1 << 20))\n    {\n        EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, (LONG)delta));\n    }\n\n    // output as CBNZ/Z <skip>; B\n    else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))\n    {\n        EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8));\n        EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4));\n    }\n\n    // output as MOV x17, Target; CBNZ/Z <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!)\n    else\n    {\n        EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget);\n        EmitInstruction(pDstInst, Cbz19::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8));\n        EmitInstruction(pDstInst, Br::AssembleBr(17));\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\nBYTE CDetourDis::CopyTbz(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    Tbz14& decoded = (Tbz14&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    BYTE* pTarget = pSource + decoded.Imm();\n    m_pbTarget = pTarget;\n    LONG64 delta = pTarget - pDest;\n    LONG64 delta4 = pTarget - (pDest + 4);\n\n    // output as TBZ/NZ\n    if (delta >= -(1 << 13) && delta < (1 << 13))\n    {\n        EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, decoded.s.Bit, (LONG)delta));\n    }\n\n    // output as TBNZ/Z <skip>; B\n    else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27)))\n    {\n        EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8));\n        EmitInstruction(pDstInst, Branch26::AssembleB((LONG)delta4));\n    }\n\n    // output as MOV x17, Target; TBNZ/Z <skip>; BR x17 (BIG assumption that x17 isn't being used for anything!!)\n    else\n    {\n        EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget);\n        EmitInstruction(pDstInst, Tbz14::Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8));\n        EmitInstruction(pDstInst, Br::AssembleBr(17));\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\nBYTE CDetourDis::CopyLdrLiteral(BYTE* pSource, BYTE* pDest, ULONG instruction)\n{\n    LdrLit19& decoded = (LdrLit19&)(instruction);\n    PULONG pDstInst = (PULONG)(pDest);\n\n    BYTE* pTarget = pSource + decoded.Imm();\n    LONG64 delta = pTarget - pDest;\n\n    // output as LDR\n    if (delta >= -(1 << 21) && delta < (1 << 21))\n    {\n        EmitInstruction(pDstInst, LdrLit19::Assemble(decoded.s.Size, decoded.s.FpNeon, decoded.s.Rt, (LONG)delta));\n    }\n\n    // output as move immediate\n    else if (decoded.s.FpNeon == 0)\n    {\n        UINT64 value = 0;\n        switch (decoded.s.Size)\n        {\n            case 0: value = *(ULONG*)pTarget;       break;\n            case 1: value = *(UINT64*)pTarget;   break;\n            case 2: value = *(LONG*)pTarget;        break;\n        }\n        EmitMovImmediate(pDstInst, decoded.s.Rt, value);\n    }\n\n    // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!)\n    else\n    {\n        EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget);\n        EmitInstruction(pDstInst, LdrFpNeonImm9::Assemble(2 + decoded.s.Size, decoded.s.Rt, 17, 0));\n    }\n\n    return (BYTE)((BYTE*)pDstInst - pDest);\n}\n\n\nPVOID WINAPI DetourCopyInstruction(_In_opt_ PVOID pDst,\n                                   _Inout_opt_ PVOID *ppDstPool,\n                                   _In_ PVOID pSrc,\n                                   _Out_opt_ PVOID *ppTarget,\n                                   _Out_opt_ LONG *plExtra)\n{\n    UNREFERENCED_PARAMETER(ppDstPool);\n\n    CDetourDis state;\n    return (PVOID)state.CopyInstruction((PBYTE)pDst,\n                                        (PBYTE)pSrc,\n                                        (PBYTE*)ppTarget,\n                                        plExtra);\n}\n\n#endif // DETOURS_ARM64\n\n#ifndef DETOURS_KERNEL\nBOOL WINAPI DetourSetCodeModule(_In_ HMODULE hModule,\n                                _In_ BOOL fLimitReferencesToModule)\n{\n#if defined(DETOURS_X64) || defined(DETOURS_X86)\n    PBYTE pbBeg = NULL;\n    PBYTE pbEnd = (PBYTE)~(ULONG_PTR)0;\n\n    if (hModule != NULL) {\n        ULONG cbModule = DetourGetModuleSize(hModule);\n\n        pbBeg = (PBYTE)hModule;\n        pbEnd = (PBYTE)hModule + cbModule;\n    }\n\n    return CDetourDis::SetCodeModule(pbBeg, pbEnd, fLimitReferencesToModule);\n#elif defined(DETOURS_ARM) || defined(DETOURS_ARM64) || defined(DETOURS_IA64)\n    (void)hModule;\n    (void)fLimitReferencesToModule;\n    return TRUE;\n#else\n#error unknown architecture (x86, x64, arm, arm64, ia64)\n#endif\n}\n#endif //!DETOURS_KERNEL\n\n//\n///////////////////////////////////////////////////////////////// End of File.\n"
  },
  {
    "path": "Detours/disolarm.cpp",
    "content": "#define DETOURS_ARM_OFFLINE_LIBRARY\n#include \"disasm.cpp\"\n"
  },
  {
    "path": "Detours/disolarm64.cpp",
    "content": "#define DETOURS_ARM64_OFFLINE_LIBRARY\n#include \"disasm.cpp\"\n"
  },
  {
    "path": "Detours/disolia64.cpp",
    "content": "#define DETOURS_IA64_OFFLINE_LIBRARY\n#include \"disasm.cpp\"\n"
  },
  {
    "path": "Detours/disolx64.cpp",
    "content": "#define DETOURS_X64_OFFLINE_LIBRARY\n#include \"disasm.cpp\"\n"
  },
  {
    "path": "Detours/disolx86.cpp",
    "content": "#define DETOURS_X86_OFFLINE_LIBRARY\n#include \"disasm.cpp\"\n"
  },
  {
    "path": "Detours/image.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Image manipulation functions (image.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n//  Used for for payloads, byways, and imports.\n//\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n\n#include <Veil.h>\n\n#if _MSC_VER >= 1310\n#pragma warning(push)\n#if _MSC_VER > 1400\n#pragma warning(disable:6102 6103) // /analyze warnings\n#endif\n#include <strsafe.h>\n#pragma warning(pop)\n#endif\n\n#if (_MSC_VER < 1299)\n#pragma warning(disable: 4710)\n#endif\n\n// #define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\nnamespace Detour\n{\n//////////////////////////////////////////////////////////////////////////////\n//\n#ifndef _STRSAFE_H_INCLUDED_\n_Must_inspect_result_\nstatic inline HRESULT StringCchLengthA(\n    _In_reads_or_z_(cchMax) LPCSTR psz,\n    _In_\n    _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax,\n    _Out_opt_\n    _Deref_out_range_(<, cchMax)\n    _Deref_out_range_(<=, _String_length_(psz))\n    _Out_ size_t* pcch)\n{\n    HRESULT hr = S_OK;\n    size_t cchMaxPrev = cchMax;\n\n    if (cchMax > 2147483647) {\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    while (cchMax && (*psz != '\\0')) {\n        psz++;\n        cchMax--;\n    }\n\n    if (cchMax == 0) {\n        // the string is longer than cchMax\n        hr = ERROR_INVALID_PARAMETER;\n    }\n\n    if (SUCCEEDED(hr) && pcch) {\n        *pcch = cchMaxPrev - cchMax;\n    }\n\n    return hr;\n}\n\n_Must_inspect_result_\nstatic inline HRESULT StringCchCopyA(\n    _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest,\n    _In_ size_t cchDest,\n    _In_ LPCSTR pszSrc)\n{\n    HRESULT hr = S_OK;\n\n    if (cchDest == 0) {\n        // can not null terminate a zero-byte dest buffer\n        hr = ERROR_INVALID_PARAMETER;\n    }\n    else {\n        while (cchDest && (*pszSrc != '\\0')) {\n            *pszDest++ = *pszSrc++;\n            cchDest--;\n        }\n\n        if (cchDest == 0) {\n            // we are going to truncate pszDest\n            pszDest--;\n            hr = ERROR_INVALID_PARAMETER;\n        }\n\n        *pszDest= '\\0';\n    }\n\n    return hr;\n}\n\n_Must_inspect_result_\nstatic inline HRESULT StringCchCatA(\n    _Out_writes_(cchDest) _Always_(_Post_z_) LPSTR pszDest,\n    _In_ size_t cchDest,\n    _In_ LPCSTR pszSrc)\n{\n    HRESULT hr;\n    size_t cchDestCurrent;\n\n    if (cchDest > 2147483647){\n        return ERROR_INVALID_PARAMETER;\n    }\n\n    hr = StringCchLengthA(pszDest, cchDest, &cchDestCurrent);\n\n    if (SUCCEEDED(hr) && cchDestCurrent < cchDest) {\n        hr = StringCchCopyA(pszDest + cchDestCurrent,\n                            cchDest - cchDestCurrent,\n                            pszSrc);\n    }\n\n    return hr;\n}\n\n#endif\n\n///////////////////////////////////////////////////////////////////////////////\n//\nclass CImageData\n{\n    friend class CImage;\n\npublic:\n    CImageData(PBYTE pbData, DWORD cbData);\n    ~CImageData();\n\n    PBYTE                   Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator);\n    PBYTE                   Find(REFGUID rguid, DWORD *pcbData);\n    PBYTE                   Set(REFGUID rguid, PBYTE pbData, DWORD cbData);\n\n    BOOL                    Delete(REFGUID rguid);\n    BOOL                    Purge();\n\n    BOOL                    IsEmpty()           { return m_cbData == 0; }\n    BOOL                    IsValid();\n\nprotected:\n    BOOL                    SizeTo(DWORD cbData);\n\nprotected:\n    _Field_size_(m_cbAlloc)\n    PBYTE                   m_pbData;\n    DWORD                   m_cbData;\n    DWORD                   m_cbAlloc;\n};\n\nclass CImageImportFile\n{\n    friend class CImage;\n    friend class CImageImportName;\n\npublic:\n    CImageImportFile();\n    ~CImageImportFile();\n\npublic:\n    CImageImportFile *      m_pNextFile;\n    BOOL                    m_fByway;\n\n    _Field_size_(m_nImportNames)\n    CImageImportName *      m_pImportNames;\n    DWORD                   m_nImportNames;\n\n    DWORD                   m_rvaOriginalFirstThunk;\n    DWORD                   m_rvaFirstThunk;\n\n    DWORD                   m_nForwarderChain;\n    LPCSTR                  m_pszOrig;\n    LPCSTR                  m_pszName;\n};\n\nclass CImageImportName\n{\n    friend class CImage;\n    friend class CImageImportFile;\n\npublic:\n    CImageImportName();\n    ~CImageImportName();\n\npublic:\n    WORD        m_nHint;\n    ULONG       m_nOrig;\n    ULONG       m_nOrdinal;\n    LPCSTR      m_pszOrig;\n    LPCSTR      m_pszName;\n};\n\nclass CImage\n{\n    friend class CImageThunks;\n    friend class CImageChars;\n    friend class CImageImportFile;\n    friend class CImageImportName;\n\npublic:\n    CImage();\n    ~CImage();\n\n    static CImage *         IsValid(PDETOUR_BINARY pBinary);\n\npublic:                                                 // File Functions\n    BOOL                    Read(HANDLE hFile);\n    BOOL                    Write(HANDLE hFile);\n    BOOL                    Close();\n\npublic:                                                 // Manipulation Functions\n    PBYTE                   DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator);\n    PBYTE                   DataFind(REFGUID rguid, DWORD *pcbData);\n    PBYTE                   DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData);\n    BOOL                    DataDelete(REFGUID rguid);\n    BOOL                    DataPurge();\n\n    BOOL                    EditImports(PVOID pContext,\n                                        PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,\n                                        PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,\n                                        PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,\n                                        PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback);\n\nprotected:\n    BOOL                    WriteFile(HANDLE hFile,\n                                      LPCVOID lpBuffer,\n                                      DWORD nNumberOfBytesToWrite,\n                                      LPDWORD lpNumberOfBytesWritten);\n    BOOL                    CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData);\n    BOOL                    ZeroFileData(HANDLE hFile, DWORD cbData);\n    BOOL                    AlignFileData(HANDLE hFile);\n\n    BOOL                    SizeOutputBuffer(DWORD cbData);\n    PBYTE                   AllocateOutput(DWORD cbData, DWORD *pnVirtAddr);\n\n    PVOID                   RvaToVa(ULONG_PTR nRva);\n    DWORD                   RvaToFileOffset(DWORD nRva);\n\n    DWORD                   FileAlign(DWORD nAddr);\n    DWORD                   SectionAlign(DWORD nAddr);\n\n    BOOL                    CheckImportsNeeded(DWORD *pnTables,\n                                               DWORD *pnThunks,\n                                               DWORD *pnChars);\n\n    CImageImportFile *      NewByway(_In_ LPCSTR pszName);\n\nprivate:\n    DWORD                   m_dwValidSignature = 0;\n    CImageData *            m_pImageData = NULL;               // Read & Write\n\n    HANDLE                  m_hMap = NULL;                     // Read & Write\n    PBYTE                   m_pMap = NULL;                     // Read & Write\n\n    DWORD                   m_nNextFileAddr = 0;            // Write\n    DWORD                   m_nNextVirtAddr = 0;            // Write\n\n    IMAGE_DOS_HEADER        m_DosHeader = { 0 };                // Read & Write\n    IMAGE_NT_HEADERS        m_NtHeader = { 0 };                 // Read & Write\n    IMAGE_SECTION_HEADER    m_SectionHeaders[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] = { 0 };\n\n    DWORD                   m_nPrePE = 0;\n    DWORD                   m_cbPrePE = 0;\n    DWORD                   m_cbPostPE = 0;\n\n    DWORD                   m_nPeOffset = 0;\n    DWORD                   m_nSectionsOffset = 0;\n    DWORD                   m_nExtraOffset = 0;\n    DWORD                   m_nFileSize = 0;\n\n    DWORD                   m_nOutputVirtAddr = 0;\n    DWORD                   m_nOutputVirtSize = 0;\n    DWORD                   m_nOutputFileAddr = 0;\n\n    _Field_size_(m_cbOutputBuffer)\n    PBYTE                   m_pbOutputBuffer = NULL;\n    DWORD                   m_cbOutputBuffer = 0;\n\n    CImageImportFile *      m_pImportFiles = NULL;\n    DWORD                   m_nImportFiles = 0;\n\n    BOOL                    m_fHadDetourSection = FALSE;\n\nprivate:\n    enum {\n        DETOUR_IMAGE_VALID_SIGNATURE = 0xfedcba01,      // \"Dtr\\0\"\n    };\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstatic BYTE s_rbDosCode[0x10] = {\n    0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD,\n    0x21,0xB8,0x01,0x4C,0xCD,0x21,'*','*'\n};\n\nstatic inline DWORD Max(DWORD a, DWORD b)\n{\n    return a > b ? a : b;\n}\n\nstatic inline DWORD Align(DWORD a, DWORD size)\n{\n    size--;\n    return (a + size) & ~size;\n}\n\nstatic inline DWORD QuadAlign(DWORD a)\n{\n    return Align(a, 8);\n}\n\nstatic LPCSTR DuplicateString(_In_ LPCSTR pszIn)\n{\n    if (pszIn == NULL) {\n        return NULL;\n    }\n\n    size_t cch;\n    HRESULT hr = StringCchLengthA(pszIn, 8192, &cch);\n    if (FAILED(hr)) {\n        SetLastError(ERROR_INVALID_PARAMETER);\n        return NULL;\n    }\n\n    PCHAR pszOut = new NOTHROW CHAR [cch + 1];\n    if (pszOut == NULL) {\n        SetLastError(ERROR_OUTOFMEMORY);\n        return NULL;\n    }\n\n    hr = StringCchCopyA(pszOut, cch + 1, pszIn);\n    if (FAILED(hr)) {\n        delete[] pszOut;\n        return NULL;\n    }\n\n    return pszOut;\n}\n\nstatic VOID ReleaseString(_In_opt_ LPCSTR psz)\n{\n    if (psz != NULL) {\n        delete[] psz;\n    }\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nCImageImportFile::CImageImportFile()\n{\n    m_pNextFile = NULL;\n    m_fByway = FALSE;\n\n    m_pImportNames = NULL;\n    m_nImportNames = 0;\n\n    m_rvaOriginalFirstThunk = 0;\n    m_rvaFirstThunk = 0;\n\n    m_nForwarderChain = (UINT)0;\n    m_pszName = NULL;\n    m_pszOrig = NULL;\n}\n\nCImageImportFile::~CImageImportFile()\n{\n    if (m_pNextFile) {\n        delete m_pNextFile;\n        m_pNextFile = NULL;\n    }\n    if (m_pImportNames) {\n        delete[] m_pImportNames;\n        m_pImportNames = NULL;\n        m_nImportNames = 0;\n    }\n    if (m_pszName) {\n        delete[] m_pszName;\n        m_pszName = NULL;\n    }\n    if (m_pszOrig) {\n        delete[] m_pszOrig;\n        m_pszOrig = NULL;\n    }\n}\n\nCImageImportName::CImageImportName()\n{\n    m_nOrig = 0;\n    m_nOrdinal = 0;\n    m_nHint = 0;\n    m_pszName = NULL;\n    m_pszOrig = NULL;\n}\n\nCImageImportName::~CImageImportName()\n{\n    if (m_pszName) {\n        delete[] m_pszName;\n        m_pszName = NULL;\n    }\n    if (m_pszOrig) {\n        delete[] m_pszOrig;\n        m_pszOrig = NULL;\n    }\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nCImageData::CImageData(PBYTE pbData, DWORD cbData)\n{\n    m_pbData = pbData;\n    m_cbData = cbData;\n    m_cbAlloc = 0;\n}\n\nCImageData::~CImageData()\n{\n    IsValid();\n\n    if (m_cbAlloc == 0) {\n        m_pbData = NULL;\n    }\n    if (m_pbData) {\n        delete[] m_pbData;\n        m_pbData = NULL;\n    }\n    m_cbData = 0;\n    m_cbAlloc = 0;\n}\n\nBOOL CImageData::SizeTo(DWORD cbData)\n{\n    IsValid();\n\n    if (cbData <= m_cbAlloc) {\n        return TRUE;\n    }\n\n    PBYTE pbNew = new NOTHROW BYTE [cbData];\n    if (pbNew == NULL) {\n        SetLastError(ERROR_OUTOFMEMORY);\n        return FALSE;\n    }\n\n    if (m_pbData) {\n        CopyMemory(pbNew, m_pbData, m_cbData);\n        if (m_cbAlloc > 0) {\n            delete[] m_pbData;\n        }\n        m_pbData = NULL;\n    }\n    m_pbData = pbNew;\n    m_cbAlloc = cbData;\n\n    IsValid();\n\n    return TRUE;\n}\n\nBOOL CImageData::Purge()\n{\n    m_cbData = 0;\n\n    IsValid();\n\n    return TRUE;\n}\n\nBOOL CImageData::IsValid()\n{\n    if (m_pbData == NULL) {\n        return TRUE;\n    }\n\n    PBYTE pbBeg = m_pbData;\n    PBYTE pbEnd = m_pbData + m_cbData;\n\n    for (PBYTE pbIter = pbBeg; pbIter < pbEnd;) {\n        PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)pbIter;\n\n        if (pRecord->cbBytes < sizeof(DETOUR_SECTION_RECORD)) {\n            return FALSE;\n        }\n        if (pRecord->nReserved != 0) {\n            return FALSE;\n        }\n\n        pbIter += pRecord->cbBytes;\n    }\n    return TRUE;\n}\n\nPBYTE CImageData::Enumerate(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator)\n{\n    IsValid();\n\n    if (pnIterator == NULL ||\n        m_cbData < *pnIterator + sizeof(DETOUR_SECTION_RECORD)) {\n\n        if (pcbData) {\n            *pcbData = 0;\n        }\n        if (pGuid) {\n            ZeroMemory(pGuid, sizeof(*pGuid));\n        }\n        return NULL;\n    }\n\n    PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + *pnIterator);\n\n    if (pGuid) {\n        *pGuid = pRecord->guid;\n    }\n    if (pcbData) {\n        *pcbData = pRecord->cbBytes - sizeof(DETOUR_SECTION_RECORD);\n    }\n    *pnIterator = (LONG)(((PBYTE)pRecord - m_pbData) + pRecord->cbBytes);\n\n    return (PBYTE)(pRecord + 1);\n}\n\nPBYTE CImageData::Find(REFGUID rguid, DWORD *pcbData)\n{\n    IsValid();\n\n    DWORD cbBytes = sizeof(DETOUR_SECTION_RECORD);\n    for (DWORD nOffset = 0; nOffset < m_cbData; nOffset += cbBytes) {\n        PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + nOffset);\n\n        cbBytes = pRecord->cbBytes;\n        if (cbBytes > m_cbData) {\n            break;\n        }\n        if (cbBytes < sizeof(DETOUR_SECTION_RECORD)) {\n            continue;\n        }\n\n        if (pRecord->guid.Data1 == rguid.Data1 &&\n            pRecord->guid.Data2 == rguid.Data2 &&\n            pRecord->guid.Data3 == rguid.Data3 &&\n            pRecord->guid.Data4[0] == rguid.Data4[0] &&\n            pRecord->guid.Data4[1] == rguid.Data4[1] &&\n            pRecord->guid.Data4[2] == rguid.Data4[2] &&\n            pRecord->guid.Data4[3] == rguid.Data4[3] &&\n            pRecord->guid.Data4[4] == rguid.Data4[4] &&\n            pRecord->guid.Data4[5] == rguid.Data4[5] &&\n            pRecord->guid.Data4[6] == rguid.Data4[6] &&\n            pRecord->guid.Data4[7] == rguid.Data4[7]) {\n\n            *pcbData = cbBytes - sizeof(DETOUR_SECTION_RECORD);\n            return (PBYTE)(pRecord + 1);\n        }\n    }\n\n    if (pcbData) {\n        *pcbData = 0;\n    }\n    return NULL;\n}\n\nBOOL CImageData::Delete(REFGUID rguid)\n{\n    IsValid();\n\n    PBYTE pbFound = NULL;\n    DWORD cbFound = 0;\n\n    pbFound = Find(rguid, &cbFound);\n    if (pbFound == NULL) {\n        SetLastError(ERROR_MOD_NOT_FOUND);\n        return FALSE;\n    }\n\n    pbFound -= sizeof(DETOUR_SECTION_RECORD);\n    cbFound += sizeof(DETOUR_SECTION_RECORD);\n\n    PBYTE pbRestData = pbFound + cbFound;\n    DWORD cbRestData = m_cbData - (LONG)(pbRestData - m_pbData);\n\n    if (cbRestData) {\n        MoveMemory(pbFound, pbRestData, cbRestData);\n    }\n    m_cbData -= cbFound;\n\n    IsValid();\n    return TRUE;\n}\n\nPBYTE CImageData::Set(REFGUID rguid, PBYTE pbData, DWORD cbData)\n{\n    IsValid();\n    Delete(rguid);\n\n    DWORD cbAlloc = QuadAlign(cbData);\n\n    if (!SizeTo(m_cbData + cbAlloc + sizeof(DETOUR_SECTION_RECORD))) {\n        return NULL;\n    }\n\n    PDETOUR_SECTION_RECORD pRecord = (PDETOUR_SECTION_RECORD)(m_pbData + m_cbData);\n    pRecord->cbBytes = cbAlloc + sizeof(DETOUR_SECTION_RECORD);\n    pRecord->nReserved = 0;\n    pRecord->guid = rguid;\n\n    PBYTE pbDest = (PBYTE)(pRecord + 1);\n    if (pbData) {\n        CopyMemory(pbDest, pbData, cbData);\n        if (cbData < cbAlloc) {\n            ZeroMemory(pbDest + cbData, cbAlloc - cbData);\n        }\n    }\n    else {\n        if (cbAlloc > 0) {\n            ZeroMemory(pbDest, cbAlloc);\n        }\n    }\n\n    m_cbData += cbAlloc + sizeof(DETOUR_SECTION_RECORD);\n\n    IsValid();\n    return pbDest;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nclass CImageThunks\n{\nprivate:\n    CImage *            m_pImage;\n    PIMAGE_THUNK_DATA   m_pThunks;\n    DWORD               m_nThunks;\n    DWORD               m_nThunksMax;\n    DWORD               m_nThunkVirtAddr;\n\npublic:\n    CImageThunks(CImage *pImage, DWORD nThunksMax, DWORD *pnAddr)\n    {\n        m_pImage = pImage;\n        m_nThunks = 0;\n        m_nThunksMax = nThunksMax;\n        m_pThunks = (PIMAGE_THUNK_DATA)\n            m_pImage->AllocateOutput(sizeof(IMAGE_THUNK_DATA) * nThunksMax,\n                                     &m_nThunkVirtAddr);\n        *pnAddr = m_nThunkVirtAddr;\n    }\n\n    PIMAGE_THUNK_DATA Current(DWORD *pnVirtAddr)\n    {\n        if (m_nThunksMax > 1) {\n            *pnVirtAddr = m_nThunkVirtAddr;\n            return m_pThunks;\n        }\n        *pnVirtAddr = 0;\n        return NULL;\n    }\n\n    PIMAGE_THUNK_DATA Allocate(ULONG_PTR nData, DWORD *pnVirtAddr)\n    {\n        if (m_nThunks < m_nThunksMax) {\n            *pnVirtAddr = m_nThunkVirtAddr;\n\n            m_nThunks++;\n            m_nThunkVirtAddr += sizeof(IMAGE_THUNK_DATA);\n            m_pThunks->u1.Ordinal = nData;\n            return m_pThunks++;\n        }\n        *pnVirtAddr = 0;\n        return NULL;\n    }\n\n    DWORD   Size()\n    {\n        return m_nThunksMax * sizeof(IMAGE_THUNK_DATA);\n    }\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\nclass CImageChars\n{\nprivate:\n    CImage *        m_pImage = NULL;\n    PCHAR           m_pChars = NULL;\n    DWORD           m_nChars = 0;\n    DWORD           m_nCharsMax = 0;\n    DWORD           m_nCharVirtAddr = 0;\n\npublic:\n    CImageChars(CImage *pImage, _In_ DWORD nCharsMax, _Out_ DWORD *pnAddr)\n    {\n        m_pImage = pImage;\n        m_nChars = 0;\n        m_nCharsMax = nCharsMax;\n        m_pChars = (PCHAR)m_pImage->AllocateOutput(nCharsMax, &m_nCharVirtAddr);\n        *pnAddr = m_nCharVirtAddr;\n    }\n\n    LPCSTR Allocate(_In_ LPCSTR pszString, _Out_ DWORD *pnVirtAddr)\n    {\n        DWORD nLen = (DWORD)strlen(pszString) + 1;\n        nLen += (nLen & 1);\n\n        if (m_nChars + nLen > m_nCharsMax) {\n            *pnVirtAddr = 0;\n            return NULL;\n        }\n\n        *pnVirtAddr = m_nCharVirtAddr;\n        HRESULT hrRet = StringCchCopyA(m_pChars, m_nCharsMax, pszString);\n\n        if (FAILED(hrRet)) {\n            return NULL;\n        }\n\n        pszString = m_pChars;\n\n        m_pChars += nLen;\n        m_nChars += nLen;\n        m_nCharVirtAddr += nLen;\n\n        return pszString;\n    }\n\n    LPCSTR Allocate(_In_ LPCSTR pszString, _In_ DWORD nHint, _Out_ DWORD *pnVirtAddr)\n    {\n        DWORD nLen = (DWORD)strlen(pszString) + 1 + sizeof(USHORT);\n        nLen += (nLen & 1);\n\n        if (m_nChars + nLen > m_nCharsMax) {\n            *pnVirtAddr = 0;\n            return NULL;\n        }\n\n        *pnVirtAddr = m_nCharVirtAddr;\n        *(USHORT *)m_pChars = (USHORT)nHint;\n\n        HRESULT hrRet = StringCchCopyA(m_pChars + sizeof(USHORT), m_nCharsMax, pszString);\n        if (FAILED(hrRet)) {\n            return NULL;\n        }\n\n        pszString = m_pChars + sizeof(USHORT);\n\n        m_pChars += nLen;\n        m_nChars += nLen;\n        m_nCharVirtAddr += nLen;\n\n        return pszString;\n    }\n\n    DWORD Size()\n    {\n        return m_nChars;\n    }\n};\n\n//////////////////////////////////////////////////////////////////////////////\n//\nCImage * CImage::IsValid(PDETOUR_BINARY pBinary)\n{\n    if (pBinary) {\n        CImage *pImage = (CImage *)pBinary;\n\n        if (pImage->m_dwValidSignature == DETOUR_IMAGE_VALID_SIGNATURE) {\n            return pImage;\n        }\n    }\n    SetLastError(ERROR_INVALID_HANDLE);\n    return NULL;\n}\n\nCImage::CImage()\n{\n    m_dwValidSignature = (DWORD)DETOUR_IMAGE_VALID_SIGNATURE;\n\n    m_hMap = NULL;\n    m_pMap = NULL;\n\n    m_nPeOffset = 0;\n    m_nSectionsOffset = 0;\n\n    m_pbOutputBuffer = NULL;\n    m_cbOutputBuffer = 0;\n\n    m_pImageData = NULL;\n\n    m_pImportFiles = NULL;\n    m_nImportFiles = 0;\n\n    m_fHadDetourSection = FALSE;\n}\n\nCImage::~CImage()\n{\n    Close();\n    m_dwValidSignature = 0;\n}\n\nBOOL CImage::Close()\n{\n    if (m_pImportFiles) {\n        delete m_pImportFiles;\n        m_pImportFiles = NULL;\n        m_nImportFiles = 0;\n    }\n\n    if (m_pImageData) {\n        delete m_pImageData;\n        m_pImageData = NULL;\n    }\n\n    if (m_pMap != NULL) {\n        UnmapViewOfFile(m_pMap);\n        m_pMap = NULL;\n    }\n\n    if (m_hMap) {\n        CloseHandle(m_hMap);\n        m_hMap = NULL;\n    }\n\n    if (m_pbOutputBuffer) {\n        delete[] m_pbOutputBuffer;\n        m_pbOutputBuffer = NULL;\n        m_cbOutputBuffer = 0;\n    }\n    return TRUE;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPBYTE CImage::DataEnum(GUID *pGuid, DWORD *pcbData, DWORD *pnIterator)\n{\n    if (m_pImageData == NULL) {\n        return NULL;\n    }\n    return m_pImageData->Enumerate(pGuid, pcbData, pnIterator);\n}\n\nPBYTE CImage::DataFind(REFGUID rguid, DWORD *pcbData)\n{\n    if (m_pImageData == NULL) {\n        return NULL;\n    }\n    return m_pImageData->Find(rguid, pcbData);\n}\n\nPBYTE CImage::DataSet(REFGUID rguid, PBYTE pbData, DWORD cbData)\n{\n    if (m_pImageData == NULL) {\n        return NULL;\n    }\n    return m_pImageData->Set(rguid, pbData, cbData);\n}\n\nBOOL CImage::DataDelete(REFGUID rguid)\n{\n    if (m_pImageData == NULL) {\n        return FALSE;\n    }\n    return m_pImageData->Delete(rguid);\n}\n\nBOOL CImage::DataPurge()\n{\n    if (m_pImageData == NULL) {\n        return TRUE;\n    }\n    return m_pImageData->Purge();\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nBOOL CImage::SizeOutputBuffer(DWORD cbData)\n{\n    if (m_cbOutputBuffer < cbData) {\n        if (cbData < 1024) {//65536\n            cbData = 1024;\n        }\n        cbData = FileAlign(cbData);\n\n        PBYTE pOutput = new NOTHROW BYTE [cbData];\n        if (pOutput == NULL) {\n            SetLastError(ERROR_OUTOFMEMORY);\n            return FALSE;\n        }\n\n        if (m_pbOutputBuffer) {\n            CopyMemory(pOutput, m_pbOutputBuffer, m_cbOutputBuffer);\n\n            delete[] m_pbOutputBuffer;\n            m_pbOutputBuffer = NULL;\n        }\n\n        ZeroMemory(pOutput + m_cbOutputBuffer, cbData - m_cbOutputBuffer),\n\n        m_pbOutputBuffer = pOutput;\n        m_cbOutputBuffer = cbData;\n    }\n    return TRUE;\n}\n\nPBYTE CImage::AllocateOutput(DWORD cbData, DWORD *pnVirtAddr)\n{\n    cbData = QuadAlign(cbData);\n\n    PBYTE pbData = m_pbOutputBuffer + m_nOutputVirtSize;\n\n    *pnVirtAddr = m_nOutputVirtAddr + m_nOutputVirtSize;\n    m_nOutputVirtSize += cbData;\n\n    if (m_nOutputVirtSize > m_cbOutputBuffer) {\n        SetLastError(ERROR_OUTOFMEMORY);\n        return NULL;\n    }\n\n    ZeroMemory(pbData, cbData);\n\n    return pbData;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nDWORD CImage::FileAlign(DWORD nAddr)\n{\n    return Align(nAddr, m_NtHeader.OptionalHeader.FileAlignment);\n}\n\nDWORD CImage::SectionAlign(DWORD nAddr)\n{\n    return Align(nAddr, m_NtHeader.OptionalHeader.SectionAlignment);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPVOID CImage::RvaToVa(ULONG_PTR nRva)\n{\n    if (nRva == 0) {\n        return NULL;\n    }\n\n    for (DWORD n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        DWORD vaStart = m_SectionHeaders[n].VirtualAddress;\n        DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;\n\n        if (nRva >= vaStart && nRva < vaEnd) {\n            return (PBYTE)m_pMap\n                + m_SectionHeaders[n].PointerToRawData\n                + nRva - m_SectionHeaders[n].VirtualAddress;\n        }\n    }\n    return NULL;\n}\n\nDWORD CImage::RvaToFileOffset(DWORD nRva)\n{\n    DWORD n;\n    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        DWORD vaStart = m_SectionHeaders[n].VirtualAddress;\n        DWORD vaEnd = vaStart + m_SectionHeaders[n].SizeOfRawData;\n\n        if (nRva >= vaStart && nRva < vaEnd) {\n            return m_SectionHeaders[n].PointerToRawData\n                + nRva - m_SectionHeaders[n].VirtualAddress;\n        }\n    }\n    return 0;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nBOOL CImage::WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,\n                       LPDWORD lpNumberOfBytesWritten)\n{\n    return ::WriteFile(hFile,\n                       lpBuffer,\n                       nNumberOfBytesToWrite,\n                       lpNumberOfBytesWritten,\n                       NULL);\n}\n\n\nBOOL CImage::CopyFileData(HANDLE hFile, DWORD nOldPos, DWORD cbData)\n{\n    DWORD cbDone = 0;\n    return WriteFile(hFile, m_pMap + nOldPos, cbData, &cbDone);\n}\n\nBOOL CImage::ZeroFileData(HANDLE hFile, DWORD cbData)\n{\n    if (!SizeOutputBuffer(4096)) {\n        return FALSE;\n    }\n\n    ZeroMemory(m_pbOutputBuffer, 4096);\n\n    for (DWORD cbLeft = cbData; cbLeft > 0;) {\n        DWORD cbStep = cbLeft > sizeof(m_pbOutputBuffer)\n            ? sizeof(m_pbOutputBuffer) : cbLeft;\n        DWORD cbDone = 0;\n\n        if (!WriteFile(hFile, m_pbOutputBuffer, cbStep, &cbDone)) {\n            return FALSE;\n        }\n        if (cbDone == 0) {\n            break;\n        }\n\n        cbLeft -= cbDone;\n    }\n    return TRUE;\n}\n\nBOOL CImage::AlignFileData(HANDLE hFile)\n{\n    DWORD nLastFileAddr = m_nNextFileAddr;\n\n    m_nNextFileAddr = FileAlign(m_nNextFileAddr);\n    m_nNextVirtAddr = SectionAlign(m_nNextVirtAddr);\n\n    if (hFile != INVALID_HANDLE_VALUE) {\n        if (m_nNextFileAddr > nLastFileAddr) {\n            if (SetFilePointer(hFile, nLastFileAddr, NULL, FILE_BEGIN) == ~0u) {\n                return FALSE;\n            }\n            return ZeroFileData(hFile, m_nNextFileAddr - nLastFileAddr);\n        }\n    }\n    return TRUE;\n}\n\nBOOL CImage::Read(HANDLE hFile)\n{\n    DWORD n;\n    PBYTE pbData = NULL;\n    DWORD cbData = 0;\n\n    if (hFile == INVALID_HANDLE_VALUE) {\n        SetLastError(ERROR_INVALID_HANDLE);\n        return FALSE;\n    }\n\n    ///////////////////////////////////////////////////////// Create mapping.\n    //\n    m_nFileSize = GetFileSize(hFile, NULL);\n    if (m_nFileSize == (DWORD)-1) {\n        return FALSE;\n    }\n\n    m_hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL);\n    if (m_hMap == NULL) {\n        return FALSE;\n    }\n\n    m_pMap = (PBYTE)MapViewOfFileEx(m_hMap, FILE_MAP_READ, 0, 0, 0, NULL);\n    if (m_pMap == NULL) {\n        return FALSE;\n    }\n\n    ////////////////////////////////////////////////////// Process DOS Header.\n    //\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)m_pMap;\n    if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n        SetLastError(ERROR_BAD_EXE_FORMAT);\n        return FALSE;\n    }\n    m_nPeOffset = pDosHeader->e_lfanew;\n    m_nPrePE = 0;\n    m_cbPrePE = QuadAlign(pDosHeader->e_lfanew);\n\n    if (m_nPeOffset > m_nFileSize ||\n        m_nPeOffset + sizeof(m_NtHeader) > m_nFileSize) {\n\n        SetLastError(ERROR_BAD_EXE_FORMAT);\n        return FALSE;\n    }\n\n    CopyMemory(&m_DosHeader, m_pMap + m_nPrePE, sizeof(m_DosHeader));\n\n    /////////////////////////////////////////////////////// Process PE Header.\n    //\n    CopyMemory(&m_NtHeader, m_pMap + m_nPeOffset, sizeof(m_NtHeader));\n    if (m_NtHeader.Signature != IMAGE_NT_SIGNATURE) {\n        SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n        return FALSE;\n    }\n    if (m_NtHeader.FileHeader.SizeOfOptionalHeader == 0) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return FALSE;\n    }\n    m_nSectionsOffset = m_nPeOffset\n        + sizeof(m_NtHeader.Signature)\n        + sizeof(m_NtHeader.FileHeader)\n        + m_NtHeader.FileHeader.SizeOfOptionalHeader;\n\n    ///////////////////////////////////////////////// Process Section Headers.\n    //\n    if (m_NtHeader.FileHeader.NumberOfSections > ARRAYSIZE(m_SectionHeaders)) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return FALSE;\n    }\n    CopyMemory(&m_SectionHeaders,\n               m_pMap + m_nSectionsOffset,\n               sizeof(m_SectionHeaders[0]) * m_NtHeader.FileHeader.NumberOfSections);\n\n    /////////////////////////////////////////////////// Parse .detour Section.\n    //\n    DWORD rvaOriginalImageDirectory = 0;\n    DWORD rvaDetourBeg = 0;\n    DWORD rvaDetourEnd = 0;\n\n    _Analysis_assume_(m_NtHeader.FileHeader.NumberOfSections <= ARRAYSIZE(m_SectionHeaders));\n\n    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        if (strcmp((PCHAR)m_SectionHeaders[n].Name, \".detour\") == 0) {\n            DETOUR_SECTION_HEADER dh;\n            CopyMemory(&dh,\n                       m_pMap + m_SectionHeaders[n].PointerToRawData,\n                       sizeof(dh));\n\n            rvaOriginalImageDirectory = dh.nOriginalImportVirtualAddress;\n            if (dh.cbPrePE != 0) {\n                m_nPrePE = m_SectionHeaders[n].PointerToRawData + sizeof(dh);\n                m_cbPrePE = dh.cbPrePE;\n            }\n            rvaDetourBeg = m_SectionHeaders[n].VirtualAddress;\n            rvaDetourEnd = rvaDetourBeg + m_SectionHeaders[n].SizeOfRawData;\n        }\n    }\n\n    //////////////////////////////////////////////////////// Get Import Table.\n    //\n    DWORD rvaImageDirectory = m_NtHeader.OptionalHeader\n        .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;\n    PIMAGE_IMPORT_DESCRIPTOR iidp\n        = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaImageDirectory);\n    PIMAGE_IMPORT_DESCRIPTOR oidp\n        = (PIMAGE_IMPORT_DESCRIPTOR)RvaToVa(rvaOriginalImageDirectory);\n\n    if (oidp == NULL) {\n        oidp = iidp;\n    }\n    if (iidp == NULL || oidp == NULL) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return FALSE;\n    }\n\n    DWORD nFiles = 0;\n    for (; iidp[nFiles].OriginalFirstThunk != 0 || iidp[nFiles].FirstThunk != 0; nFiles++) {\n    }\n\n    CImageImportFile **ppLastFile = &m_pImportFiles;\n    m_pImportFiles = NULL;\n\n    for (n = 0; n < nFiles; n++, iidp++) {\n        ULONG_PTR rvaName = iidp->Name;\n        PCHAR pszName = (PCHAR)RvaToVa(rvaName);\n        if (pszName == NULL) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            goto fail;\n        }\n\n        CImageImportFile *pImportFile = new NOTHROW CImageImportFile;\n        if (pImportFile == NULL) {\n            SetLastError(ERROR_OUTOFMEMORY);\n            goto fail;\n        }\n\n        *ppLastFile = pImportFile;\n        ppLastFile = &pImportFile->m_pNextFile;\n        m_nImportFiles++;\n\n        pImportFile->m_pszName = DuplicateString(pszName);\n        if (pImportFile->m_pszName == NULL) {\n            goto fail;\n        }\n\n        pImportFile->m_rvaOriginalFirstThunk = iidp->OriginalFirstThunk;\n        pImportFile->m_rvaFirstThunk = iidp->FirstThunk;\n        pImportFile->m_nForwarderChain = iidp->ForwarderChain;\n        pImportFile->m_pImportNames = NULL;\n        pImportFile->m_nImportNames = 0;\n        pImportFile->m_fByway = FALSE;\n\n        if ((ULONG)iidp->FirstThunk >= rvaDetourBeg &&\n            (ULONG)iidp->FirstThunk < rvaDetourEnd) {\n\n            pImportFile->m_pszOrig = NULL;\n            pImportFile->m_fByway = TRUE;\n            continue;\n        }\n\n        rvaName = oidp->Name;\n        pszName = (PCHAR)RvaToVa(rvaName);\n        if (pszName == NULL) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            goto fail;\n        }\n        pImportFile->m_pszOrig = DuplicateString(pszName);\n        if (pImportFile->m_pszOrig == NULL) {\n            goto fail;\n        }\n\n        DWORD rvaThunk = iidp->OriginalFirstThunk;\n        if( !rvaThunk ) {\n            rvaThunk = iidp->FirstThunk;\n        }\n        PIMAGE_THUNK_DATA pAddrThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk);\n        rvaThunk = oidp->OriginalFirstThunk;\n        if( !rvaThunk ) {\n            rvaThunk = oidp->FirstThunk;\n        }\n        PIMAGE_THUNK_DATA pLookThunk = (PIMAGE_THUNK_DATA)RvaToVa(rvaThunk);\n\n        DWORD nNames = 0;\n        if (pAddrThunk) {\n            for (; pAddrThunk[nNames].u1.Ordinal; nNames++) {\n            }\n        }\n\n        if (pAddrThunk && nNames) {\n            pImportFile->m_nImportNames = nNames;\n            pImportFile->m_pImportNames = new NOTHROW CImageImportName [nNames];\n            if (pImportFile->m_pImportNames == NULL) {\n                SetLastError(ERROR_OUTOFMEMORY);\n                goto fail;\n            }\n\n            CImageImportName *pImportName = &pImportFile->m_pImportNames[0];\n\n            for (DWORD f = 0; f < nNames; f++, pImportName++) {\n                pImportName->m_nOrig = 0;\n                pImportName->m_nOrdinal = 0;\n                pImportName->m_nHint = 0;\n                pImportName->m_pszName = NULL;\n                pImportName->m_pszOrig = NULL;\n\n                rvaName = pAddrThunk[f].u1.Ordinal;\n                if (rvaName & IMAGE_ORDINAL_FLAG) {\n                    pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName);\n                    pImportName->m_nOrdinal = pImportName->m_nOrig;\n                }\n                else {\n                    PIMAGE_IMPORT_BY_NAME pName\n                        = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName);\n                    if (pName) {\n                        pImportName->m_nHint = pName->Hint;\n                        pImportName->m_pszName = DuplicateString((PCHAR)pName->Name);\n                        if (pImportName->m_pszName == NULL) {\n                            goto fail;\n                        }\n                    }\n\n                    rvaName = pLookThunk[f].u1.Ordinal;\n                    if (rvaName & IMAGE_ORDINAL_FLAG) {\n                        pImportName->m_nOrig = (ULONG)IMAGE_ORDINAL(rvaName);\n                        pImportName->m_nOrdinal = (ULONG)IMAGE_ORDINAL(rvaName);\n                    }\n                    else {\n                        pName = (PIMAGE_IMPORT_BY_NAME)RvaToVa(rvaName);\n                        if (pName) {\n                            pImportName->m_pszOrig\n                                = DuplicateString((PCHAR)pName->Name);\n                            if (pImportName->m_pszOrig == NULL) {\n                                goto fail;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n        oidp++;\n    }\n\n    ////////////////////////////////////////////////////////// Parse Sections.\n    //\n    m_nExtraOffset = 0;\n    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData +\n                             m_SectionHeaders[n].SizeOfRawData,\n                             m_nExtraOffset);\n\n        if (strcmp((PCHAR)m_SectionHeaders[n].Name, \".detour\") == 0) {\n            DETOUR_SECTION_HEADER dh;\n            CopyMemory(&dh,\n                       m_pMap + m_SectionHeaders[n].PointerToRawData,\n                       sizeof(dh));\n\n            if (dh.nDataOffset == 0) {\n                dh.nDataOffset = dh.cbHeaderSize;\n            }\n\n            cbData = dh.cbDataSize - dh.nDataOffset;\n            pbData = (m_pMap +\n                      m_SectionHeaders[n].PointerToRawData +\n                      dh.nDataOffset);\n\n            m_nExtraOffset = Max(m_SectionHeaders[n].PointerToRawData +\n                                 m_SectionHeaders[n].SizeOfRawData,\n                                 m_nExtraOffset);\n\n            m_NtHeader.FileHeader.NumberOfSections--;\n\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress\n                = dh.nOriginalImportVirtualAddress;\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size\n                = dh.nOriginalImportSize;\n\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress\n                = dh.nOriginalBoundImportVirtualAddress;\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size\n                = dh.nOriginalBoundImportSize;\n\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress\n                = dh.nOriginalIatVirtualAddress;\n            m_NtHeader.OptionalHeader\n                .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size\n                = dh.nOriginalIatSize;\n\n            m_NtHeader.OptionalHeader.CheckSum = 0;\n            m_NtHeader.OptionalHeader.SizeOfImage\n                = dh.nOriginalSizeOfImage;\n\n            m_fHadDetourSection = TRUE;\n        }\n    }\n\n    m_pImageData = new NOTHROW CImageData(pbData, cbData);\n    if (m_pImageData == NULL) {\n        SetLastError(ERROR_OUTOFMEMORY);\n    }\n    return TRUE;\n\nfail:\n    return FALSE;\n}\n\nstatic inline BOOL strneq(_In_ LPCSTR pszOne, _In_ LPCSTR pszTwo)\n{\n    if (pszOne == pszTwo) {\n        return FALSE;\n    }\n    if (!pszOne || !pszTwo) {\n        return TRUE;\n    }\n    return (strcmp(pszOne, pszTwo) != 0);\n}\n\nBOOL CImage::CheckImportsNeeded(DWORD *pnTables, DWORD *pnThunks, DWORD *pnChars)\n{\n    DWORD nTables = 0;\n    DWORD nThunks = 0;\n    DWORD nChars = 0;\n    BOOL fNeedDetourSection = FALSE;\n\n    for (CImageImportFile *pImportFile = m_pImportFiles;\n         pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {\n\n        nChars += (int)strlen(pImportFile->m_pszName) + 1;\n        nChars += nChars & 1;\n\n        if (pImportFile->m_fByway) {\n            fNeedDetourSection = TRUE;\n            nThunks++;\n        }\n        else {\n            if (!fNeedDetourSection &&\n                strneq(pImportFile->m_pszName, pImportFile->m_pszOrig)) {\n\n                fNeedDetourSection = TRUE;\n            }\n            for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) {\n                CImageImportName *pImportName = &pImportFile->m_pImportNames[n];\n\n                if (!fNeedDetourSection &&\n                    strneq(pImportName->m_pszName, pImportName->m_pszOrig)) {\n\n                    fNeedDetourSection = TRUE;\n                }\n\n                if (pImportName->m_pszName) {\n                    nChars += sizeof(WORD);             // Hint\n                    nChars += (int)strlen(pImportName->m_pszName) + 1;\n                    nChars += nChars & 1;\n                }\n                nThunks++;\n            }\n        }\n        nThunks++;\n        nTables++;\n    }\n    nTables++;\n\n    *pnTables = nTables;\n    *pnThunks = nThunks;\n    *pnChars = nChars;\n\n    return fNeedDetourSection;\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nCImageImportFile * CImage::NewByway(_In_ LPCSTR pszName)\n{\n    CImageImportFile *pImportFile = new NOTHROW CImageImportFile;\n    if (pImportFile == NULL) {\n        SetLastError(ERROR_OUTOFMEMORY);\n        goto fail;\n    }\n\n    pImportFile->m_pNextFile = NULL;\n    pImportFile->m_fByway = TRUE;\n\n    pImportFile->m_pszName = DuplicateString(pszName);\n    if (pImportFile->m_pszName == NULL) {\n        goto fail;\n    }\n\n    pImportFile->m_rvaOriginalFirstThunk = 0;\n    pImportFile->m_rvaFirstThunk = 0;\n    pImportFile->m_nForwarderChain = (UINT)0;\n    pImportFile->m_pImportNames = NULL;\n    pImportFile->m_nImportNames = 0;\n\n    m_nImportFiles++;\n    return pImportFile;\n\nfail:\n    if (pImportFile) {\n        delete pImportFile;\n        pImportFile = NULL;\n    }\n    return NULL;\n}\n\nBOOL CImage::EditImports(PVOID pContext,\n                         PF_DETOUR_BINARY_BYWAY_CALLBACK pfBywayCallback,\n                         PF_DETOUR_BINARY_FILE_CALLBACK pfFileCallback,\n                         PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbolCallback,\n                         PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommitCallback)\n{\n    CImageImportFile *pImportFile = NULL;\n    CImageImportFile **ppLastFile = &m_pImportFiles;\n\n    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);\n\n    while ((pImportFile = *ppLastFile) != NULL) {\n\n        if (pfBywayCallback != NULL) {\n            LPCSTR pszFile = NULL;\n            if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) {\n                goto fail;\n            }\n\n            if (pszFile != NULL) {\n                // Insert a new Byway.\n                CImageImportFile *pByway = NewByway(pszFile);\n                if (pByway == NULL) {\n                    return FALSE;\n                }\n\n                pByway->m_pNextFile = pImportFile;\n                *ppLastFile = pByway;\n                ppLastFile = &pByway->m_pNextFile;\n                continue;                               // Retry after Byway.\n            }\n        }\n\n        if (pImportFile->m_fByway) {\n            if (pfBywayCallback != NULL) {\n                LPCSTR pszFile = NULL;\n\n                if (!(*pfBywayCallback)(pContext, pImportFile->m_pszName, &pszFile)) {\n                    goto fail;\n                }\n\n                if (pszFile != NULL) {\n                    // Replace? Byway\n                    if (pszFile != pImportFile->m_pszName) {\n                        LPCSTR pszLast = pImportFile->m_pszName;\n                        pImportFile->m_pszName = DuplicateString(pszFile);\n                        ReleaseString(pszLast);\n\n                        if (pImportFile->m_pszName == NULL) {\n                            goto fail;\n                        }\n                    }\n                }\n                else {                                  // Delete Byway\n                    *ppLastFile = pImportFile->m_pNextFile;\n                    pImportFile->m_pNextFile = NULL;\n                    delete pImportFile;\n                    m_nImportFiles--;\n                    continue;                           // Retry after delete.\n                }\n            }\n        }\n        else {\n            if (pfFileCallback != NULL) {\n                LPCSTR pszFile = NULL;\n\n                if (!(*pfFileCallback)(pContext,\n                                       pImportFile->m_pszOrig,\n                                       pImportFile->m_pszName,\n                                       &pszFile)) {\n                    goto fail;\n                }\n\n                if (pszFile != NULL) {\n                    if (pszFile != pImportFile->m_pszName) {\n                        LPCSTR pszLast = pImportFile->m_pszName;\n                        pImportFile->m_pszName = DuplicateString(pszFile);\n                        ReleaseString(pszLast);\n\n                        if (pImportFile->m_pszName == NULL) {\n                            goto fail;\n                        }\n                    }\n                }\n            }\n\n            if (pfSymbolCallback != NULL) {\n                for (DWORD n = 0; n < pImportFile->m_nImportNames; n++) {\n                    CImageImportName *pImportName = &pImportFile->m_pImportNames[n];\n\n                    LPCSTR pszName = NULL;\n                    ULONG nOrdinal = 0;\n                    if (!(*pfSymbolCallback)(pContext,\n                                             pImportName->m_nOrig,\n                                             pImportName->m_nOrdinal,\n                                             &nOrdinal,\n                                             pImportName->m_pszOrig,\n                                             pImportName->m_pszName,\n                                             &pszName)) {\n                        goto fail;\n                    }\n\n                    if (pszName != NULL) {\n                        if (pszName != pImportName->m_pszName) {\n                            pImportName->m_nOrdinal = 0;\n\n                            LPCSTR pszLast = pImportName->m_pszName;\n                            pImportName->m_pszName = DuplicateString(pszName);\n                            ReleaseString(pszLast);\n\n                            if (pImportName->m_pszName == NULL) {\n                                goto fail;\n                            }\n                        }\n                    }\n                    else if (nOrdinal != 0) {\n                        pImportName->m_nOrdinal = nOrdinal;\n\n                        if (pImportName->m_pszName != NULL) {\n                            delete[] pImportName->m_pszName;\n                            pImportName->m_pszName = NULL;\n                        }\n                    }\n                }\n            }\n        }\n\n        ppLastFile = &pImportFile->m_pNextFile;\n        pImportFile = pImportFile->m_pNextFile;\n    }\n\n    for (;;) {\n        if (pfBywayCallback != NULL) {\n            LPCSTR pszFile = NULL;\n            if (!(*pfBywayCallback)(pContext, NULL, &pszFile)) {\n                goto fail;\n            }\n            if (pszFile != NULL) {\n                // Insert a new Byway.\n                CImageImportFile *pByway = NewByway(pszFile);\n                if (pByway == NULL) {\n                    return FALSE;\n                }\n\n                pByway->m_pNextFile = pImportFile;\n                *ppLastFile = pByway;\n                ppLastFile = &pByway->m_pNextFile;\n                continue;                               // Retry after Byway.\n            }\n        }\n        break;\n    }\n\n    if (pfCommitCallback != NULL) {\n        if (!(*pfCommitCallback)(pContext)) {\n            goto fail;\n        }\n    }\n\n    SetLastError(NO_ERROR);\n    return TRUE;\n\n  fail:\n    return FALSE;\n}\n\nBOOL CImage::Write(HANDLE hFile)\n{\n    DWORD cbDone;\n\n    if (hFile == INVALID_HANDLE_VALUE) {\n        SetLastError(ERROR_INVALID_HANDLE);\n        return FALSE;\n    }\n\n    m_nNextFileAddr = 0;\n    m_nNextVirtAddr = 0;\n\n    DWORD nTables = 0;\n    DWORD nThunks = 0;\n    DWORD nChars = 0;\n    BOOL fNeedDetourSection = CheckImportsNeeded(&nTables, &nThunks, &nChars);\n\n    //////////////////////////////////////////////////////////// Copy Headers.\n    //\n    if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {\n        return FALSE;\n    }\n    if (!CopyFileData(hFile, 0, m_NtHeader.OptionalHeader.SizeOfHeaders)) {\n        return FALSE;\n    }\n\n    if (fNeedDetourSection || !m_pImageData->IsEmpty()) {\n        // Replace the file's DOS header with our own.\n        m_nPeOffset = sizeof(m_DosHeader) + sizeof(s_rbDosCode);\n        m_nSectionsOffset = m_nPeOffset\n            + sizeof(m_NtHeader.Signature)\n            + sizeof(m_NtHeader.FileHeader)\n            + m_NtHeader.FileHeader.SizeOfOptionalHeader;\n        m_DosHeader.e_lfanew = m_nPeOffset;\n\n        if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {\n            return FALSE;\n        }\n        if (!WriteFile(hFile, &m_DosHeader, sizeof(m_DosHeader), &cbDone)) {\n            return FALSE;\n        }\n        if (!WriteFile(hFile, &s_rbDosCode, sizeof(s_rbDosCode), &cbDone)) {\n            return FALSE;\n        }\n    }\n    else {\n        // Restore the file's original DOS header.\n        if (m_nPrePE != 0) {\n            m_nPeOffset = m_cbPrePE;\n            m_nSectionsOffset = m_nPeOffset\n                + sizeof(m_NtHeader.Signature)\n                + sizeof(m_NtHeader.FileHeader)\n                + m_NtHeader.FileHeader.SizeOfOptionalHeader;\n            m_DosHeader.e_lfanew = m_nPeOffset;\n\n\n            if (SetFilePointer(hFile, 0, NULL, FILE_BEGIN) == ~0u) {\n                return FALSE;\n            }\n            if (!CopyFileData(hFile, m_nPrePE, m_cbPrePE)) {\n                return FALSE;\n            }\n        }\n    }\n\n    m_nNextFileAddr = m_NtHeader.OptionalHeader.SizeOfHeaders;\n    m_nNextVirtAddr = 0;\n    if (!AlignFileData(hFile)) {\n        return FALSE;\n    }\n\n    /////////////////////////////////////////////////////////// Copy Sections.\n    //\n    DWORD n = 0;\n    for (; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        if (m_SectionHeaders[n].SizeOfRawData) {\n            if (SetFilePointer(hFile,\n                               m_SectionHeaders[n].PointerToRawData,\n                               NULL, FILE_BEGIN) == ~0u) {\n                return FALSE;\n            }\n            if (!CopyFileData(hFile,\n                              m_SectionHeaders[n].PointerToRawData,\n                              m_SectionHeaders[n].SizeOfRawData)) {\n                return FALSE;\n            }\n        }\n        m_nNextFileAddr = Max(m_SectionHeaders[n].PointerToRawData +\n                              m_SectionHeaders[n].SizeOfRawData,\n                              m_nNextFileAddr);\n#if 0\n        m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +\n                              m_SectionHeaders[n].Misc.VirtualSize,\n                              m_nNextVirtAddr);\n#else\n        m_nNextVirtAddr = Max(m_SectionHeaders[n].VirtualAddress +\n                              (m_SectionHeaders[n].Misc.VirtualSize\n                               ? m_SectionHeaders[n].Misc.VirtualSize\n                               : SectionAlign(m_SectionHeaders[n].SizeOfRawData)),\n                              m_nNextVirtAddr);\n#endif\n\n        m_nExtraOffset = Max(m_nNextFileAddr, m_nExtraOffset);\n\n        if (!AlignFileData(hFile)) {\n            return FALSE;\n        }\n    }\n\n    if (fNeedDetourSection || !m_pImageData->IsEmpty()) {\n\n        if (m_NtHeader.FileHeader.NumberOfSections >= ARRAYSIZE(m_SectionHeaders)) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return FALSE;\n        }\n\n        ////////////////////////////////////////////// Insert .detour Section.\n        //\n        DWORD nSection = m_NtHeader.FileHeader.NumberOfSections++;\n        DETOUR_SECTION_HEADER dh;\n\n        ZeroMemory(&dh, sizeof(dh));\n        ZeroMemory(&m_SectionHeaders[nSection], sizeof(m_SectionHeaders[nSection]));\n\n        dh.cbHeaderSize = sizeof(DETOUR_SECTION_HEADER);\n        dh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE;\n\n        dh.nOriginalImportVirtualAddress = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;\n        dh.nOriginalImportSize = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;\n\n        dh.nOriginalBoundImportVirtualAddress\n            = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress;\n        dh.nOriginalBoundImportSize = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size;\n\n        dh.nOriginalIatVirtualAddress = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;\n        dh.nOriginalIatSize = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;\n\n        dh.nOriginalSizeOfImage = m_NtHeader.OptionalHeader.SizeOfImage;\n\n        DWORD clrAddr = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;\n        DWORD clrSize = m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;\n        if (clrAddr && clrSize) {\n            PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr);\n            if (pHdr != NULL) {\n                DETOUR_CLR_HEADER hdr;\n                hdr = *pHdr;\n\n                dh.nOriginalClrFlags = hdr.Flags;\n            }\n        }\n\n        HRESULT hrRet = StringCchCopyA((PCHAR)m_SectionHeaders[nSection].Name, IMAGE_SIZEOF_SHORT_NAME , \".detour\");\n        if (FAILED(hrRet))\n            return FALSE;\n\n        m_SectionHeaders[nSection].Characteristics\n            = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;\n\n        m_nOutputVirtAddr = m_nNextVirtAddr;\n        m_nOutputVirtSize = 0;\n        m_nOutputFileAddr = m_nNextFileAddr;\n\n        dh.nDataOffset = 0;                     // pbData\n        dh.cbDataSize = m_pImageData->m_cbData;\n        dh.cbPrePE = m_cbPrePE;\n\n        //////////////////////////////////////////////////////////////////////////\n        //\n\n        DWORD rvaImportTable = 0;\n        DWORD rvaLookupTable = 0;\n        DWORD rvaBoundTable = 0;\n        DWORD rvaNameTable = 0;\n        DWORD nImportTableSize = nTables * sizeof(IMAGE_IMPORT_DESCRIPTOR);\n\n        if (!SizeOutputBuffer(QuadAlign(sizeof(dh))\n                              + m_cbPrePE\n                              + QuadAlign(m_pImageData->m_cbData)\n                              + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks)\n                              + QuadAlign(sizeof(IMAGE_THUNK_DATA) * nThunks)\n                              + QuadAlign(nChars)\n                              + QuadAlign(nImportTableSize))) {\n            return FALSE;\n        }\n\n        DWORD vaHead = 0;\n        PBYTE pbHead = NULL;\n        DWORD vaPrePE = 0;\n        PBYTE pbPrePE = NULL;\n        DWORD vaData = 0;\n        PBYTE pbData = NULL;\n\n        if ((pbHead = AllocateOutput(sizeof(dh), &vaHead)) == NULL) {\n            return FALSE;\n        }\n\n        if ((pbPrePE = AllocateOutput(m_cbPrePE, &vaPrePE)) == NULL) {\n            return FALSE;\n        }\n\n        CImageThunks lookupTable(this, nThunks, &rvaLookupTable);\n        CImageThunks boundTable(this, nThunks, &rvaBoundTable);\n        CImageChars nameTable(this, nChars, &rvaNameTable);\n\n        if ((pbData = AllocateOutput(m_pImageData->m_cbData, &vaData)) == NULL) {\n            return FALSE;\n        }\n\n        dh.nDataOffset = vaData - vaHead;\n        dh.cbDataSize = dh.nDataOffset + m_pImageData->m_cbData;\n        CopyMemory(pbHead, &dh, sizeof(dh));\n        CopyMemory(pbPrePE, m_pMap + m_nPrePE, m_cbPrePE);\n        CopyMemory(pbData, m_pImageData->m_pbData, m_pImageData->m_cbData);\n\n        PIMAGE_IMPORT_DESCRIPTOR piidDst = (PIMAGE_IMPORT_DESCRIPTOR)\n            AllocateOutput(nImportTableSize, &rvaImportTable);\n        if (piidDst == NULL) {\n            return FALSE;\n        }\n\n        //////////////////////////////////////////////// Step Through Imports.\n        //\n        for (CImageImportFile *pImportFile = m_pImportFiles;\n             pImportFile != NULL; pImportFile = pImportFile->m_pNextFile) {\n\n            ZeroMemory(piidDst, sizeof(piidDst));\n            nameTable.Allocate(pImportFile->m_pszName, (DWORD *)&piidDst->Name);\n            piidDst->TimeDateStamp = 0;\n            piidDst->ForwarderChain = pImportFile->m_nForwarderChain;\n\n            if (pImportFile->m_fByway) {\n                ULONG rvaIgnored;\n\n                lookupTable.Allocate(IMAGE_ORDINAL_FLAG+1,\n                                     (DWORD *)&piidDst->OriginalFirstThunk);\n                boundTable.Allocate(IMAGE_ORDINAL_FLAG+1,\n                                    (DWORD *)&piidDst->FirstThunk);\n\n                lookupTable.Allocate(0, &rvaIgnored);\n                boundTable.Allocate(0, &rvaIgnored);\n            }\n            else {\n                ULONG rvaIgnored;\n\n                piidDst->FirstThunk = (ULONG)pImportFile->m_rvaFirstThunk;\n                lookupTable.Current((DWORD *)&piidDst->OriginalFirstThunk);\n\n                for (n = 0; n < pImportFile->m_nImportNames; n++) {\n                    CImageImportName *pImportName = &pImportFile->m_pImportNames[n];\n\n                    if (pImportName->m_pszName) {\n                        ULONG nDstName = 0;\n\n                        nameTable.Allocate(pImportName->m_pszName,\n                                           pImportName->m_nHint,\n                                           &nDstName);\n                        lookupTable.Allocate(nDstName, &rvaIgnored);\n                    }\n                    else {\n                        lookupTable.Allocate(IMAGE_ORDINAL_FLAG + pImportName->m_nOrdinal,\n                                             &rvaIgnored);\n                    }\n                }\n                lookupTable.Allocate(0, &rvaIgnored);\n            }\n            piidDst++;\n        }\n        ZeroMemory(piidDst, sizeof(piidDst));\n\n        //////////////////////////////////////////////////////////////////////////\n        //\n        m_nNextVirtAddr += m_nOutputVirtSize;\n        m_nNextFileAddr += FileAlign(m_nOutputVirtSize);\n\n        if (!AlignFileData(hFile)) {\n            return FALSE;\n        }\n\n        //////////////////////////////////////////////////////////////////////////\n        //\n        m_SectionHeaders[nSection].VirtualAddress = m_nOutputVirtAddr;\n        m_SectionHeaders[nSection].Misc.VirtualSize = m_nOutputVirtSize;\n        m_SectionHeaders[nSection].PointerToRawData = m_nOutputFileAddr;\n        m_SectionHeaders[nSection].SizeOfRawData = FileAlign(m_nOutputVirtSize);\n\n        m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress\n            = rvaImportTable;\n        m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size\n            = nImportTableSize;\n\n        m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;\n        m_NtHeader.OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;\n\n        //////////////////////////////////////////////////////////////////////////\n        //\n        if (SetFilePointer(hFile, m_SectionHeaders[nSection].PointerToRawData,\n                           NULL, FILE_BEGIN) == ~0u) {\n            return FALSE;\n        }\n        if (!WriteFile(hFile, m_pbOutputBuffer, m_SectionHeaders[nSection].SizeOfRawData,\n                       &cbDone)) {\n            return FALSE;\n        }\n    }\n\n    ///////////////////////////////////////////////////// Adjust Extra Data.\n    //\n    LONG nExtraAdjust = m_nNextFileAddr - m_nExtraOffset;\n    for (n = 0; n < m_NtHeader.FileHeader.NumberOfSections; n++) {\n        if (m_SectionHeaders[n].PointerToRawData > m_nExtraOffset) {\n            m_SectionHeaders[n].PointerToRawData += nExtraAdjust;\n        }\n        if (m_SectionHeaders[n].PointerToRelocations > m_nExtraOffset) {\n            m_SectionHeaders[n].PointerToRelocations += nExtraAdjust;\n        }\n        if (m_SectionHeaders[n].PointerToLinenumbers > m_nExtraOffset) {\n            m_SectionHeaders[n].PointerToLinenumbers += nExtraAdjust;\n        }\n    }\n    if (m_NtHeader.FileHeader.PointerToSymbolTable > m_nExtraOffset) {\n        m_NtHeader.FileHeader.PointerToSymbolTable += nExtraAdjust;\n    }\n\n    m_NtHeader.OptionalHeader.CheckSum = 0;\n    m_NtHeader.OptionalHeader.SizeOfImage = m_nNextVirtAddr;\n\n    ////////////////////////////////////////////////// Adjust Debug Directory.\n    //\n    DWORD debugAddr = m_NtHeader.OptionalHeader\n        .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;\n    DWORD debugSize = m_NtHeader.OptionalHeader\n        .DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;\n    if (debugAddr && debugSize) {\n        DWORD nFileOffset = RvaToFileOffset(debugAddr);\n        if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) {\n            return FALSE;\n        }\n\n        PIMAGE_DEBUG_DIRECTORY pDir = (PIMAGE_DEBUG_DIRECTORY)RvaToVa(debugAddr);\n        if (pDir == NULL) {\n            return FALSE;\n        }\n\n        DWORD nEntries = debugSize / sizeof(*pDir);\n        for (n = 0; n < nEntries; n++) {\n            IMAGE_DEBUG_DIRECTORY dir = pDir[n];\n\n            if (dir.PointerToRawData > m_nExtraOffset) {\n                dir.PointerToRawData += nExtraAdjust;\n            }\n            if (!WriteFile(hFile, &dir, sizeof(dir), &cbDone)) {\n                return FALSE;\n            }\n        }\n    }\n\n    /////////////////////////////////////////////////////// Adjust CLR Header.\n    //\n    DWORD clrAddr = m_NtHeader.OptionalHeader\n        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress;\n    DWORD clrSize = m_NtHeader.OptionalHeader\n        .DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size;\n    if (clrAddr && clrSize && fNeedDetourSection) {\n        DWORD nFileOffset = RvaToFileOffset(clrAddr);\n        if (SetFilePointer(hFile, nFileOffset, NULL, FILE_BEGIN) == ~0u) {\n            return FALSE;\n        }\n\n        PDETOUR_CLR_HEADER pHdr = (PDETOUR_CLR_HEADER)RvaToVa(clrAddr);\n        if (pHdr == NULL) {\n            return FALSE;\n        }\n\n        DETOUR_CLR_HEADER hdr;\n        hdr = *pHdr;\n        hdr.Flags &= 0xfffffffe;    // Clear the IL_ONLY flag.\n\n        if (!WriteFile(hFile, &hdr, sizeof(hdr), &cbDone)) {\n            return FALSE;\n        }\n    }\n\n    ///////////////////////////////////////////////// Copy Left-over Data.\n    //\n    if (m_nFileSize > m_nExtraOffset) {\n        if (SetFilePointer(hFile, m_nNextFileAddr, NULL, FILE_BEGIN) == ~0u) {\n            return FALSE;\n        }\n        if (!CopyFileData(hFile, m_nExtraOffset, m_nFileSize - m_nExtraOffset)) {\n            return FALSE;\n        }\n    }\n\n\n    //////////////////////////////////////////////////// Finalize Headers.\n    //\n\n    if (SetFilePointer(hFile, m_nPeOffset, NULL, FILE_BEGIN) == ~0u) {\n        return FALSE;\n    }\n    if (!WriteFile(hFile, &m_NtHeader, sizeof(m_NtHeader), &cbDone)) {\n        return FALSE;\n    }\n\n    if (SetFilePointer(hFile, m_nSectionsOffset, NULL, FILE_BEGIN) == ~0u) {\n        return FALSE;\n    }\n    if (!WriteFile(hFile, &m_SectionHeaders,\n                   sizeof(m_SectionHeaders[0])\n                   * m_NtHeader.FileHeader.NumberOfSections,\n                   &cbDone)) {\n        return FALSE;\n    }\n\n    m_cbPostPE = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);\n    if (m_cbPostPE == ~0u) {\n        return FALSE;\n    }\n    m_cbPostPE = m_NtHeader.OptionalHeader.SizeOfHeaders - m_cbPostPE;\n\n    return TRUE;\n}\n\n};                                                      // namespace Detour\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPDETOUR_BINARY WINAPI DetourBinaryOpen(_In_ HANDLE hFile)\n{\n    Detour::CImage *pImage = new NOTHROW\n        Detour::CImage;\n    if (pImage == NULL) {\n        SetLastError(ERROR_OUTOFMEMORY);\n        return FALSE;\n    }\n\n    if (!pImage->Read(hFile)) {\n        delete pImage;\n        return FALSE;\n    }\n\n    return (PDETOUR_BINARY)pImage;\n}\n\nBOOL WINAPI DetourBinaryWrite(_In_ PDETOUR_BINARY pdi,\n                              _In_ HANDLE hFile)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pdi);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->Write(hFile);\n}\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourBinaryEnumeratePayloads(_In_ PDETOUR_BINARY pBinary,\n                                           _Out_opt_ GUID *pGuid,\n                                           _Out_ DWORD *pcbData,\n                                           _Inout_ DWORD *pnIterator)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->DataEnum(pGuid, pcbData, pnIterator);\n}\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourBinaryFindPayload(_In_ PDETOUR_BINARY pBinary,\n                                     _In_ REFGUID rguid,\n                                     _Out_ DWORD *pcbData)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->DataFind(rguid, pcbData);\n}\n\nPVOID WINAPI DetourBinarySetPayload(_In_ PDETOUR_BINARY pBinary,\n                                    _In_ REFGUID rguid,\n                                    _In_reads_opt_(cbData) PVOID pvData,\n                                    _In_ DWORD cbData)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return NULL;\n    }\n\n    return pImage->DataSet(rguid, (PBYTE)pvData, cbData);\n}\n\nBOOL WINAPI DetourBinaryDeletePayload(_In_ PDETOUR_BINARY pBinary,\n                                      _In_ REFGUID rguid)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->DataDelete(rguid);\n}\n\nBOOL WINAPI DetourBinaryPurgePayloads(_In_ PDETOUR_BINARY pBinary)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->DataPurge();\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nstatic BOOL CALLBACK ResetBywayCallback(_In_opt_ PVOID pContext,\n                                        _In_opt_ LPCSTR pszFile,\n                                        _Outptr_result_maybenull_ LPCSTR *ppszOutFile)\n{\n    UNREFERENCED_PARAMETER(pContext);\n    UNREFERENCED_PARAMETER(pszFile);\n\n    *ppszOutFile = NULL;\n    return TRUE;\n}\n\nstatic BOOL CALLBACK ResetFileCallback(_In_opt_ PVOID pContext,\n                                       _In_ LPCSTR pszOrigFile,\n                                       _In_ LPCSTR pszFile,\n                                       _Outptr_result_maybenull_ LPCSTR *ppszOutFile)\n{\n    UNREFERENCED_PARAMETER(pContext);\n    UNREFERENCED_PARAMETER(pszFile);\n\n    *ppszOutFile = pszOrigFile;\n    return TRUE;\n}\n\nstatic BOOL CALLBACK ResetSymbolCallback(_In_opt_ PVOID pContext,\n                                         _In_ ULONG nOrigOrdinal,\n                                         _In_ ULONG nOrdinal,\n                                         _Out_ ULONG *pnOutOrdinal,\n                                         _In_opt_ LPCSTR pszOrigSymbol,\n                                         _In_opt_ LPCSTR pszSymbol,\n                                         _Outptr_result_maybenull_ LPCSTR *ppszOutSymbol)\n{\n    UNREFERENCED_PARAMETER(pContext);\n    UNREFERENCED_PARAMETER(nOrdinal);\n    UNREFERENCED_PARAMETER(pszSymbol);\n\n    *pnOutOrdinal = nOrigOrdinal;\n    *ppszOutSymbol = pszOrigSymbol;\n    return TRUE;\n}\n\nBOOL WINAPI DetourBinaryResetImports(_In_ PDETOUR_BINARY pBinary)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->EditImports(NULL,\n                               ResetBywayCallback,\n                               ResetFileCallback,\n                               ResetSymbolCallback,\n                               NULL);\n}\n\n//////////////////////////////////////////////////////////////////////////////\n//\nBOOL WINAPI DetourBinaryEditImports(_In_ PDETOUR_BINARY pBinary,\n                                    _In_opt_ PVOID pContext,\n                                    _In_opt_ PF_DETOUR_BINARY_BYWAY_CALLBACK pfByway,\n                                    _In_opt_ PF_DETOUR_BINARY_FILE_CALLBACK pfFile,\n                                    _In_opt_ PF_DETOUR_BINARY_SYMBOL_CALLBACK pfSymbol,\n                                    _In_opt_ PF_DETOUR_BINARY_COMMIT_CALLBACK pfCommit)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    return pImage->EditImports(pContext,\n                               pfByway,\n                               pfFile,\n                               pfSymbol,\n                               pfCommit);\n}\n\nBOOL WINAPI DetourBinaryClose(_In_ PDETOUR_BINARY pBinary)\n{\n    Detour::CImage *pImage = Detour::CImage::IsValid(pBinary);\n    if (pImage == NULL) {\n        return FALSE;\n    }\n\n    BOOL bSuccess = pImage->Close();\n    delete pImage;\n    pImage = NULL;\n\n    return bSuccess;\n}\n\n//\n///////////////////////////////////////////////////////////////// End of File.\n"
  },
  {
    "path": "Detours/modules.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Module Enumeration Functions (modules.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n//  Module enumeration functions.\n//\n\n#define _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS 1\n\n#pragma warning(disable:4068) // unknown pragma (suppress)\n\n#if _MSC_VER >= 1900\n#pragma warning(push)\n#pragma warning(disable:4091) // empty typedef\n#endif\n\n#define _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE 1\n#include <Veil.h>\n#if (_MSC_VER < 1310)\n#else\n#pragma warning(push)\n#if _MSC_VER > 1400\n#pragma warning(disable:6102 6103) // /analyze warnings\n#endif\n#include <strsafe.h>\n#pragma warning(pop)\n#endif\n\n// #define DETOUR_DEBUG 1\n#define DETOURS_INTERNAL\n#include \"detours.h\"\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\n#if _MSC_VER >= 1900\n#pragma warning(pop)\n#endif\n\n#define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]\n#define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT]\n\n//////////////////////////////////////////////////////////////////////////////\n//\nPDETOUR_SYM_INFO DetourLoadImageHlp(VOID)\n{\n    static DETOUR_SYM_INFO symInfo;\n    static PDETOUR_SYM_INFO pSymInfo = NULL;\n    static BOOL failed = false;\n\n    if (failed) {\n        return NULL;\n    }\n    if (pSymInfo != NULL) {\n        return pSymInfo;\n    }\n\n    ZeroMemory(&symInfo, sizeof(symInfo));\n    // Create a real handle to the process.\n#if 0\n    DuplicateHandle(GetCurrentProcess(),\n                    GetCurrentProcess(),\n                    GetCurrentProcess(),\n                    &symInfo.hProcess,\n                    0,\n                    FALSE,\n                    DUPLICATE_SAME_ACCESS);\n#else\n    symInfo.hProcess = GetCurrentProcess();\n#endif\n\n    symInfo.hDbgHelp = LoadLibraryExW(L\"dbghelp.dll\", NULL, 0);\n    if (symInfo.hDbgHelp == NULL) {\n      abort:\n        failed = true;\n        if (symInfo.hDbgHelp != NULL) {\n            FreeLibrary(symInfo.hDbgHelp);\n        }\n        symInfo.pfImagehlpApiVersionEx = NULL;\n        symInfo.pfSymInitialize = NULL;\n        symInfo.pfSymSetOptions = NULL;\n        symInfo.pfSymGetOptions = NULL;\n        symInfo.pfSymLoadModule64 = NULL;\n        symInfo.pfSymGetModuleInfo64 = NULL;\n        symInfo.pfSymFromName = NULL;\n        return NULL;\n    }\n\n    symInfo.pfImagehlpApiVersionEx\n        = (PF_ImagehlpApiVersionEx)GetProcAddress(symInfo.hDbgHelp,\n                                                  \"ImagehlpApiVersionEx\");\n    symInfo.pfSymInitialize\n        = (PF_SymInitialize)GetProcAddress(symInfo.hDbgHelp, \"SymInitialize\");\n    symInfo.pfSymSetOptions\n        = (PF_SymSetOptions)GetProcAddress(symInfo.hDbgHelp, \"SymSetOptions\");\n    symInfo.pfSymGetOptions\n        = (PF_SymGetOptions)GetProcAddress(symInfo.hDbgHelp, \"SymGetOptions\");\n    symInfo.pfSymLoadModule64\n        = (PF_SymLoadModule64)GetProcAddress(symInfo.hDbgHelp, \"SymLoadModule64\");\n    symInfo.pfSymGetModuleInfo64\n        = (PF_SymGetModuleInfo64)GetProcAddress(symInfo.hDbgHelp, \"SymGetModuleInfo64\");\n    symInfo.pfSymFromName\n        = (PF_SymFromName)GetProcAddress(symInfo.hDbgHelp, \"SymFromName\");\n\n    API_VERSION av;\n    ZeroMemory(&av, sizeof(av));\n    av.MajorVersion = API_VERSION_NUMBER;\n\n    if (symInfo.pfImagehlpApiVersionEx == NULL ||\n        symInfo.pfSymInitialize == NULL ||\n        symInfo.pfSymLoadModule64 == NULL ||\n        symInfo.pfSymGetModuleInfo64 == NULL ||\n        symInfo.pfSymFromName == NULL) {\n        goto abort;\n    }\n\n    symInfo.pfImagehlpApiVersionEx(&av);\n    if (av.MajorVersion < API_VERSION_NUMBER) {\n        goto abort;\n    }\n\n    if (!symInfo.pfSymInitialize(symInfo.hProcess, NULL, FALSE)) {\n        // We won't retry the initialize if it fails.\n        goto abort;\n    }\n\n    if (symInfo.pfSymGetOptions != NULL && symInfo.pfSymSetOptions != NULL) {\n        DWORD dw = symInfo.pfSymGetOptions();\n\n        dw &= ~(SYMOPT_CASE_INSENSITIVE |\n                SYMOPT_UNDNAME |\n                SYMOPT_DEFERRED_LOADS |\n                0);\n        dw |= (\n#if defined(SYMOPT_EXACT_SYMBOLS)\n               SYMOPT_EXACT_SYMBOLS |\n#endif\n#if defined(SYMOPT_NO_UNQUALIFIED_LOADS)\n               SYMOPT_NO_UNQUALIFIED_LOADS |\n#endif\n               SYMOPT_DEFERRED_LOADS |\n#if defined(SYMOPT_FAIL_CRITICAL_ERRORS)\n               SYMOPT_FAIL_CRITICAL_ERRORS |\n#endif\n#if defined(SYMOPT_INCLUDE_32BIT_MODULES)\n               SYMOPT_INCLUDE_32BIT_MODULES |\n#endif\n               0);\n        symInfo.pfSymSetOptions(dw);\n    }\n\n    pSymInfo = &symInfo;\n    return pSymInfo;\n}\n\nPVOID WINAPI DetourFindFunction(_In_ PCSTR pszModule,\n                                _In_ PCSTR pszFunction)\n{\n    /////////////////////////////////////////////// First, try GetProcAddress.\n    //\n#pragma prefast(suppress:28752, \"We don't do the unicode conversion for LoadLibraryExA.\")\n    HMODULE hModule = LoadLibraryExA(pszModule, NULL, 0);\n    if (hModule == NULL) {\n        return NULL;\n    }\n\n    PBYTE pbCode = (PBYTE)GetProcAddress(hModule, pszFunction);\n    if (pbCode) {\n        return pbCode;\n    }\n\n    ////////////////////////////////////////////////////// Then try ImageHelp.\n    //\n    DETOUR_TRACE((\"DetourFindFunction(%hs, %hs)\\n\", pszModule, pszFunction));\n    PDETOUR_SYM_INFO pSymInfo = DetourLoadImageHlp();\n    if (pSymInfo == NULL) {\n        DETOUR_TRACE((\"DetourLoadImageHlp failed: %d\\n\",\n                      GetLastError()));\n        return NULL;\n    }\n\n    if (pSymInfo->pfSymLoadModule64(pSymInfo->hProcess, NULL,\n                                    (PCHAR)pszModule, NULL,\n                                    (DWORD64)hModule, 0) == 0) {\n        if (ERROR_SUCCESS != GetLastError()) {\n            DETOUR_TRACE((\"SymLoadModule64(%p) failed: %d\\n\",\n                          pSymInfo->hProcess, GetLastError()));\n            return NULL;\n        }\n    }\n\n    HRESULT hrRet;\n    CHAR szFullName[512];\n    IMAGEHLP_MODULE64 modinfo;\n    ZeroMemory(&modinfo, sizeof(modinfo));\n    modinfo.SizeOfStruct = sizeof(modinfo);\n    if (!pSymInfo->pfSymGetModuleInfo64(pSymInfo->hProcess, (DWORD64)hModule, &modinfo)) {\n        DETOUR_TRACE((\"SymGetModuleInfo64(%p, %p) failed: %d\\n\",\n                      pSymInfo->hProcess, hModule, GetLastError()));\n        return NULL;\n    }\n\n    hrRet = StringCchCopyA(szFullName, sizeof(szFullName)/sizeof(CHAR), modinfo.ModuleName);\n    if (FAILED(hrRet)) {\n        DETOUR_TRACE((\"StringCchCopyA failed: %08x\\n\", hrRet));\n        return NULL;\n    }\n    hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), \"!\");\n    if (FAILED(hrRet)) {\n        DETOUR_TRACE((\"StringCchCatA failed: %08x\\n\", hrRet));\n        return NULL;\n    }\n    hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), pszFunction);\n    if (FAILED(hrRet)) {\n        DETOUR_TRACE((\"StringCchCatA failed: %08x\\n\", hrRet));\n        return NULL;\n    }\n\n    struct CFullSymbol : SYMBOL_INFO {\n        CHAR szRestOfName[512];\n    } symbol;\n    ZeroMemory(&symbol, sizeof(symbol));\n    //symbol.ModBase = (ULONG64)hModule;\n    symbol.SizeOfStruct = sizeof(SYMBOL_INFO);\n#ifdef DBHLPAPI\n    symbol.MaxNameLen = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]);\n#else\n    symbol.MaxNameLength = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]);\n#endif\n\n    if (!pSymInfo->pfSymFromName(pSymInfo->hProcess, szFullName, &symbol)) {\n        DETOUR_TRACE((\"SymFromName(%hs) failed: %d\\n\", szFullName, GetLastError()));\n        return NULL;\n    }\n\n#if defined(DETOURS_IA64)\n    // On the IA64, we get a raw code pointer from the symbol engine\n    // and have to convert it to a wrapped [code pointer, global pointer].\n    //\n    PPLABEL_DESCRIPTOR pldEntry = (PPLABEL_DESCRIPTOR)DetourGetEntryPoint(hModule);\n    PPLABEL_DESCRIPTOR pldSymbol = new PLABEL_DESCRIPTOR;\n\n    pldSymbol->EntryPoint = symbol.Address;\n    pldSymbol->GlobalPointer = pldEntry->GlobalPointer;\n    return (PBYTE)pldSymbol;\n#elif defined(DETOURS_ARM)\n    // On the ARM, we get a raw code pointer, which we must convert into a\n    // valied Thumb2 function pointer.\n    return DETOURS_PBYTE_TO_PFUNC(symbol.Address);\n#else\n    return (PBYTE)symbol.Address;\n#endif\n}\n\n//////////////////////////////////////////////////// Module Image Functions.\n//\n\nHMODULE WINAPI DetourEnumerateModules(_In_opt_ HMODULE hModuleLast)\n{\n    PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY;\n\n    MEMORY_BASIC_INFORMATION mbi;\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    // Find the next memory region that contains a mapped PE image.\n    //\n    for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) {\n        if (VirtualQuery(pbLast, &mbi, sizeof(mbi)) <= 0) {\n            break;\n        }\n\n        // Skip uncommitted regions and guard pages.\n        //\n        if ((mbi.State != MEM_COMMIT) ||\n            ((mbi.Protect & 0xff) == PAGE_NOACCESS) ||\n            (mbi.Protect & PAGE_GUARD)) {\n            continue;\n        }\n\n        __try {\n            PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbLast;\n            if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE ||\n                (DWORD)pDosHeader->e_lfanew > mbi.RegionSize ||\n                (DWORD)pDosHeader->e_lfanew < sizeof(*pDosHeader)) {\n                continue;\n            }\n\n            PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                              pDosHeader->e_lfanew);\n            if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n                continue;\n            }\n\n            return (HMODULE)pDosHeader;\n        }\n#pragma prefast(suppress:28940, \"A bad pointer means this probably isn't a PE header.\")\n        __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n                 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n            continue;\n        }\n    }\n    return NULL;\n}\n\nPVOID WINAPI DetourGetEntryPoint(_In_opt_ HMODULE hModule)\n{\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;\n    if (hModule == NULL) {\n        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);\n    }\n\n    __try {\n#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return NULL;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return NULL;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return NULL;\n        }\n\n        PDETOUR_CLR_HEADER pClrHeader = NULL;\n        if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\n            if (((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 &&\n                ((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.Size != 0) {\n                pClrHeader = (PDETOUR_CLR_HEADER)\n                    (((PBYTE)pDosHeader)\n                     + ((PIMAGE_NT_HEADERS32)pNtHeader)->CLR_DIRECTORY.VirtualAddress);\n            }\n        }\n        else if (pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\n            if (((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress != 0 &&\n                ((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.Size != 0) {\n                pClrHeader = (PDETOUR_CLR_HEADER)\n                    (((PBYTE)pDosHeader)\n                     + ((PIMAGE_NT_HEADERS64)pNtHeader)->CLR_DIRECTORY.VirtualAddress);\n            }\n        }\n\n        if (pClrHeader != NULL) {\n            // For MSIL assemblies, we want to use the _Cor entry points.\n\n            HMODULE hClr = GetModuleHandleW(L\"MSCOREE.DLL\");\n            if (hClr == NULL) {\n                return NULL;\n            }\n\n            SetLastError(NO_ERROR);\n            return GetProcAddress(hClr, \"_CorExeMain\");\n        }\n\n        SetLastError(NO_ERROR);\n\n        // Pure resource DLLs have neither an entry point nor CLR information\n        // so handle them by returning NULL (LastError is NO_ERROR)\n        if (pNtHeader->OptionalHeader.AddressOfEntryPoint == 0) {\n            return NULL;\n        }\n\n        return ((PBYTE)pDosHeader) +\n            pNtHeader->OptionalHeader.AddressOfEntryPoint;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return NULL;\n    }\n}\n\nULONG WINAPI DetourGetModuleSize(_In_opt_ HMODULE hModule)\n{\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;\n    if (hModule == NULL) {\n        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);\n    }\n\n    __try {\n#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return 0;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return 0;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return 0;\n        }\n        SetLastError(NO_ERROR);\n\n        return (pNtHeader->OptionalHeader.SizeOfImage);\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return 0;\n    }\n}\n\nHMODULE WINAPI DetourGetContainingModule(_In_ PVOID pvAddr)\n{\n    MEMORY_BASIC_INFORMATION mbi;\n    ZeroMemory(&mbi, sizeof(mbi));\n\n    __try {\n        if (VirtualQuery(pvAddr, &mbi, sizeof(mbi)) <= 0) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return NULL;\n        }\n\n        // Skip uncommitted regions and guard pages.\n        //\n        if ((mbi.State != MEM_COMMIT) ||\n            ((mbi.Protect & 0xff) == PAGE_NOACCESS) ||\n            (mbi.Protect & PAGE_GUARD)) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return NULL;\n        }\n\n        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase;\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return NULL;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return NULL;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return NULL;\n        }\n        SetLastError(NO_ERROR);\n\n        return (HMODULE)pDosHeader;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n        return NULL;\n    }\n}\n\n\nstatic inline PBYTE RvaAdjust(_Pre_notnull_ PIMAGE_DOS_HEADER pDosHeader, _In_ DWORD raddr)\n{\n    if (raddr != 0) {\n        return ((PBYTE)pDosHeader) + raddr;\n    }\n    return NULL;\n}\n\nBOOL WINAPI DetourEnumerateExports(_In_ HMODULE hModule,\n                                   _In_opt_ PVOID pContext,\n                                   _In_ PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport)\n{\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;\n    if (hModule == NULL) {\n        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);\n    }\n\n    __try {\n#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return FALSE;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return FALSE;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return FALSE;\n        }\n\n        PIMAGE_EXPORT_DIRECTORY pExportDir\n            = (PIMAGE_EXPORT_DIRECTORY)\n            RvaAdjust(pDosHeader,\n                      pNtHeader->OptionalHeader\n                      .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);\n\n        if (pExportDir == NULL) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return FALSE;\n        }\n\n        PBYTE pExportDirEnd = (PBYTE)pExportDir + pNtHeader->OptionalHeader\n            .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;\n        PDWORD pdwFunctions = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfFunctions);\n        PDWORD pdwNames = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNames);\n        PWORD pwOrdinals = (PWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNameOrdinals);\n\n        for (DWORD nFunc = 0; nFunc < pExportDir->NumberOfFunctions; nFunc++) {\n            PBYTE pbCode = (pdwFunctions != NULL)\n                ? (PBYTE)RvaAdjust(pDosHeader, pdwFunctions[nFunc]) : NULL;\n            PCHAR pszName = NULL;\n\n            // if the pointer is in the export region, then it is a forwarder.\n            if (pbCode > (PBYTE)pExportDir && pbCode < pExportDirEnd) {\n                pbCode = NULL;\n            }\n\n            for (DWORD n = 0; n < pExportDir->NumberOfNames; n++) {\n                if (pwOrdinals[n] == nFunc) {\n                    pszName = (pdwNames != NULL)\n                        ? (PCHAR)RvaAdjust(pDosHeader, pdwNames[n]) : NULL;\n                    break;\n                }\n            }\n            ULONG nOrdinal = pExportDir->Base + nFunc;\n\n            if (!pfExport(pContext, nOrdinal, pszName, pbCode)) {\n                break;\n            }\n        }\n        SetLastError(NO_ERROR);\n        return TRUE;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return FALSE;\n    }\n}\n\nBOOL WINAPI DetourEnumerateImportsEx(_In_opt_ HMODULE hModule,\n                                     _In_opt_ PVOID pContext,\n                                     _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,\n                                     _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK_EX pfImportFunc)\n{\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;\n    if (hModule == NULL) {\n        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);\n    }\n\n    __try {\n#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return FALSE;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return FALSE;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return FALSE;\n        }\n\n        PIMAGE_IMPORT_DESCRIPTOR iidp\n            = (PIMAGE_IMPORT_DESCRIPTOR)\n            RvaAdjust(pDosHeader,\n                      pNtHeader->OptionalHeader\n                      .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);\n\n        if (iidp == NULL) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return FALSE;\n        }\n\n        for (; iidp->OriginalFirstThunk != 0; iidp++) {\n\n            PCSTR pszName = (PCHAR)RvaAdjust(pDosHeader, iidp->Name);\n            if (pszName == NULL) {\n                SetLastError(ERROR_EXE_MARKED_INVALID);\n                return FALSE;\n            }\n\n            PIMAGE_THUNK_DATA pThunks = (PIMAGE_THUNK_DATA)\n                RvaAdjust(pDosHeader, iidp->OriginalFirstThunk);\n            PVOID * pAddrs = (PVOID *)\n                RvaAdjust(pDosHeader, iidp->FirstThunk);\n\n            HMODULE hFile = DetourGetContainingModule(pAddrs[0]);\n\n            if (pfImportFile != NULL) {\n                if (!pfImportFile(pContext, hFile, pszName)) {\n                    break;\n                }\n            }\n\n            DWORD nNames = 0;\n            if (pThunks) {\n                for (; pThunks[nNames].u1.Ordinal; nNames++) {\n                    DWORD nOrdinal = 0;\n                    PCSTR pszFunc = NULL;\n\n                    if (IMAGE_SNAP_BY_ORDINAL(pThunks[nNames].u1.Ordinal)) {\n                        nOrdinal = (DWORD)IMAGE_ORDINAL(pThunks[nNames].u1.Ordinal);\n                    }\n                    else {\n                        pszFunc = (PCSTR)RvaAdjust(pDosHeader,\n                                                   (DWORD)pThunks[nNames].u1.AddressOfData + 2);\n                    }\n\n                    if (pfImportFunc != NULL) {\n                        if (!pfImportFunc(pContext,\n                                          nOrdinal,\n                                          pszFunc,\n                                          &pAddrs[nNames])) {\n                            break;\n                        }\n                    }\n                }\n                if (pfImportFunc != NULL) {\n                    pfImportFunc(pContext, 0, NULL, NULL);\n                }\n            }\n        }\n        if (pfImportFile != NULL) {\n            pfImportFile(pContext, NULL, NULL);\n        }\n        SetLastError(NO_ERROR);\n        return TRUE;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return FALSE;\n    }\n}\n\n// Context for DetourEnumerateImportsThunk, which adapts \"regular\" callbacks for use with \"Ex\".\nstruct _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT\n{\n    PVOID pContext;\n    PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc;\n};\n\n// Callback for DetourEnumerateImportsEx that adapts DetourEnumerateImportsEx\n// for use with a DetourEnumerateImports callback -- derefence the IAT and pass the value on.\n\nstatic\nBOOL\nCALLBACK\nDetourEnumerateImportsThunk(_In_ PVOID VoidContext,\n                            _In_ DWORD nOrdinal,\n                            _In_opt_ PCSTR pszFunc,\n                            _In_opt_ PVOID* ppvFunc)\n{\n    _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const * const\n        pContext = (_DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT*)VoidContext;\n    return pContext->pfImportFunc(pContext->pContext, nOrdinal, pszFunc, ppvFunc ? *ppvFunc : NULL);\n}\n\nBOOL WINAPI DetourEnumerateImports(_In_opt_ HMODULE hModule,\n                                   _In_opt_ PVOID pContext,\n                                   _In_opt_ PF_DETOUR_IMPORT_FILE_CALLBACK pfImportFile,\n                                   _In_opt_ PF_DETOUR_IMPORT_FUNC_CALLBACK pfImportFunc)\n{\n    _DETOUR_ENUMERATE_IMPORTS_THUNK_CONTEXT const context = { pContext, pfImportFunc };\n\n    return DetourEnumerateImportsEx(hModule,\n                                    (PVOID)&context,\n                                    pfImportFile,\n                                    &DetourEnumerateImportsThunk);\n}\n\nstatic PDETOUR_LOADED_BINARY WINAPI GetPayloadSectionFromModule(HMODULE hModule)\n{\n    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;\n    if (hModule == NULL) {\n        pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleW(NULL);\n    }\n\n    __try {\n#pragma warning(suppress:6011) // GetModuleHandleW(NULL) never returns NULL.\n        if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {\n            SetLastError(ERROR_BAD_EXE_FORMAT);\n            return NULL;\n        }\n\n        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader +\n                                                          pDosHeader->e_lfanew);\n        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) {\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return NULL;\n        }\n        if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) {\n            SetLastError(ERROR_EXE_MARKED_INVALID);\n            return NULL;\n        }\n\n        PIMAGE_SECTION_HEADER pSectionHeaders\n            = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader\n                                      + sizeof(pNtHeader->Signature)\n                                      + sizeof(pNtHeader->FileHeader)\n                                      + pNtHeader->FileHeader.SizeOfOptionalHeader);\n\n        for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++) {\n            if (strcmp((PCHAR)pSectionHeaders[n].Name, \".detour\") == 0) {\n                if (pSectionHeaders[n].VirtualAddress == 0 ||\n                    pSectionHeaders[n].SizeOfRawData == 0) {\n\n                    break;\n                }\n\n                PBYTE pbData = (PBYTE)pDosHeader + pSectionHeaders[n].VirtualAddress;\n                DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pbData;\n                if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||\n                    pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {\n\n                    break;\n                }\n\n                if (pHeader->nDataOffset == 0) {\n                    pHeader->nDataOffset = pHeader->cbHeaderSize;\n                }\n                SetLastError(NO_ERROR);\n                return (PBYTE)pHeader;\n            }\n        }\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return NULL;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_EXE_MARKED_INVALID);\n        return NULL;\n    }\n}\n\nDWORD WINAPI DetourGetSizeOfPayloads(_In_opt_ HMODULE hModule)\n{\n    PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule);\n    if (pBinary == NULL) {\n        // Error set by GetPayloadSectionFromModule.\n        return 0;\n    }\n\n    __try {\n        DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary;\n        if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||\n            pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {\n\n            SetLastError(ERROR_INVALID_HANDLE);\n            return 0;\n        }\n        SetLastError(NO_ERROR);\n        return pHeader->cbDataSize;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_INVALID_HANDLE);\n        return 0;\n    }\n}\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourFindPayload(_In_opt_ HMODULE hModule,\n                               _In_ REFGUID rguid,\n                               _Out_ DWORD *pcbData)\n{\n    PBYTE pbData = NULL;\n    if (pcbData) {\n        *pcbData = 0;\n    }\n\n    PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule);\n    if (pBinary == NULL) {\n        // Error set by GetPayloadSectionFromModule.\n        return NULL;\n    }\n\n    __try {\n        DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary;\n        if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) ||\n            pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) {\n\n            SetLastError(ERROR_INVALID_EXE_SIGNATURE);\n            return NULL;\n        }\n\n        PBYTE pbBeg = ((PBYTE)pHeader) + pHeader->nDataOffset;\n        PBYTE pbEnd = ((PBYTE)pHeader) + pHeader->cbDataSize;\n\n        for (pbData = pbBeg; pbData < pbEnd;) {\n            DETOUR_SECTION_RECORD *pSection = (DETOUR_SECTION_RECORD *)pbData;\n\n            if (pSection->guid.Data1 == rguid.Data1 &&\n                pSection->guid.Data2 == rguid.Data2 &&\n                pSection->guid.Data3 == rguid.Data3 &&\n                pSection->guid.Data4[0] == rguid.Data4[0] &&\n                pSection->guid.Data4[1] == rguid.Data4[1] &&\n                pSection->guid.Data4[2] == rguid.Data4[2] &&\n                pSection->guid.Data4[3] == rguid.Data4[3] &&\n                pSection->guid.Data4[4] == rguid.Data4[4] &&\n                pSection->guid.Data4[5] == rguid.Data4[5] &&\n                pSection->guid.Data4[6] == rguid.Data4[6] &&\n                pSection->guid.Data4[7] == rguid.Data4[7]) {\n\n                if (pcbData) {\n                    *pcbData = pSection->cbBytes - sizeof(*pSection);\n                    SetLastError(NO_ERROR);\n                    return (PBYTE)(pSection + 1);\n                }\n            }\n\n            pbData = (PBYTE)pSection + pSection->cbBytes;\n        }\n        SetLastError(ERROR_INVALID_HANDLE);\n        return NULL;\n    }\n    __except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?\n             EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {\n        SetLastError(ERROR_INVALID_HANDLE);\n        return NULL;\n    }\n}\n\n_Writable_bytes_(*pcbData)\n_Readable_bytes_(*pcbData)\n_Success_(return != NULL)\nPVOID WINAPI DetourFindPayloadEx(_In_ REFGUID rguid,\n                                 _Out_ DWORD * pcbData)\n{\n    for (HMODULE hMod = NULL; (hMod = DetourEnumerateModules(hMod)) != NULL;) {\n        PVOID pvData;\n\n        pvData = DetourFindPayload(hMod, rguid, pcbData);\n        if (pvData != NULL) {\n            return pvData;\n        }\n    }\n    SetLastError(ERROR_MOD_NOT_FOUND);\n    return NULL;\n}\n\nBOOL WINAPI DetourRestoreAfterWithEx(_In_reads_bytes_(cbData) PVOID pvData,\n                                     _In_ DWORD cbData)\n{\n    PDETOUR_EXE_RESTORE pder = (PDETOUR_EXE_RESTORE)pvData;\n\n    if (pder->cb != sizeof(*pder) || pder->cb > cbData) {\n        SetLastError(ERROR_BAD_EXE_FORMAT);\n        return FALSE;\n    }\n\n    DWORD dwPermIdh = ~0u;\n    DWORD dwPermInh = ~0u;\n    DWORD dwPermClr = ~0u;\n    DWORD dwIgnore;\n    BOOL fSucceeded = FALSE;\n    BOOL fUpdated32To64 = FALSE;\n\n    if (pder->pclr != NULL && pder->clr.Flags != ((PDETOUR_CLR_HEADER)pder->pclr)->Flags) {\n        // If we had to promote the 32/64-bit agnostic IL to 64-bit, we can't restore\n        // that.\n        fUpdated32To64 = TRUE;\n    }\n\n    if (DetourVirtualProtectSameExecute(pder->pidh, pder->cbidh,\n                                        PAGE_EXECUTE_READWRITE, &dwPermIdh)) {\n        if (DetourVirtualProtectSameExecute(pder->pinh, pder->cbinh,\n                                            PAGE_EXECUTE_READWRITE, &dwPermInh)) {\n\n            CopyMemory(pder->pidh, &pder->idh, pder->cbidh);\n            CopyMemory(pder->pinh, &pder->inh, pder->cbinh);\n\n            if (pder->pclr != NULL && !fUpdated32To64) {\n                if (DetourVirtualProtectSameExecute(pder->pclr, pder->cbclr,\n                                                    PAGE_EXECUTE_READWRITE, &dwPermClr)) {\n                    CopyMemory(pder->pclr, &pder->clr, pder->cbclr);\n                    VirtualProtect(pder->pclr, pder->cbclr, dwPermClr, &dwIgnore);\n                    fSucceeded = TRUE;\n                }\n            }\n            else {\n                fSucceeded = TRUE;\n            }\n            VirtualProtect(pder->pinh, pder->cbinh, dwPermInh, &dwIgnore);\n        }\n        VirtualProtect(pder->pidh, pder->cbidh, dwPermIdh, &dwIgnore);\n    }\n    return fSucceeded;\n}\n\nBOOL WINAPI DetourRestoreAfterWith()\n{\n    PVOID pvData;\n    DWORD cbData;\n\n    pvData = DetourFindPayloadEx(DETOUR_EXE_RESTORE_GUID, &cbData);\n\n    if (pvData != NULL && cbData != 0) {\n        return DetourRestoreAfterWithEx(pvData, cbData);\n    }\n    SetLastError(ERROR_MOD_NOT_FOUND);\n    return FALSE;\n}\n\n//  End of File\n"
  },
  {
    "path": "Detours/uimports.cpp",
    "content": "//////////////////////////////////////////////////////////////////////////////\n//\n//  Add DLLs to a module import table (uimports.cpp of detours.lib)\n//\n//  Microsoft Research Detours Package, Version 4.0.1\n//\n//  Copyright (c) Microsoft Corporation.  All rights reserved.\n//\n//  Note that this file is included into creatwth.cpp one or more times\n//  (once for each supported module format).\n//\n\n#if DETOURS_VERSION != 0x4c0c1   // 0xMAJORcMINORcPATCH\n#error detours.h version mismatch\n#endif\n\nstatic void* detour_memory_alloc(size_t size)\n{\n#ifdef DETOURS_KERNEL\n#pragma warning(suppress: 4996)\n    return ExAllocatePoolWithTag(NonPagedPool, size, DETOUR_SECTION_HEADER_SIGNATURE);\n#else\n    return RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, size);\n#endif\n}\n\nstatic void  detour_memory_free(void* p)\n{\n#ifdef DETOURS_KERNEL\n    ExFreePoolWithTag(p, DETOUR_SECTION_HEADER_SIGNATURE);\n#else\n    RtlFreeHeap(RtlProcessHeap(), 0, p);\n#endif\n}\n\n// UpdateImports32 aka UpdateImports64\nstatic BOOL UPDATE_IMPORTS_XX(HANDLE hProcess,\n                              HMODULE hModule,\n                              __in_ecount(nDlls) LPCSTR *plpDlls,\n                              DWORD nDlls)\n{\n    BOOL fSucceeded = FALSE;\n    DWORD cbNew = 0;\n\n    BYTE * pbNew = NULL;\n    DWORD i;\n    SIZE_T cbRead;\n    DWORD n;\n\n    PBYTE pbModule = (PBYTE)hModule;\n\n    IMAGE_DOS_HEADER idh;\n    ZeroMemory(&idh, sizeof(idh));\n    if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead)\n        || cbRead < sizeof(idh)) {\n\n        DETOUR_TRACE((\"ReadProcessMemory(idh@%p..%p) failed: %d\\n\",\n                      pbModule, pbModule + sizeof(idh), GetLastError()));\n\n      finish:\n        if (pbNew != NULL) {\n            detour_memory_free(pbNew);\n            pbNew = NULL;\n        }\n        return fSucceeded;\n    }\n\n    IMAGE_NT_HEADERS_XX inh;\n    ZeroMemory(&inh, sizeof(inh));\n\n    if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead)\n        || cbRead < sizeof(inh)) {\n        DETOUR_TRACE((\"ReadProcessMemory(inh@%p..%p) failed: %d\\n\",\n                      pbModule + idh.e_lfanew,\n                      pbModule + idh.e_lfanew + sizeof(inh),\n                      GetLastError()));\n        goto finish;\n    }\n\n    if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) {\n        DETOUR_TRACE((\"Wrong size image (%04x != %04x).\\n\",\n                      inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX));\n        SetLastError(ERROR_INVALID_BLOCK);\n        goto finish;\n    }\n\n    // Zero out the bound table so loader doesn't use it instead of our new table.\n    inh.BOUND_DIRECTORY.VirtualAddress = 0;\n    inh.BOUND_DIRECTORY.Size = 0;\n\n    // Find the size of the mapped file.\n    DWORD dwSec = idh.e_lfanew +\n        FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) +\n        inh.FileHeader.SizeOfOptionalHeader;\n\n    for (i = 0; i < inh.FileHeader.NumberOfSections; i++) {\n        IMAGE_SECTION_HEADER ish;\n        ZeroMemory(&ish, sizeof(ish));\n\n        if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish,\n                               sizeof(ish), &cbRead)\n            || cbRead < sizeof(ish)) {\n\n            DETOUR_TRACE((\"ReadProcessMemory(ish@%p..%p) failed: %d\\n\",\n                          pbModule + dwSec + sizeof(ish) * i,\n                          pbModule + dwSec + sizeof(ish) * (i + 1),\n                          GetLastError()));\n            goto finish;\n        }\n\n        DETOUR_TRACE((\"ish[%d] : va=%08x sr=%d\\n\", i, ish.VirtualAddress, ish.SizeOfRawData));\n\n        // If the file didn't have an IAT_DIRECTORY, we assign it...\n        if (inh.IAT_DIRECTORY.VirtualAddress == 0 &&\n            inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress &&\n            inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) {\n\n            inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress;\n            inh.IAT_DIRECTORY.Size = ish.SizeOfRawData;\n        }\n    }\n\n    DETOUR_TRACE((\"     Imports: %Ix..%Ix\\n\",\n                  (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,\n                  (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress +\n                  inh.IMPORT_DIRECTORY.Size));\n\n    DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);\n    DWORD obRem = sizeof(IMAGE_IMPORT_DESCRIPTOR) * nDlls;\n    DWORD obOld = obRem + sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls;\n    DWORD obTab = PadToDwordPtr(obOld);\n    DWORD obDll = obTab + sizeof(DWORD_XX) * 4 * nDlls;\n    DWORD obStr = obDll;\n    cbNew = obStr;\n    for (n = 0; n < nDlls; n++) {\n        cbNew += PadToDword((DWORD)strlen(plpDlls[n]) + 1);\n    }\n\n    _Analysis_assume_(cbNew >\n                      sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nDlls + nOldDlls)\n                      + sizeof(DWORD_XX) * 4 * nDlls);\n    pbNew = (PBYTE)detour_memory_alloc(cbNew);\n    if (pbNew == NULL) {\n        DETOUR_TRACE((\"new BYTE [cbNew] failed.\\n\"));\n        goto finish;\n    }\n    ZeroMemory(pbNew, cbNew);\n\n    PBYTE pbBase = pbModule;\n    PBYTE pbNext = pbBase\n        + inh.OptionalHeader.BaseOfCode\n        + inh.OptionalHeader.SizeOfCode\n        + inh.OptionalHeader.SizeOfInitializedData\n        + inh.OptionalHeader.SizeOfUninitializedData;\n    if (pbBase < pbNext) {\n        pbBase = pbNext;\n    }\n    DETOUR_TRACE((\"pbBase = %p\\n\", pbBase));\n\n    PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbBase, cbNew);\n    if (pbNewIid == NULL) {\n        DETOUR_TRACE((\"FindAndAllocateNearBase failed.\\n\"));\n        goto finish;\n    }\n\n    PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew;\n    DWORD_XX *pt;\n\n    DWORD obBase = (DWORD)(pbNewIid - pbModule);\n    DWORD dwProtect = 0;\n\n    if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) {\n        // Read the old import directory if it exists.\n        DETOUR_TRACE((\"IMPORT_DIRECTORY perms=%x\\n\", dwProtect));\n\n        if (!ReadProcessMemory(hProcess,\n                               pbModule + inh.IMPORT_DIRECTORY.VirtualAddress,\n                               &piid[nDlls],\n                               nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead)\n            || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) {\n\n            DETOUR_TRACE((\"ReadProcessMemory(imports) failed: %d\\n\", GetLastError()));\n            goto finish;\n        }\n    }\n\n    for (n = 0; n < nDlls; n++) {\n        HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]);\n        if (FAILED(hrRet)) {\n            DETOUR_TRACE((\"StringCchCopyA failed: %d\\n\", GetLastError()));\n            goto finish;\n        }\n\n        // After copying the string, we patch up the size \"??\" bits if any.\n        hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr,\n                                     cbNew - obStr,\n                                     DETOURS_STRINGIFY(DETOURS_BITS_XX));\n        if (FAILED(hrRet)) {\n            DETOUR_TRACE((\"ReplaceOptionalSizeA failed: %d\\n\", GetLastError()));\n            goto finish;\n        }\n\n        DWORD nOffset = obTab + (sizeof(DWORD_XX) * (4 * n));\n        piid[n].OriginalFirstThunk = obBase + nOffset;\n        pt = ((DWORD_XX*)(pbNew + nOffset));\n        pt[0] = IMAGE_ORDINAL_FLAG_XX + 1;\n        pt[1] = 0;\n\n        nOffset = obTab + (sizeof(DWORD_XX) * ((4 * n) + 2));\n        piid[n].FirstThunk = obBase + nOffset;\n        pt = ((DWORD_XX*)(pbNew + nOffset));\n        pt[0] = IMAGE_ORDINAL_FLAG_XX + 1;\n        pt[1] = 0;\n        piid[n].TimeDateStamp = 0;\n        piid[n].ForwarderChain = 0;\n        piid[n].Name = obBase + obStr;\n\n        obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1);\n    }\n    _Analysis_assume_(obStr <= cbNew);\n\n#if 0\n    for (i = 0; i < nDlls + nOldDlls; i++) {\n        DETOUR_TRACE((\"%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\\n\",\n                      i,\n                      piid[i].OriginalFirstThunk,\n                      piid[i].TimeDateStamp,\n                      piid[i].ForwarderChain,\n                      piid[i].Name,\n                      piid[i].FirstThunk));\n        if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) {\n            break;\n        }\n    }\n#endif\n\n    if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) {\n        DETOUR_TRACE((\"WriteProcessMemory(iid) failed: %d\\n\", GetLastError()));\n        goto finish;\n    }\n\n    DETOUR_TRACE((\"obBaseBef = %08x..%08x\\n\",\n                  inh.IMPORT_DIRECTORY.VirtualAddress,\n                  inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size));\n    DETOUR_TRACE((\"obBaseAft = %08x..%08x\\n\", obBase, obBase + obStr));\n\n    // If the file doesn't have an IAT_DIRECTORY, we create it...\n    if (inh.IAT_DIRECTORY.VirtualAddress == 0) {\n        inh.IAT_DIRECTORY.VirtualAddress = obBase;\n        inh.IAT_DIRECTORY.Size = cbNew;\n    }\n\n    inh.IMPORT_DIRECTORY.VirtualAddress = obBase;\n    inh.IMPORT_DIRECTORY.Size = cbNew;\n\n    /////////////////////// Update the NT header for the new import directory.\n    //\n    if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,\n                                           PAGE_EXECUTE_READWRITE, &dwProtect)) {\n        DETOUR_TRACE((\"VirtualProtectEx(inh) write failed: %d\\n\", GetLastError()));\n        goto finish;\n    }\n\n    inh.OptionalHeader.CheckSum = 0;\n\n    if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) {\n        DETOUR_TRACE((\"WriteProcessMemory(idh) failed: %d\\n\", GetLastError()));\n        goto finish;\n    }\n    DETOUR_TRACE((\"WriteProcessMemory(idh:%p..%p)\\n\", pbModule, pbModule + sizeof(idh)));\n\n    if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) {\n        DETOUR_TRACE((\"WriteProcessMemory(inh) failed: %d\\n\", GetLastError()));\n        goto finish;\n    }\n    DETOUR_TRACE((\"WriteProcessMemory(inh:%p..%p)\\n\",\n                  pbModule + idh.e_lfanew,\n                  pbModule + idh.e_lfanew + sizeof(inh)));\n\n    if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders,\n                          dwProtect, &dwProtect)) {\n        DETOUR_TRACE((\"VirtualProtectEx(idh) restore failed: %d\\n\", GetLastError()));\n        goto finish;\n    }\n\n    fSucceeded = TRUE;\n    goto finish;\n}\n"
  },
  {
    "path": "Detours.StaticLibrary/Detours.StaticLibrary.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{265B8ACE-DD59-45FB-A26C-3AFAA8F96469}</ProjectGuid>\n    <ProjectName>Detours.StaticLibrary</ProjectName>\n    <RootNamespace>Detours</RootNamespace>\n    <MileProjectType>StaticLibrary</MileProjectType>\n    <MileProjectEnableVCLTLSupport>true</MileProjectEnableVCLTLSupport>\n  </PropertyGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x86.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.ARM64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.Default.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.props\" />\n  <PropertyGroup Label=\"Configuration\">\n    <OutDir>$(MileProjectBinariesPath)$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>$(MileProjectObjectsPath)$(Configuration)\\$(MSBuildProjectName)\\$(Platform)\\</IntDir>\n    <GeneratedFilesDir>$(IntDir)Generated Files\\</GeneratedFilesDir>\n  </PropertyGroup>\n  <PropertyGroup Label=\"Configuration\">\n    <RunCodeAnalysis>true</RunCodeAnalysis>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <WholeProgramOptimization>false</WholeProgramOptimization>\n    </ClCompile>\n  </ItemDefinitionGroup>\n  <Target Name=\"CustomPublish\" AfterTargets=\"Build\">\n    <ItemGroup>\n      <DetoursLicense Include=\"$(SolutionDir)\\LICENSE\" />\n      <DetoursLicense Include=\"$(SolutionDir)\\README.md\" />\n      <DetoursHeaders Include=\"$(DetoursDirectory)\\detours.h\" />\n      <DetoursProduct Include=\"$(OutDir)$(TargetName).lib\" />\n      <DetoursProduct Include=\"$(OutDir)$(TargetName).pdb\" />\n    </ItemGroup>\n    <Copy SourceFiles=\"@(DetoursLicense)\" DestinationFolder=\"$(PublishDirectory)\" />\n    <Copy SourceFiles=\"@(DetoursHeaders)\" DestinationFolder=\"$(PublishDirectory)Include\" />\n    <Copy SourceFiles=\"@(DetoursProduct)\" DestinationFolder=\"$(PublishDirectory)Library\\$(Configuration)\\$(Platform)\" />\n  </Target>\n  <ItemGroup>\n    <ClInclude Include=\"$(DetoursDirectory)detours.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"$(DetoursDirectory)creatwth.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)detours.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disasm.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolarm.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolarm64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolia64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolx64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolx86.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)image.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)modules.cpp\" />\n  </ItemGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.targets\" />\n</Project>"
  },
  {
    "path": "Detours.StaticLibraryForDriver/Detours.StaticLibraryForDriver.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{17B37D16-1230-464F-B3F8-F595C6E8B6FD}</ProjectGuid>\n    <ProjectName>Detours.StaticLibraryForDriver</ProjectName>\n    <RootNamespace>Detours</RootNamespace>\n    <MileProjectType>StaticLibrary</MileProjectType>\n    <MileProjectUseKernelMode>true</MileProjectUseKernelMode>\n    <MileProjectUseWindowsDriverKit>true</MileProjectUseWindowsDriverKit>\n  </PropertyGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x86.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.ARM64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.Default.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.props\" />\n  <PropertyGroup Label=\"Configuration\">\n    <OutDir>$(MileProjectBinariesPath)$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>$(MileProjectObjectsPath)$(Configuration)\\$(MSBuildProjectName)\\$(Platform)\\</IntDir>\n    <GeneratedFilesDir>$(IntDir)Generated Files\\</GeneratedFilesDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <WholeProgramOptimization>false</WholeProgramOptimization>\n    </ClCompile>\n  </ItemDefinitionGroup>\n  <Target Name=\"CustomPublish\" AfterTargets=\"Build\">\n    <ItemGroup>\n      <DetoursLicense Include=\"$(SolutionDir)\\LICENSE\" />\n      <DetoursLicense Include=\"$(SolutionDir)\\README.md\" />\n      <DetoursHeaders Include=\"$(DetoursDirectory)\\detours.h\" />\n      <DetoursProduct Include=\"$(OutDir)$(TargetName).lib\" />\n      <DetoursProduct Include=\"$(OutDir)$(TargetName).pdb\" />\n    </ItemGroup>\n    <Copy SourceFiles=\"@(DetoursLicense)\" DestinationFolder=\"$(PublishDirectory)\" />\n    <Copy SourceFiles=\"@(DetoursHeaders)\" DestinationFolder=\"$(PublishDirectory)Include\" />\n    <Copy SourceFiles=\"@(DetoursProduct)\" DestinationFolder=\"$(PublishDirectory)Library\\$(Configuration)\\$(Platform)\" />\n  </Target>\n  <ItemGroup>\n    <ClInclude Include=\"$(DetoursDirectory)detours.h\" />\n    <ClInclude Include=\"$(DetoursDirectory)api_thunks.h\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"$(DetoursDirectory)api_thunks.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)creatwth.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)detoursx.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disasm.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolarm.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolarm64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolia64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolx64.cpp\" />\n    <ClCompile Include=\"$(DetoursDirectory)disolx86.cpp\" />\n  </ItemGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.targets\" />\n</Project>"
  },
  {
    "path": "Detours.Test/Detours.Test.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{2AAF9051-80FB-47C7-BC5A-C24FA1CF7B04}</ProjectGuid>\n    <ProjectName>Detours.Test</ProjectName>\n    <RootNamespace>Detours.Test</RootNamespace>\n    <MileProjectType>DynamicLibrary</MileProjectType>\n    <MileProjectEnableVCLTLSupport>true</MileProjectEnableVCLTLSupport>\n  </PropertyGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x86.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.ARM64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.Default.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.props\" />\n  <PropertyGroup Label=\"Configuration\">\n    <OutDir>$(MileProjectBinariesPath)$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>$(MileProjectObjectsPath)$(Configuration)\\$(MSBuildProjectName)\\$(Platform)\\</IntDir>\n    <GeneratedFilesDir>$(IntDir)Generated Files\\</GeneratedFilesDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <AdditionalIncludeDirectories>$(PublishDirectory)\\Include\\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <ModuleDefinitionFile>Module.def</ModuleDefinitionFile>\n      <AdditionalLibraryDirectories>$(PublishDirectory)Library\\$(Configuration)\\$(Platform)\\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>Detours.StaticLibrary.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <None Include=\"Module.def\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Main.cpp\" />\n  </ItemGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.targets\" />\n</Project>"
  },
  {
    "path": "Detours.Test/Main.cpp",
    "content": "#include <Veil.h>\n#include <detours.h>\n\nvoid LogPrint(\n    _In_z_ _Printf_format_string_ PCSTR Format,\n    ...);\n#define LOG(fmt, ...) LogPrint(\"[DetoursX][%s():%u] \" fmt \"\\n\", __FUNCTION__, __LINE__, ## __VA_ARGS__)\n\n\nnamespace Hook\n{\n    class DetourLockGuard\n    {\n        volatile long& mAtom;\n\n    public:\n        explicit DetourLockGuard(volatile long& atom) noexcept\n            : mAtom(atom)\n        {\n            InterlockedCompareExchange(&mAtom, true, false);\n        }\n        ~DetourLockGuard()\n        {\n            InterlockedCompareExchange(&mAtom, false, true);\n        }\n    };\n\n    enum : uint8_t {\n        IdxOfZwOpenFile = 0u,\n        IdxOfZwCreateFile\n    };\n\n    thread_local volatile long _locks[2] = { false };\n    inline volatile long& _lock(size_t idx) {\n        return _locks[idx];\n    }\n\n    static auto _ZwOpenFile = (decltype(::ZwOpenFile)*)nullptr;\n    NTSTATUS NTAPI ZwOpenFile(\n        _Out_ PHANDLE FileHandle,\n        _In_ ACCESS_MASK DesiredAccess,\n        _In_ POBJECT_ATTRIBUTES ObjectAttributes,\n        _Out_ PIO_STATUS_BLOCK IoStatusBlock,\n        _In_ ULONG ShareAccess,\n        _In_ ULONG OpenOptions\n    )\n    {\n        if (!InterlockedCompareExchange(&_lock(IdxOfZwOpenFile), false, false))\n        {\n            auto guard = DetourLockGuard(_lock(IdxOfZwOpenFile));\n            LOG(\"%wZ\", ObjectAttributes ? ObjectAttributes->ObjectName : nullptr);\n\n            UNICODE_STRING Match = RTL_CONSTANT_STRING(L\"*\\\\123.TXT\");\n            if (ObjectAttributes && RtlIsNameInExpression(&Match, ObjectAttributes->ObjectName, TRUE, NULL)) {\n                return STATUS_ACCESS_DENIED;\n            }\n        }\n\n        return _ZwOpenFile(\n            FileHandle,\n            DesiredAccess,\n            ObjectAttributes,\n            IoStatusBlock,\n            ShareAccess,\n            OpenOptions);\n    }\n\n    static auto _ZwCreateFile = (decltype(::ZwCreateFile)*)nullptr;\n    NTSTATUS NTAPI ZwCreateFile(\n        _Out_ PHANDLE FileHandle,\n        _In_ ACCESS_MASK DesiredAccess,\n        _In_ POBJECT_ATTRIBUTES ObjectAttributes,\n        _Out_ PIO_STATUS_BLOCK IoStatusBlock,\n        _In_opt_ PLARGE_INTEGER AllocationSize,\n        _In_ ULONG FileAttributes,\n        _In_ ULONG ShareAccess,\n        _In_ ULONG CreateDisposition,\n        _In_ ULONG CreateOptions,\n        _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,\n        _In_ ULONG EaLength\n    )\n    {\n        if (!InterlockedCompareExchange(&_lock(IdxOfZwCreateFile), false, false))\n        {\n            auto guard = DetourLockGuard(_lock(IdxOfZwCreateFile));\n            LOG(\"%wZ\", ObjectAttributes ? ObjectAttributes->ObjectName : nullptr);\n\n            UNICODE_STRING Match = RTL_CONSTANT_STRING(L\"*\\\\123.TXT\");\n            if (ObjectAttributes && RtlIsNameInExpression(&Match, ObjectAttributes->ObjectName, TRUE, NULL)) {\n                return STATUS_ACCESS_DENIED;\n            }\n        }\n\n        return _ZwCreateFile(\n            FileHandle,\n            DesiredAccess,\n            ObjectAttributes,\n            IoStatusBlock,\n            AllocationSize,\n            FileAttributes,\n            ShareAccess,\n            CreateDisposition,\n            CreateOptions,\n            EaBuffer,\n            EaLength);\n    }\n}\n\nnamespace Detours::Test\n{\n    EXTERN_C LONG MainEntry()\n    {\n        LOG(\"Enter\");\n\n        DetourRestoreAfterWith();\n\n        DetourTransactionBegin();\n        DetourUpdateThread(ZwCurrentThread());\n        {\n            using namespace Hook;\n\n            _ZwOpenFile   = ::ZwOpenFile;\n            _ZwCreateFile = ::ZwCreateFile;\n\n            DetourAttach((void**)&_ZwOpenFile,   Hook::ZwOpenFile);\n            DetourAttach((void**)&_ZwCreateFile, Hook::ZwCreateFile);\n        }\n        DetourTransactionCommit();\n\n        LOG(\"Exit\");\n        return STATUS_SUCCESS;\n    }\n\n    EXTERN_C VOID MainExit()\n    {\n        LOG(\"Enter\");\n\n        DetourTransactionBegin();\n        DetourUpdateThread(ZwCurrentThread());\n        {\n            using namespace Hook;\n            DetourDetach((void**)&_ZwOpenFile,   Hook::ZwOpenFile);\n            DetourDetach((void**)&_ZwCreateFile, Hook::ZwCreateFile);\n        }\n        DetourTransactionCommit();\n\n        LOG(\"Exit\");\n    }\n\n    EXTERN_C BOOL WINAPI DllMain(_In_ void* /*DllHandle*/, _In_ unsigned Reason, _In_opt_ void* /*Reserved*/)\n    {\n        if (Reason == DLL_PROCESS_ATTACH) {\n            MainEntry();\n        }\n\n        if (Reason == DLL_PROCESS_DETACH) {\n            MainExit();\n        }\n\n        return TRUE;\n    }\n}\n\nvoid LogPrint(\n    _In_z_ _Printf_format_string_ PCSTR Format,\n    ...)\n{\n    va_list args;\n    va_start(args, Format);\n\n    int len = _vscprintf(Format, args);\n    if (len < 0)\n    {\n        return;\n    }\n    len += sizeof(\"\\0\");\n\n    char* buf = (char*)malloc(len);\n    if (buf == nullptr)\n    {\n        return;\n    }\n\n    len = vsnprintf_s(buf, len, len, Format, args);\n    if (len < 0)\n    {\n        return;\n    }\n    buf[len] = '\\0';\n\n    OutputDebugStringA(buf);\n    va_end(args);\n    free(buf);\n}\n"
  },
  {
    "path": "Detours.Test/Module.def",
    "content": "EXPORTS\n    DetourFinishHelperProcess  @1   NONAME"
  },
  {
    "path": "Detours.TestForDriver/Detours.TestForDriver.inf",
    "content": ";-------------------------------------------------------------------------\n; Detours.TestForDriver.INF -- NT Legacy Driver\n;\n; Copyright (c) 2019, Microsoft.Com LLC.  All rights reserved.\n;------------------------------------------------------------------------\n\n\n; INF MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/roadmap-for-device-and-driver-installation--windows-vista-and-later-\n;\n; Class And ClassGuid MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/ifs/file-system-filter-driver-classes-and-class-guids\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/system-defined-device-setup-classes-available-to-vendors\n[Version]\nSignature   = \"$WINDOWS NT$\"\nClass       = AntiVirus\nClassGuid   = {b1d1a169-c54f-4379-81db-bee7d88d7454}\nProvider    = %ManufacturerName%\nCatalogFile = Detours.TestForDriver.cat\nDriverVer   =\nPnpLockdown = 1\n\n\n;-------------------------------------------------------------------------\n; Installation Section\n;-------------------------------------------------------------------------\n\n\n; DestinationDirs MSDN\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-destinationdirs-section\n[DestinationDirs]\nDefaultDestDir      = 12\n\n\n; SourceDisksNames MSDN\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-sourcedisksnames-section\n[SourceDisksNames]\n1 = %DiskId%,,,\"\"\n\n\n; SourceDisksFiles MSDN\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-sourcedisksfiles-section\n; Each filename entry must specify the exact name of a file on the source disk. \n;     You cannot use a %strkey% token to specify the file name.\n[SourceDisksFiles]\nDetours.TestForDriver.sys        = 1,,\n\n\n; Copyfiles MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-copyfiles-directive\n[Install.Drivers]\nDetours.TestForDriver.sys,Detours.TestForDriver.sys,,0x00004002 ; COPYFLG_NOSKIP | COPYFLG_IN_USE_RENAME\n\n\n; Delfiles MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-delfiles-directive\n[Uninstall.Drivers]\nDetours.TestForDriver.sys\n\n\n;-------------------------------------------------------------------------\n; Service installation support\n;-------------------------------------------------------------------------\n\n\n[DefaultInstall.NT$ARCH$]\nCopyFiles       = Install.Drivers\n\n\n; LegacyUninstall MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/develop/creating-a-primitive-driver\n[DefaultUninstall.NT$ARCH$]\nLegacyUninstall = 1\nDelFiles        = Uninstall.Drivers\n\n\n[DefaultInstall.NT$ARCH$.Services]\n; You may want to add the SPSVCINST_STARTSERVICE flag, like this:\n;     AddService=%ServiceName%,0x800,InstallService.Arch ; SPSVCINST_STARTSERVICE\n; AddService MSDN\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-addservice-directive\nAddService=%ServiceName%,,InstallService\n\n\n; DelService MSDN:\n;     https://docs.microsoft.com/en-us/windows-hardware/drivers/install/inf-delservice-directive\n[DefaultUninstall.NT$ARCH$.Services]\nDelService = %ServiceName%,0x200\n\n\n[InstallService]\nDisplayName    = %ServiceName%\nDescription    = %ServiceDesc%\nServiceBinary  = %12%\\Detours.TestForDriver.sys\nServiceType    = 1                  ; SERVICE_KERNEL_DRIVER\nStartType      = 3                  ; 0 = SERVICE_BOOT_START\n                                    ; 1 = SERVICE_SYSTEM_START\n                                    ; 2 = SERVICE_AUTO_START\n                                    ; 3 = SERVICE_DEMAND_START\n                                    ; 4 = SERVICE_DISABLED\nErrorControl   = 1                  ; SERVICE_ERROR_NORMAL\n\n\n;-------------------------------------------------------------------------\n; Strings section\n;-------------------------------------------------------------------------\n\n\n[Strings]\nManufacturerName        = \"MeeSong\"\nServiceName             = \"Detours.TestForDriver\"\nServiceDesc             = \"Detours.TestForDriver Legacy Driver\"\nDiskId                  = \"Detours.TestForDriver Device Installation Disk\"\n"
  },
  {
    "path": "Detours.TestForDriver/Detours.TestForDriver.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>{46EB96DF-1785-4374-BFE5-A9F44F5670C0}</ProjectGuid>\n    <ProjectName>Detours.TestForDriver</ProjectName>\n    <RootNamespace>Detours.Test</RootNamespace>\n    <MileProjectType>WDMDriver</MileProjectType>\n  </PropertyGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x86.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.x64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Platform.ARM64.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.Default.props\" />\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.props\" />\n  <PropertyGroup Label=\"Configuration\">\n    <OutDir>$(MileProjectBinariesPath)$(Configuration)\\$(Platform)\\</OutDir>\n    <IntDir>$(MileProjectObjectsPath)$(Configuration)\\$(MSBuildProjectName)\\$(Platform)\\</IntDir>\n    <GeneratedFilesDir>$(IntDir)Generated Files\\</GeneratedFilesDir>\n  </PropertyGroup>\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <AdditionalIncludeDirectories>$(PublishDirectory)\\Include\\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n        <AdditionalOptions>/INTEGRITYCHECK %(AdditionalOptions)</AdditionalOptions>\n        <AdditionalLibraryDirectories>$(PublishDirectory)Library\\$(Configuration)\\$(Platform)\\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n        <AdditionalDependencies>Detours.StaticLibraryForDriver.lib;%(AdditionalDependencies)</AdditionalDependencies>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <FilesToPackage Include=\"$(TargetPath)\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Inf Include=\"Detours.TestForDriver.inf\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"Main.cpp\" />\n  </ItemGroup>\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Cpp.targets\" />\n</Project>"
  },
  {
    "path": "Detours.TestForDriver/Main.cpp",
    "content": "// unnecessary, fix ReSharper's code analysis.\n#pragma warning(suppress: 4117)\n#define _KERNEL_MODE 1\n\n#include <Veil.h>\n#include <detours.h>\n\n#define LOG(fmt, ...) DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, \"[DetoursX][%s():%u] \" fmt \"\\n\", __FUNCTION__, __LINE__, ## __VA_ARGS__)\n\nnamespace\n{\n    PDRIVER_OBJECT MainDriverObject = nullptr;\n}\n\nnamespace Hook\n{\n    class DetourLockGuard\n    {\n        volatile long& mAtom;\n\n    public:\n        explicit DetourLockGuard(volatile long& atom) noexcept\n            : mAtom(atom)\n        {\n            InterlockedCompareExchange(&mAtom, true, false);\n        }\n        ~DetourLockGuard()\n        {\n            InterlockedCompareExchange(&mAtom, false, true);\n        }\n    };\n\n    enum : uint8_t {\n        IdxOfMmIsAddressValid = 0u,\n    };\n\n    volatile long _locks[2][256] = { { false },{ false } };\n    inline volatile long& _lock(size_t idx) {\n        return _locks[idx][KeGetCurrentProcessorNumber()];\n    }\n\n    static auto _MmIsAddressValid = (decltype(::MmIsAddressValid)*)nullptr;\n    BOOLEAN MmIsAddressValid(\n        _In_ PVOID VirtualAddress\n    )\n    {\n        if (!InterlockedCompareExchange(&_lock(IdxOfMmIsAddressValid), false, false))\n        {\n            auto guard = DetourLockGuard(_lock(IdxOfMmIsAddressValid));\n\n            constexpr int max = 3;\n            static int count = 0;\n\n            if (count < max) {\n                ++count;\n                LOG(\"%p\", VirtualAddress);\n            }\n        }\n\n        return _MmIsAddressValid(VirtualAddress);\n    }\n\n    // Note: https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/windows-kernel-mode-process-and-thread-manager#best\n    VOID CreateProcessCallback(\n        _Inout_ PEPROCESS Process,\n        _In_ HANDLE /*ProcessId*/,\n        _Inout_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo\n    )\n    {\n        do\n        {\n            if (!CreateInfo || !CreateInfo->FileOpenNameAvailable) {\n                break;\n            }\n\n             UNICODE_STRING Target = RTL_CONSTANT_STRING(L\"*\\\\NOTEPAD.EXE\");\n             if (FsRtlIsNameInExpression(&Target, (PUNICODE_STRING)CreateInfo->ImageFileName, TRUE, NULL) == FALSE) {\n                 break;\n             }\n\n             HANDLE Handle = NULL;\n             NTSTATUS Status = ObOpenObjectByPointer(Process, OBJ_KERNEL_HANDLE, NULL, PROCESS_ALL_ACCESS,\n                 *PsProcessType, KernelMode, &Handle);\n             if (!NT_SUCCESS(Status)) {\n                 break;\n             }\n\n             PIO_WORKITEM WorkItem = IoAllocateWorkItem((PDEVICE_OBJECT)MainDriverObject);\n             IoQueueWorkItemEx(WorkItem, [](\n                 _In_ PVOID IoObject, _In_opt_ PVOID Handle, _In_ PIO_WORKITEM IoWorkItem)\n             {\n                 UNREFERENCED_PARAMETER(IoObject);\n\n                 if (Handle) {\n                     LPCSTR Dlls[] = {\n                         \"C:\\\\Detours.Test.dll\",\n                     };\n\n                     if (DetourUpdateProcessWithDll(Handle, Dlls, _countof(Dlls))) {\n                         for (const auto& Dll : Dlls) {\n                             LOG(\"Import %s to NOTEPAD.EXE\", Dll);\n                         }\n                     }\n\n                     (void)ObCloseHandle(Handle, KernelMode);\n                 }\n\n                 if (IoWorkItem) {\n                     IoFreeWorkItem(IoWorkItem);\n                 }\n\n             }, DelayedWorkQueue, Handle);\n        } while (false);\n    }\n}\n\nnamespace Detours::Test\n{\n    EXTERN_C VOID DriverUnload(PDRIVER_OBJECT);\n\n    EXTERN_C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING)\n    {\n        LOG(\"Enter\");\n        MainDriverObject = DriverObject;\n        DriverObject->DriverUnload = DriverUnload;\n\n        NTSTATUS Status = PsSetCreateProcessNotifyRoutineEx(Hook::CreateProcessCallback, false);\n        if (!NT_SUCCESS(Status)) {\n            LOG(\"PsSetCreateProcessNotifyRoutineEx failed: %08X\", Status);\n            return Status;\n        }\n\n        DetourTransactionBegin();\n        DetourUpdateThread(ZwCurrentThread());\n        {\n            Hook::_MmIsAddressValid = MmIsAddressValid;\n            DetourAttach((void**)&Hook::_MmIsAddressValid, Hook::MmIsAddressValid);\n        }\n        DetourTransactionCommit();\n\n        MmIsAddressValid((PVOID)(LONG_PTR)0x1111222233334444);\n\n        LOG(\"Exit\");\n\n        return STATUS_SUCCESS;\n    }\n\n    EXTERN_C VOID DriverUnload(PDRIVER_OBJECT)\n    {\n        LOG(\"Enter\");\n\n        (void)PsSetCreateProcessNotifyRoutineEx(Hook::CreateProcessCallback, true);\n\n        DetourTransactionBegin();\n        DetourUpdateThread(ZwCurrentThread());\n        {\n            DetourDetach((void**)&Hook::_MmIsAddressValid, Hook::MmIsAddressValid);\n        }\n        DetourTransactionCommit();\n\n        LOG(\"Exit\");\n    }\n    \n}"
  },
  {
    "path": "DetoursX.slnx",
    "content": "<Solution>\n  <Configurations>\n    <Platform Name=\"ARM64\" />\n    <Platform Name=\"x64\" />\n    <Platform Name=\"x86\" />\n  </Configurations>\n  <Project Path=\"Detours.StaticLibrary/Detours.StaticLibrary.vcxproj\" Id=\"265b8ace-dd59-45fb-a26c-3afaa8f96469\" />\n  <Project Path=\"Detours.StaticLibraryForDriver/Detours.StaticLibraryForDriver.vcxproj\" Id=\"17b37d16-1230-464f-b3f8-f595c6e8b6fd\">\n    <Build Solution=\"*|x86\" Project=\"false\" />\n  </Project>\n  <Project Path=\"Detours.Test/Detours.Test.vcxproj\" Id=\"2aaf9051-80fb-47c7-bc5a-c24fa1cf7b04\">\n    <BuildDependency Project=\"Detours.StaticLibrary/Detours.StaticLibrary.vcxproj\" />\n  </Project>\n  <Project Path=\"Detours.TestForDriver/Detours.TestForDriver.vcxproj\" Id=\"46eb96df-1785-4374-bfe5-a9f44f5670c0\">\n    <BuildDependency Project=\"Detours.StaticLibraryForDriver/Detours.StaticLibraryForDriver.vcxproj\" />\n    <Build Solution=\"*|x86\" Project=\"false\" />\n  </Project>\n</Solution>\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- \nThis file allow for customizing your build process.\nSee: https://learn.microsoft.com/visualstudio/msbuild/customize-your-build\n-->\n<Project>\n  <!--\n  Uncomment if you need to enable inclusion of another Directory.Build.props file from a parent directory\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))\" />\n  -->\n  <PropertyGroup>\n    <DetoursDirectory>$(MSBuildThisFileDirectory)Detours\\</DetoursDirectory>\n    <PublishDirectory>$(MSBuildThisFileDirectory)Publish\\</PublishDirectory>\n    <MileProjectOutputPath>$(MSBuildThisFileDirectory)Output\\</MileProjectOutputPath>\n  </PropertyGroup>\n\n  <Import Sdk=\"Mile.Project.Configurations\" Project=\"Mile.Project.Build.props\" />\n\n  <ItemDefinitionGroup>\n    <ClCompile>\n      <AdditionalIncludeDirectories>$(DetoursDirectory);$(MSBuildProjectDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n  </ItemDefinitionGroup>\n  <PropertyGroup>\n    <ReplaceWildcardsInProjectItems>true</ReplaceWildcardsInProjectItems>\n    <ForceImportAfterCppProps>$(MSBuildThisFileDirectory)\\Directory.Packages.Cpp.props</ForceImportAfterCppProps>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "Directory.Build.targets",
    "content": "<!-- \nThis file allow for customizing your build process.\nSee: https://learn.microsoft.com/visualstudio/msbuild/customize-your-build\n-->\n<Project>\n  <!-- \n  Uncomment if you need to enable inclusion of another Directory.Build.targets file from a parent directory\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)../'))\" />\n  -->\n</Project>"
  },
  {
    "path": "Directory.Packages.Cpp.props",
    "content": "<!--\n  This enabled central package management. \n  This allows for controling all NuGet packages within the Directory.Packages.props file\n  See https://learn.microsoft.com/nuget/consume-packages/Central-Package-Management?WT.mc_id=DT-MVP-5003472\n  -->\n<Project>\n  <!--\n  Uncomment if you need to enable inclusion of another Directory.Packages.props file from a parent directory\n  <Import Project=\"$([MSBuild]::GetPathOfFileAbove(Directory.Packages.props, $(MSBuildThisFileDirectory)..))\" />\n  -->\n  <!--\n  This defines the set of centrally managed packages.\n  This would typically list all NuGet packages used within this solution.\n  -->\n  <ItemGroup>\n    <!--\n    <PackageReference Include=\"Microsoft.Windows.CppWinRT\" Version=\"2.0.240405.15\" />\n    -->\n    <PackageReference Include=\"Musa.Veil\" Version=\"1.4.1\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "InitializeVisualStudioEnvironment.cmd",
    "content": "@rem \n@rem PROJECT:   Mouri Internal Library Essentials\n@rem FILE:      InitializeVisualStudioEnvironment.cmd\n@rem PURPOSE:   Initialize Visual Studio environment script\n@rem \n@rem LICENSE:   The MIT License\n@rem \n@rem MAINTAINER: MouriNaruto (Kenji.Mouri@outlook.com)\n@rem \n\n@echo off\n\nset VisualStudioInstallerFolder=\"%ProgramFiles(x86)%\\Microsoft Visual Studio\\Installer\"\nif %PROCESSOR_ARCHITECTURE%==x86 set VisualStudioInstallerFolder=\"%ProgramFiles%\\Microsoft Visual Studio\\Installer\"\n\npushd %VisualStudioInstallerFolder%\nfor /f \"usebackq tokens=*\" %%i in (`vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do (\n  set VisualStudioInstallDir=%%i\n)\npopd\n\ncall \"%VisualStudioInstallDir%\\VC\\Auxiliary\\Build\\vcvarsall.bat\" x64\n"
  },
  {
    "path": "LICENSE",
    "content": "# Copyright (c) Microsoft Corporation\n\nAll rights reserved.\n\n# MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# [DetoursX](https://github.com/mirokaku/DetoursX)\n\n[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/MiroKaku/DetoursX/blob/master/LICENSE)\n![Windows](https://img.shields.io/badge/Windows-10+-orange.svg)\n![Visual Studio](https://img.shields.io/badge/Visual%20Studio-2022-purple.svg)\n\n* [简体中文](https://github.com/MiroKaku/DetoursX/blob/master/README.zh-CN.md)\n\n## About\n\nKernel extension version of Detours. (Did not bypass PatchGuard)\n\n### Support progress\n\n- [x] DetourTransactionBegin\n- [x] DetourTransactionAbort\n- [x] DetourTransactionCommit\n- [x] DetourTransactionCommitEx\n- [x] DetourUpdateThread\n- [x] DetourAttach\n- [x] DetourAttachEx\n- [x] DetourDetach\n- [x] DetourDetachEx\n- [x] DetourCodeFromPointer\n- [x] DetourCopyInstruction\n- [x] DetourUpdateProcessWithDll\n- [x] DetourUpdateProcessWithDllEx\n- [x] DetourCopyPayloadToProcess\n\n## Wiki\n\n[Microsoft Detours Wiki](https://github.com/microsoft/Detours/wiki)\n\n## License\n\nCopyright (c) Microsoft Corporation. All rights reserved.\n\nLicensed under the [MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md) License.\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "# [DetoursX](https://github.com/mirokaku/DetoursX)\n\n[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/MiroKaku/DetoursX/blob/master/LICENSE)\n![Windows](https://img.shields.io/badge/Windows-10+-orange.svg)\n![Visual Studio](https://img.shields.io/badge/Visual%20Studio-2022-purple.svg)\n\n* [English](https://github.com/MiroKaku/DetoursX/blob/master/README.md)\n\n## 1. 关于\n\nDetoursX 是基于微软 [Detours 4.0.1](https://github.com/microsoft/Detours/tree/4.0.1) 修改的内核扩展版本。可以使用 DetoursX 在内核中安全的 Hook 函数。\n（注：DetoursX 并不负责绕过 PatchGuard）\n\n### 1.1 原理\n\n* X64 模式下，在目标函数所在的区段尾部，寻找空白区存储跳板地址，用来支持远跳转。\n* 在 `DetourTransactionCommitEx` 中，通过 `KeGenericCallDpc` 进行处理器同步处理 `CopyMemory`，来达到安全 Hook 的目的。\n\n### 1.2 支持情况\n\n- [x] DetourTransactionBegin\n- [x] DetourTransactionAbort\n- [x] DetourTransactionCommit\n- [x] DetourTransactionCommitEx\n- [x] DetourUpdateThread\n- [x] DetourAttach\n- [x] DetourAttachEx\n- [x] DetourDetach\n- [x] DetourDetachEx\n- [x] DetourCodeFromPointer\n- [x] DetourCopyInstruction\n- [x] DetourUpdateProcessWithDll\n- [x] DetourUpdateProcessWithDllEx\n- [x] DetourCopyPayloadToProcess\n\n## 文档\n\n[Microsoft Detours Wiki](https://github.com/microsoft/Detours/wiki)\n\n## 许可证\n\n微软公司©版权所有，保留所有权利。\n\n根据 [MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md) 许可证获得许可。\n"
  },
  {
    "path": "global.json",
    "content": "{\n  \"msbuild-sdks\": {\n    \"Mile.Project.Configurations\": \"1.0.1622\"\n  }\n}"
  }
]