From e79f8d3658cafe2623c1fd3524dbe521a13d2be5 Mon Sep 17 00:00:00 2001 From: Ratin Gao Date: Tue, 28 Nov 2023 01:16:22 +0800 Subject: [PATCH 01/19] Initial commit --- .editorconfig | 76 +- .github/workflows/msbuild.yml | 38 + .gitignore | 45 +- .gitmodules | 3 + .../README.md | 111 + .../README.zh-CN.md | 111 + .../README.md | 56 + .../README.zh-CN.md | 56 + Docs/TechWiki/Implement Delay Hook/README.md | 98 + .../Implement Delay Hook/README.zh-CN.md | 98 + .../README.md | 246 ++ .../README.zh-CN.md | 246 ++ LICENSE | 394 ++- README.md | 162 +- README.zh-CN.md | 106 + Source/.editorconfig | 77 + Source/.gitignore | 8 + Source/Demo/DeadLock.c | 150 + Source/Demo/DelayHook.c | 90 + Source/Demo/Demo.h | 59 + Source/Demo/Demo.vcxproj | 199 ++ Source/Demo/Demo.vcxproj.filters | 14 + Source/Demo/Main.c | 144 + Source/Demo/packages.config | 4 + Source/Detours/.editorconfig | 10 + .../.github}/ISSUE_TEMPLATE/bug-report.md | 0 .../.github}/ISSUE_TEMPLATE/question.md | 0 .../pull_request_template.md | 0 .../Detours/.github}/codeql/codeql-config.yml | 0 .../Detours/.github}/dependabot.yml | 0 .../Detours/.github}/fabricbot.json | 0 .../Detours/.github}/workflows/main.yml | 0 Source/Detours/.gitignore | 41 + CREDITS.md => Source/Detours/CREDITS.md | 0 Source/Detours/LICENSE | 21 + LICENSE.md => Source/Detours/LICENSE.md | 0 Makefile => Source/Detours/Makefile | 0 Source/Detours/README.md | 56 + SECURITY.md => Source/Detours/SECURITY.md | 0 {samples => Source/Detours/samples}/Makefile | 0 .../Detours/samples}/README.TXT | 0 .../Detours/samples}/comeasy/Makefile | 0 .../Detours/samples}/comeasy/comeasy.cpp | 0 .../Detours/samples}/comeasy/wrotei.cpp | 0 .../Detours/samples}/comeasy/wrotei.rc | 0 .../Detours/samples}/commem/Makefile | 0 .../Detours/samples}/commem/commem.cpp | 0 .../Detours/samples}/common.mak | 0 .../Detours/samples}/cping/Makefile | 0 .../Detours/samples}/cping/ReadMe.Txt | 0 .../Detours/samples}/cping/cping.cpp | 0 .../Detours/samples}/cping/cping.dat | 0 .../Detours/samples}/cping/iping.idl | 0 .../Detours/samples}/disas/Makefile | 0 .../Detours/samples}/disas/arm.asm | 0 .../Detours/samples}/disas/disas.cpp | 0 .../Detours/samples}/disas/ia64.asm | 0 .../Detours/samples}/disas/unk.cpp | 0 .../Detours/samples}/disas/x64.asm | 0 .../Detours/samples}/disas/x86.cpp | 0 .../Detours/samples}/dtest/Makefile | 0 .../Detours/samples}/dtest/NORMAL_IA64.TXT | 0 .../Detours/samples}/dtest/NORMAL_X64.TXT | 0 .../Detours/samples}/dtest/NORMAL_X86.TXT | 0 .../Detours/samples}/dtest/dtarge.cpp | 0 .../Detours/samples}/dtest/dtarge.h | 0 .../Detours/samples}/dtest/dtarge.rc | 0 .../Detours/samples}/dtest/dtest.cpp | 0 .../Detours/samples}/dumpe/Makefile | 0 .../Detours/samples}/dumpe/dumpe.cpp | 0 .../Detours/samples}/dumpi/Makefile | 0 .../Detours/samples}/dumpi/dumpi.cpp | 0 .../Detours/samples}/dynamic_alloc/Makefile | 0 .../Detours/samples}/dynamic_alloc/main.cpp | 0 .../Detours/samples}/dynamic_alloc/x64.asm | 0 .../Detours/samples}/dynamic_alloc/x86.asm | 0 .../Detours/samples}/echo/Makefile | 0 .../Detours/samples}/echo/echofx.cpp | 0 .../Detours/samples}/echo/echofx.rc | 0 .../Detours/samples}/echo/echonul.cpp | 0 .../Detours/samples}/echo/main.cpp | 0 .../Detours/samples}/einst/Makefile | 0 .../Detours/samples}/einst/edll1x.cpp | 0 .../Detours/samples}/einst/edll2x.cpp | 0 .../Detours/samples}/einst/edll3x.cpp | 0 .../Detours/samples}/einst/einst.cpp | 0 .../Detours/samples}/excep/Makefile | 0 .../Detours/samples}/excep/excep.cpp | 0 .../Detours/samples}/excep/firstexc.cpp | 0 .../Detours/samples}/excep/firstexc.h | 0 .../Detours/samples}/findfunc/Makefile | 0 .../Detours/samples}/findfunc/extend.cpp | 0 .../Detours/samples}/findfunc/extend.rc | 0 .../Detours/samples}/findfunc/findfunc.cpp | 0 .../Detours/samples}/findfunc/symtest.cpp | 0 .../Detours/samples}/findfunc/target.cpp | 0 .../Detours/samples}/findfunc/target.h | 0 .../Detours/samples}/findfunc/target.rc | 0 .../Detours/samples}/impmunge/Makefile | 0 .../Detours/samples}/impmunge/impmunge.cpp | 0 .../Detours/samples}/member/Makefile | 0 .../Detours/samples}/member/member.cpp | 0 .../Detours/samples}/opengl/Makefile | 0 .../Detours/samples}/opengl/ogldet.cpp | 0 .../Detours/samples}/opengl/ogldet.rc | 0 .../Detours/samples}/opengl/testogl.cpp | 0 .../Detours/samples}/payload/Makefile | 0 .../Detours/samples}/payload/payload.cpp | 0 .../Detours/samples}/payload/payloadguid.hpp | 0 .../samples}/payload/payloadtarget.cpp | 0 .../Detours/samples}/region/Makefile | 0 .../Detours/samples}/region/region.cpp | 0 .../Detours/samples}/setdll/Makefile | 0 .../Detours/samples}/setdll/setdll.cpp | 0 .../Detours/samples}/simple/Makefile | 0 .../Detours/samples}/simple/simple.cpp | 0 .../Detours/samples}/simple/simple.rc | 0 .../Detours/samples}/simple/sleep5.cpp | 0 .../Detours/samples}/simple_safe/Makefile | 0 .../samples}/simple_safe/simple_safe.cpp | 0 .../samples}/simple_safe/simple_safe.rc | 0 .../Detours/samples}/simple_safe/sleep5.cpp | 0 .../Detours/samples}/slept/Makefile | 0 .../Detours/samples}/slept/NORMAL_IA64.TXT | 0 .../Detours/samples}/slept/NORMAL_X64.TXT | 0 .../Detours/samples}/slept/NORMAL_X86.TXT | 0 .../Detours/samples}/slept/dslept.cpp | 0 .../Detours/samples}/slept/dslept.rc | 0 .../Detours/samples}/slept/sleepbed.cpp | 0 .../Detours/samples}/slept/sleepnew.cpp | 0 .../Detours/samples}/slept/sleepold.cpp | 0 .../Detours/samples}/slept/slept.cpp | 0 .../Detours/samples}/slept/slept.h | 0 .../Detours/samples}/slept/slept.rc | 0 .../Detours/samples}/slept/verify.cpp | 0 .../Detours/samples}/syelog/Makefile | 0 .../Detours/samples}/syelog/sltest.cpp | 0 .../Detours/samples}/syelog/sltestp.cpp | 0 .../Detours/samples}/syelog/syelog.cpp | 0 .../Detours/samples}/syelog/syelog.h | 0 .../Detours/samples}/syelog/syelogd.cpp | 0 .../Detours/samples}/talloc/Makefile | 0 .../Detours/samples}/talloc/NORMAL_IA64.TXT | 0 .../Detours/samples}/talloc/NORMAL_X64.TXT | 0 .../Detours/samples}/talloc/talloc.cpp | 0 .../Detours/samples}/talloc/tdll1x.cpp | 0 .../Detours/samples}/talloc/tdll2x.cpp | 0 .../Detours/samples}/talloc/tdll3x.cpp | 0 .../Detours/samples}/talloc/tdll4x.cpp | 0 .../Detours/samples}/talloc/tdll5x.cpp | 0 .../Detours/samples}/talloc/tdll6x.cpp | 0 .../Detours/samples}/talloc/tdll7x.cpp | 0 .../Detours/samples}/talloc/tdll8x.cpp | 0 .../Detours/samples}/talloc/tdll9x.cpp | 0 .../Detours/samples}/traceapi/Makefile | 0 .../Detours/samples}/traceapi/_win32.cpp | 0 .../Detours/samples}/traceapi/testapi.cpp | 0 .../Detours/samples}/traceapi/trcapi.cpp | 0 .../Detours/samples}/traceapi/trcapi.rc | 0 .../Detours/samples}/tracebld/Makefile | 0 .../Detours/samples}/tracebld/tracebld.cpp | 0 .../Detours/samples}/tracebld/tracebld.h | 0 .../Detours/samples}/tracebld/trcbld.cpp | 0 .../Detours/samples}/tracebld/trcbld.rc | 0 .../Detours/samples}/tracelnk/Makefile | 0 .../Detours/samples}/tracelnk/trclnk.cpp | 0 .../Detours/samples}/tracelnk/trclnk.rc | 0 .../Detours/samples}/tracemem/Makefile | 0 .../Detours/samples}/tracemem/trcmem.cpp | 0 .../Detours/samples}/tracemem/trcmem.rc | 0 .../Detours/samples}/tracereg/Makefile | 0 .../Detours/samples}/tracereg/trcreg.cpp | 0 .../Detours/samples}/tracereg/trcreg.rc | 0 .../Detours/samples}/traceser/Makefile | 0 .../Detours/samples}/traceser/trcser.cpp | 0 .../Detours/samples}/traceser/trcser.rc | 0 .../Detours/samples}/tracessl/Makefile | 0 .../Detours/samples}/tracessl/trcssl.cpp | 0 .../Detours/samples}/tracessl/trcssl.rc | 0 .../Detours/samples}/tracetcp/Makefile | 0 .../Detours/samples}/tracetcp/trctcp.cpp | 0 .../Detours/samples}/tracetcp/trctcp.rc | 0 .../Detours/samples}/tryman/Makefile | 0 .../Detours/samples}/tryman/managed.cs | 0 .../Detours/samples}/tryman/size.cpp | 0 .../Detours/samples}/tryman/tryman.cpp | 0 .../Detours/samples}/tryman/tstman.cpp | 0 .../Detours/samples}/tryman/tstman.rc | 0 .../Detours/samples}/withdll/Makefile | 0 .../Detours/samples}/withdll/withdll.cpp | 0 {src => Source/Detours/src}/Makefile | 0 {src => Source/Detours/src}/creatwth.cpp | 0 {src => Source/Detours/src}/detours.cpp | 0 {src => Source/Detours/src}/detours.h | 0 {src => Source/Detours/src}/detver.h | 0 {src => Source/Detours/src}/disasm.cpp | 0 {src => Source/Detours/src}/disolarm.cpp | 0 {src => Source/Detours/src}/disolarm64.cpp | 0 {src => Source/Detours/src}/disolia64.cpp | 0 {src => Source/Detours/src}/disolx64.cpp | 0 {src => Source/Detours/src}/disolx86.cpp | 0 {src => Source/Detours/src}/image.cpp | 0 {src => Source/Detours/src}/modules.cpp | 0 {src => Source/Detours/src}/uimports.cpp | 0 system.mak => Source/Detours/system.mak | 0 {tests => Source/Detours/tests}/Makefile | 0 {tests => Source/Detours/tests}/catch.hpp | 0 {tests => Source/Detours/tests}/corruptor.cpp | 0 {tests => Source/Detours/tests}/corruptor.h | 0 {tests => Source/Detours/tests}/main.cpp | 0 {tests => Source/Detours/tests}/payload.cpp | 0 {tests => Source/Detours/tests}/payload.h | 0 .../Detours/tests}/process_helpers.cpp | 0 .../Detours/tests}/process_helpers.h | 0 .../Detours/tests}/test_image_api.cpp | 0 .../Detours/tests}/test_module_api.cpp | 0 {vc => Source/Detours/vc}/Detours.sln | 0 {vc => Source/Detours/vc}/Detours.vcxproj | 0 .../Detours/vc}/Detours.vcxproj.filters | 0 Source/Directory.Build.AfterCppDefault.props | 10 + Source/Directory.Build.props | 61 + Source/KNSoft.SlimDetours.nuspec | 27 + Source/KNSoft.SlimDetours.sln | 54 + Source/KNSoft.SlimDetours.targets | 15 + Source/KNSoft.SlimDetours.vcxproj | 229 ++ Source/KNSoft.SlimDetours.vcxproj.filters | 81 + Source/SlimDetours/Disassembler.c | 2711 +++++++++++++++++ Source/SlimDetours/Instruction.c | 670 ++++ Source/SlimDetours/Memory.c | 162 + Source/SlimDetours/SlimDetours.h | 149 + Source/SlimDetours/SlimDetours.inl | 253 ++ Source/SlimDetours/Thread.c | 175 ++ Source/SlimDetours/Trampoline.c | 462 +++ Source/SlimDetours/Transaction.c | 752 +++++ Source/packages.config | 4 + 235 files changed, 8413 insertions(+), 121 deletions(-) create mode 100644 .github/workflows/msbuild.yml create mode 100644 .gitmodules create mode 100644 Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.md create mode 100644 Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.zh-CN.md create mode 100644 Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.md create mode 100644 Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.zh-CN.md create mode 100644 Docs/TechWiki/Implement Delay Hook/README.md create mode 100644 Docs/TechWiki/Implement Delay Hook/README.zh-CN.md create mode 100644 Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.md create mode 100644 Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.zh-CN.md create mode 100644 README.zh-CN.md create mode 100644 Source/.editorconfig create mode 100644 Source/.gitignore create mode 100644 Source/Demo/DeadLock.c create mode 100644 Source/Demo/DelayHook.c create mode 100644 Source/Demo/Demo.h create mode 100644 Source/Demo/Demo.vcxproj create mode 100644 Source/Demo/Demo.vcxproj.filters create mode 100644 Source/Demo/Main.c create mode 100644 Source/Demo/packages.config create mode 100644 Source/Detours/.editorconfig rename {.github => Source/Detours/.github}/ISSUE_TEMPLATE/bug-report.md (100%) rename {.github => Source/Detours/.github}/ISSUE_TEMPLATE/question.md (100%) rename {.github => Source/Detours/.github}/PULL_REQUEST_TEMPLATE/pull_request_template.md (100%) rename {.github => Source/Detours/.github}/codeql/codeql-config.yml (100%) rename {.github => Source/Detours/.github}/dependabot.yml (100%) rename {.github => Source/Detours/.github}/fabricbot.json (100%) rename {.github => Source/Detours/.github}/workflows/main.yml (100%) create mode 100644 Source/Detours/.gitignore rename CREDITS.md => Source/Detours/CREDITS.md (100%) create mode 100644 Source/Detours/LICENSE rename LICENSE.md => Source/Detours/LICENSE.md (100%) rename Makefile => Source/Detours/Makefile (100%) create mode 100644 Source/Detours/README.md rename SECURITY.md => Source/Detours/SECURITY.md (100%) rename {samples => Source/Detours/samples}/Makefile (100%) rename {samples => Source/Detours/samples}/README.TXT (100%) rename {samples => Source/Detours/samples}/comeasy/Makefile (100%) rename {samples => Source/Detours/samples}/comeasy/comeasy.cpp (100%) rename {samples => Source/Detours/samples}/comeasy/wrotei.cpp (100%) rename {samples => Source/Detours/samples}/comeasy/wrotei.rc (100%) rename {samples => Source/Detours/samples}/commem/Makefile (100%) rename {samples => Source/Detours/samples}/commem/commem.cpp (100%) rename {samples => Source/Detours/samples}/common.mak (100%) rename {samples => Source/Detours/samples}/cping/Makefile (100%) rename {samples => Source/Detours/samples}/cping/ReadMe.Txt (100%) rename {samples => Source/Detours/samples}/cping/cping.cpp (100%) rename {samples => Source/Detours/samples}/cping/cping.dat (100%) rename {samples => Source/Detours/samples}/cping/iping.idl (100%) rename {samples => Source/Detours/samples}/disas/Makefile (100%) rename {samples => Source/Detours/samples}/disas/arm.asm (100%) rename {samples => Source/Detours/samples}/disas/disas.cpp (100%) rename {samples => Source/Detours/samples}/disas/ia64.asm (100%) rename {samples => Source/Detours/samples}/disas/unk.cpp (100%) rename {samples => Source/Detours/samples}/disas/x64.asm (100%) rename {samples => Source/Detours/samples}/disas/x86.cpp (100%) rename {samples => Source/Detours/samples}/dtest/Makefile (100%) rename {samples => Source/Detours/samples}/dtest/NORMAL_IA64.TXT (100%) rename {samples => Source/Detours/samples}/dtest/NORMAL_X64.TXT (100%) rename {samples => Source/Detours/samples}/dtest/NORMAL_X86.TXT (100%) rename {samples => Source/Detours/samples}/dtest/dtarge.cpp (100%) rename {samples => Source/Detours/samples}/dtest/dtarge.h (100%) rename {samples => Source/Detours/samples}/dtest/dtarge.rc (100%) rename {samples => Source/Detours/samples}/dtest/dtest.cpp (100%) rename {samples => Source/Detours/samples}/dumpe/Makefile (100%) rename {samples => Source/Detours/samples}/dumpe/dumpe.cpp (100%) rename {samples => Source/Detours/samples}/dumpi/Makefile (100%) rename {samples => Source/Detours/samples}/dumpi/dumpi.cpp (100%) rename {samples => Source/Detours/samples}/dynamic_alloc/Makefile (100%) rename {samples => Source/Detours/samples}/dynamic_alloc/main.cpp (100%) rename {samples => Source/Detours/samples}/dynamic_alloc/x64.asm (100%) rename {samples => Source/Detours/samples}/dynamic_alloc/x86.asm (100%) rename {samples => Source/Detours/samples}/echo/Makefile (100%) rename {samples => Source/Detours/samples}/echo/echofx.cpp (100%) rename {samples => Source/Detours/samples}/echo/echofx.rc (100%) rename {samples => Source/Detours/samples}/echo/echonul.cpp (100%) rename {samples => Source/Detours/samples}/echo/main.cpp (100%) rename {samples => Source/Detours/samples}/einst/Makefile (100%) rename {samples => Source/Detours/samples}/einst/edll1x.cpp (100%) rename {samples => Source/Detours/samples}/einst/edll2x.cpp (100%) rename {samples => Source/Detours/samples}/einst/edll3x.cpp (100%) rename {samples => Source/Detours/samples}/einst/einst.cpp (100%) rename {samples => Source/Detours/samples}/excep/Makefile (100%) rename {samples => Source/Detours/samples}/excep/excep.cpp (100%) rename {samples => Source/Detours/samples}/excep/firstexc.cpp (100%) rename {samples => Source/Detours/samples}/excep/firstexc.h (100%) rename {samples => Source/Detours/samples}/findfunc/Makefile (100%) rename {samples => Source/Detours/samples}/findfunc/extend.cpp (100%) rename {samples => Source/Detours/samples}/findfunc/extend.rc (100%) rename {samples => Source/Detours/samples}/findfunc/findfunc.cpp (100%) rename {samples => Source/Detours/samples}/findfunc/symtest.cpp (100%) rename {samples => Source/Detours/samples}/findfunc/target.cpp (100%) rename {samples => Source/Detours/samples}/findfunc/target.h (100%) rename {samples => Source/Detours/samples}/findfunc/target.rc (100%) rename {samples => Source/Detours/samples}/impmunge/Makefile (100%) rename {samples => Source/Detours/samples}/impmunge/impmunge.cpp (100%) rename {samples => Source/Detours/samples}/member/Makefile (100%) rename {samples => Source/Detours/samples}/member/member.cpp (100%) rename {samples => Source/Detours/samples}/opengl/Makefile (100%) rename {samples => Source/Detours/samples}/opengl/ogldet.cpp (100%) rename {samples => Source/Detours/samples}/opengl/ogldet.rc (100%) rename {samples => Source/Detours/samples}/opengl/testogl.cpp (100%) rename {samples => Source/Detours/samples}/payload/Makefile (100%) rename {samples => Source/Detours/samples}/payload/payload.cpp (100%) rename {samples => Source/Detours/samples}/payload/payloadguid.hpp (100%) rename {samples => Source/Detours/samples}/payload/payloadtarget.cpp (100%) rename {samples => Source/Detours/samples}/region/Makefile (100%) rename {samples => Source/Detours/samples}/region/region.cpp (100%) rename {samples => Source/Detours/samples}/setdll/Makefile (100%) rename {samples => Source/Detours/samples}/setdll/setdll.cpp (100%) rename {samples => Source/Detours/samples}/simple/Makefile (100%) rename {samples => Source/Detours/samples}/simple/simple.cpp (100%) rename {samples => Source/Detours/samples}/simple/simple.rc (100%) rename {samples => Source/Detours/samples}/simple/sleep5.cpp (100%) rename {samples => Source/Detours/samples}/simple_safe/Makefile (100%) rename {samples => Source/Detours/samples}/simple_safe/simple_safe.cpp (100%) rename {samples => Source/Detours/samples}/simple_safe/simple_safe.rc (100%) rename {samples => Source/Detours/samples}/simple_safe/sleep5.cpp (100%) rename {samples => Source/Detours/samples}/slept/Makefile (100%) rename {samples => Source/Detours/samples}/slept/NORMAL_IA64.TXT (100%) rename {samples => Source/Detours/samples}/slept/NORMAL_X64.TXT (100%) rename {samples => Source/Detours/samples}/slept/NORMAL_X86.TXT (100%) rename {samples => Source/Detours/samples}/slept/dslept.cpp (100%) rename {samples => Source/Detours/samples}/slept/dslept.rc (100%) rename {samples => Source/Detours/samples}/slept/sleepbed.cpp (100%) rename {samples => Source/Detours/samples}/slept/sleepnew.cpp (100%) rename {samples => Source/Detours/samples}/slept/sleepold.cpp (100%) rename {samples => Source/Detours/samples}/slept/slept.cpp (100%) rename {samples => Source/Detours/samples}/slept/slept.h (100%) rename {samples => Source/Detours/samples}/slept/slept.rc (100%) rename {samples => Source/Detours/samples}/slept/verify.cpp (100%) rename {samples => Source/Detours/samples}/syelog/Makefile (100%) rename {samples => Source/Detours/samples}/syelog/sltest.cpp (100%) rename {samples => Source/Detours/samples}/syelog/sltestp.cpp (100%) rename {samples => Source/Detours/samples}/syelog/syelog.cpp (100%) rename {samples => Source/Detours/samples}/syelog/syelog.h (100%) rename {samples => Source/Detours/samples}/syelog/syelogd.cpp (100%) rename {samples => Source/Detours/samples}/talloc/Makefile (100%) rename {samples => Source/Detours/samples}/talloc/NORMAL_IA64.TXT (100%) rename {samples => Source/Detours/samples}/talloc/NORMAL_X64.TXT (100%) rename {samples => Source/Detours/samples}/talloc/talloc.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll1x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll2x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll3x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll4x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll5x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll6x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll7x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll8x.cpp (100%) rename {samples => Source/Detours/samples}/talloc/tdll9x.cpp (100%) rename {samples => Source/Detours/samples}/traceapi/Makefile (100%) rename {samples => Source/Detours/samples}/traceapi/_win32.cpp (100%) rename {samples => Source/Detours/samples}/traceapi/testapi.cpp (100%) rename {samples => Source/Detours/samples}/traceapi/trcapi.cpp (100%) rename {samples => Source/Detours/samples}/traceapi/trcapi.rc (100%) rename {samples => Source/Detours/samples}/tracebld/Makefile (100%) rename {samples => Source/Detours/samples}/tracebld/tracebld.cpp (100%) rename {samples => Source/Detours/samples}/tracebld/tracebld.h (100%) rename {samples => Source/Detours/samples}/tracebld/trcbld.cpp (100%) rename {samples => Source/Detours/samples}/tracebld/trcbld.rc (100%) rename {samples => Source/Detours/samples}/tracelnk/Makefile (100%) rename {samples => Source/Detours/samples}/tracelnk/trclnk.cpp (100%) rename {samples => Source/Detours/samples}/tracelnk/trclnk.rc (100%) rename {samples => Source/Detours/samples}/tracemem/Makefile (100%) rename {samples => Source/Detours/samples}/tracemem/trcmem.cpp (100%) rename {samples => Source/Detours/samples}/tracemem/trcmem.rc (100%) rename {samples => Source/Detours/samples}/tracereg/Makefile (100%) rename {samples => Source/Detours/samples}/tracereg/trcreg.cpp (100%) rename {samples => Source/Detours/samples}/tracereg/trcreg.rc (100%) rename {samples => Source/Detours/samples}/traceser/Makefile (100%) rename {samples => Source/Detours/samples}/traceser/trcser.cpp (100%) rename {samples => Source/Detours/samples}/traceser/trcser.rc (100%) rename {samples => Source/Detours/samples}/tracessl/Makefile (100%) rename {samples => Source/Detours/samples}/tracessl/trcssl.cpp (100%) rename {samples => Source/Detours/samples}/tracessl/trcssl.rc (100%) rename {samples => Source/Detours/samples}/tracetcp/Makefile (100%) rename {samples => Source/Detours/samples}/tracetcp/trctcp.cpp (100%) rename {samples => Source/Detours/samples}/tracetcp/trctcp.rc (100%) rename {samples => Source/Detours/samples}/tryman/Makefile (100%) rename {samples => Source/Detours/samples}/tryman/managed.cs (100%) rename {samples => Source/Detours/samples}/tryman/size.cpp (100%) rename {samples => Source/Detours/samples}/tryman/tryman.cpp (100%) rename {samples => Source/Detours/samples}/tryman/tstman.cpp (100%) rename {samples => Source/Detours/samples}/tryman/tstman.rc (100%) rename {samples => Source/Detours/samples}/withdll/Makefile (100%) rename {samples => Source/Detours/samples}/withdll/withdll.cpp (100%) rename {src => Source/Detours/src}/Makefile (100%) rename {src => Source/Detours/src}/creatwth.cpp (100%) rename {src => Source/Detours/src}/detours.cpp (100%) rename {src => Source/Detours/src}/detours.h (100%) rename {src => Source/Detours/src}/detver.h (100%) rename {src => Source/Detours/src}/disasm.cpp (100%) rename {src => Source/Detours/src}/disolarm.cpp (100%) rename {src => Source/Detours/src}/disolarm64.cpp (100%) rename {src => Source/Detours/src}/disolia64.cpp (100%) rename {src => Source/Detours/src}/disolx64.cpp (100%) rename {src => Source/Detours/src}/disolx86.cpp (100%) rename {src => Source/Detours/src}/image.cpp (100%) rename {src => Source/Detours/src}/modules.cpp (100%) rename {src => Source/Detours/src}/uimports.cpp (100%) rename system.mak => Source/Detours/system.mak (100%) rename {tests => Source/Detours/tests}/Makefile (100%) rename {tests => Source/Detours/tests}/catch.hpp (100%) rename {tests => Source/Detours/tests}/corruptor.cpp (100%) rename {tests => Source/Detours/tests}/corruptor.h (100%) rename {tests => Source/Detours/tests}/main.cpp (100%) rename {tests => Source/Detours/tests}/payload.cpp (100%) rename {tests => Source/Detours/tests}/payload.h (100%) rename {tests => Source/Detours/tests}/process_helpers.cpp (100%) rename {tests => Source/Detours/tests}/process_helpers.h (100%) rename {tests => Source/Detours/tests}/test_image_api.cpp (100%) rename {tests => Source/Detours/tests}/test_module_api.cpp (100%) rename {vc => Source/Detours/vc}/Detours.sln (100%) rename {vc => Source/Detours/vc}/Detours.vcxproj (100%) rename {vc => Source/Detours/vc}/Detours.vcxproj.filters (100%) create mode 100644 Source/Directory.Build.AfterCppDefault.props create mode 100644 Source/Directory.Build.props create mode 100644 Source/KNSoft.SlimDetours.nuspec create mode 100644 Source/KNSoft.SlimDetours.sln create mode 100644 Source/KNSoft.SlimDetours.targets create mode 100644 Source/KNSoft.SlimDetours.vcxproj create mode 100644 Source/KNSoft.SlimDetours.vcxproj.filters create mode 100644 Source/SlimDetours/Disassembler.c create mode 100644 Source/SlimDetours/Instruction.c create mode 100644 Source/SlimDetours/Memory.c create mode 100644 Source/SlimDetours/SlimDetours.h create mode 100644 Source/SlimDetours/SlimDetours.inl create mode 100644 Source/SlimDetours/Thread.c create mode 100644 Source/SlimDetours/Trampoline.c create mode 100644 Source/SlimDetours/Transaction.c create mode 100644 Source/packages.config diff --git a/.editorconfig b/.editorconfig index 096df14e..ff446c2d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,10 +1,76 @@ -# Top-most EditorConfig file +# Visual Studio generated .editorconfig file with C++ settings. root = true -# Use same style for all files -[*] +[*.{c,c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] + +charset = utf-8-bom indent_style = space indent_size = 4 end_of_line = crlf -insert_final_newline = false -trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 120 + +# Visual C++ Code Style settings + +cpp_generate_documentation_comments = xml + +# Visual C++ Formatting settings + +cpp_indent_braces = false +cpp_indent_multi_line_relative_to = innermost_parenthesis +cpp_indent_within_parentheses = align_to_parenthesis +cpp_indent_preserve_within_parentheses = true +cpp_indent_case_contents = true +cpp_indent_case_labels = true +cpp_indent_case_contents_when_block = false +cpp_indent_lambda_braces_when_parameter = true +cpp_indent_goto_labels = leftmost_column +cpp_indent_preprocessor = leftmost_column +cpp_indent_access_specifiers = false +cpp_indent_namespace_contents = true +cpp_indent_preserve_comments = true +cpp_new_line_before_open_brace_namespace = new_line +cpp_new_line_before_open_brace_type = new_line +cpp_new_line_before_open_brace_function = new_line +cpp_new_line_before_open_brace_block = new_line +cpp_new_line_before_open_brace_lambda = new_line +cpp_new_line_scope_braces_on_separate_lines = true +cpp_new_line_close_brace_same_line_empty_type = true +cpp_new_line_close_brace_same_line_empty_function = true +cpp_new_line_before_catch = false +cpp_new_line_before_else = false +cpp_new_line_before_while_in_do_while = false +cpp_space_before_function_open_parenthesis = remove +cpp_space_within_parameter_list_parentheses = false +cpp_space_between_empty_parameter_list_parentheses = false +cpp_space_after_keywords_in_control_flow_statements = true +cpp_space_within_control_flow_statement_parentheses = false +cpp_space_before_lambda_open_parenthesis = false +cpp_space_within_cast_parentheses = false +cpp_space_after_cast_close_parenthesis = false +cpp_space_within_expression_parentheses = false +cpp_space_before_block_open_brace = true +cpp_space_between_empty_braces = false +cpp_space_before_initializer_list_open_brace = false +cpp_space_within_initializer_list_braces = true +cpp_space_preserve_in_initializer_list = true +cpp_space_before_open_square_bracket = false +cpp_space_within_square_brackets = false +cpp_space_before_empty_square_brackets = false +cpp_space_between_empty_square_brackets = false +cpp_space_group_square_brackets = true +cpp_space_within_lambda_brackets = false +cpp_space_between_empty_lambda_brackets = false +cpp_space_before_comma = false +cpp_space_after_comma = true +cpp_space_remove_around_member_operators = true +cpp_space_before_inheritance_colon = true +cpp_space_before_constructor_colon = true +cpp_space_remove_before_semicolon = true +cpp_space_after_semicolon = true +cpp_space_remove_around_unary_operator = true +cpp_space_around_binary_operator = insert +cpp_space_around_assignment_operator = insert +cpp_space_pointer_reference_alignment = ignore +cpp_space_around_ternary_operator = insert +cpp_wrap_preserve_blocks = never diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml new file mode 100644 index 00000000..0b8a114b --- /dev/null +++ b/.github/workflows/msbuild.yml @@ -0,0 +1,38 @@ +name: MSBuild + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + SOLUTION_FILE_PATH: .\Source\KNSoft.SlimDetours.sln + +permissions: + contents: read + +jobs: + build: + strategy: + matrix: + platform: [x64, x86, ARM64] + config: [Debug, Release] + fail-fast: false + runs-on: windows-latest + steps: + - name: Source checkout + uses: actions/checkout@main + with: + submodules: recursive + - name: Prepare MSBuild + uses: microsoft/setup-msbuild@main + - name: Build + working-directory: ${{env.GITHUB_WORKSPACE}} + run: msbuild ${{env.SOLUTION_FILE_PATH}} /restore /m /p:Configuration=${{matrix.config}} /p:Platform=${{matrix.platform}} /p:RestorePackagesConfig=true + - name: Run Demo (x64, x86) + if: ${{ matrix.platform == 'x64' || matrix.platform == 'x86' }} + working-directory: ${{env.GITHUB_WORKSPACE}} + run: | + .\Source\OutDir\${{matrix.platform}}\${{matrix.config}}\Demo.exe -Run DeadLock -Engine=SlimDetours + .\Source\OutDir\${{matrix.platform}}\${{matrix.config}}\Demo.exe -Run DelayHook diff --git a/.gitignore b/.gitignore index b5a68d43..bff2e8ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,8 @@ -# C extensions -*.so - -# Unit test / coverage reports -.coverage -.tox -nosetests.xml - -# Translations -*.mo - -# Mr Developer -.mr.developer.cfg -.project -.pydevproject +.vs +*.user -# vim -*~ -*.swp +/packages +/OutDir +IntDir -# Visual Studio build -*.ipch -.vs/ -output/ -include/ -*.exp -*.pdb -*.lib -*.dll -*.exe -obj.* -*.ipdb -*.iobj -*.tlog -*.log -*.obj -*.user -*.recipe -/bin.* -*.vcxproj.FileListAbsolute.txt -*.vcxprojAssemblyReference.cache +/*.nupkg \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..99cfc4b3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Source/Detours"] + path = Source/Detours + url = https://github.com/microsoft/Detours.git diff --git a/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.md b/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.md new file mode 100644 index 00000000..db6b7194 --- /dev/null +++ b/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.md @@ -0,0 +1,111 @@ +| **English (en-US)** | [简体中文 (zh-CN)](./README.zh-CN.md) | +| --- | --- | + + +
+ +# Avoid Deadlocking on The Heap When Updating Threads + +## Why does Detours may deadlock when updating threads? + +The original [Detours](https://github.com/microsoft/Detours) uses the CRT heap (via `new/delete`), and if another thread that also uses this heap and is holding the heap lock is suspended while updating threads, then [Detours](https://github.com/microsoft/Detours) will deadlock when it accesses the heap. + +[Raymond Chen](https://devblogs.microsoft.com/oldnewthing/author/oldnewthing) discussed the problem that CRT heap deadlocks when suspending threads in [blog "The Old New Thing"](https://devblogs.microsoft.com/oldnewthing/) article ["Are there alternatives to _lock and _unlock in Visual Studio 2015?"](https://devblogs.microsoft.com/oldnewthing/20170125-00/?p=95255) is the same scenario, and also mentions [Detours](https://github.com/microsoft/Detours), here quotes the original text and will not go into details: +> Furthermore, you would be best served to take the heap lock (Heap­Lock) before suspending the thread, because the Detours library will allocate memory during thread suspension. + +## Demo of Detours Deadlock + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) provides [Demo: DeadLock](../../../Source/Demo/DeadLock.c) to demonstrate the occurrence of a deadlock in [Detours](https://github.com/microsoft/Detours) and the resolution in [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours). + +One of the threads (`HeapUserThread`) keeps calling `malloc/free` (equivalent to `new/delete`): +```C + while (!g_bStop) + { + p = malloc(4); + if (p != NULL) + { + free(p); + } + } +``` + +Another thread (`SetHookThread`) uses [Detours](https://github.com/microsoft/Detours) or [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) constantly hook and unhook: +```C +while (!g_bStop) +{ + hr = HookTransactionBegin(g_eEngineType); + if (FAILED(hr)) + { + break; + } + if (g_eEngineType == EngineMicrosoftDetours) + { + hr = HRESULT_FROM_WIN32(DetourUpdateThread((HANDLE)lpThreadParameter)); + if (FAILED(hr)) + { + break; + } + } + hr = HookAttach(g_eEngineType, EnableHook, (PVOID*)&g_pfnEqualRect, Hooked_EqualRect); + if (FAILED(hr)) + { + HookTransactionAbort(g_eEngineType); + break; + } + hr = HookTransactionCommit(g_eEngineType); + if (FAILED(hr)) + { + break; + } + + EnableHook = !EnableHook; +} +``` +> [!NOTE] +> [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) updates threads automatically (see [🔗 TechWiki: Update Threads Automatically When Applying Inline Hooks](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Update%20Threads%20Automatically%20When%20Applying%20Inline%20Hooks/README.md)), so there is no such function as [`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread). + +Execute these 2 threads at the same time for 10 seconds, then send a stop signal (`g_bStop = TRUE;`) and wait 10 seconds again, if it times out, there is a high probability that a deadlock is occurred, a breakpoint will be triggered, and you can observe the call stack of these 2 threads in the debugger for confirmation. For example, if you specify to run this demo with [Detours](https://github.com/microsoft/Detours) `"Demo.exe -Run DeadLock -Engine=MSDetours"`, the following call stack will see a heap deadlock: +```C +Worker Thread Demo.exe!HeapUserThread Demo.exe!heap_alloc_dbg_internal + [External Code] + Demo.exe!heap_alloc_dbg_internal(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 359 + Demo.exe!heap_alloc_dbg(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 450 + Demo.exe!_malloc_dbg(unsigned __int64 size, int block_use, const char * file_name, int line_number) Line 496 + Demo.exe!malloc(unsigned __int64 size) Line 27 + Demo.exe!HeapUserThread(void * lpThreadParameter) Line 29 + [External Code] + +Worker Thread Demo.exe!SetHookThread Demo.exe!__acrt_lock + [External Code] + Demo.exe!__acrt_lock(__acrt_lock_id _Lock) Line 55 + Demo.exe!heap_alloc_dbg_internal(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 309 + Demo.exe!heap_alloc_dbg(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 450 + Demo.exe!_malloc_dbg(unsigned __int64 size, int block_use, const char * file_name, int line_number) Line 496 + Demo.exe!malloc(unsigned __int64 size) Line 27 + [External Code] + Demo.exe!DetourDetach(void * * ppPointer, void * pDetour) Line 2392 + Demo.exe!HookAttach(_DEMO_ENGINE_TYPE EngineType, int Enable, void * * ppPointer, void * pDetour) Line 140 + Demo.exe!SetHookThread(void * lpThreadParameter) Line 65 + [External Code] +``` +Use [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) run this demo `"Demo.exe -Run DeadLock -Engine=SlimDetours"` will pass successfully. + +## How did mhook and SlimDetours avoid this problem? + +[mhook](https://github.com/martona/mhook) is a well-known Windows API hooking library like [Detours](https://github.com/microsoft/Detours), it uses [`Virtual­Alloc`](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc) to allocate memory pages instead of [`Heap­Alloc`](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc) to allocate heap memory, which is a solution mentioned at the end of the above article. + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) created a new private heap for internal use, which avoids this problem and saves memory usage: +```C +_detour_memory_heap = RtlCreateHeap(HEAP_NO_SERIALIZE | HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); +``` +> [!NOTE] +> [Detours](https://github.com/microsoft/Detours) already has a transaction mechanism, and [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)' new feature "[Delay Hook](../Implement%20Delay%20Hook/README.md)" also uses SRW locks, so this heap does not need serialized access. + +
+
+ +This work is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/). +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*China national certified senior system architect* +*[ReactOS](https://github.com/reactos/reactos) contributor* diff --git a/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.zh-CN.md b/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.zh-CN.md new file mode 100644 index 00000000..d83f03b9 --- /dev/null +++ b/Docs/TechWiki/Avoid Deadlocking on The Heap When Updating Threads/README.zh-CN.md @@ -0,0 +1,111 @@ +| [English (en-US)](./README.md) | **简体中文 (zh-CN)** | +| --- | --- | + +
+ +# 更新线程时避免堆死锁 + +## 为什么Detours更新线程时可能死锁? + +原版[Detours](https://github.com/microsoft/Detours)使用了CRT堆(通过`new/delete`),更新线程时如果挂起了另一个也使用此堆且正持有堆锁的线程,[Detours](https://github.com/microsoft/Detours)再访问此堆就会发生死锁。 + +[Raymond Chen](https://devblogs.microsoft.com/oldnewthing/author/oldnewthing)在[博客“The Old New Thing”](https://devblogs.microsoft.com/oldnewthing/)的文章[《Are there alternatives to _lock and _unlock in Visual Studio 2015?》](https://devblogs.microsoft.com/oldnewthing/20170125-00/?p=95255)中详细讨论的挂起线程时出现CRT堆死锁问题正是同一个场景,也提到了[Detours](https://github.com/microsoft/Detours),这里引用其原文不再赘述: +> Furthermore, you would be best served to take the heap lock (Heap­Lock) before suspending the thread, because the Detours library will allocate memory during thread suspension. +> 此外,最好在挂起线程前占有堆锁(Heap­Lock),因为Detours库将在线程挂起期间分配内存。 + +## Detours死锁的演示 + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)提供了[示例:DeadLock](../../../Source/Demo/DeadLock.c)演示[Detours](https://github.com/microsoft/Detours)死锁的发生与在[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)中得到解决。 + +其中一个线程(`HeapUserThread`)不断调用`malloc/free`(等效于`new/delete`): +```C + while (!g_bStop) + { + p = malloc(4); + if (p != NULL) + { + free(p); + } + } +``` + +另一个线程(`SetHookThread`)不断使用[Detours](https://github.com/microsoft/Detours)或[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)挂钩和脱钩: +```C +while (!g_bStop) +{ + hr = HookTransactionBegin(g_eEngineType); + if (FAILED(hr)) + { + break; + } + if (g_eEngineType == EngineMicrosoftDetours) + { + hr = HRESULT_FROM_WIN32(DetourUpdateThread((HANDLE)lpThreadParameter)); + if (FAILED(hr)) + { + break; + } + } + hr = HookAttach(g_eEngineType, EnableHook, (PVOID*)&g_pfnEqualRect, Hooked_EqualRect); + if (FAILED(hr)) + { + HookTransactionAbort(g_eEngineType); + break; + } + hr = HookTransactionCommit(g_eEngineType); + if (FAILED(hr)) + { + break; + } + + EnableHook = !EnableHook; +} +``` +> [!NOTE] +> [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)会自动更新线程(参考[🔗 技术Wiki:应用内联钩子时自动更新线程](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Update%20Threads%20Automatically%20When%20Applying%20Inline%20Hooks/README.zh-CN.md)),所以不存在[`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread)这样的函数。 + +同时执行这2个线程10秒,然后发送停止信号(`g_bStop = TRUE;`)后再次等待10秒,如果超时则大概率发生死锁,将触发断点,可以在调试器中观察这2个线程的调用栈进行确认。例如指定使用[Detours](https://github.com/microsoft/Detours)运行此示例`"Demo.exe -Run DeadLock -Engine=MSDetours"`,以下调用栈可见堆死锁: +```C +Worker Thread Demo.exe!HeapUserThread Demo.exe!heap_alloc_dbg_internal + [External Code] + Demo.exe!heap_alloc_dbg_internal(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 359 + Demo.exe!heap_alloc_dbg(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 450 + Demo.exe!_malloc_dbg(unsigned __int64 size, int block_use, const char * file_name, int line_number) Line 496 + Demo.exe!malloc(unsigned __int64 size) Line 27 + Demo.exe!HeapUserThread(void * lpThreadParameter) Line 29 + [External Code] + +Worker Thread Demo.exe!SetHookThread Demo.exe!__acrt_lock + [External Code] + Demo.exe!__acrt_lock(__acrt_lock_id _Lock) Line 55 + Demo.exe!heap_alloc_dbg_internal(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 309 + Demo.exe!heap_alloc_dbg(const unsigned __int64 size, const int block_use, const char * const file_name, const int line_number) Line 450 + Demo.exe!_malloc_dbg(unsigned __int64 size, int block_use, const char * file_name, int line_number) Line 496 + Demo.exe!malloc(unsigned __int64 size) Line 27 + [External Code] + Demo.exe!DetourDetach(void * * ppPointer, void * pDetour) Line 2392 + Demo.exe!HookAttach(_DEMO_ENGINE_TYPE EngineType, int Enable, void * * ppPointer, void * pDetour) Line 140 + Demo.exe!SetHookThread(void * lpThreadParameter) Line 65 + [External Code] +``` +使用[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)运行此示例`"Demo.exe -Run DeadLock -Engine=SlimDetours"`则能顺利通过。 + +## mhook与SlimDetours如何避免这个问题? + +[mhook](https://github.com/martona/mhook)与[Detours](https://github.com/microsoft/Detours)一样也是一个熟知的Windows API挂钩库,它使用[`Virtual­Alloc`](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc)分配内存页代替[`Heap­Alloc`](https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc)分配堆内存,是上文末尾提到的一个解决方案。 + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)新创建了一个私有堆供内部使用,避免此问题的同时也节约了内存使用: +```C +_detour_memory_heap = RtlCreateHeap(HEAP_NO_SERIALIZE | HEAP_GROWABLE, NULL, 0, 0, NULL, NULL); +``` +> [!NOTE] +> [Detours](https://github.com/microsoft/Detours)已有事务机制,[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)新添功能“[延迟挂钩](../Implement%20Delay%20Hook/README.zh-CN.md)”也用了[SRW锁](https://learn.microsoft.com/en-us/windows/win32/sync/slim-reader-writer--srw--locks),所以此堆无需序列化访问。 + +
+
+ +本作品采用 [知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/) 进行许可。 +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*中国国家认证系统架构设计师* +*[ReactOS](https://github.com/reactos/reactos)贡献者* diff --git a/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.md b/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.md new file mode 100644 index 00000000..db8dd1f9 --- /dev/null +++ b/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.md @@ -0,0 +1,56 @@ +| **English (en-US)** | [简体中文 (zh-CN)](./README.zh-CN.md) | +| --- | --- | + +
+ +# Avoid Occupying System Reserved Region When Allocating Trampoline + +## Windows reserved region for system DLLs + +Windows introduced ASLR since NT6, a region has been reserved for system DLLs, so that the same system DLL can be mapped at the same location in the reserved region inside different processes, the relocation information can be reused after being loaded once to avoid the relocation operation again on subsequent loading. + +This mechanism is introduced in detail in the "Image randomization" section of Chapter 5 "Memory management" in "Windows Internals 7th Part 1", here will not go into details, but the exact reserved region that I have obtained by referring to the book and analyzing is given: +32-bit process:[0x50000000 ... 0x78000000], a total of 640MB +64-bit process:[0x00007FF7FFFF0000 ... 0x00007FFFFFFF0000], a total of 32GB + +Hooking libraries usually preferred to find available memory space near the hook target function when allocating trampoline, so it's very prone to occupy this reserved region when hooking system APIs, causing the system DLL that should be loaded to that location to be loaded to another place and perform additional relocation operations. + +## Other hooking libraries' practices + +[Detours](https://github.com/microsoft/Detours) as Microsoft's official hooking library, has taken into account that the system reserved region cannot be used for trampolines, but it hardcodes the [0x70000000 ... 0x80000000] address range to circumvent which is for NT5 only: +```C +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; +``` +[jdu2600](https://github.com/jdu2600) is also aware of this issue and has opened an unofficial PR [microsoft/Detours PR #307](https://github.com/microsoft/Detours/pull/307) for [Detours](https://github.com/microsoft/Detours) wants to update this range to adapt the latest Windows. + +[mhook](https://github.com/martona/mhook) is a well-known Windows API hooking library like [Detours](https://github.com/microsoft/Detours), it's a pity that it doesn't seem to take this issue into account. + +## SlimDetours implementation + +ASLR only reserves a range of 640MB in size for 32-bit systems, which can be directly circumvented. For 64-bit systems, it's a bit more complicated, ASLR reserves a range of of 32GB in size, which is too large to circumvent completely. Consider the ASLR layout rules and the location requirements of trampolines, treat 1GB range after `Ntdll.dll` as a reserved range to be circumvented is make sense, this consideration is consistent with the PR mentioned above. Note that this range may be split into two blocks, for example, the following layout: + +| Address | Load Order | System DLL Name | Size | +| :---: | :---: | :---: | :---: | +| (Top)
0x00007FFFFFFF0000
...
0x00007FFFFF9E0000 | #3 | B.dll | 6,208KB | +| 0x00007FFFFF9E0000
...
0x00007FFFFF820000 | #4 | C.dll | 1,792KB | +| ... | ... | ... | +| 0x00007FF800690000
...
0x00007FF800480000 | #1 | Ntdll.dll | 2,112KB | +| 0x00007FF800480000
...
0x00007FF7FFFF0000
(Bottom) | #2 | A.dll | 4,672KB | + +`Ntdll.dll` is randomly loaded by ASLR to a memory address lower in the reserved range, and when the subsequent DLL layout bottoms out, it will wrap to the top of the reserved range and continue to be arranged, in which case the "1GB range after `Ntdll.dll`" is 2 discontinuous regions. + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)' implementation details and circumvention range are different from the above PR, furthermore, it no longer considers the obsolete NT5, and calls `NtQuerySystemInformation` to obtain a more accurate user address space range than hardcoded to help constrain the location of trampolines, see [KNSoft.SlimDetours/Source/SlimDetours/Memory.c at main · KNSoft/KNSoft.SlimDetours](../../../Source/SlimDetours/Memory.c). + +
+
+ +This work is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/). +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*China national certified senior system architect* +*[ReactOS](https://github.com/reactos/reactos) contributor* diff --git a/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.zh-CN.md b/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.zh-CN.md new file mode 100644 index 00000000..a51b06ea --- /dev/null +++ b/Docs/TechWiki/Avoid Occupying System Reserved Region When Allocating Trampoline/README.zh-CN.md @@ -0,0 +1,56 @@ +| [English (en-US)](./README.md) | **简体中文 (zh-CN)** | +| --- | --- | + +
+ +# 分配Trampoline时避免占用系统保留区域 + +## Windows为系统DLL保留的区域 + +Windows自NT6起引入ASLR,随之为系统DLL在用户模式下预留了一段区域,使得同一个系统DLL在不同进程中都能映射到这片保留区域的同一位置,加载一次后即可复用该次重定位信息避免后续加载再次进行重定位操作。 + +这个机制在《Windows Internals 7th Part1》第五章《Memory management》的“Image randomization”小节有详细说明,此处不再赘述,只给出我参考该书并经过分析得到的确切保留范围是: +32位进程:[0x50000000 ... 0x78000000],共640MB +64位进程:[0x00007FF7FFFF0000 ... 0x00007FFFFFFF0000],共32GB + +挂钩库分配Trampoline时一般优先从挂钩目标函数附近寻找可用的内存空间,如此挂钩系统API时十分容易占用这个保留区域,导致本应加载到该位置的系统DLL加载到别地并额外进行重定位操作。 + +## 其它挂钩库的做法 + +[Detours](https://github.com/microsoft/Detours)作为微软官方的挂钩库,已考虑到系统保留区域不能给Trampoline使用这一点,但它硬编码了仅适用于NT5的[0x70000000 ... 0x80000000]地址范围进行规避: +```C +////////////////////////////////////////////////////////////////////////////// +// +// Region reserved for system DLLs, which cannot be used for trampolines. +// +static PVOID s_pSystemRegionLowerBound = (PVOID)(ULONG_PTR)0x70000000; +static PVOID s_pSystemRegionUpperBound = (PVOID)(ULONG_PTR)0x80000000; +``` +同样注意到此问题的[jdu2600](https://github.com/jdu2600)为[Detours](https://github.com/microsoft/Detours)开了一个非官方的PR [microsoft/Detours PR #307](https://github.com/microsoft/Detours/pull/307) 想更新这个范围以适配最新的Windows。 + +[mhook](https://github.com/martona/mhook)与[Detours](https://github.com/microsoft/Detours)一样也是一个熟知的Windows API挂钩库,遗憾的是它似乎没有考虑到这个问题。 + +## SlimDetours的实现 + +32位系统ASLR的预留范围大小仅640MB,直接规避即可。而对于64位系统则复杂一些,ASLR的预留范围有32GB,太大而不可能全部规避。结合ASLR的排布规则和Trampoline的选址需求,视`Ntdll.dll`之后1GB范围为要规避的保留范围是合理的,这个考虑与上面提到的PR一致。要注意这个范围可能被分成两块,例如以下场排布场景: + +| 地址 | 加载顺序 | 系统DLL名称 | 大小 | +| :---: | :---: | :---: | :---: | +| (顶)
0x00007FFFFFFF0000
...
0x00007FFFFF9E0000 | #3 | B.dll | 6,208KB | +| 0x00007FFFFF9E0000
...
0x00007FFFFF820000 | #4 | C.dll | 1,792KB | +| ... | ... | ... | +| 0x00007FF800690000
...
0x00007FF800480000 | #1 | Ntdll.dll | 2,112KB | +| 0x00007FF800480000
...
0x00007FF7FFFF0000
(底) | #2 | A.dll | 4,672KB | + +`Ntdll.dll`被ASLR随机加载到保留范围内较低的内存地址,后续DLL随后排布触底时,将切换到保留范围顶部继续排布,在这个情况下“`Ntdll.dll`之后的1GB范围”便是2块不连续的区域。 + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)的具体实现与规避范围均有别于上述PR,更进一步的,不再为已过时的NT5做考虑,并调用`NtQuerySystemInformation`获得比硬编码更确切的用户地址空间范围,协助约束Trampoline的选址,参考[KNSoft.SlimDetours/Source/SlimDetours/Memory.c于main · KNSoft/KNSoft.SlimDetours](../../../Source/SlimDetours/Memory.c)。 + +
+
+ +本作品采用 [知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/) 进行许可。 +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*中国国家认证系统架构设计师* +*[ReactOS](https://github.com/reactos/reactos)贡献者* diff --git a/Docs/TechWiki/Implement Delay Hook/README.md b/Docs/TechWiki/Implement Delay Hook/README.md new file mode 100644 index 00000000..78662042 --- /dev/null +++ b/Docs/TechWiki/Implement Delay Hook/README.md @@ -0,0 +1,98 @@ +| **English (en-US)** | [简体中文 (zh-CN)](./README.zh-CN.md) | +| --- | --- | + +
+ +# Implement Delay Hook + +## What's "delay hook" and the benefits it brings? + +The usual way to hook a function in a DLL has to load the corresponding DLL into the process space and locate its address (for example, use [`LoadLibraryW`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw) + [`GetProcAddress`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress)) at first. + +For the hooks designed for a specific program, usually theirs target function will be called sooner or later, and the DLL is also required by the process, so loading the corresponding DLL early is fair enough. But for hooks are injected into different processes (especially global hooks), they don't know this DLL is required by each process or not, so they usually still load the DLL into each process space and hook the function, even if the process itself doesn't want this DLL. + +Imagine a global hook with many dependencies trying to hook functions in various system DLLs, and then bring all the DLLs involved into all processes for loading and initialization, which the cost is enormous. + +"Delay hook" is a good solution to this problem. That is, the hook is set immediately if the target DLL is already loaded, otherwise set the hook when the target DLL is loaded into the process. + +## Technical solution + +Obviously, the key to implementing "delay hook" is to acquire DLL load notification in the first place. "[DLL Load Notification](https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification)" mechanism is introduced since NT6, this is what we need. + +See [LdrRegisterDllNotification](https://learn.microsoft.com/en-us/windows/win32/devnotes/ldrregisterdllnotification) function, DLL load (and unload) notification will be sent to the callback registered by it, and the memory space mapped from the DLL will be available at that point while we can set hook. + +Although Microsoft Learning prompts that related APIs may be changed or removed, their usage has not changed, just changed the held lock from `LdrpLoaderLock` to a dedicated `LdrpDllNotificationLock` since NT6.1. However, please keep the callback as simple as possible. + +> [!TIP] +> If you want to know the internal implementation of "[DLL Load Notification](https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification)" on Windows, see [ReactOS PR #6795](https://github.com/reactos/reactos/pull/6795) which I contributed to [ReactOS](https://github.com/reactos/reactos). Don't see [the WINE implementation](https://gitlab.winehq.org/wine/wine/-/commit/4c13e1765f559b322d8c071b2e23add914981db7), as it has mistakes as of this writing, for example, its `LdrUnregisterDllNotification` removes the node without checking it is in the list or not. + +## Using "delay hook" in SlimDetours + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) provides `SlimDetoursDelayAttach` function to register delay hooks, see comments above function declaration and [Demo: DelayHook](../../../Source/Demo/DelayHook.c) for more information. + +In this example, `SlimDetoursDelayAttach` is called to register a delay hook for `User32.dll!EqualRect` API, and confirm that the `User32.dll` is not loaded at this point by checking the return values from it and `LdrGetDllHandle`: +```C +/* Register SlimDetours delay hook */ +Status = SlimDetoursDelayAttach((PVOID*)&g_pfnEqualRect, + Hooked_EqualRect, + g_usUser32.Buffer, + g_asEqualRect.Buffer, + DelayAttachCallback, + NULL); +if (!NT_SUCCESS(Status)) +{ + TEST_FAIL("SlimDetoursDelayAttach failed with 0x%08lX\n", Status); + return; +} else if (Status != STATUS_PENDING) +{ + TEST_FAIL("SlimDetoursDelayAttach succeeded with 0x%08lX, which is not using delay attach\n", Status); + return; +} + +/* Make sure user32.dll is not loaded yet */ +Status = LdrGetDllHandle(NULL, NULL, &g_usUser32, &hUser32); +if (NT_SUCCESS(Status)) +{ + TEST_SKIP("user32.dll is loaded, test cannot continue\n"); + return; +} else if (Status != STATUS_DLL_NOT_FOUND) +{ + TEST_SKIP("LdrGetDllHandle failed with 0x%08lX\n", Status); + return; +} +``` + +Then call `LdrLoadDll` to load `User32.dll`: +```C +/* Load user32.dll now */ +Status = LdrLoadDll(NULL, NULL, &g_usUser32, &hUser32); +if (!NT_SUCCESS(Status)) +{ + TEST_SKIP("LdrLoadDll failed with 0x%08lX\n", Status); + return; +} +``` + +If `User32.dll` is loaded successfully, the previously registered delay hook should have been hooked, and then verify that the delay hook callback was called correctly and the `User32.dll!EqualRect` function was successfully hooked: +```C +/* Delay attach callback should be called and EqualRect is hooked successfully */ +TEST_OK(g_bDelayAttach); +Status = LdrGetProcedureAddress(hUser32, &g_asEqualRect, 0, (PVOID*)&pfnEqualRect); +if (NT_SUCCESS(Status)) +{ + TEST_OK(pfnEqualRect(&rc1, &rc2) == TRUE); + TEST_OK(g_lEqualRectCount == 1); +} else +{ + TEST_SKIP("LdrGetProcedureAddress failed with 0x%08lX\n", Status); +} +``` + +
+
+ +This work is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/). +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*China national certified senior system architect* +*[ReactOS](https://github.com/reactos/reactos) contributor* diff --git a/Docs/TechWiki/Implement Delay Hook/README.zh-CN.md b/Docs/TechWiki/Implement Delay Hook/README.zh-CN.md new file mode 100644 index 00000000..5e1b075c --- /dev/null +++ b/Docs/TechWiki/Implement Delay Hook/README.zh-CN.md @@ -0,0 +1,98 @@ +| [English (en-US)](./README.md) | **简体中文 (zh-CN)** | +| --- | --- | + +
+ +# 实现延迟挂钩 + +## 什么是“延迟挂钩”和它带来的好处? + +通常挂钩DLL中函数的做法需要先将对应的DLL加载到进程空间并定位它的地址(例如,使用[`LoadLibraryW`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw) + [`GetProcAddress`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress))。 + +对于为特定程序设计的钩子,通常它们的目标函数将迟早被调用,DLL也是进程需要的,所以早些加载对应的DLL没什么问题。而对于被注入到不同进程中的钩子(尤其是全局钩子),它们不知道各个进程是否需要此DLL,所以通常它们仍将DLL加载到各个进程空间并挂钩函数,即使进程本身并不想要这个DLL。 + +试想一下一个有不少依赖项的全局钩子试图挂钩各种系统DLL的函数,则会将所有涉及的DLL都带入到所有进程进行加载和初始化,开销极大。 + +“延迟挂钩”是此问题的一个好方案。即如果目标DLL已加载则立即执行挂钩,否则等到目标DLL加载到进程的时候挂钩。 + +## 技术方案 + +显然,实现“延迟挂钩”的关键是在第一时间获得加载DLL的通知。“[DLL加载通知](https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification)”机制自NT6被引入,这正是我们需要的。 + +参考[LdrRegisterDllNotification](https://learn.microsoft.com/en-us/windows/win32/devnotes/ldrregisterdllnotification)函数,DLL加载(与卸载)的通知将被发送给由此函数注册的回调,并且DLL映射的内存区域在那时可用,同时我们可以进行挂钩。 + +尽管Microsoft Learning提示相关API可能会被更改或删除,但它们的用法一直没变,仅自NT6.1将所持的锁由`LdrpLoaderLock`变为了专用的`LdrpDllNotificationLock`。总之,请保持回调尽可能简单。 + +> [!TIP] +> 如果你想了解Windows上“[DLL加载通知](https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification)”的内部实现,参考我为[ReactOS](https://github.com/reactos/reactos)贡献的[ReactOS PR #6795](https://github.com/reactos/reactos/pull/6795)。不要参考[WINE的实现](https://gitlab.winehq.org/wine/wine/-/commit/4c13e1765f559b322d8c071b2e23add914981db7),因为它截至此文编写时存在错误,例如,它的`LdrUnregisterDllNotification`没有检查节点是否处于链表中就进行了移除。 + +## 在SlimDetours中使用“延迟挂钩” + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)提供了`SlimDetoursDelayAttach`函数注册延迟挂钩,具体可参考该函数声明上方的注释以及[示例:DelayHook](../../../Source/Demo/DelayHook.c)。 + +该示例中,先调用了`SlimDetoursDelayAttach`注册对`User32.dll!EqualRect`API的延迟挂钩,并通过检查它和`LdrGetDllHandle`的返回值确认此时`User32.dll`并未加载: +```C +/* Register SlimDetours delay hook */ +Status = SlimDetoursDelayAttach((PVOID*)&g_pfnEqualRect, + Hooked_EqualRect, + g_usUser32.Buffer, + g_asEqualRect.Buffer, + DelayAttachCallback, + NULL); +if (!NT_SUCCESS(Status)) +{ + TEST_FAIL("SlimDetoursDelayAttach failed with 0x%08lX\n", Status); + return; +} else if (Status != STATUS_PENDING) +{ + TEST_FAIL("SlimDetoursDelayAttach succeeded with 0x%08lX, which is not using delay attach\n", Status); + return; +} + +/* Make sure user32.dll is not loaded yet */ +Status = LdrGetDllHandle(NULL, NULL, &g_usUser32, &hUser32); +if (NT_SUCCESS(Status)) +{ + TEST_SKIP("user32.dll is loaded, test cannot continue\n"); + return; +} else if (Status != STATUS_DLL_NOT_FOUND) +{ + TEST_SKIP("LdrGetDllHandle failed with 0x%08lX\n", Status); + return; +} +``` + +然后调用`LdrLoadDll`加载`User32.dll`: +```C +/* Load user32.dll now */ +Status = LdrLoadDll(NULL, NULL, &g_usUser32, &hUser32); +if (!NT_SUCCESS(Status)) +{ + TEST_SKIP("LdrLoadDll failed with 0x%08lX\n", Status); + return; +} +``` + +此时若`User32.dll`成功加载,则之前注册的延迟挂钩应已挂钩完成,进而验证延迟挂钩回调被正确调用以及`User32.dll!EqualRect`函数被成功挂钩: +```C +/* Delay attach callback should be called and EqualRect is hooked successfully */ +TEST_OK(g_bDelayAttach); +Status = LdrGetProcedureAddress(hUser32, &g_asEqualRect, 0, (PVOID*)&pfnEqualRect); +if (NT_SUCCESS(Status)) +{ + TEST_OK(pfnEqualRect(&rc1, &rc2) == TRUE); + TEST_OK(g_lEqualRectCount == 1); +} else +{ + TEST_SKIP("LdrGetProcedureAddress failed with 0x%08lX\n", Status); +} +``` + +
+
+ +本作品采用 [知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/) 进行许可。 +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*中国国家认证系统架构设计师* +*[ReactOS](https://github.com/reactos/reactos)贡献者* diff --git a/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.md b/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.md new file mode 100644 index 00000000..c48de26b --- /dev/null +++ b/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.md @@ -0,0 +1,246 @@ +| **English (en-US)** | [简体中文 (zh-CN)](./README.zh-CN.md) | +| --- | --- | + +
+ +# Update Threads Automatically When Applying Inline Hooks + +## The necessity of updating threads when applying inline hooks + +Inline hook has to modify the instructions at the beginning of the function to implement the jump, in order to cope with the possibility that a thread is running on the instruction to be modified, it's necessary to update the thread in this state to avoid executing an illegal combination of old and new instructions. + +## Implementations on other hooking library + +### Detours + +[Detours](https://github.com/microsoft/Detours) provides [`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread) function to update threads, but it needs the caller pass the handle of the thread that needs to be updated: +```C +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); +``` +In other words, the caller needs to traverse all threads in the process except itself and pass them to this function, which is complicated and inconvenient to use. + +But [Detours](https://github.com/microsoft/Detours) updates threads very precisely, it accurately adjusts the PC (Program Counter) in the thread context to the correct position by using [`GetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadcontext) and [`SetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext), see [Detours/src/detours.cpp at 4b8c659f · microsoft/Detours](https://github.com/microsoft/Detours/blob/4b8c659f549b0ab21cf649377c7a84eb708f5e68/src/detours.cpp#L1840) for implementation. + +> [!TIP] +> While its official example "[Using Detours](https://github.com/microsoft/Detours/wiki/Using-Detours)" has code like `DetourUpdateThread(GetCurrentThread())`, such usage is pointless and invalid, and should be used to update all threads in the process except the current thread, see also: [`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread). But even updating threads in the right way, it also brings a new risk, see [🔗 TechWiki: Avoid Deadlocking on The Heap When Updating Threads](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads/README.md). + +### mhook + +[mhook](https://github.com/martona/mhook) is a well-known Windows API hooking library like [Detours](https://github.com/microsoft/Detours), it updates threads automatically when set (or unset) hooks, the caller doesn't need to be concerned about this problem, see [mhook/mhook-lib/mhook.cpp at e58a58ca · martona/mhook](https://github.com/martona/mhook/blob/e58a58ca31dbe14f202b9b26315bff9f7a32598c/mhook-lib/mhook.cpp#L557) for implementation. + +But the way it updates threads is a bit hacky compared to the [Detours](https://github.com/microsoft/Detours) mentioned above, wait 100ms if the thread is exactly in the area where the instruction is about to be modified, try up to 3 times: +```C +while (GetThreadContext(hThread, &ctx)) +{ + ... + if (nTries < 3) + { + // oops - we should try to get the instruction pointer out of here. + ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE", dwThreadId, pIp)); + ResumeThread(hThread); + Sleep(100); + SuspendThread(hThread); + nTries++; + } + ... +} +``` + +## SlimDetours implementation + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) takes all of the above advantages into account, traverse all threads of the process at hook (or unhook) time, and then update the thread context in the same way as [Detours](https://github.com/microsoft/Detours). + +Suspend all threads in the current process except the current thread and return their handles: +```C +NTSTATUS +detour_thread_suspend( + _Outptr_result_maybenull_ PHANDLE* SuspendedHandles, + _Out_ PULONG SuspendedHandleCount) +{ + NTSTATUS Status; + ULONG i, ThreadCount, SuspendedCount; + PSYSTEM_PROCESS_INFORMATION pSPI, pCurrentSPI; + PSYSTEM_THREAD_INFORMATION pSTI; + PHANDLE Buffer; + HANDLE ThreadHandle, CurrentPID, CurrentTID; + OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, 0); + + /* Get system process and thread information */ + i = _1MB; +_Try_alloc: + pSPI = (PSYSTEM_PROCESS_INFORMATION)detour_memory_alloc(i); + if (pSPI == NULL) + { + return STATUS_NO_MEMORY; + } + Status = NtQuerySystemInformation(SystemProcessInformation, pSPI, i, &i); + if (!NT_SUCCESS(Status)) + { + detour_memory_free(pSPI); + if (Status == STATUS_INFO_LENGTH_MISMATCH) + { + goto _Try_alloc; + } + return Status; + } + + /* Find current process and threads */ + CurrentPID = NtGetCurrentProcessId(); + pCurrentSPI = pSPI; + while (pCurrentSPI->UniqueProcessId != CurrentPID) + { + if (pCurrentSPI->NextEntryOffset == 0) + { + Status = STATUS_NOT_FOUND; + goto _Exit; + } + pCurrentSPI = (PSYSTEM_PROCESS_INFORMATION)Add2Ptr(pCurrentSPI, pCurrentSPI->NextEntryOffset); + } + pSTI = (PSYSTEM_THREAD_INFORMATION)Add2Ptr(pCurrentSPI, sizeof(*pCurrentSPI)); + + /* Skip if no other threads */ + ThreadCount = pCurrentSPI->NumberOfThreads - 1; + if (ThreadCount == 0) + { + *SuspendedHandles = NULL; + *SuspendedHandleCount = 0; + Status = STATUS_SUCCESS; + goto _Exit; + } + + /* Create handle array */ + Buffer = (PHANDLE)detour_memory_alloc(ThreadCount * sizeof(HANDLE)); + if (Buffer == NULL) + { + Status = STATUS_NO_MEMORY; + goto _Exit; + } + + /* Suspend threads */ + SuspendedCount = 0; + CurrentTID = NtGetCurrentThreadId(); + for (i = 0; i < pCurrentSPI->NumberOfThreads; i++) + { + if (pSTI[i].ClientId.UniqueThread == CurrentTID || + !NT_SUCCESS(NtOpenThread(&ThreadHandle, + THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, + &ObjectAttributes, + &pSTI[i].ClientId))) + { + continue; + } + if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL))) + { + _Analysis_assume_(SuspendedCount < ThreadCount); + Buffer[SuspendedCount++] = ThreadHandle; + } else + { + NtClose(ThreadHandle); + } + } + + /* Return suspended thread handles */ + if (SuspendedCount == 0) + { + detour_memory_free(Buffer); + *SuspendedHandles = NULL; + } else + { + *SuspendedHandles = Buffer; + } + *SuspendedHandleCount = SuspendedCount; + Status = STATUS_SUCCESS; + +_Exit: + detour_memory_free(pSPI); + return Status; +} +``` + +Update threads' context PC (Program Counter) precisely: +```C +NTSTATUS +detour_thread_update( + _In_ HANDLE ThreadHandle, + _In_ PDETOUR_OPERATION PendingOperations) +{ + NTSTATUS Status; + PDETOUR_OPERATION o; + CONTEXT cxt; + BOOL bUpdateContext; + + cxt.ContextFlags = CONTEXT_CONTROL; + Status = NtGetContextThread(ThreadHandle, &cxt); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + for (o = PendingOperations; o != NULL; o = o->pNext) + { + bUpdateContext = FALSE; + if (o->fIsRemove) + { + if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pTrampoline && + cxt.CONTEXT_PC < ((ULONG_PTR)o->pTrampoline + sizeof(o->pTrampoline))) + { + cxt.CONTEXT_PC = (ULONG_PTR)o->pbTarget + + detour_align_from_trampoline(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pTrampoline)); + bUpdateContext = TRUE; + } + } else + { + if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pbTarget && + cxt.CONTEXT_PC < ((ULONG_PTR)o->pbTarget + o->pTrampoline->cbRestore)) + { + cxt.CONTEXT_PC = (ULONG_PTR)o->pTrampoline + + detour_align_from_target(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pbTarget)); + bUpdateContext = TRUE; + } + } + if (bUpdateContext) + { + Status = NtSetContextThread(ThreadHandle, &cxt); + break; + } + } + + return Status; +} +``` + +Resume suspended threads and release handles: +```C +VOID +detour_thread_resume( + _In_reads_(SuspendedHandleCount) _Frees_ptr_ PHANDLE SuspendedHandles, + _In_ ULONG SuspendedHandleCount) +{ + ULONG i; + + for (i = 0; i < SuspendedHandleCount; i++) + { + NtResumeThread(SuspendedHandles[i], NULL); + NtClose(SuspendedHandles[i]); + } + detour_memory_free(SuspendedHandles); +} +``` + +Key points: +1. Call `NtQuerySystemInformation` to acquire all threads of the current process +2. Call `NtSuspendThread` to suspend all threads except the current thread +3. Modify the instruction to implement the inline hook +4. Update the threads that were successfully suspended +5. Call `NtResumeThread` to resume the suspended threads + +See [KNSoft.SlimDetours/Source/SlimDetours/Thread.c at main · KNSoft/KNSoft.SlimDetours](../../../Source/SlimDetours/Thread.c) for the full implementation. + +
+
+ +This work is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/). +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*China national certified senior system architect* +*[ReactOS](https://github.com/reactos/reactos) contributor* diff --git a/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.zh-CN.md b/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.zh-CN.md new file mode 100644 index 00000000..85545e95 --- /dev/null +++ b/Docs/TechWiki/Update Threads Automatically When Applying Inline Hooks/README.zh-CN.md @@ -0,0 +1,246 @@ +| [English (en-US)](./README.md) | **简体中文 (zh-CN)** | +| --- | --- | + +
+ +# 应用内联钩子时自动更新线程 + +## 内联挂钩时更新线程的必要性 + +内联挂钩需要修改函数开头的指令实现跳转,为应对有线程正好运行在要修改的指令上的可能,需要更新处于此状态的线程避免其在修改指令时执行非法的新老共存的指令。 + +## 其它挂钩库中的实现 + +### Detours + +[Detours](https://github.com/microsoft/Detours)提供了[`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread)函数更新线程,但需要由调用方传入需要进行更新线程的句柄: +```C +LONG WINAPI DetourUpdateThread(_In_ HANDLE hThread); +``` +也就是说,需要由调用方遍历进程中除自己以外的所有线程并传入给此函数,用起来比较复杂且不方便。 + +但[Detours](https://github.com/microsoft/Detours)更新线程非常精细,它通过使用[`GetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadcontext)与[`SetThreadContext`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadcontext)准确地调整线程上下文中的PC(程序计数器)到正确位置,实现参考[Detours/src/detours.cpp于4b8c659f · microsoft/Detours](https://github.com/microsoft/Detours/blob/4b8c659f549b0ab21cf649377c7a84eb708f5e68/src/detours.cpp#L1840)。 + +> [!TIP] +> 虽然它的官方示例“[Using Detours](https://github.com/microsoft/Detours/wiki/Using-Detours)”中有`DetourUpdateThread(GetCurrentThread())`这样的代码,但这用法无意义且无效,应使用其更新进程中除当前线程外的所有线程,详见[`DetourUpdateThread`](https://github.com/microsoft/Detours/wiki/DetourUpdateThread)。但即便以正确的方式更新线程,也会带来一个新的风险,见[🔗 技术Wiki:更新线程时避免堆死锁](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads/README.zh-CN.md)。 + +### mhook + +[mhook](https://github.com/martona/mhook)与[Detours](https://github.com/microsoft/Detours)一样也是一个熟知的Windows API挂钩库,它在挂钩(和脱钩)时自动更新线程,调用者无需关心此问题,实现参考[mhook/mhook-lib/mhook.cpp于e58a58ca · martona/mhook](https://github.com/martona/mhook/blob/e58a58ca31dbe14f202b9b26315bff9f7a32598c/mhook-lib/mhook.cpp#L557)。 + +但它更新线程的方式比起上述的[Detours](https://github.com/microsoft/Detours)则有点笨拙,若线程正好位于要修改指令的区域则等待100毫秒,最多尝试3次: +```C +while (GetThreadContext(hThread, &ctx)) +{ + ... + if (nTries < 3) + { + // oops - we should try to get the instruction pointer out of here. + ODPRINTF((L"mhooks: SuspendOneThread: suspended thread %d - IP is at %p - IS COLLIDING WITH CODE", dwThreadId, pIp)); + ResumeThread(hThread); + Sleep(100); + SuspendThread(hThread); + nTries++; + } + ... +} +``` + +## SlimDetours中的实现 + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)兼顾了以上优点,在挂钩(或脱钩)时遍历进程的所有线程,然后沿用[Detours](https://github.com/microsoft/Detours)的方式更新线程上下文。 + +挂起当前进程中除当前线程外的所有线程,并返回它们的句柄: +```C +NTSTATUS +detour_thread_suspend( + _Outptr_result_maybenull_ PHANDLE* SuspendedHandles, + _Out_ PULONG SuspendedHandleCount) +{ + NTSTATUS Status; + ULONG i, ThreadCount, SuspendedCount; + PSYSTEM_PROCESS_INFORMATION pSPI, pCurrentSPI; + PSYSTEM_THREAD_INFORMATION pSTI; + PHANDLE Buffer; + HANDLE ThreadHandle, CurrentPID, CurrentTID; + OBJECT_ATTRIBUTES ObjectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(NULL, 0); + + /* Get system process and thread information */ + i = _1MB; +_Try_alloc: + pSPI = (PSYSTEM_PROCESS_INFORMATION)detour_memory_alloc(i); + if (pSPI == NULL) + { + return STATUS_NO_MEMORY; + } + Status = NtQuerySystemInformation(SystemProcessInformation, pSPI, i, &i); + if (!NT_SUCCESS(Status)) + { + detour_memory_free(pSPI); + if (Status == STATUS_INFO_LENGTH_MISMATCH) + { + goto _Try_alloc; + } + return Status; + } + + /* Find current process and threads */ + CurrentPID = NtGetCurrentProcessId(); + pCurrentSPI = pSPI; + while (pCurrentSPI->UniqueProcessId != CurrentPID) + { + if (pCurrentSPI->NextEntryOffset == 0) + { + Status = STATUS_NOT_FOUND; + goto _Exit; + } + pCurrentSPI = (PSYSTEM_PROCESS_INFORMATION)Add2Ptr(pCurrentSPI, pCurrentSPI->NextEntryOffset); + } + pSTI = (PSYSTEM_THREAD_INFORMATION)Add2Ptr(pCurrentSPI, sizeof(*pCurrentSPI)); + + /* Skip if no other threads */ + ThreadCount = pCurrentSPI->NumberOfThreads - 1; + if (ThreadCount == 0) + { + *SuspendedHandles = NULL; + *SuspendedHandleCount = 0; + Status = STATUS_SUCCESS; + goto _Exit; + } + + /* Create handle array */ + Buffer = (PHANDLE)detour_memory_alloc(ThreadCount * sizeof(HANDLE)); + if (Buffer == NULL) + { + Status = STATUS_NO_MEMORY; + goto _Exit; + } + + /* Suspend threads */ + SuspendedCount = 0; + CurrentTID = NtGetCurrentThreadId(); + for (i = 0; i < pCurrentSPI->NumberOfThreads; i++) + { + if (pSTI[i].ClientId.UniqueThread == CurrentTID || + !NT_SUCCESS(NtOpenThread(&ThreadHandle, + THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, + &ObjectAttributes, + &pSTI[i].ClientId))) + { + continue; + } + if (NT_SUCCESS(NtSuspendThread(ThreadHandle, NULL))) + { + _Analysis_assume_(SuspendedCount < ThreadCount); + Buffer[SuspendedCount++] = ThreadHandle; + } else + { + NtClose(ThreadHandle); + } + } + + /* Return suspended thread handles */ + if (SuspendedCount == 0) + { + detour_memory_free(Buffer); + *SuspendedHandles = NULL; + } else + { + *SuspendedHandles = Buffer; + } + *SuspendedHandleCount = SuspendedCount; + Status = STATUS_SUCCESS; + +_Exit: + detour_memory_free(pSPI); + return Status; +} +``` + +精准更新线程上下文PC(程序计数器): +```C +NTSTATUS +detour_thread_update( + _In_ HANDLE ThreadHandle, + _In_ PDETOUR_OPERATION PendingOperations) +{ + NTSTATUS Status; + PDETOUR_OPERATION o; + CONTEXT cxt; + BOOL bUpdateContext; + + cxt.ContextFlags = CONTEXT_CONTROL; + Status = NtGetContextThread(ThreadHandle, &cxt); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + for (o = PendingOperations; o != NULL; o = o->pNext) + { + bUpdateContext = FALSE; + if (o->fIsRemove) + { + if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pTrampoline && + cxt.CONTEXT_PC < ((ULONG_PTR)o->pTrampoline + sizeof(o->pTrampoline))) + { + cxt.CONTEXT_PC = (ULONG_PTR)o->pbTarget + + detour_align_from_trampoline(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pTrampoline)); + bUpdateContext = TRUE; + } + } else + { + if (cxt.CONTEXT_PC >= (ULONG_PTR)o->pbTarget && + cxt.CONTEXT_PC < ((ULONG_PTR)o->pbTarget + o->pTrampoline->cbRestore)) + { + cxt.CONTEXT_PC = (ULONG_PTR)o->pTrampoline + + detour_align_from_target(o->pTrampoline, (BYTE)(cxt.CONTEXT_PC - (ULONG_PTR)o->pbTarget)); + bUpdateContext = TRUE; + } + } + if (bUpdateContext) + { + Status = NtSetContextThread(ThreadHandle, &cxt); + break; + } + } + + return Status; +} +``` + +恢复挂起的线程和释放句柄: +```C +VOID +detour_thread_resume( + _In_reads_(SuspendedHandleCount) _Frees_ptr_ PHANDLE SuspendedHandles, + _In_ ULONG SuspendedHandleCount) +{ + ULONG i; + + for (i = 0; i < SuspendedHandleCount; i++) + { + NtResumeThread(SuspendedHandles[i], NULL); + NtClose(SuspendedHandles[i]); + } + detour_memory_free(SuspendedHandles); +} +``` + +要点: +1. 调用`NtQuerySystemInformation`以获取当前进程所有线程 +2. 调用`NtSuspendThread`挂起除当前线程外的所有线程 +3. 修改指令实现内联挂钩 +4. 更新被成功挂起的线程 +5. 调用`NtResumeThread`恢复挂起的线程 + +完整实现参考[KNSoft.SlimDetours/Source/SlimDetours/Thread.c于main · KNSoft/KNSoft.SlimDetours](../../../Source/SlimDetours/Thread.c)。 + +
+
+ +本作品采用 [知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 (CC BY-NC-SA 4.0)](http://creativecommons.org/licenses/by-nc-sa/4.0/) 进行许可。 +
+**[Ratin](https://github.com/RatinCN) <[](mailto:ratin@knsoft.org)>** +*中国国家认证系统架构设计师* +*[ReactOS](https://github.com/reactos/reactos)贡献者* diff --git a/LICENSE b/LICENSE index b2f52a2b..a612ad98 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,373 @@ -Copyright (c) Microsoft Corporation. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.md b/README.md index 84a4d14f..9e4c3740 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,106 @@ -# Microsoft Research Detours Package - -Detours is a software package for monitoring and instrumenting API calls on Windows. Detours -has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under -a standard open source license ([MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md)). This simplifies licensing for programmers using Detours -and allows the community to support Detours using open source tools and processes. - -Detours is compatible with the Windows NT family of -operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, -Windows 8, and Windows 10. It cannot be used by Windows Store apps -because Detours requires APIs not available to those applications. -This repo contains the source code for version 4.0.1 of Detours. - -For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). -For directions on how to build and run samples, see the -samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. - -## Contributing - -The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. -Here are some ways you can participate in the project: - -* [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. -* [Improve the Wiki](https://github.com/microsoft/detours/wiki). -* [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. -* Review [source code changes](https://github.com/microsoft/detours/pulls). - -Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that -you have the right to, and actually do, grant us the rights to use your contribution. -For details, visit https://cla.opensource.microsoft.com. - -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - -## Issues, questions, and feedback - -* Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). - -## Mailing list for announcements - -The detours-announce mailing list is a low-traffic email list for important announcements -about the project, such as the availability of new versions of Detours. To join it, send -an email to listserv@lists.research.microsoft.com with a -message body containing only the text SUBSCRIBE DETOURS-ANNOUNCE. -To leave it, send an email to listserv@lists.research.microsoft.com with a -message body containing only the text UNSUBSCRIBE DETOURS-ANNOUNCE. - - -## License - -Copyright (c) Microsoft Corporation. All rights reserved. - -Licensed under the [MIT](LICENSE.md) License. +| **English (en-US)** | [简体中文 (zh-CN)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/README.zh-CN.md) | +| --- | --- | + +
+ +# KNSoft.SlimDetours + +[![NuGet Downloads](https://img.shields.io/nuget/dt/KNSoft.SlimDetours)](https://www.nuget.org/packages/KNSoft.SlimDetours) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/KNSoft/KNSoft.SlimDetours/msbuild.yml)](https://github.com/KNSoft/KNSoft.SlimDetours/actions/workflows/msbuild.yml) ![PR Welcome](https://img.shields.io/badge/PR-welcome-0688CB.svg) [![GitHub License](https://img.shields.io/github/license/KNSoft/KNSoft.SlimDetours)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE) + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) is an improved Windows API hooking library base on [Microsoft Detours](https://github.com/microsoft/Detours). + +## Feature + +Compared to the original [Detours](https://github.com/microsoft/Detours), the advantages are: + +- New feature + - **Support delay hook (set hooks automatically when target DLL loaded)** [🔗 TechWiki: Implement Delay Hook](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Implement%20Delay%20Hook/README.md) + - **Automatically update threads when set hooks** [🔗 TechWiki: Update Threads Automatically When Applying Inline Hooks](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Update%20Threads%20Automatically%20When%20Applying%20Inline%20Hooks/README.md) +- Improved + - **Avoid deadlocks when updating threads** [🔗 TechWiki: Avoid Deadlocking on The Heap When Updating Threads](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads/README.md) + - Avoid occupying system reserved memory region [🔗 TechWiki: Avoid Occupying System Reserved Region When Allocating Trampoline](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Occupying%20System%20Reserved%20Region%20When%20Allocating%20Trampoline/README.md) + - Other bug fixes and code improvements +- Lite + - **Depends on `Ntdll.dll` only** + - Retain API hooking functions only + - Remove support for ARM (ARM32), IA64, WinXP, GNUC + - Smaller binary size +- Out-of-the-box + - NuGet package is released + +## Usage + +[![NuGet Downloads](https://img.shields.io/nuget/dt/KNSoft.SlimDetours)](https://www.nuget.org/packages/KNSoft.SlimDetours) + +### TL;DR + +KNSoft.SlimDetours package contains both of [SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours) and the original [Microsoft Detours](https://github.com/microsoft/Detours). + +Include header [SlimDetours.h](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/SlimDetours/SlimDetours.h) for KNSoft.SlimDetours, or header [detours.h](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/Detours/src/detours.h) for the original [Microsoft Detours](https://github.com/microsoft/Detours), then link compiled library `KNSoft.SlimDetours.lib`. + +NuGet package [KNSoft.SlimDetours](https://www.nuget.org/packages/KNSoft.SlimDetours) is out-of-the-box, install to project and the compiled library will be linked automatically. + +```C +#include // KNSoft.SlimDetours +#include // Microsoft Detours +``` + +If your project configuration name is neither "Release" nor "Debug", [MSBuild sheet](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/KNSoft.SlimDetours.targets) in NuGet package cannot link compiled library automatically, link manually is required, for example: +```C +#pragma comment(lib, "Debug/KNSoft.SlimDetours.lib") +``` + +Usage is similiar to the original [Microsoft Detours](https://github.com/microsoft/Detours), but: + +- Function name begin with `"SlimDetours"`, most of return values are `NTSTATUS`, use `NT_SUCCESS` macro to check them. +- Threads are updated automatically, `DetourUpdateThread` is removed. +```C +Status = SlimDetoursTransactionBegin(); +if (!NT_SUCCESS(Status)) +{ + return Status; +} +Status = SlimDetoursAttach((PVOID*)&g_pfnXxx, Hooked_Xxx); +if (!NT_SUCCESS(Status)) +{ + SlimDetoursTransactionAbort(); + return Status; +} +return SlimDetoursTransactionCommit(); +``` + +### Delay Hook + +"Delay Hook" will set hooks automatically when target DLL loaded. + +For example, call `SlimDetoursDelayAttach` to hook `a.dll!FuncXxx` automatically when `a.dll` loaded: +```C +SlimDetoursDelayAttach((PVOID*)&g_pfnFuncXxx, + Hooked_FuncXxx, + L"a.dll", + L"FuncXxx", + NULL, + NULL); +``` +Demo: [DelayHook.c](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/Demo/DelayHook.c) + +## Compatibility + +Project building: only support for the latest MSVC generation tools and SDKs is considered, but it is generally more widely backward compatible. + +Artifact integration: it is widely backward compatible with MSVC generation tools and different compilation configurations (e.g., `/MD`, `/MT`). + +Runtime environment: NT6 or above OS, x86/x64/ARM64 platform. + +> [!CAUTION] +> In beta stage, should be used with caution. + +## License + +[![GitHub License](https://img.shields.io/github/license/KNSoft/KNSoft.SlimDetours)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE) + +KNSoft.SlimDetours is licensed under the [MPL-2.0](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE) license. + +Source is based on [Microsoft Detours](https://github.com/microsoft/Detours) which is licensed under the [MIT](https://github.com/microsoft/Detours/blob/main/LICENSE) license. + +Also uses [KNSoft.NDK](https://github.com/KNSoft/KNSoft.NDK) to access low-level Windows NT APIs and its Unit Test Framework. diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..683914d7 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,106 @@ +| [English (en-US)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/README.md) | **简体中文 (zh-CN)** | +| --- | --- | + +
+ +# KNSoft.SlimDetours + +[![NuGet Downloads](https://img.shields.io/nuget/dt/KNSoft.SlimDetours)](https://www.nuget.org/packages/KNSoft.SlimDetours) [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/KNSoft/KNSoft.SlimDetours/msbuild.yml)](https://github.com/KNSoft/KNSoft.SlimDetours/actions/workflows/msbuild.yml) ![PR Welcome](https://img.shields.io/badge/PR-welcome-0688CB.svg) [![GitHub License](https://img.shields.io/github/license/KNSoft/KNSoft.SlimDetours)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE) + +[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)是一个基于[Microsoft Detours](https://github.com/microsoft/Detours)改进而来的Windows API挂钩库。 + +## 功能 + +相比于原版[Detours](https://github.com/microsoft/Detours),有以下优势: + +- 新功能 + - **支持延迟挂钩(目标DLL加载时自动挂钩)** [🔗 技术Wiki:实现延迟挂钩](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Implement%20Delay%20Hook/README.zh-CN.md) + - **挂钩时自动更新线程** [🔗 技术Wiki:应用内联钩子时自动更新线程](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Update%20Threads%20Automatically%20When%20Applying%20Inline%20Hooks/README.zh-CN.md) +- 经改进 + - **更新线程时避免堆死锁** [🔗 技术Wiki:更新线程时避免堆死锁](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads/README.zh-CN.md) + - 避免占用系统保留的内存区域 [🔗 技术Wiki:分配Trampoline时避免占用系统保留区域](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Docs/TechWiki/Avoid%20Occupying%20System%20Reserved%20Region%20When%20Allocating%20Trampoline/README.zh-CN.md) + - 其它Bug修复与代码改进 +- 轻量 + - **仅依赖`Ntdll.dll`** + - 仅保留API挂钩函数 + - 移除对ARM (ARM32)、IA64、WinXP、GNUC的支持 + - 更小的二进制体积 +- 开箱即用 + - NuGet包发布 + +## 用法 + +[![NuGet Downloads](https://img.shields.io/nuget/dt/KNSoft.SlimDetours)](https://www.nuget.org/packages/KNSoft.SlimDetours) + +### 提要 + +KNSoft.SlimDetours包体同时含有[SlimDetours](https://github.com/KNSoft/KNSoft.SlimDetours)与原版[Microsoft Detours](https://github.com/microsoft/Detours)。 + +对于KNSoft.SlimDetours包含[SlimDetours.h](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/SlimDetours/SlimDetours.h)头文件,或者对于原版[Microsoft Detours](https://github.com/microsoft/Detours)包含[detours.h](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/Detours/src/detours.h)头文件,然后链接编译出的库`KNSoft.SlimDetours.lib`。 + +NuGet包[KNSoft.SlimDetours](https://www.nuget.org/packages/KNSoft.SlimDetours)是开箱即用的,只要安装到项目,编译好的库就会自动加入链接。 + +```C +#include // KNSoft.SlimDetours +#include // Microsoft Detours +``` + +如果你项目的配置名称既不是“Release”也不是“Debug”,NuGet包中的[MSBuild表单](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/KNSoft.SlimDetours.targets)无法自动链接编译好的库,需要手动链接,例如: +```C +#pragma comment(lib, "Debug/KNSoft.SlimDetours.lib") +``` + +用法与原版[Microsoft Detours](https://github.com/microsoft/Detours)相似,除了: + +- 函数名以`"SlimDetours"`开头,大多数返回值是`NTSTATUS`,使用`NT_SUCCESS`宏检查。 +- 线程会被自动更新,`DetourUpdateThread`已移除。 +```C +Status = SlimDetoursTransactionBegin(); +if (!NT_SUCCESS(Status)) +{ + return Status; +} +Status = SlimDetoursAttach((PVOID*)&g_pfnXxx, Hooked_Xxx); +if (!NT_SUCCESS(Status)) +{ + SlimDetoursTransactionAbort(); + return Status; +} +return SlimDetoursTransactionCommit(); +``` + +### 延迟挂钩 + +“延迟挂钩”将在目标DLL加载时自动挂钩。 + +比如,调用`SlimDetoursDelayAttach`来在`a.dll`加载时自动挂勾`a.dll!FuncXxx`: +```C +SlimDetoursDelayAttach((PVOID*)&g_pfnFuncXxx, + Hooked_FuncXxx, + L"a.dll", + L"FuncXxx", + NULL, + NULL); +``` +演示:[DelayHook.c](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/Source/Demo/DelayHook.c) + +## 兼容性 + +项目构建:仅考虑对最新MSVC生成工具及SDK的支持,但一般也能较广泛地向下兼容。 + +制品集成:能较广泛地向下兼容MSVC生成工具,以及不同编译配置(如`/MD`、`/MT`)。 + +运行环境:NT6及以上操作系统,x86/x64/ARM64平台。 + +> [!CAUTION] +> 处于beta阶段,应小心使用。 + +## 协议 + +[![GitHub License](https://img.shields.io/github/license/KNSoft/KNSoft.SlimDetours)](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE) + +KNSoft.SlimDetours根据[MPL-2.0](https://github.com/KNSoft/KNSoft.SlimDetours/blob/main/LICENSE)协议进行许可。 + +源码基于[Microsoft Detours](https://github.com/microsoft/Detours),其根据[MIT](https://github.com/microsoft/Detours/blob/main/LICENSE)协议进行许可。 + +同时使用了[KNSoft.NDK](https://github.com/KNSoft/KNSoft.NDK)以访问底层Windows NT API及其中的单元测试框架。 diff --git a/Source/.editorconfig b/Source/.editorconfig new file mode 100644 index 00000000..aff578a0 --- /dev/null +++ b/Source/.editorconfig @@ -0,0 +1,77 @@ +# Visual Studio generated .editorconfig file with C++ settings. +root = true + +[*.{c,c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] + +charset = utf-8-bom +indent_style = space +indent_size = 4 +end_of_line = crlf +insert_final_newline = true +max_line_length = 120 + +# Visual C++ Code Style settings + +cpp_generate_documentation_comments = xml + +# Visual C++ Formatting settings + +cpp_indent_braces = false +cpp_indent_multi_line_relative_to = innermost_parenthesis +cpp_indent_within_parentheses = align_to_parenthesis +cpp_indent_preserve_within_parentheses = true +cpp_indent_case_contents = true +cpp_indent_case_labels = true +cpp_indent_case_contents_when_block = false +cpp_indent_lambda_braces_when_parameter = false +cpp_indent_goto_labels = leftmost_column +cpp_indent_preprocessor = leftmost_column +cpp_indent_access_specifiers = false +cpp_indent_namespace_contents = true +cpp_indent_preserve_comments = true +cpp_new_line_before_open_brace_namespace = new_line +cpp_new_line_before_open_brace_type = new_line +cpp_new_line_before_open_brace_function = new_line +cpp_new_line_before_open_brace_block = new_line +cpp_new_line_before_open_brace_lambda = new_line +cpp_new_line_scope_braces_on_separate_lines = true +cpp_new_line_close_brace_same_line_empty_type = true +cpp_new_line_close_brace_same_line_empty_function = true +cpp_new_line_before_catch = false +cpp_new_line_before_else = false +cpp_new_line_before_while_in_do_while = false +cpp_space_before_function_open_parenthesis = remove +cpp_space_within_parameter_list_parentheses = false +cpp_space_between_empty_parameter_list_parentheses = false +cpp_space_after_keywords_in_control_flow_statements = true +cpp_space_within_control_flow_statement_parentheses = false +cpp_space_before_lambda_open_parenthesis = false +cpp_space_within_cast_parentheses = false +cpp_space_after_cast_close_parenthesis = false +cpp_space_within_expression_parentheses = false +cpp_space_before_block_open_brace = true +cpp_space_between_empty_braces = false +cpp_space_before_initializer_list_open_brace = false +cpp_space_within_initializer_list_braces = true +cpp_space_preserve_in_initializer_list = true +cpp_space_before_open_square_bracket = false +cpp_space_within_square_brackets = false +cpp_space_before_empty_square_brackets = false +cpp_space_between_empty_square_brackets = false +cpp_space_group_square_brackets = true +cpp_space_within_lambda_brackets = false +cpp_space_between_empty_lambda_brackets = false +cpp_space_before_comma = false +cpp_space_after_comma = true +cpp_space_remove_around_member_operators = true +cpp_space_before_inheritance_colon = true +cpp_space_before_constructor_colon = true +cpp_space_remove_before_semicolon = true +cpp_space_after_semicolon = true +cpp_space_remove_around_unary_operator = true +cpp_space_around_binary_operator = insert +cpp_space_around_assignment_operator = insert +cpp_space_pointer_reference_alignment = ignore +cpp_space_around_ternary_operator = insert +cpp_use_unreal_engine_macro_formatting = true +cpp_wrap_preserve_blocks = all_one_line_scopes \ No newline at end of file diff --git a/Source/.gitignore b/Source/.gitignore new file mode 100644 index 00000000..bff2e8ed --- /dev/null +++ b/Source/.gitignore @@ -0,0 +1,8 @@ +.vs +*.user + +/packages +/OutDir +IntDir + +/*.nupkg \ No newline at end of file diff --git a/Source/Demo/DeadLock.c b/Source/Demo/DeadLock.c new file mode 100644 index 00000000..8cdb7908 --- /dev/null +++ b/Source/Demo/DeadLock.c @@ -0,0 +1,150 @@ +/* + * This demo shows the original Microsoft Detours may run into dead lock when update thread that operating CRT heap, + * KNSoft.SlimDetours has fixed this problem by using a private heap. + * + * Run "Demo.exe -Run DeadLock -Engine=MSDetours" in debugger, + * most probably breakpoint will be hitted and deadlock can be found in call stacks. + * Run "Demo.exe -Run DeadLock -Engine=SlimDetours" will pass this test. + * + * See also https://github.com/KNSoft/KNSoft.SlimDetours/tree/main/Docs/TechWiki/Avoid%20Deadlocking%20on%20The%20Heap%20When%20Updating%20Threads + */ + +#include "Demo.h" + +#define DEMO_WAIT_TIMEOUT 10000 + +static BOOL g_bStop = FALSE; +static DEMO_ENGINE_TYPE g_eEngineType = EngineInvalid; + +/* This thread repeat malloc/free */ +static +DWORD +WINAPI +HeapUserThread( + LPVOID lpThreadParameter) +{ + PVOID p; + DWORD ThreadId = GetCurrentThreadId(); + + while (!g_bStop) + { + p = malloc(4); + if (p != NULL) + { + free(p); + } + } + + UnitTest_FormatMessage("Heap user thread (%lu) exit\n", GetCurrentThreadId()); + return 0; +} + +/* This thread repeat hook/unhook */ +static +DWORD +WINAPI +SetHookThread( + LPVOID lpThreadParameter) +{ + HRESULT hr = S_OK; + BOOL EnableHook = TRUE; + + while (!g_bStop) + { + hr = HookTransactionBegin(g_eEngineType); + if (FAILED(hr)) + { + break; + } + if (g_eEngineType == EngineMicrosoftDetours) + { + hr = HRESULT_FROM_WIN32(DetourUpdateThread((HANDLE)lpThreadParameter)); + if (FAILED(hr)) + { + break; + } + } + hr = HookAttach(g_eEngineType, EnableHook, (PVOID*)&g_pfnEqualRect, Hooked_EqualRect); + if (FAILED(hr)) + { + HookTransactionAbort(g_eEngineType); + break; + } + hr = HookTransactionCommit(g_eEngineType); + if (FAILED(hr)) + { + break; + } + + EnableHook = !EnableHook; + } + + UnitTest_FormatMessage("Set hook thread (%lu) exit with 0x%08lX\n", GetCurrentThreadId(), hr); + return hr; +} + +static TEST_DECL(DeadLock) +{ + NTSTATUS Status; + HANDLE hThreads[2]; + DWORD dwRet; + + Status = GetEngineTypeFromArgs(TEST_PARAMETER_ARGC, TEST_PARAMETER_ARGV, &g_eEngineType); + if (!NT_SUCCESS(Status)) + { + TEST_SKIP("Invalid engine type\n"); + return; + } + Status = LoadEqualRect(); + if (!NT_SUCCESS(Status)) + { + TEST_SKIP("Load user32.dll!EqualRect failed with 0x%08lX\n", Status); + return; + } + + /* Create two threads, one thread calls malloc/free, another one calls detours */ + hThreads[0] = CreateThread(NULL, 0, HeapUserThread, NULL, CREATE_SUSPENDED, NULL); + if (hThreads[0] == NULL) + { + TEST_SKIP("CreateThread failed with 0x%08lX\n", GetLastError()); + return; + } + hThreads[1] = CreateThread(NULL, 0, SetHookThread, (PVOID)hThreads[0], CREATE_SUSPENDED, NULL); + if (hThreads[1] == NULL) + { + TEST_SKIP("CreateThread failed with 0x%08lX\n", GetLastError()); + g_bStop = TRUE; + ResumeThread(hThreads[0]); + WaitForSingleObject(hThreads[0], INFINITE); + CloseHandle(hThreads[0]); + return; + } + + /* Run them together and wait */ + UnitTest_FormatMessage("Run threads and wait (up to %lums)...\n", DEMO_WAIT_TIMEOUT * 2); + ResumeThread(hThreads[0]); + ResumeThread(hThreads[1]); + Sleep(DEMO_WAIT_TIMEOUT); + g_bStop = TRUE; + dwRet = WaitForMultipleObjects(ARRAYSIZE(hThreads), hThreads, TRUE, DEMO_WAIT_TIMEOUT); + CloseHandle(hThreads[0]); + CloseHandle(hThreads[1]); + + if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_OBJECT_0 + 1) + { + TEST_RESULT(Pass); + return; + } else if (dwRet == WAIT_TIMEOUT) + { + /* Deadlock very likely occurred! Break in debugger, check above two threads' call stack */ + if (IsDebuggerPresent()) + { + __debugbreak(); + } + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } else if (dwRet == WAIT_FAILED) + { + dwRet = GetLastError(); + } + TEST_SKIP("WaitForMultipleObjects failed with 0x%08lX\n", dwRet); +} diff --git a/Source/Demo/DelayHook.c b/Source/Demo/DelayHook.c new file mode 100644 index 00000000..40812b38 --- /dev/null +++ b/Source/Demo/DelayHook.c @@ -0,0 +1,90 @@ +/* + * This demo shows delay hook, user32.dll!EqualRect will be hooked automatically when user32.dll loaded. + * + * Run "Demo.exe -Run DelayHook". + * + * See also https://github.com/KNSoft/KNSoft.SlimDetours/tree/main/Docs/TechWiki/Implement%20Delay%20Hook + */ + +#include "Demo.h" + +static BOOL g_bDelayAttach = FALSE; + +static +VOID +CALLBACK +DelayAttachCallback( + _In_ NTSTATUS Status, + _In_ PVOID* ppPointer, + _In_ PCWSTR DllName, + _In_ PCSTR Function, + _In_opt_ PVOID Context) +{ + UnitTest_FormatMessage("Delay hook callback: Status = 0x%08lX, DllName = %ls, Function = %hs, Context = 0x%p\n", + Status, + DllName, + Function, + Context); + g_bDelayAttach = NT_SUCCESS(Status) && + _wcsicmp(DllName, g_usUser32.Buffer) == 0 && + _stricmp(Function, g_asEqualRect.Buffer) == 0; +} + +static TEST_DECL(DelayHook) +{ + NTSTATUS Status; + PVOID hUser32; + FN_EqualRect* pfnEqualRect; + RECT rc1 = { 0 }, rc2 = { 0 }; + + /* Register SlimDetours delay hook */ + Status = SlimDetoursDelayAttach((PVOID*)&g_pfnEqualRect, + Hooked_EqualRect, + g_usUser32.Buffer, + g_asEqualRect.Buffer, + DelayAttachCallback, + NULL); + if (!NT_SUCCESS(Status)) + { + TEST_FAIL("SlimDetoursDelayAttach failed with 0x%08lX\n", Status); + return; + } else if (Status != STATUS_PENDING) + { + TEST_FAIL("SlimDetoursDelayAttach succeeded with 0x%08lX, which is not using delay attach\n", Status); + return; + } + + /* Make sure user32.dll is not loaded yet */ + Status = LdrGetDllHandle(NULL, NULL, &g_usUser32, &hUser32); + if (NT_SUCCESS(Status)) + { + TEST_SKIP("user32.dll is loaded, test cannot continue\n"); + return; + } else if (Status != STATUS_DLL_NOT_FOUND) + { + TEST_SKIP("LdrGetDllHandle failed with 0x%08lX\n", Status); + return; + } + + /* Load user32.dll now */ + Status = LdrLoadDll(NULL, NULL, &g_usUser32, &hUser32); + if (!NT_SUCCESS(Status)) + { + TEST_SKIP("LdrLoadDll failed with 0x%08lX\n", Status); + return; + } + + /* Delay attach callback should be called and EqualRect is hooked successfully */ + TEST_OK(g_bDelayAttach); + Status = LdrGetProcedureAddress(hUser32, &g_asEqualRect, 0, (PVOID*)&pfnEqualRect); + if (NT_SUCCESS(Status)) + { + TEST_OK(pfnEqualRect(&rc1, &rc2) == TRUE); + TEST_OK(g_lEqualRectCount == 1); + } else + { + TEST_SKIP("LdrGetProcedureAddress failed with 0x%08lX\n", Status); + } + + LdrUnloadDll(hUser32); +} diff --git a/Source/Demo/Demo.h b/Source/Demo/Demo.h new file mode 100644 index 00000000..78b56b19 --- /dev/null +++ b/Source/Demo/Demo.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include "../SlimDetours/SlimDetours.h" +#include "../Detours/src/detours.h" + +EXTERN_C_START + +typedef +BOOL +WINAPI +FN_EqualRect( + _In_ CONST RECT *lprc1, + _In_ CONST RECT *lprc2); + +typedef enum _DEMO_ENGINE_TYPE +{ + EngineInvalid, + EngineSlimDetours, + EngineMicrosoftDetours +} DEMO_ENGINE_TYPE, *PDEMO_ENGINE_TYPE; + +EXTERN_C FN_EqualRect* g_pfnEqualRect; +EXTERN_C UNICODE_STRING g_usUser32; +EXTERN_C ANSI_STRING g_asEqualRect; +EXTERN_C LONG volatile g_lEqualRectCount; +EXTERN_C LONG volatile g_lEqualRectRefCount; + +NTSTATUS LoadEqualRect(VOID); + +BOOL +WINAPI +Hooked_EqualRect( + _In_ CONST RECT *lprc1, + _In_ CONST RECT *lprc2); + +HRESULT GetEngineTypeFromArgs( + _In_ INT ArgC, + _In_reads_(ArgC) _Pre_z_ PCWSTR* ArgV, + _Out_ PDEMO_ENGINE_TYPE EngineType); + +HRESULT HookTransactionBegin( + _In_ DEMO_ENGINE_TYPE EngineType); + +HRESULT HookTransactionAbort( + _In_ DEMO_ENGINE_TYPE EngineType); + +HRESULT HookTransactionCommit( + _In_ DEMO_ENGINE_TYPE EngineType); + +HRESULT HookAttach( + _In_ DEMO_ENGINE_TYPE EngineType, + _In_ BOOL Enable, + _Inout_ PVOID* ppPointer, + _In_ PVOID pDetour); + +EXTERN_C_END diff --git a/Source/Demo/Demo.vcxproj b/Source/Demo/Demo.vcxproj new file mode 100644 index 00000000..8b69fd85 --- /dev/null +++ b/Source/Demo/Demo.vcxproj @@ -0,0 +1,199 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {1c85580b-f884-4347-a67e-5083ab36ae74} + Demo + 10.0 + + + + Application + true + + + Application + false + true + + + Application + true + + + Application + true + + + Application + false + true + + + Application + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + $(OutDir)$(SolutionName).lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/Source/Demo/Demo.vcxproj.filters b/Source/Demo/Demo.vcxproj.filters new file mode 100644 index 00000000..894e0e09 --- /dev/null +++ b/Source/Demo/Demo.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Demo/Main.c b/Source/Demo/Main.c new file mode 100644 index 00000000..38bd849a --- /dev/null +++ b/Source/Demo/Main.c @@ -0,0 +1,144 @@ +#include "Demo.h" + +#include + +#define DEMO_SWITCH_ENGINE L"-Engine=" + +FN_EqualRect* g_pfnEqualRect = NULL; +UNICODE_STRING g_usUser32 = RTL_CONSTANT_STRING(L"user32.dll"); +ANSI_STRING g_asEqualRect = RTL_CONSTANT_STRING("EqualRect"); +LONG volatile g_lEqualRectCount = 0; +LONG volatile g_lEqualRectRefCount = 0; + +int _cdecl wmain( + _In_ int argc, + _In_reads_(argc) _Pre_z_ wchar_t** argv) +{ + return UnitTest_Main(argc, argv); +} + +HRESULT GetEngineTypeFromArgs( + _In_ INT ArgC, + _In_reads_(ArgC) _Pre_z_ PCWSTR* ArgV, + _Out_ PDEMO_ENGINE_TYPE EngineType) +{ + INT i; + PCWSTR pszEngineType; + + for (i = 0; i < ArgC; i++) + { + if (_wcsnicmp(ArgV[i], DEMO_SWITCH_ENGINE, _STR_CCH_LEN(DEMO_SWITCH_ENGINE)) == 0) + { + pszEngineType = ArgV[i] + _STR_CCH_LEN(DEMO_SWITCH_ENGINE); + if (_wcsicmp(pszEngineType, L"SlimDetours") == 0) + { + *EngineType = EngineSlimDetours; + return S_OK; + } else if (_wcsicmp(pszEngineType, L"MSDetours") == 0) + { + *EngineType = EngineMicrosoftDetours; + return S_OK; + } + break; + } + } + + return E_INVALIDARG; +} + +NTSTATUS LoadEqualRect(VOID) +{ + NTSTATUS Status; + PVOID hUser32; + + Status = LdrLoadDll(NULL, NULL, &g_usUser32, &hUser32); + if (!NT_SUCCESS(Status)) + { + return Status; + } + return LdrGetProcedureAddress(hUser32, &g_asEqualRect, 0, (PVOID*)&g_pfnEqualRect); +} + +BOOL +WINAPI +Hooked_EqualRect( + _In_ CONST RECT *lprc1, + _In_ CONST RECT *lprc2) +{ + BOOL Ret; + + InterlockedIncrement(&g_lEqualRectCount); + UnitTest_FormatMessage("Hooked EqualRect enter: lprc1 = (%d, %d, %d, %d), lprc2 = (%d, %d, %d, %d)\n", + lprc1->top, + lprc1->right, + lprc1->bottom, + lprc1->left, + lprc2->top, + lprc2->right, + lprc2->bottom, + lprc2->left); + InterlockedIncrement(&g_lEqualRectRefCount); + Ret = g_pfnEqualRect(lprc1, lprc2); + InterlockedDecrement(&g_lEqualRectRefCount); + UnitTest_FormatMessage("Hooked EqualRect leave with return value: %ld\n", Ret); + return Ret; +} + +HRESULT HookTransactionBegin( + _In_ DEMO_ENGINE_TYPE EngineType) +{ + if (EngineType == EngineSlimDetours) + { + return HRESULT_FROM_NT(SlimDetoursTransactionBegin()); + } else if (EngineType == EngineMicrosoftDetours) + { + return HRESULT_FROM_WIN32(DetourTransactionBegin()); + } + + return E_NOTIMPL; +} + +HRESULT HookTransactionAbort( + _In_ DEMO_ENGINE_TYPE EngineType) +{ + if (EngineType == EngineSlimDetours) + { + return HRESULT_FROM_NT(SlimDetoursTransactionAbort()); + } else if (EngineType == EngineMicrosoftDetours) + { + return HRESULT_FROM_WIN32(DetourTransactionAbort()); + } + + return E_NOTIMPL; +} + +HRESULT HookTransactionCommit( + _In_ DEMO_ENGINE_TYPE EngineType) +{ + if (EngineType == EngineSlimDetours) + { + return HRESULT_FROM_NT(SlimDetoursTransactionCommit()); + } else if (EngineType == EngineMicrosoftDetours) + { + return HRESULT_FROM_WIN32(DetourTransactionCommit()); + } + + return E_NOTIMPL; +} + +HRESULT HookAttach( + _In_ DEMO_ENGINE_TYPE EngineType, + _In_ BOOL Enable, + _Inout_ PVOID* ppPointer, + _In_ PVOID pDetour) +{ + if (EngineType == EngineSlimDetours) + { + return HRESULT_FROM_NT(Enable ? SlimDetoursAttach(ppPointer, pDetour) : SlimDetoursDetach(ppPointer, pDetour)); + } else if (EngineType == EngineMicrosoftDetours) + { + return HRESULT_FROM_WIN32(Enable ? DetourAttach(ppPointer, pDetour) : DetourDetach(ppPointer, pDetour)); + } + + return E_NOTIMPL; +} diff --git a/Source/Demo/packages.config b/Source/Demo/packages.config new file mode 100644 index 00000000..ba82655d --- /dev/null +++ b/Source/Demo/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Source/Detours/.editorconfig b/Source/Detours/.editorconfig new file mode 100644 index 00000000..096df14e --- /dev/null +++ b/Source/Detours/.editorconfig @@ -0,0 +1,10 @@ +# Top-most EditorConfig file +root = true + +# Use same style for all files +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +insert_final_newline = false +trim_trailing_whitespace = true diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/Source/Detours/.github/ISSUE_TEMPLATE/bug-report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/bug-report.md rename to Source/Detours/.github/ISSUE_TEMPLATE/bug-report.md diff --git a/.github/ISSUE_TEMPLATE/question.md b/Source/Detours/.github/ISSUE_TEMPLATE/question.md similarity index 100% rename from .github/ISSUE_TEMPLATE/question.md rename to Source/Detours/.github/ISSUE_TEMPLATE/question.md diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/Source/Detours/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE/pull_request_template.md rename to Source/Detours/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md diff --git a/.github/codeql/codeql-config.yml b/Source/Detours/.github/codeql/codeql-config.yml similarity index 100% rename from .github/codeql/codeql-config.yml rename to Source/Detours/.github/codeql/codeql-config.yml diff --git a/.github/dependabot.yml b/Source/Detours/.github/dependabot.yml similarity index 100% rename from .github/dependabot.yml rename to Source/Detours/.github/dependabot.yml diff --git a/.github/fabricbot.json b/Source/Detours/.github/fabricbot.json similarity index 100% rename from .github/fabricbot.json rename to Source/Detours/.github/fabricbot.json diff --git a/.github/workflows/main.yml b/Source/Detours/.github/workflows/main.yml similarity index 100% rename from .github/workflows/main.yml rename to Source/Detours/.github/workflows/main.yml diff --git a/Source/Detours/.gitignore b/Source/Detours/.gitignore new file mode 100644 index 00000000..b5a68d43 --- /dev/null +++ b/Source/Detours/.gitignore @@ -0,0 +1,41 @@ +# C extensions +*.so + +# Unit test / coverage reports +.coverage +.tox +nosetests.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# vim +*~ +*.swp + +# Visual Studio build +*.ipch +.vs/ +output/ +include/ +*.exp +*.pdb +*.lib +*.dll +*.exe +obj.* +*.ipdb +*.iobj +*.tlog +*.log +*.obj +*.user +*.recipe +/bin.* +*.vcxproj.FileListAbsolute.txt +*.vcxprojAssemblyReference.cache diff --git a/CREDITS.md b/Source/Detours/CREDITS.md similarity index 100% rename from CREDITS.md rename to Source/Detours/CREDITS.md diff --git a/Source/Detours/LICENSE b/Source/Detours/LICENSE new file mode 100644 index 00000000..b2f52a2b --- /dev/null +++ b/Source/Detours/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.md b/Source/Detours/LICENSE.md similarity index 100% rename from LICENSE.md rename to Source/Detours/LICENSE.md diff --git a/Makefile b/Source/Detours/Makefile similarity index 100% rename from Makefile rename to Source/Detours/Makefile diff --git a/Source/Detours/README.md b/Source/Detours/README.md new file mode 100644 index 00000000..84a4d14f --- /dev/null +++ b/Source/Detours/README.md @@ -0,0 +1,56 @@ +# Microsoft Research Detours Package + +Detours is a software package for monitoring and instrumenting API calls on Windows. Detours +has been used by many ISVs and is also used by product teams at Microsoft. Detours is now available under +a standard open source license ([MIT](https://github.com/microsoft/Detours/blob/master/LICENSE.md)). This simplifies licensing for programmers using Detours +and allows the community to support Detours using open source tools and processes. + +Detours is compatible with the Windows NT family of +operating systems: Windows NT, Windows XP, Windows Server 2003, Windows 7, +Windows 8, and Windows 10. It cannot be used by Windows Store apps +because Detours requires APIs not available to those applications. +This repo contains the source code for version 4.0.1 of Detours. + +For technical documentation on Detours, see the [Detours Wiki](https://github.com/microsoft/Detours/wiki). +For directions on how to build and run samples, see the +samples [README.txt](https://github.com/Microsoft/Detours/blob/master/samples/README.TXT) file. + +## Contributing + +The [`Detours`](https://github.com/microsoft/detours) repository is where development is done. +Here are some ways you can participate in the project: + +* [Answer questions](https://github.com/microsoft/detours/issues) about using Detours. +* [Improve the Wiki](https://github.com/microsoft/detours/wiki). +* [Submit bugs](https://github.com/microsoft/detours/issues) and help us verify fixes and changes as they are checked in. +* Review [source code changes](https://github.com/microsoft/detours/pulls). + +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. +For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +## Issues, questions, and feedback + +* Open an issue on [GitHub Issues](https://github.com/Microsoft/detours/issues). + +## Mailing list for announcements + +The detours-announce mailing list is a low-traffic email list for important announcements +about the project, such as the availability of new versions of Detours. To join it, send +an email to listserv@lists.research.microsoft.com with a +message body containing only the text SUBSCRIBE DETOURS-ANNOUNCE. +To leave it, send an email to listserv@lists.research.microsoft.com with a +message body containing only the text UNSUBSCRIBE DETOURS-ANNOUNCE. + + +## License + +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the [MIT](LICENSE.md) License. diff --git a/SECURITY.md b/Source/Detours/SECURITY.md similarity index 100% rename from SECURITY.md rename to Source/Detours/SECURITY.md diff --git a/samples/Makefile b/Source/Detours/samples/Makefile similarity index 100% rename from samples/Makefile rename to Source/Detours/samples/Makefile diff --git a/samples/README.TXT b/Source/Detours/samples/README.TXT similarity index 100% rename from samples/README.TXT rename to Source/Detours/samples/README.TXT diff --git a/samples/comeasy/Makefile b/Source/Detours/samples/comeasy/Makefile similarity index 100% rename from samples/comeasy/Makefile rename to Source/Detours/samples/comeasy/Makefile diff --git a/samples/comeasy/comeasy.cpp b/Source/Detours/samples/comeasy/comeasy.cpp similarity index 100% rename from samples/comeasy/comeasy.cpp rename to Source/Detours/samples/comeasy/comeasy.cpp diff --git a/samples/comeasy/wrotei.cpp b/Source/Detours/samples/comeasy/wrotei.cpp similarity index 100% rename from samples/comeasy/wrotei.cpp rename to Source/Detours/samples/comeasy/wrotei.cpp diff --git a/samples/comeasy/wrotei.rc b/Source/Detours/samples/comeasy/wrotei.rc similarity index 100% rename from samples/comeasy/wrotei.rc rename to Source/Detours/samples/comeasy/wrotei.rc diff --git a/samples/commem/Makefile b/Source/Detours/samples/commem/Makefile similarity index 100% rename from samples/commem/Makefile rename to Source/Detours/samples/commem/Makefile diff --git a/samples/commem/commem.cpp b/Source/Detours/samples/commem/commem.cpp similarity index 100% rename from samples/commem/commem.cpp rename to Source/Detours/samples/commem/commem.cpp diff --git a/samples/common.mak b/Source/Detours/samples/common.mak similarity index 100% rename from samples/common.mak rename to Source/Detours/samples/common.mak diff --git a/samples/cping/Makefile b/Source/Detours/samples/cping/Makefile similarity index 100% rename from samples/cping/Makefile rename to Source/Detours/samples/cping/Makefile diff --git a/samples/cping/ReadMe.Txt b/Source/Detours/samples/cping/ReadMe.Txt similarity index 100% rename from samples/cping/ReadMe.Txt rename to Source/Detours/samples/cping/ReadMe.Txt diff --git a/samples/cping/cping.cpp b/Source/Detours/samples/cping/cping.cpp similarity index 100% rename from samples/cping/cping.cpp rename to Source/Detours/samples/cping/cping.cpp diff --git a/samples/cping/cping.dat b/Source/Detours/samples/cping/cping.dat similarity index 100% rename from samples/cping/cping.dat rename to Source/Detours/samples/cping/cping.dat diff --git a/samples/cping/iping.idl b/Source/Detours/samples/cping/iping.idl similarity index 100% rename from samples/cping/iping.idl rename to Source/Detours/samples/cping/iping.idl diff --git a/samples/disas/Makefile b/Source/Detours/samples/disas/Makefile similarity index 100% rename from samples/disas/Makefile rename to Source/Detours/samples/disas/Makefile diff --git a/samples/disas/arm.asm b/Source/Detours/samples/disas/arm.asm similarity index 100% rename from samples/disas/arm.asm rename to Source/Detours/samples/disas/arm.asm diff --git a/samples/disas/disas.cpp b/Source/Detours/samples/disas/disas.cpp similarity index 100% rename from samples/disas/disas.cpp rename to Source/Detours/samples/disas/disas.cpp diff --git a/samples/disas/ia64.asm b/Source/Detours/samples/disas/ia64.asm similarity index 100% rename from samples/disas/ia64.asm rename to Source/Detours/samples/disas/ia64.asm diff --git a/samples/disas/unk.cpp b/Source/Detours/samples/disas/unk.cpp similarity index 100% rename from samples/disas/unk.cpp rename to Source/Detours/samples/disas/unk.cpp diff --git a/samples/disas/x64.asm b/Source/Detours/samples/disas/x64.asm similarity index 100% rename from samples/disas/x64.asm rename to Source/Detours/samples/disas/x64.asm diff --git a/samples/disas/x86.cpp b/Source/Detours/samples/disas/x86.cpp similarity index 100% rename from samples/disas/x86.cpp rename to Source/Detours/samples/disas/x86.cpp diff --git a/samples/dtest/Makefile b/Source/Detours/samples/dtest/Makefile similarity index 100% rename from samples/dtest/Makefile rename to Source/Detours/samples/dtest/Makefile diff --git a/samples/dtest/NORMAL_IA64.TXT b/Source/Detours/samples/dtest/NORMAL_IA64.TXT similarity index 100% rename from samples/dtest/NORMAL_IA64.TXT rename to Source/Detours/samples/dtest/NORMAL_IA64.TXT diff --git a/samples/dtest/NORMAL_X64.TXT b/Source/Detours/samples/dtest/NORMAL_X64.TXT similarity index 100% rename from samples/dtest/NORMAL_X64.TXT rename to Source/Detours/samples/dtest/NORMAL_X64.TXT diff --git a/samples/dtest/NORMAL_X86.TXT b/Source/Detours/samples/dtest/NORMAL_X86.TXT similarity index 100% rename from samples/dtest/NORMAL_X86.TXT rename to Source/Detours/samples/dtest/NORMAL_X86.TXT diff --git a/samples/dtest/dtarge.cpp b/Source/Detours/samples/dtest/dtarge.cpp similarity index 100% rename from samples/dtest/dtarge.cpp rename to Source/Detours/samples/dtest/dtarge.cpp diff --git a/samples/dtest/dtarge.h b/Source/Detours/samples/dtest/dtarge.h similarity index 100% rename from samples/dtest/dtarge.h rename to Source/Detours/samples/dtest/dtarge.h diff --git a/samples/dtest/dtarge.rc b/Source/Detours/samples/dtest/dtarge.rc similarity index 100% rename from samples/dtest/dtarge.rc rename to Source/Detours/samples/dtest/dtarge.rc diff --git a/samples/dtest/dtest.cpp b/Source/Detours/samples/dtest/dtest.cpp similarity index 100% rename from samples/dtest/dtest.cpp rename to Source/Detours/samples/dtest/dtest.cpp diff --git a/samples/dumpe/Makefile b/Source/Detours/samples/dumpe/Makefile similarity index 100% rename from samples/dumpe/Makefile rename to Source/Detours/samples/dumpe/Makefile diff --git a/samples/dumpe/dumpe.cpp b/Source/Detours/samples/dumpe/dumpe.cpp similarity index 100% rename from samples/dumpe/dumpe.cpp rename to Source/Detours/samples/dumpe/dumpe.cpp diff --git a/samples/dumpi/Makefile b/Source/Detours/samples/dumpi/Makefile similarity index 100% rename from samples/dumpi/Makefile rename to Source/Detours/samples/dumpi/Makefile diff --git a/samples/dumpi/dumpi.cpp b/Source/Detours/samples/dumpi/dumpi.cpp similarity index 100% rename from samples/dumpi/dumpi.cpp rename to Source/Detours/samples/dumpi/dumpi.cpp diff --git a/samples/dynamic_alloc/Makefile b/Source/Detours/samples/dynamic_alloc/Makefile similarity index 100% rename from samples/dynamic_alloc/Makefile rename to Source/Detours/samples/dynamic_alloc/Makefile diff --git a/samples/dynamic_alloc/main.cpp b/Source/Detours/samples/dynamic_alloc/main.cpp similarity index 100% rename from samples/dynamic_alloc/main.cpp rename to Source/Detours/samples/dynamic_alloc/main.cpp diff --git a/samples/dynamic_alloc/x64.asm b/Source/Detours/samples/dynamic_alloc/x64.asm similarity index 100% rename from samples/dynamic_alloc/x64.asm rename to Source/Detours/samples/dynamic_alloc/x64.asm diff --git a/samples/dynamic_alloc/x86.asm b/Source/Detours/samples/dynamic_alloc/x86.asm similarity index 100% rename from samples/dynamic_alloc/x86.asm rename to Source/Detours/samples/dynamic_alloc/x86.asm diff --git a/samples/echo/Makefile b/Source/Detours/samples/echo/Makefile similarity index 100% rename from samples/echo/Makefile rename to Source/Detours/samples/echo/Makefile diff --git a/samples/echo/echofx.cpp b/Source/Detours/samples/echo/echofx.cpp similarity index 100% rename from samples/echo/echofx.cpp rename to Source/Detours/samples/echo/echofx.cpp diff --git a/samples/echo/echofx.rc b/Source/Detours/samples/echo/echofx.rc similarity index 100% rename from samples/echo/echofx.rc rename to Source/Detours/samples/echo/echofx.rc diff --git a/samples/echo/echonul.cpp b/Source/Detours/samples/echo/echonul.cpp similarity index 100% rename from samples/echo/echonul.cpp rename to Source/Detours/samples/echo/echonul.cpp diff --git a/samples/echo/main.cpp b/Source/Detours/samples/echo/main.cpp similarity index 100% rename from samples/echo/main.cpp rename to Source/Detours/samples/echo/main.cpp diff --git a/samples/einst/Makefile b/Source/Detours/samples/einst/Makefile similarity index 100% rename from samples/einst/Makefile rename to Source/Detours/samples/einst/Makefile diff --git a/samples/einst/edll1x.cpp b/Source/Detours/samples/einst/edll1x.cpp similarity index 100% rename from samples/einst/edll1x.cpp rename to Source/Detours/samples/einst/edll1x.cpp diff --git a/samples/einst/edll2x.cpp b/Source/Detours/samples/einst/edll2x.cpp similarity index 100% rename from samples/einst/edll2x.cpp rename to Source/Detours/samples/einst/edll2x.cpp diff --git a/samples/einst/edll3x.cpp b/Source/Detours/samples/einst/edll3x.cpp similarity index 100% rename from samples/einst/edll3x.cpp rename to Source/Detours/samples/einst/edll3x.cpp diff --git a/samples/einst/einst.cpp b/Source/Detours/samples/einst/einst.cpp similarity index 100% rename from samples/einst/einst.cpp rename to Source/Detours/samples/einst/einst.cpp diff --git a/samples/excep/Makefile b/Source/Detours/samples/excep/Makefile similarity index 100% rename from samples/excep/Makefile rename to Source/Detours/samples/excep/Makefile diff --git a/samples/excep/excep.cpp b/Source/Detours/samples/excep/excep.cpp similarity index 100% rename from samples/excep/excep.cpp rename to Source/Detours/samples/excep/excep.cpp diff --git a/samples/excep/firstexc.cpp b/Source/Detours/samples/excep/firstexc.cpp similarity index 100% rename from samples/excep/firstexc.cpp rename to Source/Detours/samples/excep/firstexc.cpp diff --git a/samples/excep/firstexc.h b/Source/Detours/samples/excep/firstexc.h similarity index 100% rename from samples/excep/firstexc.h rename to Source/Detours/samples/excep/firstexc.h diff --git a/samples/findfunc/Makefile b/Source/Detours/samples/findfunc/Makefile similarity index 100% rename from samples/findfunc/Makefile rename to Source/Detours/samples/findfunc/Makefile diff --git a/samples/findfunc/extend.cpp b/Source/Detours/samples/findfunc/extend.cpp similarity index 100% rename from samples/findfunc/extend.cpp rename to Source/Detours/samples/findfunc/extend.cpp diff --git a/samples/findfunc/extend.rc b/Source/Detours/samples/findfunc/extend.rc similarity index 100% rename from samples/findfunc/extend.rc rename to Source/Detours/samples/findfunc/extend.rc diff --git a/samples/findfunc/findfunc.cpp b/Source/Detours/samples/findfunc/findfunc.cpp similarity index 100% rename from samples/findfunc/findfunc.cpp rename to Source/Detours/samples/findfunc/findfunc.cpp diff --git a/samples/findfunc/symtest.cpp b/Source/Detours/samples/findfunc/symtest.cpp similarity index 100% rename from samples/findfunc/symtest.cpp rename to Source/Detours/samples/findfunc/symtest.cpp diff --git a/samples/findfunc/target.cpp b/Source/Detours/samples/findfunc/target.cpp similarity index 100% rename from samples/findfunc/target.cpp rename to Source/Detours/samples/findfunc/target.cpp diff --git a/samples/findfunc/target.h b/Source/Detours/samples/findfunc/target.h similarity index 100% rename from samples/findfunc/target.h rename to Source/Detours/samples/findfunc/target.h diff --git a/samples/findfunc/target.rc b/Source/Detours/samples/findfunc/target.rc similarity index 100% rename from samples/findfunc/target.rc rename to Source/Detours/samples/findfunc/target.rc diff --git a/samples/impmunge/Makefile b/Source/Detours/samples/impmunge/Makefile similarity index 100% rename from samples/impmunge/Makefile rename to Source/Detours/samples/impmunge/Makefile diff --git a/samples/impmunge/impmunge.cpp b/Source/Detours/samples/impmunge/impmunge.cpp similarity index 100% rename from samples/impmunge/impmunge.cpp rename to Source/Detours/samples/impmunge/impmunge.cpp diff --git a/samples/member/Makefile b/Source/Detours/samples/member/Makefile similarity index 100% rename from samples/member/Makefile rename to Source/Detours/samples/member/Makefile diff --git a/samples/member/member.cpp b/Source/Detours/samples/member/member.cpp similarity index 100% rename from samples/member/member.cpp rename to Source/Detours/samples/member/member.cpp diff --git a/samples/opengl/Makefile b/Source/Detours/samples/opengl/Makefile similarity index 100% rename from samples/opengl/Makefile rename to Source/Detours/samples/opengl/Makefile diff --git a/samples/opengl/ogldet.cpp b/Source/Detours/samples/opengl/ogldet.cpp similarity index 100% rename from samples/opengl/ogldet.cpp rename to Source/Detours/samples/opengl/ogldet.cpp diff --git a/samples/opengl/ogldet.rc b/Source/Detours/samples/opengl/ogldet.rc similarity index 100% rename from samples/opengl/ogldet.rc rename to Source/Detours/samples/opengl/ogldet.rc diff --git a/samples/opengl/testogl.cpp b/Source/Detours/samples/opengl/testogl.cpp similarity index 100% rename from samples/opengl/testogl.cpp rename to Source/Detours/samples/opengl/testogl.cpp diff --git a/samples/payload/Makefile b/Source/Detours/samples/payload/Makefile similarity index 100% rename from samples/payload/Makefile rename to Source/Detours/samples/payload/Makefile diff --git a/samples/payload/payload.cpp b/Source/Detours/samples/payload/payload.cpp similarity index 100% rename from samples/payload/payload.cpp rename to Source/Detours/samples/payload/payload.cpp diff --git a/samples/payload/payloadguid.hpp b/Source/Detours/samples/payload/payloadguid.hpp similarity index 100% rename from samples/payload/payloadguid.hpp rename to Source/Detours/samples/payload/payloadguid.hpp diff --git a/samples/payload/payloadtarget.cpp b/Source/Detours/samples/payload/payloadtarget.cpp similarity index 100% rename from samples/payload/payloadtarget.cpp rename to Source/Detours/samples/payload/payloadtarget.cpp diff --git a/samples/region/Makefile b/Source/Detours/samples/region/Makefile similarity index 100% rename from samples/region/Makefile rename to Source/Detours/samples/region/Makefile diff --git a/samples/region/region.cpp b/Source/Detours/samples/region/region.cpp similarity index 100% rename from samples/region/region.cpp rename to Source/Detours/samples/region/region.cpp diff --git a/samples/setdll/Makefile b/Source/Detours/samples/setdll/Makefile similarity index 100% rename from samples/setdll/Makefile rename to Source/Detours/samples/setdll/Makefile diff --git a/samples/setdll/setdll.cpp b/Source/Detours/samples/setdll/setdll.cpp similarity index 100% rename from samples/setdll/setdll.cpp rename to Source/Detours/samples/setdll/setdll.cpp diff --git a/samples/simple/Makefile b/Source/Detours/samples/simple/Makefile similarity index 100% rename from samples/simple/Makefile rename to Source/Detours/samples/simple/Makefile diff --git a/samples/simple/simple.cpp b/Source/Detours/samples/simple/simple.cpp similarity index 100% rename from samples/simple/simple.cpp rename to Source/Detours/samples/simple/simple.cpp diff --git a/samples/simple/simple.rc b/Source/Detours/samples/simple/simple.rc similarity index 100% rename from samples/simple/simple.rc rename to Source/Detours/samples/simple/simple.rc diff --git a/samples/simple/sleep5.cpp b/Source/Detours/samples/simple/sleep5.cpp similarity index 100% rename from samples/simple/sleep5.cpp rename to Source/Detours/samples/simple/sleep5.cpp diff --git a/samples/simple_safe/Makefile b/Source/Detours/samples/simple_safe/Makefile similarity index 100% rename from samples/simple_safe/Makefile rename to Source/Detours/samples/simple_safe/Makefile diff --git a/samples/simple_safe/simple_safe.cpp b/Source/Detours/samples/simple_safe/simple_safe.cpp similarity index 100% rename from samples/simple_safe/simple_safe.cpp rename to Source/Detours/samples/simple_safe/simple_safe.cpp diff --git a/samples/simple_safe/simple_safe.rc b/Source/Detours/samples/simple_safe/simple_safe.rc similarity index 100% rename from samples/simple_safe/simple_safe.rc rename to Source/Detours/samples/simple_safe/simple_safe.rc diff --git a/samples/simple_safe/sleep5.cpp b/Source/Detours/samples/simple_safe/sleep5.cpp similarity index 100% rename from samples/simple_safe/sleep5.cpp rename to Source/Detours/samples/simple_safe/sleep5.cpp diff --git a/samples/slept/Makefile b/Source/Detours/samples/slept/Makefile similarity index 100% rename from samples/slept/Makefile rename to Source/Detours/samples/slept/Makefile diff --git a/samples/slept/NORMAL_IA64.TXT b/Source/Detours/samples/slept/NORMAL_IA64.TXT similarity index 100% rename from samples/slept/NORMAL_IA64.TXT rename to Source/Detours/samples/slept/NORMAL_IA64.TXT diff --git a/samples/slept/NORMAL_X64.TXT b/Source/Detours/samples/slept/NORMAL_X64.TXT similarity index 100% rename from samples/slept/NORMAL_X64.TXT rename to Source/Detours/samples/slept/NORMAL_X64.TXT diff --git a/samples/slept/NORMAL_X86.TXT b/Source/Detours/samples/slept/NORMAL_X86.TXT similarity index 100% rename from samples/slept/NORMAL_X86.TXT rename to Source/Detours/samples/slept/NORMAL_X86.TXT diff --git a/samples/slept/dslept.cpp b/Source/Detours/samples/slept/dslept.cpp similarity index 100% rename from samples/slept/dslept.cpp rename to Source/Detours/samples/slept/dslept.cpp diff --git a/samples/slept/dslept.rc b/Source/Detours/samples/slept/dslept.rc similarity index 100% rename from samples/slept/dslept.rc rename to Source/Detours/samples/slept/dslept.rc diff --git a/samples/slept/sleepbed.cpp b/Source/Detours/samples/slept/sleepbed.cpp similarity index 100% rename from samples/slept/sleepbed.cpp rename to Source/Detours/samples/slept/sleepbed.cpp diff --git a/samples/slept/sleepnew.cpp b/Source/Detours/samples/slept/sleepnew.cpp similarity index 100% rename from samples/slept/sleepnew.cpp rename to Source/Detours/samples/slept/sleepnew.cpp diff --git a/samples/slept/sleepold.cpp b/Source/Detours/samples/slept/sleepold.cpp similarity index 100% rename from samples/slept/sleepold.cpp rename to Source/Detours/samples/slept/sleepold.cpp diff --git a/samples/slept/slept.cpp b/Source/Detours/samples/slept/slept.cpp similarity index 100% rename from samples/slept/slept.cpp rename to Source/Detours/samples/slept/slept.cpp diff --git a/samples/slept/slept.h b/Source/Detours/samples/slept/slept.h similarity index 100% rename from samples/slept/slept.h rename to Source/Detours/samples/slept/slept.h diff --git a/samples/slept/slept.rc b/Source/Detours/samples/slept/slept.rc similarity index 100% rename from samples/slept/slept.rc rename to Source/Detours/samples/slept/slept.rc diff --git a/samples/slept/verify.cpp b/Source/Detours/samples/slept/verify.cpp similarity index 100% rename from samples/slept/verify.cpp rename to Source/Detours/samples/slept/verify.cpp diff --git a/samples/syelog/Makefile b/Source/Detours/samples/syelog/Makefile similarity index 100% rename from samples/syelog/Makefile rename to Source/Detours/samples/syelog/Makefile diff --git a/samples/syelog/sltest.cpp b/Source/Detours/samples/syelog/sltest.cpp similarity index 100% rename from samples/syelog/sltest.cpp rename to Source/Detours/samples/syelog/sltest.cpp diff --git a/samples/syelog/sltestp.cpp b/Source/Detours/samples/syelog/sltestp.cpp similarity index 100% rename from samples/syelog/sltestp.cpp rename to Source/Detours/samples/syelog/sltestp.cpp diff --git a/samples/syelog/syelog.cpp b/Source/Detours/samples/syelog/syelog.cpp similarity index 100% rename from samples/syelog/syelog.cpp rename to Source/Detours/samples/syelog/syelog.cpp diff --git a/samples/syelog/syelog.h b/Source/Detours/samples/syelog/syelog.h similarity index 100% rename from samples/syelog/syelog.h rename to Source/Detours/samples/syelog/syelog.h diff --git a/samples/syelog/syelogd.cpp b/Source/Detours/samples/syelog/syelogd.cpp similarity index 100% rename from samples/syelog/syelogd.cpp rename to Source/Detours/samples/syelog/syelogd.cpp diff --git a/samples/talloc/Makefile b/Source/Detours/samples/talloc/Makefile similarity index 100% rename from samples/talloc/Makefile rename to Source/Detours/samples/talloc/Makefile diff --git a/samples/talloc/NORMAL_IA64.TXT b/Source/Detours/samples/talloc/NORMAL_IA64.TXT similarity index 100% rename from samples/talloc/NORMAL_IA64.TXT rename to Source/Detours/samples/talloc/NORMAL_IA64.TXT diff --git a/samples/talloc/NORMAL_X64.TXT b/Source/Detours/samples/talloc/NORMAL_X64.TXT similarity index 100% rename from samples/talloc/NORMAL_X64.TXT rename to Source/Detours/samples/talloc/NORMAL_X64.TXT diff --git a/samples/talloc/talloc.cpp b/Source/Detours/samples/talloc/talloc.cpp similarity index 100% rename from samples/talloc/talloc.cpp rename to Source/Detours/samples/talloc/talloc.cpp diff --git a/samples/talloc/tdll1x.cpp b/Source/Detours/samples/talloc/tdll1x.cpp similarity index 100% rename from samples/talloc/tdll1x.cpp rename to Source/Detours/samples/talloc/tdll1x.cpp diff --git a/samples/talloc/tdll2x.cpp b/Source/Detours/samples/talloc/tdll2x.cpp similarity index 100% rename from samples/talloc/tdll2x.cpp rename to Source/Detours/samples/talloc/tdll2x.cpp diff --git a/samples/talloc/tdll3x.cpp b/Source/Detours/samples/talloc/tdll3x.cpp similarity index 100% rename from samples/talloc/tdll3x.cpp rename to Source/Detours/samples/talloc/tdll3x.cpp diff --git a/samples/talloc/tdll4x.cpp b/Source/Detours/samples/talloc/tdll4x.cpp similarity index 100% rename from samples/talloc/tdll4x.cpp rename to Source/Detours/samples/talloc/tdll4x.cpp diff --git a/samples/talloc/tdll5x.cpp b/Source/Detours/samples/talloc/tdll5x.cpp similarity index 100% rename from samples/talloc/tdll5x.cpp rename to Source/Detours/samples/talloc/tdll5x.cpp diff --git a/samples/talloc/tdll6x.cpp b/Source/Detours/samples/talloc/tdll6x.cpp similarity index 100% rename from samples/talloc/tdll6x.cpp rename to Source/Detours/samples/talloc/tdll6x.cpp diff --git a/samples/talloc/tdll7x.cpp b/Source/Detours/samples/talloc/tdll7x.cpp similarity index 100% rename from samples/talloc/tdll7x.cpp rename to Source/Detours/samples/talloc/tdll7x.cpp diff --git a/samples/talloc/tdll8x.cpp b/Source/Detours/samples/talloc/tdll8x.cpp similarity index 100% rename from samples/talloc/tdll8x.cpp rename to Source/Detours/samples/talloc/tdll8x.cpp diff --git a/samples/talloc/tdll9x.cpp b/Source/Detours/samples/talloc/tdll9x.cpp similarity index 100% rename from samples/talloc/tdll9x.cpp rename to Source/Detours/samples/talloc/tdll9x.cpp diff --git a/samples/traceapi/Makefile b/Source/Detours/samples/traceapi/Makefile similarity index 100% rename from samples/traceapi/Makefile rename to Source/Detours/samples/traceapi/Makefile diff --git a/samples/traceapi/_win32.cpp b/Source/Detours/samples/traceapi/_win32.cpp similarity index 100% rename from samples/traceapi/_win32.cpp rename to Source/Detours/samples/traceapi/_win32.cpp diff --git a/samples/traceapi/testapi.cpp b/Source/Detours/samples/traceapi/testapi.cpp similarity index 100% rename from samples/traceapi/testapi.cpp rename to Source/Detours/samples/traceapi/testapi.cpp diff --git a/samples/traceapi/trcapi.cpp b/Source/Detours/samples/traceapi/trcapi.cpp similarity index 100% rename from samples/traceapi/trcapi.cpp rename to Source/Detours/samples/traceapi/trcapi.cpp diff --git a/samples/traceapi/trcapi.rc b/Source/Detours/samples/traceapi/trcapi.rc similarity index 100% rename from samples/traceapi/trcapi.rc rename to Source/Detours/samples/traceapi/trcapi.rc diff --git a/samples/tracebld/Makefile b/Source/Detours/samples/tracebld/Makefile similarity index 100% rename from samples/tracebld/Makefile rename to Source/Detours/samples/tracebld/Makefile diff --git a/samples/tracebld/tracebld.cpp b/Source/Detours/samples/tracebld/tracebld.cpp similarity index 100% rename from samples/tracebld/tracebld.cpp rename to Source/Detours/samples/tracebld/tracebld.cpp diff --git a/samples/tracebld/tracebld.h b/Source/Detours/samples/tracebld/tracebld.h similarity index 100% rename from samples/tracebld/tracebld.h rename to Source/Detours/samples/tracebld/tracebld.h diff --git a/samples/tracebld/trcbld.cpp b/Source/Detours/samples/tracebld/trcbld.cpp similarity index 100% rename from samples/tracebld/trcbld.cpp rename to Source/Detours/samples/tracebld/trcbld.cpp diff --git a/samples/tracebld/trcbld.rc b/Source/Detours/samples/tracebld/trcbld.rc similarity index 100% rename from samples/tracebld/trcbld.rc rename to Source/Detours/samples/tracebld/trcbld.rc diff --git a/samples/tracelnk/Makefile b/Source/Detours/samples/tracelnk/Makefile similarity index 100% rename from samples/tracelnk/Makefile rename to Source/Detours/samples/tracelnk/Makefile diff --git a/samples/tracelnk/trclnk.cpp b/Source/Detours/samples/tracelnk/trclnk.cpp similarity index 100% rename from samples/tracelnk/trclnk.cpp rename to Source/Detours/samples/tracelnk/trclnk.cpp diff --git a/samples/tracelnk/trclnk.rc b/Source/Detours/samples/tracelnk/trclnk.rc similarity index 100% rename from samples/tracelnk/trclnk.rc rename to Source/Detours/samples/tracelnk/trclnk.rc diff --git a/samples/tracemem/Makefile b/Source/Detours/samples/tracemem/Makefile similarity index 100% rename from samples/tracemem/Makefile rename to Source/Detours/samples/tracemem/Makefile diff --git a/samples/tracemem/trcmem.cpp b/Source/Detours/samples/tracemem/trcmem.cpp similarity index 100% rename from samples/tracemem/trcmem.cpp rename to Source/Detours/samples/tracemem/trcmem.cpp diff --git a/samples/tracemem/trcmem.rc b/Source/Detours/samples/tracemem/trcmem.rc similarity index 100% rename from samples/tracemem/trcmem.rc rename to Source/Detours/samples/tracemem/trcmem.rc diff --git a/samples/tracereg/Makefile b/Source/Detours/samples/tracereg/Makefile similarity index 100% rename from samples/tracereg/Makefile rename to Source/Detours/samples/tracereg/Makefile diff --git a/samples/tracereg/trcreg.cpp b/Source/Detours/samples/tracereg/trcreg.cpp similarity index 100% rename from samples/tracereg/trcreg.cpp rename to Source/Detours/samples/tracereg/trcreg.cpp diff --git a/samples/tracereg/trcreg.rc b/Source/Detours/samples/tracereg/trcreg.rc similarity index 100% rename from samples/tracereg/trcreg.rc rename to Source/Detours/samples/tracereg/trcreg.rc diff --git a/samples/traceser/Makefile b/Source/Detours/samples/traceser/Makefile similarity index 100% rename from samples/traceser/Makefile rename to Source/Detours/samples/traceser/Makefile diff --git a/samples/traceser/trcser.cpp b/Source/Detours/samples/traceser/trcser.cpp similarity index 100% rename from samples/traceser/trcser.cpp rename to Source/Detours/samples/traceser/trcser.cpp diff --git a/samples/traceser/trcser.rc b/Source/Detours/samples/traceser/trcser.rc similarity index 100% rename from samples/traceser/trcser.rc rename to Source/Detours/samples/traceser/trcser.rc diff --git a/samples/tracessl/Makefile b/Source/Detours/samples/tracessl/Makefile similarity index 100% rename from samples/tracessl/Makefile rename to Source/Detours/samples/tracessl/Makefile diff --git a/samples/tracessl/trcssl.cpp b/Source/Detours/samples/tracessl/trcssl.cpp similarity index 100% rename from samples/tracessl/trcssl.cpp rename to Source/Detours/samples/tracessl/trcssl.cpp diff --git a/samples/tracessl/trcssl.rc b/Source/Detours/samples/tracessl/trcssl.rc similarity index 100% rename from samples/tracessl/trcssl.rc rename to Source/Detours/samples/tracessl/trcssl.rc diff --git a/samples/tracetcp/Makefile b/Source/Detours/samples/tracetcp/Makefile similarity index 100% rename from samples/tracetcp/Makefile rename to Source/Detours/samples/tracetcp/Makefile diff --git a/samples/tracetcp/trctcp.cpp b/Source/Detours/samples/tracetcp/trctcp.cpp similarity index 100% rename from samples/tracetcp/trctcp.cpp rename to Source/Detours/samples/tracetcp/trctcp.cpp diff --git a/samples/tracetcp/trctcp.rc b/Source/Detours/samples/tracetcp/trctcp.rc similarity index 100% rename from samples/tracetcp/trctcp.rc rename to Source/Detours/samples/tracetcp/trctcp.rc diff --git a/samples/tryman/Makefile b/Source/Detours/samples/tryman/Makefile similarity index 100% rename from samples/tryman/Makefile rename to Source/Detours/samples/tryman/Makefile diff --git a/samples/tryman/managed.cs b/Source/Detours/samples/tryman/managed.cs similarity index 100% rename from samples/tryman/managed.cs rename to Source/Detours/samples/tryman/managed.cs diff --git a/samples/tryman/size.cpp b/Source/Detours/samples/tryman/size.cpp similarity index 100% rename from samples/tryman/size.cpp rename to Source/Detours/samples/tryman/size.cpp diff --git a/samples/tryman/tryman.cpp b/Source/Detours/samples/tryman/tryman.cpp similarity index 100% rename from samples/tryman/tryman.cpp rename to Source/Detours/samples/tryman/tryman.cpp diff --git a/samples/tryman/tstman.cpp b/Source/Detours/samples/tryman/tstman.cpp similarity index 100% rename from samples/tryman/tstman.cpp rename to Source/Detours/samples/tryman/tstman.cpp diff --git a/samples/tryman/tstman.rc b/Source/Detours/samples/tryman/tstman.rc similarity index 100% rename from samples/tryman/tstman.rc rename to Source/Detours/samples/tryman/tstman.rc diff --git a/samples/withdll/Makefile b/Source/Detours/samples/withdll/Makefile similarity index 100% rename from samples/withdll/Makefile rename to Source/Detours/samples/withdll/Makefile diff --git a/samples/withdll/withdll.cpp b/Source/Detours/samples/withdll/withdll.cpp similarity index 100% rename from samples/withdll/withdll.cpp rename to Source/Detours/samples/withdll/withdll.cpp diff --git a/src/Makefile b/Source/Detours/src/Makefile similarity index 100% rename from src/Makefile rename to Source/Detours/src/Makefile diff --git a/src/creatwth.cpp b/Source/Detours/src/creatwth.cpp similarity index 100% rename from src/creatwth.cpp rename to Source/Detours/src/creatwth.cpp diff --git a/src/detours.cpp b/Source/Detours/src/detours.cpp similarity index 100% rename from src/detours.cpp rename to Source/Detours/src/detours.cpp diff --git a/src/detours.h b/Source/Detours/src/detours.h similarity index 100% rename from src/detours.h rename to Source/Detours/src/detours.h diff --git a/src/detver.h b/Source/Detours/src/detver.h similarity index 100% rename from src/detver.h rename to Source/Detours/src/detver.h diff --git a/src/disasm.cpp b/Source/Detours/src/disasm.cpp similarity index 100% rename from src/disasm.cpp rename to Source/Detours/src/disasm.cpp diff --git a/src/disolarm.cpp b/Source/Detours/src/disolarm.cpp similarity index 100% rename from src/disolarm.cpp rename to Source/Detours/src/disolarm.cpp diff --git a/src/disolarm64.cpp b/Source/Detours/src/disolarm64.cpp similarity index 100% rename from src/disolarm64.cpp rename to Source/Detours/src/disolarm64.cpp diff --git a/src/disolia64.cpp b/Source/Detours/src/disolia64.cpp similarity index 100% rename from src/disolia64.cpp rename to Source/Detours/src/disolia64.cpp diff --git a/src/disolx64.cpp b/Source/Detours/src/disolx64.cpp similarity index 100% rename from src/disolx64.cpp rename to Source/Detours/src/disolx64.cpp diff --git a/src/disolx86.cpp b/Source/Detours/src/disolx86.cpp similarity index 100% rename from src/disolx86.cpp rename to Source/Detours/src/disolx86.cpp diff --git a/src/image.cpp b/Source/Detours/src/image.cpp similarity index 100% rename from src/image.cpp rename to Source/Detours/src/image.cpp diff --git a/src/modules.cpp b/Source/Detours/src/modules.cpp similarity index 100% rename from src/modules.cpp rename to Source/Detours/src/modules.cpp diff --git a/src/uimports.cpp b/Source/Detours/src/uimports.cpp similarity index 100% rename from src/uimports.cpp rename to Source/Detours/src/uimports.cpp diff --git a/system.mak b/Source/Detours/system.mak similarity index 100% rename from system.mak rename to Source/Detours/system.mak diff --git a/tests/Makefile b/Source/Detours/tests/Makefile similarity index 100% rename from tests/Makefile rename to Source/Detours/tests/Makefile diff --git a/tests/catch.hpp b/Source/Detours/tests/catch.hpp similarity index 100% rename from tests/catch.hpp rename to Source/Detours/tests/catch.hpp diff --git a/tests/corruptor.cpp b/Source/Detours/tests/corruptor.cpp similarity index 100% rename from tests/corruptor.cpp rename to Source/Detours/tests/corruptor.cpp diff --git a/tests/corruptor.h b/Source/Detours/tests/corruptor.h similarity index 100% rename from tests/corruptor.h rename to Source/Detours/tests/corruptor.h diff --git a/tests/main.cpp b/Source/Detours/tests/main.cpp similarity index 100% rename from tests/main.cpp rename to Source/Detours/tests/main.cpp diff --git a/tests/payload.cpp b/Source/Detours/tests/payload.cpp similarity index 100% rename from tests/payload.cpp rename to Source/Detours/tests/payload.cpp diff --git a/tests/payload.h b/Source/Detours/tests/payload.h similarity index 100% rename from tests/payload.h rename to Source/Detours/tests/payload.h diff --git a/tests/process_helpers.cpp b/Source/Detours/tests/process_helpers.cpp similarity index 100% rename from tests/process_helpers.cpp rename to Source/Detours/tests/process_helpers.cpp diff --git a/tests/process_helpers.h b/Source/Detours/tests/process_helpers.h similarity index 100% rename from tests/process_helpers.h rename to Source/Detours/tests/process_helpers.h diff --git a/tests/test_image_api.cpp b/Source/Detours/tests/test_image_api.cpp similarity index 100% rename from tests/test_image_api.cpp rename to Source/Detours/tests/test_image_api.cpp diff --git a/tests/test_module_api.cpp b/Source/Detours/tests/test_module_api.cpp similarity index 100% rename from tests/test_module_api.cpp rename to Source/Detours/tests/test_module_api.cpp diff --git a/vc/Detours.sln b/Source/Detours/vc/Detours.sln similarity index 100% rename from vc/Detours.sln rename to Source/Detours/vc/Detours.sln diff --git a/vc/Detours.vcxproj b/Source/Detours/vc/Detours.vcxproj similarity index 100% rename from vc/Detours.vcxproj rename to Source/Detours/vc/Detours.vcxproj diff --git a/vc/Detours.vcxproj.filters b/Source/Detours/vc/Detours.vcxproj.filters similarity index 100% rename from vc/Detours.vcxproj.filters rename to Source/Detours/vc/Detours.vcxproj.filters diff --git a/Source/Directory.Build.AfterCppDefault.props b/Source/Directory.Build.AfterCppDefault.props new file mode 100644 index 00000000..d56c04b9 --- /dev/null +++ b/Source/Directory.Build.AfterCppDefault.props @@ -0,0 +1,10 @@ + + + $(DefaultPlatformToolset) + $(SolutionDir)OutDir\$(PlatformTarget)\$(Configuration)\ + IntDir\$(PlatformTarget)\$(Configuration)\ + + + Unicode + + \ No newline at end of file diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props new file mode 100644 index 00000000..4f030c7d --- /dev/null +++ b/Source/Directory.Build.props @@ -0,0 +1,61 @@ + + + + $(MsbuildThisFileDirectory)\Directory.Build.AfterCppDefault.props + + + + + Level3 + stdcpplatest + stdc17 + pch.h + + + + + + + MSB_CONFIGURATIONTYPE_EXE;%(PreprocessorDefinitions) + + + + + MSB_CONFIGURATIONTYPE_DLL;%(PreprocessorDefinitions) + + + + + MSB_CONFIGURATIONTYPE_LIB;%(PreprocessorDefinitions) + + + + + MSB_CONFIGURATIONTYPE_UTILITY;%(PreprocessorDefinitions) + + + + + + + MultiThreaded + + + + + MultiThreadedDebug + + + + + + $(MSBuildProjectDirectory)\$(ProjectName).Build.props + + + + + + $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../')) + + + \ No newline at end of file diff --git a/Source/KNSoft.SlimDetours.nuspec b/Source/KNSoft.SlimDetours.nuspec new file mode 100644 index 00000000..81390482 --- /dev/null +++ b/Source/KNSoft.SlimDetours.nuspec @@ -0,0 +1,27 @@ + + + + KNSoft.SlimDetours + 1.0.0-beta + KNSoft.SlimDetours + KNSoft + https://github.com/KNSoft/KNSoft.SlimDetours + true + MPL-2.0 + https://licenses.nuget.org/MPL-2.0 + README.md + KNSoft.SlimDetours is an improved Windows API hooking library base on Microsoft Detours. + native Windows Detours Hook Utility Runtime + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/KNSoft.SlimDetours.sln b/Source/KNSoft.SlimDetours.sln new file mode 100644 index 00000000..288b57e4 --- /dev/null +++ b/Source/KNSoft.SlimDetours.sln @@ -0,0 +1,54 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.35013.160 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KNSoft.SlimDetours", "KNSoft.SlimDetours.vcxproj", "{C2706F4B-8A0D-48BA-966F-239380A3C893}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Demo", "Demo\Demo.vcxproj", "{1C85580B-F884-4347-A67E-5083AB36AE74}" + ProjectSection(ProjectDependencies) = postProject + {C2706F4B-8A0D-48BA-966F-239380A3C893} = {C2706F4B-8A0D-48BA-966F-239380A3C893} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|ARM64.Build.0 = Debug|ARM64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|x64.ActiveCfg = Debug|x64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|x64.Build.0 = Debug|x64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|x86.ActiveCfg = Debug|Win32 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Debug|x86.Build.0 = Debug|Win32 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|ARM64.ActiveCfg = Release|ARM64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|ARM64.Build.0 = Release|ARM64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|x64.ActiveCfg = Release|x64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|x64.Build.0 = Release|x64 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|x86.ActiveCfg = Release|Win32 + {C2706F4B-8A0D-48BA-966F-239380A3C893}.Release|x86.Build.0 = Release|Win32 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|ARM64.Build.0 = Debug|ARM64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|x64.ActiveCfg = Debug|x64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|x64.Build.0 = Debug|x64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|x86.ActiveCfg = Debug|Win32 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Debug|x86.Build.0 = Debug|Win32 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|ARM64.ActiveCfg = Release|ARM64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|ARM64.Build.0 = Release|ARM64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|x64.ActiveCfg = Release|x64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|x64.Build.0 = Release|x64 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|x86.ActiveCfg = Release|Win32 + {1C85580B-F884-4347-A67E-5083AB36AE74}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {55F388CA-7C34-4532-ADD6-37F01F449D1B} + EndGlobalSection +EndGlobal diff --git a/Source/KNSoft.SlimDetours.targets b/Source/KNSoft.SlimDetours.targets new file mode 100644 index 00000000..f41eaa01 --- /dev/null +++ b/Source/KNSoft.SlimDetours.targets @@ -0,0 +1,15 @@ + + + + $(MSBuildThisFileDirectory)Include;$(IncludePath) + $(MSBuildThisFileDirectory)Lib\$(PlatformTarget);$(LibraryPath) + + + + + + + ntdll.lib;$(MSBuildThisFileDirectory)Lib\$(PlatformTarget)\$(Configuration)\KNSoft.SlimDetours.lib;%(AdditionalDependencies) + + + diff --git a/Source/KNSoft.SlimDetours.vcxproj b/Source/KNSoft.SlimDetours.vcxproj new file mode 100644 index 00000000..15913fc4 --- /dev/null +++ b/Source/KNSoft.SlimDetours.vcxproj @@ -0,0 +1,229 @@ + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {c2706f4b-8a0d-48ba-966f-239380a3c893} + KNSoftSlimDetours + 10.0 + + + + StaticLibrary + true + + + StaticLibrary + false + true + + + StaticLibrary + true + + + StaticLibrary + true + + + StaticLibrary + false + true + + + StaticLibrary + false + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + true + + + + + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + true + + + + + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + true + + + + + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + true + + + + + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + true + + + + + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + true + + + + + + + + + + + + + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/Source/KNSoft.SlimDetours.vcxproj.filters b/Source/KNSoft.SlimDetours.vcxproj.filters new file mode 100644 index 00000000..2614da1b --- /dev/null +++ b/Source/KNSoft.SlimDetours.vcxproj.filters @@ -0,0 +1,81 @@ + + + + + {2e8a1f68-a9e3-4334-a512-2dfa57d64b99} + + + {9f069d6e-62e2-4d06-ab78-072ae55986f2} + + + + + SlimDetours + + + SlimDetours + + + SlimDetours + + + SlimDetours + + + SlimDetours + + + SlimDetours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + Detours + + + + + SlimDetours + + + Detours + + + Detours + + + + + SlimDetours + + + Detours + + + + \ No newline at end of file diff --git a/Source/SlimDetours/Disassembler.c b/Source/SlimDetours/Disassembler.c new file mode 100644 index 00000000..9d4c3559 --- /dev/null +++ b/Source/SlimDetours/Disassembler.c @@ -0,0 +1,2711 @@ +/* + * KNSoft SlimDetours (https://github.com/KNSoft/SlimDetours) Disassembler + * Copyright (c) KNSoft.org (https://github.com/KNSoft). All rights reserved. + * Licensed under the MPL-2.0 license. + * + * Source base on Microsoft Detours: + * + * Microsoft Research Detours Package, Version 4.0.1 + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT license. + */ + +#include "SlimDetours.inl" + +#undef ASSERT +#define ASSERT(x) + +////////////////////////////////////////////////////////////////////////////// +// +// Function: +// SlimDetoursCopyInstruction(PVOID pDst, +// PVOID *ppDstPool +// PVOID pSrc, +// PVOID *ppTarget, +// LONG *plExtra) +// Purpose: +// Copy a single instruction from pSrc to pDst. +// +// Arguments: +// pDst: +// Destination address for the instruction. May be NULL in which +// case SlimDetoursCopyInstruction is used to measure an instruction. +// If not NULL then the source instruction is copied to the +// destination instruction and any relative arguments are adjusted. +// ppDstPool: +// Destination address for the end of the constant pool. The +// constant pool works backwards toward pDst. All memory between +// pDst and *ppDstPool must be available for use by this function. +// ppDstPool may be NULL if pDst is NULL. +// pSrc: +// Source address of the instruction. +// ppTarget: +// Out parameter for any target instruction address pointed to by +// the instruction. For example, a branch or a jump insruction has +// a target, but a load or store instruction doesn't. A target is +// another instruction that may be executed as a result of this +// instruction. ppTarget may be NULL. +// plExtra: +// Out parameter for the number of extra bytes needed by the +// instruction to reach the target. For example, lExtra = 3 if the +// instruction had an 8-bit relative offset, but needs a 32-bit +// relative offset. +// +// Returns: +// Returns the address of the next instruction (following in the source) +// instruction. By subtracting pSrc from the return value, the caller +// can determinte the size of the instruction copied. +// +// Comments: +// By following the pTarget, the caller can follow alternate +// instruction streams. However, it is not always possible to determine +// the target based on static analysis. For example, the destination of +// a jump relative to a register cannot be determined from just the +// instruction stream. The output value, pTarget, can have any of the +// following outputs: +// DETOUR_INSTRUCTION_TARGET_NONE: +// The instruction has no targets. +// DETOUR_INSTRUCTION_TARGET_DYNAMIC: +// The instruction has a non-deterministic (dynamic) target. +// (i.e. the jump is to an address held in a register.) +// Address: The instruction has the specified target. +// +// When copying instructions, SlimDetoursCopyInstruction insures that any +// targets remain constant. It does so by adjusting any IP relative +// offsets. +// + +//////////////////////////////////////////////////// X86 and X64 Disassembler. +// +// Includes full support for all x86 chips prior to the Pentium III, and some newer stuff. +// +#if defined(_M_X64) || defined(_M_IX86) + +typedef struct _DETOUR_DISASM +{ + BOOL bOperandOverride; + BOOL bAddressOverride; + BOOL bRaxOverride; // AMD64 only + BOOL bVex; + BOOL bEvex; + BOOL bF2; + BOOL bF3; // x86 only + BYTE nSegmentOverride; + + PBYTE* ppbTarget; + LONG* plExtra; + + LONG lScratchExtra; + PBYTE pbScratchTarget; + BYTE rbScratchDst[64]; // matches or exceeds rbCode +} DETOUR_DISASM, *PDETOUR_DISASM; + +static +VOID +detour_disasm_init( + _Out_ PDETOUR_DISASM pDisasm, + _Out_opt_ PBYTE* ppbTarget, + _Out_opt_ LONG* plExtra) +{ + pDisasm->bOperandOverride = FALSE; + pDisasm->bAddressOverride = FALSE; + pDisasm->bRaxOverride = FALSE; + pDisasm->bF2 = FALSE; + pDisasm->bF3 = FALSE; + pDisasm->bVex = FALSE; + pDisasm->bEvex = FALSE; + + pDisasm->ppbTarget = ppbTarget ? ppbTarget : &pDisasm->pbScratchTarget; + pDisasm->plExtra = plExtra ? plExtra : &pDisasm->lScratchExtra; + + *pDisasm->ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE; + *pDisasm->plExtra = 0; +} + +typedef const struct _COPYENTRY *REFCOPYENTRY; + +typedef +PBYTE(*COPYFUNC)( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc); + +// nFlagBits flags. +enum +{ + DYNAMIC = 0x1u, + ADDRESS = 0x2u, + NOENLARGE = 0x4u, + RAX = 0x8u, +}; + +// ModR/M Flags +enum +{ + SIB = 0x10u, + RIP = 0x20u, + NOTSIB = 0x0fu, +}; + +typedef struct _COPYENTRY +{ + // Many of these fields are often ignored. See ENTRY_DataIgnored. + ULONG nFixedSize : 4; // Fixed size of opcode + ULONG nFixedSize16 : 4; // Fixed size when 16 bit operand + ULONG nModOffset : 4; // Offset to mod/rm byte (0=none) + ULONG nRelOffset : 4; // Offset to relative target. + ULONG nFlagBits : 4; // Flags for DYNAMIC, etc. + COPYFUNC pfCopy; // Function pointer. +} COPYENTRY, *PCOPYENTRY; + +PBYTE CopyBytes(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyBytesPrefix(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyBytesSegment(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyBytesRax(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyBytesJump(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Invalid(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy0F(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy0F78(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy0F00(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy0FB8(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy66(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE Copy67(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyF2(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyF3(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyF6(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyF7(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyFF(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyVex3(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyVex2(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyEvex(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); +PBYTE CopyXop(_In_ PDETOUR_DISASM pDisasm, _In_opt_ REFCOPYENTRY pEntry, _In_ PBYTE pbDst, _In_ PBYTE pbSrc); + +///////////////////////////////////////////////////////// Disassembler Tables. +// + +static const BYTE g_rbModRm[] = +{ + 0,0,0,0, SIB | 1,RIP | 4,0,0, 0,0,0,0, SIB | 1,RIP | 4,0,0, // 0x + 0,0,0,0, SIB | 1,RIP | 4,0,0, 0,0,0,0, SIB | 1,RIP | 4,0,0, // 1x + 0,0,0,0, SIB | 1,RIP | 4,0,0, 0,0,0,0, SIB | 1,RIP | 4,0,0, // 2x + 0,0,0,0, SIB | 1,RIP | 4,0,0, 0,0,0,0, SIB | 1,RIP | 4,0,0, // 3x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 4x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 5x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 6x + 1,1,1,1, 2,1,1,1, 1,1,1,1, 2,1,1,1, // 7x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 8x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // 9x + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Ax + 4,4,4,4, 5,4,4,4, 4,4,4,4, 5,4,4,4, // Bx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Cx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Dx + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // Ex + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 // Fx +}; + +enum +{ + eENTRY_CopyBytes1 = 0, + eENTRY_CopyBytes1Address, + eENTRY_CopyBytes1Dynamic, + eENTRY_CopyBytes2, + eENTRY_CopyBytes2Jump, + eENTRY_CopyBytes2CantJump, + eENTRY_CopyBytes2Dynamic, + eENTRY_CopyBytes3, + eENTRY_CopyBytes3Dynamic, + eENTRY_CopyBytes3Or5, + eENTRY_CopyBytes3Or5Dynamic, + eENTRY_CopyBytes3Or5Rax, + eENTRY_CopyBytes3Or5Target, + eENTRY_CopyBytes4, + eENTRY_CopyBytes5, + eENTRY_CopyBytes5Or7Dynamic, + eENTRY_CopyBytes7, + eENTRY_CopyBytes2Mod, + eENTRY_CopyBytes2ModDynamic, + eENTRY_CopyBytes2Mod1, + eENTRY_CopyBytes2ModOperand, + eENTRY_CopyBytes3Mod, + eENTRY_CopyBytes3Mod1, + eENTRY_CopyBytesPrefix, + eENTRY_CopyBytesSegment, + eENTRY_CopyBytesRax, + eENTRY_CopyF2, + eENTRY_CopyF3, + eENTRY_Copy0F, + eENTRY_Copy0F78, + eENTRY_Copy0F00, + eENTRY_Copy0FB8, + eENTRY_Copy66, + eENTRY_Copy67, + eENTRY_CopyF6, + eENTRY_CopyF7, + eENTRY_CopyFF, + eENTRY_CopyVex2, + eENTRY_CopyVex3, + eENTRY_CopyEvex, + eENTRY_CopyXop, + eENTRY_CopyBytesXop, + eENTRY_CopyBytesXop1, + eENTRY_CopyBytesXop4, + eENTRY_Invalid +}; + +// These macros define common uses of nFixedSize, nFixedSize16, nModOffset, nRelOffset, nFlagBits, pfCopy. +#define ENTRY_DataIgnored 0, 0, 0, 0, 0, + +static const COPYENTRY g_rceCopyMap[] = +{ + /* eENTRY_CopyBytes1 */ { 1, 1, 0, 0, 0, CopyBytes }, +#if defined(_M_X64) + /* eENTRY_CopyBytes1Address */ { 9, 5, 0, 0, ADDRESS, CopyBytes }, +#else + /* eENTRY_CopyBytes1Address */ { 5, 3, 0, 0, ADDRESS, CopyBytes }, +#endif + /* eENTRY_CopyBytes1Dynamic */ { 1, 1, 0, 0, DYNAMIC, CopyBytes }, + /* eENTRY_CopyBytes2 */ { 2, 2, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes2Jump */ { ENTRY_DataIgnored CopyBytesJump }, + /* eENTRY_CopyBytes2CantJump */ { 2, 2, 0, 1, NOENLARGE, CopyBytes }, + /* eENTRY_CopyBytes2Dynamic */ { 2, 2, 0, 0, DYNAMIC, CopyBytes }, + /* eENTRY_CopyBytes3 */ { 3, 3, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes3Dynamic */ { 3, 3, 0, 0, DYNAMIC, CopyBytes }, + /* eENTRY_CopyBytes3Or5 */ { 5, 3, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes3Or5Dynamic */ { 5, 3, 0, 0, DYNAMIC, CopyBytes }, // x86 only +#if defined(_M_X64) + /* eENTRY_CopyBytes3Or5Rax */ { 5, 3, 0, 0, RAX, CopyBytes }, + /* eENTRY_CopyBytes3Or5Target */ { 5, 5, 0, 1, 0, CopyBytes }, +#else + /* eENTRY_CopyBytes3Or5Rax */ { 5, 3, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes3Or5Target */ { 5, 3, 0, 1, 0, CopyBytes }, +#endif + /* eENTRY_CopyBytes4 */ { 4, 4, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes5 */ { 5, 5, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes5Or7Dynamic */ { 7, 5, 0, 0, DYNAMIC, CopyBytes }, + /* eENTRY_CopyBytes7 */ { 7, 7, 0, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes2Mod */ { 2, 2, 1, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes2ModDynamic */ { 2, 2, 1, 0, DYNAMIC, CopyBytes }, + /* eENTRY_CopyBytes2Mod1 */ { 3, 3, 1, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes2ModOperand */ { 6, 4, 1, 0, 0, CopyBytes }, + /* eENTRY_CopyBytes3Mod */ { 3, 3, 2, 0, 0, CopyBytes }, // SSE3 0F 38 opcode modrm + /* eENTRY_CopyBytes3Mod1 */ { 4, 4, 2, 0, 0, CopyBytes }, // SSE3 0F 3A opcode modrm .. imm8 + /* eENTRY_CopyBytesPrefix */ { ENTRY_DataIgnored CopyBytesPrefix }, + /* eENTRY_CopyBytesSegment */ { ENTRY_DataIgnored CopyBytesSegment }, + /* eENTRY_CopyBytesRax */ { ENTRY_DataIgnored CopyBytesRax }, + /* eENTRY_CopyF2 */ { ENTRY_DataIgnored CopyF2 }, + /* eENTRY_CopyF3 */ { ENTRY_DataIgnored CopyF3 }, // 32bit x86 only + /* eENTRY_Copy0F */ { ENTRY_DataIgnored Copy0F }, + /* eENTRY_Copy0F78 */ { ENTRY_DataIgnored Copy0F78 }, + /* eENTRY_Copy0F00 */ { ENTRY_DataIgnored Copy0F00 }, // 32bit x86 only + /* eENTRY_Copy0FB8 */ { ENTRY_DataIgnored Copy0FB8 }, // 32bit x86 only + /* eENTRY_Copy66 */ { ENTRY_DataIgnored Copy66 }, + /* eENTRY_Copy67 */ { ENTRY_DataIgnored Copy67 }, + /* eENTRY_CopyF6 */ { ENTRY_DataIgnored CopyF6 }, + /* eENTRY_CopyF7 */ { ENTRY_DataIgnored CopyF7 }, + /* eENTRY_CopyFF */ { ENTRY_DataIgnored CopyFF }, + /* eENTRY_CopyVex2 */ { ENTRY_DataIgnored CopyVex2 }, + /* eENTRY_CopyVex3 */ { ENTRY_DataIgnored CopyVex3 }, + /* eENTRY_CopyEvex */ { ENTRY_DataIgnored CopyEvex }, // 62, 3 byte payload, then normal with implied prefixes like vex + /* eENTRY_CopyXop */ { ENTRY_DataIgnored CopyXop }, // 0x8F ... POP /0 or AMD XOP + /* eENTRY_CopyBytesXop */ { 5, 5, 4, 0, 0, CopyBytes }, // 0x8F xop1 xop2 opcode modrm + /* eENTRY_CopyBytesXop1 */ { 6, 6, 4, 0, 0, CopyBytes }, // 0x8F xop1 xop2 opcode modrm ... imm8 + /* eENTRY_CopyBytesXop4 */ { 9, 9, 4, 0, 0, CopyBytes }, // 0x8F xop1 xop2 opcode modrm ... imm32 + /* eENTRY_Invalid */ { ENTRY_DataIgnored Invalid } +}; + +static const BYTE g_rceCopyTable[] = +{ + /* 00 */ eENTRY_CopyBytes2Mod, // ADD /r + /* 01 */ eENTRY_CopyBytes2Mod, // ADD /r + /* 02 */ eENTRY_CopyBytes2Mod, // ADD /r + /* 03 */ eENTRY_CopyBytes2Mod, // ADD /r + /* 04 */ eENTRY_CopyBytes2, // ADD ib + /* 05 */ eENTRY_CopyBytes3Or5, // ADD iw +#if defined(_M_X64) + /* 06 */ eENTRY_Invalid, // Invalid + /* 07 */ eENTRY_Invalid, // Invalid +#else + /* 06 */ eENTRY_CopyBytes1, // PUSH + /* 07 */ eENTRY_CopyBytes1, // POP +#endif + /* 08 */ eENTRY_CopyBytes2Mod, // OR /r + /* 09 */ eENTRY_CopyBytes2Mod, // OR /r + /* 0A */ eENTRY_CopyBytes2Mod, // OR /r + /* 0B */ eENTRY_CopyBytes2Mod, // OR /r + /* 0C */ eENTRY_CopyBytes2, // OR ib + /* 0D */ eENTRY_CopyBytes3Or5, // OR iw +#if defined(_M_X64) + /* 0E */ eENTRY_Invalid, // Invalid +#else + /* 0E */ eENTRY_CopyBytes1, // PUSH +#endif + /* 0F */ eENTRY_Copy0F, // Extension Ops + /* 10 */ eENTRY_CopyBytes2Mod, // ADC /r + /* 11 */ eENTRY_CopyBytes2Mod, // ADC /r + /* 12 */ eENTRY_CopyBytes2Mod, // ADC /r + /* 13 */ eENTRY_CopyBytes2Mod, // ADC /r + /* 14 */ eENTRY_CopyBytes2, // ADC ib + /* 15 */ eENTRY_CopyBytes3Or5, // ADC id +#if defined(_M_X64) + /* 16 */ eENTRY_Invalid, // Invalid + /* 17 */ eENTRY_Invalid, // Invalid +#else + /* 16 */ eENTRY_CopyBytes1, // PUSH + /* 17 */ eENTRY_CopyBytes1, // POP +#endif + /* 18 */ eENTRY_CopyBytes2Mod, // SBB /r + /* 19 */ eENTRY_CopyBytes2Mod, // SBB /r + /* 1A */ eENTRY_CopyBytes2Mod, // SBB /r + /* 1B */ eENTRY_CopyBytes2Mod, // SBB /r + /* 1C */ eENTRY_CopyBytes2, // SBB ib + /* 1D */ eENTRY_CopyBytes3Or5, // SBB id +#if defined(_M_X64) + /* 1E */ eENTRY_Invalid, // Invalid + /* 1F */ eENTRY_Invalid, // Invalid +#else + /* 1E */ eENTRY_CopyBytes1, // PUSH + /* 1F */ eENTRY_CopyBytes1, // POP +#endif + /* 20 */ eENTRY_CopyBytes2Mod, // AND /r + /* 21 */ eENTRY_CopyBytes2Mod, // AND /r + /* 22 */ eENTRY_CopyBytes2Mod, // AND /r + /* 23 */ eENTRY_CopyBytes2Mod, // AND /r + /* 24 */ eENTRY_CopyBytes2, // AND ib + /* 25 */ eENTRY_CopyBytes3Or5, // AND id + /* 26 */ eENTRY_CopyBytesSegment, // ES prefix +#if defined(_M_X64) + /* 27 */ eENTRY_Invalid, // Invalid +#else + /* 27 */ eENTRY_CopyBytes1, // DAA +#endif + /* 28 */ eENTRY_CopyBytes2Mod, // SUB /r + /* 29 */ eENTRY_CopyBytes2Mod, // SUB /r + /* 2A */ eENTRY_CopyBytes2Mod, // SUB /r + /* 2B */ eENTRY_CopyBytes2Mod, // SUB /r + /* 2C */ eENTRY_CopyBytes2, // SUB ib + /* 2D */ eENTRY_CopyBytes3Or5, // SUB id + /* 2E */ eENTRY_CopyBytesSegment, // CS prefix +#if defined(_M_X64) + /* 2F */ eENTRY_Invalid, // Invalid +#else + /* 2F */ eENTRY_CopyBytes1, // DAS +#endif + /* 30 */ eENTRY_CopyBytes2Mod, // XOR /r + /* 31 */ eENTRY_CopyBytes2Mod, // XOR /r + /* 32 */ eENTRY_CopyBytes2Mod, // XOR /r + /* 33 */ eENTRY_CopyBytes2Mod, // XOR /r + /* 34 */ eENTRY_CopyBytes2, // XOR ib + /* 35 */ eENTRY_CopyBytes3Or5, // XOR id + /* 36 */ eENTRY_CopyBytesSegment, // SS prefix +#if defined(_M_X64) + /* 37 */ eENTRY_Invalid, // Invalid +#else + /* 37 */ eENTRY_CopyBytes1, // AAA +#endif + /* 38 */ eENTRY_CopyBytes2Mod, // CMP /r + /* 39 */ eENTRY_CopyBytes2Mod, // CMP /r + /* 3A */ eENTRY_CopyBytes2Mod, // CMP /r + /* 3B */ eENTRY_CopyBytes2Mod, // CMP /r + /* 3C */ eENTRY_CopyBytes2, // CMP ib + /* 3D */ eENTRY_CopyBytes3Or5, // CMP id + /* 3E */ eENTRY_CopyBytesSegment, // DS prefix +#if defined(_M_X64) + /* 3F */ eENTRY_Invalid, // Invalid +#else + /* 3F */ eENTRY_CopyBytes1, // AAS +#endif +#if defined(_M_X64) // For Rax Prefix + /* 40 */ eENTRY_CopyBytesRax, // Rax + /* 41 */ eENTRY_CopyBytesRax, // Rax + /* 42 */ eENTRY_CopyBytesRax, // Rax + /* 43 */ eENTRY_CopyBytesRax, // Rax + /* 44 */ eENTRY_CopyBytesRax, // Rax + /* 45 */ eENTRY_CopyBytesRax, // Rax + /* 46 */ eENTRY_CopyBytesRax, // Rax + /* 47 */ eENTRY_CopyBytesRax, // Rax + /* 48 */ eENTRY_CopyBytesRax, // Rax + /* 49 */ eENTRY_CopyBytesRax, // Rax + /* 4A */ eENTRY_CopyBytesRax, // Rax + /* 4B */ eENTRY_CopyBytesRax, // Rax + /* 4C */ eENTRY_CopyBytesRax, // Rax + /* 4D */ eENTRY_CopyBytesRax, // Rax + /* 4E */ eENTRY_CopyBytesRax, // Rax + /* 4F */ eENTRY_CopyBytesRax, // Rax +#else + /* 40 */ eENTRY_CopyBytes1, // INC + /* 41 */ eENTRY_CopyBytes1, // INC + /* 42 */ eENTRY_CopyBytes1, // INC + /* 43 */ eENTRY_CopyBytes1, // INC + /* 44 */ eENTRY_CopyBytes1, // INC + /* 45 */ eENTRY_CopyBytes1, // INC + /* 46 */ eENTRY_CopyBytes1, // INC + /* 47 */ eENTRY_CopyBytes1, // INC + /* 48 */ eENTRY_CopyBytes1, // DEC + /* 49 */ eENTRY_CopyBytes1, // DEC + /* 4A */ eENTRY_CopyBytes1, // DEC + /* 4B */ eENTRY_CopyBytes1, // DEC + /* 4C */ eENTRY_CopyBytes1, // DEC + /* 4D */ eENTRY_CopyBytes1, // DEC + /* 4E */ eENTRY_CopyBytes1, // DEC + /* 4F */ eENTRY_CopyBytes1, // DEC +#endif + /* 50 */ eENTRY_CopyBytes1, // PUSH + /* 51 */ eENTRY_CopyBytes1, // PUSH + /* 52 */ eENTRY_CopyBytes1, // PUSH + /* 53 */ eENTRY_CopyBytes1, // PUSH + /* 54 */ eENTRY_CopyBytes1, // PUSH + /* 55 */ eENTRY_CopyBytes1, // PUSH + /* 56 */ eENTRY_CopyBytes1, // PUSH + /* 57 */ eENTRY_CopyBytes1, // PUSH + /* 58 */ eENTRY_CopyBytes1, // POP + /* 59 */ eENTRY_CopyBytes1, // POP + /* 5A */ eENTRY_CopyBytes1, // POP + /* 5B */ eENTRY_CopyBytes1, // POP + /* 5C */ eENTRY_CopyBytes1, // POP + /* 5D */ eENTRY_CopyBytes1, // POP + /* 5E */ eENTRY_CopyBytes1, // POP + /* 5F */ eENTRY_CopyBytes1, // POP +#if defined(_M_X64) + /* 60 */ eENTRY_Invalid, // Invalid + /* 61 */ eENTRY_Invalid, // Invalid + /* 62 */ eENTRY_CopyEvex, // EVEX / AVX512 +#else + /* 60 */ eENTRY_CopyBytes1, // PUSHAD + /* 61 */ eENTRY_CopyBytes1, // POPAD + /* 62 */ eENTRY_CopyEvex, // BOUND /r and EVEX / AVX512 +#endif + /* 63 */ eENTRY_CopyBytes2Mod, // 32bit ARPL /r, 64bit MOVSXD + /* 64 */ eENTRY_CopyBytesSegment, // FS prefix + /* 65 */ eENTRY_CopyBytesSegment, // GS prefix + /* 66 */ eENTRY_Copy66, // Operand Prefix + /* 67 */ eENTRY_Copy67, // Address Prefix + /* 68 */ eENTRY_CopyBytes3Or5, // PUSH + /* 69 */ eENTRY_CopyBytes2ModOperand, // IMUL /r iz + /* 6A */ eENTRY_CopyBytes2, // PUSH + /* 6B */ eENTRY_CopyBytes2Mod1, // IMUL /r ib + /* 6C */ eENTRY_CopyBytes1, // INS + /* 6D */ eENTRY_CopyBytes1, // INS + /* 6E */ eENTRY_CopyBytes1, // OUTS/OUTSB + /* 6F */ eENTRY_CopyBytes1, // OUTS/OUTSW + /* 70 */ eENTRY_CopyBytes2Jump, // JO // 0f80 + /* 71 */ eENTRY_CopyBytes2Jump, // JNO // 0f81 + /* 72 */ eENTRY_CopyBytes2Jump, // JB/JC/JNAE // 0f82 + /* 73 */ eENTRY_CopyBytes2Jump, // JAE/JNB/JNC // 0f83 + /* 74 */ eENTRY_CopyBytes2Jump, // JE/JZ // 0f84 + /* 75 */ eENTRY_CopyBytes2Jump, // JNE/JNZ // 0f85 + /* 76 */ eENTRY_CopyBytes2Jump, // JBE/JNA // 0f86 + /* 77 */ eENTRY_CopyBytes2Jump, // JA/JNBE // 0f87 + /* 78 */ eENTRY_CopyBytes2Jump, // JS // 0f88 + /* 79 */ eENTRY_CopyBytes2Jump, // JNS // 0f89 + /* 7A */ eENTRY_CopyBytes2Jump, // JP/JPE // 0f8a + /* 7B */ eENTRY_CopyBytes2Jump, // JNP/JPO // 0f8b + /* 7C */ eENTRY_CopyBytes2Jump, // JL/JNGE // 0f8c + /* 7D */ eENTRY_CopyBytes2Jump, // JGE/JNL // 0f8d + /* 7E */ eENTRY_CopyBytes2Jump, // JLE/JNG // 0f8e + /* 7F */ eENTRY_CopyBytes2Jump, // JG/JNLE // 0f8f + /* 80 */ eENTRY_CopyBytes2Mod1, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate byte + /* 81 */ eENTRY_CopyBytes2ModOperand, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 byte reg, immediate word or dword +#if defined(_M_X64) + /* 82 */ eENTRY_Invalid, // Invalid +#else + /* 82 */ eENTRY_CopyBytes2Mod1, // MOV al,x +#endif + /* 83 */ eENTRY_CopyBytes2Mod1, // ADD/0 OR/1 ADC/2 SBB/3 AND/4 SUB/5 XOR/6 CMP/7 reg, immediate byte + /* 84 */ eENTRY_CopyBytes2Mod, // TEST /r + /* 85 */ eENTRY_CopyBytes2Mod, // TEST /r + /* 86 */ eENTRY_CopyBytes2Mod, // XCHG /r @todo + /* 87 */ eENTRY_CopyBytes2Mod, // XCHG /r @todo + /* 88 */ eENTRY_CopyBytes2Mod, // MOV /r + /* 89 */ eENTRY_CopyBytes2Mod, // MOV /r + /* 8A */ eENTRY_CopyBytes2Mod, // MOV /r + /* 8B */ eENTRY_CopyBytes2Mod, // MOV /r + /* 8C */ eENTRY_CopyBytes2Mod, // MOV /r + /* 8D */ eENTRY_CopyBytes2Mod, // LEA /r + /* 8E */ eENTRY_CopyBytes2Mod, // MOV /r + /* 8F */ eENTRY_CopyXop, // POP /0 or AMD XOP + /* 90 */ eENTRY_CopyBytes1, // NOP + /* 91 */ eENTRY_CopyBytes1, // XCHG + /* 92 */ eENTRY_CopyBytes1, // XCHG + /* 93 */ eENTRY_CopyBytes1, // XCHG + /* 94 */ eENTRY_CopyBytes1, // XCHG + /* 95 */ eENTRY_CopyBytes1, // XCHG + /* 96 */ eENTRY_CopyBytes1, // XCHG + /* 97 */ eENTRY_CopyBytes1, // XCHG + /* 98 */ eENTRY_CopyBytes1, // CWDE + /* 99 */ eENTRY_CopyBytes1, // CDQ +#if defined(_M_X64) + /* 9A */ eENTRY_Invalid, // Invalid +#else + /* 9A */ eENTRY_CopyBytes5Or7Dynamic, // CALL cp +#endif + /* 9B */ eENTRY_CopyBytes1, // WAIT/FWAIT + /* 9C */ eENTRY_CopyBytes1, // PUSHFD + /* 9D */ eENTRY_CopyBytes1, // POPFD + /* 9E */ eENTRY_CopyBytes1, // SAHF + /* 9F */ eENTRY_CopyBytes1, // LAHF + /* A0 */ eENTRY_CopyBytes1Address, // MOV + /* A1 */ eENTRY_CopyBytes1Address, // MOV + /* A2 */ eENTRY_CopyBytes1Address, // MOV + /* A3 */ eENTRY_CopyBytes1Address, // MOV + /* A4 */ eENTRY_CopyBytes1, // MOVS + /* A5 */ eENTRY_CopyBytes1, // MOVS/MOVSD + /* A6 */ eENTRY_CopyBytes1, // CMPS/CMPSB + /* A7 */ eENTRY_CopyBytes1, // CMPS/CMPSW + /* A8 */ eENTRY_CopyBytes2, // TEST + /* A9 */ eENTRY_CopyBytes3Or5, // TEST + /* AA */ eENTRY_CopyBytes1, // STOS/STOSB + /* AB */ eENTRY_CopyBytes1, // STOS/STOSW + /* AC */ eENTRY_CopyBytes1, // LODS/LODSB + /* AD */ eENTRY_CopyBytes1, // LODS/LODSW + /* AE */ eENTRY_CopyBytes1, // SCAS/SCASB + /* AF */ eENTRY_CopyBytes1, // SCAS/SCASD + /* B0 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B1 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B2 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B3 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B4 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B5 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B6 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B7 */ eENTRY_CopyBytes2, // MOV B0+rb + /* B8 */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* B9 */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BA */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BB */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BC */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BD */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BE */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* BF */ eENTRY_CopyBytes3Or5Rax, // MOV B8+rb + /* C0 */ eENTRY_CopyBytes2Mod1, // RCL/2 ib, etc. + /* C1 */ eENTRY_CopyBytes2Mod1, // RCL/2 ib, etc. + /* C2 */ eENTRY_CopyBytes3, // RET + /* C3 */ eENTRY_CopyBytes1, // RET + /* C4 */ eENTRY_CopyVex3, // LES, VEX 3-byte opcodes. + /* C5 */ eENTRY_CopyVex2, // LDS, VEX 2-byte opcodes. + /* C6 */ eENTRY_CopyBytes2Mod1, // MOV + /* C7 */ eENTRY_CopyBytes2ModOperand, // MOV/0 XBEGIN/7 + /* C8 */ eENTRY_CopyBytes4, // ENTER + /* C9 */ eENTRY_CopyBytes1, // LEAVE + /* CA */ eENTRY_CopyBytes3Dynamic, // RET + /* CB */ eENTRY_CopyBytes1Dynamic, // RET + /* CC */ eENTRY_CopyBytes1Dynamic, // INT 3 + /* CD */ eENTRY_CopyBytes2Dynamic, // INT ib +#if defined(_M_X64) + /* CE */ eENTRY_Invalid, // Invalid +#else + /* CE */ eENTRY_CopyBytes1Dynamic, // INTO +#endif + /* CF */ eENTRY_CopyBytes1Dynamic, // IRET + /* D0 */ eENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D1 */ eENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D2 */ eENTRY_CopyBytes2Mod, // RCL/2, etc. + /* D3 */ eENTRY_CopyBytes2Mod, // RCL/2, etc. +#if defined(_M_X64) + /* D4 */ eENTRY_Invalid, // Invalid + /* D5 */ eENTRY_Invalid, // Invalid +#else + /* D4 */ eENTRY_CopyBytes2, // AAM + /* D5 */ eENTRY_CopyBytes2, // AAD +#endif + /* D6 */ eENTRY_Invalid, // Invalid + /* D7 */ eENTRY_CopyBytes1, // XLAT/XLATB + /* D8 */ eENTRY_CopyBytes2Mod, // FADD, etc. + /* D9 */ eENTRY_CopyBytes2Mod, // F2XM1, etc. + /* DA */ eENTRY_CopyBytes2Mod, // FLADD, etc. + /* DB */ eENTRY_CopyBytes2Mod, // FCLEX, etc. + /* DC */ eENTRY_CopyBytes2Mod, // FADD/0, etc. + /* DD */ eENTRY_CopyBytes2Mod, // FFREE, etc. + /* DE */ eENTRY_CopyBytes2Mod, // FADDP, etc. + /* DF */ eENTRY_CopyBytes2Mod, // FBLD/4, etc. + /* E0 */ eENTRY_CopyBytes2CantJump, // LOOPNE cb + /* E1 */ eENTRY_CopyBytes2CantJump, // LOOPE cb + /* E2 */ eENTRY_CopyBytes2CantJump, // LOOP cb + /* E3 */ eENTRY_CopyBytes2CantJump, // JCXZ/JECXZ + /* E4 */ eENTRY_CopyBytes2, // IN ib + /* E5 */ eENTRY_CopyBytes2, // IN id + /* E6 */ eENTRY_CopyBytes2, // OUT ib + /* E7 */ eENTRY_CopyBytes2, // OUT ib + /* E8 */ eENTRY_CopyBytes3Or5Target, // CALL cd + /* E9 */ eENTRY_CopyBytes3Or5Target, // JMP cd +#if defined(_M_X64) + /* EA */ eENTRY_Invalid, // Invalid +#else + /* EA */ eENTRY_CopyBytes5Or7Dynamic, // JMP cp +#endif + /* EB */ eENTRY_CopyBytes2Jump, // JMP cb + /* EC */ eENTRY_CopyBytes1, // IN ib + /* ED */ eENTRY_CopyBytes1, // IN id + /* EE */ eENTRY_CopyBytes1, // OUT + /* EF */ eENTRY_CopyBytes1, // OUT + /* F0 */ eENTRY_CopyBytesPrefix, // LOCK prefix + /* F1 */ eENTRY_CopyBytes1Dynamic, // INT1 / ICEBP somewhat documented by AMD, not by Intel + /* F2 */ eENTRY_CopyF2, // REPNE prefix + + // This does presently suffice for AMD64 but it requires tracing + // through a bunch of code to verify and seems not worth maintaining. + // For x64: /* F3 */ eENTRY_CopyBytesPrefix + /* F3 */ eENTRY_CopyF3, // REPE prefix + + /* F4 */ eENTRY_CopyBytes1, // HLT + /* F5 */ eENTRY_CopyBytes1, // CMC + /* F6 */ eENTRY_CopyF6, // TEST/0, DIV/6 + /* F7 */ eENTRY_CopyF7, // TEST/0, DIV/6 + /* F8 */ eENTRY_CopyBytes1, // CLC + /* F9 */ eENTRY_CopyBytes1, // STC + /* FA */ eENTRY_CopyBytes1, // CLI + /* FB */ eENTRY_CopyBytes1, // STI + /* FC */ eENTRY_CopyBytes1, // CLD + /* FD */ eENTRY_CopyBytes1, // STD + /* FE */ eENTRY_CopyBytes2Mod, // DEC/1,INC/0 + /* FF */ eENTRY_CopyFF, // CALL/2 +}; + +static const BYTE g_rceCopyTable0F[] = +{ +#if defined(_M_IX86) + /* 00 */ eENTRY_Copy0F00, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#else + /* 00 */ eENTRY_CopyBytes2Mod, // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6/dynamic invalid/7 +#endif + /* 01 */ eENTRY_CopyBytes2Mod, // INVLPG/7, etc. + /* 02 */ eENTRY_CopyBytes2Mod, // LAR/r + /* 03 */ eENTRY_CopyBytes2Mod, // LSL/r + /* 04 */ eENTRY_Invalid, // _04 + /* 05 */ eENTRY_CopyBytes1, // SYSCALL + /* 06 */ eENTRY_CopyBytes1, // CLTS + /* 07 */ eENTRY_CopyBytes1, // SYSRET + /* 08 */ eENTRY_CopyBytes1, // INVD + /* 09 */ eENTRY_CopyBytes1, // WBINVD + /* 0A */ eENTRY_Invalid, // _0A + /* 0B */ eENTRY_CopyBytes1, // UD2 + /* 0C */ eENTRY_Invalid, // _0C + /* 0D */ eENTRY_CopyBytes2Mod, // PREFETCH + /* 0E */ eENTRY_CopyBytes1, // FEMMS (3DNow -- not in Intel documentation) + /* 0F */ eENTRY_CopyBytes2Mod1, // 3DNow Opcodes + /* 10 */ eENTRY_CopyBytes2Mod, // MOVSS MOVUPD MOVSD + /* 11 */ eENTRY_CopyBytes2Mod, // MOVSS MOVUPD MOVSD + /* 12 */ eENTRY_CopyBytes2Mod, // MOVLPD + /* 13 */ eENTRY_CopyBytes2Mod, // MOVLPD + /* 14 */ eENTRY_CopyBytes2Mod, // UNPCKLPD + /* 15 */ eENTRY_CopyBytes2Mod, // UNPCKHPD + /* 16 */ eENTRY_CopyBytes2Mod, // MOVHPD + /* 17 */ eENTRY_CopyBytes2Mod, // MOVHPD + /* 18 */ eENTRY_CopyBytes2Mod, // PREFETCHINTA... + /* 19 */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1A */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1B */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1C */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1D */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1E */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop, not documented by Intel, documented by AMD + /* 1F */ eENTRY_CopyBytes2Mod, // NOP/r multi byte nop + /* 20 */ eENTRY_CopyBytes2Mod, // MOV/r + /* 21 */ eENTRY_CopyBytes2Mod, // MOV/r + /* 22 */ eENTRY_CopyBytes2Mod, // MOV/r + /* 23 */ eENTRY_CopyBytes2Mod, // MOV/r +#if defined(_M_X64) + /* 24 */ eENTRY_Invalid, // _24 +#else + /* 24 */ eENTRY_CopyBytes2Mod, // MOV/r,TR TR is test register on 80386 and 80486, removed in Pentium +#endif + /* 25 */ eENTRY_Invalid, // _25 +#if defined(_M_X64) + /* 26 */ eENTRY_Invalid, // _26 +#else + /* 26 */ eENTRY_CopyBytes2Mod, // MOV TR/r TR is test register on 80386 and 80486, removed in Pentium +#endif + /* 27 */ eENTRY_Invalid, // _27 + /* 28 */ eENTRY_CopyBytes2Mod, // MOVAPS MOVAPD + /* 29 */ eENTRY_CopyBytes2Mod, // MOVAPS MOVAPD + /* 2A */ eENTRY_CopyBytes2Mod, // CVPI2PS & + /* 2B */ eENTRY_CopyBytes2Mod, // MOVNTPS MOVNTPD + /* 2C */ eENTRY_CopyBytes2Mod, // CVTTPS2PI & + /* 2D */ eENTRY_CopyBytes2Mod, // CVTPS2PI & + /* 2E */ eENTRY_CopyBytes2Mod, // UCOMISS UCOMISD + /* 2F */ eENTRY_CopyBytes2Mod, // COMISS COMISD + /* 30 */ eENTRY_CopyBytes1, // WRMSR + /* 31 */ eENTRY_CopyBytes1, // RDTSC + /* 32 */ eENTRY_CopyBytes1, // RDMSR + /* 33 */ eENTRY_CopyBytes1, // RDPMC + /* 34 */ eENTRY_CopyBytes1, // SYSENTER + /* 35 */ eENTRY_CopyBytes1, // SYSEXIT + /* 36 */ eENTRY_Invalid, // _36 + /* 37 */ eENTRY_CopyBytes1, // GETSEC + /* 38 */ eENTRY_CopyBytes3Mod, // SSE3 Opcodes + /* 39 */ eENTRY_Invalid, // _39 + /* 3A */ eENTRY_CopyBytes3Mod1, // SSE3 Opcodes + /* 3B */ eENTRY_Invalid, // _3B + /* 3C */ eENTRY_Invalid, // _3C + /* 3D */ eENTRY_Invalid, // _3D + /* 3E */ eENTRY_Invalid, // _3E + /* 3F */ eENTRY_Invalid, // _3F + /* 40 */ eENTRY_CopyBytes2Mod, // CMOVO (0F 40) + /* 41 */ eENTRY_CopyBytes2Mod, // CMOVNO (0F 41) + /* 42 */ eENTRY_CopyBytes2Mod, // CMOVB & CMOVNE (0F 42) + /* 43 */ eENTRY_CopyBytes2Mod, // CMOVAE & CMOVNB (0F 43) + /* 44 */ eENTRY_CopyBytes2Mod, // CMOVE & CMOVZ (0F 44) + /* 45 */ eENTRY_CopyBytes2Mod, // CMOVNE & CMOVNZ (0F 45) + /* 46 */ eENTRY_CopyBytes2Mod, // CMOVBE & CMOVNA (0F 46) + /* 47 */ eENTRY_CopyBytes2Mod, // CMOVA & CMOVNBE (0F 47) + /* 48 */ eENTRY_CopyBytes2Mod, // CMOVS (0F 48) + /* 49 */ eENTRY_CopyBytes2Mod, // CMOVNS (0F 49) + /* 4A */ eENTRY_CopyBytes2Mod, // CMOVP & CMOVPE (0F 4A) + /* 4B */ eENTRY_CopyBytes2Mod, // CMOVNP & CMOVPO (0F 4B) + /* 4C */ eENTRY_CopyBytes2Mod, // CMOVL & CMOVNGE (0F 4C) + /* 4D */ eENTRY_CopyBytes2Mod, // CMOVGE & CMOVNL (0F 4D) + /* 4E */ eENTRY_CopyBytes2Mod, // CMOVLE & CMOVNG (0F 4E) + /* 4F */ eENTRY_CopyBytes2Mod, // CMOVG & CMOVNLE (0F 4F) + /* 50 */ eENTRY_CopyBytes2Mod, // MOVMSKPD MOVMSKPD + /* 51 */ eENTRY_CopyBytes2Mod, // SQRTPS & + /* 52 */ eENTRY_CopyBytes2Mod, // RSQRTTS RSQRTPS + /* 53 */ eENTRY_CopyBytes2Mod, // RCPPS RCPSS + /* 54 */ eENTRY_CopyBytes2Mod, // ANDPS ANDPD + /* 55 */ eENTRY_CopyBytes2Mod, // ANDNPS ANDNPD + /* 56 */ eENTRY_CopyBytes2Mod, // ORPS ORPD + /* 57 */ eENTRY_CopyBytes2Mod, // XORPS XORPD + /* 58 */ eENTRY_CopyBytes2Mod, // ADDPS & + /* 59 */ eENTRY_CopyBytes2Mod, // MULPS & + /* 5A */ eENTRY_CopyBytes2Mod, // CVTPS2PD & + /* 5B */ eENTRY_CopyBytes2Mod, // CVTDQ2PS & + /* 5C */ eENTRY_CopyBytes2Mod, // SUBPS & + /* 5D */ eENTRY_CopyBytes2Mod, // MINPS & + /* 5E */ eENTRY_CopyBytes2Mod, // DIVPS & + /* 5F */ eENTRY_CopyBytes2Mod, // MASPS & + /* 60 */ eENTRY_CopyBytes2Mod, // PUNPCKLBW/r + /* 61 */ eENTRY_CopyBytes2Mod, // PUNPCKLWD/r + /* 62 */ eENTRY_CopyBytes2Mod, // PUNPCKLWD/r + /* 63 */ eENTRY_CopyBytes2Mod, // PACKSSWB/r + /* 64 */ eENTRY_CopyBytes2Mod, // PCMPGTB/r + /* 65 */ eENTRY_CopyBytes2Mod, // PCMPGTW/r + /* 66 */ eENTRY_CopyBytes2Mod, // PCMPGTD/r + /* 67 */ eENTRY_CopyBytes2Mod, // PACKUSWB/r + /* 68 */ eENTRY_CopyBytes2Mod, // PUNPCKHBW/r + /* 69 */ eENTRY_CopyBytes2Mod, // PUNPCKHWD/r + /* 6A */ eENTRY_CopyBytes2Mod, // PUNPCKHDQ/r + /* 6B */ eENTRY_CopyBytes2Mod, // PACKSSDW/r + /* 6C */ eENTRY_CopyBytes2Mod, // PUNPCKLQDQ + /* 6D */ eENTRY_CopyBytes2Mod, // PUNPCKHQDQ + /* 6E */ eENTRY_CopyBytes2Mod, // MOVD/r + /* 6F */ eENTRY_CopyBytes2Mod, // MOV/r + /* 70 */ eENTRY_CopyBytes2Mod1, // PSHUFW/r ib + /* 71 */ eENTRY_CopyBytes2Mod1, // PSLLW/6 ib,PSRAW/4 ib,PSRLW/2 ib + /* 72 */ eENTRY_CopyBytes2Mod1, // PSLLD/6 ib,PSRAD/4 ib,PSRLD/2 ib + /* 73 */ eENTRY_CopyBytes2Mod1, // PSLLQ/6 ib,PSRLQ/2 ib + /* 74 */ eENTRY_CopyBytes2Mod, // PCMPEQB/r + /* 75 */ eENTRY_CopyBytes2Mod, // PCMPEQW/r + /* 76 */ eENTRY_CopyBytes2Mod, // PCMPEQD/r + /* 77 */ eENTRY_CopyBytes1, // EMMS + // extrq/insertq require mode=3 and are followed by two immediate bytes + /* 78 */ eENTRY_Copy0F78, // VMREAD/r, 66/EXTRQ/r/ib/ib, F2/INSERTQ/r/ib/ib + // extrq/insertq require mod=3, therefore eENTRY_CopyBytes2, but it ends up the same + /* 79 */ eENTRY_CopyBytes2Mod, // VMWRITE/r, 66/EXTRQ/r, F2/INSERTQ/r + /* 7A */ eENTRY_Invalid, // _7A + /* 7B */ eENTRY_Invalid, // _7B + /* 7C */ eENTRY_CopyBytes2Mod, // HADDPS + /* 7D */ eENTRY_CopyBytes2Mod, // HSUBPS + /* 7E */ eENTRY_CopyBytes2Mod, // MOVD/r + /* 7F */ eENTRY_CopyBytes2Mod, // MOV/r + /* 80 */ eENTRY_CopyBytes3Or5Target, // JO + /* 81 */ eENTRY_CopyBytes3Or5Target, // JNO + /* 82 */ eENTRY_CopyBytes3Or5Target, // JB,JC,JNAE + /* 83 */ eENTRY_CopyBytes3Or5Target, // JAE,JNB,JNC + /* 84 */ eENTRY_CopyBytes3Or5Target, // JE,JZ,JZ + /* 85 */ eENTRY_CopyBytes3Or5Target, // JNE,JNZ + /* 86 */ eENTRY_CopyBytes3Or5Target, // JBE,JNA + /* 87 */ eENTRY_CopyBytes3Or5Target, // JA,JNBE + /* 88 */ eENTRY_CopyBytes3Or5Target, // JS + /* 89 */ eENTRY_CopyBytes3Or5Target, // JNS + /* 8A */ eENTRY_CopyBytes3Or5Target, // JP,JPE + /* 8B */ eENTRY_CopyBytes3Or5Target, // JNP,JPO + /* 8C */ eENTRY_CopyBytes3Or5Target, // JL,NGE + /* 8D */ eENTRY_CopyBytes3Or5Target, // JGE,JNL + /* 8E */ eENTRY_CopyBytes3Or5Target, // JLE,JNG + /* 8F */ eENTRY_CopyBytes3Or5Target, // JG,JNLE + /* 90 */ eENTRY_CopyBytes2Mod, // CMOVO (0F 40) + /* 91 */ eENTRY_CopyBytes2Mod, // CMOVNO (0F 41) + /* 92 */ eENTRY_CopyBytes2Mod, // CMOVB & CMOVC & CMOVNAE (0F 42) + /* 93 */ eENTRY_CopyBytes2Mod, // CMOVAE & CMOVNB & CMOVNC (0F 43) + /* 94 */ eENTRY_CopyBytes2Mod, // CMOVE & CMOVZ (0F 44) + /* 95 */ eENTRY_CopyBytes2Mod, // CMOVNE & CMOVNZ (0F 45) + /* 96 */ eENTRY_CopyBytes2Mod, // CMOVBE & CMOVNA (0F 46) + /* 97 */ eENTRY_CopyBytes2Mod, // CMOVA & CMOVNBE (0F 47) + /* 98 */ eENTRY_CopyBytes2Mod, // CMOVS (0F 48) + /* 99 */ eENTRY_CopyBytes2Mod, // CMOVNS (0F 49) + /* 9A */ eENTRY_CopyBytes2Mod, // CMOVP & CMOVPE (0F 4A) + /* 9B */ eENTRY_CopyBytes2Mod, // CMOVNP & CMOVPO (0F 4B) + /* 9C */ eENTRY_CopyBytes2Mod, // CMOVL & CMOVNGE (0F 4C) + /* 9D */ eENTRY_CopyBytes2Mod, // CMOVGE & CMOVNL (0F 4D) + /* 9E */ eENTRY_CopyBytes2Mod, // CMOVLE & CMOVNG (0F 4E) + /* 9F */ eENTRY_CopyBytes2Mod, // CMOVG & CMOVNLE (0F 4F) + /* A0 */ eENTRY_CopyBytes1, // PUSH + /* A1 */ eENTRY_CopyBytes1, // POP + /* A2 */ eENTRY_CopyBytes1, // CPUID + /* A3 */ eENTRY_CopyBytes2Mod, // BT (0F A3) + /* A4 */ eENTRY_CopyBytes2Mod1, // SHLD + /* A5 */ eENTRY_CopyBytes2Mod, // SHLD + /* A6 */ eENTRY_CopyBytes2Mod, // XBTS + /* A7 */ eENTRY_CopyBytes2Mod, // IBTS + /* A8 */ eENTRY_CopyBytes1, // PUSH + /* A9 */ eENTRY_CopyBytes1, // POP + /* AA */ eENTRY_CopyBytes1, // RSM + /* AB */ eENTRY_CopyBytes2Mod, // BTS (0F AB) + /* AC */ eENTRY_CopyBytes2Mod1, // SHRD + /* AD */ eENTRY_CopyBytes2Mod, // SHRD + + // 0F AE mod76=mem mod543=0 fxsave + // 0F AE mod76=mem mod543=1 fxrstor + // 0F AE mod76=mem mod543=2 ldmxcsr + // 0F AE mod76=mem mod543=3 stmxcsr + // 0F AE mod76=mem mod543=4 xsave + // 0F AE mod76=mem mod543=5 xrstor + // 0F AE mod76=mem mod543=6 saveopt + // 0F AE mod76=mem mod543=7 clflush + // 0F AE mod76=11b mod543=5 lfence + // 0F AE mod76=11b mod543=6 mfence + // 0F AE mod76=11b mod543=7 sfence + // F3 0F AE mod76=11b mod543=0 rdfsbase + // F3 0F AE mod76=11b mod543=1 rdgsbase + // F3 0F AE mod76=11b mod543=2 wrfsbase + // F3 0F AE mod76=11b mod543=3 wrgsbase + /* AE */ eENTRY_CopyBytes2Mod, // fxsave fxrstor ldmxcsr stmxcsr xsave xrstor saveopt clflush lfence mfence sfence rdfsbase rdgsbase wrfsbase wrgsbase + /* AF */ eENTRY_CopyBytes2Mod, // IMUL (0F AF) + /* B0 */ eENTRY_CopyBytes2Mod, // CMPXCHG (0F B0) + /* B1 */ eENTRY_CopyBytes2Mod, // CMPXCHG (0F B1) + /* B2 */ eENTRY_CopyBytes2Mod, // LSS/r + /* B3 */ eENTRY_CopyBytes2Mod, // BTR (0F B3) + /* B4 */ eENTRY_CopyBytes2Mod, // LFS/r + /* B5 */ eENTRY_CopyBytes2Mod, // LGS/r + /* B6 */ eENTRY_CopyBytes2Mod, // MOVZX/r + /* B7 */ eENTRY_CopyBytes2Mod, // MOVZX/r +#if defined(_M_IX86) + /* B8 */ eENTRY_Copy0FB8, // jmpe f3/popcnt +#else + /* B8 */ eENTRY_CopyBytes2Mod, // f3/popcnt +#endif + /* B9 */ eENTRY_Invalid, // _B9 + /* BA */ eENTRY_CopyBytes2Mod1, // BT & BTC & BTR & BTS (0F BA) + /* BB */ eENTRY_CopyBytes2Mod, // BTC (0F BB) + /* BC */ eENTRY_CopyBytes2Mod, // BSF (0F BC) + /* BD */ eENTRY_CopyBytes2Mod, // BSR (0F BD) + /* BE */ eENTRY_CopyBytes2Mod, // MOVSX/r + /* BF */ eENTRY_CopyBytes2Mod, // MOVSX/r + /* C0 */ eENTRY_CopyBytes2Mod, // XADD/r + /* C1 */ eENTRY_CopyBytes2Mod, // XADD/r + /* C2 */ eENTRY_CopyBytes2Mod1, // CMPPS & + /* C3 */ eENTRY_CopyBytes2Mod, // MOVNTI + /* C4 */ eENTRY_CopyBytes2Mod1, // PINSRW /r ib + /* C5 */ eENTRY_CopyBytes2Mod1, // PEXTRW /r ib + /* C6 */ eENTRY_CopyBytes2Mod1, // SHUFPS & SHUFPD + /* C7 */ eENTRY_CopyBytes2Mod, // CMPXCHG8B (0F C7) + /* C8 */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* C9 */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CA */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CB */ eENTRY_CopyBytes1, // CVTPD2PI BSWAP 0F C8 + rd + /* CC */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CD */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CE */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* CF */ eENTRY_CopyBytes1, // BSWAP 0F C8 + rd + /* D0 */ eENTRY_CopyBytes2Mod, // ADDSUBPS (untestd) + /* D1 */ eENTRY_CopyBytes2Mod, // PSRLW/r + /* D2 */ eENTRY_CopyBytes2Mod, // PSRLD/r + /* D3 */ eENTRY_CopyBytes2Mod, // PSRLQ/r + /* D4 */ eENTRY_CopyBytes2Mod, // PADDQ + /* D5 */ eENTRY_CopyBytes2Mod, // PMULLW/r + /* D6 */ eENTRY_CopyBytes2Mod, // MOVDQ2Q / MOVQ2DQ + /* D7 */ eENTRY_CopyBytes2Mod, // PMOVMSKB/r + /* D8 */ eENTRY_CopyBytes2Mod, // PSUBUSB/r + /* D9 */ eENTRY_CopyBytes2Mod, // PSUBUSW/r + /* DA */ eENTRY_CopyBytes2Mod, // PMINUB/r + /* DB */ eENTRY_CopyBytes2Mod, // PAND/r + /* DC */ eENTRY_CopyBytes2Mod, // PADDUSB/r + /* DD */ eENTRY_CopyBytes2Mod, // PADDUSW/r + /* DE */ eENTRY_CopyBytes2Mod, // PMAXUB/r + /* DF */ eENTRY_CopyBytes2Mod, // PANDN/r + /* E0 */ eENTRY_CopyBytes2Mod, // PAVGB + /* E1 */ eENTRY_CopyBytes2Mod, // PSRAW/r + /* E2 */ eENTRY_CopyBytes2Mod, // PSRAD/r + /* E3 */ eENTRY_CopyBytes2Mod, // PAVGW + /* E4 */ eENTRY_CopyBytes2Mod, // PMULHUW/r + /* E5 */ eENTRY_CopyBytes2Mod, // PMULHW/r + /* E6 */ eENTRY_CopyBytes2Mod, // CTDQ2PD & + /* E7 */ eENTRY_CopyBytes2Mod, // MOVNTQ + /* E8 */ eENTRY_CopyBytes2Mod, // PSUBB/r + /* E9 */ eENTRY_CopyBytes2Mod, // PSUBW/r + /* EA */ eENTRY_CopyBytes2Mod, // PMINSW/r + /* EB */ eENTRY_CopyBytes2Mod, // POR/r + /* EC */ eENTRY_CopyBytes2Mod, // PADDSB/r + /* ED */ eENTRY_CopyBytes2Mod, // PADDSW/r + /* EE */ eENTRY_CopyBytes2Mod, // PMAXSW /r + /* EF */ eENTRY_CopyBytes2Mod, // PXOR/r + /* F0 */ eENTRY_CopyBytes2Mod, // LDDQU + /* F1 */ eENTRY_CopyBytes2Mod, // PSLLW/r + /* F2 */ eENTRY_CopyBytes2Mod, // PSLLD/r + /* F3 */ eENTRY_CopyBytes2Mod, // PSLLQ/r + /* F4 */ eENTRY_CopyBytes2Mod, // PMULUDQ/r + /* F5 */ eENTRY_CopyBytes2Mod, // PMADDWD/r + /* F6 */ eENTRY_CopyBytes2Mod, // PSADBW/r + /* F7 */ eENTRY_CopyBytes2Mod, // MASKMOVQ + /* F8 */ eENTRY_CopyBytes2Mod, // PSUBB/r + /* F9 */ eENTRY_CopyBytes2Mod, // PSUBW/r + /* FA */ eENTRY_CopyBytes2Mod, // PSUBD/r + /* FB */ eENTRY_CopyBytes2Mod, // FSUBQ/r + /* FC */ eENTRY_CopyBytes2Mod, // PADDB/r + /* FD */ eENTRY_CopyBytes2Mod, // PADDW/r + /* FE */ eENTRY_CopyBytes2Mod, // PADDD/r + /* FF */ eENTRY_Invalid, // _FF +}; + +_STATIC_ASSERT(ARRAYSIZE(g_rbModRm) == 256 && + ARRAYSIZE(g_rceCopyMap) == eENTRY_Invalid + 1 && + ARRAYSIZE(g_rceCopyTable) == 256 && + ARRAYSIZE(g_rceCopyTable0F) == 256); + +/////////////////////////////////////////////////////////// Disassembler Code. +// + +static +PBYTE +AdjustTarget( + _In_ PDETOUR_DISASM pDisasm, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc, + UINT cbOp, + UINT cbTargetOffset, + UINT cbTargetSize) +{ + PBYTE pbTarget = NULL; + LONG_PTR nOldOffset; + LONG_PTR nNewOffset; + PVOID pvTargetAddr = &pbDst[cbTargetOffset]; + + switch (cbTargetSize) + { + case 1: + nOldOffset = *(signed char*)pvTargetAddr; + break; + case 2: + nOldOffset = *(UNALIGNED SHORT*)pvTargetAddr; + break; + case 4: + nOldOffset = *(UNALIGNED LONG*)pvTargetAddr; + break; +#if defined(_M_X64) + case 8: + nOldOffset = *(UNALIGNED LONGLONG*)pvTargetAddr; + break; +#endif + default: + ASSERT(!"cbTargetSize is invalid."); + nOldOffset = 0; + break; + } + + pbTarget = pbSrc + cbOp + nOldOffset; + nNewOffset = nOldOffset - (LONG_PTR)(pbDst - pbSrc); + + switch (cbTargetSize) + { + case 1: + *(CHAR*)pvTargetAddr = (CHAR)nNewOffset; + if (nNewOffset < SCHAR_MIN || nNewOffset > SCHAR_MAX) + { + *pDisasm->plExtra = sizeof(ULONG) - 1; + } + break; + case 2: + *(UNALIGNED SHORT*)pvTargetAddr = (SHORT)nNewOffset; + if (nNewOffset < SHRT_MIN || nNewOffset > SHRT_MAX) + { + *pDisasm->plExtra = sizeof(ULONG) - 2; + } + break; + case 4: + *(UNALIGNED LONG*)pvTargetAddr = (LONG)nNewOffset; + if (nNewOffset < LONG_MIN || nNewOffset > LONG_MAX) + { + *pDisasm->plExtra = sizeof(ULONG) - 4; + } + break; +#if defined(_M_X64) + case 8: + *(UNALIGNED LONGLONG*)pvTargetAddr = nNewOffset; + break; +#endif + } +#if defined(_M_X64) + // When we are only computing size, source and dest can be + // far apart, distance not encodable in 32bits. Ok. + // At least still check the lower 32bits. + + if (pbDst >= pDisasm->rbScratchDst && pbDst < (sizeof(pDisasm->rbScratchDst) + pDisasm->rbScratchDst)) + { + ASSERT((((size_t)pbDst + cbOp + nNewOffset) & 0xFFFFFFFF) == (((size_t)pbTarget) & 0xFFFFFFFF)); + } else +#endif + { + ASSERT(pbDst + cbOp + nNewOffset == pbTarget); + } + return pbTarget; +} + +static +PBYTE +Invalid( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pDisasm); + UNREFERENCED_PARAMETER(pEntry); + UNREFERENCED_PARAMETER(pbDst); + + ASSERT(!"Invalid Instruction"); + return pbSrc + 1; +} + +static +PBYTE +CopyInstruction( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // Configure scratch areas if real areas are not available. + if (NULL == pbDst) + { + pbDst = pDisasm->rbScratchDst; + } + + // Figure out how big the instruction is, do the appropriate copy, + // and figure out what the target of the instruction is if any. + // + const COPYENTRY* ce = &g_rceCopyMap[g_rceCopyTable[pbSrc[0]]]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); +} + +static +PBYTE +CopyBytes( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UINT nBytesFixed; + + if (pDisasm->bVex || pDisasm->bEvex) + { + ASSERT(pEntry->nFlagBits == 0); + ASSERT(pEntry->nFixedSize == pEntry->nFixedSize16); + } + + UINT const nModOffset = pEntry->nModOffset; + UINT const nFlagBits = pEntry->nFlagBits; + UINT const nFixedSize = pEntry->nFixedSize; + UINT const nFixedSize16 = pEntry->nFixedSize16; + + if (nFlagBits & ADDRESS) + { + nBytesFixed = pDisasm->bAddressOverride ? nFixedSize16 : nFixedSize; + } +#if defined(_M_X64) + // REX.W trumps 66 + else if (pDisasm->bRaxOverride) + { + nBytesFixed = nFixedSize + ((nFlagBits & RAX) ? 4 : 0); + } +#endif + else + { + nBytesFixed = pDisasm->bOperandOverride ? nFixedSize16 : nFixedSize; + } + + UINT nBytes = nBytesFixed; + UINT nRelOffset = pEntry->nRelOffset; + UINT cbTarget = nBytes - nRelOffset; + if (nModOffset > 0) + { + ASSERT(nRelOffset == 0); + BYTE const bModRm = pbSrc[nModOffset]; + BYTE const bFlags = g_rbModRm[bModRm]; + + nBytes += bFlags & NOTSIB; + + if (bFlags & SIB) + { + BYTE const bSib = pbSrc[nModOffset + 1]; + + if ((bSib & 0x07) == 0x05) + { + if ((bModRm & 0xc0) == 0x00) + { + nBytes += 4; + } else if ((bModRm & 0xc0) == 0x40) + { + nBytes += 1; + } else if ((bModRm & 0xc0) == 0x80) + { + nBytes += 4; + } + } + cbTarget = nBytes - nRelOffset; + } +#if defined(_M_X64) + else if (bFlags & RIP) + { + nRelOffset = nModOffset + 1; + cbTarget = 4; + } +#endif + } + CopyMemory(pbDst, pbSrc, nBytes); + + if (nRelOffset) + { + *pDisasm->ppbTarget = AdjustTarget(pDisasm, pbDst, pbSrc, nBytes, nRelOffset, cbTarget); +#if defined(_M_X64) + if (pEntry->nRelOffset == 0) + { + // This is a data target, not a code target, so we shouldn't return it. + *pDisasm->ppbTarget = NULL; + } +#endif + } + if (nFlagBits & NOENLARGE) + { + *pDisasm->plExtra = -*pDisasm->plExtra; + } + if (nFlagBits & DYNAMIC) + { + *pDisasm->ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbSrc + nBytes; +} + +static +PBYTE +CopyBytesPrefix( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + pbDst[0] = pbSrc[0]; + + REFCOPYENTRY ce = &g_rceCopyMap[g_rceCopyTable[pbSrc[1]]]; + return ce->pfCopy(pDisasm, ce, pbDst + 1, pbSrc + 1); +} + +static +PBYTE +CopyBytesSegment( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + pDisasm->nSegmentOverride = pbSrc[0]; + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +CopyBytesRax( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // AMD64 only + + UNREFERENCED_PARAMETER(pEntry); + + if (pbSrc[0] & 0x8) + { + pDisasm->bRaxOverride = TRUE; + } + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +CopyBytesJump( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + PVOID pvSrcAddr = &pbSrc[1]; + PVOID pvDstAddr = NULL; + LONG_PTR nOldOffset = (LONG_PTR)(*(signed char*)pvSrcAddr); + LONG_PTR nNewOffset = 0; + + *pDisasm->ppbTarget = pbSrc + 2 + nOldOffset; + + if (pbSrc[0] == 0xeb) + { + pbDst[0] = 0xe9; + pvDstAddr = &pbDst[1]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 3); + *(UNALIGNED LONG*)pvDstAddr = (LONG)nNewOffset; + + *pDisasm->plExtra = 3; + return pbSrc + 2; + } + + ASSERT(pbSrc[0] >= 0x70 && pbSrc[0] <= 0x7f); + + pbDst[0] = 0x0f; + pbDst[1] = 0x80 | (pbSrc[0] & 0xf); + pvDstAddr = &pbDst[2]; + nNewOffset = nOldOffset - ((pbDst - pbSrc) + 4); + *(UNALIGNED LONG*)pvDstAddr = (LONG)nNewOffset; + + *pDisasm->plExtra = 4; + return pbSrc + 2; +} + +////////////////////////////////////////////////////// Individual Bytes Codes. +// +static +PBYTE +Copy0F( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + pbDst[0] = pbSrc[0]; + + REFCOPYENTRY ce = &g_rceCopyMap[g_rceCopyTable0F[pbSrc[1]]]; + return ce->pfCopy(pDisasm, ce, pbDst + 1, pbSrc + 1); +} + +static +PBYTE +Copy0F78( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // vmread, 66/extrq, F2/insertq + + UNREFERENCED_PARAMETER(pEntry); + + const BYTE vmread = /* 78 */ eENTRY_CopyBytes2Mod; + const BYTE extrq_insertq = /* 78 */ eENTRY_CopyBytes4; + + ASSERT(!(pDisasm->bF2 && pDisasm->bOperandOverride)); + + // For insertq and presumably despite documentation extrq, mode must be 11, not checked. + // insertq/extrq/78 are followed by two immediate bytes, and given mode == 11, mod/rm byte is always one byte, + // and the 0x78 makes 4 bytes (not counting the 66/F2/F which are accounted for elsewhere) + + REFCOPYENTRY ce = &g_rceCopyMap[((pDisasm->bF2 || pDisasm->bOperandOverride) ? extrq_insertq : vmread)]; + + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); +} + +static +PBYTE +Copy0F00( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + // Notice that the sizes are the same either way, but jmpe is marked as "dynamic". + + UNREFERENCED_PARAMETER(pEntry); + + const BYTE other = /* B8 */ eENTRY_CopyBytes2Mod; // sldt/0 str/1 lldt/2 ltr/3 err/4 verw/5 jmpe/6 invalid/7 + const BYTE jmpe = /* B8 */ eENTRY_CopyBytes2ModDynamic; // jmpe/6 x86-on-IA64 syscalls + + REFCOPYENTRY ce = &g_rceCopyMap[(((6 << 3) == ((7 << 3) & pbSrc[1])) ? jmpe : other)]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); +} + +static +PBYTE +Copy0FB8( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // jmpe is 32bit x86 only + + UNREFERENCED_PARAMETER(pEntry); + + const BYTE popcnt = /* B8 */ eENTRY_CopyBytes2Mod; + const BYTE jmpe = /* B8 */ eENTRY_CopyBytes3Or5Dynamic; // jmpe x86-on-IA64 syscalls + REFCOPYENTRY ce = &g_rceCopyMap[pDisasm->bF3 ? popcnt : jmpe]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); +} + +static +PBYTE +Copy66( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // Operand-size override prefix + + UNREFERENCED_PARAMETER(pEntry); + + pDisasm->bOperandOverride = TRUE; + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +Copy67( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // Address size override prefix + + UNREFERENCED_PARAMETER(pEntry); + + pDisasm->bAddressOverride = TRUE; + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +CopyF2( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + pDisasm->bF2 = TRUE; + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +CopyF3( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // x86 only + + UNREFERENCED_PARAMETER(pEntry); + + pDisasm->bF3 = TRUE; + return CopyBytesPrefix(pDisasm, NULL, pbDst, pbSrc); +} + +static +PBYTE +CopyF6( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + // TEST BYTE /0 + if (0x00 == (0x38 & pbSrc[1])) + { + // reg(bits 543) of ModR/M == 0 + REFCOPYENTRY ce = /* f6 */ &g_rceCopyMap[eENTRY_CopyBytes2Mod1]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } else + { + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + REFCOPYENTRY ce = /* f6 */ &g_rceCopyMap[eENTRY_CopyBytes2Mod]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } +} + +static +PBYTE +CopyF7( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + UNREFERENCED_PARAMETER(pEntry); + + // TEST WORD /0 + if (0x00 == (0x38 & pbSrc[1])) + { + // reg(bits 543) of ModR/M == 0 + REFCOPYENTRY ce = /* f7 */ &g_rceCopyMap[eENTRY_CopyBytes2ModOperand]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } else + { + // DIV /6 + // IDIV /7 + // IMUL /5 + // MUL /4 + // NEG /3 + // NOT /2 + REFCOPYENTRY ce = /* f7 */ &g_rceCopyMap[eENTRY_CopyBytes2Mod]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } +} + +static +PBYTE +CopyFF( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +{ + // INC /0 + // DEC /1 + // CALL /2 + // CALL /3 + // JMP /4 + // JMP /5 + // PUSH /6 + // invalid/7 + UNREFERENCED_PARAMETER(pEntry); + + REFCOPYENTRY ce = /* ff */ &g_rceCopyMap[eENTRY_CopyBytes2Mod]; + PBYTE pbOut = ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + + BYTE const b1 = pbSrc[1]; + + if (0x15 == b1 || 0x25 == b1) + { + // CALL [], JMP [] +#if defined(_M_X64) + // All segments but FS and GS are equivalent. + if (pDisasm->nSegmentOverride != 0x64 && pDisasm->nSegmentOverride != 0x65) +#else + if (pDisasm->nSegmentOverride == 0 || pDisasm->nSegmentOverride == 0x2E) +#endif + { +#if defined(_M_X64) + INT32 offset = *(UNALIGNED INT32*) & pbSrc[2]; + PBYTE* ppbTarget = (PBYTE*)(pbSrc + 6 + offset); +#else + PBYTE* ppbTarget = (PBYTE*)(SIZE_T) * (UNALIGNED ULONG*) & pbSrc[2]; +#endif + // This can access violate on random bytes. Use DetourSetCodeModule. + *pDisasm->ppbTarget = *ppbTarget; + } else + { + *pDisasm->ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + } else if (0x10 == (0x30 & b1) || // CALL /2 or /3 --> reg(bits 543) of ModR/M == 010 or 011 + 0x20 == (0x30 & b1)) + { + // JMP /4 or /5 --> reg(bits 543) of ModR/M == 100 or 101 + *pDisasm->ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC; + } + return pbOut; +} + +static +PBYTE +CopyVexEvexCommon( + _In_ PDETOUR_DISASM pDisasm, + BYTE m, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc, + BYTE p, + _In_opt_ BYTE fp16) +// m is first instead of last in the hopes of pbDst/pbSrc being +// passed along efficiently in the registers they were already in. +{ + REFCOPYENTRY ce; + + switch (p & 3) + { + case 0: + break; + case 1: + pDisasm->bOperandOverride = TRUE; + break; + case 2: + pDisasm->bF3 = TRUE; + break; + case 3: + pDisasm->bF2 = TRUE; + break; + } + + // see https://software.intel.com/content/www/us/en/develop/download/intel-avx512-fp16-architecture-specification.html + switch (m | fp16) + { + case 1: + ce = &g_rceCopyMap[g_rceCopyTable0F[pbSrc[0]]]; + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + case 5: // fallthrough + case 6: // fallthrough + case 2: + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytes2Mod], pbDst, pbSrc); /* 38 ceF38 */ + case 3: + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytes2Mod1], pbDst, pbSrc); /* 3A ceF3A */ + default: + return Invalid(pDisasm, &g_rceCopyMap[eENTRY_Invalid], pbDst, pbSrc); /* C4 ceInvalid */ + } +} + +static +PBYTE +CopyVexCommon( + _In_ PDETOUR_DISASM pDisasm, + BYTE m, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +// m is first instead of last in the hopes of pbDst/pbSrc being +// passed along efficiently in the registers they were already in. +{ + pDisasm->bVex = TRUE; + return CopyVexEvexCommon(pDisasm, m, pbDst, pbSrc, (BYTE)(pbSrc[-1] & 3), 0); +} + +static +PBYTE +CopyVex3( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +// 3 byte VEX prefix 0xC4 +{ + UNREFERENCED_PARAMETER(pEntry); + +#if defined(_M_IX86) + if ((pbSrc[1] & 0xC0) != 0xC0) + { + REFCOPYENTRY ce = &g_rceCopyMap[eENTRY_CopyBytes2Mod]; /* C4 ceLES */ + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + pbDst[2] = pbSrc[2]; +#if defined(_M_X64) + pDisasm->bRaxOverride |= !!(pbSrc[2] & 0x80); // w in last byte, see CopyBytesRax +#else + // + // TODO + // + // Usually the VEX.W bit changes the size of a general purpose register and is ignored for 32bit. + // Sometimes it is an opcode extension. + // Look in the Intel manual, in the instruction-by-instruction reference, for ".W1", + // without nearby wording saying it is ignored for 32bit. + // For example: "VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values". + // + // Then, go through each such case and determine if W0 vs. W1 affect the size of the instruction. Probably not. + // Look for the same encoding but with "W1" changed to "W0". + // Here is one such pairing: + // VFMADD132PD/VFMADD213PD/VFMADD231PD Fused Multiply-Add of Packed Double-Precision Floating-Point Values + // + // VEX.DDS.128.66.0F38.W1 98 /r A V/V FMA Multiply packed double-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and + // put result in xmm0. + // VFMADD132PD xmm0, xmm1, xmm2/m128 + // + // VFMADD132PS/VFMADD213PS/VFMADD231PS Fused Multiply-Add of Packed Single-Precision Floating-Point Values + // VEX.DDS.128.66.0F38.W0 98 /r A V/V FMA Multiply packed single-precision floating-point values + // from xmm0 and xmm2/mem, add to xmm1 and put + // result in xmm0. + // VFMADD132PS xmm0, xmm1, xmm2/m128 + // +#endif + return CopyVexCommon(pDisasm, pbSrc[1] & 0x1F, pbDst + 3, pbSrc + 3); +} + +static +PBYTE +CopyVex2( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +// 2 byte VEX prefix 0xC5 +{ +#if defined(_M_IX86) + if ((pbSrc[1] & 0xC0) != 0xC0) + { + REFCOPYENTRY ce = &g_rceCopyMap[eENTRY_CopyBytes2Mod]; /* C5 ceLDS */ + return ce->pfCopy(pDisasm, ce, pbDst, pbSrc); + } +#endif + pbDst[0] = pbSrc[0]; + pbDst[1] = pbSrc[1]; + return CopyVexCommon(pDisasm, 1, pbDst + 2, pbSrc + 2); +} + +static +PBYTE +CopyEvex( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +// 62, 3 byte payload, x86 with implied prefixes like Vex +// for 32bit, mode 0xC0 else fallback to bound /r +{ + // NOTE: Intel and Wikipedia number these differently. + // Intel says 0-2, Wikipedia says 1-3. + + BYTE const p0 = pbSrc[1]; + +#if defined(_M_IX86) + if ((p0 & 0xC0) != 0xC0) + { + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytes2Mod], pbDst, pbSrc); /* 62 ceBound */ + } +#endif + + // This could also be handled by default in CopyVexEvexCommon + // if 4u changed to 4|8. + if (p0 & 8u) + return Invalid(pDisasm, &g_rceCopyMap[eENTRY_Invalid], pbDst, pbSrc); /* 62 ceInvalid */ + + BYTE const p1 = pbSrc[2]; + + if ((p1 & 0x04) != 0x04) + return Invalid(pDisasm, &g_rceCopyMap[eENTRY_Invalid], pbDst, pbSrc); /* 62 ceInvalid */ + + // Copy 4 byte prefix. + *(UNALIGNED ULONG*)pbDst = *(UNALIGNED ULONG*)pbSrc; + + pDisasm->bEvex = TRUE; + +#if defined(_M_X64) + pDisasm->bRaxOverride |= !!(p1 & 0x80); // w +#endif + + return CopyVexEvexCommon(pDisasm, p0 & 3u, pbDst + 4, pbSrc + 4, p1 & 3u, p0 & 4u); +} + +static +PBYTE +CopyXop( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ REFCOPYENTRY pEntry, + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc) +/* 3 byte AMD XOP prefix 0x8F +byte0: 0x8F +byte1: RXBmmmmm +byte2: WvvvvLpp +byte3: opcode +mmmmm >= 8, else pop +mmmmm only otherwise defined for 8, 9, A. +pp is like VEX but only instructions with 0 are defined +*/ +{ + UNREFERENCED_PARAMETER(pEntry); + + BYTE const m = (BYTE)(pbSrc[1] & 0x1F); + ASSERT(m <= 10); + switch (m) + { + case 8: // modrm with 8bit immediate + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytesXop1], pbDst, pbSrc); /* 8F ceXop1 */ + + case 9: // modrm with no immediate + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytesXop], pbDst, pbSrc); /* 8F ceXop */ + + case 10: // modrm with 32bit immediate + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytesXop4], pbDst, pbSrc); /* 8F ceXop4 */ + + default: + return CopyBytes(pDisasm, &g_rceCopyMap[eENTRY_CopyBytes2Mod], pbDst, pbSrc); /* 8F cePop */ + } +} + +#endif // defined(_M_X64) || defined(_M_IX86) + +#if defined(_M_ARM64) + +typedef struct _DETOUR_DISASM +{ + PBYTE pbTarget; + BYTE rbScratchDst[128]; // matches or exceeds rbCode +} DETOUR_DISASM, *PDETOUR_DISASM; + +static +VOID +detour_disasm_init( + _Out_ PDETOUR_DISASM pDisasm) +{ + pDisasm->pbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_NONE; +} + +typedef +BYTE(*COPYFUNC)( + _In_ PBYTE pbDst, + _In_ PBYTE pbSrc); + +#define c_LR 30 // The register number for the Link Register +#define c_SP 31 // The register number for the Stack Pointer +#define c_NOP 0xd503201f // A nop instruction +#define c_BREAK (0xd4200000 | (0xf000 << 5)) // A break instruction + +// +// Problematic instructions: +// +// ADR 0ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x10000000 (l = low, h = high, d = Rd) +// ADRP 1ll10000 hhhhhhhh hhhhhhhh hhhddddd & 0x9f000000 == 0x90000000 (l = low, h = high, d = Rd) +// +// B.cond 01010100 iiiiiiii iiiiiiii iii0cccc & 0xff000010 == 0x54000000 (i = delta = SignExtend(imm19:00, 64), c = cond) +// +// B 000101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x14000000 (i = delta = SignExtend(imm26:00, 64)) +// BL 100101ii iiiiiiii iiiiiiii iiiiiiii & 0xfc000000 == 0x94000000 (i = delta = SignExtend(imm26:00, 64)) +// +// CBNZ z0110101 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x35000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// CBZ z0110100 iiiiiiii iiiiiiii iiittttt & 0x7f000000 == 0x34000000 (z = size, i = delta = SignExtend(imm19:00, 64), t = Rt) +// +// LDR Wt 00011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x18000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Xt 01011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x58000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDRSW 10011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x98000000 (i = SignExtend(imm19:00, 64), t = Rt) +// PRFM 11011000 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xd8000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR St 00011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x1c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Dt 01011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x5c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR Qt 10011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0x9c000000 (i = SignExtend(imm19:00, 64), t = Rt) +// LDR inv 11011100 iiiiiiii iiiiiiii iiittttt & 0xff000000 == 0xdc000000 (i = SignExtend(imm19:00, 64), t = Rt) +// +// TBNZ z0110111 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x37000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// TBZ z0110110 bbbbbiii iiiiiiii iiittttt & 0x7f000000 == 0x36000000 (z = size, b = bitnum, i = SignExtend(imm14:00, 64), t = Rt) +// + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Rn : 5; // Source register + DWORD Imm12 : 12; // 12-bit immediate + DWORD Shift : 2; // shift (must be 0 or 1) + DWORD Opcode1 : 7; // Must be 0010001 == 0x11 + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +} AddImm12; + +static +DWORD +AddImm12_Assemble( + DWORD size, + DWORD rd, + DWORD rn, + ULONG imm, + DWORD shift) +{ + AddImm12 temp; + temp.s.Rd = rd; + temp.s.Rn = rn; + temp.s.Imm12 = imm & 0xfff; + temp.s.Shift = shift; + temp.s.Opcode1 = 0x11; + temp.s.Size = size; + return temp.Assembled; +} + +static +DWORD +AddImm12_AssembleAdd32( + DWORD rd, + DWORD rn, + ULONG imm, + DWORD shift) +{ + return AddImm12_Assemble(0, rd, rn, imm, shift); +} + +static +DWORD +AddImm12_AssembleAdd64( + DWORD rd, + DWORD rn, + ULONG imm, + DWORD shift) +{ + return AddImm12_Assemble(1, rd, rn, imm, shift); +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Imm19 : 19; // 19-bit upper immediate + DWORD Opcode1 : 5; // Must be 10000 == 0x10 + DWORD Imm2 : 2; // 2-bit lower immediate + DWORD Type : 1; // 0 = ADR, 1 = ADRP + } s; +} Adr19; + +inline +LONG +Adr19_Imm( + Adr19* p) +{ + DWORD Imm = (p->s.Imm19 << 2) | p->s.Imm2; + return (LONG)(Imm << 11) >> 11; +} + +static +DWORD +Adr19_Assemble( + DWORD type, + DWORD rd, + LONG delta) +{ + Adr19 temp; + temp.s.Rd = rd; + temp.s.Imm19 = (delta >> 2) & 0x7ffff; + temp.s.Opcode1 = 0x10; + temp.s.Imm2 = delta & 3; + temp.s.Type = type; + return temp.Assembled; +} + +static +DWORD +Adr19_AssembleAdr( + DWORD rd, + LONG delta) +{ + return Adr19_Assemble(0, rd, delta); +} + +static +DWORD +Adr19_AssembleAdrp( + DWORD rd, + LONG delta) +{ + return Adr19_Assemble(1, rd, delta); +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Condition : 4; // Condition + DWORD Opcode1 : 1; // Must be 0 + DWORD Imm19 : 19; // 19-bit immediate + DWORD Opcode2 : 8; // Must be 01010100 == 0x54 + } s; +} Bcc19; + +inline +LONG +Bcc19_Imm( + Bcc19* p) +{ + return (LONG)(p->s.Imm19 << 13) >> 11; +} + +static +DWORD +Bcc19_AssembleBcc( + DWORD condition, + LONG delta) +{ + Bcc19 temp; + temp.s.Condition = condition; + temp.s.Opcode1 = 0; + temp.s.Imm19 = delta >> 2; + temp.s.Opcode2 = 0x54; + return temp.Assembled; +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Imm26 : 26; // 26-bit immediate + DWORD Opcode1 : 5; // Must be 00101 == 0x5 + DWORD Link : 1; // 0 = B, 1 = BL + } s; +} Branch26; + +inline +LONG +Branch26_Imm( + Branch26* p) +{ + return (LONG)(p->s.Imm26 << 6) >> 4; +} + +static +DWORD +Branch26_Assemble( + DWORD link, + LONG delta) +{ + Branch26 temp; + temp.s.Imm26 = delta >> 2; + temp.s.Opcode1 = 0x5; + temp.s.Link = link; + return temp.Assembled; +} + +static +DWORD +Branch26_AssembleB( + LONG delta) +{ + return Branch26_Assemble(0, delta); +} + +static +DWORD +Branch26_AssembleBl( + LONG delta) +{ + return Branch26_Assemble(1, delta); +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Opcode1 : 5; // Must be 00000 == 0 + DWORD Rn : 5; // Register number + DWORD Opcode2 : 22; // Must be 1101011000011111000000 == 0x3587c0 for Br + // 0x358fc0 for Brl + } s; +} Br; + +static +DWORD +Br_Assemble( + DWORD rn, + BOOL link) +{ + Br temp; + temp.s.Opcode1 = 0; + temp.s.Rn = rn; + temp.s.Opcode2 = 0x3587c0; + if (link) + temp.Assembled |= 0x00200000; + return temp.Assembled; +} + +static +DWORD +Br_AssembleBr( + DWORD rn) +{ + return Br_Assemble(rn, FALSE); +} + +static +DWORD +Br_AssembleBrl( + DWORD rn) +{ + return Br_Assemble(rn, TRUE); +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rt : 5; // Register to test + DWORD Imm19 : 19; // 19-bit immediate + DWORD Nz : 1; // 0 = CBZ, 1 = CBNZ + DWORD Opcode1 : 6; // Must be 011010 == 0x1a + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +} Cbz19; + +inline +LONG +Cbz19_Imm( + Cbz19* p) +{ + return (LONG)(p->s.Imm19 << 13) >> 11; +} + +static +DWORD +Cbz19_Assemble( + DWORD size, + DWORD nz, + DWORD rt, + LONG delta) +{ + Cbz19 temp; + temp.s.Rt = rt; + temp.s.Imm19 = delta >> 2; + temp.s.Nz = nz; + temp.s.Opcode1 = 0x1a; + temp.s.Size = size; + return temp.Assembled; +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rt : 5; // Destination register + DWORD Imm19 : 19; // 19-bit immediate + DWORD Opcode1 : 2; // Must be 0 + DWORD FpNeon : 1; // 0 = LDR Wt/LDR Xt/LDRSW/PRFM, 1 = LDR St/LDR Dt/LDR Qt + DWORD Opcode2 : 3; // Must be 011 = 3 + DWORD Size : 2; // 00 = LDR Wt/LDR St, 01 = LDR Xt/LDR Dt, 10 = LDRSW/LDR Qt, 11 = PRFM/invalid + } s; +} LdrLit19; + +inline +LONG +LdrLit19_Imm( + LdrLit19* p) +{ + return (LONG)(p->s.Imm19 << 13) >> 11; +} + +static +DWORD +LdrLit19_Assemble( + DWORD size, + DWORD fpneon, + DWORD rt, + LONG delta) +{ + LdrLit19 temp; + temp.s.Rt = rt; + temp.s.Imm19 = delta >> 2; + temp.s.Opcode1 = 0; + temp.s.FpNeon = fpneon; + temp.s.Opcode2 = 3; + temp.s.Size = size; + return temp.Assembled; +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rt : 5; // Destination register + DWORD Rn : 5; // Base register + DWORD Imm12 : 12; // 12-bit immediate + DWORD Opcode1 : 1; // Must be 1 == 1 + DWORD Opc : 1; // Part of size + DWORD Opcode2 : 6; // Must be 111101 == 0x3d + DWORD Size : 2; // Size (0=8-bit, 1=16-bit, 2=32-bit, 3=64-bit, 4=128-bit) + } s; +} LdrFpNeonImm9; + +static +DWORD +LdrFpNeonImm9_Assemble( + DWORD size, + DWORD rt, + DWORD rn, + ULONG imm) +{ + LdrFpNeonImm9 temp; + temp.s.Rt = rt; + temp.s.Rn = rn; + temp.s.Imm12 = imm; + temp.s.Opcode1 = 1; + temp.s.Opc = size >> 2; + temp.s.Opcode2 = 0x3d; + temp.s.Size = size & 3; + return temp.Assembled; +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rd : 5; // Destination register + DWORD Imm16 : 16; // Immediate + DWORD Shift : 2; // Shift amount (0=0, 1=16, 2=32, 3=48) + DWORD Opcode : 6; // Must be 100101 == 0x25 + DWORD Type : 2; // 0 = MOVN, 1 = reserved, 2 = MOVZ, 3 = MOVK + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +} Mov16; + +static +DWORD +Mov16_Assemble( + DWORD size, + DWORD type, + DWORD rd, + DWORD imm, + DWORD shift) +{ + Mov16 temp; + temp.s.Rd = rd; + temp.s.Imm16 = imm; + temp.s.Shift = shift; + temp.s.Opcode = 0x25; + temp.s.Type = type; + temp.s.Size = size; + return temp.Assembled; +} + +static +DWORD +Mov16_AssembleMovn32( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(0, 0, rd, imm, shift); +} + +static +DWORD +Mov16_AssembleMovn64( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(1, 0, rd, imm, shift); +} + +static +DWORD +Mov16_AssembleMovz32( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(0, 2, rd, imm, shift); +} + +static +DWORD +Mov16_AssembleMovz64( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(1, 2, rd, imm, shift); +} + +static +DWORD +Mov16_AssembleMovk32( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(0, 3, rd, imm, shift); +} + +static +DWORD +Mov16_AssembleMovk64( + DWORD rd, + DWORD imm, + DWORD shift) +{ + return Mov16_Assemble(1, 3, rd, imm, shift); +} + +typedef union +{ + DWORD Assembled; + struct + { + DWORD Rt : 5; // Register to test + DWORD Imm14 : 14; // 14-bit immediate + DWORD Bit : 5; // 5-bit index + DWORD Nz : 1; // 0 = TBZ, 1 = TBNZ + DWORD Opcode1 : 6; // Must be 011011 == 0x1b + DWORD Size : 1; // 0 = 32-bit, 1 = 64-bit + } s; +} Tbz14; + +inline +LONG +Tbz14_Imm( + Tbz14* p) +{ + return (LONG)(p->s.Imm14 << 18) >> 16; +} + +static +DWORD +Tbz14_Assemble( + DWORD size, + DWORD nz, + DWORD rt, + DWORD bit, + LONG delta) +{ + Tbz14 temp; + temp.s.Rt = rt; + temp.s.Imm14 = delta >> 2; + temp.s.Bit = bit; + temp.s.Nz = nz; + temp.s.Opcode1 = 0x1b; + temp.s.Size = size; + return temp.Assembled; +} + +inline +ULONG +GetInstruction( + _In_ BYTE* pSource) +{ + return *(PULONG)pSource; +} + +static +PULONG +EmitInstruction( + _In_ PULONG pDstInst, + ULONG instruction) +{ + *pDstInst = instruction; + return pDstInst + 1; +} + +static +PULONG +EmitMovImmediate( + PULONG pDstInst, + BYTE rd, + UINT64 immediate) +{ + DWORD piece[4]; + piece[3] = (DWORD)((immediate >> 48) & 0xffff); + piece[2] = (DWORD)((immediate >> 32) & 0xffff); + piece[1] = (DWORD)((immediate >> 16) & 0xffff); + piece[0] = (DWORD)((immediate >> 0) & 0xffff); + + // special case: MOVN with 32-bit dest + if (piece[3] == 0 && piece[2] == 0 && piece[1] == 0xffff) + { + pDstInst = EmitInstruction(pDstInst, Mov16_AssembleMovn32(rd, piece[0] ^ 0xffff, 0)); + } + // MOVN/MOVZ with 64-bit dest + else + { + int zero_pieces = (piece[3] == 0x0000) + (piece[2] == 0x0000) + (piece[1] == 0x0000) + (piece[0] == 0x0000); + int ffff_pieces = (piece[3] == 0xffff) + (piece[2] == 0xffff) + (piece[1] == 0xffff) + (piece[0] == 0xffff); + DWORD defaultPiece = (ffff_pieces > zero_pieces) ? 0xffff : 0x0000; + BOOL first = TRUE; + for (int pieceNum = 3; pieceNum >= 0; pieceNum--) + { + DWORD curPiece = piece[pieceNum]; + if (curPiece != defaultPiece || (pieceNum == 0 && first)) + { + if (first) + { + if (defaultPiece == 0xffff) + { + pDstInst = EmitInstruction(pDstInst, Mov16_AssembleMovn64(rd, curPiece ^ 0xffff, pieceNum)); + } else + { + pDstInst = EmitInstruction(pDstInst, Mov16_AssembleMovz64(rd, curPiece, pieceNum)); + } + first = FALSE; + } else + { + pDstInst = EmitInstruction(pDstInst, Mov16_AssembleMovk64(rd, curPiece, pieceNum)); + } + } + } + } + + return pDstInst; +} + +static +BYTE +PureCopy32( + _In_ PBYTE pSource, + _In_ PBYTE pDest) +{ + *(ULONG*)pDest = *(ULONG*)pSource; + return sizeof(ULONG); +} + +/////////////////////////////////////////////////////////// Disassembler Code. +// + +static +BYTE +CopyAdr( + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + Adr19 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + // ADR case + if (decoded.s.Type == 0) + { + BYTE* pTarget = pSource + Adr19_Imm(&decoded); + LONG64 delta = pTarget - pDest; + LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12); + + // output as ADR + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + pDstInst = EmitInstruction(pDstInst, Adr19_AssembleAdr(decoded.s.Rd, (LONG)delta)); + } + // output as ADRP; ADD + else if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20))) + { + pDstInst = EmitInstruction(pDstInst, Adr19_AssembleAdrp(decoded.s.Rd, (LONG)deltaPage)); + pDstInst = EmitInstruction(pDstInst, AddImm12_AssembleAdd32(decoded.s.Rd, + decoded.s.Rd, + ((ULONG)(ULONG_PTR)pTarget) & 0xfff, + 0)); + } + // output as immediate move + else + { + pDstInst = EmitMovImmediate(pDstInst, (BYTE)decoded.s.Rd, (ULONG_PTR)pTarget); + } + } + // ADRP case + else + { + BYTE* pTarget = (BYTE*)((((ULONG_PTR)pSource >> 12) + Adr19_Imm(&decoded)) << 12); + LONG64 deltaPage = ((ULONG_PTR)pTarget >> 12) - ((ULONG_PTR)pDest >> 12); + + // output as ADRP + if (deltaPage >= -(1 << 20) && (deltaPage < (1 << 20))) + { + pDstInst = EmitInstruction(pDstInst, Adr19_AssembleAdrp(decoded.s.Rd, (LONG)deltaPage)); + } + // output as immediate move + else + { + pDstInst = EmitMovImmediate(pDstInst, (BYTE)decoded.s.Rd, (ULONG_PTR)pTarget); + } + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +BYTE +CopyBcc( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + Bcc19 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + Bcc19_Imm(&decoded); + pDisasm->pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as BCC + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + pDstInst = EmitInstruction(pDstInst, Bcc19_AssembleBcc(decoded.s.Condition, (LONG)delta)); + } + // output as BCC ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + pDstInst = EmitInstruction(pDstInst, Bcc19_AssembleBcc(decoded.s.Condition ^ 1, 8)); + pDstInst = EmitInstruction(pDstInst, Branch26_AssembleB((LONG)delta4)); + } + // output as MOV x17, Target; BCC ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + pDstInst = EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + pDstInst = EmitInstruction(pDstInst, Bcc19_AssembleBcc(decoded.s.Condition ^ 1, 8)); + pDstInst = EmitInstruction(pDstInst, Br_AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +BYTE +CopyB_or_Bl( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction, + BOOL link) +{ + Branch26 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + Branch26_Imm(&decoded); + pDisasm->pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + + // output as B or BRL + if (delta >= -(1 << 27) && (delta < (1 << 27))) + { + pDstInst = EmitInstruction(pDstInst, Branch26_Assemble(link, (LONG)delta)); + } + // output as MOV x17, Target; BR or BRL x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + pDstInst = EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + pDstInst = EmitInstruction(pDstInst, Br_Assemble(17, link)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +BYTE +CopyB( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + return CopyB_or_Bl(pDisasm, pSource, pDest, instruction, FALSE); +} + +static +BYTE +CopyBl( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + return CopyB_or_Bl(pDisasm, pSource, pDest, instruction, FALSE); +} + +static +BYTE +CopyCbz( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + Cbz19 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + Cbz19_Imm(&decoded); + pDisasm->pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as CBZ/NZ + if (delta >= -(1 << 20) && delta < (1 << 20)) + { + pDstInst = EmitInstruction(pDstInst, Cbz19_Assemble(decoded.s.Size, decoded.s.Nz, decoded.s.Rt, (LONG)delta)); + } + // output as CBNZ/Z ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + pDstInst = EmitInstruction(pDstInst, Cbz19_Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8)); + pDstInst = EmitInstruction(pDstInst, Branch26_AssembleB((LONG)delta4)); + } + // output as MOV x17, Target; CBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + pDstInst = EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + pDstInst = EmitInstruction(pDstInst, Cbz19_Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, 8)); + pDstInst = EmitInstruction(pDstInst, Br_AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +BYTE +CopyTbz( + _In_ PDETOUR_DISASM pDisasm, + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + Tbz14 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + Tbz14_Imm(&decoded); + pDisasm->pbTarget = pTarget; + LONG64 delta = pTarget - pDest; + LONG64 delta4 = pTarget - (pDest + 4); + + // output as TBZ/NZ + if (delta >= -(1 << 13) && delta < (1 << 13)) + { + pDstInst = EmitInstruction(pDstInst, Tbz14_Assemble(decoded.s.Size, + decoded.s.Nz, + decoded.s.Rt, + decoded.s.Bit, + (LONG)delta)); + } + // output as TBNZ/Z ; B + else if (delta4 >= -(1 << 27) && (delta4 < (1 << 27))) + { + pDstInst = EmitInstruction(pDstInst, Tbz14_Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8)); + pDstInst = EmitInstruction(pDstInst, Branch26_AssembleB((LONG)delta4)); + } + // output as MOV x17, Target; TBNZ/Z ; BR x17 (BIG assumption that x17 isn't being used for anything!!) + else + { + pDstInst = EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + pDstInst = EmitInstruction(pDstInst, Tbz14_Assemble(decoded.s.Size, decoded.s.Nz ^ 1, decoded.s.Rt, decoded.s.Bit, 8)); + pDstInst = EmitInstruction(pDstInst, Br_AssembleBr(17)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +BYTE +CopyLdrLiteral( + BYTE* pSource, + BYTE* pDest, + ULONG instruction) +{ + LdrLit19 decoded = { .Assembled = instruction }; + PULONG pDstInst = (PULONG)(pDest); + + BYTE* pTarget = pSource + LdrLit19_Imm(&decoded); + LONG64 delta = pTarget - pDest; + + // output as LDR + if (delta >= -(1 << 21) && delta < (1 << 21)) + { + pDstInst = EmitInstruction(pDstInst, LdrLit19_Assemble(decoded.s.Size, decoded.s.FpNeon, decoded.s.Rt, (LONG)delta)); + } + + // output as move immediate + else if (decoded.s.FpNeon == 0) + { + UINT64 value = 0; + switch (decoded.s.Size) + { + case 0: value = *(ULONG*)pTarget; break; + case 1: value = *(UINT64*)pTarget; break; + case 2: value = *(LONG*)pTarget; break; + } + pDstInst = EmitMovImmediate(pDstInst, (BYTE)decoded.s.Rt, value); + } + // FP/NEON register: compute address in x17 and load from there (BIG assumption that x17 isn't being used for anything!!) + else + { + pDstInst = EmitMovImmediate(pDstInst, 17, (ULONG_PTR)pTarget); + pDstInst = EmitInstruction(pDstInst, LdrFpNeonImm9_Assemble(2 + decoded.s.Size, decoded.s.Rt, 17, 0)); + } + + return (BYTE)((BYTE*)pDstInst - pDest); +} + +static +PBYTE +CopyInstruction( + _In_ PDETOUR_DISASM pDisasm, + _In_opt_ PBYTE pDst, + _In_ PBYTE pSrc, + PBYTE* ppTarget, + LONG* plExtra) +{ + if (pDst == NULL) + { + pDst = pDisasm->rbScratchDst; + } + + DWORD Instruction = GetInstruction(pSrc); + + ULONG CopiedSize; + if ((Instruction & 0x1f000000) == 0x10000000) + { + CopiedSize = CopyAdr(pSrc, pDst, Instruction); + } else if ((Instruction & 0xff000010) == 0x54000000) + { + CopiedSize = CopyBcc(pDisasm, pSrc, pDst, Instruction); + } else if ((Instruction & 0x7c000000) == 0x14000000) + { + CopiedSize = CopyB_or_Bl(pDisasm, pSrc, pDst, Instruction, (Instruction & 0x80000000) != 0); + } else if ((Instruction & 0x7e000000) == 0x34000000) + { + CopiedSize = CopyCbz(pDisasm, pSrc, pDst, Instruction); + } else if ((Instruction & 0x7e000000) == 0x36000000) + { + CopiedSize = CopyTbz(pDisasm, pSrc, pDst, Instruction); + } else if ((Instruction & 0x3b000000) == 0x18000000) + { + CopiedSize = CopyLdrLiteral(pSrc, pDst, Instruction); + } else + { + CopiedSize = PureCopy32(pSrc, pDst); + } + + // If the target is needed, store our target + if (ppTarget) + { + *ppTarget = pDisasm->pbTarget; + } + if (plExtra) + { + *plExtra = CopiedSize - sizeof(DWORD); + } + + return pSrc + 4; +} + +#endif // defined(_M_ARM64) + +PVOID +NTAPI +SlimDetoursCopyInstruction( + _In_opt_ PVOID pDst, + _In_ PVOID pSrc, + _Out_opt_ PVOID* ppTarget, + _Out_opt_ LONG* plExtra) +{ + DETOUR_DISASM Disasm; + +#if defined(_M_X64) || defined(_M_IX86) + detour_disasm_init(&Disasm, (PBYTE*)ppTarget, plExtra); + return (PVOID)CopyInstruction(&Disasm, (PBYTE)pDst, (PBYTE)pSrc); +#elif defined(_M_ARM64) + detour_disasm_init(&Disasm); + return (PVOID)CopyInstruction(&Disasm, + (PBYTE)pDst, + (PBYTE)pSrc, + (PBYTE*)ppTarget, + plExtra); +#else + return NULL; +#endif +} diff --git a/Source/SlimDetours/Instruction.c b/Source/SlimDetours/Instruction.c new file mode 100644 index 00000000..656613c6 --- /dev/null +++ b/Source/SlimDetours/Instruction.c @@ -0,0 +1,670 @@ +/* + * KNSoft SlimDetours (https://github.com/KNSoft/SlimDetours) Instruction Utility + * Copyright (c) KNSoft.org (https://github.com/KNSoft). All rights reserved. + * Licensed under the MPL-2.0 license. + * + * Source base on Microsoft Detours: + * + * Microsoft Research Detours Package, Version 4.0.1 + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT license. + */ + +#include "SlimDetours.inl" + +static +BOOL +detour_is_imported( + _In_ PVOID pbCode, + _In_ PVOID pbAddress) +{ + NTSTATUS Status; + MEMORY_BASIC_INFORMATION mbi; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNtHeader; + PVOID pEndOfMem; + WORD wNtMagic; + + Status = NtQueryVirtualMemory(NtCurrentProcess(), pbCode, MemoryBasicInformation, &mbi, sizeof(mbi), NULL); + if (!NT_SUCCESS(Status)) + { + return FALSE; + } + + /* Type should be MEM_IMAGE */ + if (mbi.Type != MEM_IMAGE) + { + return FALSE; + } + + /* Cannot be uncommitted regions or guard pages */ + if ((mbi.State != MEM_COMMIT) || ((mbi.Protect & 0xFF) == PAGE_NOACCESS) || (mbi.Protect & PAGE_GUARD)) + { + return FALSE; + } + + /* + * RegionSize should >= PAGE_SIZE and PAGE_SIZE always >= sizeof(IMAGE_DOS_HEADER), + * so we can access IMAGE_DOS_HEADER safely without boundary check. + */ + _STATIC_ASSERT(PAGE_SIZE >= sizeof(IMAGE_DOS_HEADER)); + if (mbi.RegionSize < PAGE_SIZE) + { + return FALSE; + } + + /* Check IMAGE_DOS_HEADER */ + pDosHeader = (PIMAGE_DOS_HEADER)mbi.AllocationBase; + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + return FALSE; + } + if (pDosHeader->e_lfanew < sizeof(*pDosHeader) || (ULONG)pDosHeader->e_lfanew > mbi.RegionSize) + { + return FALSE; + } + + /* Now we need perform boundary check in every single step */ + pEndOfMem = Add2Ptr(mbi.AllocationBase, mbi.RegionSize); + + /* + * Step forward to IMAGE_NT_HEADERS and check IMAGE_NT_SIGNATURE, + * check FileHeader.SizeOfOptionalHeader == 0 seems pointless + * unless compare it with sizeof(IMAGE_OPTIONAL_HEADER) explicitly. + */ + pNtHeader = (PIMAGE_NT_HEADERS)Add2Ptr(pDosHeader, pDosHeader->e_lfanew); + if (Add2Ptr(pNtHeader, sizeof(*pNtHeader)) > pEndOfMem) + { + return FALSE; + } + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) + { + return FALSE; + } + + /* Step forward to IMAGE_OPTIONAL_HEADER and check magic */ + _STATIC_ASSERT(UFIELD_OFFSET(IMAGE_OPTIONAL_HEADER, Magic) == 0); + wNtMagic = pNtHeader->OptionalHeader.Magic; + if ((wNtMagic != IMAGE_NT_OPTIONAL_HDR64_MAGIC || + pNtHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64)) && + (wNtMagic != IMAGE_NT_OPTIONAL_HDR32_MAGIC || + pNtHeader->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32))) + { + return FALSE; + } + + if (pbAddress < Add2Ptr(mbi.AllocationBase, + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) || + pbAddress >= Add2Ptr(mbi.AllocationBase, + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress + + pNtHeader->OptionalHeader + .DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size)) + { + return FALSE; + } + + return TRUE; +} + +#if defined(_M_IX86) || defined(_M_X64) + +_Ret_notnull_ +PBYTE +detour_gen_jmp_immediate( + _In_ PBYTE pbCode, + _In_ PBYTE pbJmpVal) +{ + PBYTE pbJmpSrc = pbCode + 5; + *pbCode++ = 0xe9; // jmp +imm32 + *((INT32*)pbCode)++ = (INT32)(pbJmpVal - pbJmpSrc); + return pbCode; +} + +_Ret_notnull_ +PBYTE +detour_gen_jmp_indirect( + _In_ PBYTE pbCode, + _In_ PBYTE* ppbJmpVal) +{ +#if defined(_M_X64) + PBYTE pbJmpSrc = pbCode + 6; +#endif + *pbCode++ = 0xff; // jmp [+imm32] + *pbCode++ = 0x25; +#if defined(_M_X64) + *((INT32*)pbCode)++ = (INT32)((PBYTE)ppbJmpVal - pbJmpSrc); +#else + *((INT32*)pbCode)++ = (INT32)((PBYTE)ppbJmpVal); +#endif + return pbCode; +} + +_Ret_notnull_ +PBYTE +detour_gen_brk( + _In_ PBYTE pbCode, + _In_ PBYTE pbLimit) +{ + while (pbCode < pbLimit) + { + *pbCode++ = 0xcc; // brk; + } + return pbCode; +} + +_Ret_notnull_ +PBYTE +detour_skip_jmp( + _In_ PBYTE pbCode) +{ + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) + { + // Looks like an import alias jump, then get the code it points to. +#if defined(_M_IX86) + // jmp [imm32] + PBYTE pbTarget = *(UNALIGNED PBYTE*) & pbCode[2]; +#else + // jmp [+imm32] + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32*) & pbCode[2]; +#endif + + if (detour_is_imported(pbCode, pbTarget)) + { + PBYTE pbNew = *(UNALIGNED PBYTE*)pbTarget; + DETOUR_TRACE("%p->%p: skipped over import table.\n", pbCode, pbNew); + pbCode = pbNew; + } + } + + // Then, skip over a patch jump + if (pbCode[0] == 0xeb) + { + // jmp +imm8 + PBYTE pbNew = pbCode + 2 + *(CHAR*)&pbCode[1]; + DETOUR_TRACE("%p->%p: skipped over short jump.\n", pbCode, pbNew); + pbCode = pbNew; + + // First, skip over the import vector if there is one. + if (pbCode[0] == 0xff && pbCode[1] == 0x25) + { + // Looks like an import alias jump, then get the code it points to. +#if defined(_M_IX86) + // jmp [imm32] + PBYTE pbTarget = *(UNALIGNED PBYTE*) & pbCode[2]; +#else + // jmp [+imm32] + PBYTE pbTarget = pbCode + 6 + *(UNALIGNED INT32*) & pbCode[2]; +#endif + if (detour_is_imported(pbCode, pbTarget)) + { + pbNew = *(UNALIGNED PBYTE*)pbTarget; + DETOUR_TRACE("%p->%p: skipped over import table.\n", pbCode, pbNew); + pbCode = pbNew; + } + } + // Finally, skip over a long jump if it is the target of the patch jump. + else if (pbCode[0] == 0xe9) + { + // jmp +imm32 + pbNew = pbCode + 5 + *(UNALIGNED INT32*) & pbCode[1]; + DETOUR_TRACE("%p->%p: skipped over long jump.\n", pbCode, pbNew); + pbCode = pbNew; + } + } + return pbCode; +} + +VOID +detour_find_jmp_bounds( + _In_ PBYTE pbCode, + _Outptr_ PVOID* ppLower, + _Outptr_ PVOID* ppUpper) +{ + // We have to place trampolines within +/- 2GB of code. + PVOID lo = detour_memory_2gb_below(pbCode); + PVOID hi = detour_memory_2gb_above(pbCode); + DETOUR_TRACE("[%p..%p..%p]\n", lo, pbCode, hi); + + // And, within +/- 2GB of relative jmp targets. + if (pbCode[0] == 0xe9) + { + // jmp +imm32 + PBYTE pbNew = pbCode + 5 + *(UNALIGNED INT32*) & pbCode[1]; + + if (pbNew < pbCode) + { + hi = detour_memory_2gb_above(pbNew); + } else + { + lo = detour_memory_2gb_below(pbNew); + } + DETOUR_TRACE("[%p..%p..%p] +imm32\n", lo, pbCode, hi); + } +#if defined(_M_X64) + // And, within +/- 2GB of relative jmp vectors. + else if (pbCode[0] == 0xff && pbCode[1] == 0x25) + { + // jmp [+imm32] + PBYTE pbNew = pbCode + 6 + *(UNALIGNED INT32*) & pbCode[2]; + + if (pbNew < pbCode) + { + hi = detour_memory_2gb_above(pbNew); + } else + { + lo = detour_memory_2gb_below(pbNew); + } + DETOUR_TRACE("[%p..%p..%p] [+imm32]\n", lo, pbCode, hi); + } +#endif + + *ppLower = lo; + *ppUpper = hi; +} + +BOOL +detour_does_code_end_function( + _In_ PBYTE pbCode) +{ + if (pbCode[0] == 0xeb || // jmp +imm8 + pbCode[0] == 0xe9 || // jmp +imm32 + pbCode[0] == 0xe0 || // jmp eax + pbCode[0] == 0xc2 || // ret +imm8 + pbCode[0] == 0xc3 || // ret + pbCode[0] == 0xcc) + { + // brk + return TRUE; + } else if (pbCode[0] == 0xf3 && pbCode[1] == 0xc3) + { + // rep ret + return TRUE; + } else if (pbCode[0] == 0xff && pbCode[1] == 0x25) + { + // jmp [+imm32] + return TRUE; + } else if ((pbCode[0] == 0x26 || // jmp es: + pbCode[0] == 0x2e || // jmp cs: + pbCode[0] == 0x36 || // jmp ss: + pbCode[0] == 0x3e || // jmp ds: + pbCode[0] == 0x64 || // jmp fs: + pbCode[0] == 0x65) && // jmp gs: + pbCode[1] == 0xff && // jmp [+imm32] + pbCode[2] == 0x25) + { + return TRUE; + } + return FALSE; +} + +ULONG +detour_is_code_filler( + _In_ PBYTE pbCode) +{ + // 1-byte through 11-byte NOPs. + if (pbCode[0] == 0x90) + { + return 1; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x90) + { + return 2; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x00) + { + return 3; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x40 && pbCode[3] == 0x00) + { + return 4; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x44 && pbCode[3] == 0x00 && pbCode[4] == 0x00) + { + return 5; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && pbCode[3] == 0x44 && pbCode[4] == 0x00 && + pbCode[5] == 0x00) + { + return 6; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x80 && pbCode[3] == 0x00 && pbCode[4] == 0x00 && + pbCode[5] == 0x00 && pbCode[6] == 0x00) + { + return 7; + } + if (pbCode[0] == 0x0F && pbCode[1] == 0x1F && pbCode[2] == 0x84 && pbCode[3] == 0x00 && pbCode[4] == 0x00 && + pbCode[5] == 0x00 && pbCode[6] == 0x00 && pbCode[7] == 0x00) + { + return 8; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x0F && pbCode[2] == 0x1F && pbCode[3] == 0x84 && pbCode[4] == 0x00 && + pbCode[5] == 0x00 && pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00) + { + return 9; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x0F && pbCode[3] == 0x1F && pbCode[4] == 0x84 && + pbCode[5] == 0x00 && pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && pbCode[9] == 0x00) + { + return 10; + } + if (pbCode[0] == 0x66 && pbCode[1] == 0x66 && pbCode[2] == 0x66 && pbCode[3] == 0x0F && pbCode[4] == 0x1F && + pbCode[5] == 0x84 && pbCode[6] == 0x00 && pbCode[7] == 0x00 && pbCode[8] == 0x00 && pbCode[9] == 0x00 && + pbCode[10] == 0x00) + { + return 11; + } + + // int 3. + if (pbCode[0] == 0xCC) + { + return 1; + } + return 0; +} + +#endif // defined(_M_IX86) || defined(_M_X64) + +#if defined(_M_ARM64) +inline +ULONG +fetch_opcode( + PBYTE pbCode) +{ + return *(ULONG*)pbCode; +} + +inline +PBYTE +write_opcode( + PBYTE pbCode, + ULONG Opcode) +{ + *(ULONG*)pbCode = Opcode; + return pbCode + 4; +} + +struct ARM64_INDIRECT_JMP +{ + struct + { + ULONG Rd : 5; + ULONG immhi : 19; + ULONG iop : 5; + ULONG immlo : 2; + ULONG op : 1; + } ardp; + + struct + { + ULONG Rt : 5; + ULONG Rn : 5; + ULONG imm : 12; + ULONG opc : 2; + ULONG iop1 : 2; + ULONG V : 1; + ULONG iop2 : 3; + ULONG size : 2; + } ldr; + + ULONG br; +}; + +union ARM64_INDIRECT_IMM +{ + struct + { + ULONG64 pad : 12; + ULONG64 adrp_immlo : 2; + ULONG64 adrp_immhi : 19; + }; + + LONG64 value; +}; + +_Ret_notnull_ +PBYTE +detour_gen_jmp_indirect( + _In_ PBYTE pbCode, + _In_ PULONG64 pbJmpVal) +{ + // adrp x17, [jmpval] + // ldr x17, [x17, jmpval] + // br x17 + + struct ARM64_INDIRECT_JMP* pIndJmp; + union ARM64_INDIRECT_IMM jmpIndAddr; + + jmpIndAddr.value = (((LONG64)pbJmpVal) & 0xFFFFFFFFFFFFF000) - + (((LONG64)pbCode) & 0xFFFFFFFFFFFFF000); + + pIndJmp = (struct ARM64_INDIRECT_JMP*)pbCode; + pbCode = (PBYTE)(pIndJmp + 1); + + pIndJmp->ardp.Rd = 17; + pIndJmp->ardp.immhi = (ULONG)jmpIndAddr.adrp_immhi; + pIndJmp->ardp.iop = 0x10; + pIndJmp->ardp.immlo = (ULONG)jmpIndAddr.adrp_immlo; + pIndJmp->ardp.op = 1; + + pIndJmp->ldr.Rt = 17; + pIndJmp->ldr.Rn = 17; + pIndJmp->ldr.imm = (((ULONG64)pbJmpVal) & 0xFFF) / 8; + pIndJmp->ldr.opc = 1; + pIndJmp->ldr.iop1 = 1; + pIndJmp->ldr.V = 0; + pIndJmp->ldr.iop2 = 7; + pIndJmp->ldr.size = 3; + + pIndJmp->br = 0xD61F0220; + + return pbCode; +} + +_Ret_notnull_ +PBYTE +detour_gen_jmp_immediate( + _In_ PBYTE pbCode, + _In_opt_ PBYTE* ppPool, + _In_ PBYTE pbJmpVal) +{ + PBYTE pbLiteral; + if (ppPool != NULL) + { + *ppPool = *ppPool - 8; + pbLiteral = *ppPool; + } else + { + pbLiteral = pbCode + 8; + } + + *((PBYTE*)pbLiteral) = pbJmpVal; + LONG delta = (LONG)(pbLiteral - pbCode); + + pbCode = write_opcode(pbCode, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + pbCode = write_opcode(pbCode, 0xd61f0000 | (17 << 5)); // BR X17 + + if (ppPool == NULL) + { + pbCode += 8; + } + return pbCode; +} + +_Ret_notnull_ +PBYTE +detour_gen_brk( + _In_ PBYTE pbCode, + _In_ PBYTE pbLimit) +{ + while (pbCode < pbLimit) + { + pbCode = write_opcode(pbCode, 0xd4100000 | (0xf000 << 5)); + } + return pbCode; +} + +inline +INT64 +detour_sign_extend( + UINT64 value, + UINT bits) +{ + const UINT left = 64 - bits; + const INT64 m1 = -1; + const INT64 wide = (INT64)(value << left); + const INT64 sign = (wide < 0) ? (m1 << left) : 0; + return value | sign; +} + +_Ret_notnull_ +PBYTE +detour_skip_jmp( + _In_ PBYTE pbCode) +{ + // Skip over the import jump if there is one. + pbCode = (PBYTE)pbCode; + ULONG Opcode = fetch_opcode(pbCode); + + if ((Opcode & 0x9f00001f) == 0x90000010) + { + // adrp x16, IAT + ULONG Opcode2 = fetch_opcode(pbCode + 4); + + if ((Opcode2 & 0xffe003ff) == 0xf9400210) + { + // ldr x16, [x16, IAT] + ULONG Opcode3 = fetch_opcode(pbCode + 8); + + if (Opcode3 == 0xd61f0200) + { + // br x16 + +/* https://static.docs.arm.com/ddi0487/bb/DDI0487B_b_armv8_arm.pdf + The ADRP instruction shifts a signed, 21-bit immediate left by 12 bits, adds it to the value of the program counter with + the bottom 12 bits cleared to zero, and then writes the result to a general-purpose register. This permits the + calculation of the address at a 4KB aligned memory region. In conjunction with an ADD (immediate) instruction, or + a Load/Store instruction with a 12-bit immediate offset, this allows for the calculation of, or access to, any address + within +/- 4GB of the current PC. + +PC-rel. addressing + This section describes the encoding of the PC-rel. addressing instruction class. The encodings in this section are + decoded from Data Processing -- Immediate on page C4-226. + Add/subtract (immediate) + This section describes the encoding of the Add/subtract (immediate) instruction class. The encodings in this section + are decoded from Data Processing -- Immediate on page C4-226. + Decode fields + Instruction page + op + 0 ADR + 1 ADRP + +C6.2.10 ADRP + Form PC-relative address to 4KB page adds an immediate value that is shifted left by 12 bits, to the PC value to + form a PC-relative address, with the bottom 12 bits masked out, and writes the result to the destination register. + ADRP ,