Just like we mentioned in The Main Thread Problem, some questions do not have direct answer just because they are invalid by definition.
Today, the invalid question would be:
How do I kill a process tree in Windows?
Unfortunately, the question is invalid, since Windows by design doesn't keep a tree of process creation relationship. Each process does have a parent process ID (except for the Windows Session Manager SMSS.exe), however this information is not going to change when the parent process got terminated.
To verify this, simply run tlist.exe -t and see the rootless processes, at least the following processes don't have a parent on my Win7 machine:
csrss.exe (536) conhost.exe (6036) CicMarshalWnd conhost.exe (8432) CicMarshalWndwinlogon.exe (672)explorer.exe (5640) Program Manager
Now back to the question, probably the intention was to kill all processes spawned from or forked by a certain process. If this is the case, Job Object might help. But please be cautious:
2 and 3 are subject to change in the near future.
Another option I could think of is to hook process creations and maintain our own data structure.
This could be done in either user mode or kernel mode, one possible approach could be:
extern "C" DDKAPI NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath);
static DDKAPI VOID CreateProcessNotifyRoutine(HANDLE hPPID, HANDLE hPID, BOOLEAN bCreate);
static DDKAPI VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
DDKAPI NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
pDriverObject->DriverUnload = DriverUnload;
retval = PsSetCreateProcessNotifyRoutine(CreateProcessNotifyRoutine, FALSE);
DDKAPI VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
DDKAPI VOID CreateProcessNotifyRoutine(HANDLE hPPID, HANDLE hPID, BOOLEAN bCreate)
DbgPrint("%s(PPID=%u, PID=%u)", bCreate ? "CreateProcess" : "TerminateProcess", hPPID, hPID);
I wouldn't recommend the driver way for several reasons:
In my previous blog Early Debugging, we've demonstrated how early can you get using a user mode debugger.
Normally we don't want to be such early, there are some other places we would want to start with:
Now let's talk a bit about the native C/C++ Runtime. When you start writing applications using C/C++ on Windows, normally you would be using CRT already, unless you explicitly tell the linker not to use it, like what I did in A Debugging Approach to IFEO.
The CRT (C Runtime Library) comes with Windows and Visual C++ Redistributable (let's not talk about the special version which serves CLR), also you can link a static version into your EXE/DLL.
CRT provides the fundamental C++ runtime support, some obvious features are:
Let's get to the code:
/* crtexport.cpp */
BOOL WINAPI Foobar()
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvContext)
Note: don't put DebugBreak inside DLL entry point as I do, unless you understand that the loader lock would make JIT debugger unhappy.
/* crtimport.cpp */
BOOL WINAPI Foobar();
cl.exe /LD /Zi crtexport.cpp
cl.exe /Zi crtimport.cpp crtexport.lib
Set two breakpoints, one at DllMain and one at the main function, then launch the application in Visual Studio Debugger:
Since our DLL is statically imported, the entry point of DLL is executed before the entry point of EXE.
As you might have noticed, the actual OEP is _DllMainCRTStartup. You can double click on the crtexport.dll!_DllMainCRTStartup frame and bring up the CRT startup code to start reading - on my machine the startup code is located at C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\dllcrt0.c.
Also, by taking a look at the Output window, we can see that CFoobar::CFoobar() has already been called, which means the global object was initialized before entering our DllMain. This is of course done by the CRT initialization code in __DllMainCRTStartup, which understands the contract between compiler and runtime.
Now you understand how the constructor of global variables gets called, think about the destructor semantic:
The actual OEP for the EXE is __tmainCRTStartup. You can double click on the crtimport.exe!__tmainCRTStartup frame and take a look at the code - on my machine the startup code is located at C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src\crt0.c.
As we mentioned in The Main Thread Problem, __tmainCRTStartup runs in the "main thread", and would kill all the other threads before it is going to destroy the global variables. One thing to mention is that CRT makes use of _endthreadex instead of calling ExitThread directly, since _endthreadex would destruct objects constructed on the stack and free the related TLS data, while ExitThread knows nothing about the _tiddata block.
A few more questions: