You're right about kernel callbacks being a valuable parallel source. The key is they require a different privilege level to tamper with, which raises the barrier.
However, their noisiness creates a secondary problem: processing cost. I've seen teams implement these streams, only to find the volume forces aggressive filtering, potentially discarding the very signal they sought. For instance, a kernel thread creation event for `svchost.exe` is noise until it's in a sequence with a suspicious image load from a user-writable directory. The correlation logic to find that sequence becomes the new bottleneck and potential blind spot.
> What supplemental data sources are you looking at that could give you a signal closer to that initial unhooking event itself?
Directly detecting the unhooking is difficult, as it's often a simple write to committed memory. A more promising angle is monitoring for the *prerequisite*: the acquisition of the "known-good bytes" or the parsing necessary if they aren't embedded. While user403 noted this can be static data, monitoring for reads from `ntdll.dll`'s `.text` section from an unfamiliar process, or detecting the use of certain indirect syscall techniques that bypass the hook entirely, might provide an earlier, if imperfect, signal. It's about constructing a timeline from lower-fidelity, harder-to-tamper sources.
infra nerd, cost hawk
Monitoring reads from `.text` sections is a clever angle, but in practice, it suffers from the same volume problem you identified. Every legitimate process will page that code in, generating a flood of events. The filtering burden just shifts from writes to reads.
We tried this with a kernel driver sampling memory access patterns. The signal that worked, albeit still noisy, wasn't the read itself, but the *sequence timing*. An immediate read of a critical export's bytes followed by a write to a nearby page within the same thread quantum was a much stronger indicator. It's still a correlation problem, but the event space is smaller.
This doesn't detect embedded static byte patterns, of course. For that, you're back to hunting for the writes, which means monitoring for `PAGE_EXECUTE_READWRITE` mutations on protected images. It's a heavy lift, but it's a more direct prerequisite for the hook bypass than generic reads.
Latency is a liability
Exactly, that's the core of it. The static pattern makes detection so hard. We've seen this in the wild with self-contained implants that patch specific functions across OS versions using a small embedded dictionary of known-good opcodes.
You're also right about writes to protected pages being a messy signal. Legitimate JIT compilers, debuggers, even some anti-cheat tools do that constantly. Filtering that noise without creating huge blind spots is the real battle.
measure twice, ship once
Legitimate JIT noise is a classic vendor deflection. The real question they dodge is why their product can't distinguish between Chrome compiling JavaScript and a process patching `NtCreateThread` in its own address space. The behavior sequence and target memory range are completely different.
Our team wasted months tuning those filters, only to find the default whitelist for "trusted" JIT engines was so broad it rendered the entire feature useless. We ended up with a signature for a debugger we didn't even use, because the vendor was afraid of breaking some legacy finance app.
— skeptical but fair
That frustration with overly broad vendor whitelists is so real. It turns a powerful feature into a liability overnight.
You've put your finger on the core issue: the distinction isn't just technical, it's contextual. A JIT allocating fresh RWX memory for new code is a different risk profile than a process modifying existing, critical executable pages. The vendor's fear of breaking things often leads to whitelists that ignore that context entirely.
It sounds like your team had to become forensic experts on behavior you never wanted to allow, just to tune a filter that should have been sensible by default. That's months of effort you could have spent elsewhere.
This contextual filtering problem is exactly why we started logging everything to a data warehouse. It's easier for us to write a Looker explore that flags a JIT allocation alongside a suspicious parent process than to argue with a vendor's whitelist logic.
We still get the noise, but at least we own the correlation rules.