Conversation

Dll injection in general is just a horrible idea from a stability standpoint in almost all cases. There are only two fairly reliable solutions:
1
4
1. Injecting when the process/thread is in a known good state - This means (non-special) APCs, and window hook libraries. This is limited in what process it works on, but does avoid most problems, in part because the thread expects callbacks already, just not this kind.
1
2
2. Injecting in a more rootkit-like fashion - This means freestanding / ntdll-only injected dll, preferably mapped as a section, and called with some hook (like an instrumentation callback). This always works, but severely limits you in how you can write your injected module.
1
2
Well, the WriteProcessMemory + CreateRemoteThread + LoadLibrary combo worked fairly well for me so far. What I describe in the blog post is an exceptional incompatibility, but generally I'm not sure there's a better alternative with the same level of flexibility.
1
Well, the second method I listed is way more flexible in terms of what you can safely inject into (basically anything), it is rather inflexible on the side of what you can inject. But considering you "control" that side it might be a workable tradeoff.
1
I meant, flexible on the side of what I can inject. Windhawk is a modular customization framework, anyone can write mods to any programs. I wouldn't want to constrain devs to using ntdll, forcing to adhere to the DllMain limits, etc. But it might be a good tradeoff for others.
1
BTW regarding "an exceptional incompatibility", I'm no longer sure about that. Just today, a user showed a similar crash in a TLS callback. My findings (from the Windhawk Discord channel) are below. Seems that it's sometimes not safe to create threads before CRT is initialized.
Image
1
Yeah, that's yet another of the corner cases. I just assumed our ideas of what an acceptable crash rate is differs. What you have here is a case of "LoadLibrary in DllMain", except complicated by injection and threads. But the result is the same unreliable behavior.
1
(yes, not exactly because that one is extra bad due to critical sections, but you still mess up initialization order)
1
Except that here, unlike DllMain, I can't know when I'm in this (CRT initialization) context, can I? It's just non-remarkable code that runs at the beginning, no locks/mutexts I can grab and use. The best I can do is wait a bit before CreateThread and hope for the best.
1
AFAIK you can't, yes. This is why recording software and similar use APC or SetWindowHookEx, because the reverse (not enumerating times that are bad for injection, but using one that's most likely a good time) is safer, and they just don't care about windowless processes.