Skip to content

Chain multiple hooks to the same function in a single transaction - #10

Open
m417z wants to merge 1 commit into
KNSoft:mainfrom
m417z:attach-hook-chain
Open

Chain multiple hooks to the same function in a single transaction#10
m417z wants to merge 1 commit into
KNSoft:mainfrom
m417z:attach-hook-chain

Conversation

@m417z

@m417z m417z commented Apr 4, 2025

Copy link
Copy Markdown

Currently, it's assumed that there can only be a single operation per target function in a single transaction. Attaching multiple hooks to the same function doesn't work properly, and removing one hook and setting another doesn't work either.

This change addresses this limitation as following:

  • Hook addition operations are done before hook removal operations during the commit. Hook addition relies on the disassembly of the original function as it was seen before the commit started. Hook removal operations may modify the function and invalidate this assumption.
  • Consecutive hook addition operations for the same target are now chained.

Note that hook removal operations handle the case of the function changing during the commit. In this case, the hook will be put in bypass mode, resulting in a correct removal of the hook but in a memory leak for the allocated trampoline. This case can be optimized in the future.

@RatinCN RatinCN assigned RatinCN and unassigned RatinCN Apr 16, 2025
@RatinCN RatinCN added the enhancement New feature or request label Apr 16, 2025
@RatinCN
RatinCN force-pushed the main branch 2 times, most recently from 8937581 to 30ff0bc Compare September 4, 2025 08:45
@m417z
m417z force-pushed the attach-hook-chain branch 2 times, most recently from f90c914 to d604904 Compare September 8, 2026 01:03
@m417z

m417z commented Sep 8, 2026

Copy link
Copy Markdown
Author

This feature was used in production for a large amount of users since December 2025, and had no reported issues or crashes.

I rebased it over the recent ARM64EC changes, made some minor improvements, and also added a demo/test that demonstrates it and makes sure it works correctly.

Currently, it's assumed that there can only be a single operation per
target function in a single transaction. Attaching multiple hooks to the
same function doesn't work properly, and removing one hook and setting
another doesn't work either.

This change addresses this limitation as following:

* Hook addition operations are done before hook removal operations
  during the commit. Hook addition relies on the disassembly of the
  original function as it was seen before the commit started. Hook
  removal operations may modify the function and invalidate this
  assumption.
* Consecutive hook addition operations for the same target are now
  chained.

Note that hook removal operations handle the case of the function
changing during the commit. In this case, the hook will be put in bypass
mode, resulting in a correct removal of the hook but in a memory leak
for the allocated trampoline. This case can be optimized in the future.
@m417z
m417z force-pushed the attach-hook-chain branch from d604904 to 5d936b2 Compare September 8, 2026 01:51
@RatinCN

RatinCN commented Sep 10, 2026

Copy link
Copy Markdown
Member

This feature was used in production for a large amount of users since December 2025, and had no reported issues or crashes.

I rebased it over the recent ARM64EC changes, made some minor improvements, and also added a demo/test that demonstrates it and makes sure it works correctly.

Sorry for the late reply. I understand the need for multiple handlers for the same function. However, I think this is better handled at the application or host level by installing a single hook and dispatching to the individual handlers.

This would also give the upper layer explicit control over handler ordering, lifetime, and chaining semantics—for example, allowing a handler to modify arguments, skip the next handler, modify the return value, or invoke the next handler more than once—without requiring multiple inline hooks on the target function.

I’d prefer to keep SlimDetours focused on the low-level hooking mechanism rather than extend its transaction and trampoline management to support hook chains. Is there a particular constraint in your use case that makes a single-hook dispatcher impractical?

@m417z

m417z commented Sep 11, 2026

Copy link
Copy Markdown
Author

Perhaps my use case is too specific. The idea is that there are different independent DLLs that are provided with an API to hook functions:

BOOL Wh_SetFunctionHook(void* targetFunction, void* hookFunction, void** originalFunction);

Two DLLs can set a hook for the same function. For performance and stability, the engine sets all hooks in a single transaction, so I need this case to be supported.

Is there a particular constraint in your use case that makes a single-hook dispatcher impractical?

I don't think it's possible to support arbitrary function hooks this way. I'll need to know the hooked function calling convention, and to even then, I can't call it without some inline assembly or code generation. Other things will break as well, such as the ability to use _ReturnAddress intrinsic in the hook to check where the function is called from.

In any case, if you think it's out of scope, we can close this PR and I can keep maintaining it in a fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants