Hi all,
I am developing a security application and am trying to accomplish a reliable DLL injection in a global system scale. The aim is to have each and every process load a custom user mode DLL that further intercepts some API calls to implement process monitoring and control when necessary. User mode code (CreateRemoteThread, LoadLibrary, SetWindowsHookEx etc) does pretty well to inject all currently running processes and at least most of the Windows GUI applications. Console applications including cmd.exe and short-lived cmd line based utilies are however ignored.
A kernel mode driver hook (largely based on http://www.codeproject.com/kb/system/hooksys.aspx ) comes to rescue with interception of NtCreateProcess, -ProcessEx, -Thread and -ThreadEx. The implementation of altering SSDT to hook these functions and to manipulate the starting address of the first thread of each process to a special thread that calls LoadLibrary before jumping to the real thread’s start address actually works. But only on XP. In XPSP3 it works even smooth and reliably, (modest) interoperability testing included.
The problems come with Vista and Windows 7 compatibility.
On Vista, apparently NtCreateProcess and -ProcessEx are abandoned. Hooking NtCreateUserProcess through the usual SSDT way works, and calls are spotted when user mode applications are started. The process handle returned by the real NtCreateUserProcess seems reasonable, 0x0900 and such. On the thread front, only NtCreateThreadEx gets hits. And the number of hits is nothing small. The function gets called pretty much constantly (not like busy loop constantly, but typically every few seconds) but the parameters (process handle etc) do not make much sense. Compared to the process handles returned by NtCreateUserProcess, the process handles passed are huge. I have not been able to spot matching process handle at the time of new application starting. It would feel logical, that a process is created first and then one or more threads spawned on it. Anyway, on Vista the hooking itself seems to work but cannot get anything done because the first thread creation cannot be detected, at least not in a reliable way.
Then on Windows 7, the story is shorter. I have tried numerous examples found plus my own code, and nothing runs. I have created free and checked versions of the drivers, signed them (with real certificate (and in Vista’s case also with self-created test certificates when Vista is in test-mode)), and who knows what other variations. W7 BSOD’s everytime when a write operation is done against the SSDT. It would seem obvious that Microsoft has decided to put a stop on the kernel mode API hooking, but have they? I have also tried reading a function address of NtCreateProcess (with the service index) from the service table and writing the exact same index back, but even that brings the BSOD up.
The service index numbers of these functions are figured out in user mode and passed to the driver over IOCTL call. Because of reasons unknown to my beginner KM skills, functions Nt/ZwCreateProcess didn’t resolve in the driver compilation. One sample was hooking ZwClose and that could be resolved in the driver code and the service index resolved there. Still, this UM->KM index number carrying works on XP and looks like it works on Vista/7 as well.
Everything described here has been in 32-bit environments. All test platforms are MSDN retail versions, would checked OS build make any difference?
I also played around with EasyHook (from CodePlex) for a while as it claimed Vista/7 compatibility and capability to provide smooth kernel mode hooking. Well, it was sort of smooth, but by the time I got till calling the LhInstallHook() function, the same BSOD kicks in from the EasyHookDrv32.sys. That one worked on XP as well so this approach didn’t seem to be any solution.
I have read a lot about why undocumented things shouldn’t be used and am aware of the downsides, but it seems like there is no other way to accomplish certain things and this is what anti-virus etc. vendors are all doing, isn’t it? If there is a better supported and clean way of doing this, then I will be super glad to learn about it.
Any comments, suggestions and insight will be appreciated.
Thanks.
KM